ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
sleep_sched.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file sleep_sched.h
6 * @brief Dynamic sleep-cycle scheduler (PROTOCORE_ENABLE_SLEEP_SCHED).
7 *
8 * Decides, from the time since the last activity, whether a low-power device should sleep between
9 * requests and for how long - so a battery / solar node idles most of the time yet still serves. It is
10 * a pure decision core (`protocore_sleep_next`): given `now`, the last-activity timestamp, and a config, it
11 * returns the number of milliseconds to sleep (0 = stay awake). The device stays awake until it has
12 * been idle for `idle_ms`, then sleeps in windows that ramp from `min_ms` up to `max_ms` the longer the
13 * idle streak runs (so a briefly-idle device wakes often and responsively, a long-idle one sleeps deep).
14 *
15 * Pure, zero heap, no stdlib, and takes an explicit `now`, so it is fully host-testable with a synthetic
16 * clock. The scheduler only decides the window; the app applies it per its own policy (a light
17 * sleep with a timer wakeup, modem sleep, or deep sleep). It sits beside
18 * network_drivers/physical/radio_power (modem sleep): that trims radio power while awake, this schedules the sleep.
19 */
20
21#ifndef PROTOCORE_SLEEP_SCHED_H
22#define PROTOCORE_SLEEP_SCHED_H
23
24#include "protocore_config.h" // the entry point: protocore_types.h for the widths
25
26#if PROTOCORE_ENABLE_SLEEP_SCHED
27
29
30/** @brief Scheduler configuration (all times in ms). */
31typedef struct
32{
33 uint32_t idle_ms; ///< stay fully awake until idle at least this long.
34 uint32_t min_ms; ///< first sleep window once idle (also the floor).
35 uint32_t max_ms; ///< longest single sleep window (the ceiling as the idle streak grows).
36 uint32_t ramp_ms; ///< every additional `ramp_ms` of idle doubles the window (0 => jump to max_ms).
37} protocore_sleep_cfg;
38
39/** @brief What the decision reads: where the clock stands against the last activity. */
40typedef struct
41{
42 uint32_t now; ///< current time (protocore_millis units)
43 uint32_t last_active_ms; ///< timestamp of the last activity (a request, a send, app work)
44 const protocore_sleep_cfg *cfg; ///< the thresholds
45} SleepAskArgs;
46
47/**
48 * @brief The dynamic sleep-cycle scheduler.
49 *
50 * A caller sets the members the call takes, invokes it through ::SleepSched, and reads the window
51 * off the same handle.
52 *
53 * @var SleepSchedNs::ask where the clock stands against the last activity
54 * @var SleepSchedNs::ms milliseconds to sleep, or 0 to stay awake
55 * @var SleepSchedNs::next decide the window
56 *
57 * Wrap-safe: uses the unsigned delta `now - last_active_ms`, correct across a millis() rollover.
58 * Reports 0 while idle < `idle_ms`; otherwise a window clamped to [min_ms, max_ms] that grows with
59 * the idle streak (doubling every `cfg.ramp_ms`, or straight to `max_ms` when `ramp_ms` is 0). If
60 * `max_ms` < `min_ms` the result is clamped to `min_ms`.
61 *
62 * No storage member: the decision reads its operands and holds nothing.
63 */
64typedef struct
65{
66 SleepAskArgs ask;
67 uint32_t ms;
68} SleepSchedVars;
69
70/** @brief The operands and the outcome. */
71extern SleepSchedVars SleepSchedV;
72
73/** @brief The entries. */
74typedef struct
75{
76 void (*const next)(uint8_t *work);
77} SleepSchedNs;
78
79// What the table binds, defined once in the .c and taking one parameter each: everything
80// else an entry needs is an operand in SleepSchedV or a region of the borrow at a fixed offset.
81void protocore_sleep_sched_next(uint8_t *work);
82
83// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
84// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
85// `SleepSched.next(work)` resolves to a named function and becomes a DIRECT call. An extern table
86// leaves the call indirect and the symbol live at every level, -O2 -flto included.
87static const SleepSchedNs SleepSched __attribute__((unused)) = {
88 .next = protocore_sleep_sched_next,
89};
90
92
93#endif // PROTOCORE_ENABLE_SLEEP_SCHED
94
95#endif // PROTOCORE_SLEEP_SCHED_H
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
#define PROTOCORE_END_DECLS
Definition types.h:97