ruview @ v0.6.5-esp32
integrity
- size
- 52.7 MiB
- downloaded
- last checked
release notes
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-partial
The 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
download
curl -fL -o v0.6.5-esp32.zip https://ratatoskr.space/pkg/ruview/v0.6.5-esp32.zip
printf '%s %s\n' 'ba6be022f04f454ae77a4207c44260a938073617883e67ca552fff478effb762' 'v0.6.5-esp32.zip' | sha256sum -c -
$url = "https://ratatoskr.space/pkg/ruview/v0.6.5-esp32.zip"
$out = "v0.6.5-esp32.zip"
Invoke-WebRequest -Uri $url -OutFile $out
if ((Get-FileHash $out -Algorithm SHA256).Hash.ToLowerInvariant() -ne "ba6be022f04f454ae77a4207c44260a938073617883e67ca552fff478effb762") { throw "sha256 mismatch" }
curl -fL -o v0.6.5-esp32.tar.gz https://ratatoskr.space/pkg/ruview/v0.6.5-esp32.tar.gz
printf '%s %s\n' 'c2d91b4f15e9e6642c4dbfab14fd858a577bf870a25abffd7d5b9f5e241e0326' 'v0.6.5-esp32.tar.gz' | sha256sum -c -
$url = "https://ratatoskr.space/pkg/ruview/v0.6.5-esp32.tar.gz"
$out = "v0.6.5-esp32.tar.gz"
Invoke-WebRequest -Uri $url -OutFile $out
if ((Get-FileHash $out -Algorithm SHA256).Hash.ToLowerInvariant() -ne "c2d91b4f15e9e6642c4dbfab14fd858a577bf870a25abffd7d5b9f5e241e0326") { throw "sha256 mismatch" }
download via yggdrasil mesh
curl -fL -o v0.6.5-esp32.zip http://14cc7d57b5e70f679b851fe5b272ce17c70632ff4beb5b35ab64bc706b2485af.pk.ygg/pkg/ruview/v0.6.5-esp32.zip
printf '%s %s\n' 'ba6be022f04f454ae77a4207c44260a938073617883e67ca552fff478effb762' 'v0.6.5-esp32.zip' | sha256sum -c -
$url = "http://14cc7d57b5e70f679b851fe5b272ce17c70632ff4beb5b35ab64bc706b2485af.pk.ygg/pkg/ruview/v0.6.5-esp32.zip"
$out = "v0.6.5-esp32.zip"
Invoke-WebRequest -Uri $url -OutFile $out
if ((Get-FileHash $out -Algorithm SHA256).Hash.ToLowerInvariant() -ne "ba6be022f04f454ae77a4207c44260a938073617883e67ca552fff478effb762") { throw "sha256 mismatch" }
curl -fL -o v0.6.5-esp32.tar.gz http://14cc7d57b5e70f679b851fe5b272ce17c70632ff4beb5b35ab64bc706b2485af.pk.ygg/pkg/ruview/v0.6.5-esp32.tar.gz
printf '%s %s\n' 'c2d91b4f15e9e6642c4dbfab14fd858a577bf870a25abffd7d5b9f5e241e0326' 'v0.6.5-esp32.tar.gz' | sha256sum -c -
$url = "http://14cc7d57b5e70f679b851fe5b272ce17c70632ff4beb5b35ab64bc706b2485af.pk.ygg/pkg/ruview/v0.6.5-esp32.tar.gz"
$out = "v0.6.5-esp32.tar.gz"
Invoke-WebRequest -Uri $url -OutFile $out
if ((Get-FileHash $out -Algorithm SHA256).Hash.ToLowerInvariant() -ne "c2d91b4f15e9e6642c4dbfab14fd858a577bf870a25abffd7d5b9f5e241e0326") { throw "sha256 mismatch" }
| artifact | format | size | hashes |
|---|---|---|---|
| v0.6.5-esp32.zip | zip | 52.6 MiB |
blake3-24 94f7ee8e78c8a26d93b35f9e12d5034362ac7ca6f9cda975
sha256 ba6be022f04f454ae77a4207c44260a938073617883e67ca552fff478effb762
sha1 b81cc9e2e2d859f2c76c52ea7c3a52f5b0135c93
|
| v0.6.5-esp32.tar.gz | tar.gz | 51.3 MiB |
blake3-24 dee684dac6fd1bbe3a1cb4c594fc984bd10451c64b35dca8
sha256 c2d91b4f15e9e6642c4dbfab14fd858a577bf870a25abffd7d5b9f5e241e0326
sha1 75dcb42a5107e89f8d4007145fc2743383f41235
|
install
http_archive(
name = "ruview",
urls = ["https://ratatoskr.space/pkg/ruview/v0.6.5-esp32.tar.gz"],
integrity = "sha256-wtkbTxXp5mQsTb+rFP2Fild7+HCiWr/9fVufXiQeAyY=",
strip_prefix = "ruview-v0.6.5-esp32",
)
.url = "https://ratatoskr.space/pkg/ruview/v0.6.5-esp32.tar.gz",
install via yggdrasil mesh
http_archive(
name = "ruview",
urls = ["http://14cc7d57b5e70f679b851fe5b272ce17c70632ff4beb5b35ab64bc706b2485af.pk.ygg/pkg/ruview/v0.6.5-esp32.tar.gz"],
integrity = "sha256-wtkbTxXp5mQsTb+rFP2Fild7+HCiWr/9fVufXiQeAyY=",
strip_prefix = "ruview-v0.6.5-esp32",
)
.url = "http://14cc7d57b5e70f679b851fe5b272ce17c70632ff4beb5b35ab64bc706b2485af.pk.ygg/pkg/ruview/v0.6.5-esp32.tar.gz",