Skip to content

FirmwareUpdateModule

Surfaces OTA firmware flash progress as live display controls in the frontend. Auto-created on every boot if absent; can be deleted and will be restored on next reboot.


What it does

FirmwareUpdateModule does not perform the upload itself — that is handled by two HTTP routes in AppRoutes.cpp:

  • POST /api/firmware — receives a raw binary from the browser file picker and writes it chunk-by-chunk to the OTA partition without buffering in RAM.
  • POST /api/firmware/url — fetches a firmware binary from a URL and writes it to the OTA partition in the background (ESP32 only; returns 501 on PC).

Both routes write progress into two shared globals (g_otaStatus, g_otaPct). FirmwareUpdateModule.loop() copies those globals into its controls each tick, so the WebSocket state push picks up the change and the frontend updates without polling.

After a successful flash the device reboots automatically (600 ms delay, so the browser receives the final response first).


Controls

Key Type Description
update_status display Status string: idle, flashing, done - rebooting, or error: <reason>
update_pct progress Upload progress 0-100%

Both controls are read-only. They update live via WebSocket during an active flash.


How to flash new firmware

From the browser (file upload)

  1. Build the firmware binary for your target (e.g. uv run deploy/build.py -target esp32dev). The binary is written to deploy/build/esp32dev/.pio/build/esp32dev/firmware.bin.
  2. Open the device in the browser.
  3. Find the FirmwareUpdate module card. Click the file picker and select firmware.bin.
  4. Watch update_status change to flashing and update_pct count up to 100%.
  5. The device reboots automatically when done. The browser reconnects once it comes back up.

From a URL (ESP32 only)

Send a POST request with the firmware URL:

curl -X POST http://<device-ip>/api/firmware/url \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com/firmware.bin"}'

The device fetches and flashes in the background and returns HTTP 202 immediately. Monitor progress via the update_status and update_pct controls.

Via the deploy pipeline

uv run deploy/flash.py   # flashes all devices marked test:true in deploy/devicelist.json

This uses the PlatformIO upload mechanism over USB serial, bypassing the HTTP OTA route entirely.


Platform notes

ESP32: URL-based OTA (POST /api/firmware/url) is supported. File upload OTA (POST /api/firmware) is also supported. After a successful flash, the device reboots into the new firmware.

PC: URL-based OTA returns HTTP 501 (not supported). File upload OTA compiles and links but the underlying pal::ota_begin / pal::ota_write / pal::ota_end are no-ops; this is intended for development builds only.


Source