Files
led-controller/docs/espnow-binary-protocol.md
2026-05-28 00:38:21 +12:00

3.0 KiB
Raw Blame History

ESP-NOW binary protocol

See also: espnow-architecture.md (diagrams, flows, configuration).

All ESP-NOW datagrams and Pi↔bridge WebSocket frames use binary only (no JSON on the wire). Maximum ESP-NOW payload length: 250 bytes.

ESP-NOW packet

Offset Field
0 Magic 0x4C ('L')
1 Message type
2… Type-specific body

Message types

Value Name Direction
0x01 ANNOUNCE Driver → broadcast
0x02 GROUPS Controller → driver
0x03 CMD Controller → driver
0x04 GROUP_CMD Controller → broadcast
0x05 PING_REQ Controller → broadcast
0x06 PING_RSP Driver → controller (unicast)
0x10 BRIDGE_CH Controller → broadcast

ANNOUNCE (0x01)

Driver settings at boot. Sender MAC is taken from the ESP-NOW peer address (not repeated in the body).

Field Type
name_len u8
name UTF-8
num_leds u16 LE
color_order u8 enum: 0=rgb, 1=rbg, 2=grb, 3=gbr, 4=brg, 5=bgr
startup_mode u8: 0=default, 1=last, 2=off
brightness u8 0255
device_type u8: 0=led

GROUPS (0x02)

Field Type
count u8
× count u8 id_len + UTF-8 group id

CMD (0x03)

Bytes 2… are a v2 binary envelope (see src/util/binary_envelope.py): 5-byte header + presets/select/default blobs. Total packet ≤ 250 bytes.

GROUP_CMD (0x04)

Field Type
group_id_len u8
group_id UTF-8
cmd_envelope v2 binary envelope

Drivers apply the nested envelope only if group_id is in their stored group list.

PING_REQ (0x05)

Controller discovery ping (broadcast). Drivers reply with PING_RSP after a random delay (50500 ms) to reduce ESP-NOW collisions.

Field Type
ping_id u32 LE

PING_RSP (0x06)

Unicast to the bridge/controller peer that sent the request (ESP-NOW source MAC of the received PING_REQ).

Field Type
ping_id u32 LE
name_len u8
name UTF-8

BRIDGE_CH (0x10)

Field Type
channel u8 (111)

Sets the bridge ESP32 STA channel (not forwarded to LED drivers as a command).

Pi ↔ bridge WebSocket frame

Binary WebSocket messages only.

Offset Field
0 flags: bit0 = broadcast destination; bit1 reserved
16 peer MAC (6 bytes); ignored if broadcast
7… ESP-NOW packet (magic + type + body)

Broadcast destination uses peer ff:ff:ff:ff:ff:ff.

The bridge maintains at most 20 ESP-NOW peers (LRU eviction).

v2 command envelope

Native binary sections (no JSON). Header:

Byte Meaning
0 Version 2
1 Brightness wire 0127 (maps to 0255); 128255 = unchanged
2 Presets section length
3 Select section length
4 Default section length

See binary_envelope.py for blob layouts.