ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
rcwl0516.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#ifndef PROTOCORE_RCWL0516_H
5#define PROTOCORE_RCWL0516_H
6
7#include "protocore_config.h" // the entry point: protocore_types.h for the widths
8
10
11/**
12 * @file rcwl0516.h
13 * @brief RCWL-0516 microwave Doppler presence sensor, and the shared one-GPIO presence facade
14(PROTOCORE_ENABLE_RCWL0516).
15 *
16 * The RCWL-0516 (RCWL-9196 controller + MMBR941M RF amp, ~3.18 GHz Doppler) has no data protocol at
17 * all: a single 3.3 V **OUT** pin that latches HIGH when a moving reflector is detected and returns
18 * LOW once its own retrigger window expires. Everything interesting is therefore in *time*, not in
19 * bytes - which is what this module provides.
20 *
21 * Two problems a bare `digitalRead()` does not solve, and this does:
22 *
23 * 1. **Chatter.** The OUT pin is driven by an analog comparator, so around the detection threshold
24 * it can flicker. A raw read turns one person walking past into a burst of presence events.
25 * A level must therefore hold for @ref PresenceCore::debounce_ms before it is believed.
26 *
27 * 2. **Gaps.** The module drops OUT between retriggers, so a person who is present but briefly
28 * still reads as absent for a moment. Presence is therefore held for
29 * @ref PresenceCore::hold_ms past the last believed-HIGH sample, which turns a stream of
30 * retriggers into one continuous "occupied" span instead of a flapping boolean.
31 *
32 * The core is pure and takes an explicit @p now, exactly like `services/hotswap`: it decides, the
33 * binding acts. That makes the whole machine host-testable by injecting pin levels against a
34 * synthetic clock, with no GPIO and no real time involved. All timing comparisons are unsigned
35 * differences, so they are wrap-safe across a `millis()` rollover.
36 *
37 * @ref PresenceCore is deliberately sensor-agnostic: it is a debounced, hold-extended view of one
38 * active-high presence pin. The RCWL-0516 is simply its first user, via the
39 * @ref protocore_rcwl0516_core_init defaults - the HMMD's OUT pin, a PIR, or an HB100 can reuse the same
40 * core by supplying their own two constants.
41 *
42 * Fail-safe start: a freshly initialized core reports *absent* and treats the pin as idle, so
43 * presence is only ever reported after it has actually been observed and believed. Claiming
44 * presence you have not yet measured is the failure mode worth avoiding.
45 *
46 * @c work is PROTOCORE_RCWL0516_BORROW bytes the CALLER took, at an address it knows. It is not held past the call, so
47nothing here aliases it. How those bytes are
48 * carved is this module's and is never named here.
49 *
50 * @author Douglas Quigg (dstroy0)
51 * @date 2026
52 */
53
54/**
55 * @brief Default hold time (ms) for the RCWL-0516.
56 *
57 * The module's own retrigger window is ~2 s, so holding for at least that long bridges the gap
58 * between retriggers while a target is still present.
59 */
60#ifndef PROTOCORE_RCWL0516_HOLD_MS
61#define PROTOCORE_RCWL0516_HOLD_MS 2000
62#endif
63
64/** @brief Default debounce (ms) for the RCWL-0516 - long enough to swallow comparator chatter. */
65#ifndef PROTOCORE_RCWL0516_DEBOUNCE_MS
66#define PROTOCORE_RCWL0516_DEBOUNCE_MS 50
67#endif
68
69/** @brief Debounced, hold-extended state of one active-high presence pin. Pure: it decides. */
70typedef struct
71{
72 uint32_t debounce_ms; ///< a level must hold this long before it is believed.
73 uint32_t hold_ms; ///< presence persists this long past the last believed-HIGH sample.
74 uint32_t raw_since_ms; ///< when the raw pin level last changed.
75 uint32_t last_high_ms; ///< when the believed level was last HIGH.
76 uint8_t raw; ///< last raw pin level as sampled (0/1).
77 uint8_t stable; ///< believed level, after debouncing (0/1).
78 uint8_t present; ///< presence output (0/1) - @ref stable, extended by @ref hold_ms.
79 uint8_t changed; ///< set when @ref present flipped; cleared by @ref protocore_presence_take_event.
81
82/** @brief Dispatch table. Addressed by offset, so the layout is asserted below. */
83typedef struct
84{
85 void (*presence_init)(uint8_t *, PresenceCore *, uint32_t, uint32_t, uint32_t);
86 proto_bool (*presence_update)(uint8_t *, PresenceCore *, proto_bool, uint32_t);
87 proto_bool (*presence_get)(uint8_t *, const PresenceCore *);
88 proto_bool (*presence_take_event)(uint8_t *, PresenceCore *);
89 void (*core_init)(uint8_t *, PresenceCore *, uint32_t);
90 proto_bool (*begin)(uint8_t *, int);
91 proto_bool (*poll)(uint8_t *);
92 void (*present)(uint8_t *);
94PROTOCORE_NS_LAYOUT(Rcwl0516Ns, presence_init, presence_update, presence_get, presence_take_event, core_init, begin,
95 poll, present);
96
97/**
98 * @brief Initialize to *absent* at now, with the pin treated as idle (LOW). .
99 * @param work PROTOCORE_RCWL0516_BORROW bytes the caller took. Not held past the call.
100 * @param c C
101 * @param debounce_ms 0 disables debouncing (every sample is believed immediately)
102 * @param hold_ms 0 disables the hold (presence follows the debounced level exactly)
103 * @param now Now
104 */
105void protocore_rcwl0516_presence_init(uint8_t *work, PresenceCore *c, uint32_t debounce_ms, uint32_t hold_ms,
106 uint32_t now);
107/**
108 * @brief Feed one sample of the presence pin. Call it as often as .
109 * @param work PROTOCORE_RCWL0516_BORROW bytes the caller took. Not held past the call.
110 * @param c C
111 * @param pin_high Pin high
112 * @param now Now
113 * @return PROTO_TRUE on success.
114 */
115proto_bool protocore_rcwl0516_presence_update(uint8_t *work, PresenceCore *c, proto_bool pin_high, uint32_t now);
116/**
117 * @brief Current presence, without sampling.
118 * @param work PROTOCORE_RCWL0516_BORROW bytes the caller took. Not held past the call.
119 * @param c C
120 * @return PROTO_TRUE on success.
121 */
123/**
124 * @brief Consume the presence-changed event.
125 * @param work PROTOCORE_RCWL0516_BORROW bytes the caller took. Not held past the call.
126 * @param c C
127 * @return PROTO_TRUE on success.
128 */
130/**
131 * @brief Initialize c with the RCWL-0516 defaults .
132 * @param work PROTOCORE_RCWL0516_BORROW bytes the caller took. Not held past the call.
133 * @param c C
134 * @param now Now
135 */
136void protocore_rcwl0516_core_init(uint8_t *work, PresenceCore *c, uint32_t now);
137/**
138 * @brief Configure out_pin as an input and start the core. true where the .
139 * @param work PROTOCORE_RCWL0516_BORROW bytes the caller took. Not held past the call.
140 * @param out_pin Out pin
141 * @return PROTO_TRUE on success.
142 */
143proto_bool protocore_rcwl0516_begin(uint8_t *work, int out_pin);
144/**
145 * @brief Sample the pin at the current time. true if presence changed on .
146 * @param work PROTOCORE_RCWL0516_BORROW bytes the caller took. Not held past the call.
147 * @return PROTO_TRUE on success.
148 */
150/**
151 * @brief Latest debounced, hold-extended presence.
152 * @param work PROTOCORE_RCWL0516_BORROW bytes the caller took. Not held past the call.
153 */
154void protocore_rcwl0516_present(uint8_t *work);
155
156/**
157 * @brief The PROTOCORE_RCWL0516_BORROW bytes this module's state lives in.
158 *
159 * Stated beside the namespace rather than on it: an entry takes a borrow, and this is where
160 * that borrow comes from. Taken once from the end of the pool, which no mark and no release
161 * walks, so the state lasts the life of the program.
162 *
163 * @return the span.
164 */
166
167/** @brief Module namespace. */
176
178
179#endif // PROTOCORE_RCWL0516_H
#define PROTOCORE_NS_LAYOUT(T,...)
Pin every dispatch slot of a table that is nothing but function pointers.
#define PROTOCORE_NS
Storage for a dispatch table. The const is load bearing.
void protocore_rcwl0516_presence_init(uint8_t *work, PresenceCore *c, uint32_t debounce_ms, uint32_t hold_ms, uint32_t now)
Initialize to absent at now, with the pin treated as idle (LOW). .
proto_bool protocore_rcwl0516_presence_get(uint8_t *work, const PresenceCore *c)
Current presence, without sampling.
proto_bool protocore_rcwl0516_presence_take_event(uint8_t *work, PresenceCore *c)
Consume the presence-changed event.
uint8_t * protocore_rcwl0516_span(void)
The PROTOCORE_RCWL0516_BORROW bytes this module's state lives in.
proto_bool protocore_rcwl0516_poll(uint8_t *work)
Sample the pin at the current time. true if presence changed on .
void protocore_rcwl0516_core_init(uint8_t *work, PresenceCore *c, uint32_t now)
Initialize c with the RCWL-0516 defaults .
PROTOCORE_NS Rcwl0516Ns Rcwl0516 PROTOCORE_UNUSED
Module namespace.
Definition rcwl0516.h:168
proto_bool protocore_rcwl0516_begin(uint8_t *work, int out_pin)
Configure out_pin as an input and start the core. true where the .
void protocore_rcwl0516_present(uint8_t *work)
Latest debounced, hold-extended presence.
proto_bool protocore_rcwl0516_presence_update(uint8_t *work, PresenceCore *c, proto_bool pin_high, uint32_t now)
Feed one sample of the presence pin. Call it as often as .
Debounced, hold-extended state of one active-high presence pin. Pure: it decides.
Definition rcwl0516.h:71
uint32_t hold_ms
presence persists this long past the last believed-HIGH sample.
Definition rcwl0516.h:73
uint8_t present
presence output (0/1) - stable, extended by hold_ms.
Definition rcwl0516.h:78
uint32_t raw_since_ms
when the raw pin level last changed.
Definition rcwl0516.h:74
uint8_t changed
set when present flipped; cleared by protocore_presence_take_event.
Definition rcwl0516.h:79
uint8_t raw
last raw pin level as sampled (0/1).
Definition rcwl0516.h:76
uint32_t debounce_ms
a level must hold this long before it is believed.
Definition rcwl0516.h:72
uint32_t last_high_ms
when the believed level was last HIGH.
Definition rcwl0516.h:75
uint8_t stable
believed level, after debouncing (0/1).
Definition rcwl0516.h:77
Dispatch table. Addressed by offset, so the layout is asserted below.
Definition rcwl0516.h:84
void(* presence_init)(uint8_t *, PresenceCore *, uint32_t, uint32_t, uint32_t)
Definition rcwl0516.h:85
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97