File espos_power_policy.h¶
File List > espos_power > include > espos_power_policy.h
Go to the documentation of this file
/*
* SPDX-FileCopyrightText: 2026 Dirk Wahrheit
* SPDX-License-Identifier: Apache-2.0
*
* espos_power policy โ may a duty-cycling device go back to sleep now?
*
* Pure C over plain inputs, so every rule is a host test. The rules are few,
* and each one exists because a device that is asleep almost all the time is
* almost never reachable, so a mistake here does not show as a bug report but
* as a device nobody can talk to any more:
*
* - Never while the running image is unconfirmed. Every wake from deep sleep
* is a boot, and the bootloader marks an image that is still
* PENDING_VERIFY at boot as aborted. A device that slept before its update
* confirmed itself would roll that update back on the first wake.
* - Stay awake for a window after any boot that is not the cycle's own timer
* wake โ power-on, an update's reboot, a crash. That is the way back in:
* the web UI and OTA are reachable, and cutting the power always reopens
* it.
* - A wake has a deadline. A network that never comes up, or a server that
* does not answer, must not keep a battery device awake until it is flat.
* An application hold does not beat the deadline for the same reason.
*
* Threading: none. Nothing here allocates, blocks or calls out.
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
ESPOS_POWER_STAY = 0,
ESPOS_POWER_SLEEP = 1,
} espos_power_decision_t;
/* Why the policy decided what it did; also what GET /api/v1/power reports. */
typedef enum {
ESPOS_POWER_WHY_OFF = 0, /* STAY: power.mode is off */
ESPOS_POWER_WHY_UNCONFIRMED = 1, /* STAY: the running image has not confirmed itself */
ESPOS_POWER_WHY_WINDOW = 2, /* STAY: awake window after a boot that was not a timer wake */
ESPOS_POWER_WHY_NETWORK = 3, /* STAY: waiting for the network */
ESPOS_POWER_WHY_STREAM = 4, /* STAY: waiting for the SignalK stream */
ESPOS_POWER_WHY_PUBLISHING = 5, /* STAY: ready, giving the application time to publish */
ESPOS_POWER_WHY_HOLD = 6, /* STAY: the application holds the device awake */
ESPOS_POWER_WHY_DONE = 7, /* SLEEP: everything this wake was for is done */
ESPOS_POWER_WHY_DEADLINE = 8, /* SLEEP: the wake ran out of time */
ESPOS_POWER_WHY_OTA = 9, /* STAY: an update is being checked, downloaded or installed */
ESPOS_POWER_WHY_MAX = 10,
} espos_power_why_t;
typedef struct {
uint32_t window_ms; /* power.window_s: awake after a boot that was not a timer wake */
uint32_t awake_max_ms; /* power.awake_max_s: a wake's time budget once the cycle runs */
uint32_t publish_ms; /* power.publish_ms: time connected before sleeping */
} espos_power_policy_cfg_t;
typedef struct {
bool enabled; /* power.mode is cycle */
bool timer_wake; /* this boot is the cycle's own wake from deep sleep */
bool image_unconfirmed; /* the running OTA image is still pending verification */
bool ota_busy; /* an update is being checked, downloaded or installed */
bool net_up;
uint32_t net_up_for_ms; /* how long the network has been up; ignored when it is not */
bool have_stream; /* SignalK is built and streaming is enabled: wait for it */
bool stream_connected;
uint32_t stream_up_for_ms; /* how long the stream has been connected; ignored when it is not */
uint32_t uptime_ms; /* since this boot */
uint32_t holds; /* application holds (espos_power_hold) */
} espos_power_policy_in_t;
espos_power_decision_t espos_power_policy_decide(const espos_power_policy_cfg_t *cfg, const espos_power_policy_in_t *in,
espos_power_why_t *why);
uint32_t espos_power_policy_deadline_ms(const espos_power_policy_cfg_t *cfg, bool timer_wake);
const char *espos_power_why_str(espos_power_why_t why);
#ifdef __cplusplus
}
#endif