diff --git a/Cargo.lock b/Cargo.lock
index 831ec41..2089447 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -312,6 +312,7 @@ dependencies = [
"embedded-graphics",
"embedded-graphics-core",
"embedded-hal 1.0.0",
+ "embedded-hal-async",
"embedded-hal-bus",
"embedded-io 0.7.1",
"esp-alloc",
diff --git a/Cargo.toml b/Cargo.toml
index 298d773..d70a2d6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -45,6 +45,7 @@ pn532 = "0.5.0"
ndef = "0.5.0"
num_enum = { version = "0.7.6", default-features = false }
embassy-sync = "0.8.0"
+embedded-hal-async = "1.0.0"
# For fine tuning these settings, please refer to https://doc.rust-lang.org/cargo/reference/profiles.html
[profile.dev]
diff --git a/src/bin/main.rs b/src/bin/main.rs
index 6da0014..78ec0fc 100644
--- a/src/bin/main.rs
+++ b/src/bin/main.rs
@@ -75,12 +75,12 @@ async fn main(spawner: Spawner) {
let button_config = InputConfig::default().with_pull(Pull::Up);
let inputs = Inputs {
- up: Input::new(peripherals.GPIO38, button_config.clone()),
- down: Input::new(peripherals.GPIO39, button_config.clone()),
- left: Input::new(peripherals.GPIO40, button_config.clone()),
- right: Input::new(peripherals.GPIO41, button_config.clone()),
- back: Input::new(peripherals.GPIO42, button_config.clone()),
- ok: Input::new(peripherals.GPIO45, button_config.clone()),
+ up: Input::new(peripherals.GPIO38, button_config),
+ down: Input::new(peripherals.GPIO39, button_config),
+ left: Input::new(peripherals.GPIO40, button_config),
+ right: Input::new(peripherals.GPIO41, button_config),
+ back: Input::new(peripherals.GPIO42, button_config),
+ ok: Input::new(peripherals.GPIO45, button_config),
};
log::info!("Setup complete, entering main loop");
diff --git a/src/navigation/inputs.rs b/src/navigation/inputs.rs
index b29a094..b5cabae 100644
--- a/src/navigation/inputs.rs
+++ b/src/navigation/inputs.rs
@@ -1,3 +1,7 @@
+use embassy_futures::select::select_array;
+use embassy_time::{Duration, Timer};
+use embedded_hal::digital::InputPin;
+use embedded_hal_async::digital::Wait;
use esp_hal::gpio::Input;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -16,3 +20,62 @@ pub struct Inputs {
pub back: Input<'static>,
pub ok: Input<'static>,
}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ButtonAction {
+ Up = 0,
+ Down = 1,
+ Left = 2,
+ Right = 3,
+ Ok = 4,
+ Back = 5,
+}
+
+impl Inputs {
+ pub async fn wait_for_press(&mut self) -> ButtonAction {
+ let (_, index) = select_array([
+ wait_for_button(&mut self.up),
+ wait_for_button(&mut self.down),
+ wait_for_button(&mut self.left),
+ wait_for_button(&mut self.right),
+ wait_for_button(&mut self.ok),
+ wait_for_button(&mut self.back),
+ ])
+ .await;
+
+ match index {
+ 0 => ButtonAction::Up,
+ 1 => ButtonAction::Down,
+ 2 => ButtonAction::Back,
+ 3 => ButtonAction::Ok,
+ 4 => ButtonAction::Ok,
+ 5 => ButtonAction::Back,
+ id => panic!("Unknown button {id} pressed"),
+ }
+ }
+}
+
+/// Waits for a button press, filtering out fast transients and mechanical bounce.
+async fn wait_for_button
(pin: &mut P)
+where
+ P: Wait + InputPin,
+{
+ loop {
+ // 1. Wait for the hardware interrupt (could be a real press or crosstalk)
+ let _ = pin.wait_for_falling_edge().await;
+
+ // 2. Debounce window: let the noise settle
+ Timer::after(Duration::from_millis(30)).await;
+
+ // 3. Verify the pin is still held down
+ // (Adapt unwrap() based on your specific HAL's Error type)
+ if pin.is_low().unwrap_or(false) {
+ // OPTIONAL: Wait for the button to be released before returning
+ // so holding it down doesn't spam triggers.
+ // let _ = pin.wait_for_rising_edge().await;
+ // Timer::after(Duration::from_millis(30)).await;
+
+ return;
+ }
+ }
+}
diff --git a/src/navigation/navigation.rs b/src/navigation/navigation.rs
index 794dacf..d72af27 100644
--- a/src/navigation/navigation.rs
+++ b/src/navigation/navigation.rs
@@ -1,13 +1,12 @@
use crate::card::model::Card;
+use crate::navigation::inputs::ButtonAction;
use crate::navigation::state::NavigationState;
use crate::views::view::View;
use crate::{display::primary_lcd::PrimaryLcdDisplay, navigation::inputs::Inputs};
-use embassy_futures::select::{Either, Either6, select};
-use embassy_futures::select::select6;
-use embassy_time::{Duration, Timer};
+use embassy_futures::select::{Either, select};
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
-
+use embassy_time::{Duration, Timer};
pub type Mutex = embassy_sync::mutex::Mutex;
pub type Signal = embassy_sync::signal::Signal;
@@ -16,12 +15,7 @@ const DEBOUNCE_DURATION_MILLIS: u64 = 100;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Action {
- Up,
- Down,
- Left,
- Right,
- Ok,
- Back,
+ Button(ButtonAction),
Timer,
}
@@ -40,39 +34,29 @@ pub trait Navigable {
}
#[embassy_executor::task]
-pub async fn run(mut inputs: Inputs, display: &'static mut PrimaryLcdDisplay<'static>, ) {
+pub async fn run(mut inputs: Inputs, display: &'static mut PrimaryLcdDisplay<'static>) {
log::info!("starting navigation");
let mut state = NavigationState::new();
state.screens.last().unwrap().display(display).await;
loop {
- let selection_buttons = select6(
- inputs.up.wait_for_low(),
- inputs.down.wait_for_low(),
- inputs.left.wait_for_low(),
- inputs.right.wait_for_low(),
- inputs.ok.wait_for_low(),
- inputs.back.wait_for_low(),
- );
-
- let selection = select(selection_buttons, Timer::after(Duration::from_millis(DEBOUNCE_DURATION_MILLIS))).await;
+ let selection = select(
+ inputs.wait_for_press(),
+ Timer::after(Duration::from_millis(DEBOUNCE_DURATION_MILLIS)),
+ )
+ .await;
let action = match selection {
- Either::First(selection_buttons) => match selection_buttons {
- Either6::First(_) => Action::Up,
- Either6::Second(_) => Action::Down,
- Either6::Third(_) => Action::Back,
- Either6::Fourth(_) => Action::Ok,
- Either6::Fifth(_) => Action::Ok,
- Either6::Sixth(_) => Action::Back,
- },
+ Either::First(button) => Action::Button(button),
Either::Second(_) => Action::Timer,
};
- log::info!("Action: {:?}", action);
+ if Action::Timer != action {
+ log::info!("Action: {:?}", action);
+ }
- let new_state = if action == Action::Back {
+ let new_state = if action == Action::Button(ButtonAction::Back) {
if state.screens.len() > 1 {
state.screens.pop();
}
diff --git a/src/views/main_menu.rs b/src/views/main_menu.rs
index 41470a5..1755d5c 100644
--- a/src/views/main_menu.rs
+++ b/src/views/main_menu.rs
@@ -1,10 +1,10 @@
use alloc::vec;
-use core::future;
use crate::card::model::Card;
use crate::card::{decoder::split_nfc_hex, mock::*};
use crate::display::primary_lcd::PrimaryLcdDisplay;
+use crate::navigation::inputs::ButtonAction;
use crate::views::card_view::CardView;
use crate::views::journal_view::JournalView;
use crate::views::{menu_item, settings_menu::SettingsMenu, view::View};
@@ -37,43 +37,54 @@ impl Navigable for MainMenu {
}
fn handle_input(&self, input: Action) -> impl core::future::Future