use embassy_futures::select::{Either, select};
use embassy_time::Duration;
use embassy_time::Timer;
#[doc(hidden)]
pub const BUTTON_DEBOUNCE_DELAY: Duration = Duration::from_millis(10);
#[doc(hidden)]
pub const LONG_PRESS_DURATION: Duration = Duration::from_millis(500);
#[doc(hidden)]
pub const BUTTON_POLL_INTERVAL: Duration = Duration::from_millis(1);
#[allow(async_fn_in_trait)]
#[doc(hidden)]
pub trait __ButtonMonitor {
fn is_pressed_raw(&self) -> bool;
async fn wait_until_pressed_state(&mut self, pressed: bool);
}
#[allow(async_fn_in_trait)]
pub trait Button: __ButtonMonitor {
fn is_pressed(&self) -> bool {
<Self as __ButtonMonitor>::is_pressed_raw(self)
}
async fn wait_for_press(&mut self) {
loop {
<Self as __ButtonMonitor>::wait_until_pressed_state(self, false).await;
Timer::after(BUTTON_DEBOUNCE_DELAY).await;
if !self.is_pressed() {
break;
}
}
loop {
<Self as __ButtonMonitor>::wait_until_pressed_state(self, true).await;
Timer::after(BUTTON_DEBOUNCE_DELAY).await;
if self.is_pressed() {
break;
}
}
}
async fn wait_for_press_duration(&mut self) -> PressDuration {
loop {
<Self as __ButtonMonitor>::wait_until_pressed_state(self, false).await;
Timer::after(BUTTON_DEBOUNCE_DELAY).await;
if !self.is_pressed() {
break;
}
}
loop {
<Self as __ButtonMonitor>::wait_until_pressed_state(self, true).await;
Timer::after(BUTTON_DEBOUNCE_DELAY).await;
if self.is_pressed() {
break;
}
}
let wait_for_stable_up = async {
loop {
<Self as __ButtonMonitor>::wait_until_pressed_state(self, false).await;
Timer::after(BUTTON_DEBOUNCE_DELAY).await;
if !self.is_pressed() {
break;
}
}
};
match select(wait_for_stable_up, Timer::after(LONG_PRESS_DURATION)).await {
Either::First(_) => PressDuration::Short,
Either::Second(()) => PressDuration::Long,
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PressedTo {
Voltage,
Ground,
}
impl PressedTo {
#[must_use]
pub const fn pressed_is_high(self) -> bool {
matches!(self, Self::Voltage)
}
#[must_use]
pub const fn is_pressed(self, level_is_high: bool) -> bool {
if self.pressed_is_high() {
level_is_high
} else {
!level_is_high
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PressDuration {
Short,
Long,
}
#[cfg(test)]
mod tests {
use super::PressedTo;
#[test]
fn pressed_to_ground_maps_low_to_pressed() {
assert!(PressedTo::Ground.is_pressed(false));
assert!(!PressedTo::Ground.is_pressed(true));
}
#[test]
fn pressed_to_voltage_maps_high_to_pressed() {
assert!(!PressedTo::Voltage.is_pressed(false));
assert!(PressedTo::Voltage.is_pressed(true));
}
}