From f259c5d554b283e585b29b43376e2aeb4e85d7d8 Mon Sep 17 00:00:00 2001 From: Ben Nicholson Date: Thu, 24 Sep 2026 23:28:55 +1000 Subject: [PATCH] Step 2b verified: OTA update, rejections and rollback on board Test on board (ESP32-P4 rev v1.3): - Rejected with 400 and nothing booted differently: garbage, wrong project, min chip rev v3.0, truncated image. - A (USB, ota_0) -> B via /api/ota: self-test passed, confirmed. - Manual rollback B -> A; A -> B again. - B -> C (forced self-test failure): C boots on trial in ota_0, fails, rolls back to B in ota_1. - D installed over the network; a later truncated upload hides 'previous' and clears can_rollback. Docs: network flash command, serial-reset caveat, rollback test build, OTA details in aes67-core-base.md. Co-Authored-By: Claude Opus 5.5 --- CLAUDE.md | 8 +++++++- docs/aes67-core-base.md | 5 +++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index bf4dcee..3ae477d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -26,6 +26,12 @@ Repo: https://gitea.apointless.space/bsncubed/aes67-ESP32-P4 Our board: **v1.3** (set in sdkconfig.defaults, min rev v1.0). - Our board: **32 MB** flash (GigaDevice c8/4019). App slots must stay below 16 MB (cache mapping above 16 MB is experimental in IDF). - Embed `web/index.html` via `EMBED_TXTFILES` in `aes67_web`. +- Flash over the network (normal way since step 2b; keep USB for recovery): + `curl -f --data-binary @build/aes67_p4.bin -H 'Content-Type: application/octet-stream' http://p4-aes67/api/ota` + The board reboots into the new image on trial; check `GET /api/ota` shows the new version with `pending_verify: false`. +- Serial: opening /dev/ttyACM0 can reset the board. Don't open it while an OTA image is on trial (a reset then counts as a failed boot and rolls back). +- Rollback test build (self-test always fails), in its own build dir: + `idf.py -B build-selftest-fail -DSDKCONFIG=build-selftest-fail/sdkconfig -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.selftest_fail" build` ## Board quick reference (full details in docs) - Ethernet: IP101GRI, RMII, PHY addr 1, ref clock from PHY into GPIO50 (EMAC_CLK_EXT_IN). @@ -38,7 +44,7 @@ Repo: https://gitea.apointless.space/bsncubed/aes67-ESP32-P4 - [x] 1. Ethernet: IP101 up, DHCP, IP logged. Ping works. - [x] 2. Web server + config store (cJSON in NVS) + embedded index.html; /api/config, /api/status (stub values), /api/reboot. - [x] 2a. Finding the device: mDNS (hostname.local + _http._tcp), then LLDP (switch shows name + IP). -- [ ] 2b. Firmware update: /api/ota upload + rollback self-test. Test: update to a new build, then deliberately flash a build that fails its self-test and confirm it rolls back. After this, flash over the network; keep USB for recovery. +- [x] 2b. Firmware update: /api/ota upload + rollback self-test. Test: update to a new build, then deliberately flash a build that fails its self-test and confirm it rolls back. After this, flash over the network; keep USB for recovery. - [ ] 3. PTP TimeReceiver: lock to an existing GM (Riedel), fill `status.ptp`. Confirm EMAC hardware timestamps work. First check whether the installed ESP-IDF has a PTP example/component for the P4 before writing one. - [ ] 4. AES67 TX with a 1 kHz test tone, PTP-paced; /stream.sdp. Verify: import SDP on a Riedel Artist 4-wire AES67 port, and check packets/timestamps in Wireshark. - [ ] 5. SAP discovery, then syslog, then health/temperatures, then VLAN split (one at a time). diff --git a/docs/aes67-core-base.md b/docs/aes67-core-base.md index e79780d..0240bed 100644 --- a/docs/aes67-core-base.md +++ b/docs/aes67-core-base.md @@ -108,8 +108,9 @@ When this device is the GM, offset/frequency/delay show "–". - POST /api/ota: raw .bin as `application/octet-stream`, streamed with esp_ota_begin/write/end into the inactive slot (never buffered whole in RAM). 200 then reboot; 4xx with a message if rejected. - Upload only, by design: the device never pulls firmware from a URL or git server (no esp_https_ota, no update checks). - POST /api/ota/confirm: mark the running app valid. POST /api/ota/rollback: boot the previous slot. -- Reject before writing: wrong `project_name` in the image's app description (stops flashing another project's .bin), and an image whose chip revision range doesn't match this chip (matters on the rev < 3 boards; esp_ota_end / image verify checks the header). Reject downgrades only if a flag is set (off by default). -- Rollback: enable `CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE`. New firmware boots in "pending verify". Self-test after boot: Ethernet has an IP and the web server answers (optionally PTP locked within N seconds). Pass: `esp_ota_mark_app_valid_cancel_rollback()` automatically. Fail or crash before that: reboot, and the bootloader rolls back to the previous slot. The UI also offers manual confirm/rollback while pending. +- Reject before writing (checked on the first chunk): image magic, chip ID, wrong `project_name` in the image's app description (stops flashing another project's .bin), and a chip revision outside the image's min/max range (matters on the rev < 3 boards). esp_ota_end then verifies the whole image. Reject downgrades only if a flag is set (off by default; the flag doesn't exist yet). +- `previous`/`previous_version` are only reported when that slot is bootable (`esp_ota_check_rollback_is_possible()`), so a half-written upload or a rolled-back image is never offered. +- Rollback: enable `CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE`. New firmware boots in "pending verify". Self-test after boot (60 s limit): Ethernet has an IP and the web server answers (an HTTP GET of its own /api/status returns 200; optionally PTP locked within N seconds later). Test-only `CONFIG_AES67_OTA_SELFTEST_FORCE_FAIL` (sdkconfig.selftest_fail) forces a failure to test rollback. Pass: `esp_ota_mark_app_valid_cancel_rollback()` automatically. Fail or crash before that: reboot, and the bootloader rolls back to the previous slot. The UI also offers manual confirm/rollback while pending. - Keep AES67 TX running during the upload if possible (OTA writes are slow flash erases; run them at lower priority than PTP/TX). Expect short audio glitches; the reboot drops the stream for a few seconds. - Security: no auth yet (LAN only), like the rest of the API. Add a bearer token and optionally signed images (secure boot v2 / signed OTA) before exposing the device on an untrusted network. - UI: Firmware section: upload with progress bar, waits for the reboot and reports the new version (or that it rolled back), confirm/rollback buttons while pending.