Skip to content

Xlights cannot auto-discover WLED 16.0.0 (Art-Net "ArtPoll reply" not sent) #5584

Description

@happysmartlight

What happened?

Image Image

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

  • I agree to follow this project's Code of Conduct

Activity

  1. changed the title [-]Xlgihts cannot Discover to WLED 16.0.0[/-] [+]Xlights cannot Discover to WLED 16.0.0[/+] on May 9, 2026
  2. 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
  3. added
    connectivityIssue regarding protocols, WiFi connection or availability of interfaces
    needs investigationThe bug has not yet been reproduced by me. Analysis or more details are needed.
    on May 9, 2026
  4. softhack007 commented on May 9, 2026

    @softhack007
    Member

    @coderabbitai how does auto-discovery work in xLights?

  5. coderabbitai commented on May 9, 2026

    @coderabbitai
    Contributor
    🧩 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 address 224.0.30.5 on UDP port 30005. Controllers that support ZCPP respond with a ZCPP_TYPE_DISCOVERY_RESPONSE = 0x01 packet. 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.

  6. coderabbitai commented on May 9, 2026

    @coderabbitai
  7. softhack007 commented on May 9, 2026

    @softhack007
    Member

    So 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?

  8. coderabbitai commented on May 9, 2026

    @coderabbitai
    Contributor

    Good 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.local service 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:

    1. mDNS-related changes — any change in how WLED registers or advertises the _wled._tcp.local service between versions
    2. JSON API response format changes — if xLights queries /json/info after mDNS discovery, a format change could break identification
    3. 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.

  9. happysmartlight commented on May 9, 2026

    @happysmartlight
    Author

    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.
    Image

  10. softhack007 commented on May 9, 2026

    @softhack007
    Member

    @coderabbitai does Art-Net have a protocol-specific auto-discovery mechanism? If yes, does xlights use that?

  11. coderabbitai commented on May 9, 2026

    @coderabbitai
    Contributor

    @softhack007, yes — Art-Net has a well-defined, protocol-native auto-discovery mechanism:

    Art-Net Auto-Discovery: ArtPoll / ArtPollReply

    The ArtPoll packet 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 ArtPoll packet to IP address 2.255.255.255 (subnet mask 255.0.0.0) at UDP port 0x1936 — this is the Directed Broadcast address. The Controller may assume a maximum timeout of 3 seconds between sending ArtPoll and receiving all ArtPollReply packets; if no response arrives in that time, the Node is considered disconnected.

    Nodes on the network that support Art-Net respond with an ArtPollReply packet 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 ArtPoll packets and processes ArtPollReply responses as part of its discovery scan.


    Implication for WLED

    Given what @happysmartlight described — 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 to ArtPoll broadcasts with an ArtPollReply packet. If something changed in how WLED 0.16 handles ArtPoll (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's ArtPollReply response logic changed between 0.15 and 0.16.

  12. softhack007 commented on May 9, 2026

    @softhack007
    Member

    @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.

  13. softhack007 commented on May 9, 2026

    @softhack007
    Member

    @coderabbitai please compare the wled source code that handles ArtPollReply between branch 0_15_x and 0_16. Are there relevant differences that could explain the behaviour?

  14. happysmartlight commented on May 9, 2026

    @happysmartlight
    Author
    Image

    I noticed that between the two versions, this point seems different—like the bot mentioned something about ArtPollReply. @softhack007

  15. coderabbitai commented on May 9, 2026

    @coderabbitai
    Contributor
    🧩 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 -l
    

    Length 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 -30
    

    Length of output: 102


    🏁 Script executed:

    #!/bin/bash
    # Search broadly for ArtNet-related code
    rg -rn -i "artnet\|artpoll" -l
    

    Length 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 -a
    

    Length 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>&1
    

    Length 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 -la
    

    Length 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.cpp
    

    Length 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.cpp
    

    Length 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 -60
    

    Length 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 -80
    

    Length 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.cpp
    

    Length 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 in 16_x in wled00/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 new pktLen >= 16 guard fails for every ArtPoll packet. The protocol variable stays as P_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 ArtPollReply in 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.

  16. softhack007 commented on May 9, 2026

    @softhack007
    Member
    Image 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.

  17. softhack007 commented on May 9, 2026

    @softhack007
    Member

    An ArtPoll packet is exactly 14 bytes.

    14 < 16 → the new pktLen >= 16 guard fails for every ArtPoll packet. The protocol variable stays as P_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?

  18. DedeHai commented on May 9, 2026

    @DedeHai
    Collaborator

    An ArtPoll packet is exactly 14 bytes.

    14 < 16 → the new pktLen >= 16 guard fails for every ArtPoll packet. The protocol variable stays as P_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)

  19. happysmartlight commented on May 10, 2026

    @happysmartlight
    Author

    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;
      }
    
    Image
  20. DedeHai commented on May 10, 2026

    @DedeHai
    Collaborator

    I see the issue, there is an error in my logic. will fix.

  21. added
    confirmedThe bug is reproducable and confirmed
    and removed
    needs investigationThe bug has not yet been reproduced by me. Analysis or more details are needed.
    on May 10, 2026
  22. 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
  23. added
    fixed in sourceThis issue is unsolved in the latest release but fixed in master
    on May 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugconfirmedThe bug is reproducable and confirmedconnectivityIssue regarding protocols, WiFi connection or availability of interfacesfixed in sourceThis issue is unsolved in the latest release but fixed in master

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions