diff --git a/ROADMAP.md b/ROADMAP.md index fbef661..6e5b8fe 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -22,7 +22,7 @@ - [x] Build and flash to device - [x] Update CHEATSHEET.md with new commands -## v0.3 - OTA Updates [IN PROGRESS] +## v0.3 - OTA Updates [DONE] - [x] Dual OTA partition table (ota_0 + ota_1, 1920 KB each) - [x] 4MB flash config, custom partitions in sdkconfig.defaults - [x] OTA command handler + ota_task in firmware @@ -31,8 +31,8 @@ - [x] Version field in STATUS reply - [x] Pi-side `esp-ota` tool (HTTP server + OTA orchestration) - [x] `esp-fleet ota` subcommand (sequential fleet update) -- [ ] USB-flash first device (partition table change) -- [ ] End-to-end OTA test +- [x] USB-flash first device (partition table change) +- [x] End-to-end OTA test ## v0.4 - Adaptive Sampling - [ ] On-device wander calculation (simplified) diff --git a/TASKS.md b/TASKS.md index 3489ded..437bbd9 100644 --- a/TASKS.md +++ b/TASKS.md @@ -2,31 +2,37 @@ **Last Updated:** 2026-02-04 -## Current Sprint: v0.3 - OTA Updates +## Current Sprint: v0.4 - Adaptive Sampling ### P0 - Critical -- [x] Create dual OTA partition table (`partitions.csv`) -- [x] Update `sdkconfig.defaults` (4MB flash, custom partitions, rollback, HTTP OTA) -- [x] Firmware: OTA command, ota_task, LED_OTA, rollback validation -- [x] Firmware: Add version to STATUS reply -- [ ] `idf.py reconfigure` to regenerate sdkconfig -- [ ] Build and USB-flash first device (partition table change requires USB) +- [ ] On-device CSI wander calculation (simplified) +- [ ] Adaptive rate: 10 pkt/s idle → 100 pkt/s on motion ### P1 - Important -- [x] Pi-side `esp-ota` tool (HTTP server + OTA orchestration) -- [x] `esp-fleet ota` subcommand (sequential fleet OTA) -- [ ] Test OTA end-to-end: `esp-ota amber-maple.local` -- [ ] Regenerate `sdkconfig.sample` +- [ ] Rate change notification to Pi +- [ ] Tunable motion threshold via UDP command ### P2 - Normal - [ ] OTA update remaining fleet (muddy-storm, hollow-acorn) via USB - [ ] Test rollback (flash bad firmware, verify auto-revert) -- [ ] Document esp-ota in USAGE.md ### P3 - Low - [ ] Document esp-crab dual-antenna capabilities - [ ] Document esp-radar console features +## Completed: v0.3 - OTA Updates + +- [x] Dual OTA partition table (`partitions.csv`) +- [x] 4MB flash, custom partitions, rollback in sdkconfig.defaults +- [x] Firmware: OTA command, ota_task, LED_OTA, rollback validation +- [x] Firmware: version in STATUS reply +- [x] Pi-side `esp-ota` tool (HTTP server + OTA orchestration) +- [x] `esp-fleet ota` subcommand (sequential fleet update) +- [x] Build and USB-flash amber-maple (partition table change) +- [x] End-to-end OTA test verified +- [x] Regenerate sdkconfig.sample +- [x] Update CHEATSHEET.md, USAGE.md + ## Completed: v0.2 - Remote Management - [x] Firmware: UDP command listener (port 5501) @@ -55,5 +61,5 @@ - NVS offset changes with new partition table — first USB flash resets saved config - First device must be USB-flashed to switch partition table, subsequent updates via OTA - `esp_https_ota` is built into ESP-IDF core — no extra deps needed -- OTA download ~790 KB on LAN takes ~3-5s, well under 30s watchdog +- OTA download ~896 KB on LAN takes ~3-5s, well under 30s watchdog - CSI data keeps flowing during OTA download diff --git a/docs/USAGE.md b/docs/USAGE.md index 420c96c..38af77b 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -163,6 +163,86 @@ The router firmware uses only LLTF for broader router compatibility. The ESP-NOW | `CONFIG_FORCE_GAIN` | `0` | Force AGC/FFT gain to baseline (disabled) | | `CONFIG_GAIN_CONTROL` | `1` (auto) | Enable gain compensation (ESP32-S3, C3, C5, C6, C61) | +## Remote Management + +### Commands (esp-cmd) + +Send commands to individual devices over UDP port 5501: + +```bash +esp-cmd STATUS # Uptime, heap, RSSI, tx_power, rate, version +esp-cmd IDENTIFY # LED solid 5s +esp-cmd RATE <10-100> # Set ping rate (NVS saved) +esp-cmd POWER <2-20> # Set TX power dBm (NVS saved) +esp-cmd OTA # Trigger OTA update (prefer esp-ota) +esp-cmd REBOOT # Restart device +``` + +### Fleet Management (esp-fleet) + +Send commands to all sensors in parallel: + +```bash +esp-fleet status # Query all devices +esp-fleet identify # Blink all LEDs +esp-fleet rate 50 # Set rate on all +esp-fleet reboot # Reboot all +esp-fleet ota # OTA update all (sequential) +esp-fleet ota /path/to/fw # OTA with custom firmware +``` + +## OTA Updates + +### Overview + +Firmware updates are delivered over WiFi using ESP-IDF's `esp_https_ota` with a dual OTA partition layout. The device downloads a new binary from an HTTP server on the Pi, writes it to the inactive OTA slot, and reboots. + +### Partition Layout + +| Partition | Offset | Size | Purpose | +|-----------|--------|------|---------| +| nvs | 0x9000 | 16 KB | NVS config storage | +| otadata | 0xd000 | 8 KB | OTA boot selection | +| phy_init | 0xf000 | 4 KB | PHY calibration | +| ota_0 | 0x10000 | 1920 KB | App slot A | +| ota_1 | 0x1F0000 | 1920 KB | App slot B | + +### Using esp-ota + +The `esp-ota` tool handles the full OTA workflow: + +```bash +esp-ota amber-maple.local # Default build path +esp-ota amber-maple.local -f custom.bin # Custom firmware +esp-ota amber-maple.local --no-wait # Don't verify reboot +``` + +**What it does:** + +1. Verifies the device is alive (`STATUS`) +2. Starts a temporary HTTP server on port 8070 +3. Sends `OTA http://:8070/.bin` to the device +4. Waits for the device to download, flash, reboot +5. Verifies the device responds with the new version + +### Rollback + +The bootloader supports automatic rollback on failed updates: + +1. New firmware is written to the inactive OTA slot +2. Device reboots into the new firmware in `PENDING_VERIFY` state +3. If the firmware boots successfully (WiFi connects, mDNS starts, command listener ready), it marks itself as valid +4. If the firmware crashes or hangs, the 30s watchdog triggers a reboot and the bootloader rolls back to the previous slot + +### First-Time Setup + +The initial flash **must be done via USB** because switching from a single-app to a dual-OTA partition table requires erasing the entire flash. After the first USB flash, all subsequent updates can be done via OTA. + +```bash +idf.py -p /dev/ttyUSB0 flash # First time: USB required +esp-ota amber-maple.local # All subsequent updates: OTA +``` + ## Receiving CSI Data on the Pi Listen on UDP port 5500: