1#[cfg(target_os = "none")]
6use core::sync::atomic::{AtomicBool, Ordering};
7#[cfg(target_os = "none")]
8use device_envoy_core::button::{Button, __ButtonMonitor};
9#[cfg(target_os = "none")]
10use embassy_executor::Spawner;
11#[cfg(target_os = "none")]
12use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal};
13#[cfg(target_os = "none")]
14use embassy_time::Timer;
15
16#[cfg(target_os = "none")]
17use super::{PressDuration, PressedTo};
18#[cfg(target_os = "none")]
19use crate::{Error, Result};
20
21#[cfg(target_os = "none")]
23#[doc(hidden)]
26pub struct ButtonWatchStaticEsp {
27 signal: Signal<CriticalSectionRawMutex, PressDuration>,
28 state_signal: Signal<CriticalSectionRawMutex, bool>,
29 state_changed_signal: Signal<CriticalSectionRawMutex, ()>,
30 initialized_signal: Signal<CriticalSectionRawMutex, ()>,
31 is_pressed: AtomicBool,
32 initialized: AtomicBool,
33}
34
35#[cfg(target_os = "none")]
36impl ButtonWatchStaticEsp {
37 const fn new() -> Self {
38 Self {
39 signal: Signal::new(),
40 state_signal: Signal::new(),
41 state_changed_signal: Signal::new(),
42 initialized_signal: Signal::new(),
43 is_pressed: AtomicBool::new(false),
44 initialized: AtomicBool::new(false),
45 }
46 }
47}
48
49#[cfg(target_os = "none")]
54#[doc(hidden)]
57pub struct ButtonWatchEsp<'a> {
58 signal: &'a Signal<CriticalSectionRawMutex, PressDuration>,
59 state_signal: &'a Signal<CriticalSectionRawMutex, bool>,
60 state_changed_signal: &'a Signal<CriticalSectionRawMutex, ()>,
61 initialized_signal: &'a Signal<CriticalSectionRawMutex, ()>,
62 is_pressed: &'a AtomicBool,
63 initialized: &'a AtomicBool,
64}
65
66#[cfg(target_os = "none")]
67impl ButtonWatchEsp<'_> {
68 #[must_use]
70 pub const fn new_static() -> ButtonWatchStaticEsp {
71 ButtonWatchStaticEsp::new()
72 }
73
74 #[must_use = "Must be used to manage the spawned task"]
76 pub async fn new_from_pin(
77 button_watch_static: &'static ButtonWatchStaticEsp,
78 button_pin: impl esp_hal::gpio::InputPin + 'static,
79 pressed_to: PressedTo,
80 spawner: Spawner,
81 ) -> Result<Self> {
82 let pull = match pressed_to {
83 PressedTo::Voltage => esp_hal::gpio::Pull::Down,
84 PressedTo::Ground => esp_hal::gpio::Pull::Up,
85 };
86 let input = esp_hal::gpio::Input::new(
87 button_pin,
88 esp_hal::gpio::InputConfig::default().with_pull(pull),
89 );
90 let token = button_watch_task_from_pin(
91 input,
92 pressed_to,
93 &button_watch_static.signal,
94 &button_watch_static.state_signal,
95 &button_watch_static.state_changed_signal,
96 &button_watch_static.initialized_signal,
97 &button_watch_static.is_pressed,
98 &button_watch_static.initialized,
99 );
100 spawner.spawn(token).map_err(Error::TaskSpawn)?;
101 let button_watch = Self {
102 signal: &button_watch_static.signal,
103 state_signal: &button_watch_static.state_signal,
104 state_changed_signal: &button_watch_static.state_changed_signal,
105 initialized_signal: &button_watch_static.initialized_signal,
106 is_pressed: &button_watch_static.is_pressed,
107 initialized: &button_watch_static.initialized,
108 };
109 button_watch.wait_until_initialized().await;
110 Ok(button_watch)
111 }
112
113 pub async fn wait_until_initialized(&self) {
115 if self.initialized.load(Ordering::Acquire) {
116 return;
117 }
118 self.initialized_signal.wait().await;
119 }
120
121 pub async fn wait_for_state_change(&self) {
123 self.state_changed_signal.wait().await;
124 }
125}
126
127#[cfg(target_os = "none")]
128impl __ButtonMonitor for ButtonWatchEsp<'_> {
129 fn is_pressed_raw(&self) -> bool {
130 self.is_pressed.load(Ordering::Relaxed)
131 }
132
133 async fn wait_until_pressed_state(&mut self, pressed: bool) {
134 if self.is_pressed.load(Ordering::Relaxed) == pressed {
135 return;
136 }
137
138 loop {
139 let state = self.state_signal.wait().await;
140 if state == pressed {
141 return;
142 }
143 }
144 }
145}
146
147#[cfg(target_os = "none")]
148impl Button for ButtonWatchEsp<'_> {
149 async fn wait_for_press_duration(&mut self) -> PressDuration {
150 self.signal.wait().await
151 }
152}
153
154#[embassy_executor::task]
155#[cfg(target_os = "none")]
156async fn button_watch_task_from_pin(
157 mut input: esp_hal::gpio::Input<'static>,
158 pressed_to: PressedTo,
159 signal: &'static Signal<CriticalSectionRawMutex, PressDuration>,
160 state_signal: &'static Signal<CriticalSectionRawMutex, bool>,
161 state_changed_signal: &'static Signal<CriticalSectionRawMutex, ()>,
162 initialized_signal: &'static Signal<CriticalSectionRawMutex, ()>,
163 is_pressed: &'static AtomicBool,
164 initialized: &'static AtomicBool,
165) -> ! {
166 let mut input_button = InputButton {
167 input: &mut input,
168 pressed_to,
169 };
170 signal_press_durations(
171 &mut input_button,
172 signal,
173 state_signal,
174 state_changed_signal,
175 initialized_signal,
176 is_pressed,
177 initialized,
178 )
179 .await
180}
181
182#[cfg(target_os = "none")]
183struct InputButton<'a> {
184 input: &'a mut esp_hal::gpio::Input<'static>,
185 pressed_to: PressedTo,
186}
187
188#[cfg(target_os = "none")]
189impl __ButtonMonitor for InputButton<'_> {
190 fn is_pressed_raw(&self) -> bool {
191 self.pressed_to.is_pressed(self.input.is_high())
192 }
193
194 async fn wait_until_pressed_state(&mut self, pressed: bool) {
195 match (pressed, self.pressed_to) {
196 (true, PressedTo::Voltage) | (false, PressedTo::Ground) => {
197 self.input.wait_for_high().await;
198 }
199 (true, PressedTo::Ground) | (false, PressedTo::Voltage) => {
200 self.input.wait_for_low().await;
201 }
202 }
203 }
204}
205
206#[cfg(target_os = "none")]
207async fn signal_press_durations<B: __ButtonMonitor>(
208 button: &mut B,
209 signal: &'static Signal<CriticalSectionRawMutex, PressDuration>,
210 state_signal: &'static Signal<CriticalSectionRawMutex, bool>,
211 state_changed_signal: &'static Signal<CriticalSectionRawMutex, ()>,
212 initialized_signal: &'static Signal<CriticalSectionRawMutex, ()>,
213 is_pressed: &'static AtomicBool,
214 initialized: &'static AtomicBool,
215) -> ! {
216 let initial_pressed = <B as __ButtonMonitor>::is_pressed_raw(button);
217 is_pressed.store(initial_pressed, Ordering::Relaxed);
218 state_signal.signal(initial_pressed);
219 initialized.store(true, Ordering::Release);
220 initialized_signal.signal(());
221
222 loop {
223 <B as __ButtonMonitor>::wait_until_pressed_state(button, false).await;
224
225 <B as __ButtonMonitor>::wait_until_pressed_state(button, true).await;
226 is_pressed.store(true, Ordering::Relaxed);
227 state_signal.signal(true);
228 state_changed_signal.signal(());
229
230 Timer::after(device_envoy_core::button::BUTTON_DEBOUNCE_DELAY).await;
231 if !<B as __ButtonMonitor>::is_pressed_raw(button) {
232 is_pressed.store(false, Ordering::Relaxed);
233 state_signal.signal(false);
234 state_changed_signal.signal(());
235 continue;
236 }
237
238 let press_duration = embassy_futures::select::select(
239 <B as __ButtonMonitor>::wait_until_pressed_state(button, false),
240 Timer::after(device_envoy_core::button::LONG_PRESS_DURATION),
241 )
242 .await;
243
244 match press_duration {
245 embassy_futures::select::Either::First(()) => {
246 is_pressed.store(false, Ordering::Relaxed);
247 state_signal.signal(false);
248 state_changed_signal.signal(());
249 signal.signal(PressDuration::Short);
250 }
251 embassy_futures::select::Either::Second(()) => {
252 signal.signal(PressDuration::Long);
253 <B as __ButtonMonitor>::wait_until_pressed_state(button, false).await;
254 is_pressed.store(false, Ordering::Relaxed);
255 state_signal.signal(false);
256 state_changed_signal.signal(());
257 }
258 }
259 }
260}
261
262#[doc(hidden)]
345#[macro_export]
346macro_rules! button_watch {
347 ($($tt:tt)*) => { $crate::__button_watch_impl! { $($tt)* } };
348}
349
350#[doc(hidden)]
354#[macro_export]
355macro_rules! __button_watch_impl {
356 (
357 $(#[$meta:meta])*
358 $vis:vis $name:ident {
359 pin: $pin:ident,
360 }
361 ) => {
362 $crate::__button_watch_impl! {
363 @impl
364 meta: [$(#[$meta])*],
365 vis: $vis,
366 name: $name,
367 pin: $pin
368 }
369 };
370 (
371 $(#[$meta:meta])*
372 $name:ident {
373 pin: $pin:ident,
374 }
375 ) => {
376 $crate::__button_watch_impl! {
377 @impl
378 meta: [$(#[$meta])*],
379 vis: ,
380 name: $name,
381 pin: $pin
382 }
383 };
384 (
385 @impl
386 meta: [$(#[$meta:meta])*],
387 vis: $vis:vis,
388 name: $name:ident,
389 pin: $pin:ident
390 ) => {
391 ::paste::paste! {
392 $(#[$meta])*
393 #[doc = concat!(
394 "Button monitor generated by [`button_watch!`].\n\n",
395 "Monitors button presses in a background task. ",
396 "See the [button module documentation](mod@$crate::button) for usage."
397 )]
398 $vis struct $name {
399 button_watch: $crate::button::ButtonWatchEsp<'static>,
400 }
401
402 impl $name {
403 pub async fn new(
415 button_pin: $crate::esp_hal::peripherals::$pin<'static>,
416 pressed_to: $crate::button::PressedTo,
417 spawner: ::embassy_executor::Spawner,
418 ) -> $crate::Result<&'static mut Self> {
419 static BUTTON_WATCH_STATIC: $crate::button::ButtonWatchStaticEsp =
420 $crate::button::ButtonWatchEsp::new_static();
421 static BUTTON_WATCH_CELL: ::static_cell::StaticCell<$name> =
422 ::static_cell::StaticCell::new();
423
424 let button_watch = $crate::button::ButtonWatchEsp::new_from_pin(
425 &BUTTON_WATCH_STATIC,
426 button_pin,
427 pressed_to,
428 spawner,
429 )
430 .await?;
431
432 let instance = BUTTON_WATCH_CELL.init($name { button_watch });
433 Ok(instance)
434 }
435 }
436
437 impl ::core::ops::Deref for $name {
438 type Target = $crate::button::ButtonWatchEsp<'static>;
439
440 fn deref(&self) -> &Self::Target {
441 &self.button_watch
442 }
443 }
444
445 impl $crate::button::__ButtonMonitor for $name {
446 fn is_pressed_raw(&self) -> bool {
447 <$crate::button::ButtonWatchEsp<'static> as $crate::button::Button>::is_pressed(
448 &self.button_watch,
449 )
450 }
451
452 async fn wait_until_pressed_state(&mut self, pressed: bool) {
453 <$crate::button::ButtonWatchEsp<'static> as $crate::button::__ButtonMonitor>::wait_until_pressed_state(
454 &mut self.button_watch,
455 pressed,
456 )
457 .await
458 }
459 }
460
461 impl $crate::button::Button for $name {
462 async fn wait_for_press_duration(&mut self) -> $crate::button::PressDuration {
463 <$crate::button::ButtonWatchEsp<'static> as $crate::button::Button>::wait_for_press_duration(
464 &mut self.button_watch,
465 )
466 .await
467 }
468 }
469 }
470 };
471}