From 889da2e62ced19c0e7745206e0388f8a81c35785 Mon Sep 17 00:00:00 2001 From: ede1998 Date: Sun, 23 Aug 2026 10:54:21 +0200 Subject: [PATCH 1/8] prepare Outputs type --- src/bin/main.rs | 44 +++++++++++++++++++++--------------- src/card/model.rs | 12 +++++----- src/display/tertiary_lcd.rs | 2 ++ src/navigation.rs | 1 + src/navigation/navigation.rs | 9 ++++---- src/navigation/outputs.rs | 12 ++++++++++ src/navigation/state.rs | 6 ++--- src/views/menu_item.rs | 2 +- 8 files changed, 56 insertions(+), 32 deletions(-) create mode 100644 src/navigation/outputs.rs diff --git a/src/bin/main.rs b/src/bin/main.rs index fa8a950..80f4141 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -11,10 +11,11 @@ use alloc::boxed::Box; use creaturedex::card::decoder::split_nfc_hex; use creaturedex::card::model::Card; use creaturedex::display::shared_bus::{DualSecondaryDisplay, init_dual_displays}; -use creaturedex::display::tertiary_lcd::{init_tertiary_lcd, write_wrapped}; +use creaturedex::display::tertiary_lcd::{TertiaryI2cLcd, init_tertiary_lcd, write_wrapped}; use creaturedex::drivers::nfc_pn532::NfcPn532Driver; use creaturedex::navigation::inputs::Inputs; use creaturedex::navigation::navigation::{self, CARD_DATA}; +use creaturedex::navigation::outputs::Outputs; use creaturedex::network::wifi::setup_wifi; use embassy_executor::Spawner; use embassy_time::Instant; @@ -65,7 +66,7 @@ async fn main(spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_rtos::start(timg0.timer0, sw_interrupt.software_interrupt0); - let (display, oled) = Box::leak(Box::new(init_dual_displays( + let (display, mut oled) = init_dual_displays( peripherals.SPI2, peripherals.GPIO36, // sck peripherals.GPIO35, // mosi @@ -75,18 +76,12 @@ async fn main(spawner: Spawner) { peripherals.GPIO10, // primary reset peripherals.GPIO47, // secondary reset peripherals.GPIO12, // shared dc - ))); + ); - let _secondary_oled_cs_2 = Output::new( - peripherals.GPIO14, - Level::High, - OutputConfig::default(), - ); - let _secondary_oled_cs_3 = Output::new( - peripherals.GPIO21, - Level::High, - OutputConfig::default(), - ); + let _secondary_oled_cs_2 = + Output::new(peripherals.GPIO14, Level::High, OutputConfig::default()); + let _secondary_oled_cs_3 = + Output::new(peripherals.GPIO21, Level::High, OutputConfig::default()); let config = I2cConfig::default().with_frequency(esp_hal::time::Rate::from_khz(400)); let i2c = match I2c::new(peripherals.I2C0, config) { @@ -101,10 +96,10 @@ async fn main(spawner: Spawner) { let shared_i2c = Box::leak(Box::new(AtomicCell::new(i2c))); log::info!("Shared I2C bus ready on GPIO17 (SDA) and GPIO18 (SCL)"); - let mut lcd = init_tertiary_lcd(AtomicDevice::new(shared_i2c)); - if let Err(_) = write_wrapped(&mut lcd, "Testing more than 16 chars what happens now?") { + let mut lcd: TertiaryI2cLcd = init_tertiary_lcd(AtomicDevice::new(shared_i2c)); + if let Err(e) = 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" + "Tertiary LCD startup text write failed over shared I2C bus; check the shared bus, wiring, and device responses: {e}" ); } @@ -127,14 +122,27 @@ async fn main(spawner: Spawner) { ok: Input::new(peripherals.GPIO0, button_config), }; - display_shit(oled); + display_shit(&mut oled); + + let outputs = Outputs { + primary_display: display, + secondary_display_1: oled, + // secondary_display_2: todo!(), + // secondary_display_3: todo!(), + tertiary_display: lcd, + _led_a: (), + }; log::info!("Setup complete, entering main loop"); - spawner.spawn(navigation::run(inputs, display).expect("run task failed")); + spawner.spawn(navigation::run(inputs, outputs).expect("run task failed")); spawner.spawn(nfc_driver_task(nfc_driver).expect("nfc driver task failed")); } #[embassy_executor::task] +#[allow( + clippy::large_stack_frames, + reason = "ignoring this for now because it still works" +)] async fn nfc_driver_task( mut nfc_driver: NfcPn532Driver>>, ) { diff --git a/src/card/model.rs b/src/card/model.rs index 5747671..bb2ee96 100644 --- a/src/card/model.rs +++ b/src/card/model.rs @@ -48,9 +48,9 @@ impl Display for CardType { } pub type PrimaryColor = Rgb565; -impl Into for CardType { - fn into(self) -> PrimaryColor { - match self { +impl From for PrimaryColor { + fn from(val: CardType) -> Self { + match val { CardType::Default => Rgb888::new(0xD8, 0x68, 0x30).into(), CardType::Blueprint => Rgb888::new(0x6D, 0x81, 0xFF).into(), CardType::CYBER => Rgb888::new(0xFF, 0xFF, 0x04).into(), @@ -64,9 +64,9 @@ impl Into for CardType { } pub type Palette = [Rgb565; 16]; -impl Into for CardType { - fn into(self) -> Palette { - match self { +impl From for Palette { + fn from(val: CardType) -> Self { + match val { CardType::Default => [ Rgb888::new(31, 23, 35).into(), Rgb888::new(60, 47, 82).into(), diff --git a/src/display/tertiary_lcd.rs b/src/display/tertiary_lcd.rs index e066344..d3f10dc 100644 --- a/src/display/tertiary_lcd.rs +++ b/src/display/tertiary_lcd.rs @@ -1,9 +1,11 @@ use embedded_hal::i2c::I2c; +use embedded_hal_bus::i2c::AtomicDevice; use esp_hal::delay::Delay; use i2c_character_display::{CharacterDisplayPCF8574T, LcdDisplayType}; use log::error; pub type TertiaryLcd = CharacterDisplayPCF8574T; +pub type TertiaryI2cLcd<'a> = TertiaryLcd>>; pub fn init_tertiary_lcd(i2c: I2C) -> TertiaryLcd where diff --git a/src/navigation.rs b/src/navigation.rs index 6bee9cb..cbefd23 100644 --- a/src/navigation.rs +++ b/src/navigation.rs @@ -1,3 +1,4 @@ pub mod inputs; pub mod navigation; pub mod state; +pub mod outputs; diff --git a/src/navigation/navigation.rs b/src/navigation/navigation.rs index dd8fc34..d726cf2 100644 --- a/src/navigation/navigation.rs +++ b/src/navigation/navigation.rs @@ -1,7 +1,7 @@ use crate::card::model::Card; -use crate::display::shared_bus::DualPrimaryDisplay; use crate::navigation::inputs::ButtonAction; use crate::navigation::inputs::Inputs; +use crate::navigation::outputs::Outputs; use crate::navigation::state::NavigationState; use crate::views::view::View; @@ -33,15 +33,16 @@ pub trait Navigable { where D: DrawTarget + Send, D::Error: core::fmt::Debug; + // fn display(&self, outputs: &mut Outputs) -> impl core::future::Future + Send; fn handle_input(&self, input: Action) -> impl core::future::Future + Send; } #[embassy_executor::task] -pub async fn run(mut inputs: Inputs, display: &'static mut DualPrimaryDisplay<'static>) { +pub async fn run(mut inputs: Inputs, mut outputs: Outputs) { log::info!("starting navigation"); let mut state = NavigationState::new(); - state.screens.last().unwrap().display(display).await; + state.screens.last().unwrap().display(&mut outputs.primary_display).await; loop { let selection = select( @@ -87,7 +88,7 @@ pub async fn run(mut inputs: Inputs, display: &'static mut DualPrimaryDisplay<'s state.screens.push(new_state.view); if action != Action::Timer || new_state.redraw { - state.screens.last().unwrap().display(display).await; + state.screens.last().unwrap().display(&mut outputs.primary_display).await; } Timer::after(Duration::from_millis(DEBOUNCE_DURATION_MILLIS)).await; diff --git a/src/navigation/outputs.rs b/src/navigation/outputs.rs new file mode 100644 index 0000000..9a1683a --- /dev/null +++ b/src/navigation/outputs.rs @@ -0,0 +1,12 @@ +use crate::display::{shared_bus::{DualPrimaryDisplay, DualSecondaryDisplay}, tertiary_lcd::TertiaryI2cLcd}; + + + +pub struct Outputs { + pub primary_display: DualPrimaryDisplay<'static>, + pub secondary_display_1: DualSecondaryDisplay<'static>, + // pub secondary_display_2: DualSecondaryDisplay<'static>, + // pub secondary_display_3: DualSecondaryDisplay<'static>, + pub tertiary_display: TertiaryI2cLcd<'static>, + pub _led_a: (), +} \ No newline at end of file diff --git a/src/navigation/state.rs b/src/navigation/state.rs index 63ae293..a564a74 100644 --- a/src/navigation/state.rs +++ b/src/navigation/state.rs @@ -24,16 +24,16 @@ impl NavigationState { match &self.screens.len() { 0 => { log::info!("No screens to go back to"); - return self.screens.last().unwrap(); + self.screens.last().unwrap() } 1 => { log::info!("Only one screen on stack, cannot go back"); - return self.screens.last().unwrap(); + self.screens.last().unwrap() } _ => { log::info!("Going back to previous screen"); self.screens.pop(); - return self.screens.last().unwrap(); + self.screens.last().unwrap() } } } diff --git a/src/views/menu_item.rs b/src/views/menu_item.rs index 5802bb4..f97e8a9 100644 --- a/src/views/menu_item.rs +++ b/src/views/menu_item.rs @@ -7,7 +7,7 @@ use embedded_graphics::prelude::Point; use embedded_graphics::prelude::RgbColor; use embedded_graphics::text::Text; -pub fn show<'a, D>(display: &mut D, name: &str, position: i32, selected: i32) +pub fn show(display: &mut D, name: &str, position: i32, selected: i32) where D: DrawTarget, D::Error: core::fmt::Debug, -- 2.39.5 From dbbc93b528d4ecf88ff97e8b7376129337f709f1 Mon Sep 17 00:00:00 2001 From: ede1998 Date: Sun, 23 Aug 2026 11:35:15 +0200 Subject: [PATCH 2/8] Use Outputs type in Navigable trait --- Cargo.lock | 3 +- Cargo.toml | 1 + src/navigation/navigation.rs | 12 +-- src/navigation/outputs.rs | 22 +++++- src/views/card_view.rs | 138 +++++++++++++++++------------------ src/views/journal_view.rs | 16 ++-- src/views/main_menu.rs | 7 +- src/views/scan_menu.rs | 8 +- src/views/settings_menu.rs | 8 +- src/views/view.rs | 18 ++--- 10 files changed, 114 insertions(+), 119 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c7ada03..2c79bcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1702,8 +1702,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "i2c-character-display" version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86a620ec16541e28c0e52ce21cd23d734a5d7efaae3d609b2a58694b0ef67739" +source = "git+https://github.com/ede1998/i2c-character-display.git?rev=57aef1f49da183c120b8e995ef77549e3515d3c6#57aef1f49da183c120b8e995ef77549e3515d3c6" dependencies = [ "bitfield 0.17.0", "embedded-hal 1.0.0", diff --git a/Cargo.toml b/Cargo.toml index 55b0e77..f7b92a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ ch1115 = { version = "0.1.2", features = ["graphics"] } [patch.crates-io] ch1115 = { git = "https://github.com/ede1998/ch1115.git", rev = "2d3a3ba38050efe012e6a210afe3e125ec280c37" } +i2c-character-display = { git = "https://github.com/ede1998/i2c-character-display.git", rev = "57aef1f49da183c120b8e995ef77549e3515d3c6" } # For fine tuning these settings, please refer to https://doc.rust-lang.org/cargo/reference/profiles.html [profile.dev] diff --git a/src/navigation/navigation.rs b/src/navigation/navigation.rs index d726cf2..d0f3ecb 100644 --- a/src/navigation/navigation.rs +++ b/src/navigation/navigation.rs @@ -8,8 +8,6 @@ use crate::views::view::View; use embassy_futures::select::{Either, select}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_time::{Duration, Timer}; -use embedded_graphics::draw_target::DrawTarget; -use embedded_graphics::pixelcolor::Rgb565; pub type Mutex = embassy_sync::mutex::Mutex; pub type Signal = embassy_sync::signal::Signal; @@ -29,11 +27,7 @@ pub struct NewState { } pub trait Navigable { - fn display(&self, display: &mut D) -> impl core::future::Future + Send - where - D: DrawTarget + Send, - D::Error: core::fmt::Debug; - // fn display(&self, outputs: &mut Outputs) -> impl core::future::Future + Send; + fn display(&self, outputs: &mut Outputs) -> impl core::future::Future + Send; fn handle_input(&self, input: Action) -> impl core::future::Future + Send; } @@ -42,7 +36,7 @@ pub async fn run(mut inputs: Inputs, mut outputs: Outputs) { log::info!("starting navigation"); let mut state = NavigationState::new(); - state.screens.last().unwrap().display(&mut outputs.primary_display).await; + state.screens.last().unwrap().display(&mut outputs).await; loop { let selection = select( @@ -88,7 +82,7 @@ pub async fn run(mut inputs: Inputs, mut outputs: Outputs) { state.screens.push(new_state.view); if action != Action::Timer || new_state.redraw { - state.screens.last().unwrap().display(&mut outputs.primary_display).await; + state.screens.last().unwrap().display(&mut outputs).await; } Timer::after(Duration::from_millis(DEBOUNCE_DURATION_MILLIS)).await; diff --git a/src/navigation/outputs.rs b/src/navigation/outputs.rs index 9a1683a..277e478 100644 --- a/src/navigation/outputs.rs +++ b/src/navigation/outputs.rs @@ -1,6 +1,12 @@ -use crate::display::{shared_bus::{DualPrimaryDisplay, DualSecondaryDisplay}, tertiary_lcd::TertiaryI2cLcd}; - +use embedded_graphics::{ + draw_target::DrawTarget as _, + pixelcolor::{Rgb565, RgbColor as _}, +}; +use crate::display::{ + shared_bus::{DualPrimaryDisplay, DualSecondaryDisplay}, + tertiary_lcd::TertiaryI2cLcd, +}; pub struct Outputs { pub primary_display: DualPrimaryDisplay<'static>, @@ -9,4 +15,14 @@ pub struct Outputs { // pub secondary_display_3: DualSecondaryDisplay<'static>, pub tertiary_display: TertiaryI2cLcd<'static>, pub _led_a: (), -} \ No newline at end of file +} + +impl Outputs { + pub fn clear_screens(&mut self) { + self.primary_display.clear(Rgb565::BLACK).unwrap(); + self.secondary_display_1.clear().unwrap(); + // self.secondary_display_2.clear().unwrap(); + // self.secondary_display_3.clear().unwrap(); + self.tertiary_display.clear().unwrap(); + } +} diff --git a/src/views/card_view.rs b/src/views/card_view.rs index 5f84710..f3ad385 100644 --- a/src/views/card_view.rs +++ b/src/views/card_view.rs @@ -1,5 +1,6 @@ use crate::display::sprite::render_sprite_onto_ili9341; use crate::navigation::navigation::NewState; +use crate::navigation::outputs::Outputs; use alloc::format; use embedded_graphics::mono_font::MonoTextStyle; use embedded_graphics::mono_font::ascii::FONT_6X10; @@ -24,80 +25,77 @@ pub struct CardView { } impl Navigable for CardView { - fn display(&self, display: &mut D) -> impl core::future::Future + Send - where - D: DrawTarget, - D::Error: core::fmt::Debug, - { - display.clear(Rgb565::BLACK).unwrap(); + fn display(&self, outputs: &mut Outputs) -> impl core::future::Future + Send { + async move { + let display = &mut outputs.primary_display; + display.clear(Rgb565::BLACK).unwrap(); - let background = Rectangle::new(Point::new(0, 0), Size::new(240, 320)); - let bg_color = self.card.cardtype.clone().into(); + let background = Rectangle::new(Point::new(0, 0), Size::new(240, 320)); + let bg_color = self.card.cardtype.clone().into(); - let _ = display.fill_solid(&background, bg_color); + let _ = display.fill_solid(&background, bg_color); - let style = MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE); - Text::new( - &format!("Name: {}", self.card.name), - Point::new(20, 240), - style, - ) - .draw(display) - .unwrap(); - Text::new( - &format!("Type: {}", self.card.cardtype), - Point::new(20, 250), - style, - ) - .draw(display) - .unwrap(); - Text::new( - &format!("Event: {}", self.card.event), - Point::new(20, 260), - style, - ) - .draw(display) - .unwrap(); - Text::new( - &format!("UUID: {}", self.card.uuid), - Point::new(20, 270), - style, - ) - .draw(display) - .unwrap(); - Text::new( - &format!("Trait1: {}", self.card.trait1), - Point::new(20, 280), - style, - ) - .draw(display) - .unwrap(); - Text::new( - &format!("Trait2: {}", self.card.trait2), - Point::new(20, 290), - style, - ) - .draw(display) - .unwrap(); - Text::new( - &format!("Trait3: {}", self.card.trait3), - Point::new(20, 300), - style, - ) - .draw(display) - .unwrap(); - Text::new( - &format!("Secret: {}", self.card.secret), - Point::new(20, 310), - style, - ) - .draw(display) - .unwrap(); + let style = MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE); + Text::new( + &format!("Name: {}", self.card.name), + Point::new(20, 240), + style, + ) + .draw(display) + .unwrap(); + Text::new( + &format!("Type: {}", self.card.cardtype), + Point::new(20, 250), + style, + ) + .draw(display) + .unwrap(); + Text::new( + &format!("Event: {}", self.card.event), + Point::new(20, 260), + style, + ) + .draw(display) + .unwrap(); + Text::new( + &format!("UUID: {}", self.card.uuid), + Point::new(20, 270), + style, + ) + .draw(display) + .unwrap(); + Text::new( + &format!("Trait1: {}", self.card.trait1), + Point::new(20, 280), + style, + ) + .draw(display) + .unwrap(); + Text::new( + &format!("Trait2: {}", self.card.trait2), + Point::new(20, 290), + style, + ) + .draw(display) + .unwrap(); + Text::new( + &format!("Trait3: {}", self.card.trait3), + Point::new(20, 300), + style, + ) + .draw(display) + .unwrap(); + Text::new( + &format!("Secret: {}", self.card.secret), + Point::new(20, 310), + style, + ) + .draw(display) + .unwrap(); - let palette: Palette = self.card.cardtype.clone().into(); - render_sprite_onto_ili9341(display, self.card.sprite.data.as_slice(), &palette); - - core::future::ready(()) + let palette: Palette = self.card.cardtype.clone().into(); + render_sprite_onto_ili9341(display, self.card.sprite.data.as_slice(), &palette); + } } fn handle_input(&self, input: Action) -> impl core::future::Future + Send { diff --git a/src/views/journal_view.rs b/src/views/journal_view.rs index 34b7632..1226464 100644 --- a/src/views/journal_view.rs +++ b/src/views/journal_view.rs @@ -7,9 +7,7 @@ use embedded_graphics::prelude::RgbColor; use embedded_graphics_core::draw_target::DrawTarget; use crate::{ - card::model::Card, - navigation::navigation::{Action, Navigable, NewState}, - views::view::View, + card::model::Card, navigation::{navigation::{Action, Navigable, NewState}, outputs::Outputs}, views::view::View, }; #[derive(Debug, Clone)] @@ -19,14 +17,10 @@ pub struct JournalView { } impl Navigable for JournalView { - fn display(&self, display: &mut D) -> impl core::future::Future + Send - where - D: DrawTarget, - D::Error: core::fmt::Debug, - { - display.clear(Rgb565::BLACK).unwrap(); - - core::future::ready(()) + fn display(&self, outputs: &mut Outputs) -> impl core::future::Future + Send { + async move { + outputs.primary_display.clear(Rgb565::BLACK).unwrap(); + } } fn handle_input(&self, input: Action) -> impl core::future::Future + Send { diff --git a/src/views/main_menu.rs b/src/views/main_menu.rs index ddc61e3..f7cd08d 100644 --- a/src/views/main_menu.rs +++ b/src/views/main_menu.rs @@ -4,6 +4,7 @@ use crate::card::model::Card; use crate::card::{decoder::split_nfc_hex, mock::*}; use crate::navigation::inputs::ButtonAction; +use crate::navigation::outputs::Outputs; use crate::views::card_view::CardView; use crate::views::journal_view::JournalView; use crate::views::{menu_item, settings_menu::SettingsMenu, view::View}; @@ -21,11 +22,9 @@ pub struct MainMenu { } impl Navigable for MainMenu { - fn display(&self, display: &mut D) -> impl core::future::Future + Send - where - D: DrawTarget, - D::Error: core::fmt::Debug, + fn display(&self, outputs: &mut Outputs) -> impl core::future::Future + Send { + let display = &mut outputs.primary_display; display.clear(Rgb565::BLACK).unwrap(); menu_item::show(display, "Scan card", 0, self.selected); diff --git a/src/views/scan_menu.rs b/src/views/scan_menu.rs index d9c40c9..56c2c4f 100644 --- a/src/views/scan_menu.rs +++ b/src/views/scan_menu.rs @@ -1,10 +1,12 @@ use crate::navigation::inputs::ButtonAction; use crate::navigation::navigation::CARD_DATA; use crate::navigation::navigation::NewState; +use crate::navigation::outputs::Outputs; use alloc::format; use embedded_graphics::Drawable; use embedded_graphics::geometry::AngleUnit as _; +use embedded_graphics::geometry::Dimensions as _; use embedded_graphics::geometry::Size; use embedded_graphics::mono_font::MonoTextStyle; use embedded_graphics::mono_font::ascii::FONT_10X20; @@ -31,11 +33,9 @@ pub struct ScanMenu { } impl Navigable for ScanMenu { - fn display(&self, display: &mut D) -> impl core::future::Future + Send - where - D: DrawTarget, - D::Error: core::fmt::Debug, + fn display(&self, outputs: &mut Outputs) -> impl core::future::Future + Send { + let display = &mut outputs.primary_display; let style = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE); display.clear(Rgb565::BLACK).unwrap(); diff --git a/src/views/settings_menu.rs b/src/views/settings_menu.rs index f050572..f855e91 100644 --- a/src/views/settings_menu.rs +++ b/src/views/settings_menu.rs @@ -1,4 +1,5 @@ use crate::navigation::navigation::{Action, Navigable, NewState}; +use crate::navigation::outputs::Outputs; use crate::views::{menu_item, view::View}; use embedded_graphics::pixelcolor::Rgb565; @@ -12,11 +13,8 @@ pub struct SettingsMenu { } impl Navigable for SettingsMenu { - fn display(&self, display: &mut D) -> impl core::future::Future + Send - where - D: DrawTarget, - D::Error: core::fmt::Debug, - { + fn display(&self, outputs: &mut Outputs) -> impl core::future::Future + Send { + let display = &mut outputs.primary_display; display.clear(Rgb565::BLACK).unwrap(); menu_item::show(display, "System Inforation", 0, self.selected); menu_item::show(display, "Wifi Information", 1, self.selected); diff --git a/src/views/view.rs b/src/views/view.rs index 8683638..380c854 100644 --- a/src/views/view.rs +++ b/src/views/view.rs @@ -1,6 +1,5 @@ -use embedded_graphics::draw_target::DrawTarget; -use embedded_graphics::pixelcolor::Rgb565; +use crate::navigation::outputs::Outputs; use crate::views::card_view::CardView; use crate::views::{ journal_view::JournalView, main_menu::MainMenu, scan_menu::ScanMenu, @@ -19,18 +18,15 @@ pub enum View { } impl Navigable for View { - fn display(&self, display: &mut D) -> impl core::future::Future + Send - where - D: DrawTarget + Send, - D::Error: core::fmt::Debug, + fn display(&self, outputs: &mut Outputs) -> impl core::future::Future + Send { async move { match self { - View::Main(main_menu) => main_menu.display(display).await, - View::Scan(scan_menu) => scan_menu.display(display).await, - View::Settings(settings_menu) => settings_menu.display(display).await, - View::Journal(journal_view) => journal_view.display(display).await, - View::Card(card_view) => card_view.display(display).await, + View::Main(main_menu) => main_menu.display(outputs).await, + View::Scan(scan_menu) => scan_menu.display(outputs).await, + View::Settings(settings_menu) => settings_menu.display(outputs).await, + View::Journal(journal_view) => journal_view.display(outputs).await, + View::Card(card_view) => card_view.display(outputs).await, } } } -- 2.39.5 From 537692d34894c94fb920bbbc1cd1e392ab147627 Mon Sep 17 00:00:00 2001 From: ede1998 Date: Sun, 23 Aug 2026 12:11:26 +0200 Subject: [PATCH 3/8] Use builder struct instead of 20 arguments --- src/bin/main.rs | 34 ++++++------- src/display/shared_bus.rs | 104 ++++++++++++++++++++++---------------- 2 files changed, 76 insertions(+), 62 deletions(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index 80f4141..bf7600f 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -10,7 +10,7 @@ use alloc::boxed::Box; use creaturedex::card::decoder::split_nfc_hex; use creaturedex::card::model::Card; -use creaturedex::display::shared_bus::{DualSecondaryDisplay, init_dual_displays}; +use creaturedex::display::shared_bus::{self, DualSecondaryDisplay}; use creaturedex::display::tertiary_lcd::{TertiaryI2cLcd, init_tertiary_lcd, write_wrapped}; use creaturedex::drivers::nfc_pn532::NfcPn532Driver; use creaturedex::navigation::inputs::Inputs; @@ -29,7 +29,7 @@ use embedded_hal_bus::i2c::AtomicDevice; use embedded_hal_bus::util::AtomicCell; use esp_hal::Blocking; use esp_hal::clock::CpuClock; -use esp_hal::gpio::{Input, InputConfig, Level, Output, OutputConfig, Pull}; +use esp_hal::gpio::{Input, InputConfig, Pull}; use esp_hal::i2c::master::{Config as I2cConfig, I2c}; use esp_hal::timer::timg::TimerGroup; use log::error; @@ -66,22 +66,20 @@ async fn main(spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_rtos::start(timg0.timer0, sw_interrupt.software_interrupt0); - let (display, mut oled) = init_dual_displays( - peripherals.SPI2, - peripherals.GPIO36, // sck - peripherals.GPIO35, // mosi - peripherals.GPIO37, // miso - peripherals.GPIO11, // primary cs - peripherals.GPIO13, // oled cs - peripherals.GPIO10, // primary reset - peripherals.GPIO47, // secondary reset - peripherals.GPIO12, // shared dc - ); - - let _secondary_oled_cs_2 = - Output::new(peripherals.GPIO14, Level::High, OutputConfig::default()); - let _secondary_oled_cs_3 = - Output::new(peripherals.GPIO21, Level::High, OutputConfig::default()); + let (display, mut oled) = shared_bus::DisplayPinConfiguration { + spi_peripheral: peripherals.SPI2, + sck: peripherals.GPIO36, + mosi: peripherals.GPIO35, + miso: peripherals.GPIO37, + cs_primary: peripherals.GPIO11, + cs_secondary_1: peripherals.GPIO13, + cs_secondary_2: peripherals.GPIO14, + cs_secondary_3: peripherals.GPIO21, + reset_primary: peripherals.GPIO10, + reset_secondary: peripherals.GPIO47, + dc_pin: peripherals.GPIO12, + } + .build(); let config = I2cConfig::default().with_frequency(esp_hal::time::Rate::from_khz(400)); let i2c = match I2c::new(peripherals.I2C0, config) { diff --git a/src/display/shared_bus.rs b/src/display/shared_bus.rs index 2a42521..964c49d 100644 --- a/src/display/shared_bus.rs +++ b/src/display/shared_bus.rs @@ -10,8 +10,8 @@ use esp_hal::spi::Mode; use esp_hal::spi::master::{Config as SpiConfig, Spi}; use esp_hal::time::Rate; -use crate::display::secondary_oled::{SecondaryOledDisplay, init_secondary_oled_on_bus}; use crate::display::primary_lcd::{PrimaryLcdDisplay, init_primary_lcd_on_bus}; +use crate::display::secondary_oled::{SecondaryOledDisplay, init_secondary_oled_on_bus}; use core::cell::RefCell; use embassy_sync::blocking_mutex::Mutex as BlockingMutex; @@ -52,62 +52,78 @@ pub type DualPrimaryDisplay<'a> = pub type DualSecondaryDisplay<'a> = SecondaryOledDisplay<'a, DisplaySpiBus, SharedPin<'a, Output<'a>>, SharedPin<'a, Output<'a>>>; -pub fn init_dual_displays( - spi_peripheral: SPI, - sck: SCK, - mosi: MOSI, - miso: MISO, - cs_primary: CS1, - cs_secondary: CS2, - reset_primary: RES1, - reset_secondary: RES2, - dc_pin: DC, -) -> (DualPrimaryDisplay<'static>, DualSecondaryDisplay<'static>) +pub struct DisplayPinConfiguration { + pub spi_peripheral: SPI, + pub sck: SCK, + pub mosi: MOSI, + pub miso: MISO, + pub cs_primary: CS0, + pub cs_secondary_1: CS1, + pub cs_secondary_2: CS2, + pub cs_secondary_3: CS3, + pub reset_primary: RES1, + pub reset_secondary: RES2, + pub dc_pin: DC, +} + +impl + DisplayPinConfiguration where SPI: esp_hal::spi::master::Instance + 'static, SCK: PeripheralOutput<'static>, MOSI: PeripheralOutput<'static>, MISO: PeripheralInput<'static>, + CS0: OutputPin + 'static, CS1: OutputPin + 'static, CS2: OutputPin + 'static, + CS3: OutputPin + 'static, RES1: OutputPin + 'static, RES2: OutputPin + 'static, DC: OutputPin + 'static, { - let spi = Spi::new( - spi_peripheral, - SpiConfig::default() - .with_frequency(Rate::from_khz(20_000)) - .with_mode(Mode::_0), - ) - .unwrap() - .with_sck(sck) - .with_mosi(mosi) - .with_miso(miso); + pub fn build(self) -> (DualPrimaryDisplay<'static>, DualSecondaryDisplay<'static>) { + let spi = Spi::new( + self.spi_peripheral, + SpiConfig::default() + .with_frequency(Rate::from_khz(20_000)) + .with_mode(Mode::_0), + ) + .unwrap() + .with_sck(self.sck) + .with_mosi(self.mosi) + .with_miso(self.miso); - let bus_static: &'static AtomicCell<_> = Box::leak(Box::new(AtomicCell::new(spi))); - let reset_primary_static = - SharedPin::pre_wrap(Output::new(reset_primary, Level::High, OutputConfig::default())); - let reset_secondary_static = - SharedPin::pre_wrap(Output::new(reset_secondary, Level::High, OutputConfig::default())); - let dc_static = SharedPin::pre_wrap(Output::new(dc_pin, Level::High, OutputConfig::default())); - let mut delay = Delay::new(); + let bus_static: &'static AtomicCell<_> = Box::leak(Box::new(AtomicCell::new(spi))); + let reset_primary_static = SharedPin::pre_wrap(Output::new( + self.reset_primary, + Level::High, + OutputConfig::default(), + )); + let reset_secondary_static = SharedPin::pre_wrap(Output::new( + self.reset_secondary, + Level::High, + OutputConfig::default(), + )); + let dc_static = + SharedPin::pre_wrap(Output::new(self.dc_pin, Level::High, OutputConfig::default())); + let mut delay = Delay::new(); - let primary = init_primary_lcd_on_bus( - bus_static, - cs_primary, - SharedPin::new(reset_primary_static), - SharedPin::new(dc_static), - &mut delay, - ); + let primary = init_primary_lcd_on_bus( + bus_static, + self.cs_primary, + SharedPin::new(reset_primary_static), + SharedPin::new(dc_static), + &mut delay, + ); - let secondary = init_secondary_oled_on_bus( - bus_static, - cs_secondary, - SharedPin::new(reset_secondary_static), - SharedPin::new(dc_static), - &mut delay, - ); + let secondary = init_secondary_oled_on_bus( + bus_static, + self.cs_secondary_1, + SharedPin::new(reset_secondary_static), + SharedPin::new(dc_static), + &mut delay, + ); - (primary, secondary) + (primary, secondary) + } } -- 2.39.5 From 357701a68535ee910bdb7d9961618bad161c86d8 Mon Sep 17 00:00:00 2001 From: ede1998 Date: Sun, 23 Aug 2026 13:10:33 +0200 Subject: [PATCH 4/8] Add shared reset pin --- src/display/shared_bus.rs | 104 ++++++++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 21 deletions(-) diff --git a/src/display/shared_bus.rs b/src/display/shared_bus.rs index 964c49d..37145bc 100644 --- a/src/display/shared_bus.rs +++ b/src/display/shared_bus.rs @@ -16,19 +16,21 @@ use crate::display::secondary_oled::{SecondaryOledDisplay, init_secondary_oled_o use core::cell::RefCell; use embassy_sync::blocking_mutex::Mutex as BlockingMutex; -#[derive(Clone)] pub struct SharedPin<'a, P> { mutex: &'a BlockingMutex>, } -impl<'a, P> SharedPin<'a, P> { - pub fn new(mutex: &'a BlockingMutex>) -> Self { - Self { mutex } +impl<'a, P> Clone for SharedPin<'a, P> { + fn clone(&self) -> Self { + Self { mutex: self.mutex } } +} - pub fn pre_wrap(pin: P) -> &'static BlockingMutex> { +impl<'a, P> SharedPin<'a, P> { + pub fn new(pin: P) -> Self { let mutexed = BlockingMutex::new(RefCell::new(pin)); - Box::leak(Box::new(mutexed)) + let mutex = Box::leak(Box::new(mutexed)); + Self { mutex } } } @@ -46,11 +48,66 @@ impl<'a, P: EhOutputPin> EhOutputPin for SharedPin<'a, P> { } } +pub struct SharedResetPin<'a, P> { + mutex: &'a BlockingMutex>, +} + +impl<'a, P> SharedResetPin<'a, P> { + pub fn new(pin: P, disabled: bool) -> Self { + let mutexed = BlockingMutex::new(RefCell::new((pin, disabled))); + let mutex = Box::leak(Box::new(mutexed)); + Self { mutex } + } + + pub fn disable(&self, disabled: bool) { + self.mutex.lock(|p| { + p.borrow_mut().1 = disabled; + }); + if disabled { + log::debug!("Reset pin disabled (masked). No more resets now."); + } else { + log::debug!("Reset pin enabled (unmasked). Reset now possible."); + } + } +} + +impl<'a, P> Clone for SharedResetPin<'a, P> { + fn clone(&self) -> Self { + Self { mutex: self.mutex } + } +} + +impl<'a, P: EhOutputPin> ErrorType for SharedResetPin<'a, P> { + type Error = P::Error; +} + +impl<'a, P: EhOutputPin> EhOutputPin for SharedResetPin<'a, P> { + fn set_low(&mut self) -> Result<(), Self::Error> { + self.mutex.lock(|p| { + let mut guard = p.borrow_mut(); + let disabled = guard.1; + let pin = &mut guard.0; + if !disabled { pin.set_low() } else { Ok(()) } + }) + } + + fn set_high(&mut self) -> Result<(), Self::Error> { + self.mutex.lock(|p| { + let mut guard = p.borrow_mut(); + let disabled = guard.1; + let pin = &mut guard.0; + if !disabled { pin.set_high() } else { Ok(()) } + }) + } +} + +type SharedOutput<'a> = SharedPin<'a, Output<'a>>; +type SharedReset<'a> = SharedResetPin<'a, Output<'a>>; pub type DisplaySpiBus = Spi<'static, esp_hal::Blocking>; pub type DualPrimaryDisplay<'a> = - PrimaryLcdDisplay<'a, DisplaySpiBus, SharedPin<'a, Output<'a>>, SharedPin<'a, Output<'a>>>; + PrimaryLcdDisplay<'a, DisplaySpiBus, SharedOutput<'a>, Output<'a>>; pub type DualSecondaryDisplay<'a> = - SecondaryOledDisplay<'a, DisplaySpiBus, SharedPin<'a, Output<'a>>, SharedPin<'a, Output<'a>>>; + SecondaryOledDisplay<'a, DisplaySpiBus, SharedReset<'a>, SharedOutput<'a>>; pub struct DisplayPinConfiguration { pub spi_peripheral: SPI, @@ -94,33 +151,38 @@ where .with_miso(self.miso); let bus_static: &'static AtomicCell<_> = Box::leak(Box::new(AtomicCell::new(spi))); - let reset_primary_static = SharedPin::pre_wrap(Output::new( - self.reset_primary, + let reset_primary = Output::new(self.reset_primary, Level::High, OutputConfig::default()); + let reset_secondary = SharedResetPin::new( + Output::new(self.reset_secondary, Level::High, OutputConfig::default()), + false, + ); + let dc = SharedPin::new(Output::new( + self.dc_pin, Level::High, OutputConfig::default(), )); - let reset_secondary_static = SharedPin::pre_wrap(Output::new( - self.reset_secondary, - Level::High, - OutputConfig::default(), - )); - let dc_static = - SharedPin::pre_wrap(Output::new(self.dc_pin, Level::High, OutputConfig::default())); let mut delay = Delay::new(); let primary = init_primary_lcd_on_bus( bus_static, self.cs_primary, - SharedPin::new(reset_primary_static), - SharedPin::new(dc_static), + reset_primary, + dc.clone(), &mut delay, ); + // TODO + // Build display + // Trigger reset + // disable init + // rest of init code + + let secondary = init_secondary_oled_on_bus( bus_static, self.cs_secondary_1, - SharedPin::new(reset_secondary_static), - SharedPin::new(dc_static), + reset_secondary, + dc, &mut delay, ); -- 2.39.5 From b2a8fdea5e410394a630a86ae1c2c7587658aa8a Mon Sep 17 00:00:00 2001 From: ede1998 Date: Sun, 23 Aug 2026 16:06:36 +0200 Subject: [PATCH 5/8] triple oled initialization --- src/bin/main.rs | 30 ++++++++------ src/display/primary_lcd.rs | 61 ++++++++++++++--------------- src/display/secondary_oled.rs | 73 +++++++++++++++++++++++------------ src/display/shared_bus.rs | 67 ++++++++++++++++---------------- src/display/sprite.rs | 5 +-- src/navigation/outputs.rs | 6 +-- 6 files changed, 135 insertions(+), 107 deletions(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index bf7600f..ef4e201 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -10,7 +10,8 @@ use alloc::boxed::Box; use creaturedex::card::decoder::split_nfc_hex; use creaturedex::card::model::Card; -use creaturedex::display::shared_bus::{self, DualSecondaryDisplay}; +use creaturedex::display::secondary_oled::DualSecondaryDisplay; +use creaturedex::display::shared_bus; use creaturedex::display::tertiary_lcd::{TertiaryI2cLcd, init_tertiary_lcd, write_wrapped}; use creaturedex::drivers::nfc_pn532::NfcPn532Driver; use creaturedex::navigation::inputs::Inputs; @@ -66,7 +67,7 @@ async fn main(spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_rtos::start(timg0.timer0, sw_interrupt.software_interrupt0); - let (display, mut oled) = shared_bus::DisplayPinConfiguration { + let (primary, mut secondaries) = shared_bus::DisplayPinConfiguration { spi_peripheral: peripherals.SPI2, sck: peripherals.GPIO36, mosi: peripherals.GPIO35, @@ -120,15 +121,20 @@ async fn main(spawner: Spawner) { ok: Input::new(peripherals.GPIO0, button_config), }; - display_shit(&mut oled); + display_shit(&mut secondaries[0], "foo 1"); + display_shit(&mut secondaries[1], "bar 1"); + display_shit(&mut secondaries[2], "baz 1"); - let outputs = Outputs { - primary_display: display, - secondary_display_1: oled, - // secondary_display_2: todo!(), - // secondary_display_3: todo!(), - tertiary_display: lcd, - _led_a: (), + let outputs = { + let [sec_1, sec_2, sec_3] = secondaries; + Outputs { + primary_display: primary, + secondary_display_1: sec_1, + secondary_display_2: sec_2, + secondary_display_3: sec_3, + tertiary_display: lcd, + _led_a: (), + } }; log::info!("Setup complete, entering main loop"); @@ -173,11 +179,11 @@ async fn nfc_driver_task( } } -fn display_shit(oled: &mut DualSecondaryDisplay<'static>) { +fn display_shit(oled: &mut DualSecondaryDisplay<'static>, text: &str) { oled.clear().unwrap(); let text_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On); - Text::new("Hello OLED", Point::new(10, 20), text_style) + Text::new(text, Point::new(10, 20), text_style) .draw(oled) .unwrap(); oled.flush().unwrap(); diff --git a/src/display/primary_lcd.rs b/src/display/primary_lcd.rs index a8a5578..8941a98 100644 --- a/src/display/primary_lcd.rs +++ b/src/display/primary_lcd.rs @@ -2,42 +2,41 @@ use display_interface_spi::SPIInterface; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::pixelcolor::Rgb565; use embedded_graphics::prelude::RgbColor; -use embedded_hal::digital::OutputPin as EhOutputPin; use embedded_hal_bus::spi::AtomicDevice; use embedded_hal_bus::util::AtomicCell; use esp_hal::delay::Delay; -use esp_hal::gpio::{Level, Output, OutputConfig, OutputPin}; +use esp_hal::gpio::Output; use ili9341::{DisplaySize240x320, Ili9341, Orientation}; -pub type PrimaryLcdDisplay<'a, BUS, DC, RES> = +use crate::display::shared_bus::{DisplaySpiBus, SharedOutput}; + +type GenericDualPrimaryDisplay<'a, BUS, DC, RES> = Ili9341, Delay>, DC>, RES>; +pub type DualPrimaryDisplay<'a> = + GenericDualPrimaryDisplay<'a, DisplaySpiBus, SharedOutput<'a>, Output<'a>>; -pub fn init_primary_lcd_on_bus<'a, BUS, CS, RES, DC>( - spi_bus: &'a AtomicCell, - cs: CS, - reset: RES, - dc: DC, - delay: &mut Delay, -) -> PrimaryLcdDisplay<'a, BUS, DC, RES> -where - BUS: embedded_hal::spi::SpiBus, - CS: OutputPin + 'static, - RES: EhOutputPin, - DC: EhOutputPin, -{ - let cs_pin = Output::new(cs, Level::High, OutputConfig::default()); - let spi_dev = AtomicDevice::new(spi_bus, cs_pin, *delay).unwrap(); - let iface = SPIInterface::new(spi_dev, dc); - - let mut display = Ili9341::new( - iface, - reset, - delay, - Orientation::Portrait, - DisplaySize240x320, - ) - .unwrap(); - - display.clear(Rgb565::BLACK).unwrap(); - display +pub struct PrimaryDisplayPinConfiguration<'a> { + pub spi_bus: &'a AtomicCell, + pub cs: Output<'a>, + pub reset: Output<'a>, + pub dc: SharedOutput<'a>, +} + +impl<'a> PrimaryDisplayPinConfiguration<'a> { + pub fn build(self) -> DualPrimaryDisplay<'a> { + let spi_dev = AtomicDevice::new(self.spi_bus, self.cs, Delay::new()).unwrap(); + let iface = SPIInterface::new(spi_dev, self.dc); + + let mut display = Ili9341::new( + iface, + self.reset, + &mut Delay::new(), + Orientation::Portrait, + DisplaySize240x320, + ) + .unwrap(); + + display.clear(Rgb565::BLACK).unwrap(); + display + } } diff --git a/src/display/secondary_oled.rs b/src/display/secondary_oled.rs index b003bdb..340faee 100644 --- a/src/display/secondary_oled.rs +++ b/src/display/secondary_oled.rs @@ -1,34 +1,59 @@ +use alloc::format; use ch1115::{Ch1115, Size128x64}; use display_interface_spi::SPIInterface; -use embedded_hal::digital::OutputPin as EhOutputPin; use embedded_hal_bus::spi::AtomicDevice; use embedded_hal_bus::util::AtomicCell; use esp_hal::delay::Delay; -use esp_hal::gpio::{Level, Output, OutputConfig, OutputPin}; +use esp_hal::gpio::Output; + +use crate::display::shared_bus::{DisplaySpiBus, SharedOutput, SharedReset}; pub type SecondaryOledDisplay<'a, BUS, RST, DC> = Ch1115, Delay>, DC>, RST, Size128x64>; +pub type DualSecondaryDisplay<'a> = + SecondaryOledDisplay<'a, DisplaySpiBus, SharedReset<'a>, SharedOutput<'a>>; -pub fn init_secondary_oled_on_bus<'a, BUS, CS, RES, DC>( - spi_bus: &'a AtomicCell, - cs: CS, - res: RES, - dc: DC, - delay: &mut Delay, -) -> SecondaryOledDisplay<'a, BUS, RES, DC> -where - BUS: embedded_hal::spi::SpiBus, - CS: OutputPin + 'static, - RES: EhOutputPin, - DC: EhOutputPin, -{ - let cs_pin = Output::new(cs, Level::High, OutputConfig::default()); - let spi_dev = AtomicDevice::new(spi_bus, cs_pin, *delay).unwrap(); - let interface = SPIInterface::new(spi_dev, dc); - - let mut display = Ch1115::new(interface, res, Size128x64); - - display.init(&mut Delay::new()).unwrap(); - - display +pub struct SecondaryDisplayPinConfiguration<'a> { + pub spi_bus: &'a AtomicCell, + pub cs_pins: [Output<'a>; 3], + pub res: SharedReset<'a>, + pub dc: SharedOutput<'a>, +} + +impl<'a> SecondaryDisplayPinConfiguration<'a> { + pub fn build(self) -> [DualSecondaryDisplay<'a>; 3] { + let mut displays = self.cs_pins.map(|cs| { + let spi_dev = AtomicDevice::new(self.spi_bus, cs, Delay::new()).unwrap(); + let interface = SPIInterface::new(spi_dev, self.dc.clone()); + + Ch1115::new(interface, self.res.clone(), Size128x64) + }); + + init_single_reset(&mut displays, self.res); + + displays + } +} + +fn init_single_reset<'a: 'b, 'b>( + displays: impl IntoIterator>, + shared_reset: SharedReset<'a>, +) { + let mut displays = displays.into_iter().peekable(); + + // Reset all secondaries via shared reset + let Some(any_display) = displays.peek_mut() else { + // no display to initialize + return; + }; + any_display.hard_reset(&mut Delay::new()).unwrap(); + + shared_reset.disable(true); + + for (i, display) in displays.enumerate() { + display + .init(&mut Delay::new()) + .map_err(|e| format!("Failed to init secondary display {}: {e:?}", i + 1)) + .unwrap(); + } } diff --git a/src/display/shared_bus.rs b/src/display/shared_bus.rs index 37145bc..f8a55b4 100644 --- a/src/display/shared_bus.rs +++ b/src/display/shared_bus.rs @@ -3,15 +3,16 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embedded_hal::digital::ErrorType; use embedded_hal::digital::OutputPin as EhOutputPin; use embedded_hal_bus::util::AtomicCell; -use esp_hal::delay::Delay; use esp_hal::gpio::interconnect::{PeripheralInput, PeripheralOutput}; use esp_hal::gpio::{Level, Output, OutputConfig, OutputPin}; use esp_hal::spi::Mode; use esp_hal::spi::master::{Config as SpiConfig, Spi}; use esp_hal::time::Rate; -use crate::display::primary_lcd::{PrimaryLcdDisplay, init_primary_lcd_on_bus}; -use crate::display::secondary_oled::{SecondaryOledDisplay, init_secondary_oled_on_bus}; +use crate::display::primary_lcd::DualPrimaryDisplay; +use crate::display::primary_lcd::PrimaryDisplayPinConfiguration; +use crate::display::secondary_oled::DualSecondaryDisplay; +use crate::display::secondary_oled::SecondaryDisplayPinConfiguration; use core::cell::RefCell; use embassy_sync::blocking_mutex::Mutex as BlockingMutex; @@ -101,13 +102,9 @@ impl<'a, P: EhOutputPin> EhOutputPin for SharedResetPin<'a, P> { } } -type SharedOutput<'a> = SharedPin<'a, Output<'a>>; -type SharedReset<'a> = SharedResetPin<'a, Output<'a>>; +pub type SharedOutput<'a> = SharedPin<'a, Output<'a>>; +pub type SharedReset<'a> = SharedResetPin<'a, Output<'a>>; pub type DisplaySpiBus = Spi<'static, esp_hal::Blocking>; -pub type DualPrimaryDisplay<'a> = - PrimaryLcdDisplay<'a, DisplaySpiBus, SharedOutput<'a>, Output<'a>>; -pub type DualSecondaryDisplay<'a> = - SecondaryOledDisplay<'a, DisplaySpiBus, SharedReset<'a>, SharedOutput<'a>>; pub struct DisplayPinConfiguration { pub spi_peripheral: SPI, @@ -138,7 +135,12 @@ where RES2: OutputPin + 'static, DC: OutputPin + 'static, { - pub fn build(self) -> (DualPrimaryDisplay<'static>, DualSecondaryDisplay<'static>) { + pub fn build( + self, + ) -> ( + DualPrimaryDisplay<'static>, + [DualSecondaryDisplay<'static>; 3], + ) { let spi = Spi::new( self.spi_peripheral, SpiConfig::default() @@ -151,7 +153,6 @@ where .with_miso(self.miso); let bus_static: &'static AtomicCell<_> = Box::leak(Box::new(AtomicCell::new(spi))); - let reset_primary = Output::new(self.reset_primary, Level::High, OutputConfig::default()); let reset_secondary = SharedResetPin::new( Output::new(self.reset_secondary, Level::High, OutputConfig::default()), false, @@ -161,31 +162,31 @@ where Level::High, OutputConfig::default(), )); - let mut delay = Delay::new(); - let primary = init_primary_lcd_on_bus( - bus_static, - self.cs_primary, - reset_primary, - dc.clone(), - &mut delay, - ); + let primary = PrimaryDisplayPinConfiguration { + spi_bus: bus_static, + cs: low_active(self.cs_primary), + reset: low_active(self.reset_primary), + dc: dc.clone(), + } + .build(); - // TODO - // Build display - // Trigger reset - // disable init - // rest of init code - - - let secondary = init_secondary_oled_on_bus( - bus_static, - self.cs_secondary_1, - reset_secondary, + let secondaries = SecondaryDisplayPinConfiguration { + spi_bus: bus_static, + cs_pins: [ + low_active(self.cs_secondary_1), + low_active(self.cs_secondary_2), + low_active(self.cs_secondary_3), + ], + res: reset_secondary, dc, - &mut delay, - ); + } + .build(); - (primary, secondary) + (primary, secondaries) } } + +fn low_active<'a, CS: OutputPin + 'a>(pin: CS) -> Output<'a> { + Output::new(pin, Level::High, OutputConfig::default()) +} diff --git a/src/display/sprite.rs b/src/display/sprite.rs index e2d6d5d..b5dec5b 100644 --- a/src/display/sprite.rs +++ b/src/display/sprite.rs @@ -47,10 +47,7 @@ where let width = u32::try_from(end_x - start_x).unwrap(); let height = u32::try_from(end_y - start_y).unwrap(); - let area = Rectangle::new( - Point::new(start_x.try_into().unwrap(), start_y.try_into().unwrap()), - Size::new(width, height), - ); + let area = Rectangle::new(Point::new(start_x, start_y), Size::new(width, height)); let _ = ili9341.fill_solid(&area, color); } diff --git a/src/navigation/outputs.rs b/src/navigation/outputs.rs index 277e478..5181a99 100644 --- a/src/navigation/outputs.rs +++ b/src/navigation/outputs.rs @@ -4,15 +4,15 @@ use embedded_graphics::{ }; use crate::display::{ - shared_bus::{DualPrimaryDisplay, DualSecondaryDisplay}, + primary_lcd::DualPrimaryDisplay, secondary_oled::DualSecondaryDisplay, tertiary_lcd::TertiaryI2cLcd, }; pub struct Outputs { pub primary_display: DualPrimaryDisplay<'static>, pub secondary_display_1: DualSecondaryDisplay<'static>, - // pub secondary_display_2: DualSecondaryDisplay<'static>, - // pub secondary_display_3: DualSecondaryDisplay<'static>, + pub secondary_display_2: DualSecondaryDisplay<'static>, + pub secondary_display_3: DualSecondaryDisplay<'static>, pub tertiary_display: TertiaryI2cLcd<'static>, pub _led_a: (), } -- 2.39.5 From f59ee96cbc3e0bac1c17da3c9e597d4a6b18d005 Mon Sep 17 00:00:00 2001 From: ede1998 Date: Sun, 23 Aug 2026 16:13:55 +0200 Subject: [PATCH 6/8] Rename display type aliases --- src/bin/main.rs | 8 ++++---- src/display/primary_lcd.rs | 8 ++++---- src/display/secondary_oled.rs | 10 +++++----- src/display/shared_bus.rs | 11 +++-------- src/display/tertiary_lcd.rs | 11 ++++++----- src/navigation.rs | 2 +- src/navigation/outputs.rs | 13 ++++++------- src/views/journal_view.rs | 7 ++++++- src/views/main_menu.rs | 3 +-- src/views/scan_menu.rs | 3 +-- src/views/view.rs | 4 +--- 11 files changed, 38 insertions(+), 42 deletions(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index ef4e201..f5809a2 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -10,9 +10,9 @@ use alloc::boxed::Box; use creaturedex::card::decoder::split_nfc_hex; use creaturedex::card::model::Card; -use creaturedex::display::secondary_oled::DualSecondaryDisplay; +use creaturedex::display::secondary_oled::SecondaryDisplay; use creaturedex::display::shared_bus; -use creaturedex::display::tertiary_lcd::{TertiaryI2cLcd, init_tertiary_lcd, write_wrapped}; +use creaturedex::display::tertiary_lcd::{TertiaryDisplay, init_tertiary_lcd, write_wrapped}; use creaturedex::drivers::nfc_pn532::NfcPn532Driver; use creaturedex::navigation::inputs::Inputs; use creaturedex::navigation::navigation::{self, CARD_DATA}; @@ -95,7 +95,7 @@ async fn main(spawner: Spawner) { let shared_i2c = Box::leak(Box::new(AtomicCell::new(i2c))); log::info!("Shared I2C bus ready on GPIO17 (SDA) and GPIO18 (SCL)"); - let mut lcd: TertiaryI2cLcd = init_tertiary_lcd(AtomicDevice::new(shared_i2c)); + let mut lcd: TertiaryDisplay = init_tertiary_lcd(AtomicDevice::new(shared_i2c)); if let Err(e) = 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: {e}" @@ -179,7 +179,7 @@ async fn nfc_driver_task( } } -fn display_shit(oled: &mut DualSecondaryDisplay<'static>, text: &str) { +fn display_shit(oled: &mut SecondaryDisplay<'static>, text: &str) { oled.clear().unwrap(); let text_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On); diff --git a/src/display/primary_lcd.rs b/src/display/primary_lcd.rs index 8941a98..9759d51 100644 --- a/src/display/primary_lcd.rs +++ b/src/display/primary_lcd.rs @@ -10,10 +10,10 @@ use ili9341::{DisplaySize240x320, Ili9341, Orientation}; use crate::display::shared_bus::{DisplaySpiBus, SharedOutput}; -type GenericDualPrimaryDisplay<'a, BUS, DC, RES> = +type GenericPrimaryDisplay<'a, BUS, DC, RES> = Ili9341, Delay>, DC>, RES>; -pub type DualPrimaryDisplay<'a> = - GenericDualPrimaryDisplay<'a, DisplaySpiBus, SharedOutput<'a>, Output<'a>>; +pub type PrimaryDisplay<'a> = + GenericPrimaryDisplay<'a, DisplaySpiBus, SharedOutput<'a>, Output<'a>>; pub struct PrimaryDisplayPinConfiguration<'a> { pub spi_bus: &'a AtomicCell, @@ -23,7 +23,7 @@ pub struct PrimaryDisplayPinConfiguration<'a> { } impl<'a> PrimaryDisplayPinConfiguration<'a> { - pub fn build(self) -> DualPrimaryDisplay<'a> { + pub fn build(self) -> PrimaryDisplay<'a> { let spi_dev = AtomicDevice::new(self.spi_bus, self.cs, Delay::new()).unwrap(); let iface = SPIInterface::new(spi_dev, self.dc); diff --git a/src/display/secondary_oled.rs b/src/display/secondary_oled.rs index 340faee..92d624d 100644 --- a/src/display/secondary_oled.rs +++ b/src/display/secondary_oled.rs @@ -8,10 +8,10 @@ use esp_hal::gpio::Output; use crate::display::shared_bus::{DisplaySpiBus, SharedOutput, SharedReset}; -pub type SecondaryOledDisplay<'a, BUS, RST, DC> = +pub type GenericSecondaryDisplay<'a, BUS, RST, DC> = Ch1115, Delay>, DC>, RST, Size128x64>; -pub type DualSecondaryDisplay<'a> = - SecondaryOledDisplay<'a, DisplaySpiBus, SharedReset<'a>, SharedOutput<'a>>; +pub type SecondaryDisplay<'a> = + GenericSecondaryDisplay<'a, DisplaySpiBus, SharedReset<'a>, SharedOutput<'a>>; pub struct SecondaryDisplayPinConfiguration<'a> { pub spi_bus: &'a AtomicCell, @@ -21,7 +21,7 @@ pub struct SecondaryDisplayPinConfiguration<'a> { } impl<'a> SecondaryDisplayPinConfiguration<'a> { - pub fn build(self) -> [DualSecondaryDisplay<'a>; 3] { + pub fn build(self) -> [SecondaryDisplay<'a>; 3] { let mut displays = self.cs_pins.map(|cs| { let spi_dev = AtomicDevice::new(self.spi_bus, cs, Delay::new()).unwrap(); let interface = SPIInterface::new(spi_dev, self.dc.clone()); @@ -36,7 +36,7 @@ impl<'a> SecondaryDisplayPinConfiguration<'a> { } fn init_single_reset<'a: 'b, 'b>( - displays: impl IntoIterator>, + displays: impl IntoIterator>, shared_reset: SharedReset<'a>, ) { let mut displays = displays.into_iter().peekable(); diff --git a/src/display/shared_bus.rs b/src/display/shared_bus.rs index f8a55b4..9ab78bf 100644 --- a/src/display/shared_bus.rs +++ b/src/display/shared_bus.rs @@ -9,9 +9,9 @@ use esp_hal::spi::Mode; use esp_hal::spi::master::{Config as SpiConfig, Spi}; use esp_hal::time::Rate; -use crate::display::primary_lcd::DualPrimaryDisplay; +use crate::display::primary_lcd::PrimaryDisplay; use crate::display::primary_lcd::PrimaryDisplayPinConfiguration; -use crate::display::secondary_oled::DualSecondaryDisplay; +use crate::display::secondary_oled::SecondaryDisplay; use crate::display::secondary_oled::SecondaryDisplayPinConfiguration; use core::cell::RefCell; @@ -135,12 +135,7 @@ where RES2: OutputPin + 'static, DC: OutputPin + 'static, { - pub fn build( - self, - ) -> ( - DualPrimaryDisplay<'static>, - [DualSecondaryDisplay<'static>; 3], - ) { + pub fn build(self) -> (PrimaryDisplay<'static>, [SecondaryDisplay<'static>; 3]) { let spi = Spi::new( self.spi_peripheral, SpiConfig::default() diff --git a/src/display/tertiary_lcd.rs b/src/display/tertiary_lcd.rs index d3f10dc..5fc11dc 100644 --- a/src/display/tertiary_lcd.rs +++ b/src/display/tertiary_lcd.rs @@ -4,10 +4,11 @@ use esp_hal::delay::Delay; use i2c_character_display::{CharacterDisplayPCF8574T, LcdDisplayType}; use log::error; -pub type TertiaryLcd = CharacterDisplayPCF8574T; -pub type TertiaryI2cLcd<'a> = TertiaryLcd>>; +type GenericTertiaryDisplay = CharacterDisplayPCF8574T; +pub type TertiaryDisplay<'a> = + GenericTertiaryDisplay>>; -pub fn init_tertiary_lcd(i2c: I2C) -> TertiaryLcd +pub fn init_tertiary_lcd(i2c: I2C) -> GenericTertiaryDisplay where I2C: I2c, { @@ -27,7 +28,7 @@ where } pub fn write_line( - lcd: &mut TertiaryLcd, + lcd: &mut GenericTertiaryDisplay, line: u8, text: &str, ) -> Result<(), i2c_character_display::CharacterDisplayError> @@ -49,7 +50,7 @@ where } pub fn write_wrapped( - lcd: &mut TertiaryLcd, + lcd: &mut GenericTertiaryDisplay, text: &str, ) -> Result<(), i2c_character_display::CharacterDisplayError> where diff --git a/src/navigation.rs b/src/navigation.rs index cbefd23..968af8e 100644 --- a/src/navigation.rs +++ b/src/navigation.rs @@ -1,4 +1,4 @@ pub mod inputs; pub mod navigation; -pub mod state; pub mod outputs; +pub mod state; diff --git a/src/navigation/outputs.rs b/src/navigation/outputs.rs index 5181a99..98257a2 100644 --- a/src/navigation/outputs.rs +++ b/src/navigation/outputs.rs @@ -4,16 +4,15 @@ use embedded_graphics::{ }; use crate::display::{ - primary_lcd::DualPrimaryDisplay, secondary_oled::DualSecondaryDisplay, - tertiary_lcd::TertiaryI2cLcd, + primary_lcd::PrimaryDisplay, secondary_oled::SecondaryDisplay, tertiary_lcd::TertiaryDisplay, }; pub struct Outputs { - pub primary_display: DualPrimaryDisplay<'static>, - pub secondary_display_1: DualSecondaryDisplay<'static>, - pub secondary_display_2: DualSecondaryDisplay<'static>, - pub secondary_display_3: DualSecondaryDisplay<'static>, - pub tertiary_display: TertiaryI2cLcd<'static>, + pub primary_display: PrimaryDisplay<'static>, + pub secondary_display_1: SecondaryDisplay<'static>, + pub secondary_display_2: SecondaryDisplay<'static>, + pub secondary_display_3: SecondaryDisplay<'static>, + pub tertiary_display: TertiaryDisplay<'static>, pub _led_a: (), } diff --git a/src/views/journal_view.rs b/src/views/journal_view.rs index 1226464..952bcc9 100644 --- a/src/views/journal_view.rs +++ b/src/views/journal_view.rs @@ -7,7 +7,12 @@ use embedded_graphics::prelude::RgbColor; use embedded_graphics_core::draw_target::DrawTarget; use crate::{ - card::model::Card, navigation::{navigation::{Action, Navigable, NewState}, outputs::Outputs}, views::view::View, + card::model::Card, + navigation::{ + navigation::{Action, Navigable, NewState}, + outputs::Outputs, + }, + views::view::View, }; #[derive(Debug, Clone)] diff --git a/src/views/main_menu.rs b/src/views/main_menu.rs index f7cd08d..4ff0e91 100644 --- a/src/views/main_menu.rs +++ b/src/views/main_menu.rs @@ -22,8 +22,7 @@ pub struct MainMenu { } impl Navigable for MainMenu { - fn display(&self, outputs: &mut Outputs) -> impl core::future::Future + Send - { + fn display(&self, outputs: &mut Outputs) -> impl core::future::Future + Send { let display = &mut outputs.primary_display; display.clear(Rgb565::BLACK).unwrap(); diff --git a/src/views/scan_menu.rs b/src/views/scan_menu.rs index 56c2c4f..3d92773 100644 --- a/src/views/scan_menu.rs +++ b/src/views/scan_menu.rs @@ -33,8 +33,7 @@ pub struct ScanMenu { } impl Navigable for ScanMenu { - fn display(&self, outputs: &mut Outputs) -> impl core::future::Future + Send - { + fn display(&self, outputs: &mut Outputs) -> impl core::future::Future + Send { let display = &mut outputs.primary_display; let style = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE); display.clear(Rgb565::BLACK).unwrap(); diff --git a/src/views/view.rs b/src/views/view.rs index 380c854..f3da7c9 100644 --- a/src/views/view.rs +++ b/src/views/view.rs @@ -1,4 +1,3 @@ - use crate::navigation::outputs::Outputs; use crate::views::card_view::CardView; use crate::views::{ @@ -18,8 +17,7 @@ pub enum View { } impl Navigable for View { - fn display(&self, outputs: &mut Outputs) -> impl core::future::Future + Send - { + fn display(&self, outputs: &mut Outputs) -> impl core::future::Future + Send { async move { match self { View::Main(main_menu) => main_menu.display(outputs).await, -- 2.39.5 From beb972e0d1f8ffb45acfc9f8215682c5d282fc6f Mon Sep 17 00:00:00 2001 From: ede1998 Date: Sun, 23 Aug 2026 16:41:43 +0200 Subject: [PATCH 7/8] refactor all hardware init in drivers --- src/bin/main.rs | 32 ++++++++--------- src/display.rs | 4 --- src/drivers.rs | 5 +++ src/drivers/i2c_bus.rs | 36 +++++++++++++++++++ src/{display => drivers}/primary_lcd.rs | 2 +- src/{display => drivers}/secondary_oled.rs | 2 +- .../shared_bus.rs => drivers/spi_bus.rs} | 8 ++--- src/{display => drivers}/tertiary_lcd.rs | 0 src/navigation/outputs.rs | 2 +- 9 files changed, 62 insertions(+), 29 deletions(-) create mode 100644 src/drivers/i2c_bus.rs rename src/{display => drivers}/primary_lcd.rs (95%) rename src/{display => drivers}/secondary_oled.rs (95%) rename src/{display/shared_bus.rs => drivers/spi_bus.rs} (95%) rename src/{display => drivers}/tertiary_lcd.rs (100%) diff --git a/src/bin/main.rs b/src/bin/main.rs index f5809a2..b0ae8c7 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -7,13 +7,13 @@ )] #![deny(clippy::large_stack_frames)] -use alloc::boxed::Box; use creaturedex::card::decoder::split_nfc_hex; use creaturedex::card::model::Card; -use creaturedex::display::secondary_oled::SecondaryDisplay; -use creaturedex::display::shared_bus; -use creaturedex::display::tertiary_lcd::{TertiaryDisplay, init_tertiary_lcd, write_wrapped}; +use creaturedex::drivers::i2c_bus::I2cBusPinConfiguration; use creaturedex::drivers::nfc_pn532::NfcPn532Driver; +use creaturedex::drivers::secondary_oled::SecondaryDisplay; +use creaturedex::drivers::spi_bus; +use creaturedex::drivers::tertiary_lcd::{TertiaryDisplay, init_tertiary_lcd, write_wrapped}; use creaturedex::navigation::inputs::Inputs; use creaturedex::navigation::navigation::{self, CARD_DATA}; use creaturedex::navigation::outputs::Outputs; @@ -27,11 +27,10 @@ use embedded_graphics::{ text::Text, }; use embedded_hal_bus::i2c::AtomicDevice; -use embedded_hal_bus::util::AtomicCell; use esp_hal::Blocking; use esp_hal::clock::CpuClock; use esp_hal::gpio::{Input, InputConfig, Pull}; -use esp_hal::i2c::master::{Config as I2cConfig, I2c}; +use esp_hal::i2c::master::I2c; use esp_hal::timer::timg::TimerGroup; use log::error; @@ -67,7 +66,7 @@ async fn main(spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_rtos::start(timg0.timer0, sw_interrupt.software_interrupt0); - let (primary, mut secondaries) = shared_bus::DisplayPinConfiguration { + let (primary, mut secondaries) = spi_bus::DisplayPinConfiguration { spi_peripheral: peripherals.SPI2, sck: peripherals.GPIO36, mosi: peripherals.GPIO35, @@ -82,18 +81,15 @@ async fn main(spawner: Spawner) { } .build(); - let config = I2cConfig::default().with_frequency(esp_hal::time::Rate::from_khz(400)); - 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"); - } + // Initialize the shared I2C bus using the refactored i2c_bus module. + let shared_i2c = { + let config = I2cBusPinConfiguration { + i2c_peripheral: peripherals.I2C0, + scl: peripherals.GPIO18, + sda: peripherals.GPIO17, + }; + config.build() }; - let shared_i2c = Box::leak(Box::new(AtomicCell::new(i2c))); - log::info!("Shared I2C bus ready on GPIO17 (SDA) and GPIO18 (SCL)"); let mut lcd: TertiaryDisplay = init_tertiary_lcd(AtomicDevice::new(shared_i2c)); if let Err(e) = write_wrapped(&mut lcd, "Testing more than 16 chars what happens now?") { diff --git a/src/display.rs b/src/display.rs index 614d495..2013c3f 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,5 +1 @@ -pub mod primary_lcd; -pub mod secondary_oled; -pub mod shared_bus; pub mod sprite; -pub mod tertiary_lcd; diff --git a/src/drivers.rs b/src/drivers.rs index 0becd6c..a4704c0 100644 --- a/src/drivers.rs +++ b/src/drivers.rs @@ -1 +1,6 @@ +pub mod i2c_bus; pub mod nfc_pn532; +pub mod primary_lcd; +pub mod secondary_oled; +pub mod spi_bus; +pub mod tertiary_lcd; diff --git a/src/drivers/i2c_bus.rs b/src/drivers/i2c_bus.rs new file mode 100644 index 0000000..fecf1d2 --- /dev/null +++ b/src/drivers/i2c_bus.rs @@ -0,0 +1,36 @@ +use alloc::boxed::Box; +use embedded_hal_bus::util::AtomicCell; +use esp_hal::gpio::interconnect::PeripheralInput; +use esp_hal::i2c::master::Config; +use esp_hal::{gpio::interconnect::PeripheralOutput, i2c::master::I2c}; + +type InnerI2cBus = esp_hal::i2c::master::I2c<'static, esp_hal::Blocking>; +pub type I2cBus = &'static AtomicCell; + +pub struct I2cBusPinConfiguration { + pub i2c_peripheral: I2C, + pub scl: SCL, + pub sda: SDA, +} + +impl I2cBusPinConfiguration +where + I2C: esp_hal::i2c::master::Instance + 'static, + SCL: PeripheralOutput<'static> + PeripheralInput<'static>, + SDA: PeripheralOutput<'static> + PeripheralInput<'static>, +{ + pub fn build(self) -> I2cBus { + let config = Config::default().with_frequency(esp_hal::time::Rate::from_khz(400)); + let i2c = match I2c::new(self.i2c_peripheral, config) { + Ok(bus) => bus.with_sda(self.sda).with_scl(self.scl), + 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)"); + + shared_i2c + } +} diff --git a/src/display/primary_lcd.rs b/src/drivers/primary_lcd.rs similarity index 95% rename from src/display/primary_lcd.rs rename to src/drivers/primary_lcd.rs index 9759d51..34a4a8c 100644 --- a/src/display/primary_lcd.rs +++ b/src/drivers/primary_lcd.rs @@ -8,7 +8,7 @@ use esp_hal::delay::Delay; use esp_hal::gpio::Output; use ili9341::{DisplaySize240x320, Ili9341, Orientation}; -use crate::display::shared_bus::{DisplaySpiBus, SharedOutput}; +use crate::drivers::spi_bus::{DisplaySpiBus, SharedOutput}; type GenericPrimaryDisplay<'a, BUS, DC, RES> = Ili9341, Delay>, DC>, RES>; diff --git a/src/display/secondary_oled.rs b/src/drivers/secondary_oled.rs similarity index 95% rename from src/display/secondary_oled.rs rename to src/drivers/secondary_oled.rs index 92d624d..86b6101 100644 --- a/src/display/secondary_oled.rs +++ b/src/drivers/secondary_oled.rs @@ -6,7 +6,7 @@ use embedded_hal_bus::util::AtomicCell; use esp_hal::delay::Delay; use esp_hal::gpio::Output; -use crate::display::shared_bus::{DisplaySpiBus, SharedOutput, SharedReset}; +use crate::drivers::spi_bus::{DisplaySpiBus, SharedOutput, SharedReset}; pub type GenericSecondaryDisplay<'a, BUS, RST, DC> = Ch1115, Delay>, DC>, RST, Size128x64>; diff --git a/src/display/shared_bus.rs b/src/drivers/spi_bus.rs similarity index 95% rename from src/display/shared_bus.rs rename to src/drivers/spi_bus.rs index 9ab78bf..036b1a5 100644 --- a/src/display/shared_bus.rs +++ b/src/drivers/spi_bus.rs @@ -9,10 +9,10 @@ use esp_hal::spi::Mode; use esp_hal::spi::master::{Config as SpiConfig, Spi}; use esp_hal::time::Rate; -use crate::display::primary_lcd::PrimaryDisplay; -use crate::display::primary_lcd::PrimaryDisplayPinConfiguration; -use crate::display::secondary_oled::SecondaryDisplay; -use crate::display::secondary_oled::SecondaryDisplayPinConfiguration; +use crate::drivers::primary_lcd::PrimaryDisplay; +use crate::drivers::primary_lcd::PrimaryDisplayPinConfiguration; +use crate::drivers::secondary_oled::SecondaryDisplay; +use crate::drivers::secondary_oled::SecondaryDisplayPinConfiguration; use core::cell::RefCell; use embassy_sync::blocking_mutex::Mutex as BlockingMutex; diff --git a/src/display/tertiary_lcd.rs b/src/drivers/tertiary_lcd.rs similarity index 100% rename from src/display/tertiary_lcd.rs rename to src/drivers/tertiary_lcd.rs diff --git a/src/navigation/outputs.rs b/src/navigation/outputs.rs index 98257a2..03aba5a 100644 --- a/src/navigation/outputs.rs +++ b/src/navigation/outputs.rs @@ -3,7 +3,7 @@ use embedded_graphics::{ pixelcolor::{Rgb565, RgbColor as _}, }; -use crate::display::{ +use crate::drivers::{ primary_lcd::PrimaryDisplay, secondary_oled::SecondaryDisplay, tertiary_lcd::TertiaryDisplay, }; -- 2.39.5 From ce661a4bdee86f9159a138b4cc8fe8d175f36297 Mon Sep 17 00:00:00 2001 From: ede1998 Date: Sun, 23 Aug 2026 19:28:08 +0200 Subject: [PATCH 8/8] refactor nfc reader code a bit --- src/drivers/nfc_pn532.rs | 83 ++++++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 20 deletions(-) diff --git a/src/drivers/nfc_pn532.rs b/src/drivers/nfc_pn532.rs index 1cb6554..9297bd1 100644 --- a/src/drivers/nfc_pn532.rs +++ b/src/drivers/nfc_pn532.rs @@ -1,4 +1,5 @@ use alloc::vec::Vec; +use core::error::Error; use embedded_hal::i2c::I2c; use log::{error, info}; use pn532::i2c::I2CInterface; @@ -8,6 +9,7 @@ use pn532::{Pn532, Request}; pub type Pn532Device = Pn532, (), 34>; pub const PAGES_PER_READ: usize = 4; +pub const BYTES_PER_READ: usize = PAGES_PER_READ * 4; /// PN532 NFC reader driver. pub struct NfcPn532Driver @@ -17,6 +19,35 @@ where pn532: Pn532Device, } +#[derive(Debug, Clone)] +struct PageParseError { + iteration: u8, + bytes: [u8; N], + status_code: Option, +} + +impl Error for PageParseError {} + +impl core::fmt::Display for PageParseError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let PageParseError { + iteration, + bytes, + status_code, + } = self; + match status_code { + Some(code) => write!( + f, + "NFC pages parse error in iteration {iteration} with error code 0x{code:02x}: {bytes:02x?}" + ), + None => write!( + f, + "No data available for NFC page in iteration {iteration}: {bytes:02x?}" + ), + } + } +} + impl NfcPn532Driver where I2C: I2c, @@ -57,16 +88,21 @@ where } } - pub fn parse_nfc_page(iteration: u8, bytes: &[u8]) -> Result<[u8; PAGES_PER_READ * 4], ()> { - let success = bytes.first().is_some_and(|x| *x == 0); - if success { - let slice_len = bytes.len().min(PAGES_PER_READ * 4 + 1); - let mut arr = [0; PAGES_PER_READ * 4]; - arr[..slice_len - 1].copy_from_slice(&bytes[1..slice_len]); - Ok(arr) - } else { - info!("err {iteration}: {:02x?}", bytes); - Err(()) + fn parse_nfc_page( + iteration: u8, + bytes: &[u8], + ) -> Result<[u8; BYTES_PER_READ], PageParseError> { + let slice_len = bytes.len().min(BYTES_PER_READ + 1); + let mut arr = [0; BYTES_PER_READ]; + arr[..slice_len - 1].copy_from_slice(&bytes[1..slice_len]); + + match bytes.first() { + Some(0x00) => Ok(arr), + error => Err(PageParseError { + iteration, + bytes: arr, + status_code: error.copied(), + }), } } @@ -79,18 +115,25 @@ where let mut data: Vec = Vec::with_capacity(858); for i in (0x0B..=230).step_by(PAGES_PER_READ) { - let mut parse_result = Err(()); - while parse_result.is_err() { - let Ok(result) = self + let bytes = loop { + let read = self .pn532 - .process_async(&Request::ntag_read(i), PAGES_PER_READ * 4 + 1) - .await - else { - continue; + .process_async(&Request::ntag_read(i), BYTES_PER_READ + 1) + .await; + + let bytes = match read { + Ok(bytes) => bytes, + Err(e) => { + error!("Reading page {i} failed: {e:?}"); + continue; + } }; - parse_result = Self::parse_nfc_page(i, result); - } - let bytes = parse_result.unwrap(); + + match Self::parse_nfc_page(i, bytes) { + Ok(data) => break data, + Err(e) => error!("Parsing page failed: {e}"), + } + }; let bytes = if i == 0x0B { &bytes[1..] } else { &bytes }; data.extend(bytes); } -- 2.39.5