-
Notifications
You must be signed in to change notification settings - Fork 3
OTA Flashing
Once a receiver is online and reachable from the dongle, you can update its firmware over the air instead of plugging in a USB cable. This is the right tool when:
- Receivers are mounted in their enclosures and inconvenient to open.
- You're at a show site without a laptop and serial cable.
- You're updating an entire fleet at once and don't want to handle each unit.
The OTA path uses the same os4_receiver_v<N>.bin artifact that USB flashing uses (the app partition only, written at offset 0x10000). NVS — and therefore the receiver's NODE_ID and ident — is preserved.
- Brand-new chips. OTA needs working firmware on the receiver to negotiate the transfer. A virgin chip needs an initial USB flash; afterwards it can be OTA'd indefinitely.
-
Bricked receivers. If a receiver is in a bootloop, the radio never comes up, and OTA can't reach it. Recover via USB (
flash_receiver.py --full). - Bilusocn 433 MHz receivers. They're TX-only one-way devices; the dongle can't talk back.
OTA lives in the web UI:
- Open the Settings page → Debug tab.
- Find the OTA Flash panel.
- Pick the target receiver from the dropdown (only online
BKYD_TS_24_1receivers are listed). - Click Choose firmware and pick a
.binfile from yourdevices/os4_receiver/bin/directory (typicallyos4_receiver_v<N>.binorlatest.bin). - Optionally pick an OTA data rate (see below).
- Click Start flash.
The progress bar streams live updates from the daemon. A typical 340 KB receiver image takes ~30–60 s at 2 Mbps.
Sequence:
- The UI uploads the
.binto/tmp/ota_staging/<filename>inside the container (via thePOST /api/system/ota_flashroute). - The daemon's
OtaFlashDriverreads the entire image into memory and computes its CRC32. - The daemon issues a
flash_begincommand to the dongle:flash_begin <ident> <total_size> <total_chunks> <crc32_hex> <rate>. - The dongle relays an
OTA_BEGINframe to the target receiver over nRF24. The receiver allocates anUpdate.begin()write region, switches to the requested data rate, and repliesbegin_okvia ACK payload. - For each 29-byte chunk:
flash_data <idx> <hex>→ dongle relaysOTA_DATA→ receiver writes to its OTA partition → ACK payload reports state. Strict ACK gating — chunk N+1 isn't sent until chunk N is acked. - After the last chunk:
flash_end→ receiver doesUpdate.end(true)and reboots into the new image. - The dongle pings the receiver until it sees a
RECEIVER_STATUSfrom the new firmware (rejoin window, 30 s timeout).
If a chunk nacks, the driver retries up to 12 times with backoff. After 3, 6, and 9 failures, it escalates flash_recover (REPLAY → SOFT → FULL) to recover the radio link.
The dongle and receiver normally run at 250 kbps (RF24's most reliable rate). For OTA, you can pick a higher rate:
| Rate flag | Speed | When to use |
|---|---|---|
0 |
250 kbps | Slowest, most reliable. Use if you're on a noisy channel or far away. |
1 |
1 Mbps | A reasonable balance. |
2 |
2 Mbps (default) | Fastest. Works fine in normal conditions and at typical operating distances. |
After OTA finishes, the link returns to 250 kbps automatically.
The daemon refuses to start an OTA if any of these are true:
- A show is currently loaded.
- The system is armed.
- The target receiver isn't currently online.
- The target is
BILUSOCN_433_TX_ONLY. - The image file can't be read.
So in practice: unload any staged show first, disarm the box, then start the OTA.
The progress bar in the UI shows:
- Current chunk / total chunks.
- Bytes transferred / total bytes.
- Throughput (KB/s).
- Current phase (
begin,streaming,end,rejoin,done).
If you see lots of retries or the rate drops, the channel is congested or the link is marginal. Either move the receiver, switch RF channels (Settings → Debug → RF Scan finds clean channels), or step the rate down.
Click Abort in the OTA panel. The driver sends flash_abort, the dongle relays OTA_ABORT, the receiver tears down its Update partition, and the radio returns to 250 kbps. The receiver remains on the previous firmware — OTA never overwrites the running image; only the inactive OTA slot is written, and the activation only happens on flash_end.
After a successful flash_end, the dongle waits up to 30 seconds for the receiver to re-emit a RECEIVER_STATUS from the new firmware. If that doesn't happen, the OTA reports error / timeout. Recovery options:
-
Power-cycle the receiver. A reboot usually clears it. The receiver should come up on the new firmware (because
Update.end(true)already swapped the active partition). -
Re-flash over USB.
flash_receiver.py(no flag) writes the same artifact and resetsboot_app0toapp0. -
Worst case, full recovery.
flash_receiver.py --full. This always works on a chip that isn't physically broken.
- Driver:
host/pythings/pc_daemon/protocol_handler/OtaFlashDriver.py - Dongle wire format: see Wire protocol reference
- Receiver-side handling: see Receiver firmware → OTA
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