v0.6.2-esp32

google/comprehensive-rustv0.6.2-esp32Apr 20, 2026by ruvnet

AI Summary

Introduces the ADR-081 Adaptive CSI Mesh Kernel with a 5-layer architecture and replaces raw CSI with a 60-byte packet format to reduce bandwidth. Includes a critical fix for the Timer Service stack overflow.

Key Highlights

  • 5-layer adaptive CSI kernel (Radio Abstraction → Adaptive Controller → Mesh Plane → Feature State → Rust handoff)
  • ~99.7% bandwidth reduction via 60-byte `rv_feature_state_t` packets (magic `0xC5110006`)
  • Mesh sensing plane with 4 roles, 7 message types, and CRC32 integrity
  • Host-testable pure decision policy with 33/33 assertions
  • Added 4 MB ESP32-S3 SuperMini variant to CI builds

Breaking Changes

  • Wire format default changed to `rv_feature_state_t` (magic `0xC5110006`); raw ADR-018 CSI remains available as a debug stream gated by the channel plan.

New Features

  • ADR-081 Adaptive CSI Mesh Kernel
  • 60-byte feature state packets
  • Mesh sensing plane (HEALTH/ANOMALY_ALERT)
  • Timer Service stack overflow fix
  • 4 MB variant support

Full Release Notes

First firmware release of the **ADR-081 Adaptive CSI Mesh Kernel**, plus the Timer Service stack-overflow regression fix caught by on-device validation. Ships both 8 MB and 4 MB variants from CI.

## Highlights

- **5-layer adaptive CSI kernel** (Radio Abstraction → Adaptive Controller → Mesh Plane → Feature State → Rust handoff) — swap radio silicon without touching the Rust signal/ruvector/mat crates
- **60-byte `rv_feature_state_t` packet** (magic `0xC5110006`) replaces raw CSI as the default upstream — **~99.7 % bandwidth reduction** (300 B/s at 5 Hz vs ~100 KB/s raw)
- **Mesh sensing plane** (`rv_mesh`) with 4 roles, 7 message types, CRC32-integrity envelope — HEALTH every 30 s, ANOMALY_ALERT on state transitions
- **Host-testable pure decision policy** (`adaptive_controller_decide.c`) — **33/33 assertions**, `decide()` at 3.2 ns/call
- **4 MB variant** (ESP32-S3 SuperMini) now built in CI alongside the 8 MB default

## Validated on real hardware

ESP32-S3 QFN56 rev v0.2 (MAC `3c:0f:02:e9:b5:f8`), 38 s continuous run after flash:

- `App version: 0.6.2` ✅
- **0** stack-overflow / panic / reboot markers
- **28** `adaptive_ctrl` medium ticks, steady state, no drift
- **149** `rv_feature_state_t` emissions at ~5 Hz on earlier 30 s validation
- Real WiFi CSI: 37 – 72 pps yield, RSSI -43 to -57 dBm

## Fixes

| Fix | Detail |
| --- | --- |
| Timer Svc stack overflow (new) | `emit_feature_state()` + `stream_sender` network I/O runs inside FreeRTOS Timer Svc; ESP-IDF default 2 KiB stack overflows ~1 s after boot. Bumped `CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=8192` in all three `sdkconfig.defaults*`. Follow-up: move heavy work out of the timer daemon. |
| `adaptive_controller.c` implicit decl (#404) | `fast_loop_cb` called `emit_feature_state()` before its static definition, tripping `-Werror=implicit-function-declaration`. Added forward declaration. |
| `provision.py` esptool v5 compat (#391) | `write-flash` (hyphenated) in dry-run hint for esptool ≥ 5. |
| `provision.py` silent NVS wipe (#391) | Now refuses to run without `--ssid`, `--password`, `--target-ip` unless `--force-partial` is passed; `--force-partial` prints which keys will be wiped. |
| Defensive `node_id` capture (#232, #375, #385, #386, #390) | `csi_collector_init()` now captures `g_nvs_config.node_id` once into a module-local; a canary WARN fires on divergence. |

## CI change

`firmware-ci.yml` is now a matrix job building both variants in parallel and uploading variant-named artifacts — 6-binary releases no longer need local ESP-IDF.

## Installing

Six binaries are attached below. The default flash layout for **ESP32-S3 8 MB**:

```bash
esptool.py --chip esp32s3 --port /dev/ttyUSB0 --baud 460800 \
  --before default_reset --after hard_reset \
  write_flash -z --flash_mode dio --flash_freq 80m --flash_size 8MB \
  0x0      bootloader.bin \
  0x8000   partition-table.bin \
  0xf000   ota_data_initial.bin \
  0x20000  esp32-csi-node.bin
```

For **ESP32-S3 SuperMini 4 MB**, use `esp32-csi-node-4mb.bin` at `0x20000` and `partition-table-4mb.bin` at `0x8000` (reuse the same bootloader and OTA data).

After flash, provision WiFi + target (replaces the full `csi_cfg` NVS namespace — pass all creds at once):

```bash
python firmware/esp32-csi-node/provision.py \
  --port /dev/ttyUSB0 \
  --ssid <SSID> --password <PSK> --target-ip <DEST_IP>
```

## Artifacts

| File | Size | Target |
| --- | --- | --- |
| `esp32-csi-node.bin` | 1.06 MB | 8 MB app (flash @ `0x20000`) |
| `esp32-csi-node-4mb.bin` | 851 KB | 4 MB app (flash @ `0x20000`) |
| `bootloader.bin` | 18 KB | Both (flash @ `0x0`) |
| `partition-table.bin` | 3 KB | 8 MB layout (flash @ `0x8000`) |
| `partition-table-4mb.bin` | 3 KB | 4 MB layout (flash @ `0x8000`) |
| `ota_data_initial.bin` | 8 KB | OTA data (flash @ `0xf000`) |

## Compatibility

- **ESP-IDF:** v5.4
- **Target:** ESP32-S3 (Xtensa dual-core). **Not supported:** ESP32 (original), ESP32-C3 (single-core, can't run CSI DSP).
- **Supersedes:** [v0.6.1-esp32](../../releases/tag/v0.6.1-esp32)
- **Wire format:** `rv_feature_state_t` magic `0xC5110006` is the new default upstream; raw ADR-018 CSI (magic `0xC5110001`) remains available as a debug stream gated by the channel plan.

## Reference

- ADR-081 — Adaptive CSI Mesh Firmware Kernel (`docs/adr/ADR-081-adaptive-csi-mesh-firmware-kernel.md`)
- Full changelog entry: [`CHANGELOG.md#v062-esp32`](../../blob/v0.6.2-esp32/CHANGELOG.md)
- Source tag: [`ae40e2b`](../../commit/ae40e2b)