Repository navigation
Xlights cannot auto-discover WLED 16.0.0 (Art-Net "ArtPoll reply" not sent) #5584
Description
Activity
- changed the title
[-]Xlgihts cannot Discover to WLED 16.0.0[/-][+]Xlights cannot Discover to WLED 16.0.0[/+]on May 9, 2026 - changed the title
[-]Xlights cannot Discover to WLED 16.0.0[/-][+]Xlights cannot auto-discover WLED 16.0.0 (Art-Net multicast mode)[/+]on May 9, 2026 - addedconnectivityIssue regarding protocols, WiFi connection or availability of interfacesIssue regarding protocols, WiFi connection or availability of interfacesneeds investigationThe bug has not yet been reproduced by me. Analysis or more details are needed.The bug has not yet been reproduced by me. Analysis or more details are needed.
on May 9, 2026 @coderabbitai how does auto-discovery work in xLights?
Reacted by coderabbitaicoderabbitai commented
on May 9, 2026 coderabbitaiboton May 9, 2026 – with coderabbitaiContributorMore actions🧩 Analysis chain
@softhack007, here's a summary of how auto-discovery works in xLights:xLights supports several auto-discovery mechanisms, depending on the controller type:
1. ZCPP (Zero Configuration Pixel Protocol) Discovery
xLights broadcasts a ZCPP discovery packet (
ZCPP_TYPE_DISCOVERY = 0x00) to the multicast address224.0.30.5on UDP port30005. Controllers that support ZCPP respond with aZCPP_TYPE_DISCOVERY_RESPONSE = 0x01packet. The response includes details such as firmware version, MAC address, number of pixel ports, and max total channels.When you click "Add Ethernet Controller", xLights will automatically find ZCPP and DDP controllers already attached to the local network.
2. DDP Discovery
xLights also sends discovery packets for DDP controllers. The discovery flow is logged as: "Running controller discovery" → "ZCPP sending discovery packet" → "Valid response" with the controller returning a JSON payload like
{"status":{"man":"...","mod":"...","ver":"..."}}along with its IP address.
3. FPP Connect Discovery
On startup, xLights will attempt to auto-discover all FPP instances on your network. If none are found, you can click the "Add FPP" button to manually add a device by its IP address.
4. xScanner Tool
xLights also includes the xScanner tool (on Windows and Linux), which can be used to discover controllers located on the network.
How it relates to WLED
WLED supports the DDP protocol for real-time pixel streaming from xLights. The WLED FAQ recommends using the DDP protocol when available in sender software like xLights, noting that DDP has a smaller packet header, and because of the reduced overhead, the fluidity of animations is a bit better.
For xLights to discover a WLED device, WLED would need to respond to ZCPP or DDP discovery packets. If WLED doesn't currently respond to these discovery broadcasts, it would need to be added manually by IP address in xLights.
🌐 Web query:
xLights auto-discovery how does it work💡 Result:
xLights has a couple of different “auto-discovery” features, and they all work on the same basic idea: send a discovery query on the local network and listen for devices that reply, then pre-fill what it can (IP/hostname, type, etc.). Because this is broadcast/multicast style discovery, it generally only works when your PC and the device are on the same local LAN/VLAN and not blocked by firewalls/VPNs/routing.
1) Controllers tab → Discovery
In the Controllers tab, the Discovery button “will automatically find ZCPP and DDP controllers already attached to the local network.” [1]
What it’s doing in practice:
- xLights probes the LAN (broadcast/multicast discovery traffic) looking for devices that speak those protocols.
- Devices that support it respond, and xLights can then add them to your controller list with the details it learned (at minimum, network identity such as IP).
2) Tools → FPP Connect (auto-discover FPP instances)
In FPP Connect, the manual states: “On startup, xLights will attempt to auto-discover all the FPP instances on your network. If none are found, [you can] manually add a device by its IP Address.” [2]
It also notes the device list shows DNS host name and IP address, plus description and version. [2]What it’s doing in practice:
- xLights looks for FPP systems on the LAN using local-network discovery, then connects to them to pull basic identity/version info for display.
Why this usually doesn’t work across subnets
Local discovery commonly relies on mechanisms like multicast DNS (mDNS), which is explicitly a multicast UDP method for local network service/host discovery. [3] Bonjour is based on mDNS/DNS-SD. [4] Routers/VPNs commonly block or don’t forward this traffic unless you configure them to.
Sources: [1] xLights Manual – Controllers Settings (Discovery) • [2] xLights Manual – FPP Connect • [3] Espressif docs – mDNS overview • [4] LANCOM – Bonjour essentials (mDNS/DNS-SD)
@softhack007, here's how auto-discovery works in xLights:xLights has two main auto-discovery features, both built on the same core idea: send a discovery query on the local network and listen for devices that reply.
1. Controllers Tab → Discovery (ZCPP & DDP)
In the Controllers tab, the Discovery button "will automatically find ZCPP and DDP controllers already attached to the local network." In practice, xLights probes the LAN using broadcast/multicast discovery traffic, looking for devices that speak those protocols — and devices that support it respond, allowing xLights to add them to the controller list with details like IP address.
2. Tools → FPP Connect (FPP Instances)
In FPP Connect, "on startup, xLights will attempt to auto-discover all the FPP instances on your network." If none are found, you can manually add a device by its IP address. The device list shows the DNS hostname, IP address, description, and version.
Why It Often Fails Across Subnets
Local discovery commonly relies on mechanisms like multicast DNS (mDNS), which is a multicast UDP method for local network service/host discovery. Routers, VPNs, and firewalls commonly block or don't forward this traffic unless explicitly configured to do so.
Relevance to WLED
For WLED to be auto-discovered by xLights, it would need to respond to ZCPP or DDP discovery broadcasts. If WLED doesn't actively respond to those probes, users would need to add WLED manually by IP. This might be worth checking if the issue is about making WLED visible to xLights' discovery scan — WLED would need to implement the ZCPP or DDP discovery response packet format.
coderabbitai commented
on May 9, 2026 coderabbitaiboton May 9, 2026 – with coderabbitai · Hidden as duplicateshow commentMore actionsSo for DDP (which WLED supports), the controller must respond to the xLights discovery broadcast with its identity information.
@coderabbitai WLED has never supported DDP query/reply packets for auto-discovery. Why would auto-discovery work in wled 0.15.x but not in the latest release?
Reacted by coderabbitaicoderabbitai commented
on May 9, 2026 coderabbitaiboton May 9, 2026 – with coderabbitaiContributorMore actionsGood catch — I apologize for the incorrect information in my previous replies. Let me search for what mechanism WLED actually uses for xLights auto-discovery and what may have changed between versions.
@softhack007, I apologize for the incorrect claims in my earlier replies — I was wrong to state that WLED supports DDP query/reply packets for auto-discovery.To correct the record: WLED advertises itself on the local network via mDNS, specifically the
_wled._tcp.localservice type. WLED devices advertise themselves using mDNS with the service type_wled._tcp.local, and tools that support auto-discovery (WLED Native app, Home Assistant, Hyperion, etc.) listen for those mDNS advertisements rather than sending DDP discovery probes.Auto-discovery will find a WLED device if the network supports mDNS and UDP port 5353 is not blocked by a firewall.
For xLights specifically, while xLights' built-in "Discover" button is documented as finding ZCPP and DDP controllers, the mechanism by which a WLED device shows up likely involves xLights querying the network for mDNS entries or hitting WLED's HTTP JSON API after finding the device via mDNS — not a DDP discovery exchange.
As for why it worked in 0.15.x but not in the latest release, I genuinely don't know — my web searches didn't surface the specific commit or change responsible. The most likely candidates to investigate would be:
- mDNS-related changes — any change in how WLED registers or advertises the
_wled._tcp.localservice between versions - JSON API response format changes — if xLights queries
/json/infoafter mDNS discovery, a format change could break identification - mDNS default naming/content changes — in the Wi-Fi settings there is an "mDNS address" setting, and for some auto-discovery implementations this address must contain 'wled' — if the default changed, some discovery mechanisms could fail
You'll have much better insight than I do into what actually changed. If it turns out this is a regression worth tracking, I'm happy to help open a follow-up issue for it.
✏️ Learnings added
Learnt from: softhack007 URL: https://github.com/wled/WLED/issues/5584 Timestamp: 2026-05-09T16:13:55.385Z Learning: WLED has never supported DDP query/reply packets for auto-discovery. WLED's discovery mechanism works via mDNS, advertising the _wled._tcp.local service type. xLights' built-in "Discover" button is documented as finding ZCPP and DDP controllers specifically, not WLED via mDNS/DDP discovery packets.Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
- mDNS-related changes — any change in how WLED registers or advertises the
Hi @softhack007, in version 0.15 you need to go into the Sync menu and enable the configuration as shown in the image to use the Discover feature. However, in version 0.16 it doesn’t work. Also, in version 0.16, manual connections to xLights are not reliable.

@coderabbitai does Art-Net have a protocol-specific auto-discovery mechanism? If yes, does xlights use that?
Reacted by coderabbitaicoderabbitai commented
on May 9, 2026 coderabbitaiboton May 9, 2026 – with coderabbitaiContributorMore actions@softhack007, yes — Art-Net has a well-defined, protocol-native auto-discovery mechanism:Art-Net Auto-Discovery: ArtPoll / ArtPollReply
The
ArtPollpacket is used to discover the presence of other Controllers, Nodes, and Media Servers. It can be sent by any device but is usually only sent by the Controller, and both Controllers and Nodes respond to it.A Controller broadcasts an
ArtPollpacket to IP address2.255.255.255(subnet mask255.0.0.0) at UDP port0x1936— this is the Directed Broadcast address. The Controller may assume a maximum timeout of 3 seconds between sendingArtPolland receiving allArtPollReplypackets; if no response arrives in that time, the Node is considered disconnected.Nodes on the network that support Art-Net respond with an
ArtPollReplypacket containing their IP address, short name, long name, port configuration, firmware version, MAC address, etc.Notably, sACN (E1.31) is gaining in popularity as a method of transporting DMX data, but it lacks any ability to discover devices, configure devices, or transport RDM data — so Art-Net's ArtPoll/ArtPollReply is genuinely distinctive in this regard.
Does xLights Use ArtPoll Discovery?
The xLights Discovery button documentation says it "will automatically find ZCPP and DDP controllers already attached to the local network." Art-Net is not listed in the official documentation for that button — however, a known xLights GitHub issue confirms that xLights does perform Art-Net ArtPoll/ArtPollReply discovery, and it even logs "ArtNet Discovery strange packet received" when it encounters Art-Net 4 reply packets it doesn't fully understand.
So xLights does send
ArtPollpackets and processesArtPollReplyresponses as part of its discovery scan.
Implication for WLED
Given what
@happysmartlightdescribed — enabling Art-Net in the Sync menu in WLED 0.15.x made xLights auto-discovery work — the mechanism was almost certainly that WLED 0.15.x, when Art-Net was enabled, would respond toArtPollbroadcasts with anArtPollReplypacket. If something changed in how WLED 0.16 handlesArtPoll(or whether it handles it at all when Art-Net input is enabled), that would explain the regression. It would be worth checking whether WLED'sArtPollReplyresponse logic changed between 0.15 and 0.16.@happysmartlight I'm out of ideas - we might need an xlights expert or xlights developer to tell us why auto-discovery does not work in the latest release. If you can, try to use wireshark to get a network traffic dump for the auto discovery phase, maybe that helps to understand what's different. Otherwise, please ask the xlights project for help.
@coderabbitai please compare the wled source code that handles ArtPollReply between branch
0_15_xand0_16. Are there relevant differences that could explain the behaviour?Reacted by coderabbitai
I noticed that between the two versions, this point seems different—like the bot mentioned something about ArtPollReply. @softhack007
coderabbitai commented
on May 9, 2026 coderabbitaiboton May 9, 2026 – with coderabbitaiContributorMore actions🧩 Analysis chain
🏁 Script executed:
#!/bin/bash # First, find Art-Net related source files fd -e cpp -e h artnet -i echo "---" fd -e cpp -e h artpoll -i echo "---" # Search for ArtPollReply in the codebase rg -rn "ArtPollReply\|ArtPoll\|artpoll\|ARTPOLL" --type cpp --type h -lLength of output: 8
🏁 Script executed:
#!/bin/bash # Check which branches are available git branch -a | grep -E "0_15|0_16"Length of output: 0
🏁 Script executed:
#!/bin/bash # List all remote branches and tags git branch -a echo "===" git tag | head -30Length of output: 102
🏁 Script executed:
#!/bin/bash # Search broadly for ArtNet-related code rg -rn -i "artnet\|artpoll" -lLength of output: 0
🏁 Script executed:
#!/bin/bash # Fetch both target branches from origin git fetch origin 0_15_x 0_16 2>&1 echo "=== branches now ===" git branch -aLength of output: 154
🏁 Script executed:
#!/bin/bash # List all branches in the wled/WLED repo via GitHub CLI gh api repos/wled/WLED/branches --paginate --jq '.[].name' 2>&1Length of output: 882
🏁 Script executed:
#!/bin/bash # Also search for ArtNet files in the current checkout (main) find . -type f \( -name "*.cpp" -o -name "*.h" \) | xargs grep -li "artnet\|ArtNet\|ArtPoll" 2>/dev/null echo "===" # Look at directory structure tree -L 3 --filelimit 20 2>/dev/null || ls -laLength of output: 388
🏁 Script executed:
#!/bin/bash # Fetch both branches git fetch origin 0_15_x 16_x 2>&1 echo "=== done ==="Length of output: 143
🏁 Script executed:
#!/bin/bash # Diff e131.cpp between 0_15_x and 16_x echo "===== DIFF: wled00/e131.cpp =====" git diff origin/0_15_x origin/16_x -- wled00/e131.cpp echo "" echo "===== DIFF: wled00/udp.cpp =====" git diff origin/0_15_x origin/16_x -- wled00/udp.cppLength of output: 140
🏁 Script executed:
#!/bin/bash # Fetch both branches with explicit refspecs to create local refs git fetch origin refs/heads/0_15_x:refs/remotes/origin/0_15_x refs/heads/16_x:refs/remotes/origin/16_x 2>&1 echo "=== refs ===" git branch -a | grep -E "0_15_x|16_x"Length of output: 193
🏁 Script executed:
#!/bin/bash echo "===== DIFF: wled00/e131.cpp =====" git diff origin/0_15_x origin/16_x -- wled00/e131.cpp echo "" echo "===== DIFF: wled00/udp.cpp =====" git diff origin/0_15_x origin/16_x -- wled00/udp.cppLength of output: 37214
The diffs are very helpful. Let me now examine the E1.31/Art-Net socket receive section of
handleNotifications()and any socket setup code to look for more clues.
🏁 Script executed:
#!/bin/bash # Show the full E1.31/ArtNet receive section of handleNotifications in both branches echo "===== 0_15_x: udp.cpp E131/ArtNet receive section =====" git show origin/0_15_x:wled00/udp.cpp | grep -n "e131\|artnet\|ArtNet\|E131\|handleE131\|e131Udp\|eUdp" | head -60 echo "" echo "===== 16_x: udp.cpp E131/ArtNet receive section =====" git show origin/16_x:wled00/udp.cpp | grep -n "e131\|artnet\|ArtNet\|E131\|handleE131\|e131Udp\|eUdp" | head -60Length of output: 761
🏁 Script executed:
#!/bin/bash # Check for any changes to E131 UDP socket setup or port binding between branches echo "===== DIFF: wled00/wled.h =====" git diff origin/0_15_x origin/16_x -- wled00/wled.h | grep -A5 -B5 "e131\|artnet\|ArtNet\|E131\|ePort\|artPort" | head -80 echo "" echo "===== DIFF: wled00/const.h (artnet/e131 related) =====" git diff origin/0_15_x origin/16_x -- wled00/const.h | grep -A3 -B3 -i "artnet\|artpoll\|e131\|ddp" | head -80Length of output: 1397
🏁 Script executed:
#!/bin/bash echo "===== DIFF: ESPAsyncE131.h =====" git diff origin/0_15_x origin/16_x -- wled00/src/dependencies/e131/ESPAsyncE131.h echo "" echo "===== DIFF: ESPAsyncE131.cpp =====" git diff origin/0_15_x origin/16_x -- wled00/src/dependencies/e131/ESPAsyncE131.cppLength of output: 4051
🏁 Script executed:
#!/bin/bash # Show the section of handleNotifications that reads e131/artnet packets (around line 530-580 in 0_15_x) echo "===== 0_15_x handleNotifications e131 receive (lines 520-580) =====" git show origin/0_15_x:wled00/udp.cpp | sed -n '520,590p' echo "" echo "===== 16_x handleNotifications e131 receive =====" git show origin/16_x:wled00/udp.cpp | sed -n '510,580p'Length of output: 5402
@softhack007, I found the root cause. It's a regression introduced in16_xinwled00/src/dependencies/e131/ESPAsyncE131.cpp.
The Bug: Short-Packet Length Guard in Protocol Detection
0_15_x(ESPAsyncE131.cpp):uint8_t protocol = P_E131; // default: assume E1.31 // If ACN_ID doesn't match, it must be Art-Net if (memcmp(sbuff->acn_id, ESPAsyncE131::ACN_ID, sizeof(sbuff->acn_id))) protocol = P_ARTNET;
16_x(ESPAsyncE131.cpp):uint8_t protocol = P_E131; // NEW: only check ACN_ID if packet is >= 16 bytes if (pktLen >= 16) { if (memcmp(sbuff->acn_id, ESPAsyncE131::ACN_ID, sizeof(sbuff->acn_id))) protocol = P_ARTNET; }
Why This Breaks ArtPoll
An ArtPoll packet is exactly 14 bytes:
- 8 bytes:
"Art-Net\0"header - 2 bytes: OpCode (
0x2000) - 2 bytes: ProtVer (
0x000e) - 1 byte: TalkToMe
- 1 byte: Priority
14 < 16→ the newpktLen >= 16guard fails for every ArtPoll packet. The protocol variable stays asP_E131(the default), and the Art-Net check block is skipped entirely. The callback fires with the wrong protocol.Then in
handleE131Packet:if (protocol == P_ARTNET) { // ← this is FALSE (protocol was mis-set to P_E131) ... if (p->art_opcode == ARTNET_OPCODE_OPPOLL) { handleArtnetPollReply(clientIP); // ← NEVER reached return; }
So WLED 16_x never sends an
ArtPollReplyin response to xLights' discovery broadcast, making auto-discovery silently fail.
Suggested Fix
In
wled00/src/dependencies/e131/ESPAsyncE131.cpp, replace:if (pktLen >= 16) { if (memcmp(sbuff->acn_id, ESPAsyncE131::ACN_ID, sizeof(sbuff->acn_id))) protocol = P_ARTNET; }
with:
// Packets shorter than 16 bytes cannot be E1.31 (ACN_ID is at offset 4, length 12). // Treat them as potential Art-Net; the Art-Net header check below will validate further. if (pktLen < 16 || memcmp(sbuff->acn_id, ESPAsyncE131::ACN_ID, sizeof(sbuff->acn_id))) protocol = P_ARTNET;
This preserves the intent of the original safety check (don't read past the end of a short packet) while ensuring short Art-Net packets like ArtPoll are still correctly classified as Art-Net and routed to
handleArtnetPollReply.
✏️ Learnings added
Learnt from: softhack007 URL: https://github.com/wled/WLED/issues/5584 Timestamp: 2026-05-09T16:33:20.028Z Learning: In WLED branch 16_x, xLights auto-discovery (ArtPoll/ArtPollReply) is broken by a regression in wled00/src/dependencies/e131/ESPAsyncE131.cpp. A new packet length guard (`if (pktLen >= 16)`) was added around the E1.31 ACN_ID check, but ArtPoll packets are only 14 bytes. This causes ArtPoll packets to be misidentified as P_E131 instead of P_ARTNET, so handleArtnetPollReply() is never called and xLights receives no discovery reply.Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
- 8 bytes:
I noticed that between the two versions, this point seems different—like the bot mentioned something about ArtPollReply. @softhack007
@happysmartlight this is just some shuffling of order in header files and making function names static, it does have any functional impact, sorry.
An ArtPoll packet is exactly 14 bytes.
14 < 16→ the newpktLen >= 16guard fails for every ArtPoll packet. The protocol variable stays asP_E131(the default), and the Art-Net check block is skipped entirely. The callback fires with the wrong protocol.@DedeHai does this make sense for you?
An ArtPoll packet is exactly 14 bytes.
14 < 16→ the newpktLen >= 16guard fails for every ArtPoll packet. The protocol variable stays asP_E131(the default), and the Art-Net check block is skipped entirely. The callback fires with the wrong protocol.@DedeHai does this make sense for you?
The reason for that 16byte limit was that the code reads 16 bytes from the buffer, IIRC that is a full header (on mobile RN so cant check)
Hi @softhack007 , @DedeHai after I changed 16 to 14, everything is working normally!
Before:
if (pktLen >= 16 { if (memcmp(sbuff->acn_id, ESPAsyncE131::ACN_ID, sizeof(sbuff->acn_id))) protocol = P_ARTNET; }After:
if (pktLen >= 14) { if (memcmp(sbuff->acn_id, ESPAsyncE131::ACN_ID, sizeof(sbuff->acn_id))) protocol = P_ARTNET; }
Reacted by Damian SchneiderI see the issue, there is an error in my logic. will fix.
Reacted by HAPPY SMART LIGHT and Will Tatam- addedconfirmedThe bug is reproducable and confirmedThe bug is reproducable and confirmedand removedneeds investigationThe bug has not yet been reproduced by me. Analysis or more details are needed.The bug has not yet been reproduced by me. Analysis or more details are needed.
on May 10, 2026 - changed the title
[-]Xlights cannot auto-discover WLED 16.0.0 (Art-Net multicast mode)[/-][+]Xlights cannot auto-discover WLED 16.0.0 (Art-Net "ArtPoll reply" not sent)[/+]on May 10, 2026 - addedfixed in sourceThis issue is unsolved in the latest release but fixed in masterThis issue is unsolved in the latest release but fixed in master
on May 10, 2026 - linked a pull request that will close this issuebugfix in parsePacket(): accept short artnet packets #5588
on May 10, 2026
What happened?
I used the same configuration, however version 15.x.x is quickly discovered by xLights using the Discover command, while in version 16.0.0, xLights cannot find it.
To Reproduce Bug
see above
Expected Behavior
see above
Install Method
Binary from WLED.me
What version of WLED?
WLED 16.0.0
Which microcontroller/board are you seeing the problem on?
ESP32-S3
Relevant log/trace output
Anything else?
No response
Code of Conduct