Document ESP-NOW slave OTA and dashboard upload flow.

Describe master-to-slave distribution after UART OTA, partial final block, reboot, and goTool /api/ota behaviour.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
simon 2026-05-19 01:02:45 +02:00
parent 9b7bda8551
commit 5a948a5c8c
2 changed files with 36 additions and 10 deletions

View File

@ -27,7 +27,7 @@ go run . -port /dev/ttyUSB0 clients
| `unicast-test` | `0x07` | Sends ESP-NOW unicast test to one slave (`-client`, `-seq`) |
| `test` | — | Run an automated scenario (JSON configs under `testdata/`) |
| `serve` | — | Web dashboard at `http://localhost:8080` (WebSocket live updates) |
| `ota` | 1619 | UART firmware upload to inactive OTA slot (200 B chunks, 4 KiB flash blocks) |
| `ota` | 1619 | UART firmware upload to master; firmware then pushes to slaves via ESP-NOW |
`clients` requires slaves to have responded to master discover broadcasts first.
@ -68,13 +68,18 @@ The dashboard can configure nodes using the same UART commands as the CLI:
| Alle Slaves | per-slave ESP-NOW (Master bleibt unverändert; CLI `-all` setzt auch den Master) |
| Unicast test | `unicast-test -client ID` |
HTTP API (used by the web UI): `GET/POST /api/deadzone`, `POST /api/unicast-test`.
HTTP API (used by the web UI): `GET/POST /api/deadzone`, `POST /api/unicast-test`, `POST /api/ota` (multipart field `firmware`, max 2 MiB).
| UI / API | Behaviour |
|----------|-----------|
| Firmware OTA card | Same as `ota` CLI; progress via WebSocket `ota_progress` events |
| `POST /api/ota` | Upload `.bin` to master only — slaves are updated by firmware over ESP-NOW after `OTA_END` |
```bash
go run . -port /dev/ttyUSB0 ota build/powerpod.bin
```
Waits for **ready** after start (~30 s erase), sends 200-byte `OTA_PAYLOAD` frames, reads **block_ack** every 4 KiB, then `OTA_END` and **success**.
Waits for **ready** after start (~30 s erase), sends 200-byte `OTA_PAYLOAD` frames, reads **block_ack** every 4 KiB, then `OTA_END`. The master then distributes to all available slaves (no extra host traffic); **success** is reported only when that finishes. Allow several minutes for large images. Reboot master and slaves to boot the new firmware.
```bash
go run . -port /dev/ttyUSB0 unicast-test -client 16 -seq 42

View File

@ -115,7 +115,19 @@ Schema: `proto/esp_now_messages.proto`. Encode/decode: `esp_now_proto.c`. The ES
| `ESPNOW_OTA_END` | Master → slave | `EspNowOtaEnd` |
| `ESPNOW_OTA_STATUS` | Slave → master | `EspNowOtaStatus` (same status codes as UART OTA) |
After a successful UART OTA on the master, `ota_espnow.c` reads the staged image from flash in **4 KiB** blocks (sent as **200 B** ESP-NOW chunks) and waits for **block_ack** from every available slave before continuing. Slaves use the same `ota_uart` 4 KiB buffer as UART OTA.
### ESP-NOW OTA (master → slaves)
Triggered automatically after a successful UART `OTA_END` on the master (or manually via UART `OTA_START_ESPNOW` if an image is already **staged**). Implementation: `ota_espnow.c`.
| Step | Master → slave | Slave → master |
|------|----------------|----------------|
| 1 | `ESPNOW_OTA_START` + `total_size` | `ESPNOW_OTA_STATUS` preparing, then **ready** |
| 2 | `ESPNOW_OTA_PAYLOAD` (**≤200 B**, shared `seq`) | **block_ack** after each **4096 B** written to flash |
| 3 | `ESPNOW_OTA_END` | **success** or **failed** (+ `bytes_written`) |
Master reads the staged partition with `esp_partition_read` (same image just written via UART). Only **available** registry slaves are updated. The last transfer block may be **under 4096 B** — no block_ack is waited for that block; slaves flush the remainder on `ESPNOW_OTA_END`.
Status codes match UART `OtaStatusPayload` (`1``5`). After success, master and slaves have the boot partition set — **reboot all nodes** to run the new firmware.
`EspNowSlavePresence`: `network`, `mac` (6 bytes), `version`, `slave_id`, `available`, `used`.
@ -226,15 +238,21 @@ At boot, firmware logs the running partition and OTA slot index (A/B).
### UART OTA (A/B)
Master only. Inactive app partition is selected with `esp_ota_get_next_update_partition()`; `esp_ota_begin` erases it (can take ~30 s — host should wait).
**UART upload is master-only.** Slaves receive the same image afterwards via [ESP-NOW OTA](#esp-now-ota-master--slaves).
| Step | Host → device | Device → host |
|------|----------------|---------------|
Inactive app partition is selected with `esp_ota_get_next_update_partition()`; `esp_ota_begin` erases it (can take ~30 s — host should wait).
| Step | Host → master | Master → host |
|------|---------------|---------------|
| 1 | `OTA_START` + `total_size` | `OTA_STATUS` preparing, then **ready** (+ `target_slot` 0/1) |
| 2 | `OTA_PAYLOAD` chunks (**≤200 B**, `seq` optional) | `OTA_STATUS` **block_ack** only after each **4096 B** written to flash |
| 3 | `OTA_END` | `OTA_STATUS` **success** or **failed** (+ `bytes_written`) |
| 3 | `OTA_END` | Stages image, runs ESP-NOW OTA to all available slaves, sets boot partition, then `OTA_STATUS` **success** or **failed** |
Implementation: `ota_uart.c` (4 KiB buffer, `esp_ota_write`), `cmd_ota.c`.
`OTA_END` can take a long time on the wire (slave flash + ESP-NOW); the host should use a generous read timeout.
`OTA_START_ESPNOW` (type `20`): re-run ESP-NOW distribution from the last staged image without a new UART upload (no-op if nothing staged).
Implementation: `ota_uart.c` (4 KiB buffer, `esp_ota_write`), `ota_espnow.c`, `cmd_ota.c`.
Host upload:
@ -340,7 +358,10 @@ Target: ESP32-S3. Close serial monitor on the UART adapter port before running `
| `powerpod.c` | `app_main`, DIP/network config, init order |
| `powerpod.h` | Pin defines |
| `app_config.h` | `app_config_t` |
| `esp_now_comm.c/h` | WiFi, ESP-NOW, discover / slave info |
| `esp_now_comm.c/h` | WiFi, ESP-NOW, discover / slave info / OTA send |
| `ota_uart.c/h` | Shared 4 KiB OTA flash buffer (UART + ESP-NOW) |
| `ota_espnow.c/h` | Master: distribute staged image to slaves |
| `cmd_ota.c/h` | UART OTA command handlers (master only) |
| `uart.c/h` | Framed UART RX/TX |
| `uart_proto.c/h` | Encode/send `UartMessage` |
| `cmd_handler.c/h` | Command queue and dispatch |