DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
time_source.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file time_source.cpp
6 * @brief Multi-source time fallback matrix - implementation.
7 *
8 * A fixed BSS table of sources queried in ascending priority. See time_source.h.
9 */
10
11#include "time_source.h"
12#include "shared_primitives/http_date.h" // detws_http_date() - the shared IMF-fixdate formatter
13
14#if DETWS_ENABLE_TIME_SOURCE
15
16namespace
17{
18struct Src
19{
20 const char *name;
21 TimeSourceFn fn;
22 uint8_t priority;
23 bool used;
24};
25
26// All time-source state, owned by one instance (internal linkage): the priority-ordered
27// source table and the last-selected source name, grouped so it is one named owner.
28struct TimeSourceCtx
29{
30 Src sources[DETWS_TIME_SOURCE_MAX];
31 const char *active = nullptr;
32};
33TimeSourceCtx s_ts;
34} // namespace
35
36bool detws_time_source_add(const char *name, uint8_t priority, TimeSourceFn fn)
37{
38 if (!fn)
39 return false;
40 for (int i = 0; i < DETWS_TIME_SOURCE_MAX; i++)
41 {
42 if (!s_ts.sources[i].used)
43 {
44 s_ts.sources[i].name = name;
45 s_ts.sources[i].fn = fn;
46 s_ts.sources[i].priority = priority;
47 s_ts.sources[i].used = true;
48 return true;
49 }
50 }
51 return false; // table full
52}
53
54uint32_t detws_time_now(void)
55{
56 s_ts.active = nullptr;
57
58 // Query sources in ascending priority (lowest value first); stop at the first
59 // that returns a nonzero epoch. A selection scan avoids sorting and, more
60 // importantly, does not invoke lower-priority callbacks once a higher-priority
61 // source has answered (reading an RTC / GPS can be costly).
62 uint32_t queried = 0; // bitmask of sources already tried
63 for (int pass = 0; pass < DETWS_TIME_SOURCE_MAX; pass++)
64 {
65 int sel = -1;
66 for (int i = 0; i < DETWS_TIME_SOURCE_MAX; i++)
67 {
68 if (!s_ts.sources[i].used || (queried & (1u << i)))
69 continue;
70 if (sel < 0 || s_ts.sources[i].priority < s_ts.sources[sel].priority)
71 sel = i;
72 }
73 if (sel < 0)
74 break; // no more sources
75
76 queried |= (1u << sel);
77 uint32_t epoch = s_ts.sources[sel].fn();
78 if (epoch != 0)
79 {
80 s_ts.active = s_ts.sources[sel].name;
81 return epoch;
82 }
83 }
84 return 0;
85}
86
87const char *detws_time_source_active(void)
88{
89 return s_ts.active;
90}
91
93{
94 for (int i = 0; i < DETWS_TIME_SOURCE_MAX; i++)
95 s_ts.sources[i] = Src{};
96 s_ts.active = nullptr;
97}
98
99#else // DETWS_ENABLE_TIME_SOURCE == 0 -> no-op stubs
100
101bool detws_time_source_add(const char *, uint8_t, TimeSourceFn)
102{
103 return false;
104}
105uint32_t detws_time_now(void)
106{
107 return 0;
108}
110{
111 return nullptr;
112}
114{
115}
116
117#endif // DETWS_ENABLE_TIME_SOURCE
118
119// The current best time (detws_time_now, any registered NTP / GPS / RTC / ... source) as an RFC 7231
120// IMF-fixdate. Defined unconditionally: with the registry disabled detws_time_now() is 0, so this
121// returns 0 (no Date). Lets the HTTP Date header draw from whatever time source is enabled.
122size_t detws_time_http_date(char *out, size_t out_cap)
123{
124 return detws_http_date((time_t)detws_time_now(), out, out_cap);
125}
#define DETWS_TIME_SOURCE_MAX
Maximum registered time sources (DETWS_ENABLE_TIME_SOURCE).
Format a Unix epoch as an RFC 7231 IMF-fixdate (one shared copy).
size_t detws_http_date(time_t epoch, char *out, size_t out_cap)
Write epoch as an RFC 7231 IMF-fixdate into out (always GMT).
Definition http_date.h:30
bool detws_time_source_add(const char *, uint8_t, TimeSourceFn)
Register a time source.
const char * detws_time_source_active(void)
Name of the source that satisfied the last detws_time_now(), or nullptr.
void detws_time_source_reset(void)
Clear all registered sources.
uint32_t detws_time_now(void)
Current best time.
size_t detws_time_http_date(char *out, size_t out_cap)
The current best time (detws_time_now, any registered NTP / GPS / RTC / ... source) formatted as an R...
Multi-source time fallback matrix (DETWS_ENABLE_TIME_SOURCE).
uint32_t(* TimeSourceFn)(void)
A time source: returns the current Unix epoch seconds for this source, or 0 if it currently has no va...
Definition time_source.h:36