Beatled Protocol

Binary UDP protocol for communication between the server (Raspberry Pi 4) and LED controllers (Pico W, ESP32, or POSIX simulator).

Transport

ChannelPortDirectionDescription
UDP Unicast9090BidirectionalDevice ↔ Server commands
UDP Broadcast8765Server → DevicesBeat notifications
HTTPS8443Client → ServerWeb client REST API

All multi-byte fields are in network byte order (big-endian). All structs are packed (__attribute__((__packed__))).

Message Format

Every message starts with a 1-byte type header:

┌───────┐
│ type  │  uint8_t — beatled_message_type_t
└───────┘

Message Types

TypeValueDirectionTransport
ERROR0Server → DeviceUnicast
HELLO_REQUEST1Device → ServerUnicast
HELLO_RESPONSE2Server → DeviceUnicast
TEMPO_REQUEST3Device → ServerUnicast
TEMPO_RESPONSE4Server → DeviceUnicast
TIME_REQUEST5Device → ServerUnicast
TIME_RESPONSE6Server → DeviceUnicast
PROGRAM7Server → DeviceUnicast
NEXT_BEAT8Server → DeviceUnicast
BEAT9Server → DevicesBroadcast

ERROR (0)

Sent by the server when a request cannot be processed.

OffsetFieldTypeDescription
0typeuint8_t0
1error_codeuint8_tError code (see below)

Size: 2 bytes

Error codes:

CodeNameDescription
0UNKNOWNUnspecified error
1UNKNOWN_MESSAGE_TYPEUnrecognized message type
2NO_DATANo data available (e.g. no tempo detected yet)

HELLO_REQUEST (1)

Sent by a controller to register with the server. Contains the device’s unique board ID as a hex string.

OffsetFieldTypeDescription
0typeuint8_t1
1board_idchar[17]Null-terminated hex string (8 bytes × 2 hex chars + NUL)

Size: 18 bytes


HELLO_RESPONSE (2)

Sent by the server to acknowledge registration and assign a client ID.

OffsetFieldTypeDescription
0typeuint8_t2
1client_iduint16_tAssigned client identifier

Size: 3 bytes


TEMPO_REQUEST (3)

Sent by a device to request the current tempo state. Uses the base message header only (optionally includes stale beat reference and period, but these are ignored by the server).

OffsetFieldTypeDescription
0typeuint8_t3
1beat_time_refuint64_t(unused, may be zero)
9tempo_period_usuint32_t(unused, may be zero)

Size: 13 bytes


TEMPO_RESPONSE (4)

Server response with the current tempo state.

OffsetFieldTypeDescription
0typeuint8_t4
1beat_time_refuint64_tReference beat timestamp (microseconds since epoch)
9tempo_period_usuint32_tBeat period in microseconds
13program_iduint16_tActive LED program ID

Size: 15 bytes


TIME_REQUEST (5)

Sent by a device to initiate NTP-style clock synchronization. The device records its local time as orig_time before sending.

OffsetFieldTypeDescription
0typeuint8_t5
1orig_timeuint64_tDevice’s local time at send (microseconds)

Size: 9 bytes


TIME_RESPONSE (6)

Server response containing three timestamps for clock offset calculation.

OffsetFieldTypeDescription
0typeuint8_t6
1orig_timeuint64_tEchoed from request (device’s send time)
9recv_timeuint64_tServer’s time when request was received
17xmit_timeuint64_tServer’s time when response was sent

Size: 25 bytes

Clock Offset Calculation

Using the NTP symmetric algorithm:

T1 = orig_time    (device send time, device clock)
T2 = recv_time    (server receive time, server clock)
T3 = xmit_time    (server transmit time, server clock)
T4 = local time   (device receive time, device clock)

offset = ((T2 - T1) + (T3 - T4)) / 2

The offset is added to device local timestamps to convert them to server time. Multiple rounds of time sync are performed to improve accuracy.


PROGRAM (7)

Sent by the server to change the active LED program on a device.

OffsetFieldTypeDescription
0typeuint8_t7
1program_iduint16_tNew LED program ID

Size: 3 bytes


NEXT_BEAT (8)

Sent by the server (unicast) to each registered device before each beat. Devices use next_beat_time_ref to schedule LED updates at the precise moment.

OffsetFieldTypeDescription
0typeuint8_t8
1next_beat_time_refuint64_tPredicted time of next beat (microseconds, server clock)
9tempo_period_usuint32_tCurrent beat period in microseconds
13beat_countuint32_tRunning beat counter
17program_iduint16_tActive LED program ID

Size: 19 bytes


BEAT (9)

Broadcast to all devices when a beat is detected. Informational — devices primarily use NEXT_BEAT for timing.

OffsetFieldTypeDescription
0typeuint8_t9
1beat_time_refuint64_tTime of detected beat (microseconds, server clock)
9tempo_period_usuint32_tCurrent beat period in microseconds
13beat_countuint32_tRunning beat counter
17program_iduint16_tActive LED program ID

Size: 19 bytes


See Controller Registration and Synchronization for the full startup sequence and state machine walkthrough.