From 0f003335b3a61a505ca979b3daba602f4cfafd5a Mon Sep 17 00:00:00 2001 From: 0x1d Date: Sun, 26 Oct 2025 12:43:22 +0100 Subject: [PATCH] feat: new cluster protocol, event naming --- examples/neopattern/NeoPatternService.cpp | 4 ++-- include/spore/internal/Globals.h | 9 ++++----- platformio.ini | 19 ++++++++++++++++--- src/spore/core/ClusterManager.cpp | 18 +++++++++--------- 4 files changed, 31 insertions(+), 19 deletions(-) diff --git a/examples/neopattern/NeoPatternService.cpp b/examples/neopattern/NeoPatternService.cpp index 3872e83..0fa23d7 100644 --- a/examples/neopattern/NeoPatternService.cpp +++ b/examples/neopattern/NeoPatternService.cpp @@ -195,13 +195,13 @@ void NeoPatternService::registerEventHandlers() { JsonDocument doc; DeserializationError err = deserializeJson(doc, *jsonStr); if (err) { - LOG_WARN("NeoPattern", String("Failed to parse CLUSTER_EVENT data: ") + err.c_str()); + LOG_WARN("NeoPattern", String("Failed to parse cluster/event data: ") + err.c_str()); return; } JsonObject obj = doc.as(); bool applied = applyControlParams(obj); if (applied) { - LOG_INFO("NeoPattern", "Applied control from CLUSTER_EVENT"); + LOG_INFO("NeoPattern", "Applied control from cluster/event"); } }); diff --git a/include/spore/internal/Globals.h b/include/spore/internal/Globals.h index f07ccf5..c62e34a 100644 --- a/include/spore/internal/Globals.h +++ b/include/spore/internal/Globals.h @@ -5,11 +5,10 @@ // Cluster protocol and API constants namespace ClusterProtocol { - // Simplified heartbeat-only protocol - constexpr const char* HEARTBEAT_MSG = "CLUSTER_HEARTBEAT"; - constexpr const char* NODE_UPDATE_MSG = "NODE_UPDATE"; - constexpr const char* CLUSTER_EVENT_MSG = "CLUSTER_EVENT"; - constexpr const char* RAW_MSG = "RAW"; + constexpr const char* HEARTBEAT_MSG = "cluster/heartbeat"; + constexpr const char* NODE_UPDATE_MSG = "node/update"; + constexpr const char* CLUSTER_EVENT_MSG = "cluster/event"; + constexpr const char* RAW_MSG = "raw"; constexpr uint16_t UDP_PORT = 4210; // Increased buffer to accommodate larger RAW pixel streams and node info JSON over UDP constexpr size_t UDP_BUF_SIZE = 2048; diff --git a/platformio.ini b/platformio.ini index fba0ac4..eda03bc 100644 --- a/platformio.ini +++ b/platformio.ini @@ -18,6 +18,15 @@ monitor_speed = 115200 lib_deps = esp32async/ESPAsyncWebServer@^3.8.0 bblanchon/ArduinoJson@^7.4.2 +build_flags = + -Os ; Optimize for size + -ffunction-sections ; Place each function in its own section + -fdata-sections ; Place data in separate sections + -Wl,--gc-sections ; Remove unused sections at link time + -DNDEBUG ; Disable debug assertions + -DVTABLES_IN_FLASH ; Move virtual tables to flash + -fno-exceptions ; Disable C++ exceptions + -fno-rtti ; Disable runtime type information [env:base] platform = platformio/espressif8266@^4.2.1 @@ -31,6 +40,7 @@ board_build.filesystem = littlefs ; note: somehow partition table is not working, so we need to use the ldscript board_build.ldscript = eagle.flash.1m64.ld ; 64KB -> FS Size lib_deps = ${common.lib_deps} +build_flags = ${common.build_flags} build_src_filter = + + @@ -51,6 +61,7 @@ board_build.flash_mode = dio ; D1 Mini uses DIO on 4 Mbit flash board_build.flash_size = 4M board_build.ldscript = eagle.flash.4m1m.ld lib_deps = ${common.lib_deps} +build_flags = ${common.build_flags} build_src_filter = + + @@ -71,6 +82,7 @@ board_build.flash_mode = dout board_build.ldscript = eagle.flash.1m64.ld lib_deps = ${common.lib_deps} ;data_dir = examples/relay/data +build_flags = ${common.build_flags} build_src_filter = + + @@ -91,7 +103,7 @@ board_build.flash_mode = dout board_build.ldscript = eagle.flash.1m64.ld lib_deps = ${common.lib_deps} adafruit/Adafruit NeoPixel@^1.15.1 -build_flags = -DLED_STRIP_PIN=2 +build_flags = -DLED_STRIP_PIN=2 ;${common.build_flags} build_src_filter = + + @@ -112,7 +124,7 @@ board_build.flash_mode = dout board_build.ldscript = eagle.flash.1m64.ld lib_deps = ${common.lib_deps} adafruit/Adafruit NeoPixel@^1.15.1 -build_flags = +build_flags = ${common.build_flags} build_src_filter = + + @@ -133,7 +145,7 @@ board_build.flash_mode = dout board_build.ldscript = eagle.flash.4m1m.ld lib_deps = ${common.lib_deps} adafruit/Adafruit NeoPixel@^1.15.1 -build_flags = -DPIXEL_PIN=TX -DPIXEL_COUNT=256 -DMATRIX_WIDTH=16 +build_flags = -DPIXEL_PIN=TX -DPIXEL_COUNT=256 -DMATRIX_WIDTH=16 ${common.build_flags} build_src_filter = + + @@ -156,6 +168,7 @@ board_build.ldscript = eagle.flash.4m1m.ld lib_deps = ${common.lib_deps} adafruit/Adafruit NeoPixel@^1.15.1 dfrobot/DFRobotDFPlayerMini@^1.0.6 +build_flags = ${common.build_flags} build_src_filter = + + diff --git a/src/spore/core/ClusterManager.cpp b/src/spore/core/ClusterManager.cpp index 9dc3121..f8561dd 100644 --- a/src/spore/core/ClusterManager.cpp +++ b/src/spore/core/ClusterManager.cpp @@ -9,7 +9,7 @@ ClusterManager::ClusterManager(NodeContext& ctx, TaskManager& taskMgr) : ctx(ctx NodeInfo* node = static_cast(data); this->addOrUpdateNode(node->hostname, node->ip); }); - // Centralized broadcast handler: services fire 'cluster/broadcast' with CLUSTER_EVENT JSON payload + // Centralized broadcast handler: services fire 'cluster/broadcast' with cluster/event JSON payload ctx.on("cluster/broadcast", [this](void* data) { String* jsonStr = static_cast(data); if (!jsonStr) { @@ -20,7 +20,7 @@ ClusterManager::ClusterManager(NodeContext& ctx, TaskManager& taskMgr) : ctx(ctx IPAddress ip = WiFi.localIP(); IPAddress mask = WiFi.subnetMask(); IPAddress bcast(ip[0] | ~mask[0], ip[1] | ~mask[1], ip[2] | ~mask[2], ip[3] | ~mask[3]); - LOG_DEBUG("Cluster", String("Broadcasting CLUSTER_EVENT to ") + bcast.toString() + " len=" + String(jsonStr->length())); + LOG_DEBUG("Cluster", String("Broadcasting cluster/event to ") + bcast.toString() + " len=" + String(jsonStr->length())); this->ctx.udp->beginPacket(bcast, this->ctx.config.udp_port); String msg = String(ClusterProtocol::CLUSTER_EVENT_MSG) + ":" + *jsonStr; this->ctx.udp->write(msg.c_str()); @@ -120,7 +120,7 @@ bool ClusterManager::isRawMsg(const char* msg) { // Discovery functionality removed - using heartbeat-only approach void ClusterManager::onHeartbeat(const char* msg) { - // Extract hostname from heartbeat message: "CLUSTER_HEARTBEAT:hostname" + // Extract hostname from heartbeat message: "cluster/heartbeat:hostname" const char* colon = strchr(msg, ':'); if (!colon) { LOG_WARN("Cluster", "Invalid heartbeat message format"); @@ -138,7 +138,7 @@ void ClusterManager::onHeartbeat(const char* msg) { } void ClusterManager::onNodeUpdate(const char* msg) { - // Message format: "NODE_UPDATE:hostname:{json}" + // Message format: "node/update:hostname:{json}" const char* firstColon = strchr(msg, ':'); if (!firstColon) { LOG_WARN("Cluster", "Invalid NODE_UPDATE message format"); @@ -286,17 +286,17 @@ void ClusterManager::sendNodeInfo(const String& targetHostname, const IPAddress& } void ClusterManager::onClusterEvent(const char* msg) { - // Message format: CLUSTER_EVENT:{"event":"...","data":""} + // Message format: cluster/event:{"event":"...","data":""} const char* jsonStart = msg + strlen(ClusterProtocol::CLUSTER_EVENT_MSG) + 1; // skip prefix and ':' if (*jsonStart == '\0') { - LOG_DEBUG("Cluster", "CLUSTER_EVENT received with empty payload"); + LOG_DEBUG("Cluster", "cluster/event received with empty payload"); return; } - LOG_DEBUG("Cluster", String("CLUSTER_EVENT raw from ") + ctx.udp->remoteIP().toString() + " len=" + String(strlen(jsonStart))); + LOG_DEBUG("Cluster", String("cluster/event raw from ") + ctx.udp->remoteIP().toString() + " len=" + String(strlen(jsonStart))); JsonDocument doc; DeserializationError err = deserializeJson(doc, jsonStart); if (err) { - LOG_ERROR("Cluster", String("Failed to parse CLUSTER_EVENT JSON from ") + ctx.udp->remoteIP().toString()); + LOG_ERROR("Cluster", String("Failed to parse cluster/event JSON from ") + ctx.udp->remoteIP().toString()); return; } // Robust extraction of event and data @@ -320,7 +320,7 @@ void ClusterManager::onClusterEvent(const char* msg) { if (eventStr.length() == 0 || data.length() == 0) { String dbg; serializeJson(doc, dbg); - LOG_WARN("Cluster", String("CLUSTER_EVENT missing 'event' or 'data' | payload=") + dbg); + LOG_WARN("Cluster", String("cluster/event missing 'event' or 'data' | payload=") + dbg); return; }