use anyhow::{Error, Result}; use std::{num::{NonZero, NonZeroU32}, sync::Arc}; use esp_idf_svc::{hal::{gpio::{Input, InputPin, InterruptType, Output, OutputPin, PinDriver}, task::notification::{Notification, Notifier}}, sys::EspError}; pub trait Indicator { fn set_state(&mut self, state: bool) -> Result<()>; fn get_state(&self) -> bool; } pub trait Button { fn subscribe(&mut self) -> Result<()>; fn unsubscribe(&mut self) -> Result<()>; fn wait_event(&self, timeout: u32) -> Option>; } pub struct GPIOButton<'d, P: InputPin> { pin: PinDriver<'d, P, Input>, int_type: InterruptType, notification: Notification, notifier: Arc, } impl<'d, P: InputPin> GPIOButton<'d, P> { pub fn new(pin: P, int_type: InterruptType) -> Result { let notification: Notification = Notification::new(); let notifier = notification.notifier(); return Ok(Self { pin: PinDriver::input(pin)?, int_type: int_type, notification: notification, notifier: notifier, }); } pub fn enable_interrupt(&mut self) -> Result<(), EspError>{ self.pin.enable_interrupt()?; return Ok(()); } } impl<'d, P: InputPin> Button for GPIOButton<'d, P> { fn subscribe(&mut self) -> Result<()> { self.pin.set_interrupt_type(self.int_type)?; unsafe { let notifier = self.notifier.clone(); self.pin.subscribe(move || { notifier.notify_and_yield(NonZeroU32::new(1).unwrap()); })?; } self.pin.enable_interrupt()?; return Ok(()); } fn unsubscribe(&mut self) -> Result<()> { self.pin.disable_interrupt()?; self.pin.unsubscribe()?; return Ok(()); } fn wait_event(&self, timeout: u32) -> Option> { return self.notification.wait(timeout); } } pub struct GPIOIndicator<'d, P: OutputPin> { pin: PinDriver<'d, P, Output>, inverted: bool, } impl<'d, P: OutputPin> GPIOIndicator<'d, P> { pub fn new(pin: P) -> Result { let mut indicator = Self { pin: PinDriver::output(pin)?, inverted: false, }; indicator.set_state(false)?; return Ok(indicator); } pub fn set_inverted(&mut self, value: bool) { self.inverted = value; } } impl<'d, P: OutputPin> Indicator for GPIOIndicator<'d, P> { fn set_state(&mut self, state: bool) -> Result<()> { if state != !self.inverted { self.pin.set_high()?; } else { self.pin.set_low()?; } return Ok(()); } fn get_state(&self) -> bool { return self.pin.is_set_high() != !self.inverted; } } pub struct GPIOIndicatedButton<'d, B: InputPin, I: OutputPin> { pub button: GPIOButton<'d, B>, pub indicator: GPIOIndicator<'d, I>, } impl<'d, B: InputPin, I: OutputPin> GPIOIndicatedButton<'d, B, I> { pub fn new(btn_pin: B, btn_int_type: InterruptType, indicator_pin: I) -> Result { return Ok(Self { button: GPIOButton::new(btn_pin, btn_int_type)?, indicator: GPIOIndicator::new(indicator_pin)?, }); } }