LSH-Core
Deterministic firmware core for Controllino-based Labo Smart Home nodes
 
Loading...
Searching...
No Matches
actuator.hpp
Go to the documentation of this file.
1
21#ifndef LSH_CORE_PERIPHERALS_OUTPUT_ACTUATOR_HPP
22#define LSH_CORE_PERIPHERALS_OUTPUT_ACTUATOR_HPP
23
24#include <stdint.h>
25
28#include "internal/pin_tag.hpp"
30#ifdef CONFIG_USE_FAST_ACTUATORS
32#endif
34#include "util/time_keeper.hpp"
35
36#if !CONFIG_USE_COMPACT_ACTUATOR_SWITCH_TIMES && (LSH_EFFECTIVE_ACTUATOR_DEBOUNCE_TIME_MS != 0U || LSH_STATIC_CONFIG_AUTO_OFF_ACTUATORS > 0)
37#define LSH_CORE_ACTUATOR_NEEDS_LOCAL_SWITCH_TIME 1
38#else
39#define LSH_CORE_ACTUATOR_NEEDS_LOCAL_SWITCH_TIME 0
40#endif
41
42#if LSH_CORE_ACTUATOR_NEEDS_LOCAL_SWITCH_TIME || (CONFIG_USE_COMPACT_ACTUATOR_SWITCH_TIMES && LSH_STATIC_CONFIG_AUTO_OFF_ACTUATORS > 0)
43#define LSH_CORE_ACTUATOR_NEEDS_SWITCH_TIMESTAMP 1
44#else
45#define LSH_CORE_ACTUATOR_NEEDS_SWITCH_TIMESTAMP 0
46#endif
47
53{
54private:
55 static constexpr uint8_t ACTUATOR_FLAG_ACTUAL_STATE = 0x01U;
56 static constexpr uint8_t ACTUATOR_FLAG_PROTECTED = 0x02U;
57
58 static constexpr auto initialFlags(bool normalState) noexcept -> uint8_t
59 {
60 return normalState ? ACTUATOR_FLAG_ACTUAL_STATE : 0U;
61 }
62
63#ifndef CONFIG_USE_FAST_ACTUATORS
64 const uint8_t pinNumber;
65#else
66 const uint8_t pinMask;
67 volatile uint8_t *const pinPort;
68
75 explicit Actuator(lsh::core::avr::FastOutputPinBinding binding, bool normalState) noexcept :
76 pinMask(binding.mask), pinPort(binding.pinPort), flags(initialFlags(normalState))
77 {
78 // Prime the output latch before enabling the pin as OUTPUT. That keeps
79 // boot-time electrical transitions deterministic even if the previous
80 // firmware or bootloader left the port register in an unexpected state.
81 const uint8_t oldSREG = SREG;
82 cli();
83 if (!normalState)
84 {
85 *this->pinPort &= ~this->pinMask;
86 }
87 else
88 {
89 *this->pinPort |= this->pinMask;
90 }
91 *binding.modePort |= this->pinMask;
92 SREG = oldSREG;
93 }
94#endif
95#if defined(LSH_DEBUG) || defined(LSH_STATIC_CONFIG_RUNTIME_CHECKS)
96 uint8_t index = UINT8_MAX;
97#endif
98 uint8_t flags = 0U;
99#if LSH_CORE_ACTUATOR_NEEDS_LOCAL_SWITCH_TIME
100 uint32_t lastTimeSwitched = 0U;
101#endif
109 void writePinState(bool state)
110 {
111#ifdef CONFIG_USE_FAST_ACTUATORS
112 if (!state)
113 {
114 *this->pinPort &= ~this->pinMask;
115 }
116 else
117 {
118 *this->pinPort |= this->pinMask;
119 }
120#else
121 digitalWrite(this->pinNumber, static_cast<uint8_t>(state));
122#endif
123 }
124
132 void updateCachedStateFlag(bool state)
133 {
134 if (state)
135 {
136 this->flags |= ACTUATOR_FLAG_ACTUAL_STATE;
137 }
138 else
139 {
140 this->flags &= static_cast<uint8_t>(~ACTUATOR_FLAG_ACTUAL_STATE);
141 }
142 }
143
150 [[nodiscard]] auto wouldChangeState(bool state) const -> bool
151 {
152 const uint8_t stateFlag = state ? ACTUATOR_FLAG_ACTUAL_STATE : 0U;
153 return (this->flags & ACTUATOR_FLAG_ACTUAL_STATE) != stateFlag;
154 }
155
163 [[nodiscard]] auto debounceAllowsSwitch(uint32_t now_ms) const -> bool
164 {
165#if LSH_CORE_ACTUATOR_NEEDS_LOCAL_SWITCH_TIME
166 using constants::timings::ACTUATOR_DEBOUNCE_TIME_MS;
167 return ACTUATOR_DEBOUNCE_TIME_MS == 0U || (now_ms - this->lastTimeSwitched >= ACTUATOR_DEBOUNCE_TIME_MS);
168#else
169 static_cast<void>(now_ms);
170 return true;
171#endif
172 }
173
183 [[nodiscard]] auto applyStateChange(bool state, uint32_t now_ms, uint8_t actuatorIndex) -> bool;
184
193 template <uint8_t ActuatorIndex>
194 [[nodiscard]] __attribute__((always_inline)) inline auto applyStateChangeStatic(bool state, uint32_t now_ms) -> bool
195 {
196 static_assert(ActuatorIndex < CONFIG_MAX_ACTUATORS, "ActuatorIndex is outside the generated static profile.");
197 if (!this->debounceAllowsSwitch(now_ms))
198 {
199 return false;
200 }
201
202 this->writePinState(state);
203 this->updateCachedStateFlag(state);
204#if LSH_CORE_ACTUATOR_NEEDS_LOCAL_SWITCH_TIME
205 this->lastTimeSwitched = now_ms;
206#endif
207 Actuators::updatePackedStateStatic<ActuatorIndex>(state);
208#if CONFIG_USE_COMPACT_ACTUATOR_SWITCH_TIMES && LSH_STATIC_CONFIG_AUTO_OFF_ACTUATORS > 0
209 Actuators::recordSwitchTime(ActuatorIndex, now_ms);
210#endif
211 return true;
212 }
213
223 [[nodiscard]] auto runtimeIndex() const -> uint8_t
224 {
225#if defined(LSH_DEBUG) || defined(LSH_STATIC_CONFIG_RUNTIME_CHECKS)
226 return this->index;
227#else
228 return UINT8_MAX;
229#endif
230 }
231
232public:
233#ifndef CONFIG_USE_FAST_ACTUATORS
240 explicit LSH_OPTIONAL_CONSTEXPR_CTOR Actuator(uint8_t pin, bool normalState = false) noexcept :
241 pinNumber(pin), flags(initialFlags(normalState))
242 {
243 // Set the output latch first, then enable OUTPUT. This mirrors the fast
244 // path and avoids a short wrong-level pulse during construction.
245 digitalWrite(pin, static_cast<uint8_t>(normalState));
246 pinMode(pin, OUTPUT);
247 }
248
255 template <uint8_t Pin>
256 explicit LSH_OPTIONAL_CONSTEXPR_CTOR Actuator(lsh::core::PinTag<Pin>, bool normalState = false) noexcept :
257 Actuator(static_cast<uint8_t>(Pin), normalState)
258 {}
259#else
266 explicit Actuator(uint8_t pin, bool normalState = false) noexcept : Actuator(lsh::core::avr::makeFastOutputPinBinding(pin), normalState)
267 {}
268
276 template <uint8_t Pin>
277 explicit Actuator(lsh::core::PinTag<Pin>, bool normalState = false) noexcept :
278 Actuator(lsh::core::avr::makeFastOutputPinBinding(lsh::core::PinTag<Pin>{}), normalState)
279 {}
280#endif
281
282 /* Workaround for https://stackoverflow.com/questions/28788353/clang-wweak-vtables-and-pure-abstract-class
283 and https://stackoverflow.com/questions/28786473/clang-no-out-of-line-virtual-method-definitions-pure-abstract-c-class/40550578 */
284
285#if LSH_USING_CPP17
286 Actuator(const Actuator &) = delete;
287 Actuator(Actuator &&) = delete;
288 auto operator=(const Actuator &) -> Actuator & = delete;
289 auto operator=(Actuator &&) -> Actuator & = delete;
290#endif // LSH_USING_CPP17
291
292 // Setters
293 [[nodiscard]] auto setState(bool state) -> bool; // Sets the new state of the actuator, respecting debounce time.
294 [[nodiscard]] auto setState(bool state, uint32_t now_ms)
295 -> bool; // Sets the new state using a caller-cached timestamp for generated multi-actuator paths.
296 [[nodiscard]] auto setStateForIndex(uint8_t actuatorIndex, bool state)
297 -> bool; // Sets state while providing the generated dense actuator index.
298 [[nodiscard]] auto setStateForIndex(uint8_t actuatorIndex, bool state, uint32_t now_ms)
299 -> bool; // Sets state with generated dense index and caller-cached timestamp.
300
301 template <uint8_t ActuatorIndex> [[nodiscard]] auto setStateStatic(bool state) -> bool
302 {
303 if (!this->wouldChangeState(state))
304 {
305 return false;
306 }
307#if LSH_CORE_ACTUATOR_NEEDS_SWITCH_TIMESTAMP
308 return this->applyStateChangeStatic<ActuatorIndex>(state, timeKeeper::getTime());
309#else
310 return this->applyStateChangeStatic<ActuatorIndex>(state, 0U);
311#endif
312 }
313
314 template <uint8_t ActuatorIndex> [[nodiscard]] auto setStateStatic(bool state, uint32_t now_ms) -> bool
315 {
316 if (!this->wouldChangeState(state))
317 {
318 return false;
319 }
320 return this->applyStateChangeStatic<ActuatorIndex>(state, now_ms);
321 }
322
323 void setIndex(uint8_t indexToSet); // Set the actuator index on Actuators namespace Array
324 auto setProtected(bool hasProtection)
325 -> Actuator &; // Set protection against global "turn-off" actions (e.g., a general super long click).
326
327 // Getters
328 [[nodiscard]] auto getIndex() const -> uint8_t; // Get the actuator index on Actuators namespace Array
329 [[nodiscard]] auto getState() const -> bool; // Returns the state of the actuator (false=OFF, true=ON)
330
331 // Utils
332 [[nodiscard]] auto toggleState() -> bool; // Switch the actuator
333 [[nodiscard]] auto toggleState(uint32_t now_ms) -> bool; // Switch the actuator using a caller-cached timestamp
334 [[nodiscard]] auto toggleStateForIndex(uint8_t actuatorIndex) -> bool;
335 [[nodiscard]] auto toggleStateForIndex(uint8_t actuatorIndex, uint32_t now_ms) -> bool;
336
337 template <uint8_t ActuatorIndex> [[nodiscard]] auto toggleStateStatic() -> bool
338 {
339 return this->setStateStatic<ActuatorIndex>((this->flags & ACTUATOR_FLAG_ACTUAL_STATE) == 0U);
340 }
341
342 template <uint8_t ActuatorIndex> [[nodiscard]] auto toggleStateStatic(uint32_t now_ms) -> bool
343 {
344 return this->setStateStatic<ActuatorIndex>((this->flags & ACTUATOR_FLAG_ACTUAL_STATE) == 0U, now_ms);
345 }
346
347 [[nodiscard]] auto checkAutoOffTimer(uint32_t now_ms, uint32_t autoOffTimer_ms)
348 -> bool; // Checks the provided auto-off timer using a caller-cached timestamp.
349 [[nodiscard]] auto checkAutoOffTimerForIndex(uint8_t actuatorIndex, uint32_t now_ms, uint32_t autoOffTimer_ms)
350 -> bool; // Checks auto-off while providing the generated dense actuator index.
351};
352
353#endif // LSH_CORE_PERIPHERALS_OUTPUT_ACTUATOR_HPP
Declares the manager for the global collection of Actuator objects.
Typed helpers for AVR fast I/O access without Arduino macro casts.
Represents an actuator (relay) attached to a digital pin.
Definition actuator.hpp:53
auto checkAutoOffTimer(uint32_t now_ms, uint32_t autoOffTimer_ms) -> bool
Checks the provided auto-off timer, switch OFF the actuator if it's over.
Definition actuator.cpp:255
auto setStateForIndex(uint8_t actuatorIndex, bool state) -> bool
Set the new actuator state with a generated dense runtime index.
Definition actuator.cpp:81
auto toggleStateForIndex(uint8_t actuatorIndex) -> bool
Toggle the actuator with a generated dense runtime index.
Definition actuator.cpp:229
void setIndex(uint8_t indexToSet)
store the actuator index in Actuators namespace array.
Definition actuator.cpp:146
auto setProtected(bool hasProtection) -> Actuator &
Set protection against some turn ON/OFF behaviour.
Definition actuator.cpp:161
auto toggleState() -> bool
Switch the state of the actuator (if it was OFF is going to be ON and vice versa).
Definition actuator.cpp:205
LSH_OPTIONAL_CONSTEXPR_CTOR Actuator(lsh::core::PinTag< Pin >, bool normalState=false) noexcept
Construct an actuator from a compile-time pin tag on the slow-I/O path.
Definition actuator.hpp:256
auto getState() const -> bool
Get the state of the actuator.
Definition actuator.cpp:194
auto setState(bool state) -> bool
Set the new actuator state if the new state can be set.
Definition actuator.cpp:39
auto getIndex() const -> uint8_t
Get the actuator index on Actuators namespace array.
Definition actuator.cpp:179
auto checkAutoOffTimerForIndex(uint8_t actuatorIndex, uint32_t now_ms, uint32_t autoOffTimer_ms) -> bool
Checks an auto-off timer with the generated dense actuator index.
Definition actuator.cpp:269
LSH_OPTIONAL_CONSTEXPR_CTOR Actuator(uint8_t pin, bool normalState=false) noexcept
Construct a new Actuator object, conventional IO version.
Definition actuator.hpp:240
Centralized C++ feature-detection macros for lsh-core.
void recordSwitchTime(uint8_t actuatorIndex, uint32_t now_ms)
Records the latest switch time for an actuator when compact actuator timer storage is enabled.
Definition actuator_manager.cpp:155
auto getTime() -> uint32_t
Gets the cached timestamp from the last timeKeeper::update() call.
Definition time_keeper.hpp:42
Compile-time pin tag used to route peripheral construction through constant pin paths.
Type-level wrapper around one Arduino pin number.
Definition pin_tag.hpp:41
Resolved fast-output binding for one Arduino pin.
Definition avr_fast_io.hpp:62
volatile uint8_t * modePort
Final AVR DDR register used to configure the pin direction.
Definition avr_fast_io.hpp:65
volatile uint8_t * pinPort
Final AVR output register used to drive the pin.
Definition avr_fast_io.hpp:64
uint8_t mask
Final bit mask for the pin inside the AVR port.
Definition avr_fast_io.hpp:63
Declares a utility for consistent time management within the main loop.
Centralizes all build-time configurable timing constants for the framework.
Internal bridge that imports static profile resources into the library's scope.