2 Commits
Author SHA1 Message Date
ede1998 b6f43d1055 fix: recognize actual button, not wrong one 2026-08-22 18:29:35 +02:00
lukas 07c5210d7b Implemented tertiary LCD display 2026-08-22 18:25:48 +02:00
10 changed files with 385 additions and 192 deletions
Generated
+19 -1
View File
@@ -96,6 +96,12 @@ dependencies = [
"vsimd", "vsimd",
] ]
[[package]]
name = "bitfield"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f798d2d157e547aa99aab0967df39edd0b70307312b6f8bd2848e6abe40896e0"
[[package]] [[package]]
name = "bitfield" name = "bitfield"
version = "0.19.5" version = "0.19.5"
@@ -312,6 +318,7 @@ dependencies = [
"embedded-graphics", "embedded-graphics",
"embedded-graphics-core", "embedded-graphics-core",
"embedded-hal 1.0.0", "embedded-hal 1.0.0",
"embedded-hal-async",
"embedded-hal-bus", "embedded-hal-bus",
"embedded-io 0.7.1", "embedded-io 0.7.1",
"esp-alloc", "esp-alloc",
@@ -320,6 +327,7 @@ dependencies = [
"esp-hal-wifimanager", "esp-hal-wifimanager",
"esp-println", "esp-println",
"esp-rtos", "esp-rtos",
"i2c-character-display",
"ili9341", "ili9341",
"log", "log",
"ndef", "ndef",
@@ -1098,7 +1106,7 @@ version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bfcf2a0842903717f4663f6a08512c32b0f6b2d7fb7db3c8a6895d2e6d49f72" checksum = "2bfcf2a0842903717f4663f6a08512c32b0f6b2d7fb7db3c8a6895d2e6d49f72"
dependencies = [ dependencies = [
"bitfield", "bitfield 0.19.5",
"bitflags 2.13.1", "bitflags 2.13.1",
"bytemuck", "bytemuck",
"cfg-if", "cfg-if",
@@ -1680,6 +1688,16 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "i2c-character-display"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86a620ec16541e28c0e52ce21cd23d734a5d7efaae3d609b2a58694b0ef67739"
dependencies = [
"bitfield 0.17.0",
"embedded-hal 1.0.0",
]
[[package]] [[package]]
name = "ident_case" name = "ident_case"
version = "1.0.1" version = "1.0.1"
+2
View File
@@ -45,6 +45,8 @@ pn532 = "0.5.0"
ndef = "0.5.0" ndef = "0.5.0"
num_enum = { version = "0.7.6", default-features = false } num_enum = { version = "0.7.6", default-features = false }
embassy-sync = "0.8.0" embassy-sync = "0.8.0"
embedded-hal-async = "1.0.0"
i2c-character-display = "0.5.1"
# For fine tuning these settings, please refer to https://doc.rust-lang.org/cargo/reference/profiles.html # For fine tuning these settings, please refer to https://doc.rust-lang.org/cargo/reference/profiles.html
[profile.dev] [profile.dev]
+20 -9
View File
@@ -1,8 +1,8 @@
{ {
"version": 1, "version": 1,
"author": "CreatureDex", "author": "CreatureDex",
"editor": "wokwi", "editor": "wokwi",
"parts": [ "parts": [
{ {
"type": "board-esp32-s3-devkitc-1", "type": "board-esp32-s3-devkitc-1",
"id": "esp1", "id": "esp1",
@@ -52,9 +52,16 @@
"top": 25.4, "top": 25.4,
"left": 115.2, "left": 115.2,
"attrs": { "color": "Lime", "xray": "1", "key": "1", "label": "Ok" } "attrs": { "color": "Lime", "xray": "1", "key": "1", "label": "Ok" }
},
{
"type": "wokwi-lcd1602",
"id": "lcd2",
"top": -291.2,
"left": -167.2,
"attrs": { "pins": "i2c" }
} }
], ],
"connections": [ "connections": [
[ "esp1:TX", "$serialMonitor:RX", "", [] ], [ "esp1:TX", "$serialMonitor:RX", "", [] ],
[ "esp1:RX", "$serialMonitor:TX", "", [] ], [ "esp1:RX", "$serialMonitor:TX", "", [] ],
[ "lcd1:VCC", "esp1:3V3", "red", [ "v19.2", "h-57.6", "v-105.6", "h-124.95" ] ], [ "lcd1:VCC", "esp1:3V3", "red", [ "v19.2", "h-57.6", "v-105.6", "h-124.95" ] ],
@@ -76,7 +83,11 @@
[ "btn-back:2.l", "esp1:42", "Magenta", [ "h-19.2", "v211.58" ] ], [ "btn-back:2.l", "esp1:42", "Magenta", [ "h-19.2", "v211.58" ] ],
[ "btn-back:1.l", "btn-up:1.r", "black", [ "h0" ] ], [ "btn-back:1.l", "btn-up:1.r", "black", [ "h0" ] ],
[ "btn-ok:1.l", "btn-down:1.r", "black", [ "h0" ] ], [ "btn-ok:1.l", "btn-down:1.r", "black", [ "h0" ] ],
[ "btn-ok:2.l", "esp1:45", "Lime", [ "h-9.6", "v173" ] ] [ "btn-ok:2.l", "esp1:45", "Lime", [ "h-9.6", "v173" ] ],
], [ "lcd2:GND", "esp1:GND.2", "black", [ "h0" ] ],
"dependencies": {} [ "lcd2:VCC", "esp1:3V3.1", "red", [ "h0" ] ],
[ "lcd2:SDA", "esp1:17", "green", [ "h-134.4", "v413" ] ],
[ "lcd2:SCL", "esp1:18", "green", [ "h-124.8", "v38.7" ] ]
],
"dependencies": {}
} }
+32 -12
View File
@@ -11,14 +11,19 @@ use alloc::boxed::Box;
use creaturedex::card::decoder::split_nfc_hex; use creaturedex::card::decoder::split_nfc_hex;
use creaturedex::card::model::Card; use creaturedex::card::model::Card;
use creaturedex::display::primary_lcd::{PrimaryLcdDisplay, init_primary_lcd}; use creaturedex::display::primary_lcd::{PrimaryLcdDisplay, init_primary_lcd};
use creaturedex::display::tertiary_lcd::{init_tertiary_lcd, write_wrapped};
use creaturedex::drivers::nfc_pn532::NfcPn532Driver; use creaturedex::drivers::nfc_pn532::NfcPn532Driver;
use creaturedex::navigation::inputs::Inputs; use creaturedex::navigation::inputs::Inputs;
use creaturedex::navigation::navigation::{self, CARD_DATA}; use creaturedex::navigation::navigation::{self, CARD_DATA};
use creaturedex::network::wifi::setup_wifi; use creaturedex::network::wifi::setup_wifi;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embedded_hal_bus::i2c::AtomicDevice;
use embedded_hal_bus::util::AtomicCell;
use esp_hal::Blocking;
use esp_hal::clock::CpuClock; use esp_hal::clock::CpuClock;
use esp_hal::gpio::{Input, InputConfig, Pull}; use esp_hal::gpio::{Input, InputConfig, Pull};
use esp_hal::timer::timg::TimerGroup; use esp_hal::timer::timg::TimerGroup;
use esp_hal::i2c::master::{Config as I2cConfig, I2c};
use log::error; use log::error;
#[panic_handler] #[panic_handler]
@@ -63,24 +68,39 @@ async fn main(spawner: Spawner) {
peripherals.GPIO12, // shared dc peripherals.GPIO12, // shared dc
))); )));
setup_wifi(peripherals.WIFI, peripherals.FLASH, &spawner).await; let config = I2cConfig::default().with_frequency(esp_hal::time::Rate::from_khz(20));
let i2c = match I2c::new(peripherals.I2C0, config) {
Ok(bus) => bus.with_sda(peripherals.GPIO17).with_scl(peripherals.GPIO18),
Err(e) => {
log::error!("Shared I2C bus initialization failed for GPIO17/GPIO18: {e:?}");
panic!("Shared I2C bus initialization failed");
}
};
let shared_i2c = Box::leak(Box::new(AtomicCell::new(i2c)));
log::info!("Shared I2C bus ready on GPIO17 (SDA) and GPIO18 (SCL)");
let mut nfc_driver = let mut lcd = init_tertiary_lcd(AtomicDevice::new(shared_i2c));
NfcPn532Driver::new(peripherals.I2C0, peripherals.GPIO17, peripherals.GPIO18); if let Err(_) = write_wrapped(&mut lcd, "Testing more than 16 chars what happens now?") {
log::error!("Tertiary LCD startup text write failed over shared I2C bus; check the shared bus, wiring, and device responses");
}
// setup_wifi(peripherals.WIFI, peripherals.FLASH, &spawner).await;
let mut nfc_driver = NfcPn532Driver::new(AtomicDevice::new(shared_i2c));
if nfc_driver.configure_sam().await.is_ok() { if nfc_driver.configure_sam().await.is_ok() {
log::info!("NFC SAM configuration successful"); log::info!("NFC SAM configuration successful on the shared I2C bus");
} else { } else {
log::error!("NFC SAM configuration failed"); log::error!("NFC SAM configuration failed on the shared I2C bus");
} }
let button_config = InputConfig::default().with_pull(Pull::Up); let button_config = InputConfig::default().with_pull(Pull::Up);
let inputs = Inputs { let inputs = Inputs {
up: Input::new(peripherals.GPIO38, button_config.clone()), up: Input::new(peripherals.GPIO38, button_config),
down: Input::new(peripherals.GPIO39, button_config.clone()), down: Input::new(peripherals.GPIO39, button_config),
left: Input::new(peripherals.GPIO40, button_config.clone()), left: Input::new(peripherals.GPIO40, button_config),
right: Input::new(peripherals.GPIO41, button_config.clone()), right: Input::new(peripherals.GPIO41, button_config),
back: Input::new(peripherals.GPIO42, button_config.clone()), back: Input::new(peripherals.GPIO42, button_config),
ok: Input::new(peripherals.GPIO45, button_config.clone()), ok: Input::new(peripherals.GPIO45, button_config),
}; };
log::info!("Setup complete, entering main loop"); log::info!("Setup complete, entering main loop");
@@ -90,7 +110,7 @@ async fn main(spawner: Spawner) {
#[embassy_executor::task] #[embassy_executor::task]
async fn nfc_driver_task(mut nfc_driver: NfcPn532Driver<'static>) { async fn nfc_driver_task(mut nfc_driver: NfcPn532Driver<AtomicDevice<'static, I2c<'static, Blocking>>>) {
loop { loop {
while CARD_DATA.lock().await.is_none() { while CARD_DATA.lock().await.is_none() {
match nfc_driver.read_card_data().await { match nfc_driver.read_card_data().await {
+88 -1
View File
@@ -1 +1,88 @@
// Tertiary LCD module use embedded_hal::i2c::I2c;
use esp_hal::delay::Delay;
use i2c_character_display::{CharacterDisplayPCF8574T, LcdDisplayType};
use log::error;
pub type TertiaryLcd<I2C> = CharacterDisplayPCF8574T<I2C, Delay>;
pub fn init_tertiary_lcd<I2C>(i2c: I2C) -> TertiaryLcd<I2C>
where
I2C: I2c,
{
let mut lcd = CharacterDisplayPCF8574T::new(i2c, LcdDisplayType::Lcd16x2, Delay::new());
match lcd.init() {
Ok(()) => {
log::info!("I2C tertiary LCD initialized on PCF8574T at address 0x27");
lcd
}
Err(_) => {
error!("I2C tertiary LCD init failed on PCF8574T at address 0x27; check bus wiring, power, and device address");
panic!("tertiary LCD initialization failed");
}
}
}
pub fn write_line<I2C>(
lcd: &mut TertiaryLcd<I2C>,
line: u8,
text: &str,
) -> Result<(), i2c_character_display::CharacterDisplayError<I2C>>
where
I2C: I2c,
{
if let Err(e) = lcd.set_cursor(0, line) {
error!("I2C LCD cursor move failed on line {line}; bus or device may be unavailable");
return Err(e);
}
if let Err(e) = lcd.print(text) {
error!("I2C LCD print failed on line {line}; payload length={} and device may be busy or disconnected", text.len());
return Err(e);
}
Ok(())
}
pub fn write_wrapped<I2C>(
lcd: &mut TertiaryLcd<I2C>,
text: &str,
) -> Result<(), i2c_character_display::CharacterDisplayError<I2C>>
where
I2C: I2c,
{
const DISPLAY_WIDTH: usize = 16;
const DISPLAY_ROWS: usize = 2;
if let Err(e) = lcd.clear() {
error!("I2C LCD clear failed before wrapped write; check the shared bus and display response");
return Err(e);
}
let mut offset = 0;
for row in 0..DISPLAY_ROWS {
let remaining = &text[offset..];
if remaining.is_empty() {
return Ok(());
}
let chunk = if remaining.len() > DISPLAY_WIDTH {
&remaining[..DISPLAY_WIDTH]
} else {
remaining
};
if let Err(e) = lcd.set_cursor(0, row as u8) {
error!("I2C LCD cursor move failed while writing wrapped row {row}; bus may be busy or device unresponsive");
return Err(e);
}
if let Err(e) = lcd.print(chunk) {
error!("I2C LCD wrapped write failed on row {row}; chunk length={} and text may exceed display width", chunk.len());
return Err(e);
}
offset += chunk.len();
if offset >= text.len() {
return Ok(());
}
}
Ok(())
}
+23 -27
View File
@@ -1,9 +1,7 @@
use alloc::vec::Vec; use alloc::vec::Vec;
use core::convert::Infallible; use core::convert::Infallible;
use esp_hal::gpio::interconnect::{PeripheralInput, PeripheralOutput}; use embedded_hal::i2c::I2c;
use esp_hal::i2c::master::{Config as I2cConfig, I2c}; use esp_hal::time::{Duration, Instant};
use esp_hal::peripherals::I2C0;
use esp_hal::time::{Duration, Instant, Rate};
use log::{error, info}; use log::{error, info};
use pn532::i2c::I2CInterface; use pn532::i2c::I2CInterface;
use pn532::requests::SAMMode; use pn532::requests::SAMMode;
@@ -55,25 +53,21 @@ impl pn532::CountDown for TimerWrapper {
} }
} }
pub type Pn532Device<'a> = Pn532<I2CInterface<I2c<'a, esp_hal::Blocking>>, (), 32>; pub type Pn532Device<I2C> = Pn532<I2CInterface<I2C>, (), 32>;
/// PN532 NFC reader driver. /// PN532 NFC reader driver.
pub struct NfcPn532Driver<'a> { pub struct NfcPn532Driver<I2C>
pn532: Pn532Device<'a>, where
I2C: I2c,
{
pn532: Pn532Device<I2C>,
} }
impl<'a> NfcPn532Driver<'a> { impl<I2C> NfcPn532Driver<I2C>
pub fn new<SDA, SCL>(i2c0: I2C0<'static>, sda: SDA, scl: SCL) -> Self where
where I2C: I2c,
SDA: PeripheralInput<'static> + PeripheralOutput<'static>, {
SCL: PeripheralInput<'static> + PeripheralOutput<'static>, pub fn new(i2c: I2C) -> Self {
{
let config = I2cConfig::default().with_frequency(Rate::from_khz(20));
let i2c = I2c::new(i2c0, config)
.expect("Failed to init I2C")
.with_sda(sda)
.with_scl(scl);
let pn532 = Pn532::new_async(I2CInterface { i2c }); let pn532 = Pn532::new_async(I2CInterface { i2c });
Self { pn532 } Self { pn532 }
} }
@@ -83,10 +77,10 @@ impl<'a> NfcPn532Driver<'a> {
&Request::sam_configuration(SAMMode::Normal, false), &Request::sam_configuration(SAMMode::Normal, false),
0, 0,
).await { ).await {
error!("Could not initialize PN532: {e:?}"); error!("PN532 SAM configuration failed over I2C: {e:?}");
Err(()) Err(())
} else { } else {
info!("Successfully init'ed PN532"); info!("PN532 SAM configuration successful on shared I2C bus");
Ok(()) Ok(())
} }
} }
@@ -94,11 +88,11 @@ impl<'a> NfcPn532Driver<'a> {
pub async fn poll_target(&mut self) -> Result<(), ()> { pub async fn poll_target(&mut self) -> Result<(), ()> {
match self.pn532.process_async(&Request::INLIST_ONE_ISO_A_TARGET, 23).await { match self.pn532.process_async(&Request::INLIST_ONE_ISO_A_TARGET, 23).await {
Ok(uid) => { Ok(uid) => {
info!("uid = {uid:?}"); info!("PN532 detected NFC target UID: {uid:?}");
Ok(()) Ok(())
} }
Err(e) => { Err(e) => {
info!("No NFC card found: {e:?}"); info!("No NFC card found on shared I2C bus: {e:?}");
Err(()) Err(())
} }
} }
@@ -117,12 +111,14 @@ impl<'a> NfcPn532Driver<'a> {
} }
pub async fn read_card_data(&mut self) -> Result<Vec<u8>, ()> { pub async fn read_card_data(&mut self) -> Result<Vec<u8>, ()> {
self.poll_target().await?; if let Err(e) = self.poll_target().await {
error!("PN532 I2C target poll failed before reading card data: {e:?}");
return Err(e);
}
let mut data: Vec<u8> = Vec::with_capacity(858); let mut data: Vec<u8> = Vec::with_capacity(858);
for i in 0x0B..=230 { for i in 0x0B..=230 {
let mut parse_result = Err(()); let mut parse_result = Err(());
while parse_result.is_err() { while parse_result.is_err() {
let Ok(result) = self.pn532.process_async(&Request::ntag_read(i), 17).await else { let Ok(result) = self.pn532.process_async(&Request::ntag_read(i), 17).await else {
@@ -139,11 +135,11 @@ impl<'a> NfcPn532Driver<'a> {
data.extend(bytes); data.extend(bytes);
} }
info!("Full data: {data:02x?}"); info!("PN532 full card payload read over I2C: {:02x?}", data);
Ok(data) Ok(data)
} }
pub fn device_mut(&mut self) -> &mut Pn532Device<'a> { pub fn device_mut(&mut self) -> &mut Pn532Device<I2C> {
&mut self.pn532 &mut self.pn532
} }
} }
+63
View File
@@ -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; use esp_hal::gpio::Input;
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -16,3 +20,62 @@ pub struct Inputs {
pub back: Input<'static>, pub back: Input<'static>,
pub ok: 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::Left,
3 => ButtonAction::Right,
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<P>(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;
}
}
}
+14 -30
View File
@@ -1,13 +1,12 @@
use crate::card::model::Card; use crate::card::model::Card;
use crate::navigation::inputs::ButtonAction;
use crate::navigation::state::NavigationState; use crate::navigation::state::NavigationState;
use crate::views::view::View; use crate::views::view::View;
use crate::{display::primary_lcd::PrimaryLcdDisplay, navigation::inputs::Inputs}; use crate::{display::primary_lcd::PrimaryLcdDisplay, navigation::inputs::Inputs};
use embassy_futures::select::{Either, Either6, select}; use embassy_futures::select::{Either, select};
use embassy_futures::select::select6;
use embassy_time::{Duration, Timer};
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_time::{Duration, Timer};
pub type Mutex<T> = embassy_sync::mutex::Mutex<CriticalSectionRawMutex, T>; pub type Mutex<T> = embassy_sync::mutex::Mutex<CriticalSectionRawMutex, T>;
pub type Signal<T> = embassy_sync::signal::Signal<CriticalSectionRawMutex, T>; pub type Signal<T> = embassy_sync::signal::Signal<CriticalSectionRawMutex, T>;
@@ -16,12 +15,7 @@ const DEBOUNCE_DURATION_MILLIS: u64 = 100;
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Action { pub enum Action {
Up, Button(ButtonAction),
Down,
Left,
Right,
Ok,
Back,
Timer, Timer,
} }
@@ -40,39 +34,29 @@ pub trait Navigable {
} }
#[embassy_executor::task] #[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"); log::info!("starting navigation");
let mut state = NavigationState::new(); let mut state = NavigationState::new();
state.screens.last().unwrap().display(display).await; state.screens.last().unwrap().display(display).await;
loop { loop {
let selection_buttons = select6( let selection = select(
inputs.up.wait_for_low(), inputs.wait_for_press(),
inputs.down.wait_for_low(), Timer::after(Duration::from_millis(DEBOUNCE_DURATION_MILLIS)),
inputs.left.wait_for_low(), )
inputs.right.wait_for_low(), .await;
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 action = match selection { let action = match selection {
Either::First(selection_buttons) => match selection_buttons { Either::First(button) => Action::Button(button),
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::Second(_) => Action::Timer, Either::Second(_) => Action::Timer,
}; };
if Action::Timer != action {
log::info!("Action: {:?}", 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 { if state.screens.len() > 1 {
state.screens.pop(); state.screens.pop();
} }
+18 -7
View File
@@ -1,10 +1,10 @@
use alloc::vec; use alloc::vec;
use core::future;
use crate::card::model::Card; use crate::card::model::Card;
use crate::card::{decoder::split_nfc_hex, mock::*}; use crate::card::{decoder::split_nfc_hex, mock::*};
use crate::display::primary_lcd::PrimaryLcdDisplay; use crate::display::primary_lcd::PrimaryLcdDisplay;
use crate::navigation::inputs::ButtonAction;
use crate::views::card_view::CardView; use crate::views::card_view::CardView;
use crate::views::journal_view::JournalView; use crate::views::journal_view::JournalView;
use crate::views::{menu_item, settings_menu::SettingsMenu, view::View}; use crate::views::{menu_item, settings_menu::SettingsMenu, view::View};
@@ -37,11 +37,21 @@ impl Navigable for MainMenu {
} }
fn handle_input(&self, input: Action) -> impl core::future::Future<Output = NewState> + Send { fn handle_input(&self, input: Action) -> impl core::future::Future<Output = NewState> + Send {
async move {
let Action::Button(input) = input else {
// Skip timer inputs
return NewState {
view: View::Main(self.clone()),
replace_view: true,
redraw: false,
};
};
let card_mock1 = Card::try_from(split_nfc_hex(&MOCK_PAYLOAD3).unwrap()).unwrap(); let card_mock1 = Card::try_from(split_nfc_hex(&MOCK_PAYLOAD3).unwrap()).unwrap();
let card_mock2 = Card::try_from(split_nfc_hex(&MOCK_PAYLOAD2).unwrap()).unwrap(); let card_mock2 = Card::try_from(split_nfc_hex(&MOCK_PAYLOAD2).unwrap()).unwrap();
let new_menu = match input { let new_menu = match input {
Action::Up => { ButtonAction::Up => {
let new_selected = if (self.selected - 1) < 0 { let new_selected = if (self.selected - 1) < 0 {
MAX_SELECTED - 1 MAX_SELECTED - 1
} else { } else {
@@ -51,13 +61,13 @@ impl Navigable for MainMenu {
selected: new_selected, selected: new_selected,
}) })
} }
Action::Down => { ButtonAction::Down => {
let new_selected = self.selected.wrapping_add(1) % MAX_SELECTED; let new_selected = self.selected.wrapping_add(1) % MAX_SELECTED;
View::Main(MainMenu { View::Main(MainMenu {
selected: new_selected, selected: new_selected,
}) })
} }
Action::Ok => match self.selected { ButtonAction::Ok => match self.selected {
0 => View::Scan(crate::views::scan_menu::ScanMenu { progress: 0 }), 0 => View::Scan(crate::views::scan_menu::ScanMenu { progress: 0 }),
1 => View::Card(CardView { card: card_mock1 }), 1 => View::Card(CardView { card: card_mock1 }),
2 => View::Journal(JournalView { 2 => View::Journal(JournalView {
@@ -70,10 +80,11 @@ impl Navigable for MainMenu {
_ => View::Main(self.clone()), _ => View::Main(self.clone()),
}; };
future::ready(NewState { NewState {
replace_view: matches!(new_menu, View::Main(_)), replace_view: matches!(new_menu, View::Main(_)),
view: new_menu, view: new_menu,
redraw: !matches!(input, Action::Timer), redraw: true,
}) }
}
} }
} }
+3 -2
View File
@@ -1,4 +1,5 @@
use crate::display::primary_lcd::PrimaryLcdDisplay; use crate::display::primary_lcd::PrimaryLcdDisplay;
use crate::navigation::inputs::ButtonAction;
use crate::navigation::navigation::CARD_DATA; use crate::navigation::navigation::CARD_DATA;
use crate::navigation::navigation::NewState; use crate::navigation::navigation::NewState;
@@ -96,7 +97,7 @@ impl Navigable for ScanMenu {
} }
} else { } else {
let new_progress = match input { let new_progress = match input {
Action::Up => self.progress.wrapping_add(5), Action::Button(ButtonAction::Up) => self.progress.wrapping_add(5),
_ => self.progress, _ => self.progress,
}; };
NewState { NewState {
@@ -104,7 +105,7 @@ impl Navigable for ScanMenu {
progress: new_progress, progress: new_progress,
}), }),
replace_view: true, replace_view: true,
redraw: matches!(input, Action::Up | Action::Timer), redraw: matches!(input, Action::Button(ButtonAction::Up) | Action::Timer),
} }
} }
} }