arduino / ArduinoCore-mbed

328 stars 191 forks source link

Portenta or any Arduino MBED simple send receive WiFi websocket example #589

Closed hpssjellis closed 1 year ago

hpssjellis commented 1 year ago

I am looking for a simple WiFi Arduino MBED websocket client example preferably for the Portenta or NiclVision?

I can see lots of Portenta ethernet examples, but none of them seem specific to websockets

It looks like there are websocket libraries.

Khoi has some examples online but I can't seem to get them working and would prefer to use Arduino code to simplify the installation. here and here and here

P.S. I have a super simple websocket server which just uses these two websites.

  1. gitpod (needs a github login) auto loads everything. https://gitpod.io/#github.com/hpssjellis/gitpod-portenta-websocket
  2. The demo website: https://hpssjellis.github.io/gitpod-portenta-websocket/public/static-html03.html

The github for those 2 links is at https://github.com/hpssjellis/gitpod-portenta-websocket

image

The following code works fine on the Nano 33 IOT.


#include <SPI.h>
#include <WiFiNINA.h>

//#include "arduino_secrets.h" 

#define SECRET_SSID ""   // your network
#define SECRET_PASS "" // your password

///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID;        // your network SSID (name)
char pass[] = SECRET_PASS;    // your network password (use for WPA, or use as key for WEP)
int keyIndex = 0;            // your network key Index number (needed only for WEP)

int status = WL_IDLE_STATUS;
// if you don't want to use DNS (and reduce your sketch size)
// use the numeric IP instead of the name for the server:
//IPAddress server(74,125,232,128);  // numeric IP for Google (no DNS)

///////////////////////// Node Websocket server url without http:// or ending /         ////////

//   so https://myURL/

//   becomes

//   myURL

// new version websocket server
//char server[] = "8080-hpssjellis-gitpodporten-iie72xjq62h.ws-us78.gitpod.io";    

//old version
char server[] = "8080-hpssjellis-arduinosimpl-k1oalzba9ks.ws-us78.gitpod.io";    

///////////////////////// above is important  ////////////////////////////////////////////////////

String myRandWebSocket = String(rand()*10000+10000); //attempt at random security

// Initialize the Ethernet client library
// with the IP address and port of the server
// that you want to connect to (port 80 is default for HTTP):
//WiFiClient client;
WiFiSSLClient client;

void setup() {
  pinMode(LED_BUILTIN,OUTPUT);
  //Initialize serial and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  // check for the WiFi module:
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    while (true);
  }

  String fv = WiFi.firmwareVersion();
  if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
    Serial.println("Please upgrade the firmware");
  }

  // attempt to connect to Wifi network:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);

    // wait 10 seconds for connection:
    delay(10000);
  }
  Serial.println("Connected to wifi");
  printWifiStatus();

  Serial.println("\nStarting connection to server...");
  // if you get a connection, report back via serial:  
  //if (client.connect(server, 80)) {
  if (client.connect(server, 443)) {
    Serial.println("connected to server");
    // Make a HTTP request:
    client.println("GET / HTTP/1.1");
    client.println("Host: "+String(server));
    client.println("Upgrade: websocket");
    client.println("Connection: Upgrade");
    client.println("Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==");
    client.println("Sec-WebSocket-Version: 13");
    client.println();   
  }
}

void loop() {
  // if there are incoming bytes available
  // from the server, read them and print them:
  while (client.available()) {
    char c = client.read();
    Serial.write(c);
    if (c == 'A') {                    // can use any single letter or character
       digitalWrite(LED_BUILTIN, 1);               
       Serial.println();
    }    
    if (c == 'B') {
       digitalWrite(LED_BUILTIN, 0);               
       Serial.println();
    }
  }

  // if the server's disconnected, stop the client:
  if (!client.connected()) {
    Serial.println();
    Serial.println("disconnecting from server.");
    client.stop();

    // do nothing forevermore:
    while (true);
  }
}

void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your board's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}
hpssjellis commented 1 year ago

@facchinm any chance of giving me a few hints on how to get websockets working with the Portenta? I just need a proven working example.

As far as I can tell the mbed connectivity .h files are here

https://github.com/arduino/ArduinoCore-mbed/tree/master/cores/arduino/mbed/connectivity/netsocket/include/netsocket

not sure where the .cpp connected files reside?

