ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
rcwl0516.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 rcwl0516.cpp
6 * @brief One-GPIO presence facade - implementation. See rcwl0516.h.
7 *
8 * Three steps per sample: track how long the raw level has been steady, promote it to the believed
9 * level once it outlasts the debounce, then extend presence for the hold past the last believed
10 * HIGH. Every elapsed-time test is an unsigned difference (`now - stamp >= limit`), which is exactly
11 * what makes it wrap-safe: at a millis() rollover the subtraction wraps with it and still yields the
12 * true elapsed interval.
13 */
14
16
17#if PC_ENABLE_RCWL0516
18
19#if defined(ARDUINO)
20#include <Arduino.h>
21#endif
22namespace
23{
24// Elapsed-time test, wrap-safe across a millis() rollover (unsigned arithmetic is modulo 2^32).
25// A limit of 0 is always satisfied, which is what disables debounce / hold.
26inline bool elapsed(uint32_t now, uint32_t since, uint32_t limit)
27{
28 return (now - since) >= limit;
29}
30} // namespace
31
32void pc_presence_core_init(PresenceCore *c, uint32_t debounce_ms, uint32_t hold_ms, uint32_t now)
33{
34 if (!c)
35 {
36 return;
37 }
38 c->debounce_ms = debounce_ms;
39 c->hold_ms = hold_ms;
40 c->raw_since_ms = now;
41 c->last_high_ms = now;
42 c->raw = 0;
43 c->stable = 0;
44 c->present = 0; // fail-safe: absent until observed
45 c->changed = 0;
46}
47
48bool pc_presence_core_update(PresenceCore *c, bool pin_high, uint32_t now)
49{
50 if (!c)
51 {
52 return false;
53 }
54 const uint8_t lvl = pin_high ? 1u : 0u;
55
56 // 1) restart the debounce whenever the raw level moves
57 if (lvl != c->raw)
58 {
59 c->raw = lvl;
60 c->raw_since_ms = now;
61 }
62
63 // 2) believe the raw level once it has outlasted the debounce
64 if (elapsed(now, c->raw_since_ms, c->debounce_ms))
65 {
66 c->stable = c->raw;
67 }
68
69 // 3) presence follows the believed level, but decays only after the hold
70 const uint8_t was = c->present;
71 if (c->stable)
72 {
73 c->last_high_ms = now;
74 c->present = 1;
75 }
76 else if (c->present && elapsed(now, c->last_high_ms, c->hold_ms))
77 {
78 c->present = 0;
79 }
80
81 if (c->present != was)
82 {
83 c->changed = 1;
84 }
85 return c->present != 0;
86}
87
88bool pc_presence_core_get(const PresenceCore *c)
89{
90 return c && c->present != 0;
91}
92
93bool pc_presence_take_event(PresenceCore *c)
94{
95 if (!c || !c->changed)
96 {
97 return false;
98 }
99 c->changed = 0;
100 return true;
101}
102
103void pc_rcwl0516_core_init(PresenceCore *c, uint32_t now)
104{
105 pc_presence_core_init(c, PC_RCWL0516_DEBOUNCE_MS, PC_RCWL0516_HOLD_MS, now);
106}
107
108// ---------------------------------------------------------------------------
109// Binding
110// ---------------------------------------------------------------------------
111
112#if defined(ARDUINO)
113
114namespace
115{
116// All RCWL-0516 binding state, owned by one instance (internal linkage): the presence core and the
117// pin it samples, grouped so it is one named owner unreachable from any other translation unit.
118struct Rcwl0516Ctx
119{
120 PresenceCore core;
121 int pin = -1;
122};
123Rcwl0516Ctx s_rcwl;
124} // namespace
125
126bool pc_rcwl0516_begin(int out_pin)
127{
128 s_rcwl.pin = out_pin;
129 pinMode(out_pin, INPUT); // the module drives OUT actively; no pull needed
130 pc_rcwl0516_core_init(&s_rcwl.core, (uint32_t)millis());
131 return true;
132}
133
134bool pc_rcwl0516_poll()
135{
136 if (s_rcwl.pin < 0)
137 {
138 return false;
139 }
140 pc_presence_core_update(&s_rcwl.core, digitalRead(s_rcwl.pin) == HIGH, (uint32_t)millis());
141 return pc_presence_take_event(&s_rcwl.core);
142}
143
144bool pc_rcwl0516_present()
145{
146 return pc_presence_core_get(&s_rcwl.core);
147}
148
149#else // host build: no GPIO
150
151bool pc_rcwl0516_begin(int)
152{
153 return false;
154}
155
156bool pc_rcwl0516_poll()
157{
158 return false;
159}
160
161bool pc_rcwl0516_present()
162{
163 return false;
164}
165
166#endif // ARDUINO
167
168#endif // PC_ENABLE_RCWL0516
RCWL-0516 microwave Doppler presence sensor, and the shared one-GPIO presence facade (PC_ENABLE_RCWL0...