ESPectre SDK 2.8.0-280-gac7af68
Wi-Fi CSI motion sensing for ESP32 firmware
Loading...
Searching...
No Matches
runtime_interface.h
Go to the documentation of this file.
1/*
2 * ESPectre - Runtime Interface
3 *
4 * Platform-agnostic runtime interface and configuration contract.
5 *
6 * Author: Francesco Pace <francesco.pace@gmail.com>
7 * SPDX-License-Identifier: GPL-3.0-only
8 * Commercial licensing available under separate agreement; see LICENSING.md.
9 */
10#pragma once
11
12#include <cstdint>
13#include <string>
14
16#include "runtime_events.h"
17#include "runtime_snapshot.h"
19#include "csi_traffic_types.h"
20
21/**
22 * @file runtime_interface.h
23 * @brief Runtime configuration and the backend contract behind it.
24 *
25 * Most integrations do not implement `IEspectreRuntime`; they configure a
26 * `RuntimeConfig`, hand it to `RuntimeFrontendController`, and let the
27 * controller pick the backend. Implement the interface only when you are
28 * replacing the ESP-IDF backend, for example in a simulator or a host harness.
29 */
30
31namespace espectre {
32
33/** Wi-Fi band selection requested by the embedding frontend. */
34enum class WifiBandPolicy : uint8_t {
35 /** Restrict association to 2.4 GHz. This is the validated production default. */
37 /** Restrict association to 5 GHz. Supported only by dual-band targets. */
39 /** Let a dual-band radio choose between 2.4 GHz and 5 GHz. */
40 AUTO = 2,
41};
42
43/**
44 * Everything the runtime needs to know before `setup()`.
45 *
46 * Every member is default-constructed to a supported production value, so
47 * `RuntimeConfig{}` is a working configuration for Lightweight Detection on
48 * internally generated traffic. Override only what your product changes.
49 *
50 * Ranges are declared in `runtime_sensing_schema.h` as
51 * `RUNTIME_<FIELD>_MIN` / `_MAX` / `_DEFAULT`, and the free functions in
52 * `runtime_config_utils.h` validate against them. On ESP-IDF you can build
53 * this from menuconfig with `make_runtime_sensing_config_from_kconfig()`
54 * instead of assigning fields by hand.
55 *
56 * The config is copied into the runtime at `setup()`. Later edits to your own
57 * copy have no effect; use the `set_*_runtime()` control methods instead.
58 */
60 /** Which backend to build: motion sensing, or raw CSI streaming to a collector. */
62 /**
63 * Band available to the station while the runtime keeps the PHY at HT20.
64 *
65 * `BAND_5G` and `AUTO` require dual-band silicon. Keeping `BAND_2G` as the
66 * default preserves the band covered by the production detector corpus.
67 */
69 /** Detection profile to run. Lightweight self-calibrates; High Accuracy uses trained weights. */
71 /**
72 * Motion probability threshold, on the same 0..1 scale as
73 * `RuntimeSnapshot::movement_metric`.
74 *
75 * Lightweight Detection overwrites this during startup calibration, so the
76 * configured value only governs the pre-calibration window. High-Accuracy Detection keeps it as given.
77 */
79 /**
80 * Detector window duration in milliseconds (1000..2000).
81 *
82 * Runtimes resolve the duration to a fixed temporal grid from
83 * `csi_target_pps`; live arrival jitter never resizes the detector.
84 */
86 /**
87 * Advertise runtime detector switching.
88 *
89 * When true the runtime restores the persisted detector choice at `setup()`
90 * and sets `RuntimeCapabilities::supports_runtime_detector_selection`.
91 */
93 /**
94 * Target CSI sensing cadence, in packets per second.
95 *
96 * This value is always positive and defines detector temporal slots as well
97 * as the target for managed traffic. `csi_traffic_mode` alone selects who
98 * supplies traffic. The detector coefficients are fitted at 100 pps; see
99 * `docs/ALGORITHMS.md` before moving far from it.
100 */
102 /** Which packet the internal generator sends to solicit CSI. */
104 /** Where the CSI-bearing traffic comes from. See `csi_traffic_types.h`. */
106 /** UDP port used by the external and pacing CSI traffic modes. */
108 /**
109 * IPv4 multicast group joined by the UDP listener in `external` and `pacing`.
110 *
111 * Empty disables the IGMP join. Unicast to the device IP still works.
112 */
114 /** Payload marker that identifies accepted external CSI traffic (0..16 bytes). */
116 /**
117 * Stable device identity used by the ESPectre Protocol and CSI streaming.
118 *
119 * Leave at zero to derive it from the Wi-Fi MAC via
120 * `derive_runtime_device_id()`.
121 */
122 uint64_t device_id{0U};
123 /** `RuntimeProfile::STREAM` only: UDP port of the host CSI collector (1..65535). */
125 /** `RuntimeProfile::STREAM` only: interval between stream status logs, in ms (100..60000). */
127 /** `RuntimeProfile::STREAM` only: CSI records coalesced into one datagram (1..7). */
129 /** Milliseconds between `IRuntimeListener::on_periodic_update()` callbacks. */
131 /** Detector evaluation cadence in milliseconds. */
133 /** Consecutive above-threshold evaluations before reporting motion (1..20). */
135 /** Consecutive below-threshold evaluations before clearing motion (1..20). */
137 /** Enable the low-pass filter on the turbulence stream. Off by default. */
139 /** Low-pass cutoff in Hz (5.0..20.0). Ignored unless `lowpass_enabled`. */
141 /** Enable Hampel outlier rejection on the turbulence stream. On by default. */
143 /** Hampel window in samples (3..11). Ignored unless `hampel_enabled`. */
145 /** Hampel MAD multiplier (1.0..10.0). Ignored unless `hampel_enabled`. */
147};
148
149/**
150 * The sensing backend behind `RuntimeFrontendController`.
151 *
152 * Implement this only to replace the shipped ESP-IDF backend. Integrations
153 * consume it indirectly: the controller owns the instance, forwards control
154 * calls, and gates them on `get_capabilities()`.
155 *
156 * @par Threading
157 * Implementations are not required to be thread-safe and the shipped one is
158 * not. Run `setup()`, `loop()`, and `shutdown()` on the task that owns the
159 * runtime, and deliver listener callbacks on the caller's task rather than
160 * from an interrupt or a driver callback. See `espectre_sdk.h` for the
161 * complete contract, including the control-call caveat.
162 */
164 public:
165 virtual ~IEspectreRuntime() = default;
166
167 /**
168 * Bring the runtime up: radio hooks, CSI capture, detector, traffic.
169 *
170 * @return false if the runtime cannot sense. The caller must not call
171 * `loop()` afterwards; the controller drops the instance instead.
172 */
173 virtual bool setup() = 0;
174 /** Stop sensing and release everything `setup()` acquired. Safe to repeat. */
175 virtual void shutdown() = 0;
176 /**
177 * Advance runtime work and drain deferred events.
178 *
179 * Call it continuously from your loop task. This is where listener
180 * callbacks are delivered, so a slow callback delays the next iteration.
181 */
182 virtual void loop() = 0;
183 /**
184 * Gate the runtime-owned services without tearing the runtime down.
185 *
186 * Disarmed, the runtime stays configured but starts no CSI capture or
187 * traffic. The current Wi-Fi association is preserved so arming again can
188 * restart capture without waiting for another IP event. Matter uses this to
189 * stay quiet until commissioning completes; Native uses it to pause sensing
190 * while a frontend intentionally suspends sensing.
191 */
192 virtual void set_services_armed(bool armed) = 0;
193 /** Enable or suppress the high-rate `on_live_telemetry()` stream. */
194 virtual void set_live_telemetry_enabled(bool enabled) = 0;
195
196 /**
197 * Retune the motion threshold while running.
198 *
199 * @return false when the value is out of range for the active detector, or
200 * when the runtime cannot apply it.
201 */
202 virtual bool set_threshold_runtime(float threshold) = 0;
203 /**
204 * Retune the hit filter while running.
205 *
206 * @return false when either count is outside 1..20, or when the runtime
207 * cannot apply the change.
208 */
209 virtual bool set_motion_hits_runtime(uint8_t motion_on_hits, uint8_t motion_off_hits) = 0;
210 /**
211 * Switch who owns the CSI-bearing traffic while running.
212 *
213 * Defaulted rather than pure so existing out-of-tree backends keep
214 * compiling. A backend that does not implement live traffic retuning should
215 * return false and let the frontend reject the command.
216 */
217 virtual bool set_csi_traffic_mode_runtime(CsiTrafficMode mode) { return false; }
218 /**
219 * Change the internal traffic generator packet type while running.
220 *
221 * Backends that do not own traffic retuning keep the default false.
222 */
223 virtual bool set_traffic_generator_mode_runtime(RuntimeTrafficMode mode) { return false; }
224 /**
225 * Switch detector while running, rebuilding detector state.
226 *
227 * @return false when the algorithm is unknown or the switch fails.
228 */
230 /**
231 * Restart startup calibration against the current ambient channel.
232 *
233 * @return false when calibration cannot start, for example with no Wi-Fi
234 * link yet. Progress arrives through the calibration callbacks.
235 */
236 virtual bool trigger_recalibration() = 0;
237 /** True while startup calibration is running and detection is not yet valid. */
238 virtual bool is_calibrating() const = 0;
239
240 /** Current sensing state. Cheap enough to poll from your loop. */
241 virtual RuntimeSnapshot get_snapshot() const = 0;
242 /**
243 * Capture, traffic, and link counters for diagnostic frontends.
244 *
245 * The counters are cumulative and monotonic within a session. Feed them to
246 * `RuntimeDiagnosticsSampler` from an existing periodic sensing callback to
247 * get rates without adding a diagnostic timer.
248 *
249 * Defaulted rather than pure so that adding it does not break out-of-tree
250 * backends. A runtime that collects nothing keeps the zeroed snapshot, which
251 * is what a frontend reads as "no counters from this backend".
252 */
253 virtual RuntimeDiagnosticsSnapshot get_diagnostics() const { return {}; }
254 /** What this backend actually supports. Stable after `setup()`. */
256
257 /**
258 * Install the event sink, or `nullptr` to detach.
259 *
260 * Set it before `setup()` so calibration events are not missed. The runtime
261 * does not take ownership; the listener must outlive the runtime.
262 */
263 virtual void set_listener(IRuntimeListener *listener) = 0;
264};
265
266} // namespace espectre
The sensing backend behind RuntimeFrontendController.
virtual void set_listener(IRuntimeListener *listener)=0
Install the event sink, or nullptr to detach.
virtual bool set_traffic_generator_mode_runtime(RuntimeTrafficMode mode)
Change the internal traffic generator packet type while running.
virtual void loop()=0
Advance runtime work and drain deferred events.
virtual void set_services_armed(bool armed)=0
Gate the runtime-owned services without tearing the runtime down.
virtual RuntimeCapabilities get_capabilities() const =0
What this backend actually supports.
virtual bool is_calibrating() const =0
True while startup calibration is running and detection is not yet valid.
virtual void shutdown()=0
Stop sensing and release everything setup() acquired.
virtual ~IEspectreRuntime()=default
virtual bool setup()=0
Bring the runtime up: radio hooks, CSI capture, detector, traffic.
virtual bool set_detection_algorithm_runtime(DetectionAlgorithm algorithm)=0
Switch detector while running, rebuilding detector state.
virtual bool set_motion_hits_runtime(uint8_t motion_on_hits, uint8_t motion_off_hits)=0
Retune the hit filter while running.
virtual RuntimeDiagnosticsSnapshot get_diagnostics() const
Capture, traffic, and link counters for diagnostic frontends.
virtual bool trigger_recalibration()=0
Restart startup calibration against the current ambient channel.
virtual bool set_csi_traffic_mode_runtime(CsiTrafficMode mode)
Switch who owns the CSI-bearing traffic while running.
virtual void set_live_telemetry_enabled(bool enabled)=0
Enable or suppress the high-rate on_live_telemetry() stream.
virtual bool set_threshold_runtime(float threshold)=0
Retune the motion threshold while running.
virtual RuntimeSnapshot get_snapshot() const =0
Current sensing state.
Everything the runtime tells your firmware.
constexpr uint8_t RUNTIME_MOTION_ON_HITS_DEFAULT
constexpr uint8_t RUNTIME_STREAM_TX_BATCH_RECORDS_DEFAULT
constexpr uint8_t RUNTIME_HAMPEL_WINDOW_DEFAULT
constexpr float RUNTIME_SEGMENTATION_THRESHOLD_DEFAULT
constexpr uint32_t RUNTIME_PUBLISH_INTERVAL_MS_DEFAULT
constexpr uint16_t RUNTIME_STREAM_COLLECTOR_PORT_DEFAULT
constexpr uint8_t RUNTIME_MOTION_OFF_HITS_DEFAULT
constexpr uint32_t RUNTIME_STREAM_LOG_INTERVAL_MS_DEFAULT
RuntimeProfile
Which runtime backend the controller builds.
@ SENSING
Detect motion on-device and report state.
constexpr uint32_t RUNTIME_SEGMENTATION_WINDOW_SIZE_MS_DEFAULT
RuntimeTrafficMode
Which packet the internal generator sends to solicit CSI from the AP.
constexpr uint32_t RUNTIME_CSI_TARGET_PPS_DEFAULT
constexpr const char *const RUNTIME_CSI_TRAFFIC_MULTICAST_GROUP_DEFAULT
CsiTrafficMode
Where the CSI-bearing traffic comes from.
@ INTERNAL
The runtime generates its own traffic at csi_target_pps.
constexpr bool RUNTIME_LOWPASS_ENABLED_DEFAULT
constexpr float RUNTIME_LOWPASS_CUTOFF_DEFAULT
constexpr uint16_t RUNTIME_CSI_TRAFFIC_UDP_PORT_DEFAULT
constexpr uint32_t RUNTIME_EVALUATION_INTERVAL_MS_DEFAULT
WifiBandPolicy
Wi-Fi band selection requested by the embedding frontend.
@ BAND_5G
Restrict association to 5 GHz.
@ AUTO
Let a dual-band radio choose between 2.4 GHz and 5 GHz.
@ BAND_2G
Restrict association to 2.4 GHz.
DetectionAlgorithm
Which detector runs.
@ LIGHTWEIGHT
Lightweight feature fusion.
constexpr float RUNTIME_HAMPEL_THRESHOLD_DEFAULT
constexpr bool RUNTIME_HAMPEL_ENABLED_DEFAULT
The schema behind RuntimeConfig: enums, defaults, and valid ranges.
What a runtime actually offers its frontend.
Everything the runtime needs to know before setup().
std::string csi_traffic_multicast_group
IPv4 multicast group joined by the UDP listener in external and pacing.
uint8_t hampel_window
Hampel window in samples (3..11).
CsiTrafficMode csi_traffic_mode
Where the CSI-bearing traffic comes from.
uint32_t csi_target_pps
Target CSI sensing cadence, in packets per second.
uint32_t stream_log_interval_ms
RuntimeProfile::STREAM only: interval between stream status logs, in ms (100..60000).
uint32_t segmentation_window_size_ms
Detector window duration in milliseconds (1000..2000).
RuntimeProfile runtime_profile
Which backend to build: motion sensing, or raw CSI streaming to a collector.
uint16_t csi_traffic_udp_port
UDP port used by the external and pacing CSI traffic modes.
bool hampel_enabled
Enable Hampel outlier rejection on the turbulence stream.
float hampel_threshold
Hampel MAD multiplier (1.0..10.0).
DetectionAlgorithm detection_algorithm
Detection profile to run.
std::string csi_traffic_expected_payload
Payload marker that identifies accepted external CSI traffic (0..16 bytes).
uint8_t motion_off_hits
Consecutive below-threshold evaluations before clearing motion (1..20).
float segmentation_threshold
Motion probability threshold, on the same 0..1 scale as RuntimeSnapshot::movement_metric.
RuntimeTrafficMode traffic_generator_mode
Which packet the internal generator sends to solicit CSI.
bool lowpass_enabled
Enable the low-pass filter on the turbulence stream.
WifiBandPolicy wifi_band_policy
Band available to the station while the runtime keeps the PHY at HT20.
uint32_t publish_interval_ms
Milliseconds between IRuntimeListener::on_periodic_update() callbacks.
uint64_t device_id
Stable device identity used by the ESPectre Protocol and CSI streaming.
uint8_t motion_on_hits
Consecutive above-threshold evaluations before reporting motion (1..20).
float lowpass_cutoff
Low-pass cutoff in Hz (5.0..20.0).
uint8_t stream_tx_batch_records
RuntimeProfile::STREAM only: CSI records coalesced into one datagram (1..7).
uint32_t evaluation_interval_ms
Detector evaluation cadence in milliseconds.
bool runtime_detector_selection_enabled
Advertise runtime detector switching.
uint16_t collector_port
RuntimeProfile::STREAM only: UDP port of the host CSI collector (1..65535).
Low-frequency counters and radio state used by optional diagnostic surfaces.
A consistent view of the sensing state at one instant.