From 4de6656bc4e9fbcfe9d35b63e7d8f9e70f3d0404 Mon Sep 17 00:00:00 2001 From: Arthur Suzuki Date: Thu, 21 Aug 2025 01:00:22 +0200 Subject: [PATCH] new usermod hooks "onUdpPacket" this new hooks will help you implement new and custom protocols in usermods. I've provided an example (see usermods/udp_name_sync). The example will help you share the main segment name across different WLED instances. The segment name can be useful to sync with some effects like GIF image or scrolling text. If you define new packet format in your usermod, make sure it will either not collide with already used version of wled udp packet : - 0 is for udp sync - 1 is for AudioReactive data - 2 is for udp_name_sync :) Also, the onUdpPacket will override "parseNotification" if it returns "true". Have fun! --- usermods/udp_name_sync/library.json | 5 ++ usermods/udp_name_sync/udp_name_sync.cpp | 79 ++++++++++++++++++++++++ wled00/fcn_declare.h | 2 + wled00/udp.cpp | 3 + wled00/um_manager.cpp | 4 ++ 5 files changed, 93 insertions(+) create mode 100644 usermods/udp_name_sync/library.json create mode 100644 usermods/udp_name_sync/udp_name_sync.cpp diff --git a/usermods/udp_name_sync/library.json b/usermods/udp_name_sync/library.json new file mode 100644 index 00000000..4c5bb448 --- /dev/null +++ b/usermods/udp_name_sync/library.json @@ -0,0 +1,5 @@ +{ + "name": "udp_name_sync", + "build": { "libArchive": false }, + "dependencies": {} +} diff --git a/usermods/udp_name_sync/udp_name_sync.cpp b/usermods/udp_name_sync/udp_name_sync.cpp new file mode 100644 index 00000000..d926e1b6 --- /dev/null +++ b/usermods/udp_name_sync/udp_name_sync.cpp @@ -0,0 +1,79 @@ +#include "wled.h" + +class UdpNameSync : public Usermod { + + private: + + bool enabled = false; + bool initDone = false; + unsigned long lastTime = 0; + char segmentName[WLED_MAX_SEGNAME_LEN] = {0}; + static const char _name[]; + static const char _enabled[]; + + public: + /** + * Enable/Disable the usermod + */ + inline void enable(bool enable) { enabled = enable; } + + /** + * Get usermod enabled/disabled state + */ + inline bool isEnabled() { return enabled; } + + void setup() override { + initDone = true; + } + + void loop() override { + if (!WLED_CONNECTED) return; + if (!udpConnected) return; + Segment& mainseg = strip.getMainSegment(); + if (!strlen(segmentName) && !mainseg.name) return; //name was never set, do nothing + + IPAddress broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP()); + byte udpOut[WLED_MAX_SEGNAME_LEN + 2]; + udpOut[0] = 2; // 0: wled notifier protocol, 1: warls protocol, 2 is free + + if (strlen(segmentName) && !mainseg.name) { //name is back to null + notifierUdp.beginPacket(broadcastIp, udpPort); + strcpy(segmentName,""); + DEBUG_PRINTLN(F("UdpNameSync: sending Null name")); + notifierUdp.write( udpOut , 2); + notifierUdp.endPacket(); + return; + } + + if (0 == strcmp(mainseg.name, segmentName)) return; //same name, do nothing + + notifierUdp.beginPacket(broadcastIp, udpPort); + DEBUG_PRINT(F("UdpNameSync: saving segment name ")); + DEBUG_PRINTLN(mainseg.name); + byte length = strlen(mainseg.name); + strlcpy(segmentName, mainseg.name, length+1); + strlcpy((char *)&udpOut[1], segmentName, length+1); + notifierUdp.write(udpOut, length + 2); + notifierUdp.endPacket(); + DEBUG_PRINT(F("UdpNameSync: Sent segment name : ")); + DEBUG_PRINTLN(segmentName); + } + + bool onUdpPacket(uint8_t * payload, uint8_t len) override { + DEBUG_PRINT(F("UdpNameSync: Received packet")); + if (payload[0] != 2) return false; + //else + Segment& mainseg = strip.getMainSegment(); + mainseg.setName((char *)&payload[1]); + DEBUG_PRINT(F("UdpNameSync: set segment name")); + return true; + } +}; + + +// add more strings here to reduce flash memory usage +const char UdpNameSync::_name[] PROGMEM = "UdpNameSync"; +const char UdpNameSync::_enabled[] PROGMEM = "enabled"; + +static UdpNameSync udp_name_sync; +REGISTER_USERMOD(udp_name_sync); diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 0cd28a31..326ae122 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -442,6 +442,7 @@ class Usermod { virtual void onMqttConnect(bool sessionPresent) {} // fired when MQTT connection is established (so usermod can subscribe) virtual bool onMqttMessage(char* topic, char* payload) { return false; } // fired upon MQTT message received (wled topic) virtual bool onEspNowMessage(uint8_t* sender, uint8_t* payload, uint8_t len) { return false; } // fired upon ESP-NOW message received + virtual bool onUdpPacket(uint8_t* payload, uint8_t len) { return false; } //fired upon UDP packet received virtual void onUpdateBegin(bool) {} // fired prior to and after unsuccessful firmware update virtual void onStateChange(uint8_t mode) {} // fired upon WLED state change virtual uint16_t getId() {return USERMOD_ID_UNSPECIFIED;} @@ -481,6 +482,7 @@ namespace UsermodManager { #ifndef WLED_DISABLE_ESPNOW bool onEspNowMessage(uint8_t* sender, uint8_t* payload, uint8_t len); #endif + bool onUdpPacket(uint8_t* payload, uint8_t len); void onUpdateBegin(bool); void onStateChange(uint8_t); Usermod* lookup(uint16_t mod_id); diff --git a/wled00/udp.cpp b/wled00/udp.cpp index bdb60c36..7b9e53bc 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -557,6 +557,9 @@ void handleNotifications() return; } + // usermods hook can override processing + if (UsermodManager::onUdpPacket(udpIn, packetSize)) return; + //wled notifier, ignore if realtime packets active if (udpIn[0] == 0 && !realtimeMode && receiveGroups) { diff --git a/wled00/um_manager.cpp b/wled00/um_manager.cpp index 1a7cc226..483fdc68 100644 --- a/wled00/um_manager.cpp +++ b/wled00/um_manager.cpp @@ -68,6 +68,10 @@ bool UsermodManager::onEspNowMessage(uint8_t* sender, uint8_t* payload, uint8_t return false; } #endif +bool UsermodManager::onUdpPacket(uint8_t* payload, uint8_t len) { + for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) if ((*mod)->onUdpPacket(payload, len)) return true; + return false; +} void UsermodManager::onUpdateBegin(bool init) { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->onUpdateBegin(init); } // notify usermods that update is to begin void UsermodManager::onStateChange(uint8_t mode) { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->onStateChange(mode); } // notify usermods that WLED state changed