From 899cfbdbe7e5829ab49ffcf27f4a40c7336d664b Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Sat, 24 Aug 2024 17:01:45 +0700 Subject: [PATCH] feat: Support Nano 33 IoT, MKR WIFI 1010, XIAO, Wio Terminal (#901) --- .../compile-arduino_wifinina-examples.yaml | 17 ++- .../compile-seeed-studio-examples.yaml | 81 ++++++++++++++ .../workflows/compile-unor4wifi-examples.yaml | 17 ++- README.md | 3 +- .../WebSocketClient/WebSocketClient.ino | 105 ++++++++++++++++++ library.json | 2 +- src/SocketIOclient.cpp | 2 +- src/SocketIOclient.h | 4 +- src/WebSockets.h | 32 +++++- src/WebSocketsClient.cpp | 26 ++++- src/WebSocketsClient.h | 9 ++ src/WebSocketsServer.cpp | 4 +- src/libb64/cdecode.c | 2 +- src/libb64/cencode.c | 2 +- 14 files changed, 286 insertions(+), 20 deletions(-) create mode 100644 .github/workflows/compile-seeed-studio-examples.yaml create mode 100644 examples/seeed-studio/xio-wio-terminal/WebSocketClient/WebSocketClient.ino diff --git a/.github/workflows/compile-arduino_wifinina-examples.yaml b/.github/workflows/compile-arduino_wifinina-examples.yaml index 166847f..15c1773 100644 --- a/.github/workflows/compile-arduino_wifinina-examples.yaml +++ b/.github/workflows/compile-arduino_wifinina-examples.yaml @@ -1,7 +1,22 @@ name: Compile Arduino WiFiNINA Examples # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows -on: [push, pull_request] +on: + push: + paths: + - ".github/workflows/compile-arduino_wifinina-examples.yaml" + - "examples/arduino_wifinina/**" + - "src/**" + pull_request: + paths: + - ".github/workflows/compile-arduino_wifinina-examples.yaml" + - "examples/arduino_wifinina/**" + - "src/**" + schedule: + # Run every Tuesday at 8 AM UTC to catch breakage caused by changes to external resources (libraries, platforms). + - cron: "0 8 * * TUE" + workflow_dispatch: + repository_dispatch: jobs: build: diff --git a/.github/workflows/compile-seeed-studio-examples.yaml b/.github/workflows/compile-seeed-studio-examples.yaml new file mode 100644 index 0000000..859aae4 --- /dev/null +++ b/.github/workflows/compile-seeed-studio-examples.yaml @@ -0,0 +1,81 @@ +name: Compile SeedStudio Examples + +# See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows +on: + push: + paths: + - ".github/workflows/compile-seeed-studio-examples.yaml" + - "examples/seeed-studio/**" + - "src/**" + pull_request: + paths: + - ".github/workflows/compile-seeed-studio-examples.yaml" + - "examples/seeed-studio/**" + - "src/**" + schedule: + # Run every Tuesday at 8 AM UTC to catch breakage caused by changes to external resources (libraries, platforms). + - cron: "0 8 * * TUE" + workflow_dispatch: + repository_dispatch: + +jobs: + build: + name: ${{ matrix.board.fqbn }} + runs-on: ubuntu-latest + + env: + SKETCHES_REPORTS_PATH: sketches-reports + + strategy: + fail-fast: false + + matrix: + board: + - fqbn: Seeeduino:samd:seeed_XIAO_m0:usbstack=arduino,debug=off + platforms: | + - name: Seeeduino:samd + source-url: https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json + libraries: | + - name: Seeed Arduino rpcWiFi + - name: Seeed Arduino rpcUnified + - name: Seeed_Arduino_mbedtls + - name: Seeed Arduino FS + - name: Seeed Arduino SFUD + artifact-name-suffix: seeeduino-xia0 + - fqbn: Seeeduino:samd:seeed_wio_terminal + platforms: | + - name: Seeeduino:samd + source-url: https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json + libraries: | + - name: Seeed Arduino rpcWiFi + - name: Seeed Arduino rpcUnified + - name: Seeed_Arduino_mbedtls + - name: Seeed Arduino FS + - name: Seeed Arduino SFUD + artifact-name-suffix: seeeduino-wio_terminal + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Compile examples + uses: arduino/compile-sketches@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + fqbn: ${{ matrix.board.fqbn }} + platforms: ${{ matrix.board.platforms }} + libraries: | + # Install the library from the local path. + - source-path: ./ + ${{ matrix.board.libraries }} + sketch-paths: | + - examples/seeed-studio/xio-wio-terminal/WebSocketClient + enable-deltas-report: true + sketches-report-path: ${{ env.SKETCHES_REPORTS_PATH }} + + - name: Save sketches report as workflow artifact + uses: actions/upload-artifact@v4 + with: + if-no-files-found: error + path: ${{ env.SKETCHES_REPORTS_PATH }} + name: sketches-report-${{ matrix.board.artifact-name-suffix }} \ No newline at end of file diff --git a/.github/workflows/compile-unor4wifi-examples.yaml b/.github/workflows/compile-unor4wifi-examples.yaml index 9be9878..71b94df 100644 --- a/.github/workflows/compile-unor4wifi-examples.yaml +++ b/.github/workflows/compile-unor4wifi-examples.yaml @@ -1,7 +1,22 @@ name: Compile Arduino UNO R4 WiFi Examples # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows -on: [push, pull_request] +on: + push: + paths: + - ".github/workflows/compile-unor4wifi-examples.yaml" + - "examples/arduino_renesas/**" + - "src/**" + pull_request: + paths: + - ".github/workflows/compile-unor4wifi-examples.yaml" + - "examples/arduino_renesas/**" + - "src/**" + schedule: + # Run every Tuesday at 8 AM UTC to catch breakage caused by changes to external resources (libraries, platforms). + - cron: "0 8 * * TUE" + workflow_dispatch: + repository_dispatch: jobs: build: diff --git a/README.md b/README.md index 2dcec63..bc37c8a 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,8 @@ a WebSocket Server and Client for Arduino based on RFC6455. - ATmega2560 with Ethernet Shield (ATmega branch) - ATmega2560 with enc28j60 (ATmega branch) - Arduino UNO [R4 WiFi](https://github.com/arduino/ArduinoCore-renesas) - - Arduino Nano 33 IoT, MKR WIFI 1010 + - Arduino Nano 33 IoT, MKR WIFI 1010 (requires [WiFiNINA](https://github.com/arduino-libraries/WiFiNINA/) library) + - Seeeduino XIAO, Seeeduino Wio Terminal (requires [rpcWiFi](https://github.com/Seeed-Studio/Seeed_Arduino_rpcWiFi) library) ###### Note: ###### diff --git a/examples/seeed-studio/xio-wio-terminal/WebSocketClient/WebSocketClient.ino b/examples/seeed-studio/xio-wio-terminal/WebSocketClient/WebSocketClient.ino new file mode 100644 index 0000000..d26aac0 --- /dev/null +++ b/examples/seeed-studio/xio-wio-terminal/WebSocketClient/WebSocketClient.ino @@ -0,0 +1,105 @@ +/* + * WebSocketClient.ino + * + * Created on: 10.08.2024 + * + */ + +#include +#include +#include +#include + +WebSocketsClient webSocket; +WiFiMulti wifiMulti; + +#define USE_SERIAL Serial + +void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) { + const uint8_t* src = (const uint8_t*) mem; + USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len); + for(uint32_t i = 0; i < len; i++) { + if(i % cols == 0) { + USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i); + } + USE_SERIAL.printf("%02X ", *src); + src++; + } + USE_SERIAL.printf("\n"); +} + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + + // send message to server when Connected + webSocket.sendTXT("Connected"); + break; + case WStype_TEXT: + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + // send message to server + // webSocket.sendTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + case WStype_ERROR: + case WStype_FRAGMENT_TEXT_START: + case WStype_FRAGMENT_BIN_START: + case WStype_FRAGMENT: + case WStype_PONG: + case WStype_PING: + case WStype_FRAGMENT_FIN: + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + wifiMulti.addAP("SSID", "passpasspass"); + + //WiFi.disconnect(); + while(wifiMulti.run() != WL_CONNECTED) { + delay(100); + } + + // server address, port and URL + webSocket.begin("192.168.0.123", 81, "/"); + + // event handler + webSocket.onEvent(webSocketEvent); + + // use HTTP Basic Authorization this is optional remove if not needed + webSocket.setAuthorization("user", "Password"); + + // try ever 5000 again if connection has failed + webSocket.setReconnectInterval(5000); + +} + +void loop() { + webSocket.loop(); +} diff --git a/library.json b/library.json index 56ae925..a2159dc 100644 --- a/library.json +++ b/library.json @@ -16,7 +16,7 @@ "keywords": "wifi, http, web, server, client, websocket", "license": "LGPL-2.1", "name": "WebSockets", - "platforms": "atmelavr, espressif8266, espressif32, raspberrypi, renesas_uno", + "platforms": "*", "repository": { "type": "git", "url": "https://github.com/Links2004/arduinoWebSockets.git" diff --git a/src/SocketIOclient.cpp b/src/SocketIOclient.cpp index 1533316..dd5037c 100644 --- a/src/SocketIOclient.cpp +++ b/src/SocketIOclient.cpp @@ -261,4 +261,4 @@ void SocketIOclient::handleCbEvent(WStype_t type, uint8_t * payload, size_t leng case WStype_PONG: break; } -} +} \ No newline at end of file diff --git a/src/SocketIOclient.h b/src/SocketIOclient.h index e33a6fd..99af80b 100644 --- a/src/SocketIOclient.h +++ b/src/SocketIOclient.h @@ -55,9 +55,11 @@ class SocketIOclient : protected WebSocketsClient { void beginSSL(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino", uint32_t pingInterval = 60 * 1000, uint32_t pongTimeout = 90 * 1000, uint8_t disconnectTimeoutCount = 5); #ifndef SSL_AXTLS void beginSSLWithCA(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * CA_cert = NULL, const char * protocol = "arduino", uint32_t pingInterval = 60 * 1000, uint32_t pongTimeout = 90 * 1000, uint8_t disconnectTimeoutCount = 5); - void beginSSLWithCA(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", BearSSL::X509List * CA_cert = NULL, const char * protocol = "arduino", uint32_t pingInterval = 60 * 1000, uint32_t pongTimeout = 90 * 1000, uint8_t disconnectTimeoutCount = 5); void setSSLClientCertKey(const char * clientCert = NULL, const char * clientPrivateKey = NULL); + #if defined(SSL_BARESSL) + void beginSSLWithCA(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", BearSSL::X509List * CA_cert = NULL, const char * protocol = "arduino", uint32_t pingInterval = 60 * 1000, uint32_t pongTimeout = 90 * 1000, uint8_t disconnectTimeoutCount = 5); void setSSLClientCertKey(BearSSL::X509List * clientCert = NULL, BearSSL::PrivateKey * clientPrivateKey = NULL); + #endif #endif #endif bool isConnected(void); diff --git a/src/WebSockets.h b/src/WebSockets.h index f0a2bf9..13a022a 100644 --- a/src/WebSockets.h +++ b/src/WebSockets.h @@ -105,6 +105,12 @@ #define WEBSOCKETS_YIELD() yield() #define WEBSOCKETS_YIELD_MORE() delay(1) +#elif defined(WIO_TERMINAL) || defined(SEEED_XIAO_M0) + +#define WEBSOCKETS_MAX_DATA_SIZE (15 * 1024) +#define WEBSOCKETS_YIELD() yield() +#define WEBSOCKETS_YIELD_MORE() delay(1) + #else // atmega328p has only 2KB ram! @@ -128,7 +134,7 @@ #define NETWORK_RP2040 (6) #define NETWORK_UNOWIFIR4 (7) #define NETWORK_WIFI_NINA (8) - +#define NETWORK_SAMD_SEED (9) // max size of the WS Message Header #define WEBSOCKETS_MAX_HEADER_SIZE (14) @@ -153,6 +159,9 @@ #elif defined(ARDUINO_SAMD_MKRWIFI1010) || defined(ARDUINO_SAMD_NANO_33_IOT) #define WEBSOCKETS_NETWORK_TYPE NETWORK_WIFI_NINA +#elif defined(WIO_TERMINAL) || defined(SEEED_XIAO_M0) +#define WEBSOCKETS_NETWORK_TYPE NETWORK_SAMD_SEED + #else #define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100 @@ -248,10 +257,6 @@ #elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_UNOWIFIR4) #include - -#define WEBSOCKETS_NETWORK_CLASS WiFiClient -#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer - #define WEBSOCKETS_NETWORK_CLASS WiFiClient #define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer @@ -264,6 +269,23 @@ #define WEBSOCKETS_NETWORK_CLASS WiFiClient #define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer +#define WEBSOCKETS_NETWORK_SSL_CLASS WiFiSSLClient + +#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_SAMD_SEED) +#if __has_include() && __has_include() + #include + #include +#else + #error "Please install rpcWiFi library!" +#endif + +#define WEBSOCKETS_NETWORK_CLASS WiFiClient +#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer +#define WEBSOCKETS_NETWORK_SSL_CLASS WiFiClientSecure + +#define WEBSOCKETS_NETWORK_CLASS WiFiClient +#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer + #else #error "no network type selected!" #endif diff --git a/src/WebSocketsClient.cpp b/src/WebSocketsClient.cpp index 1b8f7a3..34357ab 100644 --- a/src/WebSocketsClient.cpp +++ b/src/WebSocketsClient.cpp @@ -155,6 +155,7 @@ void WebSocketsClient::beginSSL(const char * host, uint16_t port, const char * u _CA_cert = NULL; } +#if defined(SSL_BARESSL) void WebSocketsClient::beginSslWithCA(const char * host, uint16_t port, const char * url, BearSSL::X509List * CA_cert, const char * protocol) { begin(host, port, url, protocol); _client.isSSL = true; @@ -166,14 +167,16 @@ void WebSocketsClient::beginSslWithCA(const char * host, uint16_t port, const ch beginSslWithCA(host, port, url, new BearSSL::X509List(CA_cert), protocol); } +void WebSocketsClient::setSSLClientCertKey(const char * clientCert, const char * clientPrivateKey) { + setSSLClientCertKey(new BearSSL::X509List(clientCert), new BearSSL::PrivateKey(clientPrivateKey)); +} + void WebSocketsClient::setSSLClientCertKey(BearSSL::X509List * clientCert, BearSSL::PrivateKey * clientPrivateKey) { _client_cert = clientCert; _client_key = clientPrivateKey; } +#endif // SSL_BARESSL -void WebSocketsClient::setSSLClientCertKey(const char * clientCert, const char * clientPrivateKey) { - setSSLClientCertKey(new BearSSL::X509List(clientCert), new BearSSL::PrivateKey(clientPrivateKey)); -} #endif // SSL_AXTLS #endif // HAS_SSL @@ -242,7 +245,12 @@ void WebSocketsClient::loop(void) { if(_client.isSSL) { DEBUG_WEBSOCKETS("[WS-Client] connect wss...\n"); if(_client.ssl) { - delete _client.ssl; + #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_WIFI_NINA) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_SAMD_SEED) || (WEBSOCKETS_NETWORK_TYPE ==NETWORK_UNOWIFIR4) + // does not support delete (no destructor) + #else + delete _client.ssl; + #endif + _client.ssl = NULL; _client.tcp = NULL; } @@ -256,6 +264,10 @@ void WebSocketsClient::loop(void) { _client.ssl->setCACert((const uint8_t *)_CA_cert, strlen(_CA_cert) + 1); #elif(defined(ESP8266) || defined(ARDUINO_ARCH_RP2040)) && defined(SSL_BARESSL) _client.ssl->setTrustAnchors(_CA_cert); +#elif defined(WIO_TERMINAL) || defined(SEEED_XIAO_M0) + _client.ssl->setCACert(_CA_cert); +#elif defined(ARDUINO_SAMD_MKRWIFI1010) || defined(ARDUINO_SAMD_NANO_33_IOT) + // no setCACert #else #error setCACert not implemented #endif @@ -283,7 +295,11 @@ void WebSocketsClient::loop(void) { } else { DEBUG_WEBSOCKETS("[WS-Client] connect ws...\n"); if(_client.tcp) { + #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_WIFI_NINA) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_SAMD_SEED) || (WEBSOCKETS_NETWORK_TYPE ==NETWORK_UNOWIFIR4) + // does not support delete (no destructor) + #else delete _client.tcp; + #endif _client.tcp = NULL; } _client.tcp = new WEBSOCKETS_NETWORK_CLASS(); @@ -534,7 +550,7 @@ void WebSocketsClient::clientDisconnect(WSclient_t * client) { #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) client->status = WSC_NOT_CONNECTED; #else - #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_WIFI_NINA) + #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_WIFI_NINA) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_SAMD_SEED) || (WEBSOCKETS_NETWORK_TYPE ==NETWORK_UNOWIFIR4) // does not support delete (no destructor) #else delete client->tcp; diff --git a/src/WebSocketsClient.h b/src/WebSocketsClient.h index c2a6b92..95a126a 100644 --- a/src/WebSocketsClient.h +++ b/src/WebSocketsClient.h @@ -48,8 +48,10 @@ class WebSocketsClient : protected WebSockets { void beginSSL(String host, uint16_t port, String url = "/", String fingerprint = "", String protocol = "arduino"); #else void beginSSL(const char * host, uint16_t port, const char * url = "/", const uint8_t * fingerprint = NULL, const char * protocol = "arduino"); + #if defined(SSL_BARESSL) void beginSslWithCA(const char * host, uint16_t port, const char * url = "/", BearSSL::X509List * CA_cert = NULL, const char * protocol = "arduino"); void setSSLClientCertKey(BearSSL::X509List * clientCert = NULL, BearSSL::PrivateKey * clientPrivateKey = NULL); + #endif void setSSLClientCertKey(const char * clientCert = NULL, const char * clientPrivateKey = NULL); #endif void beginSslWithCA(const char * host, uint16_t port, const char * url = "/", const char * CA_cert = NULL, const char * protocol = "arduino"); @@ -129,13 +131,20 @@ class WebSocketsClient : protected WebSockets { #define SSL_FINGERPRINT_NULL "" #else const uint8_t * _fingerprint; + #if defined(SSL_BARESSL) BearSSL::X509List * _CA_cert; BearSSL::X509List * _client_cert; BearSSL::PrivateKey * _client_key; + #endif #define SSL_FINGERPRINT_IS_SET (_fingerprint != NULL) #define SSL_FINGERPRINT_NULL NULL #endif +#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_SAMD_SEED) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_WIFI_NINA) + const char * _CA_cert; + const uint8_t * _CA_bundle; +#endif + #endif WSclient_t _client; diff --git a/src/WebSocketsServer.cpp b/src/WebSocketsServer.cpp index 697c1d2..2eccbc4 100644 --- a/src/WebSocketsServer.cpp +++ b/src/WebSocketsServer.cpp @@ -65,7 +65,7 @@ WebSocketsServerCore::~WebSocketsServerCore() { } WebSocketsServer::~WebSocketsServer() { - #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_WIFI_NINA) + #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_WIFI_NINA) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_SAMD_SEED) // does not support delete (no destructor) #else delete _server; @@ -543,7 +543,7 @@ void WebSocketsServerCore::dropNativeClient(WSclient_t * client) { } #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) client->status = WSC_NOT_CONNECTED; -#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_WIFI_NINA) +#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_WIFI_NINA) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_SAMD_SEED) // does not support delete (no destructor) #else delete client->tcp; diff --git a/src/libb64/cdecode.c b/src/libb64/cdecode.c index 615068a..c40d0a8 100644 --- a/src/libb64/cdecode.c +++ b/src/libb64/cdecode.c @@ -9,7 +9,7 @@ For details, see http://sourceforge.net/projects/libb64 #include #endif -#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(WIO_TERMINAL) || defined(SEEED_XIAO_M0) #define CORE_HAS_LIBB64 #endif diff --git a/src/libb64/cencode.c b/src/libb64/cencode.c index cdc0f67..937474d 100644 --- a/src/libb64/cencode.c +++ b/src/libb64/cencode.c @@ -9,7 +9,7 @@ For details, see http://sourceforge.net/projects/libb64 #include #endif -#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(WIO_TERMINAL) || defined(SEEED_XIAO_M0) #define CORE_HAS_LIBB64 #endif