diff --git a/goTool/README.md b/goTool/README.md index dad1f68..591e978 100644 --- a/goTool/README.md +++ b/goTool/README.md @@ -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` | 16–19 | UART firmware upload to inactive OTA slot (200 B chunks, 4 KiB flash blocks) | +| `ota` | 16–19 | 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 diff --git a/main/README.md b/main/README.md index 30a6b97..0e19bdc 100644 --- a/main/README.md +++ b/main/README.md @@ -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 |