Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

DDP Compression Extension — Gauging Upstream Interest

Aperta
#5,810 19 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
5/5
Tempo stimato
Più di una settimana
Idoneità per principianti
25/100
Tipo di issue
Funzionalità
Chiarezza
Da chiarire
Stato di attività
Tranquilla
Stack tecnologico
cpp, python

Direzione di ricerca

Start by reviewing the linked DRAFT_PR.md files and the proposed wled00/ddp_compress.h, then compare the receiver changes around handleDDPPacket() with the existing DDP implementation. Check the related issues and PR #5774 for compatibility concerns. Done would require upstream agreement on the wire format and scope before implementation can be defined.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

AI enhancement

DDP Compression Extension — Gauging Upstream Interest

I've been running WLED on an M5StickC over a PPP serial link (USB at 1.5Mbaud,
~172 KB/s effective) and hit the obvious bandwidth wall. To make it work I
implemented a compression extension for DDP — it uses the reserved bits in the
existing header so it's backwards compatible with standard senders and receivers.

Posting here to share what I've built and see if there's any appetite for
carrying something like this upstream. Happy to submit PRs if it's useful to
others, or keep it as a fork-only thing if not.


The problem

DDP is great for LED streaming. The bandwidth requirements are not:

Transport Effective BW 800px RGB @ 30fps 12,800px RGB @ 30fps
WiFi (20 Mbps) 2.3 MB/s fine fine
PPP/USB serial (1.5 Mbaud) 172 KB/s 71 fps — tight 4.5 fps — unusable
Serial (115200 baud) 11.5 KB/s 4.7 fps impossible
LoRa (250 kbps) ~25 KB/s 10 fps impossible

WiFi is fine. Anything slower gets painful quickly.


What I built

A delta+RLE compression extension that uses the reserved bits in the DDP header:

  • Flags byte (byte 0), bit 5: COMPRESSED flag (0x20)
  • Sequence byte (byte 1), upper nibble: compression type
    • 0x10 = delta+RLE (XOR against previous frame, then PackBits RLE)
    • 0x20 = RLE only (keyframe — no delta, for resync after packet loss)
    • 0x30 = transform (global fade/scale + sparse explicit writes — decoder only so far)

Standard DDP senders never set bit 5, so existing receivers are unaffected.
A receiver that doesn't understand compression will display noise on compressed
packets — which is the right behaviour for an opt-in extension.

Measured compression ratios (real hardware, M5StickC, 800 LEDs, 40fps)
Pattern Raw Compressed Ratio Wire bandwidth
Rainbow (worst case) 2,400 B 2,400 B 1:1 93.7 KB/s
Solid colour pulse 2,400 B ~1,900 B 1.2:1 77.8 KB/s
Sparse twinkle (2% change/frame) 2,400 B 120 B 20:1 4.6 KB/s
Static (no change) 2,400 B ~16 B ~150:1 <1 KB/s

The worst case (rainbow, every pixel different every frame) gets nothing. Most
real-world animations are somewhere between "solid pulse" and "sparse twinkle"
and compress well.

What this means at 95% compression (typical for sparse animations):

Transport 800px before 800px after 12,800px before 12,800px after
PPP 1.5Mbaud 71 fps >100 fps 4.5 fps 89 fps
Serial 115200 4.7 fps ~70 fps impossible 4.4 fps
LoRa 250kbps 10 fps ~150 fps impossible 9 fps

Implementation

The codec is a ~150-line header-only C file (ddp_compress.h) with:

  • RLEDecoder struct: streaming decoder, operates directly on the received
    packet buffer, no heap allocation
  • rle_encode(): PackBits encoder with caller-provided output buffer
  • rle_encode_adaptive(): tries delta+RLE and RLE-only, returns the smaller

The receiver integration is in handleDDPPacket() — guarded by
#ifdef WLED_ENABLE_DDP_COMPRESSION so it's zero-cost when disabled.

I also wrote Python tools for testing:

  • ddp_bench.py: benchmark tool with raw vs compressed comparison, IFS fractal
    renderer, per-segment targeting, RGBW support
  • ddp_codec.py: standalone codec library for senders
  • ifs_ddp.py: IFS fractal → DDP streamer with auto MTU detection

All of this is in my fork: https://github.com/aenertia/WLED/tree/dev/ppp-wifi

The relevant branches:


Use cases beyond serial

Once I had it working I realised the same approach applies to a few other
scenarios that might be of broader interest:

Low-baud serial — RS-485 runs, long cable runs at 115200 baud. Raw DDP
gives 4.7fps for 800 pixels. Compressed gives ~70fps. That's the difference
between "technically works" and "actually useful" for wired installations where
WiFi isn't an option.

LoRa and other RF — I haven't tested this personally, but the maths works
out. At 250kbps LoRa, raw DDP gives 10fps for 800 pixels; compressed gives
~150fps. Outdoor installations, agricultural lighting, distributed art
installations — anywhere WiFi doesn't reach. DDP is already UDP-based so
running it over a LoRa UDP bridge needs no firmware changes beyond enabling
compression.

WLED-to-WLED streaming — WLED already has realtimeBroadcast() for
WLED-to-WLED sync. The receiver side already handles compressed DDP. Adding
compression to the send path would reduce WiFi airtime for sync setups and
make large-pixel-count sync feasible on constrained devices. The encoder is
already there; it just needs wiring into the send path.

Capture and replay — this one's speculative but I think it's interesting.
The DDP header has a 4-byte timecode field (bytes 10–13, T flag in byte 0)
that WLED currently ignores. Combined with compression, it makes recording
DDP sequences to flash/SPIFFS feasible:

  • 10 seconds at 30fps, 800 pixels, raw: 720 KB — doesn't fit in 256KB SPIFFS
  • Same, compressed at 95%: 36 KB — fits easily

The timecode field enables frame-accurate replay: read the next frame's
timestamp, wait until millis() matches, render. No host required. This
would turn WLED into a standalone animation player for pre-recorded sequences —
useful for installations where a host PC isn't practical.

I haven't implemented the recorder/player yet — that would need the codec to
land first. But it's a natural next step if there's interest.


Relation to existing issues

A few upstream issues that this might be relevant to:

  • #5755 — DDP-over-WebSocket fragmented packets (bandwidth pressure)
  • #5412 — DDP out-of-sequence packets on WiFi (compression reduces packet count)
  • #4320 — UI unresponsive during DDP realtime (compression reduces CPU load from packet processing)

Also worth coordinating with PR #5774
(split udp.cpp into per-protocol files) since the receiver integration touches
the same file.


The ask

Is there appetite for something like this in WLED? I'm happy to:

  • Submit the codec header as a standalone PR (no WLED core changes, easy to review)
  • Follow up with the receiver integration if the codec lands
  • Adjust the wire format if there are concerns about the reserved-bit usage
  • Keep it as a fork-only thing if it's too niche

The codec is small and the receiver integration is guarded by a build flag, so
the cost of carrying it is low. But I understand if serial/LoRa use cases
aren't a priority for the project.


References

Lingua principale
C++
Stelle
18.8k
Fork
4.4k
Merge medio
2g 11h
PR unite (30g)
16

Preparare l'ambiente

Apri in Codespaces

Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di wled/WLED

Tutte le issue di wled/WLED

Issue simili

Altre issue su C++

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.