Skip to content

Commit 6e015c4

Browse files
authored
fix: provision.py esptool v5 + refuse partial NVS flashes (#391) (#392)
* fix: provision.py esptool v5 syntax + refuse partial NVS flashes (#391) Bug 1: `write_flash` -> `write-flash` for esptool v5.x compat - Actual flash command (flash_nvs, line 153) was already fixed - Dry-run manual-flash hint (line 301) still printed old syntax Bug 2: Refuse partial invocations that would silently wipe NVS - provision.py flashes a fresh NVS binary at offset 0x9000, which REPLACES the entire csi_cfg namespace. Any key not passed on the CLI is erased. - Previously: `provision.py --port COM8 --target-port 5005` would silently wipe ssid, password, target_ip, node_id, etc., causing "Retrying WiFi connection (10/10)" in the field. - Now: refuse unless all of --ssid/--password/--target-ip provided, or --force-partial is set (prints warning listing wiped keys). Validation: - Dry-run: binary generates to 24576 bytes, hint uses write-flash - Safety check: partial invocation rejected with clear message - Force-partial: warning lists keys that will be wiped - Hardware: esptool v5.1.0 `read-flash 0x9000 0x100` works on attached ESP32-S3 (COM8); NVS preserved, device reconnected at 192.168.1.104 with node_id=2 intact after reset. Co-Authored-By: claude-flow <ruv@ruv.net> * docs: CHANGELOG catch-up for v0.5.5, v0.6.0, v0.7.0 (#367) The changelog was stale at v0.5.4 — three releases were cut without updating it. Added full entries for each, plus an [Unreleased] block for the #391 provision.py fixes. version.txt correctly stays at 0.6.0 — v0.7.0 was a model/pipeline release, not a new firmware binary. Latest firmware is v0.6.0-esp32. Closes #367 Co-Authored-By: claude-flow <ruv@ruv.net>
1 parent 2a05378 commit 6e015c4

File tree

2 files changed

+98
-3
lines changed

2 files changed

+98
-3
lines changed

CHANGELOG.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,64 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
10+
### Fixed
11+
- **`provision.py` esptool v5 compat** (#391) — Stale `write_flash` (underscore) syntax in the dry-run manual-flash hint now uses `write-flash` (hyphenated) for esptool >= 5.x. The primary flash command was already correct.
12+
- **`provision.py` silent NVS wipe** (#391) — The script replaces the entire `csi_cfg` NVS namespace on every run, so partial invocations were silently erasing WiFi credentials and causing `Retrying WiFi connection (10/10)` in the field. Now refuses to run without `--ssid`, `--password`, and `--target-ip` unless `--force-partial` is passed. `--force-partial` prints a warning listing which keys will be wiped.
13+
14+
### Docs
15+
- **CHANGELOG catch-up** (#367) — Added missing entries for v0.5.5, v0.6.0, and v0.7.0 releases.
16+
17+
## [v0.7.0] — 2026-04-06
18+
19+
Model release (no new firmware binary). Firmware remains at v0.6.0-esp32.
20+
21+
### Added
22+
- **Camera ground-truth training pipeline (ADR-079)** — End-to-end supervised WiFlow pose training using MediaPipe + real ESP32 CSI.
23+
- `scripts/collect-ground-truth.py` — MediaPipe PoseLandmarker webcam capture (17 COCO keypoints, 30fps), synchronized with CSI recording over nanosecond timestamps.
24+
- `scripts/align-ground-truth.js` — Time-aligns camera keypoints with 20-frame CSI windows by binary search, confidence-weighted averaging.
25+
- `scripts/train-wiflow-supervised.js` — 3-phase curriculum training (contrastive → supervised SmoothL1 → bone/temporal refinement) with 4 scale presets (lite/small/medium/full).
26+
- `scripts/eval-wiflow.js` — PCK@10/20/50, MPJPE, per-joint breakdown, baseline proxy mode.
27+
- `scripts/record-csi-udp.py` — Lightweight ESP32 CSI UDP recorder (no Rust build required).
28+
- **ruvector optimizations (O6-O10)** — Subcarrier selection (70→35, 50% reduction), attention-weighted subcarriers, Stoer-Wagner min-cut person separation, multi-SPSA gradient estimation, Mac M4 Pro training via Tailscale.
29+
- **Scalable WiFlow presets**`lite` (189K params, ~19 min) through `full` (7.7M params, ~8 hrs) to match dataset size.
30+
- **Pre-trained WiFlow v1 model** — 92.9% PCK@20, 974 KB, 186,946 params. Published to [HuggingFace](https://huggingface.co/ruv/ruview) under `wiflow-v1/`.
31+
32+
### Validated
33+
- **92.9% PCK@20** pose accuracy from a 5-minute data collection session with one $9 ESP32-S3 and one laptop webcam.
34+
- Training pipeline validated on real paired data: 345 samples, 19 min training, eval loss 0.082, bone constraint 0.008.
35+
36+
## [v0.6.0-esp32] — 2026-04-03
37+
38+
### Added
39+
- **Pre-trained CSI sensing weights published** — First official pre-trained models on [HuggingFace](https://huggingface.co/ruv/ruview). `model.safetensors` (48 KB), `model-q4.bin` (8 KB 4-bit), `model-q2.bin` (4 KB), `presence-head.json`, per-node LoRA adapters.
40+
- **17 sensing applications** — Sleep monitor, apnea detector, stress monitor, gait analyzer, RF tomography, passive radar, material classifier, through-wall detector, device fingerprint, and more. Each as a standalone `scripts/*.js`.
41+
- **ADRs 069-078** — 10 new architecture decisions covering Cognitum Seed integration, self-supervised pretraining, ruvllm pipeline, WiFlow architecture, channel hopping, SNN, MinCut person separation, CNN spectrograms, novel RF applications, multi-frequency mesh.
42+
- **Kalman tracker** (PR #341 by @taylorjdawson) — temporal smoothing of pose keypoints.
43+
44+
### Fixed
45+
- Security fix merged via PR #310.
46+
47+
### Performance
48+
- Presence detection: 100% accuracy on 60,630 overnight samples.
49+
- Inference: 0.008 ms per sample, 164K embeddings/sec.
50+
- Contrastive self-supervised training: 51.6% improvement over baseline.
51+
52+
## [v0.5.5-esp32] — 2026-04-03
53+
54+
### Added
55+
- **WiFlow SOTA architecture (ADR-072)** — TCN + axial attention pose decoder, 1.8M params, 881 KB at 4-bit. 17 COCO keypoints from CSI amplitude only (no phase).
56+
- **Multi-frequency mesh scanning (ADR-073)** — ESP32 nodes hop across channels 1/3/5/6/9/11 at 200ms dwell. Neighbor WiFi networks used as passive radar illuminators. Null subcarriers reduced from 19% to 16%.
57+
- **Spiking neural network (ADR-074)** — STDP online learning, adapts to new rooms in <30s with no labels, 16-160x less compute than batch training.
58+
- **MinCut person counting (ADR-075)** — Stoer-Wagner min-cut on subcarrier correlation graph. Fixes #348 (was always reporting 4 people).
59+
- **CNN spectrogram embeddings (ADR-076)** — Treat 64×20 CSI as an image, produce 128-dim environment fingerprints (0.95+ same-room similarity).
60+
- **Graph transformer fusion** — Multi-node CSI fusion via GATv2 attention (replaces naive averaging).
61+
- **Camera-free pose training pipeline** — Trains 17-keypoint model from 10 sensor signals with no camera required.
62+
63+
### Fixed
64+
- **#348 person counting** — MinCut correctly counts 1-4 people (24/24 validation windows).
65+
866
## [v0.5.4-esp32] — 2026-04-02
967

1068
### Added

firmware/esp32-csi-node/provision.py

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,13 @@
99
python provision.py --port COM7 --ssid "MyWiFi" --password "secret" --target-ip 192.168.1.20
1010
1111
Requirements:
12-
pip install esptool nvs-partition-gen
12+
pip install 'esptool>=5.0' nvs-partition-gen
1313
(or use the nvs_partition_gen.py bundled with ESP-IDF)
14+
15+
WARNING -- FULL-REPLACE SEMANTICS (issue #391):
16+
Every invocation REPLACES the entire `csi_cfg` NVS namespace on the device.
17+
Any key you don't pass on the CLI is erased. Always include WiFi credentials
18+
(--ssid, --password, --target-ip) unless you pass --force-partial.
1419
"""
1520

1621
import argparse
@@ -150,7 +155,7 @@ def flash_nvs(port, baud, nvs_bin):
150155
"--chip", "esp32s3",
151156
"--port", port,
152157
"--baud", str(baud),
153-
"write_flash",
158+
"write-flash",
154159
hex(NVS_PARTITION_OFFSET), bin_path,
155160
]
156161
print(f"Flashing NVS partition ({len(nvs_bin)} bytes) to {port}...")
@@ -199,6 +204,10 @@ def main():
199204
parser.add_argument("--swarm-hb", type=int, help="Swarm heartbeat interval in seconds (default 30)")
200205
parser.add_argument("--swarm-ingest", type=int, help="Swarm vector ingest interval in seconds (default 5)")
201206
parser.add_argument("--dry-run", action="store_true", help="Generate NVS binary but don't flash")
207+
parser.add_argument("--force-partial", action="store_true",
208+
help="Allow partial config without WiFi credentials. "
209+
"WARNING: flashing REPLACES the entire csi_cfg NVS namespace - "
210+
"any key not passed on the CLI will be erased (issue #391).")
202211

203212
args = parser.parse_args()
204213

@@ -215,6 +224,34 @@ def main():
215224
if not has_value:
216225
parser.error("At least one config value must be specified")
217226

227+
# Bug 2 (#391): Prevent silent wipe of WiFi credentials on partial invocations.
228+
# Flashing the generated NVS binary to offset 0x9000 REPLACES the entire
229+
# csi_cfg namespace — there is no merge with existing NVS. Require the full
230+
# WiFi trio unless the user explicitly opts in with --force-partial.
231+
wifi_trio_missing = [
232+
name for name, val in [
233+
("--ssid", args.ssid),
234+
("--password", args.password),
235+
("--target-ip", args.target_ip),
236+
] if val is None or val == ""
237+
]
238+
if wifi_trio_missing and not args.force_partial:
239+
parser.error(
240+
f"Missing required WiFi credentials: {', '.join(wifi_trio_missing)}.\n"
241+
f"\n"
242+
f" provision.py REPLACES the entire csi_cfg NVS namespace on each run.\n"
243+
f" Any key not passed on the CLI will be erased -- including WiFi creds.\n"
244+
f"\n"
245+
f" Either pass all of --ssid, --password, --target-ip,\n"
246+
f" or add --force-partial to acknowledge that other NVS keys will be wiped."
247+
)
248+
if args.force_partial and wifi_trio_missing:
249+
print("WARNING: --force-partial is set. The following NVS keys will be WIPED "
250+
"(not present in this invocation):", file=sys.stderr)
251+
for k in wifi_trio_missing:
252+
print(f" - {k.lstrip('-')}", file=sys.stderr)
253+
print(" Plus any other csi_cfg keys not passed on the CLI.\n", file=sys.stderr)
254+
218255
# Validate TDM: if one is given, both should be
219256
if (args.tdm_slot is not None) != (args.tdm_total is not None):
220257
parser.error("--tdm-slot and --tdm-total must be specified together")
@@ -298,7 +335,7 @@ def main():
298335
f.write(nvs_bin)
299336
print(f"NVS binary saved to {out} ({len(nvs_bin)} bytes)")
300337
print(f"Flash manually: python -m esptool --chip esp32s3 --port {args.port} "
301-
f"write_flash 0x9000 {out}")
338+
f"write-flash 0x9000 {out}")
302339
return
303340

304341
flash_nvs(args.port, args.baud, nvs_bin)

0 commit comments

Comments
 (0)