-
Notifications
You must be signed in to change notification settings - Fork 3
Wire Protocol Reference
The serial protocol between the daemon (host) and the dongle. All lines are \n-terminated. Lines beginning with { are JSON; other lines are space-separated text commands. The OTA hot-path text commands (flash_data, flash_recover, flash_ping) are recognized before the general parser.
| Command | Form | Meaning |
|---|---|---|
msync |
msync 0 <epoch_ms> |
Set the dongle's wall clock for cue scheduling. Sent on init and on bounce(). |
sync |
sync <ident> 0 1 |
Register a receiver in the dongle's poll list (forces TDMA poll start). |
forget |
forget <ident> |
Drop a receiver from the poll list. Scrubs queued commands too. |
scan |
scan <passes> <ch_start> <ch_end> |
Run an RF spectrum scan (250 ms – 1 s). |
433fire |
433fire >>BITS:N<< x |
Bit-bang BITS on the 433 MHz line, N repetitions. |
fire |
fire <ident> <position> |
Manual fire one cue on one BYH receiver. position is 0-based. |
startload |
startload <ident> <count> <showId> |
Begin loading a show on one receiver. |
showloadn |
showloadn <ident> <count> <t1_ms> <pos1> ... <repeat> |
Load up to 6 cues per line. Times in ms; positions 0-based. |
showload |
showload <ident> <t1> <p1> <t2> <p2> 2 |
Legacy 2-cue load. |
showstart |
showstart <ident> <epoch_ms> 0 <showId> <repeat> |
Tell receiver when to start firing. |
play |
play 0 |
Resume / begin firing. |
pause |
pause 0 |
Freeze receiver schedule. |
stop |
stop 0 |
Stop firing immediately. |
reset |
reset <ident> 0 |
Clear loaded show state on one receiver. |
rxcfg |
rxcfg <ident> or rxcfg <ident> fd <50..5000>
|
Request CONFIG_RESPONSE; optionally write fire duration first. |
The daemon sends a single JSON object containing any subset of these fields:
{
"led_brightness": 50,
"daemon_act": 1,
"web_act_state": 1,
"tx_active": 2,
"show_load_state": 1,
"show_run_state": 5,
"error_state": 0,
"arm_state": 1,
"receiver_timeout_ms": 30000,
"response_timeout_ms": 100,
"clock_sync_interval_ms": 2000,
"debug_mode": 0,
"rf_channel": 76,
"rf_system_id": 0
}Fields:
| Key | Type | Range / values | Effect |
|---|---|---|---|
led_brightness |
int | 1–100 | NeoPixel master brightness. |
daemon_act |
int | 0/1 | LED 0 state (daemon activity). |
web_act_state |
int | 0–7 | LED 1 state (browser activity). |
tx_active |
int | 0–3 | LED 2 state (TX activity). |
show_load_state |
int | 0–3 | LED 3 state. |
show_run_state |
int | 0–8 | LED 4 state — RUN_STATE enum. |
error_state |
int | 0–4 | LED 5 state — ERR_STATE enum. |
arm_state |
int | 0/1 | LED 6 state — pulses red when armed. |
receiver_timeout_ms |
int | 60000–21600000 | "No contact = offline" threshold. |
response_timeout_ms |
int | 5–500 | Per-command response timeout. |
clock_sync_interval_ms |
int | 50–30000 | Polling cadence; clamped before use. |
debug_mode |
int | 0/1 | Verbose logging on the dongle. |
rf_channel |
int | 0–125 | Hot-swap nRF24 channel. |
rf_system_id |
int | 0–255 | Address salt; reflashing receivers required. |
| Command | Form | Meaning |
|---|---|---|
flash_begin |
flash_begin <ident> <total_size> <total_chunks> <crc32_hex> <rate> |
Allocate Update partition. rate 0/1/2 = 250k/1M/2M. |
flash_data |
flash_data <idx> <hex> |
Send one 29-byte chunk (hex-encoded). |
flash_recover |
flash_recover <idx> <level> |
Escalate recovery: 0 REPLAY, 1 SOFT, 2 FULL. |
flash_ping |
flash_ping |
Liveness check; expects OP reply. |
flash_end |
flash_end |
Finalize and reboot the receiver. |
flash_abort |
flash_abort |
Abort the in-flight transfer. |
{
"timestamp": 1715680000123,
"q": 12,
"qmax": 128,
"fw": 16,
"ch": 76,
"csim": 2000,
"l": 4,
"receivers": [
{
"i": "RX146", "n": 146, "b": 88, "s": 42,
"l": 1, "r": 1, "t": 1715680000050,
"x": 4, "sp": 99.5,
"c": [18446744073709551615, 0],
"fw": 23, "bv": 9, "nb": 1, "nbd": false, "ca": 8, "fd": 1000
}
]
}| Key | Meaning |
|---|---|
timestamp |
Dongle's millis() at frame build time. |
q |
Command queue depth. |
qmax |
Command queue capacity (128). |
fw |
Dongle firmware version (currently 16). |
ch |
Active RF channel. |
csim |
Post-clamp clock-sync interval ms. |
l |
Aggregate average latency (ms) over recent samples. |
receivers[] |
Per-receiver telemetry, abbreviated keys (see below). |
| Abbrev | Field | Meaning |
|---|---|---|
i |
ident |
Receiver ident, e.g. RX146. |
n |
node |
NODE_ID. |
b |
battery |
Scaled 5..253. |
s |
showId |
Currently-loaded show id (lower 14 bits). |
l |
loadComplete |
bool. |
r |
startReady |
bool. |
t |
lmt |
Last contact time (dongle ms; host adjusts to wall ms). |
x |
lat |
Latency sample (ms). Absent when contact failed. |
sp |
successPercent |
Rolling success percentage. |
c |
continuity |
[u64, u64] — continuity bitmask, cues 0..63 / 64..127. |
fw |
fwVersion |
Receiver FW version (FW v22+). |
bv |
boardVersion |
Receiver PCB version. |
nb |
numBoards |
Detected cue board count. |
nbd |
noBoardsDetected |
True if board ladder reads ~0. |
ca |
cuesAvailable |
Physically-usable cues. |
fd |
fireDurationMs |
Active fire duration. |
Same shape as one element of receivers[], with "type": "rxupd". Emitted whenever the dongle gets new state from one receiver, without waiting for the next aggregate frame.
{ "type": "rxupd", "i": "RX146", "n": 146, "b": 88, ... }After rxcfg <ident> or rxcfg <ident> fd <ms>:
{
"type": "rxcfg",
"i": "RX146", "n": 146,
"fw": 23, "bv": 9, "nb": 1, "nbd": false,
"ca": 8, "fd": 1000,
"t": 1715680000050
}Persisted to Receivers.fw_version, board_version, cues_available, config_data by the daemon's _persist_rxcfg_to_db.
After scan <passes> <ch_start> <ch_end>:
{
"type": "scan_result",
"fw": 16,
"passes": 10,
"ch_start": 0,
"ch_end": 125,
"current_ch": 85,
"started_ms": 1715680000123,
"duration_ms": 487,
"results": [
{ "ch": 0, "hits": 0 },
{ "ch": 1, "hits": 1 },
...
]
}The host writes the full payload to /data/last_scan.json and a summary (recommended channel, peak, etc.) into /data/state for the UI.
When any switch changes (or every 10 s as a heartbeat):
{
"gpio": "gpio_status",
"start_stop": 1,
"armed": 0,
"man_fire": 1
}Values are raw digitalRead results: INPUT_PULLUP → HIGH (1) when open, LOW (0) when closed.
When the daemon sends {"type":"config_serial",...} to the bridge (not the dongle), it gets back:
{ "tcpstatus": true, "serial_config": { "port": "/dev/tty.usbmodem01", "baud": 115200 } }Or on failure:
{ "type": "config_response", "error": "Could not configure port: ..." }These are bridge-level messages, not dongle messages, but they look the same on the wire because the bridge transparently relays everything.
The dongle uses a mix of compact text and JSON for OTA progress. Compact lines minimize byte count for the high-frequency hot path:
| Line | Meaning |
|---|---|
OA <idx> <state> <bytes_received> <attempts> |
Chunk ACK. |
ON <idx> <rf_ok> <got_ack> <state> <err> <last> <bytes_received> <fatal> |
Chunk NACK. |
OS ... |
OTA heartbeat / throughput / phase update. |
OP |
Pong reply to flash_ping. |
Lifecycle JSON via emitOtaEvent:
{ "type": "ota", "phase": "begin_ok" }
{ "type": "ota", "phase": "end_sent" }
{ "type": "ota", "phase": "done" }
{ "type": "ota", "phase": "timeout", "msg": "..." }
{ "type": "ota", "phase": "aborted" }The host's OtaFlashDriver.feed_event handles both compact and JSON forms.
The dongle emits human-readable strings like:
-
"M+ (RF24 Master Hub Online v4: ACK-payloads)"on boot. -
"C+ msync 1715680000123 ok"on successful command. -
"C+ forget RX146"on receiver removal. -
"CV 433"on a malformed433fire. -
"C? rxcfg RX999 not in poll list". -
"ERR: Command queue full.". -
"WARN: ..."for soft errors. -
"INFO: ..."for general logging.
Most of these are diagnostic — the daemon ignores them but they're useful when watching the serial line directly during development.
- Daemon command reference — the JSON commands the daemon accepts from the API.
- Receiver firmware — the over-the-air message catalog (struct-packed binary).
- Dongle firmware — implementation details.
Getting started
- Overview
- Desktop installers (macOS / Windows)
- macOS
- Linux
- Windows
- Production vs Development
- Connecting the dongle
- Flash a receiver
- Flash a dongle
- OTA flashing
Raspberry Pi
System overview
Subsystems
Hardware
- Receiver firmware
- Dongle firmware
- RF protocol
- Contributor Portal — BOMs, schematics, and board resources
UI walkthrough
Reference
Downloads
- Firmware
- Installers
Module Build & User Guides
- Cue
- Receiver
- Dongle