I have been trying the new https://studio.keil.arm.com/ to try getting pure MBED sockets working but I don't really know how to burn the MBED Portenta bootloader. (I do have a segger j-link mini edu SWD/J-Tag device), but would really prefer to stay with the Arduino environment.

Any suggestions?

facchinm commented 1 year ago

Hi Jeremy, once connected to a network (either Wifi or Ethernet or event GSM) you can safely use mbed sockets apis, as we did for example in this library https://github.com/arduino/ArduinoCore-mbed/blob/master/libraries/SocketWrapper/src/utility/http_request.h#L54-L102 . In case you need some help I'd be happy to give hints but the whole stuff should be quite straightforward.

About the question on .cpp, they are all compiled into libmbed.a, so you can use the apis directly. Here are the sources https://github.com/arduino/mbed-os/tree/extrapatches-6.16.0/connectivity/netsocket/source

manchoz commented 1 year ago

@hpssjellis, there is also a WebSocket implementation in the ArduinoHttpClient library. A relevant example is here: SimpleWebSocket.ino

Have you already tried it?

hpssjellis commented 1 year ago

Thanks @manchoz and @facchinm I am getting closer, but everything I do points towards an issue with #include <WiFiSSLClient.h> for websockets. The code from @manchoz with some changes is having a bit of success. Not really sure how to implement the link example from @facchinm, however this link to the arduino mbed source will be very useful https://github.com/arduino/mbed-os/tree/extrapatches-6.16.0/connectivity/netsocket/source

The following code using WiFiClient.h looks like it actually talks to the server starting the handshake but then never receives any data

/*
  Simple WebSocket client for ArduinoHttpClient library
  Connects to the WebSocket server, and sends a hello
  message every 5 seconds

  created 28 Jun 2016
  by Sandeep Mistry
  modified 22 Jan 2019
  by Tom Igoe

  this example is in the public domain
*/
#include <ArduinoHttpClient.h>
//#include <WiFi101.h>
//#include "arduino_secrets.h"

#include <WiFi.h>
#include <WiFiSSLClient.h>

//#include "arduino_secrets.h"     // more safe
#define SECRET_SSID ""   // your network
#define SECRET_PASS "" // your password

///////please enter your sensitive data in the Secret tab/arduino_secrets.h
/////// Wifi Settings ///////
char ssid[] = SECRET_SSID;
char pass[] = SECRET_PASS;

// note this address is dynamically made so will be different each run
char serverAddress[] = "8080-hpssjellis-gitpodporten-k56rqynrp6i.ws-us79.gitpod.io";  // server address
int port = 443;

//WiFiClient wifi;
WiFiClient wifi;
//WiFiSSLClient wifi;
WebSocketClient client = WebSocketClient(wifi, serverAddress, port);
int status = WL_IDLE_STATUS;
int count = 0;

void setup() {
  Serial.begin(115200);
  while ( status != WL_CONNECTED) {
    Serial.print("Attempting to connect to Network named: ");
    Serial.println(ssid);                   // print the network name (SSID);

    // Connect to WPA/WPA2 network:
    status = WiFi.begin(ssid, pass);
  }

  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  Serial.println("starting WebSocket client");
  client.begin();

   if (client.connected()) {
  //if (client.connect(server, 443)) {
    Serial.println("connected to server");
    // Make a HTTP request:
   // client.println("GET / HTTP/1.1");
    client.println("GET /chat");
    client.println("Host: "+String(serverAddress));
    client.println("Upgrade: websocket");
    client.println("Connection: Upgrade");
    client.println("Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==");
    client.println("Sec-WebSocket-Version: 13");
    client.println();

   // 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++;

  } 
}

void loop() {

  while (client.connected()) {

    // 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(1000);
  }

  Serial.println("disconnected");
}
hpssjellis commented 1 year ago

An update here:

I think my online gitpod server is creating some issues with the ports 8080 and 443 so I will try a different websocket server. Perhaps 2 portenta is a better starting point.

I do have https://github.com/khoih-prog/WebSockets2_Generic websocket server and client working between 2 Portenta. They are doing a different handshake than I am sending, so the issues may be with my code.

Note: The IP's are local

[WS] WebsocketsClient::generateHandshake: handshake = GET / HTTP/1.1
Host: 192.168.197.101
Sec-WebSocket-Key: MDEyMzQ1Njc4OWFiY2RlZg==
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Version: 13
User-Agent: TinyWebsockets Client
Authorization: Basic 
Origin: https://github.com/khoih-prog/Websockets2_Generic

