feat: add a finite state machine for debouncing
This commit is contained in:
parent
93f5d8e904
commit
3f519376b2
2 changed files with 57 additions and 11 deletions
64
src/gpio.rs
64
src/gpio.rs
|
|
@ -1,4 +1,4 @@
|
|||
use std::time::Duration;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::fmt;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
|
|
@ -11,6 +11,14 @@ pub enum CircuitEvent {
|
|||
Closed,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum FsmState {
|
||||
Idle,
|
||||
DebouncingHigh,
|
||||
High,
|
||||
DebouncingLow,
|
||||
}
|
||||
|
||||
impl fmt::Display for CircuitEvent {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
|
|
@ -23,32 +31,68 @@ impl fmt::Display for CircuitEvent {
|
|||
pub struct GpioMonitor {
|
||||
pin: InputPin,
|
||||
poll_interval: Duration,
|
||||
debounce_delay: Duration,
|
||||
state: FsmState,
|
||||
last_potential_transition_time: Instant,
|
||||
}
|
||||
|
||||
impl GpioMonitor {
|
||||
pub fn new(pin_number: u8, poll_interval: Duration) -> Result<Self> {
|
||||
pub fn new(pin_number: u8, poll_interval: Duration, debounce_delay: Duration) -> Result<Self> {
|
||||
let gpio = Gpio::new()
|
||||
.context("Failed to initialize GPIO")?;
|
||||
let pin = gpio.get(pin_number)
|
||||
.context(format!("Failed to get GPIO pin {}", pin_number))?
|
||||
.into_input_pullup();
|
||||
|
||||
Ok(Self { pin, poll_interval })
|
||||
Ok(Self {
|
||||
pin,
|
||||
poll_interval,
|
||||
debounce_delay,
|
||||
state: FsmState::Idle,
|
||||
last_potential_transition_time: Instant::now(),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn monitor<F>(&mut self, mut callback: F) -> Result<()>
|
||||
where
|
||||
F: FnMut(CircuitEvent) + Send + 'static,
|
||||
{
|
||||
let mut previous_state = self.get_current_state();
|
||||
callback(previous_state); // Send initial state
|
||||
|
||||
loop {
|
||||
let current_state = self.get_current_state();
|
||||
let current_switch_reading = self.get_current_state() == CircuitEvent::Closed;
|
||||
let time_since_last_change = self.last_potential_transition_time.elapsed();
|
||||
|
||||
if current_state != previous_state {
|
||||
callback(current_state);
|
||||
previous_state = current_state;
|
||||
match self.state {
|
||||
FsmState::Idle => {
|
||||
if current_switch_reading {
|
||||
self.state = FsmState::DebouncingHigh;
|
||||
self.last_potential_transition_time = Instant::now();
|
||||
}
|
||||
}
|
||||
|
||||
FsmState::DebouncingHigh => {
|
||||
if !current_switch_reading {
|
||||
self.state = FsmState::Idle;
|
||||
} else if time_since_last_change >= self.debounce_delay {
|
||||
self.state = FsmState::High;
|
||||
callback(CircuitEvent::Closed);
|
||||
}
|
||||
}
|
||||
|
||||
FsmState::High => {
|
||||
if !current_switch_reading {
|
||||
self.state = FsmState::DebouncingLow;
|
||||
self.last_potential_transition_time = Instant::now();
|
||||
}
|
||||
}
|
||||
|
||||
FsmState::DebouncingLow => {
|
||||
if current_switch_reading {
|
||||
self.state = FsmState::High;
|
||||
} else if time_since_last_change >= self.debounce_delay {
|
||||
self.state = FsmState::Idle;
|
||||
callback(CircuitEvent::Open);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tokio::time::sleep(self.poll_interval).await;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt};
|
|||
|
||||
const DEFAULT_GPIO_PIN: u8 = 17;
|
||||
const DEFAULT_POLL_INTERVAL_MS: u64 = 100;
|
||||
const DEFAULT_DEBOUNCE_DELAY_SECS: u64 = 5;
|
||||
const LOG_DIR: &str = "logs";
|
||||
const LOG_PREFIX: &str = "noisebell";
|
||||
const LOG_SUFFIX: &str = "log";
|
||||
|
|
@ -54,7 +55,8 @@ async fn main() -> Result<()> {
|
|||
info!("initializing gpio monitor");
|
||||
let mut gpio_monitor = gpio::GpioMonitor::new(
|
||||
DEFAULT_GPIO_PIN,
|
||||
Duration::from_millis(DEFAULT_POLL_INTERVAL_MS)
|
||||
Duration::from_millis(DEFAULT_POLL_INTERVAL_MS),
|
||||
Duration::from_secs(DEFAULT_DEBOUNCE_DELAY_SECS)
|
||||
)?;
|
||||
|
||||
// Set up the callback for state changes
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue