Skip to content

Conversation

@Liliputech
Copy link
Contributor

@Liliputech Liliputech commented Aug 20, 2025

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!

Summary by CodeRabbit

  • New Features

    • UDP Name Sync: devices broadcast and receive main segment name changes for network-wide name consistency.
    • Usermod UDP hook: usermods can observe and handle incoming UDP packets for custom integrations.
  • Behavior Change

    • Real-time and TPM2.NET UDP processing now respect the "direct receive" setting; the UDP hook is invoked after core UDP handling.
  • Chores

    • Added library manifest for the UDP Name Sync component.

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!
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 20, 2025

Walkthrough

Adds a UDP name-sync usermod and library manifest; introduces a Usermod UDP packet callback plus UsermodManager dispatcher using size_t length; wires a usermod hook into UDP processing and gates TPM2.NET and realtime UDP handling behind a receiveDirect check. (49 words)

Changes

Cohort / File(s) Summary of edits
New usermod: UDP name sync
usermods/udp_name_sync/udp_name_sync.cpp
Adds UdpNameSync : public Usermod with enable(bool), isEnabled(), setup(), loop(), and onUdpPacket(uint8_t* payload, size_t len); broadcasts and receives main-segment name updates via UDP (packet type 200); caches last sent name and registers the usermod.
Usermod library manifest
usermods/udp_name_sync/library.json
Adds library manifest for udp_name_sync with "build": { "libArchive": false } and no dependencies.
Usermod API declarations
wled00/fcn_declare.h
Removes Usermod::onEspNowMessage member; adds virtual Usermod::onUdpPacket(uint8_t* payload, size_t len) defaulting to false; adds UsermodManager::onUdpPacket(uint8_t* payload, size_t len) declaration; UDP length parameter uses size_t.
Usermod manager dispatch
wled00/um_manager.cpp
Implements UsermodManager::onUdpPacket(uint8_t* payload, size_t len) to iterate registered usermods and invoke each onUdpPacket, returning true if any handled the packet.
UDP processing integration & gating
wled00/udp.cpp
Calls UsermodManager::onUdpPacket(udpIn, packetSize) after standard handling; restructures UDP handling so TPM2.NET and realtime UDP processing run only when receiveDirect is true and consolidates packet assembly/processing logic (per-packet framing, counters, and show/trigger flow).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • willmmiles

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 62fad4d and c8757d4.

📒 Files selected for processing (1)
  • usermods/udp_name_sync/udp_name_sync.cpp (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • usermods/udp_name_sync/udp_name_sync.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full)
  • GitHub Check: wled_build / Build Environments (esp32s3_4M_qspi)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
  • GitHub Check: wled_build / Build Environments (esp32_eth)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (esp32_wrover)
  • GitHub Check: wled_build / Build Environments (esp32c3dev)
  • GitHub Check: wled_build / Build Environments (esp32dev)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_compat)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_8MB_opi)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_160)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_160)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_compat)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_compat)
  • GitHub Check: wled_build / Build Environments (esp8266_2m)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_160)
  • GitHub Check: wled_build / Build Environments (nodemcuv2)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@Liliputech
Copy link
Contributor Author

this is a rework of a previous PR : #4468
I tried to make it as flexible as possible so it can benefit to other people who want to implement whatever they have in mind with UDP packet.
Also this way backward compatibility is garanted since I don't touch the original UDP sync protocol.
I've implemented the things that I wanted to implement but it is not enabled by default.
the new usermod "udp_name_sync" can serve as an example on how to implement the new hook.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (4)
usermods/udp_name_sync/library.json (1)

1-5: Add basic manifest metadata (version/description) to aid PIO caching and discoverability

The manifest works as-is, but adding a version and short description helps PlatformIO and future maintenance.

Apply this diff:

 {
   "name": "udp_name_sync",
+  "version": "0.1.0",
+  "description": "Usermod to synchronize the main segment name across WLED instances over UDP.",
   "build": { "libArchive": false },
   "dependencies": {}
 }
wled00/fcn_declare.h (1)

445-446: Optional: pass sender metadata to usermods

Many protocols need sender IP/port to filter or authenticate. Consider expanding the signature later to include remote IP/port while keeping the current overload for backwards compatibility.

wled00/udp.cpp (1)

560-562: Be explicit about override scope of the hook

Placing the hook here means a usermod that returns true will prevent subsequent handling of WLED notifier, TPM2.NET, UDP realtime (1..5), and UDP API parsing. If the intent is to allow overriding only WLED notifier parsing, consider moving the hook call to just before parseNotifyPacket(...) and after the TPM2/Realtime branches. Otherwise, document the broader override effect so usermods return true only for their own packets.

Proposed placement (outside this hunk for reference):

  • After the TPM2.NET and realtime branches, but before the WLED notifier check (udpIn[0] == 0 ...).
usermods/udp_name_sync/udp_name_sync.cpp (1)

29-36: Respect the enabled flag; remove unused fields

enabled is never used; initDone/lastTime are unused. Either gate behavior with enabled and implement rate limiting using lastTime, or remove the fields.

Minimal gating:

-    void loop() override {
-      if (!WLED_CONNECTED) return;
-      if (!udpConnected) return;
+    void loop() override {
+      if (!enabled) return;
+      if (!WLED_CONNECTED) return;
+      if (!udpConnected) return;

Optional: use lastTime to throttle broadcasts (e.g., min 100ms between sends).

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3b5c6ca and 4de6656.

📒 Files selected for processing (5)
  • usermods/udp_name_sync/library.json (1 hunks)
  • usermods/udp_name_sync/udp_name_sync.cpp (1 hunks)
  • wled00/fcn_declare.h (2 hunks)
  • wled00/udp.cpp (1 hunks)
  • wled00/um_manager.cpp (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
wled00/um_manager.cpp (1)
usermods/udp_name_sync/udp_name_sync.cpp (2)
  • payload (62-70)
  • payload (62-62)
wled00/udp.cpp (1)
wled00/um_manager.cpp (2)
  • onUdpPacket (71-74)
  • onUdpPacket (71-71)
wled00/fcn_declare.h (2)
wled00/um_manager.cpp (2)
  • onUdpPacket (71-74)
  • onUdpPacket (71-71)
usermods/udp_name_sync/udp_name_sync.cpp (2)
  • payload (62-70)
  • payload (62-62)

Copy link
Member

@willmmiles willmmiles left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm generally on board with the approach - certainly it'll let us more gracefully handle any other protocols with port collisions.

netmindz and others added 3 commits August 21, 2025 08:53
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
… empty

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
usermods/udp_name_sync/udp_name_sync.cpp (1)

69-77: Validate length and avoid trusting null termination from the wire

Accessing payload[0] and calling setName((char*)&payload[1]) without validating len or bounding/copying can read past the UDP buffer and crash on malformed packets. Copy within len-1 into a local buffer, terminate, then setName().

-    bool onUdpPacket(uint8_t * payload, size_t len) override {
+    bool onUdpPacket(uint8_t * payload, size_t len) override {
       DEBUG_PRINT(F("UdpNameSync: Received packet"));
-      if (receiveDirect) return false;
-      if (payload[0] != 200) return false;
-      //else
-      Segment& mainseg = strip.getMainSegment();
-      mainseg.setName((char *)&payload[1]);
+      if (receiveDirect) return false;
+      if (len < 2) return false;            // header + at least NUL
+      if (payload[0] != 200) return false;
+      Segment& mainseg = strip.getMainSegment();
+      char tmp[WLED_MAX_SEGNAME_LEN] = {0};
+      size_t copyLen = len - 1;
+      if (copyLen > sizeof(tmp) - 1) copyLen = sizeof(tmp) - 1;
+      memcpy(tmp, &payload[1], copyLen);
+      tmp[copyLen] = '\0';
+      mainseg.setName(tmp);
       DEBUG_PRINT(F("UdpNameSync: set segment name"));
       return true;
     }
🧹 Nitpick comments (6)
wled00/fcn_declare.h (1)

485-485: Namespace declaration aligned; consider documenting short-circuit semantics

The UsermodManager::onUdpPacket(size_t len) declaration matches the class API. Consider clarifying in a comment that returning true from any usermod bypasses core UDP parsing (parseNotification), to set expectations for implementers.

-  bool onUdpPacket(uint8_t* payload, size_t len);
+  bool onUdpPacket(uint8_t* payload, size_t len); // if any usermod returns true, core UDP handling is skipped
usermods/udp_name_sync/udp_name_sync.cpp (5)

35-35: Broadcast IP: base it on localIP, not gateway

Using gatewayIP works only if the gateway shares the same subnet bits; localIP makes the intent clear and avoids surprises on atypical setups.

-      IPAddress broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP());
+      IPAddress broadcastIp = uint32_t(Network.localIP()) | ~uint32_t(Network.subnetMask());

37-37: Stale comment: 2 is not free

You correctly use 200. Update the comment to reflect that 0..5 are used by core protocols; 200+ is for custom/usermod traffic.

-      udpOut[0] = 200; // 0: wled notifier protocol, 1: warls protocol, 2 is free
+      udpOut[0] = 200; // custom usermod packet type (avoid 0..5 used by core protocols)

60-61: Drop unused variable

length is computed but not used.

-      size_t length = strlen(mainseg.name);
-      strlcpy(segmentName, mainseg.name, sizeof(segmentName));
+      strlcpy(segmentName, mainseg.name, sizeof(segmentName));

18-24: Small API polish: const-correctness and naming

Mark isEnabled() const and avoid parameter shadowing in enable().

-    inline void enable(bool enable) { enabled = enable; }
+    inline void enable(bool value) { enabled = value; }
@@
-    inline bool isEnabled() { return enabled; }
+    inline bool isEnabled() const { return enabled; }

8-10: Unused members

initDone and lastTime are currently unused. Either wire them into logic (e.g., rate limit broadcasts) or remove to reduce ROM/RAM.

-    bool initDone = false;
-    unsigned long lastTime = 0;

If you keep initDone, consider documenting its intended use or using it in loop().

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f3e3f58 and 550b4d9.

📒 Files selected for processing (2)
  • usermods/udp_name_sync/udp_name_sync.cpp (1 hunks)
  • wled00/fcn_declare.h (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-21T00:00:17.164Z
Learnt from: willmmiles
PR: wled/WLED#4859
File: usermods/udp_name_sync/udp_name_sync.cpp:62-70
Timestamp: 2025-08-21T00:00:17.164Z
Learning: In WLED codebase, UDP packet processing code must never rely on null termination being present on the wire and must always validate packet size first. The codebase already has some vulnerabilities where malformed data can crash the software, so avoiding introduction of new ones is a priority.

Applied to files:

  • wled00/fcn_declare.h
  • usermods/udp_name_sync/udp_name_sync.cpp
📚 Learning: 2025-02-19T12:43:34.199Z
Learnt from: blazoncek
PR: wled/WLED#4482
File: wled00/udp.cpp:147-149
Timestamp: 2025-02-19T12:43:34.199Z
Learning: ESP8266 and ESP32 platforms have different maximum segment name lengths in WLED, which can cause truncation when syncing segment names between devices. This platform difference affects the user experience when using the segment name sync feature.

Applied to files:

  • wled00/fcn_declare.h
🧬 Code graph analysis (1)
wled00/fcn_declare.h (2)
wled00/um_manager.cpp (2)
  • onUdpPacket (71-74)
  • onUdpPacket (71-71)
usermods/udp_name_sync/udp_name_sync.cpp (2)
  • payload (69-78)
  • payload (69-69)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (16)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_compat)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_compat)
  • GitHub Check: wled_build / Build Environments (esp32_wrover)
  • GitHub Check: wled_build / Build Environments (esp32dev)
  • GitHub Check: wled_build / Build Environments (esp32c3dev)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_8MB_opi)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_compat)
  • GitHub Check: wled_build / Build Environments (esp32_eth)
  • GitHub Check: wled_build / Build Environments (esp32s3_4M_qspi)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_160)
  • GitHub Check: wled_build / Build Environments (esp8266_2m)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_160)
  • GitHub Check: wled_build / Build Environments (nodemcuv2)
🔇 Additional comments (4)
wled00/fcn_declare.h (2)

445-446: Good API: use size_t for UDP payload length

Switching to size_t for onUdpPacket avoids silent truncation and enables safe handling of full UDP frames. No issues here.


444-446: All onUdpPacket overrides use the updated size_t signature
I searched through usermods and wled00 for any onUdpPacket overrides using a uint8_t length parameter and found none—every override (including usermods/udp_name_sync/udp_name_sync.cpp:69) now correctly uses size_t for the payload length. No further changes required.

usermods/udp_name_sync/udp_name_sync.cpp (2)

33-47: Edge case: initial state vs. cleared name logic is fine, but ensure cache update order

The “name cleared” branch resets segmentName[0] before sending. That keeps the cache consistent—good. After the general send, you already copy the current name into segmentName—also good.


61-64: Good: bounded copies and length-aware send

Using strlcpy into a fixed buffer and computing the write length with strnlen prevents overrun and preserves cross-platform max name length differences.

@Liliputech Liliputech requested a review from willmmiles August 24, 2025 14:19
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
wled00/udp.cpp (1)

661-663: Hook call location is “observer of unconsumed packets”, not an override; also pass actual read length

  • The callback is placed at the very end of handleNotifications(), after all core handlers return early. This means it cannot override parseNotifyPacket or realtime handling even if it returns true. That contradicts the PR summary’s “onUdpPacket will override parseNotification when it returns true.” If the intended behavior is “observe only packets not consumed by core,” please update the description/docs accordingly. Otherwise, you’ll need to invoke the hook before core handlers and honor its boolean return to short-circuit them.
  • Minor: You currently pass packetSize; prefer the number of bytes actually read (len).
-  UsermodManager::onUdpPacket(udpIn, packetSize);
+  // pass actual bytes read
+  UsermodManager::onUdpPacket(udpIn, len);

Follow-up options:

  • If “observer” semantics are intended: consider making the hook return void to avoid implying short-circuit behavior, and document that the payload pointer is valid only during the call and must not be retained.
  • If “override” semantics are required: move a guarded call (e.g., only for udpIn[0]==0 notifier packets with custom callMode >=200) before parseNotifyPacket and return on true, while keeping this trailing call for all other unhandled packets to balance safety and flexibility. I can draft that variant if you want it in this PR.

Note: The prior bot warning about size_t→uint8_t truncation is resolved; um_manager.cpp already takes size_t len.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 550b4d9 and 4b5c3a3.

📒 Files selected for processing (2)
  • usermods/udp_name_sync/udp_name_sync.cpp (1 hunks)
  • wled00/udp.cpp (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • usermods/udp_name_sync/udp_name_sync.cpp
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-21T00:00:17.164Z
Learnt from: willmmiles
PR: wled/WLED#4859
File: usermods/udp_name_sync/udp_name_sync.cpp:62-70
Timestamp: 2025-08-21T00:00:17.164Z
Learning: In WLED codebase, UDP packet processing code must never rely on null termination being present on the wire and must always validate packet size first. The codebase already has some vulnerabilities where malformed data can crash the software, so avoiding introduction of new ones is a priority.

Applied to files:

  • wled00/udp.cpp
🧬 Code graph analysis (1)
wled00/udp.cpp (1)
wled00/um_manager.cpp (2)
  • onUdpPacket (71-74)
  • onUdpPacket (71-71)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (17)
  • GitHub Check: wled_build / Build Environments (esp32_wrover)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_8MB_opi)
  • GitHub Check: wled_build / Build Environments (esp32s3_4M_qspi)
  • GitHub Check: wled_build / Build Environments (esp32c3dev)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
  • GitHub Check: wled_build / Build Environments (esp32_eth)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_compat)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_compat)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_160)
  • GitHub Check: wled_build / Build Environments (esp32dev)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_compat)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full)
  • GitHub Check: wled_build / Build Environments (esp8266_2m)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_160)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_160)

@Liliputech
Copy link
Contributor Author

have applied all suggestions and fixed comments, looks ready for a merge :)

@netmindz
Copy link
Member

The majority of the nitpick comments I think are valid

#4859 (review)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
usermods/udp_name_sync/udp_name_sync.cpp (1)

59-66: Fix unsafe UDP parsing: validate length and don’t trust null-termination

Reads past buffer when len==0 and assumes payload+1 is NUL-terminated. Copy-bounds and terminate before setName(). Also future-proof by using a shared packet-type constant.

Apply:

@@
-    bool onUdpPacket(uint8_t * payload, size_t len) override {
+    bool onUdpPacket(uint8_t * payload, size_t len) override {
       DEBUG_PRINT(F("UdpNameSync: Received packet"));
-      if (receiveDirect) return false;
-      if (payload[0] != 200) return false;
-      //else
-      Segment& mainseg = strip.getMainSegment();
-      mainseg.setName((char *)&payload[1]);
+      if (!enabled) return false;
+      if (receiveDirect) return false;
+      if (len < 2) return false;                 // need type + at least 1 byte for name (can be 0)
+      if (payload[0] != kPacketType) return false;
+      Segment& mainseg = strip.getMainSegment();
+      char tmp[WLED_MAX_SEGNAME_LEN] = {0};
+      size_t copyLen = len - 1;
+      if (copyLen > sizeof(tmp) - 1) copyLen = sizeof(tmp) - 1;
+      memcpy(tmp, &payload[1], copyLen);
+      tmp[copyLen] = '\0';
+      mainseg.setName(tmp);
       DEBUG_PRINT(F("UdpNameSync: set segment name"));
       return true;
     }
🧹 Nitpick comments (2)
usermods/udp_name_sync/udp_name_sync.cpp (2)

7-11: Replace magic number 200 with a single constant

Improves readability and ensures sender/receiver stay in sync.

Apply:

@@ class UdpNameSync : public Usermod {
   private:
+    static constexpr uint8_t kPacketType = 200; // custom usermod packet type
@@
-      udpOut[0] = 200; // custom usermod packet type (avoid 0..5 used by core protocols)  
+      udpOut[0] = kPacketType; // custom usermod packet type (avoid 0..5 used by core protocols)
@@
-      if (payload[0] != 200) return false;
+      if (payload[0] != kPacketType) return false;

Also applies to: 34-35, 62-63


45-51: Avoid printing potential null pointer; reuse curName

Use the already null-safe curName for logging.

Apply:

@@
-      notifierUdp.beginPacket(broadcastIp, udpPort);
-      DEBUG_PRINT(F("UdpNameSync: saving segment name "));
-      DEBUG_PRINTLN(mainseg.name);
+      notifierUdp.beginPacket(broadcastIp, udpPort);
+      DEBUG_PRINT(F("UdpNameSync: saving segment name "));
+      DEBUG_PRINTLN(curName);
       strlcpy(segmentName, mainseg.name, sizeof(segmentName));
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f8ce598 and a60be25.

📒 Files selected for processing (1)
  • usermods/udp_name_sync/udp_name_sync.cpp (1 hunks)
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-08-21T00:00:17.196Z
Learnt from: willmmiles
PR: wled/WLED#4859
File: usermods/udp_name_sync/udp_name_sync.cpp:62-70
Timestamp: 2025-08-21T00:00:17.196Z
Learning: In WLED codebase, UDP packet processing code must never rely on null termination being present on the wire and must always validate packet size first. The codebase already has some vulnerabilities where malformed data can crash the software, so avoiding introduction of new ones is a priority.

Applied to files:

  • usermods/udp_name_sync/udp_name_sync.cpp
📚 Learning: 2025-02-19T12:41:22.676Z
Learnt from: blazoncek
PR: wled/WLED#4482
File: wled00/udp.cpp:343-347
Timestamp: 2025-02-19T12:41:22.676Z
Learning: The setName() method in WLED's Segment class (FX_fcn.cpp) handles all necessary validation internally, including:
1. Null pointer checks
2. Length validation against WLED_MAX_SEGNAME_LEN
3. Memory allocation/reallocation
No additional validation or name clearing is needed before calling setName().

Applied to files:

  • usermods/udp_name_sync/udp_name_sync.cpp
📚 Learning: 2025-04-30T05:41:03.633Z
Learnt from: blazoncek
PR: wled/WLED#4667
File: usermods/user_fx/user_fx.cpp:27-30
Timestamp: 2025-04-30T05:41:03.633Z
Learning: In WLED, the Segment::allocateData() method already includes optimization to check if data is allocated and sufficiently sized, handling buffer reuse to reduce memory fragmentation. Adding an external check like `if (SEGENV.data == nullptr && !SEGENV.allocateData(dataSize))` is unnecessary and could be problematic, as it bypasses proper size verification.

Applied to files:

  • usermods/udp_name_sync/udp_name_sync.cpp
📚 Learning: 2025-04-24T09:31:06.879Z
Learnt from: blazoncek
PR: wled/WLED#4658
File: wled00/led.cpp:90-91
Timestamp: 2025-04-24T09:31:06.879Z
Learning: In the WLED codebase, the `getSegment()` function guards against out-of-bounds segment IDs by falling back to the main segment ID, and `getFirstSelectedSegId()` falls back to `getMainSegmentId()` if no segments are selected, ensuring no crashes when used through the `setValuesFromFirstSelectedSeg()` macro.

Applied to files:

  • usermods/udp_name_sync/udp_name_sync.cpp
📚 Learning: 2025-04-24T09:31:06.879Z
Learnt from: blazoncek
PR: wled/WLED#4658
File: wled00/led.cpp:90-91
Timestamp: 2025-04-24T09:31:06.879Z
Learning: In the WLED codebase, the `getSegment()` function guards against out-of-bounds segment IDs, and `getFirstSelectedSegId()` falls back to `getMainSegmentId()` if no segments are selected, ensuring no crashes when used through the `setValuesFromFirstSelectedSeg()` macro.

Applied to files:

  • usermods/udp_name_sync/udp_name_sync.cpp
📚 Learning: 2025-02-19T12:41:22.676Z
Learnt from: blazoncek
PR: wled/WLED#4482
File: wled00/udp.cpp:343-347
Timestamp: 2025-02-19T12:41:22.676Z
Learning: The setName() method in WLED handles segment name validation internally. No additional length checks or name clearing is needed before calling setName().

Applied to files:

  • usermods/udp_name_sync/udp_name_sync.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: wled_build / Build Environments (esp32dev)
  • GitHub Check: wled_build / Build Environments (esp32_eth)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (esp32s3_4M_qspi)
  • GitHub Check: wled_build / Build Environments (esp32c3dev)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
  • GitHub Check: wled_build / Build Environments (esp32_wrover)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_160)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_compat)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_8MB_opi)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_160)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_160)
  • GitHub Check: wled_build / Build Environments (nodemcuv2)
🔇 Additional comments (1)
usermods/udp_name_sync/udp_name_sync.cpp (1)

32-57: Sender path looks solid (size-bounded copies; explicit empty-name case)

Use of strlcpy/strnlen with correct buffer sizes and explicit udpOut[1]=0 for empty name is good. Broadcast IP computation is standard for IPv4.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
usermods/udp_name_sync/udp_name_sync.cpp (5)

33-33: Micro: avoid strlen() in a boolean emptiness check

Checking the first char is cheaper and clearer.

-      if (!strlen(segmentName) && !mainseg.name) return; //name was never set, do nothing
+      if (segmentName[0] == '\0' && !mainseg.name) return; // name was never set, do nothing

51-58: Trim debug noise and compute length once

Reduce duplicate prints and avoid two passes over the payload.

-      notifierUdp.beginPacket(broadcastIp, udpPort);
-      DEBUG_PRINT(F("UdpNameSync: saving segment name "));
-      DEBUG_PRINTLN(mainseg.name);
-      DEBUG_PRINTLN(curName);
-      strlcpy(segmentName, mainseg.name, sizeof(segmentName));
-      strlcpy((char *)&udpOut[1], segmentName, sizeof(udpOut) - 1); // leave room for header byte
-      notifierUdp.write(udpOut, 2 + strnlen((char *)&udpOut[1], sizeof(udpOut) - 1));
+      notifierUdp.beginPacket(broadcastIp, udpPort);
+      DEBUG_PRINT(F("UdpNameSync: saving segment name "));
+      DEBUG_PRINTLN(curName);
+      strlcpy(segmentName, curName, sizeof(segmentName));
+      strlcpy((char *)&udpOut[1], segmentName, sizeof(udpOut) - 1); // leave room for header byte
+      size_t nameLen = strnlen((char *)&udpOut[1], sizeof(udpOut) - 1);
+      notifierUdp.write(udpOut, 2 + nameLen);

