无需请求即可通过 Express 向 Arduino 发送数据

Send data to Arduino via Express without request

我有一个 Express 服务器 运行,Arduinos 目前向它发出 POST 请求。服务器然后进行一些其他处理。

我正在尝试让 Express 向 Arduino 发送一个事件(数据),然后让 Arduino 在接收到数据时产生一个中断。

但是,我希望 Express 能够向 Arduino 发送数据,而无需 Arduino 首先向服务器发送 HTTP 请求。

我可以定期轮询服务器,但我不希望事件经常发生,而且我认为每隔一秒左右轮询一次服务器以查看是否有新数据可用是一种浪费。

const express = require("express");
const bodyParser = require("body-parser");

app.get("/", (req, res) => {
  return res.status(200).send({ message: "Success!" });
});

//Invalid code but lets say the Arduino was on IP 192.168.0.223 
//and we wanted to push data to it

app.sendData("192.168.0.233", {temperature: 20.0});

澄清一下,上面是我的代码的一部分。如果我从 Arduino 到 Express 路由 '/' 发出请求 ,那么我可以发送一个 响应 ,其中包含从服务器到 Arduino 的数据。

但是,如果 Arduino 一开始就没有发出请求,我该如何向它发送(或推送)数据?

#include <SPI.h>
#include <Ethernet.h>

byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 1, 177);


EthernetServer server(80);

void setup() {

  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  Serial.println("Ethernet WebServer Example");

  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);

  // Check for Ethernet hardware present
  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
    while (true) {
      delay(1); // do nothing, no point running without Ethernet hardware
    }
  }
  if (Ethernet.linkStatus() == LinkOFF) {
    Serial.println("Ethernet cable is not connected.");
  }

  // start the server
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());
}


void loop() {
  // listen for incoming clients
  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) { <-- this is blocking code 
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");  // the connection will be closed after completion of the response
          client.println("Refresh: 5");  // refresh the page automatically every 5 sec
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html>");
          // output the value of each analog input pin
          for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
            int sensorReading = analogRead(analogChannel);
            client.print("analog input ");
            client.print(analogChannel);
            client.print(" is ");
            client.print(sensorReading);
            client.println("<br />");
          }
          client.println("</html>");
          break;
        }
        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        } else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
    Serial.println("client disconnected");
  }
}

上面snippet在Arduino上搭建了一个服务器,但它仍然要主动监听连接,然后检查客户端是否连接,然后读入数据。如果这是必须完成的方式,那么我将重新考虑我的方法。

我希望服务器简单地将数据发送到 IP,Arduino 将生成一个我可以服务的 interrupt。 UART 是如何工作的?没有初始请求数据出现并生成您可以处理的中断。

感谢@jfriend00 为我指明了 websockets 的方向和这篇文章 general guidance

我决定为服务器使用 ws

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 3005 });

wss.on('connection', function connection(ws) {
  ws.on('message', function incoming(message) {
    console.log('received: %s', message);
  });

  
  setInterval(() => {
    ws.send(`${new Date()}`);
  }, 1000);
});

而在 Arduino 上 ATmega branch of the WebSockets library

#define MONITOR_SPEED               921600

#include <SPI.h>
#include <Ethernet.h>
#include <WebSocketsClient.h>

WebSocketsClient webSocket;

//MAC address for arduino- these can be manually assigned 
//but cannot conflict with any other address on the same network
byte mac[] = {0xAE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};

// Set the static IP address to use if the DHCP fails to assign
static IPAddress ip(192, 168, 0, 177);
static IPAddress myDns(192, 168, 0, 1);
static EthernetClient client;


void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {

    switch(type) {
        case WStype_DISCONNECTED:
            Serial.println("[WSc] Disconnected!\n");
            break;
        case WStype_CONNECTED:
            {
                Serial.print("[WSc] Connected to url: ");
                Serial.println((char *)payload);
                // send message to server when Connected
                webSocket.sendTXT("Connected");
                
            }
            break;
        case WStype_TEXT:
            Serial.print("[WSc] get text: ");
            Serial.println((char *)payload);
            // send message to server
            // webSocket.sendTXT("message here");
            break;
        case WStype_BIN:
            Serial.print("[WSc] get binary length: ");
            Serial.println(length);
           // hexdump(payload, length);

            // send data to server
            // webSocket.sendBIN(payload, length);
            break;
    }

}


void setup()
{

    // Open serial communications and wait for port to open:
    Serial.begin(MONITOR_SPEED);  //921600
    while (!Serial)
    {
        ; // wait for serial port to connect. Needed for native USB port only
    }

    // start the Ethernet connection:
    Serial.println("Initialize Ethernet with DHCP:");
    if (Ethernet.begin(mac) == 0)
    {

        Serial.println("Failed to configure Ethernet using DHCP");
        
        // Check for Ethernet hardware present
        if (Ethernet.hardwareStatus() == EthernetNoHardware)
        {
            Serial.println("Ethernet shield was not found.");
            Serial.println("Program will continue to run without shield but will not report to web server");
        }

        if (Ethernet.linkStatus() == LinkOFF)
        {
            Serial.println("Ethernet cable is not connected.");
        }

        // try to congifure using IP address instead of DHCP:
        Ethernet.begin(mac, ip, myDns);
    }

    byte macBuffer[6];              // create a buffer to hold the MAC address
    Ethernet.MACAddress(macBuffer); // fill the buffer
    Serial.print(" MAC address : ");
    for (byte octet = 0; octet < 6; octet++)
    {
        Serial.print(macBuffer[octet], HEX);
        if (octet < 5)
        {
            Serial.print(':');
        }
    }
    Serial.println("");
    Serial.print("  DHCP assigned IP : ");
    Serial.println(Ethernet.localIP());

    webSocket.begin("192.168.0.189", 3005);
    webSocket.onEvent(webSocketEvent);
    
}



void loop()
{
   webSocket.loop();
   Ethernet.maintain();
   if (!client.connected()) {
     client.stop();
   }
  
}

这应该会在您的串行监视器上打印当前日期和时间:

[WSc] get text: Fri Mar 19 2021 10:13:12 GMT-0400 (Atlantic Standard Time)
[WSc] get text: Fri Mar 19 2021 10:13:13 GMT-0400 (Atlantic Standard Time)
[WSc] get text: Fri Mar 19 2021 10:13:14 GMT-0400 (Atlantic Standard Time)
[WSc] get text: Fri Mar 19 2021 10:13:15 GMT-0400 (Atlantic Standard Time)
[WSc] get text: Fri Mar 19 2021 10:13:16 GMT-0400 (Atlantic Standard Time)

在您的服务器上:

received: Connected