From ed8137e6864018ae4329f34268bd712c2bf1ffdb Mon Sep 17 00:00:00 2001 From: cjkas Date: Mon, 23 Mar 2026 14:49:17 +0100 Subject: [PATCH] replace sync sockets --- .claude/settings.local.json | 3 +- .gitignore | 1 + .vscode/c_cpp_properties.json | 14 +- platformio.ini | 1 - src/Sockets.cpp | 257 ++++++++++++++++++---------------- src/Sockets.h | 5 +- src/WResp.cpp | 8 +- src/WResp.h | 7 +- 8 files changed, 154 insertions(+), 142 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 6bc1b04..9b45ded 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -1,7 +1,8 @@ { "permissions": { "allow": [ - "Bash(~/.platformio/penv/Scripts/platformio.exe run:*)" + "Bash(~/.platformio/penv/Scripts/platformio.exe run:*)", + "Bash(grep -n \"emitSockets\" \"c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/src\"/*.cpp)" ] } } diff --git a/.gitignore b/.gitignore index 66186f9..d11457f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ data/ build/ coredump_report.txt coredump.bin +logs/ \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 218700a..44054fa 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -12,21 +12,20 @@ "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/src", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/WebServer/src", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/AsyncUDP/src", + "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/Ethernet/src", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/ESPmDNS/src", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/Update/src", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/HTTPClient/src", + "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/WiFiClientSecure/src", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/Preferences/src", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/LittleFS/src", "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/ESPAsyncWebServer/src", + "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/FS/src", "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/AsyncTCP/src", "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/PubSubClient/src", "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/SmartRC-CC1101-Driver-Lib", - "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/WebSockets/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/WiFiClientSecure/src", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/SPI/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/Ethernet/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src", "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/ArduinoJson/src", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/newlib/platform_include", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/include", @@ -254,21 +253,20 @@ "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/src", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/WebServer/src", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/AsyncUDP/src", + "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/Ethernet/src", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/ESPmDNS/src", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/Update/src", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/HTTPClient/src", + "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/WiFiClientSecure/src", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/Preferences/src", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/LittleFS/src", "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/ESPAsyncWebServer/src", + "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/FS/src", "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/AsyncTCP/src", "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/PubSubClient/src", "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/SmartRC-CC1101-Driver-Lib", - "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/WebSockets/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/WiFiClientSecure/src", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/SPI/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/Ethernet/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src", "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/ArduinoJson/src", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/newlib/platform_include", "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/include", diff --git a/platformio.ini b/platformio.ini index 7ea5b5b..b237059 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,7 +17,6 @@ board = esp32dev framework = arduino lib_deps = bblanchon/ArduinoJson@^7.2.2 - links2004/WebSockets@^2.7.3 lsatan/SmartRC-CC1101-Driver-Lib@^2.5.7 knolleary/PubSubClient@^2.8 esp32async/ESPAsyncWebServer @ ^3.10.3 diff --git a/src/Sockets.cpp b/src/Sockets.cpp index 558f982..fb371e1 100644 --- a/src/Sockets.cpp +++ b/src/Sockets.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include "Sockets.h" #include "ConfigSettings.h" @@ -14,33 +13,56 @@ extern SomfyShadeController somfy; extern SocketEmitter sockEmit; extern GitUpdater git; +AsyncWebServer wsServer(8080); +AsyncWebSocket ws("/"); -WebSocketsServer sockServer = WebSocketsServer(8080); +#define MAX_WS_CLIENTS 5 +static uint32_t clientMap[MAX_WS_CLIENTS] = {0,0,0,0,0}; + +static uint8_t mapClientId(uint32_t asyncId) { + for(uint8_t i = 0; i < MAX_WS_CLIENTS; i++) + if(clientMap[i] == asyncId) return i; + return 255; +} +static uint32_t getAsyncId(uint8_t slot) { + if(slot < MAX_WS_CLIENTS) return clientMap[slot]; + return 0; +} +static uint8_t addClient(uint32_t asyncId) { + for(uint8_t i = 0; i < MAX_WS_CLIENTS; i++) { + if(clientMap[i] == 0) { clientMap[i] = asyncId; return i; } + } + return 255; +} +static void removeClient(uint32_t asyncId) { + for(uint8_t i = 0; i < MAX_WS_CLIENTS; i++) + if(clientMap[i] == asyncId) clientMap[i] = 0; +} #define MAX_SOCK_RESPONSE 2048 static char g_response[MAX_SOCK_RESPONSE]; bool room_t::isJoined(uint8_t num) { - for(uint8_t i = 0; i < sizeof(this->clients); i++) { - if(this->clients[i] == num) return true; - } - return false; + for(uint8_t i = 0; i < sizeof(this->clients); i++) { + if(this->clients[i] == num) return true; + } + return false; } bool room_t::join(uint8_t num) { - if(this->isJoined(num)) return true; - for(uint8_t i = 0; i < sizeof(this->clients); i++) { - if(this->clients[i] == 255) { - this->clients[i] = num; - return true; - } + if(this->isJoined(num)) return true; + for(uint8_t i = 0; i < sizeof(this->clients); i++) { + if(this->clients[i] == 255) { + this->clients[i] = num; + return true; + } } - return false; + return false; } -bool room_t::leave(uint8_t num) { - if(!this->isJoined(num)) return false; - for(uint8_t i = 0; i < sizeof(this->clients); i++) { - if(this->clients[i] == num) this->clients[i] = 255; - } +bool room_t::leave(uint8_t num) { + if(!this->isJoined(num)) return false; + for(uint8_t i = 0; i < sizeof(this->clients); i++) { + if(this->clients[i] == num) this->clients[i] = 255; + } return true; } void room_t::clear() { @@ -53,49 +75,44 @@ uint8_t room_t::activeClients() { } return n; } -/********************************************************************* - * ClientSocketEvent class members - ********************************************************************/ -/* -void ClientSocketEvent::prepareMessage(const char *evt, const char *payload) { - if(strlen(payload) + 5 >= sizeof(this->msg)) Serial.printf("Socket buffer overflow %d > 2048\n", strlen(payload) + 5 + strlen(evt)); - snprintf(this->msg, sizeof(this->msg), "42[%s,%s]", evt, payload); -} -void ClientSocketEvent::prepareMessage(const char *evt, JsonDocument &doc) { - memset(this->msg, 0x00, sizeof(this->msg)); - snprintf(this->msg, sizeof(this->msg), "42[%s,", evt); - serializeJson(doc, &this->msg[strlen(this->msg)], sizeof(this->msg) - strlen(this->msg) - 2); - strcat(this->msg, "]"); -} -*/ /********************************************************************* * SocketEmitter class members ********************************************************************/ void SocketEmitter::startup() { - + } void SocketEmitter::begin() { - sockServer.begin(); - sockServer.enableHeartbeat(3000, 2000, 2); - sockServer.onEvent(this->wsEvent); + ws.onEvent(SocketEmitter::wsEvent); + wsServer.addHandler(&ws); + wsServer.begin(); Serial.println("Socket Server Started..."); - //settings.printAvailHeap(); } void SocketEmitter::loop() { + ws.cleanupClients(); this->initClients(); - sockServer.loop(); } JsonSockEvent *SocketEmitter::beginEmit(const char *evt) { - this->json.beginEvent(&sockServer, evt, g_response, sizeof(g_response)); + this->json.beginEvent(&ws, evt, g_response, sizeof(g_response)); return &this->json; } -void SocketEmitter::endEmit(uint8_t num) { this->json.endEvent(num); esp_task_wdt_reset(); sockServer.loop(); } +void SocketEmitter::endEmit(uint8_t num) { + if(num == 255) { + this->json.endEvent(0); + } else { + uint32_t asyncId = getAsyncId(num); + this->json.endEvent(asyncId); + } + esp_task_wdt_reset(); +} void SocketEmitter::endEmitRoom(uint8_t room) { if(room < SOCK_MAX_ROOMS) { room_t *r = &this->rooms[room]; for(uint8_t i = 0; i < sizeof(r->clients); i++) { - if(r->clients[i] != 255) this->json.endEvent(r->clients[i]); + if(r->clients[i] != 255) { + uint32_t asyncId = getAsyncId(r->clients[i]); + if(asyncId != 0) this->json.endEvent(asyncId); + } } } } @@ -105,18 +122,19 @@ uint8_t SocketEmitter::activeClients(uint8_t room) { } void SocketEmitter::initClients() { for(uint8_t i = 0; i < sizeof(this->newClients); i++) { - uint8_t num = this->newClients[i]; - if(num != 255) { - if(sockServer.clientIsConnected(num)) { - Serial.printf("Initializing Socket Client %u\n", num); + uint8_t slot = this->newClients[i]; + if(slot != 255) { + uint32_t asyncId = getAsyncId(slot); + if(asyncId != 0 && ws.hasClient(asyncId)) { + Serial.printf("Initializing Socket Client %u (asyncId=%lu)\n", slot, asyncId); esp_task_wdt_reset(); - settings.emitSockets(num); - if(!sockServer.clientIsConnected(num)) { this->newClients[i] = 255; continue; } - somfy.emitState(num); - if(!sockServer.clientIsConnected(num)) { this->newClients[i] = 255; continue; } - git.emitUpdateCheck(num); - if(!sockServer.clientIsConnected(num)) { this->newClients[i] = 255; continue; } - net.emitSockets(num); + settings.emitSockets(slot); + if(!ws.hasClient(asyncId)) { this->newClients[i] = 255; continue; } + somfy.emitState(slot); + if(!ws.hasClient(asyncId)) { this->newClients[i] = 255; continue; } + git.emitUpdateCheck(slot); + if(!ws.hasClient(asyncId)) { this->newClients[i] = 255; continue; } + net.emitSockets(slot); esp_task_wdt_reset(); } this->newClients[i] = 255; @@ -132,75 +150,72 @@ void SocketEmitter::delayInit(uint8_t num) { } } } -void SocketEmitter::end() { - sockServer.close(); +void SocketEmitter::end() { + ws.closeAll(); + wsServer.end(); for(uint8_t i = 0; i < SOCK_MAX_ROOMS; i++) this->rooms[i].clear(); + memset(clientMap, 0, sizeof(clientMap)); } -void SocketEmitter::disconnect() { sockServer.disconnect(); } -void SocketEmitter::wsEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) { - switch(type) { - case WStype_ERROR: - if(length > 0) - Serial.printf("Socket Error: %s\n", payload); - else - Serial.println("Socket Error: \n"); - break; - case WStype_DISCONNECTED: - if(length > 0) - Serial.printf("Socket [%u] Disconnected!\n [%s]", num, payload); - else - Serial.printf("Socket [%u] Disconnected!\n", num); - for(uint8_t i = 0; i < SOCK_MAX_ROOMS; i++) { - sockEmit.rooms[i].leave(num); - } - break; - case WStype_CONNECTED: - { - IPAddress ip = sockServer.remoteIP(num); - Serial.printf("Socket [%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); - // Send all the current shade settings to the client. - sockServer.sendTXT(num, "Connected"); - //sockServer.loop(); - sockEmit.delayInit(num); - } - break; - case WStype_TEXT: - if(strncmp((char *)payload, "join:", 5) == 0) { - // In this instance the client wants to join a room. Let's do some - // work to get the ordinal of the room that the client wants to join. - uint8_t roomNum = atoi((char *)&payload[5]); - Serial.printf("Client %u joining room %u\n", num, roomNum); - if(roomNum < SOCK_MAX_ROOMS) sockEmit.rooms[roomNum].join(num); - } - else if(strncmp((char *)payload, "leave:", 6) == 0) { - uint8_t roomNum = atoi((char *)&payload[6]); - Serial.printf("Client %u leaving room %u\n", num, roomNum); - if(roomNum < SOCK_MAX_ROOMS) sockEmit.rooms[roomNum].leave(num); - } - else { - Serial.printf("Socket [%u] text: %s\n", num, payload); - } - // send message to client - // webSocket.sendTXT(num, "message here"); - - // send data to all connected clients - // sockServer.broadcastTXT("message here"); - break; - case WStype_BIN: - Serial.printf("[%u] get binary length: %u\n", num, length); - //hexdump(payload, length); - - // send message to client - // sockServer.sendBIN(num, payload, length); - break; - case WStype_PONG: - //Serial.printf("Pong from %u\n", num); - break; - case WStype_PING: - //Serial.printf("Ping from %u\n", num); - break; - default: - break; - } +void SocketEmitter::disconnect() { + ws.closeAll(); + memset(clientMap, 0, sizeof(clientMap)); +} +void SocketEmitter::wsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) { + uint32_t asyncId = client->id(); + switch(type) { + case WS_EVT_CONNECT: + { + uint8_t slot = addClient(asyncId); + if(slot == 255) { + Serial.printf("Socket: No free client slots, closing %lu\n", asyncId); + client->close(); + return; + } + IPAddress ip = client->remoteIP(); + Serial.printf("Socket [%lu] Connected from %d.%d.%d.%d (slot %u)\n", asyncId, ip[0], ip[1], ip[2], ip[3], slot); + client->text("Connected"); + client->setCloseClientOnQueueFull(false); + sockEmit.delayInit(slot); + } + break; + case WS_EVT_DISCONNECT: + { + uint8_t slot = mapClientId(asyncId); + Serial.printf("Socket [%lu] Disconnected (slot %u)\n", asyncId, slot); + if(slot != 255) { + for(uint8_t i = 0; i < SOCK_MAX_ROOMS; i++) + sockEmit.rooms[i].leave(slot); + } + removeClient(asyncId); + } + break; + case WS_EVT_DATA: + { + AwsFrameInfo *info = (AwsFrameInfo*)arg; + if(info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) { + uint8_t slot = mapClientId(asyncId); + data[len] = 0; + if(strncmp((char*)data, "join:", 5) == 0) { + uint8_t roomNum = atoi((char*)&data[5]); + Serial.printf("Client %u joining room %u\n", slot, roomNum); + if(roomNum < SOCK_MAX_ROOMS && slot != 255) sockEmit.rooms[roomNum].join(slot); + } + else if(strncmp((char*)data, "leave:", 6) == 0) { + uint8_t roomNum = atoi((char*)&data[6]); + Serial.printf("Client %u leaving room %u\n", slot, roomNum); + if(roomNum < SOCK_MAX_ROOMS && slot != 255) sockEmit.rooms[roomNum].leave(slot); + } + else { + Serial.printf("Socket [%lu] text: %s\n", asyncId, data); + } + } + } + break; + case WS_EVT_ERROR: + Serial.printf("Socket [%lu] Error\n", asyncId); + break; + case WS_EVT_PONG: + break; + } } diff --git a/src/Sockets.h b/src/Sockets.h index d76ab93..948b730 100644 --- a/src/Sockets.h +++ b/src/Sockets.h @@ -1,4 +1,4 @@ -#include +#include #include "WResp.h" #ifndef sockets_h #define sockets_h @@ -21,7 +21,6 @@ class SocketEmitter { void delayInit(uint8_t num); public: JsonSockEvent json; - //ClientSocketEvent evt; room_t rooms[SOCK_MAX_ROOMS]; uint8_t activeClients(uint8_t room); void initClients(); @@ -33,6 +32,6 @@ class SocketEmitter { JsonSockEvent * beginEmit(const char *evt); void endEmit(uint8_t num = 255); void endEmitRoom(uint8_t num); - static void wsEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length); + static void wsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len); }; #endif diff --git a/src/WResp.cpp b/src/WResp.cpp index bff3476..258dfd9 100644 --- a/src/WResp.cpp +++ b/src/WResp.cpp @@ -1,5 +1,5 @@ #include "WResp.h" -void JsonSockEvent::beginEvent(WebSocketsServer *server, const char *evt, char *buff, size_t buffSize) { +void JsonSockEvent::beginEvent(AsyncWebSocket *server, const char *evt, char *buff, size_t buffSize) { this->server = server; this->buff = buff; this->buffSize = buffSize; @@ -15,10 +15,10 @@ void JsonSockEvent::closeEvent() { this->_nocomma = true; this->_closed = true; } -void JsonSockEvent::endEvent(uint8_t num) { +void JsonSockEvent::endEvent(uint32_t clientId) { this->closeEvent(); - if(num == 255) this->server->broadcastTXT(this->buff); - else this->server->sendTXT(num, this->buff); + if(clientId == 0) this->server->textAll(this->buff); + else this->server->text(clientId, this->buff); } void JsonSockEvent::_safecat(const char *val, bool escape) { size_t len = (escape ? this->calcEscapedLength(val) : strlen(val)) + strlen(this->buff); diff --git a/src/WResp.h b/src/WResp.h index 516ae9e..4235ff7 100644 --- a/src/WResp.h +++ b/src/WResp.h @@ -1,5 +1,4 @@ #include -#include #include #include #include "Somfy.h" @@ -78,9 +77,9 @@ class JsonSockEvent : public JsonFormatter { bool _closed = false; void _safecat(const char *val, bool escape = false) override; public: - WebSocketsServer *server = nullptr; - void beginEvent(WebSocketsServer *server, const char *evt, char *buff, size_t buffSize); - void endEvent(uint8_t clientNum = 255); + AsyncWebSocket *server = nullptr; + void beginEvent(AsyncWebSocket *server, const char *evt, char *buff, size_t buffSize); + void endEvent(uint32_t clientId = 0); void closeEvent(); }; #endif