24-26: Clarify enablement model (compile-time vs runtime)

setup() forces the mod enabled when compiled. If the PR intent is “not enabled by default” at runtime, consider defaulting to false and wiring a config toggle; otherwise, add a brief comment that it’s disabled by default at build-time (not included unless selected).

-    void setup() override {
-      enable(true);
-    }
+    void setup() override {
+      // Enabled when this usermod is compiled in; flip to false if you prefer runtime opt-in.
+      enable(true);
+    }

If you want a runtime toggle, I can add readFromConfig()/addToConfig() for “enabled”.


82-85: Remove or use PROGMEM strings

_name and _enabled are currently unused. Either wire them into config/UI or drop to save a few bytes.

-const char UdpNameSync::_name[]    PROGMEM = "UdpNameSync";
-const char UdpNameSync::_enabled[] PROGMEM = "enabled";
+// (Optional) Define only when used by config/UI to reduce flash usage.
+// const char UdpNameSync::_name[]    PROGMEM = "UdpNameSync";
+// const char UdpNameSync::_enabled[] PROGMEM = "enabled";

35-37: Optional: cache broadcast IP between sends

Computing broadcast each loop is fine; if you ever send more often, cache it and refresh on interface change to shave cycles.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a60be25 and 62fad4d.

📒 Files selected for processing (1)
  • usermods/udp_name_sync/udp_name_sync.cpp (1 hunks)
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-08-21T00:00:17.196Z
Learnt from: willmmiles
PR: wled/WLED#4859
File: usermods/udp_name_sync/udp_name_sync.cpp:62-70
Timestamp: 2025-08-21T00:00:17.196Z
Learning: In WLED codebase, UDP packet processing code must never rely on null termination being present on the wire and must always validate packet size first. The codebase already has some vulnerabilities where malformed data can crash the software, so avoiding introduction of new ones is a priority.

Applied to files:

  • usermods/udp_name_sync/udp_name_sync.cpp
📚 Learning: 2025-02-19T12:41:22.676Z
Learnt from: blazoncek
PR: wled/WLED#4482
File: wled00/udp.cpp:343-347
Timestamp: 2025-02-19T12:41:22.676Z
Learning: The setName() method in WLED's Segment class (FX_fcn.cpp) handles all necessary validation internally, including:
1. Null pointer checks
2. Length validation against WLED_MAX_SEGNAME_LEN
3. Memory allocation/reallocation
No additional validation or name clearing is needed before calling setName().

Applied to files:

  • usermods/udp_name_sync/udp_name_sync.cpp
📚 Learning: 2025-04-30T05:41:03.633Z
Learnt from: blazoncek
PR: wled/WLED#4667
File: usermods/user_fx/user_fx.cpp:27-30
Timestamp: 2025-04-30T05:41:03.633Z
Learning: In WLED, the Segment::allocateData() method already includes optimization to check if data is allocated and sufficiently sized, handling buffer reuse to reduce memory fragmentation. Adding an external check like `if (SEGENV.data == nullptr && !SEGENV.allocateData(dataSize))` is unnecessary and could be problematic, as it bypasses proper size verification.

