Add remote-sleep endpoint (POST /system/sleep) + provision.py --ota-psk support#1391
Open
SouLLeSS313 wants to merge 1 commit into
Open
Add remote-sleep endpoint (POST /system/sleep) + provision.py --ota-psk support#1391SouLLeSS313 wants to merge 1 commit into
SouLLeSS313 wants to merge 1 commit into
Conversation
…sk support
Adds a way to remotely power down an ESP32 CSI node over HTTP, for
deployments where physically walking to each node isn't convenient (e.g.
controlling a multi-node mesh from a companion server).
- firmware/esp32-csi-node/main/ota_update.c: new POST /system/sleep on the
existing OTA HTTP server (port 8032), calling esp_deep_sleep_start().
Reuses the OTA endpoint's existing PSK auth (ota_check_auth) rather than
adding a second auth mechanism -- fail-closed the same way as /ota until
a PSK is provisioned. Deliberately does NOT attempt a wake-on-network
path: deep sleep turns off the WiFi radio, so nothing can reach the node
again over the air. Waking it back up needs a physical power-cycle (or,
on hardware with the wake GPIO wired up, an external trigger) -- this is
a real limitation of WiFi-based remote control, not an oversight, and
the code/response message says so rather than implying otherwise.
- firmware/esp32-csi-node/provision.py: adds --ota-psk, which the file's
own log messages already referenced ("run provision.py --ota-psk <hex>")
but which didn't actually exist as a flag. Writes to the "security" NVS
namespace ota_update.c already reads. Benefits from the existing
additive-by-default state-file merging (ruvnet#391/ruvnet#574) -- provisioning just
the PSK on an already-configured node doesn't require re-passing WiFi
credentials.
- v2/crates/wifi-densepose-sensing-server/src/main.rs: NodeState gains
last_ip (the source IP of the node's most recent UDP packet), exposed as
ip_address on GET /api/v1/nodes. Lets a server-side caller (e.g. a script
that wants to sleep every currently-active node) find each node on the
LAN without a separate discovery mechanism -- the sensing server already
knows this from every packet it receives.
Verified live on 3x ESP32-S3 (devkitc-overlay firmware): flashed +
provisioned all 3 with a real PSK, confirmed each accepts POST
/system/sleep with the correct bearer token (200, board goes offline
within ~10s) and rejects it appropriately without one. cargo test -p
wifi-densepose-sensing-server: 484 passed, 0 failed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an optional remote-shutdown capability for ESP32 CSI nodes — useful for anyone running a multi-node mesh who wants to power nodes down from a companion server/script without walking to each physical board. Built and tested this against my own 3-node deployment; opening it in case it's useful upstream, not because anything's broken — happy to adjust scope/approach if you'd rather do this differently.
What's added
POST /system/sleepon the existing OTA HTTP server (port 8032) — callsesp_deep_sleep_start(). Reuses the OTA endpoint's existing PSK auth (ota_check_auth) rather than introducing a second auth mechanism, so it's fail-closed the same way/otaalready is until a PSK is provisioned.provision.py --ota-psk <hex>— this flag didn't actually exist despite the firmware's own log message telling operators to run it ("No OTA PSK in NVS — ... (provision.py --ota-psk <hex>)"). Writes to thesecurityNVS namespaceota_update.calready reads. Benefits from the existing additive-by-default state merging (provision.py: esptool v5 incompat + NVS partition wipes existing keys when partial update #391/esp32-csi-node: mDNS discovery for seed_url + provisioning UX fixes #574) — provisioning just the PSK on an already-configured node doesn't require re-passing WiFi credentials.GET /api/v1/nodesgainsip_addressper node (sensing-server side) — the source IP of that node's most recent UDP packet. This is what makes the sleep endpoint actually usable from a script: something wanting to sleep every currently-active node needs to know where they are, and the sensing server already has that from every packet it receives; no separate discovery mechanism needed.Deliberately NOT included: remote wake
/system/sleephas no wake timer and there's no wake-on-network path. Deep sleep turns off the WiFi radio, so nothing can reach the node again over the air — waking it back up needs a physical power-cycle (or, on hardware with a wake GPIO wired up, an external trigger, which this PR doesn't add). This is a real limitation of WiFi-based remote control, not an oversight, and the response message/docs say so rather than implying a remote wake exists. Happy to add a timed-wake option (esp_sleep_enable_timer_wakeup) as a follow-up if there's interest — kept this PR to just the "off" half since that's what I actually needed and tested.Testing
cargo test -p wifi-densepose-sensing-server: 484 passed, 0 failed.--ota-pskflag, then individually confirmed each:POST /system/sleepwith the correct bearer token →200 {"status":"ok",...}, board goes offline (confirmed via the sensing server's/api/v1/nodes—statusflips tostalewithin ~10-15s).ip_addressfield in/api/v1/nodescorrectly tracks each node's actual current IP, including across a DHCP reassignment after reflash.Files changed
firmware/esp32-csi-node/main/ota_update.cfirmware/esp32-csi-node/provision.pyv2/crates/wifi-densepose-sensing-server/src/main.rs