Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
radio_poll.h
Go to the documentation of this file.
1#pragma once
2
3/// @file radio_poll.h
4/// @brief Preemption-safe "poll a condition until it holds or a deadline passes" helper.
5/// @ingroup hioc_radio
6///
7/// Every blocking radio wait that samples a device and then compares a clock has the same hazard:
8/// if the task is descheduled between the two reads for longer than the timeout (a Wi-Fi scan, a
9/// flash write), the clock says "expired" while the device sample is already stale. Deciding on
10/// that stale sample declares a ready device dead. poll_until() fixes the order once, for every
11/// wait that must not misjudge a device on the strength of an old sample.
12
13#include "esphome/core/hal.h"
14#include "esphome/core/application.h"
15
16#include <cstdint>
17
18namespace esphome {
19namespace home_io_control {
20
21/// Outcome of @ref poll_until.
22struct PollResult {
23 bool ready{false}; ///< The condition held on the deciding sample.
24 uint32_t elapsed_ms{0}; ///< Time from the first sample to the last clock read before the deciding one.
25};
26
27/// @brief Poll `ready()` until it is true or `timeout_ms` has passed, feeding the watchdog between samples.
28///
29/// The decision to give up is always taken on a sample read *after* the deadline was seen to pass,
30/// so a task preempted for longer than the timeout can only make the wait longer; it can never make
31/// a condition that became true look false. A caller can tell such an overrun from a clean success
32/// by `ready && elapsed_ms > timeout_ms`.
33///
34/// The start time is the only clock read before the first sample, and each loop iteration reads the
35/// clock once, so an already-satisfied condition costs exactly one `millis()` call (this runs before
36/// every SPI transaction on the software-PHY chips, and host tests on the counting clock rely on it).
37/// @tparam Ready Callable returning bool; true means the condition holds.
38/// @param ready Condition to sample.
39/// @param timeout_ms Window in milliseconds; the wait gives up once more than this has elapsed.
40template<typename Ready> PollResult poll_until(Ready ready, uint32_t timeout_ms) {
41 uint32_t const start = millis();
42 if (ready())
43 return {true, 0};
44 while (true) {
45 uint32_t const elapsed = millis() - start;
46 if (elapsed > timeout_ms) {
47 bool const last = ready(); // the deciding sample, taken after the deadline
48 return {last, elapsed};
49 }
50 if (ready())
51 return {true, elapsed};
52 App.feed_wdt();
53 }
54}
55
56} // namespace home_io_control
57} // namespace esphome
PollResult poll_until(Ready ready, uint32_t timeout_ms)
Poll ready() until it is true or timeout_ms has passed, feeding the watchdog between samples.
Definition radio_poll.h:40
uint32_t elapsed_ms
Time from the first sample to the last clock read before the deciding one.
Definition radio_poll.h:24
bool ready
The condition held on the deciding sample.
Definition radio_poll.h:23