DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
telemetry.h
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 telemetry.h
6 * @brief Zero-heap telemetry math helpers (DETWS_ENABLE_TELEMETRY).
7 *
8 * Pure-computation building blocks for turning a stream of sensor samples into
9 * dashboard figures, alert triggers, and odometer-style totals - no heap, no
10 * Arduino dependency, all state in caller-supplied storage or small POD structs,
11 * so the whole cluster unit-tests on the host:
12 *
13 * - DetwsWindow moving-window statistics (mean / variance / stddev / min /
14 * max) over a caller-provided ring buffer, O(1) mean/variance
15 * via running sums.
16 * - DetwsRate derivative / rate-of-change between successive samples
17 * (units per second), for slope alerts.
18 * - DetwsTotalizer trapezoidal integration of a rate over time (a running
19 * total / odometer).
20 *
21 * @author Douglas Quigg (dstroy0)
22 * @date 2026
23 */
24
25#ifndef DETERMINISTICESPASYNCWEBSERVER_TELEMETRY_H
26#define DETERMINISTICESPASYNCWEBSERVER_TELEMETRY_H
27
28#include "ServerConfig.h"
29#include <stdint.h>
30
31#if DETWS_ENABLE_TELEMETRY
32
33// ---------------------------------------------------------------------------
34// Moving-window statistics
35// ---------------------------------------------------------------------------
36
37/**
38 * @brief Moving-window stats accumulator over a caller-provided ring buffer.
39 *
40 * The caller owns the `float` storage (no heap). Mean and variance are kept O(1)
41 * via running sums; min/max are an O(window) scan.
42 */
43struct DetwsWindow
44{
45 float *buf; ///< caller-provided sample storage (>= cap floats).
46 uint16_t cap; ///< window capacity (samples).
47 uint16_t count; ///< samples currently held (<= cap).
48 uint16_t head; ///< next write index (oldest sample when full).
49 double sum; ///< running sum of held samples.
50 double sum_sq; ///< running sum of squares of held samples.
51};
52
53/** @brief Bind @p w to @p buf (capacity @p cap samples) and reset it to empty. */
54void detws_window_init(DetwsWindow *w, float *buf, uint16_t cap);
55
56/** @brief Add @p sample, evicting the oldest once the window is full. */
57void detws_window_push(DetwsWindow *w, float sample);
58
59/** @brief Number of samples currently in the window. */
60uint16_t detws_window_count(const DetwsWindow *w);
61
62/** @brief Arithmetic mean of the window (0 when empty). */
63float detws_window_mean(const DetwsWindow *w);
64
65/** @brief Population variance of the window (0 when empty). */
66float detws_window_variance(const DetwsWindow *w);
67
68/** @brief Population standard deviation of the window (0 when empty). */
69float detws_window_stddev(const DetwsWindow *w);
70
71/** @brief Smallest sample in the window (0 when empty). */
72float detws_window_min(const DetwsWindow *w);
73
74/** @brief Largest sample in the window (0 when empty). */
75float detws_window_max(const DetwsWindow *w);
76
77// ---------------------------------------------------------------------------
78// Rate of change (first derivative)
79// ---------------------------------------------------------------------------
80
81/** @brief Derivative / rate-of-change tracker between successive samples. */
82struct DetwsRate
83{
84 float last_value; ///< previous sample value.
85 uint32_t last_ms; ///< millis() of the previous sample.
86 bool primed; ///< false until the first sample is seen.
87};
88
89/** @brief Reset @p r so the next sample is treated as the first. */
90void detws_rate_init(DetwsRate *r);
91
92/**
93 * @brief Feed a sample; returns the rate of change in units per second since the
94 * previous sample.
95 *
96 * Returns 0 on the first sample (nothing to differentiate) and when the elapsed
97 * time is 0. The elapsed-time math is unsigned, so it survives a millis()
98 * rollover.
99 */
100float detws_rate_update(DetwsRate *r, float value, uint32_t now_ms);
101
102// ---------------------------------------------------------------------------
103// Totalizer (run-time integral / odometer)
104// ---------------------------------------------------------------------------
105
106/** @brief Running total from trapezoidal integration of a rate over time. */
107struct DetwsTotalizer
108{
109 double total; ///< accumulated total (in rate-units * seconds).
110 float last_rate; ///< previous rate sample.
111 uint32_t last_ms; ///< millis() of the previous rate sample.
112 bool primed; ///< false until the first rate sample is seen.
113};
114
115/** @brief Reset @p t to a zero total with no prior sample. */
116void detws_totalizer_init(DetwsTotalizer *t);
117
118/**
119 * @brief Integrate @p rate (units per second) over the time since the last call
120 * (trapezoidal rule) and return the running total.
121 *
122 * The first call only seeds the baseline (total stays 0). Unsigned elapsed-time
123 * math survives a millis() rollover.
124 */
125double detws_totalizer_add(DetwsTotalizer *t, float rate, uint32_t now_ms);
126
127/** @brief Current running total. */
128double detws_totalizer_total(const DetwsTotalizer *t);
129
130/** @brief Reset the running total to 0 and drop the prior sample. */
131void detws_totalizer_reset(DetwsTotalizer *t);
132
133#endif // DETWS_ENABLE_TELEMETRY
134#endif // DETERMINISTICESPASYNCWEBSERVER_TELEMETRY_H
User-facing configuration for DeterministicESPAsyncWebServer.