Power and deep sleep (espos_power)¶
A duty cycle for a device on a battery: wake, get on the network, publish,
flush the SignalK stream, deep-sleep for power.interval_s, again. Optional
and off by default: add the component to a project and set power.mode to
cycle. The example is
duty_cycle.
ESP_ERROR_CHECK(espos_start(NULL)); /* starts the cycle's task last */
espos_sk_publish_number("sensors.x.value", v); /* what this wake is for */
/* nothing else: espos_power flushes and sleeps once the stream carried it */
Staying reachable¶
A device that sleeps almost all the time is almost never reachable, so the
rules are mostly about not losing it. They are pure C
(espos_power_policy.h, tested in test/host/espos_power_test) and are checked
in this order, each winning over every later one:
| Rule | Why |
|---|---|
power.mode off → stay awake |
the default: a freshly flashed device must learn its network and server first |
| running image unconfirmed → stay awake | every wake from deep sleep is a boot, and the bootloader marks an image still pending verification as aborted. Sleeping before an update confirmed itself would roll it back at the next wake. espOS confirms an image once the network is up, and rolls it back itself after ota.confirm_tmo_s if it never gets there |
| an update being checked, downloaded or installed → stay awake | a wake lasts seconds, and an update longer than that would be cut off on every wake and never finish. espos_ota's own timeouts end a download that hangs. Needs espos_ota in the build |
a boot that is not the cycle's timer wake, inside power.window_s → stay awake |
after a power-on, an update or a crash the web UI and OTA are reachable. Cutting the power always reopens this window — it is the way back in |
| past the wake's deadline → sleep | a network that never comes up must not drain the battery. The deadline is power.awake_max_s after a timer wake, and window_s + awake_max_s after any other boot |
| network down → stay awake | |
| SignalK built and streaming enabled, stream not connected → stay awake | |
connected for less than power.publish_ms → stay awake |
the application's values go out. Without SignalK this counts from the network coming up |
| an application hold → stay awake | espos_power_hold() / espos_power_release(), counted. A hold does not beat the deadline |
| otherwise → sleep |
Going to sleep¶
In a fixed order, because it cannot be taken back:
espos_sk_flush(power.flush_ms)while the stream is still connected — a message still being written counts as pending (signalk.md, "Flushing before sleep");- a short pause, because handed to the socket is not yet on the wire;
- the wake counter and this wake's duration into RTC memory;
- deep sleep with a timer wake after
power.interval_s.
espos_power_sleep_now() does the same on the application's schedule, and
still refuses while an update is unconfirmed.
What survives a sleep¶
| What | Where |
|---|---|
| wake counter, duration of the previous wake | RTC memory (espos_power), trusted only after a deep-sleep reset with an intact record |
| wall clock | RTC memory (time.md, "Deep sleep") |
| SignalK token, pending access request, TLS pin | NVS (signalk.md) |
| deltas buffered while offline | nothing — the ring is RAM; flush before sleeping |
A power-on starts the counter at 0.
Configuration (power)¶
| Key | Default | |
|---|---|---|
mode |
off |
off or cycle; followed live, never while an update is unconfirmed |
interval_s |
300 | sleep between wakes (10–86400) |
window_s |
300 | awake after a power-on, update or crash (30–86400) |
awake_max_s |
30 | a wake's time budget (5–600) |
publish_ms |
1500 | connected this long before sleeping (0–60000) |
flush_ms |
3000 | how long to wait for buffered deltas (0–30000) |
GET /api/v1/power¶
{"mode": "cycle", "decision": "stay", "why": "stream", "timer_wake": true, "wake_count": 12,
"last_awake_ms": 4210, "interval_s": 300, "uptime_ms": 2600, "deadline_ms": 30000, "holds": 0}
why is one of off unconfirmed ota window network stream publishing hold
done deadline.
Not here yet¶
- OTA for a sleeping fleet. An update that has started holds the device
awake until it is done, but nothing starts one on a short wake: the manifest
check waits 20 s after boot and keeps its schedule in RAM. For now, push an
update (
POST /api/v1/ota) while the device is awake, or in its awake window after a power cycle. - WiFi fast connect. A wake scans every channel; connecting straight to the cached access point and channel is the obvious next step.
- Light sleep between readings for a device that must stay connected.