v0.6.5-esp32 — Tmr Svc bootloop fix + OTA fail-closed visibility
Two real bugs uncovered while preparing this release on real hardware (ESP32-S3 8MB, COM7). Both regressions from earlier cleanup work that this release locks back down. Plus one security re-validation.
What changed
1. Fix bootloop on fresh builds — sdkconfig.defaults was missing the Tmr Svc stack bump
sdkconfig.defaults.template had CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=8192 (ADR-081: adaptive_controller emits feature_state from inside Timer Svc callbacks, exceeding the FreeRTOS 2 KiB default). The line was absent from sdkconfig.defaults (the file the build actually reads), so any fresh full-clean build on top of v0.6.4 reproduced:
***ERROR*** A stack overflow in task Tmr Svc has been detected.
Backtrace: 0x40375d41:0x3fcbe060 0x4037eac9:0x3fcbe080 …
Rebooting...
Cherry-picked the line into sdkconfig.defaults. Verified on hardware: 0 panics, 0 reboots in 30 s of operation with --edge-tier 2.
2. Fix invisible OTA fail-closed posture — _ex init path didn't load the PSK
PR #623 merged the RuView#596 audit fix: ota_check_auth() now fails closed when no PSK is provisioned, refusing all OTA uploads until provision.py --ota-psk <hex> writes the NVS key.
But main.c:230 calls ota_update_init_ex(), not ota_update_init(). Pre-this-release, only ota_update_init() loaded the PSK from NVS. So in production:
s_ota_pskstayed empty regardless of NVS contents- The fail-closed check rejected every request (correct security posture), but
- The boot-time diagnostic warning never printed, leaving operators no signal about why their OTA pushes 403'd
Extracted ota_load_psk_from_nvs() and call it from both _init() and _init_ex(). Verified on hardware:
W (3126) ota_update: NVS namespace 'security' not found —
OTA upload endpoint will REJECT all requests until provisioned. Fail-closed per RuView#596.
3. Confirms #438 (boot loop at edge-tier ≥ 1) is not reproducible
Issue #438 was filed against v0.4.3.1. v0.6.5 boots cleanly at --edge-tier 2 with full vitals + edge DSP active. Anyone hitting that bug should upgrade to this release.
Hardware validation matrix
| Check | Result |
|---|---|
| 8MB binary builds | 1,109,744 bytes (47% partition free) |
| 4MB binary builds | 893,792 bytes (53% partition free) |
| Flash 8MB to COM7 | Hash verified, RC=0 |
| Boot at edge-tier=2 | Clean — Edge DSP task started on core 1 (tier=2)
|
| 30 s steady operation | 0 panics, 0 reboots, 870 log lines, CSI streaming |
| OTA fail-closed warning | Prints at boot 3.1 s after reset |
| Embedded version | App version: 0.6.5
|
| WiFi modem sleep | Set ps type: 0 (RuView#521 — WIFI_PS_NONE active)
|
| Adaptive calibration | Completed: mean=6.76 sigma=4.93 threshold=21.54 over 1200 frames
|
Cumulative changes since v0.6.4-esp32
In firmware/esp32-csi-node/:
release: ESP32-S3 firmware v0.6.5 — Tmr Svc stack + OTA load-PSK both init paths(this release)fix(firmware): OTA upload fails closed when no PSK in NVS (RuView#596 audit) (#623)fix(led): disable onboard WS2812 LED during CSI collection (#273)refactor(mmwave): use sizeof() in mr60_process_frame bounds checks (#414)firmware/esp32-csi-node: IDF 6 build, HE CSI config, unicore DSP, provision chip detect (#522)docs(firmware): truth-up Tier 2 wording — slot-capacity heuristic, not learned person counter (#573)fix: bug triage for #559, #561, #588 + CI fixes for fuzz/swarm tests (#590)
Upgrade notes
Breaking change for OTA users (from PR #623, now visible at boot): unprovisioned nodes refuse all OTA pushes. Before pushing firmware over the air to a fresh node, provision a PSK:
python firmware/esp32-csi-node/provision.py --port COM7 --ota-psk <64-hex-chars> --force-partialThe OTA HTTP server still starts so this provision.py (over USB-CDC) works without re-flashing.
Flashing
# 8MB variant
python -m esptool --chip esp32s3 --port COM7 --baud 460800 \
write_flash --flash_mode dio --flash_size 8MB \
0x0 bootloader.bin \
0x8000 partition-table.bin \
0xf000 ota_data_initial.bin \
0x20000 esp32-csi-node.bin
# 4MB variant (use partition-table-4mb.bin + esp32-csi-node-4mb.bin)
python -m esptool --chip esp32s3 --port COM7 --baud 460800 \
write_flash --flash_mode dio --flash_size 4MB \
0x0 bootloader.bin \
0x8000 partition-table-4mb.bin \
0xf000 ota_data_initial.bin \
0x20000 esp32-csi-node-4mb.bin