[WS] WebsocketsClient::connect: base64Authorization = 
[WS] WebsocketsClient::connect: step 2
[WS] WebsocketsClient::connect: step 3
[WS] WebsocketsClient::connect: step 4
[WS] WebsocketsClient::doestStartsWith: str = HTTP/1.1 101 Switching Protocols

[WS] WebsocketsClient::doestStartsWith: prefix = HTTP/1.1 101
[WS] WebsocketsClient::connect: step 5
[WS] WebsocketsClient::connect: step 6
[WS] WebsocketsClient::generateHandshake: key = Connection
[WS] WebsocketsClient::generateHandshake: value = Upgrade
[WS] WebsocketsClient::generateHandshake: key = Upgrade
[WS] WebsocketsClient::generateHandshake: value = websocket
[WS] WebsocketsClient::generateHandshake: key = Sec-WebSocket-Version
[WS] WebsocketsClient::generateHandshake: value = 13
[WS] WebsocketsClient::generateHandshake: key = Sec-WebSocket-Accept
[WS] WebsocketsClient::generateHandshake: value = BACScCJPNqyz+UBoqMH89VmURoA=
[WS] WebsocketsClient::connect: step 7

I would like to get it working with Arduino MBED libraries so I will continue messing with it.

hpssjellis commented 1 year ago

Like normal @facchinm is correct. It is fairly easy. I got the Arduino SSL client working with this code. I still have some blob binary communication issues to fix, but at least I am now connected and can close this issue.


//    https://www.amebaiot.com/zh/rtl8195-arduino-api-wifisslclient/

//    example at   https://github.com/ambiot/amb1_arduino/blob/dev/Arduino_package/hardware/libraries/WiFi/examples/WiFiSSLClient/WiFiSSLClient.ino

#include <WiFi.h>
#include <WiFiSSLClient.h>

char ssid[] = "";        // your network SSID (name)
char pass[] = "";       // your network password (use for WPA, or use as key for WEP)
int keyIndex = 0;                   // your network key Index number (needed only for WEP)

int status = WL_IDLE_STATUS;

char HTTPS_SERVER[] = "os.mbed.com";
char HTTPS_PATH[] = "/media/uploads/mbed_official/hello.txt";

// no certificate needed, Arduino has made it already a part of the SSLclient.

WiFiSSLClient client;

void setup() {
    //Initialize serial and wait for port to open
    Serial.begin(9600);

    // attempt to connect to Wifi network
    while (status != WL_CONNECTED) {
        Serial.print("\r\n Attempting to connect to SSID: ");
        Serial.println(ssid);
        // Connect to WPA/WPA2 network. Change this line if using open or WEP network
        status = WiFi.begin(ssid,pass);

        // wait for connection
        delay(5000);
    }
    Serial.println("Connected to wifi");
    printWifiStatus();

    Serial.println("\nStarting connection to server...");
 // client.setRootCA((unsigned char*)rootCABuff);  // already done by arduino
    // if a connection is formed, report back via serial
    if (client.connect(HTTPS_SERVER, 443)) {
        Serial.println("connected to server");
        // Make a HTTP request:
        client.print("GET ");
        client.print(HTTPS_PATH);
        client.println(" HTTP/1.1");
        client.print("Host: ");
        client.println(HTTPS_SERVER);
        client.println("Connection: close");
        client.println();
    } else {
        Serial.println("connected to server failed");
    }
    delay(100);
}

void loop() {
    // if there are incoming bytes available
    // from the server, read them and print them
    while (client.available()) {
        char c = client.read();
        Serial.write(c);
    }

    // if the server's disconnected, stop the client
    if (!client.connected()) {
        Serial.println();
        Serial.println("disconnecting from server.");
        client.stop();

        // do nothing
        while (true);
    }
}

void printWifiStatus() {
    // print the SSID of the network you're attached to:
    Serial.print("SSID: ");
    Serial.println(WiFi.SSID());

    // print your WiFi shield's IP address:
    IPAddress ip = WiFi.localIP();
    Serial.print("IP Address: ");
    Serial.println(ip);

    // print the received signal strength:
    long rssi = WiFi.RSSI();
    Serial.print("signal strength (RSSI):");
    Serial.print(rssi);
    Serial.println(" dBm");
}