Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b544545

Browse files
committed
docs: ADR-044 provisioning tool enhancements
5-phase plan to close remaining gaps in provision.py: - Phase 1: 7 missing NVS keys (hop_count, chan_list, dwell_ms, power_duty, wasm_max, wasm_verify, wasm_pubkey) - Phase 2: JSON config file for mesh provisioning - Phase 3: Named presets (basic, vitals, mesh-3, mesh-6-vitals) - Phase 4: --read (dump NVS) and --verify (boot check) - Phase 5: Auto-detect serial port Co-Authored-By: claude-flow <[email protected]>
1 parent b6f7b8a commit b544545

1 file changed

Lines changed: 214 additions & 0 deletions

File tree

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
# ADR-044: Provisioning Tool Enhancements
2+
3+
**Status**: Proposed
4+
**Date**: 2026-03-03
5+
**Deciders**: @ruvnet
6+
**Supersedes**: None
7+
**Related**: ADR-029, ADR-032, ADR-039, ADR-040
8+
9+
---
10+
11+
## Context
12+
13+
The ESP32-S3 CSI node provisioning script (`firmware/esp32-csi-node/provision.py`) is the primary tool for configuring pre-built firmware binaries without recompiling. It writes NVS key-value pairs that the firmware reads at boot.
14+
15+
After #131 added TDM and edge intelligence flags, the script now covers the most-requested NVS keys. However, there remain gaps between what the firmware reads from NVS (`nvs_config.c`, 20 keys) and what the provisioning script can write (13 keys). Additionally, the script lacks usability features that would help field operators deploying multi-node meshes.
16+
17+
### Gap 1: Missing NVS Keys (7 keys)
18+
19+
The firmware reads these NVS keys at boot but the provisioning script has no corresponding CLI flags:
20+
21+
| NVS Key | Type | Firmware Default | Purpose |
22+
|---------|------|-----------------|---------|
23+
| `hop_count` | u8 | 1 (no hop) | Number of channels to hop through |
24+
| `chan_list` | blob (u8[6]) | {1,6,11} | Channel numbers for hopping sequence |
25+
| `dwell_ms` | u32 | 100 | Time to dwell on each channel before hopping (ms) |
26+
| `power_duty` | u8 | 100 | Power duty cycle percentage (10-100%) for battery life |
27+
| `wasm_max` | u8 | 4 | Max concurrent WASM modules (ADR-040) |
28+
| `wasm_verify` | u8 | 0 | Require Ed25519 signature for WASM uploads (0/1) |
29+
| `wasm_pubkey` | blob (32B) | zeros | Ed25519 public key for WASM signature verification |
30+
31+
### Gap 2: No Read-Back
32+
33+
There is no way to read the current NVS configuration from a device. Field operators must remember what was provisioned or reflash everything. This is especially problematic for multi-node meshes where each node has different TDM slots.
34+
35+
### Gap 3: No Verification
36+
37+
After flashing, there is no automated check that the device booted successfully with the new configuration. Operators must manually run a serial monitor and inspect logs.
38+
39+
### Gap 4: No Config File Support
40+
41+
Provisioning a 6-node mesh requires running the script 6 times with largely overlapping flags (same SSID, password, target IP) and only TDM slot varying. There is no way to define a mesh configuration in a file.
42+
43+
### Gap 5: No Presets
44+
45+
Common deployment scenarios (single-node basic, 3-node mesh, 6-node mesh with vitals) require operators to know which flags to combine. Named presets would lower the barrier to entry.
46+
47+
### Gap 6: No Auto-Detect
48+
49+
The `--port` flag is required even though the script could auto-detect connected ESP32-S3 devices via `esptool.py`.
50+
51+
---
52+
53+
## Decision
54+
55+
Enhance `provision.py` with the following capabilities, implemented incrementally.
56+
57+
### Phase 1: Complete NVS Coverage
58+
59+
Add flags for all remaining firmware NVS keys:
60+
61+
```
62+
--hop-count N Channel hop count (1=no hop, default: 1)
63+
--channels 1,6,11 Comma-separated channel list for hopping
64+
--dwell-ms N Dwell time per channel in ms (default: 100)
65+
--power-duty N Power duty cycle 10-100% (default: 100)
66+
--wasm-max N Max concurrent WASM modules 1-8 (default: 4)
67+
--wasm-verify Require Ed25519 signature for WASM uploads
68+
--wasm-pubkey FILE Path to Ed25519 public key file (32 bytes raw or PEM)
69+
```
70+
71+
Validation:
72+
- `--channels` length must match `--hop-count`
73+
- `--power-duty` clamped to 10-100
74+
- `--wasm-pubkey` implies `--wasm-verify`
75+
76+
### Phase 2: Config File and Mesh Provisioning
77+
78+
Add `--config FILE` to load settings from a JSON or TOML file:
79+
80+
```json
81+
{
82+
"common": {
83+
"ssid": "SensorNet",
84+
"password": "secret",
85+
"target_ip": "192.168.1.20",
86+
"target_port": 5005,
87+
"edge_tier": 2
88+
},
89+
"nodes": [
90+
{ "port": "COM7", "node_id": 0, "tdm_slot": 0 },
91+
{ "port": "COM8", "node_id": 1, "tdm_slot": 1 },
92+
{ "port": "COM9", "node_id": 2, "tdm_slot": 2 }
93+
]
94+
}
95+
```
96+
97+
`--config mesh.json` provisions all listed nodes in sequence, computing `tdm_total` automatically from the `nodes` array length.
98+
99+
### Phase 3: Presets
100+
101+
Add `--preset NAME` for common deployment profiles:
102+
103+
| Preset | What It Sets |
104+
|--------|-------------|
105+
| `basic` | Single node, edge_tier=0, no TDM, no hopping |
106+
| `vitals` | Single node, edge_tier=2, vital_int=1000, subk_count=32 |
107+
| `mesh-3` | 3-node TDM, edge_tier=1, hop_count=3, channels=1,6,11 |
108+
| `mesh-6-vitals` | 6-node TDM, edge_tier=2, hop_count=3, channels=1,6,11, vital_int=500 |
109+
110+
Presets set defaults that can be overridden by explicit flags.
111+
112+
### Phase 4: Read-Back and Verify
113+
114+
Add `--read` to dump the current NVS configuration from a connected device:
115+
116+
```bash
117+
python provision.py --port COM7 --read
118+
# Output:
119+
# ssid: SensorNet
120+
# target_ip: 192.168.1.20
121+
# tdm_slot: 0
122+
# tdm_nodes: 3
123+
# edge_tier: 2
124+
# ...
125+
```
126+
127+
Implementation: use `esptool.py read_flash` to read the NVS partition, then parse the NVS binary format to extract key-value pairs.
128+
129+
Add `--verify` to provision and then confirm the device booted:
130+
131+
```bash
132+
python provision.py --port COM7 --ssid "Net" --password "pass" --target-ip 192.168.1.20 --verify
133+
# After flash, opens serial monitor for 5 seconds
134+
# Checks for "CSI streaming active" log line
135+
# Reports PASS or FAIL
136+
```
137+
138+
### Phase 5: Auto-Detect Port
139+
140+
When `--port` is omitted, scan for connected ESP32-S3 devices:
141+
142+
```bash
143+
python provision.py --ssid "Net" --password "pass" --target-ip 192.168.1.20
144+
# Auto-detected ESP32-S3 on COM7 (Silicon Labs CP210x)
145+
# Proceed? [Y/n]
146+
```
147+
148+
Implementation: use `esptool.py` or `serial.tools.list_ports` to enumerate ports.
149+
150+
---
151+
152+
## Rationale
153+
154+
### Why incremental phases?
155+
156+
Phase 1 is a small diff that closes the NVS coverage gap immediately. Phases 2-5 add progressively more UX polish. Each phase is independently useful and can be shipped separately.
157+
158+
### Why JSON config over YAML/TOML?
159+
160+
JSON requires no additional Python dependencies (stdlib `json` module). TOML requires `tomllib` (Python 3.11+) or `tomli`. JSON is sufficient for this use case.
161+
162+
### Why not a GUI?
163+
164+
The target users are embedded developers and field operators who are already running `esptool` from the command line. A TUI/GUI would add dependencies and complexity for minimal benefit.
165+
166+
---
167+
168+
## Consequences
169+
170+
### Positive
171+
172+
- **Complete NVS coverage**: Every firmware-readable key can be set from the provisioning tool
173+
- **Mesh provisioning in one command**: `--config mesh.json` replaces 6 separate invocations
174+
- **Lower barrier to entry**: Presets eliminate the need to know which flags to combine
175+
- **Auditability**: `--read` lets operators inspect and verify deployed configurations
176+
- **Fewer mis-provisions**: `--verify` catches flashing failures before the operator walks away
177+
178+
### Negative
179+
180+
- **NVS binary parsing** (Phase 4) requires understanding the ESP-IDF NVS binary format, which is not officially documented as a stable API
181+
- **Auto-detect** (Phase 5) may produce false positives if other ESP32 variants are connected
182+
183+
### Risks
184+
185+
| Risk | Likelihood | Impact | Mitigation |
186+
|------|-----------|--------|------------|
187+
| NVS binary format changes in ESP-IDF v6 | Low | Medium | Pin to known ESP-IDF NVS page format; add format version check |
188+
| `--verify` serial parsing is fragile | Medium | Low | Match on stable log tag `[CSI_MAIN]`; timeout after 10s |
189+
| Config file credentials in plaintext | Medium | Medium | Document that config files should not be committed; add `.gitignore` pattern |
190+
191+
---
192+
193+
## Implementation Priority
194+
195+
| Phase | Effort | Impact | Priority |
196+
|-------|--------|--------|----------|
197+
| Phase 1: Complete NVS coverage | Small (1 file, ~50 lines) | High — closes feature gap | P0 |
198+
| Phase 2: Config file + mesh | Medium (~100 lines) | High — biggest UX win | P1 |
199+
| Phase 3: Presets | Small (~40 lines) | Medium — convenience | P2 |
200+
| Phase 4: Read-back + verify | Medium (~150 lines) | Medium — debugging aid | P2 |
201+
| Phase 5: Auto-detect | Small (~30 lines) | Low — minor convenience | P3 |
202+
203+
---
204+
205+
## References
206+
207+
- `firmware/esp32-csi-node/main/nvs_config.h` — NVS config struct (20 fields)
208+
- `firmware/esp32-csi-node/main/nvs_config.c` — NVS read logic (20 keys)
209+
- `firmware/esp32-csi-node/provision.py` — Current provisioning script (13 of 20 keys)
210+
- ADR-029: RuvSense multistatic sensing mode (TDM, channel hopping)
211+
- ADR-032: Multistatic mesh security hardening (mesh keys)
212+
- ADR-039: ESP32-S3 edge intelligence (edge tiers, vitals)
213+
- ADR-040: WASM programmable sensing (WASM modules, signature verification)
214+
- Issue #130: Provisioning script doesn't support TDM

0 commit comments

Comments
 (0)