Applied to files:

  • usermods/udp_name_sync/udp_name_sync.cpp
📚 Learning: 2025-04-24T09:31:06.879Z
Learnt from: blazoncek
PR: wled/WLED#4658
File: wled00/led.cpp:90-91
Timestamp: 2025-04-24T09:31:06.879Z
Learning: In the WLED codebase, the `getSegment()` function guards against out-of-bounds segment IDs by falling back to the main segment ID, and `getFirstSelectedSegId()` falls back to `getMainSegmentId()` if no segments are selected, ensuring no crashes when used through the `setValuesFromFirstSelectedSeg()` macro.

Applied to files:

  • usermods/udp_name_sync/udp_name_sync.cpp
📚 Learning: 2025-04-24T09:31:06.879Z
Learnt from: blazoncek
PR: wled/WLED#4658
File: wled00/led.cpp:90-91
Timestamp: 2025-04-24T09:31:06.879Z
Learning: In the WLED codebase, the `getSegment()` function guards against out-of-bounds segment IDs, and `getFirstSelectedSegId()` falls back to `getMainSegmentId()` if no segments are selected, ensuring no crashes when used through the `setValuesFromFirstSelectedSeg()` macro.

Applied to files:

  • usermods/udp_name_sync/udp_name_sync.cpp
📚 Learning: 2025-02-19T12:41:22.676Z
Learnt from: blazoncek
PR: wled/WLED#4482
File: wled00/udp.cpp:343-347
Timestamp: 2025-02-19T12:41:22.676Z
Learning: The setName() method in WLED handles segment name validation internally. No additional length checks or name clearing is needed before calling setName().

Applied to files:

  • usermods/udp_name_sync/udp_name_sync.cpp
🧬 Code graph analysis (1)
usermods/udp_name_sync/udp_name_sync.cpp (1)
usermods/wizlights/wizlights.cpp (1)
  • WLED_CONNECTED (83-103)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: wled_build / Build Environments (esp32c3dev)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
  • GitHub Check: wled_build / Build Environments (esp32_wrover)
  • GitHub Check: wled_build / Build Environments (esp32s3_4M_qspi)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_8MB_opi)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
  • GitHub Check: wled_build / Build Environments (esp32_eth)
  • GitHub Check: wled_build / Build Environments (esp32dev)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_compat)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_160)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_160)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_160)
  • GitHub Check: wled_build / Build Environments (nodemcuv2)
  • GitHub Check: wled_build / Build Environments (esp8266_2m)
🔇 Additional comments (4)
usermods/udp_name_sync/udp_name_sync.cpp (4)

28-31: Good gating in loop()

Early returns on enabled/connection state keep the hot path cheap. LGTM.


35-46: Empty-name send path is now well-initialized

Header set and payload byte zeroed avoids garbage on the wire. Nice.


48-50: Correct, bounded name equality check

Using strncmp with a fixed bound avoids UB on null pointers and long names. Good.


63-77: Robust UDP parsing (length-first, bounded copy, no reliance on NUL on the wire)

This follows WLED guidance on packet safety and uses Segment::setName() as intended. Solid.

@Liliputech
Copy link
Contributor Author

hello, is there anything left to do here? I think I'm quite done resolving all conversations and nitpicks for CodeRabbit :)

Copy link
Member

@willmmiles willmmiles left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that pretty much covers it. Thank you for your contribution!

@willmmiles willmmiles merged commit a032117 into wled:main Sep 5, 2025
20 checks passed
@Liliputech
Copy link
Contributor Author

thank you so much for your approval!

@Liliputech Liliputech deleted the udp_name_sync_rework branch September 5, 2025 05:46
@netmindz
Copy link
Member

netmindz commented Sep 5, 2025

Can you also submit updates to the docs so that others know how to write their own UDP usermods?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants