Skip to content

Add remote-sleep endpoint (POST /system/sleep) + provision.py --ota-psk support#1391

Open
SouLLeSS313 wants to merge 1 commit into
ruvnet:mainfrom
SouLLeSS313:feat/remote-sleep-command
Open

Add remote-sleep endpoint (POST /system/sleep) + provision.py --ota-psk support#1391
SouLLeSS313 wants to merge 1 commit into
ruvnet:mainfrom
SouLLeSS313:feat/remote-sleep-command

Conversation

@SouLLeSS313

Copy link
Copy Markdown

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

  1. POST /system/sleep on the existing OTA HTTP server (port 8032) — calls esp_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 /ota already is until a PSK is provisioned.

  2. 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 the security NVS namespace ota_update.c already 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.

  3. GET /api/v1/nodes gains ip_address per 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/sleep has 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.
  • Live on 3× ESP32-S3 (devkitc-overlay firmware, real WiFi CSI): flashed and provisioned all 3 with a real PSK via the new --ota-psk flag, then individually confirmed each:
    • Accepts POST /system/sleep with the correct bearer token → 200 {"status":"ok",...}, board goes offline (confirmed via the sensing server's /api/v1/nodesstatus flips to stale within ~10-15s).
    • The ip_address field in /api/v1/nodes correctly tracks each node's actual current IP, including across a DHCP reassignment after reflash.

Files changed

  • firmware/esp32-csi-node/main/ota_update.c
  • firmware/esp32-csi-node/provision.py
  • v2/crates/wifi-densepose-sensing-server/src/main.rs

…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant