A drop-in C++ header that lets your ESP32-C6 receive LED colour data from ambilight and LED control software — without needing WLED firmware or NeoPixelBus.
Confirmed working with:
- ✅ HyperHDR — via WLED UDP protocol
- ✅ Hyperion.ng — via DDP protocol
The ESP32-C6 is an attractive microcontroller — it's affordable, capable, and supports WiFi 6. However, if you want to use it with ambilight software like HyperHDR or Hyperion.ng, you quickly run into a wall:
WLED firmware cannot be flashed to the C6. The chip architecture is incompatible with current WLED builds. This is a known open issue with no official fix at time of writing.
ESPHome on the ESP32-C6 requires the esp-idf framework (the Arduino framework is not supported on C6). Unfortunately:
neopixelbuslight platform does not work with esp-idffastledlight platform does not work with esp-idf
This means the typical ESPHome approach to driving addressable LED strips is unavailable on the C6.
ESPHome's esp32_rmt_led_strip platform uses the ESP32's RMT peripheral directly and does work with esp-idf on the C6. So driving LEDs is actually fine — the gap is purely on the receiving side: getting colour data into the ESP32 from external software.
Most forum answers to this problem fall into one of three camps:
- "Use a different board" — swap your C6 for an ESP32, ESP32-S2, or similar. Unhelpful if you've already bought C6s.
- "Flash HyperSerialESP32" — the HyperSerial project provides fast USB serial firmware for HyperHDR, but it also does not support the C6.
- "Wait for WLED C6 support" — the issue has been open for years.
None of these help if you have a pile of C6s and want to use them now.
This project implements a multi-protocol LED shim as a single C++ header file (wled_udp.h) that you include in your ESPHome configuration.
It runs four things simultaneously:
- Serves a fake WLED
/jsonHTTP endpoint on port 80 — just enough JSON to convince HyperHDR (and other WLED-aware software) that it's talking to a real WLED device and pass the LED count handshake. - Listens for WLED realtime UDP packets on port 21324 — handles WARLS, DRGB, and DNRGB protocols used by HyperHDR.
- Listens for DDP packets on port 4048 — handles the Distributed Display Protocol used by Hyperion.ng and other software.
- Stores received colour data in a shared buffer — which your ESPHome
addressable_lambdalight effect reads every 33ms (~30fps) and writes to the physical LED strip viaesp32_rmt_led_strip.
Both WLED UDP and DDP write into the same shared buffer, so whichever software is active just works — no configuration changes needed to switch between them.
- ESP32-C6 (tested on
esp32-c6-devkitc-1) - ESPHome with esp-idf framework (required for C6)
- HyperHDR, Hyperion.ng, or other compatible software
- Addressable LED strip compatible with
esp32_rmt_led_strip(WS2812B, WS2815, etc.)
| File | Description |
|---|---|
wled_udp.h |
The shim — copy this to your ESPHome config directory |
wled_udp_example.yaml |
Minimal working ESPHome YAML to get started |
Copy wled_udp.h into your ESPHome configuration directory (the same folder where your .yaml files live).
Use wled_udp_example.yaml as your starting point. The only values you need to change are at the top:
substitutions:
led_count: "66" # ← Set this to your actual LED countAnd further down:
pin: GPIO3 # ← Set this to your LED data pin
rgb_order: GRB # ← Adjust if colours appear wrong (WS2812B/WS2815 = GRB)The led_count substitution flows through automatically to the header via a build flag and to the light config — you only need to set it once.
This is important. The shim occupies port 80 to serve the /json stub. If you use ESPHome's web_server component, it must be moved to a different port:
web_server:
port: 8080If you don't use web_server, you can omit it entirely.
Flash your ESP32-C6 via ESPHome as normal. In your LED software, add a new WLED device pointing at your ESP32-C6's IP address and follow that software's usual setup instructions. The shim handles the rest transparently.
In your ESPHome/Home Assistant light entity, activate the WLED effect. Without this, the LED strip won't output anything even if data is being received.
HyperHDR's WLED driver performs a GET /json request before sending any LED data. It uses the response to verify the device is a WLED instance and to confirm the LED count.
The shim runs a minimal TCP server on port 80 that responds with just enough JSON to satisfy this check:
{
"state": {"on": true, "bri": 255},
"info": {
"ver": "0.14.0",
"leds": {"count": 66, "rgbw": false},
"name": "WLED",
"udpport": 21324
}
}The LED count is driven by the same WLED_NUM_LEDS define as everything else, so it always stays in sync.
Used by HyperHDR. The shim handles three WLED realtime protocols:
| Protocol | ID | Description |
|---|---|---|
| WARLS | 1 |
Sparse updates — sends only changed LEDs by index |
| DRGB | 2 |
Full frame — sends all LED colours in one packet |
| DNRGB | 4 |
Full frame with start offset — supports >256 LEDs |
Used by Hyperion.ng and other DDP-capable software. DDP (Distributed Display Protocol) uses a compact 10-byte header followed by raw RGB data:
| Bytes | Content |
|---|---|
| 0 | Flags (version, push, query, reply) |
| 1 | Sequence number |
| 2 | Data type |
| 3 | Destination ID |
| 4–7 | Byte offset (32-bit MSB first) |
| 8–9 | Data length (16-bit MSB first) |
| 10+ | RGB data |
The offset field allows large frames to be split across multiple packets, which the shim handles correctly by writing each chunk into the right position in the shared buffer.
Both protocols write into the same shared buffer (g_wled_buf) protected by a FreeRTOS mutex. An addressable_lambda effect polls the buffer every 33ms and writes colour values to the LED strip:
if (xSemaphoreTake(g_wled_mutex, 0) == pdTRUE) {
for (int i = 0; i < 66; i++) {
it[i] = Color(
g_wled_buf[i * 3 + 0],
g_wled_buf[i * 3 + 1],
g_wled_buf[i * 3 + 2]
);
}
xSemaphoreGive(g_wled_mutex);
}The mutex TryTake with timeout 0 means if a UDP task is mid-write, the frame is simply skipped rather than blocking — keeping the LED output smooth. If no data has been received for 3 seconds, the strip blanks so LEDs don't freeze on the last frame if software disconnects.
Tested with HyperHDR and Hyperion.ng only. HyperHDR (WLED UDP) and Hyperion.ng (DDP) are confirmed working. Other software (SignalRGB, LightFX, Prismatik, LedFX, etc.) may work, may partially work, or may require additional endpoint support. If you test another application and find issues, please open an issue or PR.
Hyperion.ng on Wayland. Hyperion.ng's X11 screen grabber does not work under Wayland. LED output via DDP works fine — the grabber issue only affects screen capture, not the LED data path. You can still manually set colours or use other input sources.
Linux development only. This was developed on Linux. It has not been tested on Windows or macOS ESPHome environments. It should work, but is unverified.
No WLED effects. This shim only handles receiving realtime colour data from an external source. It does not implement WLED's built-in effects engine, segment support, or any other WLED features. It is purely a protocol bridge.
esp-idf framework required. This will not compile under the Arduino framework. The ESP32-C6 requires esp-idf anyway, but if you attempt to use this on another board with the Arduino framework it won't work.
Port 80 is consumed.
The shim owns port 80 for the HTTP stub. Move any other services (ESPHome web_server, etc.) to alternative ports.
Single LED output only. The current implementation drives one LED strip from one data pin. Multi-segment or multi-output configurations are not supported in this initial release.
Remember to enable the WLED effect. The ESPHome light entity must have the WLED effect active to output anything. This is easy to forget — if your LEDs aren't responding despite data being received, check the effect is enabled.
You might have bought ESP32-C6s specifically for their WiFi 6 capability, their price point, or because they were available. Having a bag of perfectly good microcontrollers that "just don't work" with your software stack is frustrating — and "buy different hardware" is a rubbish answer when the hardware works fine for everything else.
This shim exists so you can use what you have.
If you test this with software other than HyperHDR and Hyperion.ng and it works (or doesn't), please open an issue or PR with your findings. Contributions are welcome.
- WLED Project for the UDP realtime protocol specification
- HyperHDR for the ambilight software this was developed and tested against
- Hyperion.ng for DDP compatibility testing
- 3waylabs for the DDP protocol specification
- The ESP32-C6 WLED support issue thread, where the frustration that prompted this project lives
MIT — do whatever you like with it.