arduino-libraries / ArduinoHttpClient

Arduino HTTP Client library
287 stars 172 forks source link

Connect Websocket client to Socket.IO server #136

Open TannerGilbert opened 2 years ago

TannerGilbert commented 2 years ago

I'm currently trying to connect my Arduino Portenta H7 to a Socket.IO server, but I'm unable to establish a connection.

Arduino code:

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

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(10, 0, 0, 100);

EthernetClient ethernet;

char serverAddress[] = "10.0.0.14/socket.io/?EIO=4&transport=websocket";  // server address
int port = 8080;

WebSocketClient client = WebSocketClient(ethernet, serverAddress, port);
int count = 0;

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.");
  }
}

void loop() {
  Serial.println("starting WebSocket client");
  client.begin();

  while (client.connected()) {
    Serial.print("Sending hello ");
    Serial.println(count);

    // send a hello #
    client.beginMessage(TYPE_TEXT);
    client.print("hello ");
    client.print(count);
    client.endMessage();

    // increment count for next message
    count++;

    // check if a message is available to be received
    int messageSize = client.parseMessage();

    if (messageSize > 0) {
      Serial.println("Received a message:");
      Serial.println(client.readString());
    }

    // wait 5 seconds
    delay(5000);
  }

  Serial.println("disconnected");
}

Socket.IO Example Server:

import socketio
from aiohttp import web

sio = socketio.AsyncServer()
app = web.Application()
sio.attach(app)

async def index(request):
    with open("index.html") as f:
        return web.Response(text=f.read(), content_type="text/html")

@sio.on("*")
def catch_all(event, sid, data):
    print("Socket ID: ", sid)
    print(data)

app.router.add_get("/", index)

if __name__ == "__main__":
    web.run_app(app)

Versions:

I also tried using arduinoWebSockets, but I couldn't get it to work with the Arduino Portenta H7. Any help is greatly appreciated.

TannerGilbert commented 2 years ago

Got it to work with arduinoWebSockets and node.js.

Server:

const io = require('socket.io')(8888);
io.on('connection', (socket) => {
    console.info(`[${socket.id }] new connection`, socket.request.connection.remoteAddress);

    socket.on('message', function(message) {        
        console.log(message)
        socket.broadcast.emit("message", message);
    });

    socket.on('reconnect', function() {
        console.warn(`[${socket.id }] reconnect.`);
    });

    socket.on('disconnect', () => {
        console.error(`[${socket.id }] disconnect.`);
    });
});

Client:

#include <Arduino.h>

#include <Ethernet.h>
#include <ArduinoJson.h>
#include <WebSocketsClient.h>
#include <SocketIOclient.h>

byte mac[] = {
    0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
IPAddress ip(10, 0, 0, 100);

EthernetClient ethernet;

SocketIOclient socketIO;

void socketIOEvent(socketIOmessageType_t type, uint8_t *payload, size_t length)
{
  switch (type)
  {
  case sIOtype_DISCONNECT:
    Serial.println("[IOc] Disconnected!");
    break;
  case sIOtype_CONNECT:
    Serial.println("[IOc] Connected!");
    // join default namespace (no auto join in Socket.IO V3)
    socketIO.send(sIOtype_CONNECT, "/");
    break;
  case sIOtype_EVENT:
  {
    char *sptr = NULL;
    int id = strtol((char *)payload, &sptr, 10);
    Serial.print("[IOc] get event: ");
    // Serial.print(payload);
    Serial.print(" id: ");
    Serial.println(id);
    if (id)
    {
      payload = (uint8_t *)sptr;
    }
    DynamicJsonDocument doc(1024);
    DeserializationError error = deserializeJson(doc, payload, length);
    if (error)
    {
      Serial.print(F("deserializeJson() failed: "));
      Serial.println(error.c_str());
      return;
    }

    String eventName = doc[0];
    Serial.print("[IOc] event name: ");
    Serial.println(eventName.c_str());

    // Message Includes a ID for a ACK (callback)
    if (id)
    {
      // creat JSON message for Socket.IO (ack)
      DynamicJsonDocument docOut(1024);
      JsonArray array = docOut.to<JsonArray>();

      // add payload (parameters) for the ack (callback function)
      JsonObject param1 = array.createNestedObject();
      param1["now"] = millis();

      // JSON to String (serializion)
      String output;
      output += id;
      serializeJson(docOut, output);

      // Send event
      socketIO.send(sIOtype_ACK, output);
    }
  }
  break;
  case sIOtype_ACK:
    Serial.print("[IOc] get ack: ");
    Serial.println(length);
    break;
  case sIOtype_ERROR:
    Serial.print("[IOc] get error: ");
    Serial.println(length);
    break;
  case sIOtype_BINARY_EVENT:
    Serial.print("[IOc] get binary: ");
    Serial.println(length);
    break;
  case sIOtype_BINARY_ACK:
    Serial.print("[IOc] get binary ack: ");
    Serial.println(length);
    break;
  }
}

void setup()
{
  Serial.begin(115200);

  // 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.");
  }

  // server address, port and URL
  socketIO.begin("10.0.0.14", 8888, "/socket.io/?EIO=4");

  // event handler
  socketIO.onEvent(socketIOEvent);
}

unsigned long messageTimestamp = 0;
void loop()
{
  socketIO.loop();

  uint64_t now = millis();

  if (now - messageTimestamp > 2000 && socketIO.isConnected())
  {
    messageTimestamp = now;

    // creat JSON message for Socket.IO (event)
    DynamicJsonDocument doc(1024);
    JsonArray array = doc.to<JsonArray>();

    // add event name
    array.add("message");

    // add payload (parameters) for the event
    JsonObject param1 = array.createNestedObject();
    param1["now"] = (uint32_t)now;

    // JSON to String (serializion)
    String output;
    serializeJson(doc, output);

    // Send event
    socketIO.sendEVENT(output);

    // Print JSON for debugging
    Serial.println(output);
  }
}