Implemented Scan card menu #29
Generated
+1
@@ -307,6 +307,7 @@ dependencies = [
|
|||||||
"display-interface-spi",
|
"display-interface-spi",
|
||||||
"embassy-executor",
|
"embassy-executor",
|
||||||
"embassy-futures",
|
"embassy-futures",
|
||||||
|
"embassy-sync 0.8.0",
|
||||||
"embassy-time",
|
"embassy-time",
|
||||||
"embedded-graphics",
|
"embedded-graphics",
|
||||||
"embedded-graphics-core",
|
"embedded-graphics-core",
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ base64 = { version = "0.22", default-features = false, features = ["alloc"] }
|
|||||||
pn532 = "0.5.0"
|
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"
|
||||||
|
|
||||||
# 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]
|
||||||
|
|||||||
+30
-6
@@ -8,14 +8,16 @@
|
|||||||
#![deny(clippy::large_stack_frames)]
|
#![deny(clippy::large_stack_frames)]
|
||||||
|
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
|
use creaturedex::card::decoder::split_nfc_hex;
|
||||||
|
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::drivers::nfc_pn532::NfcPn532Driver;
|
use creaturedex::drivers::nfc_pn532::NfcPn532Driver;
|
||||||
use creaturedex::navigation::inputs::Inputs;
|
use creaturedex::navigation::inputs::Inputs;
|
||||||
use creaturedex::navigation::navigation;
|
use creaturedex::navigation::navigation::{self, CARD_DATA};
|
||||||
|
use creaturedex::network::wifi::setup_wifi;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
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::time::Duration;
|
|
||||||
use esp_hal::timer::timg::TimerGroup;
|
use esp_hal::timer::timg::TimerGroup;
|
||||||
use log::error;
|
use log::error;
|
||||||
|
|
||||||
@@ -61,18 +63,16 @@ async fn main(spawner: Spawner) {
|
|||||||
peripherals.GPIO12, // shared dc
|
peripherals.GPIO12, // shared dc
|
||||||
)));
|
)));
|
||||||
|
|
||||||
//setup_wifi(peripherals.WIFI, peripherals.FLASH, &spawner).await;
|
setup_wifi(peripherals.WIFI, peripherals.FLASH, &spawner).await;
|
||||||
|
|
||||||
let mut nfc_driver =
|
let mut nfc_driver =
|
||||||
NfcPn532Driver::new(peripherals.I2C0, peripherals.GPIO17, peripherals.GPIO18);
|
NfcPn532Driver::new(peripherals.I2C0, peripherals.GPIO17, peripherals.GPIO18);
|
||||||
let timeout = Duration::from_millis(1000);
|
if nfc_driver.configure_sam().await.is_ok() {
|
||||||
if nfc_driver.configure_sam(timeout).is_ok() {
|
|
||||||
log::info!("NFC SAM configuration successful");
|
log::info!("NFC SAM configuration successful");
|
||||||
} else {
|
} else {
|
||||||
log::error!("NFC SAM configuration failed");
|
log::error!("NFC SAM configuration failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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.clone()),
|
||||||
@@ -85,4 +85,28 @@ async fn main(spawner: Spawner) {
|
|||||||
|
|
||||||
log::info!("Setup complete, entering main loop");
|
log::info!("Setup complete, entering main loop");
|
||||||
spawner.spawn(navigation::run(inputs, display).expect("run task failed"));
|
spawner.spawn(navigation::run(inputs, display).expect("run task failed"));
|
||||||
|
spawner.spawn(nfc_driver_task(nfc_driver).expect("nfc driver task failed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn nfc_driver_task(mut nfc_driver: NfcPn532Driver<'static>) {
|
||||||
|
loop {
|
||||||
|
while CARD_DATA.lock().await.is_none() {
|
||||||
|
match nfc_driver.read_card_data().await {
|
||||||
|
Ok(card_data) => {
|
||||||
|
log::info!("NFC Card Data Read: {:?}", card_data);
|
||||||
|
let Some(split_data) = split_nfc_hex(&card_data) else {
|
||||||
|
log::error!("Failed to split NFC card data");
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
CARD_DATA.lock().await.replace(Card::try_from(split_data).unwrap());
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
log::info!("No NFC card found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
embassy_time::Timer::after(embassy_time::Duration::from_millis(1000)).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -235,10 +235,10 @@ pub struct Card {
|
|||||||
pub sprite: Sprite,
|
pub sprite: Sprite,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<NfcPayload<'static>> for Card {
|
impl<'a> TryFrom<NfcPayload<'a>> for Card {
|
||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
|
|
||||||
fn try_from(payload: NfcPayload) -> Result<Self, Self::Error> {
|
fn try_from(payload: NfcPayload<'a>) -> Result<Self, Self::Error> {
|
||||||
let uuid: String = "empty for now".into(); //str::from_utf8(payload.card_uuid).unwrap().into();
|
let uuid: String = "empty for now".into(); //str::from_utf8(payload.card_uuid).unwrap().into();
|
||||||
let event = decode_known_event(payload.event_encoding);
|
let event = decode_known_event(payload.event_encoding);
|
||||||
let cardtype = CardType::from(payload.card_type);
|
let cardtype = CardType::from(payload.card_type);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::card::model::Palette;
|
use crate::card::model::Palette;
|
||||||
use embedded_graphics::{
|
use embedded_graphics::{
|
||||||
geometry::Size, pixelcolor::Rgb565, pixelcolor::RgbColor, prelude::Point, primitives::Rectangle,
|
geometry::Size, pixelcolor::Rgb565, prelude::Point, primitives::Rectangle,
|
||||||
};
|
};
|
||||||
use embedded_graphics_core::draw_target::DrawTarget;
|
use embedded_graphics_core::draw_target::DrawTarget;
|
||||||
|
|
||||||
|
|||||||
+17
-13
@@ -55,7 +55,7 @@ impl pn532::CountDown for TimerWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Pn532Device<'a> = Pn532<I2CInterface<I2c<'a, esp_hal::Blocking>>, TimerWrapper, 32>;
|
pub type Pn532Device<'a> = Pn532<I2CInterface<I2c<'a, esp_hal::Blocking>>, (), 32>;
|
||||||
|
|
||||||
/// PN532 NFC reader driver.
|
/// PN532 NFC reader driver.
|
||||||
pub struct NfcPn532Driver<'a> {
|
pub struct NfcPn532Driver<'a> {
|
||||||
@@ -74,16 +74,15 @@ impl<'a> NfcPn532Driver<'a> {
|
|||||||
.with_sda(sda)
|
.with_sda(sda)
|
||||||
.with_scl(scl);
|
.with_scl(scl);
|
||||||
|
|
||||||
let pn532 = Pn532::new(I2CInterface { i2c }, TimerWrapper::new());
|
let pn532 = Pn532::new_async(I2CInterface { i2c });
|
||||||
Self { pn532 }
|
Self { pn532 }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn configure_sam(&mut self, timeout: Duration) -> Result<(), ()> {
|
pub async fn configure_sam(&mut self) -> Result<(), ()> {
|
||||||
if let Err(e) = self.pn532.process(
|
if let Err(e) = self.pn532.process_async(
|
||||||
&Request::sam_configuration(SAMMode::Normal, false),
|
&Request::sam_configuration(SAMMode::Normal, false),
|
||||||
0,
|
0,
|
||||||
timeout,
|
).await {
|
||||||
) {
|
|
||||||
error!("Could not initialize PN532: {e:?}");
|
error!("Could not initialize PN532: {e:?}");
|
||||||
Err(())
|
Err(())
|
||||||
} else {
|
} else {
|
||||||
@@ -92,8 +91,8 @@ impl<'a> NfcPn532Driver<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn poll_target(&mut self, timeout: Duration) -> Result<(), ()> {
|
pub async fn poll_target(&mut self) -> Result<(), ()> {
|
||||||
match self.pn532.process(&Request::INLIST_ONE_ISO_A_TARGET, 23, timeout) {
|
match self.pn532.process_async(&Request::INLIST_ONE_ISO_A_TARGET, 23).await {
|
||||||
Ok(uid) => {
|
Ok(uid) => {
|
||||||
info!("uid = {uid:?}");
|
info!("uid = {uid:?}");
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -117,21 +116,26 @@ impl<'a> NfcPn532Driver<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_card_data(&mut self) -> Result<Vec<u8>, ()> {
|
pub async fn read_card_data(&mut self) -> Result<Vec<u8>, ()> {
|
||||||
let ms_1000 = Duration::from_millis(1000);
|
self.poll_target().await?;
|
||||||
self.poll_target(Duration::from_secs(10))?;
|
|
||||||
|
|
||||||
let mut data: Vec<u8> = Vec::with_capacity(858);
|
let mut data: Vec<u8> = Vec::with_capacity(858);
|
||||||
|
|
||||||
for i in 0x04..=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(&Request::ntag_read(i), 17, ms_1000) else {
|
let Ok(result) = self.pn532.process_async(&Request::ntag_read(i), 17).await else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
parse_result = Self::parse_nfc_page(i, result);
|
parse_result = Self::parse_nfc_page(i, result);
|
||||||
}
|
}
|
||||||
let bytes = parse_result.unwrap();
|
let bytes = parse_result.unwrap();
|
||||||
|
let bytes = if i == 0x0B {
|
||||||
|
&bytes[1..]
|
||||||
|
} else {
|
||||||
|
&bytes
|
||||||
|
};
|
||||||
data.extend(bytes);
|
data.extend(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
|
use crate::card::model::Card;
|
||||||
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::Either6;
|
use embassy_futures::select::{Either, Either6, select};
|
||||||
use embassy_futures::select::select6;
|
use embassy_futures::select::select6;
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Timer};
|
||||||
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
|
|
||||||
|
|
||||||
|
pub type Mutex<T> = embassy_sync::mutex::Mutex<CriticalSectionRawMutex, T>;
|
||||||
|
pub type Signal<T> = embassy_sync::signal::Signal<CriticalSectionRawMutex, T>;
|
||||||
|
pub static CARD_DATA: Mutex<Option<Card>> = Mutex::new(None);
|
||||||
const DEBOUNCE_DURATION_MILLIS: u64 = 100;
|
const DEBOUNCE_DURATION_MILLIS: u64 = 100;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
@@ -16,6 +22,13 @@ pub enum Action {
|
|||||||
Right,
|
Right,
|
||||||
Ok,
|
Ok,
|
||||||
Back,
|
Back,
|
||||||
|
Timer,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NewState {
|
||||||
|
pub view: View,
|
||||||
|
pub replace_view: bool,
|
||||||
|
pub redraw: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Navigable {
|
pub trait Navigable {
|
||||||
@@ -23,52 +36,72 @@ pub trait Navigable {
|
|||||||
&self,
|
&self,
|
||||||
display: &mut PrimaryLcdDisplay<'static>,
|
display: &mut PrimaryLcdDisplay<'static>,
|
||||||
) -> impl core::future::Future<Output = ()> + Send;
|
) -> impl core::future::Future<Output = ()> + Send;
|
||||||
fn handle_input(&self, input: Action) -> impl core::future::Future<Output = View> + Send;
|
fn handle_input(&self, input: Action) -> impl core::future::Future<Output = NewState> + Send;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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 = select6(
|
let selection_buttons = select6(
|
||||||
inputs.up.wait_for_low(),
|
inputs.up.wait_for_low(),
|
||||||
inputs.down.wait_for_low(),
|
inputs.down.wait_for_low(),
|
||||||
inputs.left.wait_for_low(),
|
inputs.left.wait_for_low(),
|
||||||
inputs.right.wait_for_low(),
|
inputs.right.wait_for_low(),
|
||||||
inputs.ok.wait_for_low(),
|
inputs.ok.wait_for_low(),
|
||||||
inputs.back.wait_for_low(),
|
inputs.back.wait_for_low(),
|
||||||
)
|
);
|
||||||
.await;
|
|
||||||
|
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 {
|
||||||
Either6::First(_) => Action::Up,
|
Either6::First(_) => Action::Up,
|
||||||
Either6::Second(_) => Action::Down,
|
Either6::Second(_) => Action::Down,
|
||||||
Either6::Third(_) => Action::Left,
|
Either6::Third(_) => Action::Back,
|
||||||
Either6::Fourth(_) => Action::Right,
|
Either6::Fourth(_) => Action::Ok,
|
||||||
Either6::Fifth(_) => Action::Ok,
|
Either6::Fifth(_) => Action::Ok,
|
||||||
Either6::Sixth(_) => Action::Back,
|
Either6::Sixth(_) => Action::Back,
|
||||||
|
},
|
||||||
|
Either::Second(_) => Action::Timer,
|
||||||
};
|
};
|
||||||
|
|
||||||
log::info!("Action: {:?}", action);
|
log::info!("Action: {:?}", action);
|
||||||
|
|
||||||
let screen = match action {
|
let new_state = if action == Action::Back {
|
||||||
Action::Back => state.back(),
|
if state.screens.len() > 1 {
|
||||||
_ => {
|
state.screens.pop();
|
||||||
state
|
}
|
||||||
|
NewState {
|
||||||
|
view: state.screens.last().expect("no screen on stack").clone(),
|
||||||
|
replace_view: false,
|
||||||
|
redraw: true,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let new_state = state
|
||||||
.screens
|
.screens
|
||||||
.last()
|
.last()
|
||||||
.expect("no screen on stack")
|
.expect("no screen on stack")
|
||||||
.handle_input(action)
|
.handle_input(action)
|
||||||
.await
|
.await;
|
||||||
|
if action == Action::Timer && !new_state.redraw {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
new_state
|
||||||
};
|
};
|
||||||
|
|
||||||
screen.display(display).await;
|
if new_state.replace_view {
|
||||||
state.screens.push(screen);
|
state.screens.pop();
|
||||||
|
}
|
||||||
|
state.screens.push(new_state.view);
|
||||||
|
|
||||||
|
if action != Action::Timer || new_state.redraw {
|
||||||
|
state.screens.last().unwrap().display(display).await;
|
||||||
|
}
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(DEBOUNCE_DURATION_MILLIS)).await;
|
Timer::after(Duration::from_millis(DEBOUNCE_DURATION_MILLIS)).await;
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-6
@@ -20,12 +20,22 @@ impl NavigationState {
|
|||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn back(&mut self) -> View {
|
pub fn back(&mut self) -> &View {
|
||||||
let screen = self.screens.pop().unwrap();
|
match &self.screens.len() {
|
||||||
if self.screens.is_empty() {
|
0 => {
|
||||||
log::info!("last screen");
|
log::info!("No screens to go back to");
|
||||||
return screen;
|
return self.screens.last().unwrap();
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
log::info!("Only one screen on stack, cannot go back");
|
||||||
|
return self.screens.last().unwrap();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
log::info!("Going back to previous screen");
|
||||||
|
self.screens.pop();
|
||||||
|
return self.screens.last().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
self.screens.pop().unwrap()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::display::sprite::render_sprite_onto_ili9341;
|
use crate::display::sprite::render_sprite_onto_ili9341;
|
||||||
|
use crate::navigation::navigation::NewState;
|
||||||
use alloc::format;
|
use alloc::format;
|
||||||
use embedded_graphics::mono_font::MonoTextStyle;
|
use embedded_graphics::mono_font::MonoTextStyle;
|
||||||
use embedded_graphics::mono_font::ascii::FONT_6X10;
|
use embedded_graphics::mono_font::ascii::FONT_6X10;
|
||||||
@@ -99,12 +100,13 @@ impl Navigable for CardView {
|
|||||||
core::future::ready(())
|
core::future::ready(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_input(&self, input: Action) -> impl core::future::Future<Output = View> + Send {
|
fn handle_input(&self, input: Action) -> impl core::future::Future<Output = NewState> + Send {
|
||||||
let new_menu = match input {
|
let new_state = NewState {
|
||||||
Action::Ok => View::Card(self.clone()),
|
view: View::Card(self.clone()),
|
||||||
_ => View::Card(self.clone()),
|
replace_view: true,
|
||||||
|
redraw: !matches!(input, Action::Timer),
|
||||||
};
|
};
|
||||||
|
|
||||||
future::ready(new_menu)
|
future::ready(new_state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,7 @@ use embedded_graphics::prelude::RgbColor;
|
|||||||
use embedded_graphics_core::draw_target::DrawTarget;
|
use embedded_graphics_core::draw_target::DrawTarget;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
card::model::Card,
|
card::model::Card, display::primary_lcd::PrimaryLcdDisplay, navigation::navigation::{Action, Navigable, NewState}, views::view::View,
|
||||||
display::primary_lcd::PrimaryLcdDisplay,
|
|
||||||
navigation::navigation::{Action, Navigable},
|
|
||||||
views::view::View,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -29,12 +26,13 @@ impl Navigable for JournalView {
|
|||||||
core::future::ready(())
|
core::future::ready(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_input(&self, input: Action) -> impl core::future::Future<Output = View> + Send {
|
fn handle_input(&self, input: Action) -> impl core::future::Future<Output = NewState> + Send {
|
||||||
let new_menu = match input {
|
let new_state = NewState {
|
||||||
Action::Ok => View::Journal(self.clone()),
|
view: View::Journal(self.clone()),
|
||||||
_ => View::Journal(self.clone()),
|
replace_view: true,
|
||||||
|
redraw: !matches!(input, Action::Timer),
|
||||||
};
|
};
|
||||||
|
|
||||||
future::ready(new_menu)
|
future::ready(new_state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-10
@@ -9,12 +9,12 @@ 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};
|
||||||
|
|
||||||
use crate::navigation::navigation::{Action, Navigable};
|
use crate::navigation::navigation::{Action, Navigable, NewState};
|
||||||
use embedded_graphics::pixelcolor::Rgb565;
|
use embedded_graphics::pixelcolor::Rgb565;
|
||||||
use embedded_graphics::prelude::RgbColor;
|
use embedded_graphics::prelude::RgbColor;
|
||||||
use embedded_graphics_core::draw_target::DrawTarget;
|
use embedded_graphics_core::draw_target::DrawTarget;
|
||||||
|
|
||||||
pub const MAX_SELECTED: i32 = 3;
|
pub const MAX_SELECTED: i32 = 4;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MainMenu {
|
pub struct MainMenu {
|
||||||
@@ -28,14 +28,15 @@ impl Navigable for MainMenu {
|
|||||||
) -> impl core::future::Future<Output = ()> + Send {
|
) -> impl core::future::Future<Output = ()> + Send {
|
||||||
display.clear(Rgb565::BLACK).unwrap();
|
display.clear(Rgb565::BLACK).unwrap();
|
||||||
|
|
||||||
menu_item::show(display, "Last card", 0, self.selected);
|
menu_item::show(display, "Scan card", 0, self.selected);
|
||||||
menu_item::show(display, "Journal", 1, self.selected);
|
menu_item::show(display, "Last card", 1, self.selected);
|
||||||
menu_item::show(display, "Settings", 2, self.selected);
|
menu_item::show(display, "Journal", 2, self.selected);
|
||||||
|
menu_item::show(display, "Settings", 3, self.selected);
|
||||||
|
|
||||||
core::future::ready(())
|
core::future::ready(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_input(&self, input: Action) -> impl core::future::Future<Output = View> + Send {
|
fn handle_input(&self, input: Action) -> impl core::future::Future<Output = NewState> + Send {
|
||||||
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();
|
||||||
|
|
||||||
@@ -57,17 +58,22 @@ impl Navigable for MainMenu {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Action::Ok => match self.selected {
|
Action::Ok => match self.selected {
|
||||||
0 => View::Card(CardView { card: card_mock1 }),
|
0 => View::Scan(crate::views::scan_menu::ScanMenu { progress: 0 }),
|
||||||
1 => View::Journal(JournalView {
|
1 => View::Card(CardView { card: card_mock1 }),
|
||||||
|
2 => View::Journal(JournalView {
|
||||||
cards: vec![card_mock1, card_mock2],
|
cards: vec![card_mock1, card_mock2],
|
||||||
selected_card: 0,
|
selected_card: 0,
|
||||||
}),
|
}),
|
||||||
2 => View::Settings(SettingsMenu { selected: 0 }),
|
3 => View::Settings(SettingsMenu { selected: 0 }),
|
||||||
_ => View::Main(self.clone()),
|
_ => View::Main(self.clone()),
|
||||||
},
|
},
|
||||||
_ => View::Main(self.clone()),
|
_ => View::Main(self.clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
future::ready(new_menu)
|
future::ready(NewState {
|
||||||
|
replace_view: matches!(new_menu, View::Main(_)),
|
||||||
|
view: new_menu,
|
||||||
|
redraw: !matches!(input, Action::Timer),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-4
@@ -1,4 +1,6 @@
|
|||||||
use crate::display::primary_lcd::PrimaryLcdDisplay;
|
use crate::display::primary_lcd::PrimaryLcdDisplay;
|
||||||
|
use crate::navigation::navigation::CARD_DATA;
|
||||||
|
use crate::navigation::navigation::NewState;
|
||||||
|
|
||||||
use alloc::format;
|
use alloc::format;
|
||||||
use embedded_graphics::Drawable;
|
use embedded_graphics::Drawable;
|
||||||
@@ -82,16 +84,29 @@ impl Navigable for ScanMenu {
|
|||||||
core::future::ready(())
|
core::future::ready(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_input(&self, input: Action) -> impl core::future::Future<Output = View> + Send {
|
fn handle_input(&self, input: Action) -> impl core::future::Future<Output = NewState> + Send {
|
||||||
async move {
|
async move {
|
||||||
|
log::info!("Scan view detected, checking for NFC card data");
|
||||||
|
if let Some(card_data) = CARD_DATA.lock().await.take() {
|
||||||
|
log::info!("NFC Card to be displayed: {:?}", card_data);
|
||||||
|
NewState {
|
||||||
|
view: View::Card(crate::views::card_view::CardView { card: card_data }),
|
||||||
|
replace_view: true,
|
||||||
|
redraw: true,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
let new_progress = match input {
|
let new_progress = match input {
|
||||||
Action::Up => self.progress.wrapping_add(5),
|
Action::Up => self.progress.wrapping_add(5),
|
||||||
_ => self.progress,
|
_ => self.progress,
|
||||||
};
|
};
|
||||||
|
NewState {
|
||||||
View::Scan(ScanMenu {
|
view: View::Scan(ScanMenu {
|
||||||
progress: new_progress,
|
progress: new_progress,
|
||||||
})
|
}),
|
||||||
|
replace_view: true,
|
||||||
|
redraw: matches!(input, Action::Up | Action::Timer),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::display::primary_lcd::PrimaryLcdDisplay;
|
use crate::display::primary_lcd::PrimaryLcdDisplay;
|
||||||
|
|
||||||
use crate::navigation::navigation::{Action, Navigable};
|
use crate::navigation::navigation::{Action, Navigable, NewState};
|
||||||
use crate::views::{menu_item, view::View};
|
use crate::views::{menu_item, view::View};
|
||||||
|
|
||||||
use embedded_graphics::pixelcolor::Rgb565;
|
use embedded_graphics::pixelcolor::Rgb565;
|
||||||
@@ -25,7 +25,13 @@ impl Navigable for SettingsMenu {
|
|||||||
core::future::ready(())
|
core::future::ready(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_input(&self, _input: Action) -> impl core::future::Future<Output = View> + Send {
|
fn handle_input(&self, input: Action) -> impl core::future::Future<Output = NewState> + Send {
|
||||||
async move { View::Settings(SettingsMenu { selected: 0 }) }
|
async move {
|
||||||
|
NewState {
|
||||||
|
view: View::Settings(SettingsMenu { selected: 0 }),
|
||||||
|
replace_view: true,
|
||||||
|
redraw: !matches!(input, Action::Timer),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -4,7 +4,7 @@ use crate::views::{
|
|||||||
settings_menu::SettingsMenu,
|
settings_menu::SettingsMenu,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::navigation::navigation::Navigable;
|
use crate::navigation::navigation::{Navigable, NewState};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum View {
|
pub enum View {
|
||||||
@@ -34,7 +34,7 @@ impl Navigable for View {
|
|||||||
fn handle_input(
|
fn handle_input(
|
||||||
&self,
|
&self,
|
||||||
input: crate::navigation::navigation::Action,
|
input: crate::navigation::navigation::Action,
|
||||||
) -> impl core::future::Future<Output = Self> + Send {
|
) -> impl core::future::Future<Output = NewState> + Send {
|
||||||
async move {
|
async move {
|
||||||
match self {
|
match self {
|
||||||
View::Main(main_menu) => main_menu.handle_input(input).await,
|
View::Main(main_menu) => main_menu.handle_input(input).await,
|
||||||
|
|||||||
Reference in New Issue
Block a user