1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
use anyhow::{anyhow, Result};
use esp_idf_hal::gpio::{InputMode, InputPin, PinDriver};
use std::sync::{Arc, Mutex};
use crate::{
infra::{Poller, State, Switch},
message::{Notifier, Trigger},
time::{sleep, yield_now},
};
/// Represents a button with a notifier and a GPIO pin.
///
/// # Type Parameters
/// * `'a` - Lifetime of the button.
/// * `T` - Type of the GPIO pin.
/// * `MODE` - Input mode of the GPIO pin.
/// * `TR` - The trigger type implementing the `Trigger` trait.
pub struct Button<'a, T, MODE, TR>
where
T: InputPin,
MODE: InputMode,
TR: Trigger,
{
notifier: Notifier<TR>,
trigger: &'static TR,
pin: PinDriver<'a, T, MODE>,
state: Arc<Mutex<State>>,
}
impl<'a, T, MODE, TR> Button<'a, T, MODE, TR>
where
T: InputPin,
MODE: InputMode,
TR: Trigger,
{
/// Creates a new `Button` instance.
///
/// # Arguments
/// * `notifier` - A notifier to send button press events.
/// * `trigger` - The trigger to emit when the button is pressed.
/// * `pin` - A GPIO pin driver.
/// * `state` - Shared state of the button.
///
/// # Returns
/// A new `Button` instance.
///
/// # Errors
/// Returns an error if the button cannot be initialized.
pub fn new(
notifier: Notifier<TR>,
trigger: &'static TR,
pin: PinDriver<'a, T, MODE>,
state: Arc<Mutex<State>>,
) -> Result<Self> {
Ok(Self {
notifier,
trigger,
pin,
state,
})
}
/// Checks if the button is pressed.
///
/// # Returns
/// `true` if the button is pressed, `false` otherwise.
fn pressed(&self) -> bool {
self.pin.is_low()
}
}
impl<T, MODE, TR> Poller for Button<'_, T, MODE, TR>
where
T: InputPin,
MODE: InputMode,
TR: Trigger,
{
/// Polls the button for state changes.
///
/// This function continuously checks the button state and notifies when it is pressed.
///
/// # Errors
/// Returns an error if the notifier fails or if the state cannot be toggled.
fn poll(&mut self) -> Result<!> {
// Using polling instead of interrupts for the button as on some boards
// (e.g. M5Stack's Atom Lite) the interrupt pin of the button is too close
// to the WiFi antenna which causes interference.
loop {
if self.pressed() {
self.notifier.notify(self.trigger)?;
self.toggle()?;
sleep(500);
}
yield_now();
}
}
}
impl<T, MODE, TR> Switch for Button<'_, T, MODE, TR>
where
T: InputPin,
MODE: InputMode,
TR: Trigger,
{
/// Toggles the state of the button.
///
/// # Returns
/// `Ok(())` on success.
///
/// # Errors
/// Returns an error if the mutex lock cannot be acquired.
fn toggle(&mut self) -> Result<()> {
let mut state = self
.state
.lock()
.map_err(|e| anyhow!("Mutex lock error: {:?}", e))?;
state.toggle();
Ok(())
}
}