rework card view to display information on all displays as intended

This commit is contained in:
2026-09-27 13:41:41 +02:00
parent 00e23d4090
commit afcfe1693b
6 changed files with 193 additions and 78 deletions
Generated
+10
View File
@@ -206,6 +206,7 @@ dependencies = [
"log",
"num_enum",
"pn532",
"profont",
"static_assertions",
]
@@ -1595,6 +1596,15 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "profont"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "016681370a9dd6e7ddb4c1a959922fd59dc45e5ebaa5ff5b13090267898ced34"
dependencies = [
"embedded-graphics",
]
[[package]]
name = "quote"
version = "1.0.47"
+1
View File
@@ -58,6 +58,7 @@ esp-backtrace = { version = "0.19.0", features = [
"println",
] }
esp-radio = { version = "0.18.0", features = ["esp32s3", "wifi"] }
profont = "0.7.0"
[build-dependencies]
log = "0.4.27"
+27
View File
@@ -1,5 +1,9 @@
use alloc::format;
use ch1115::{Ch1115, Size128x64};
use core::convert::Infallible;
use core::error::Error;
use core::fmt::Debug;
use core::fmt::Display;
use display_interface_spi::SPIInterface;
use embedded_hal_bus::spi::AtomicDevice;
use embedded_hal_bus::util::AtomicCell;
@@ -8,6 +12,29 @@ use esp_hal::gpio::Output;
use crate::drivers::spi_bus::{DisplaySpiBus, SharedOutput, SharedReset};
#[derive(Debug)]
pub struct SecondaryDisplayError(ch1115::Error<Infallible>);
impl Display for SecondaryDisplayError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.0.fmt(f)
}
}
impl Error for SecondaryDisplayError {}
impl From<ch1115::Error<Infallible>> for SecondaryDisplayError {
fn from(value: ch1115::Error<Infallible>) -> Self {
Self(value)
}
}
impl From<Infallible> for SecondaryDisplayError {
fn from(value: Infallible) -> Self {
Self(ch1115::Error::Pin(value))
}
}
pub type GenericSecondaryDisplay<'a, BUS, RST, DC> =
Ch1115<SPIInterface<AtomicDevice<'a, BUS, Output<'a>, Delay>, DC>, RST, Size128x64>;
pub type SecondaryDisplay<'a> =
+3
View File
@@ -1,3 +1,4 @@
use core::error::Error;
use embedded_hal_bus::i2c::AtomicDevice;
use esp_hal::delay::Delay;
use i2c_character_display::{CharacterDisplayPCF8574T, LcdDisplayType};
@@ -48,6 +49,8 @@ pub enum TertiaryDisplayWriteError<'a> {
Format(LinesError),
}
impl<'a> Error for TertiaryDisplayWriteError<'a> {}
impl<'a> From<TertiaryDisplayError<'a>> for TertiaryDisplayWriteError<'a> {
fn from(value: TertiaryDisplayError<'a>) -> Self {
Self::Display(value)
+19 -6
View File
@@ -1,10 +1,14 @@
use alloc::boxed::Box;
use core::error;
use embedded_graphics::{
draw_target::DrawTarget as _,
pixelcolor::{Rgb565, RgbColor as _},
};
use crate::drivers::{
primary_lcd::PrimaryDisplay, secondary_oled::SecondaryDisplay, tertiary_lcd::TertiaryDisplay,
primary_lcd::{PrimaryDisplay, PrimaryDisplayError},
secondary_oled::{SecondaryDisplay, SecondaryDisplayError},
tertiary_lcd::TertiaryDisplay,
};
pub struct Outputs {
@@ -17,11 +21,20 @@ pub struct Outputs {
}
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();
pub fn clear_screens(&mut self) -> Result<(), Box<dyn error::Error>> {
self.primary_display
.clear(Rgb565::BLACK)
.map_err(PrimaryDisplayError::from)?;
self.secondary_display_1
.clear()
.map_err(SecondaryDisplayError::from)?;
self.secondary_display_2
.clear()
.map_err(SecondaryDisplayError::from)?;
self.secondary_display_3
.clear()
.map_err(SecondaryDisplayError::from)?;
self.tertiary_display.clear().unwrap();
Ok(())
}
}
+133 -72
View File
@@ -1,5 +1,7 @@
use crate::display::sprite::render_sprite_onto_ili9341;
use crate::drivers::primary_lcd::PrimaryDisplayError;
use crate::drivers::primary_lcd::{PrimaryDisplay, PrimaryDisplayError};
use crate::drivers::secondary_oled::{SecondaryDisplay, SecondaryDisplayError};
use crate::drivers::tertiary_lcd::{TertiaryDisplay, TertiaryDisplayWriteError};
use crate::navigation::navigation::NewState;
use crate::navigation::outputs::Outputs;
use crate::peripherals::Peripherals;
@@ -8,14 +10,23 @@ use alloc::format;
use core::error;
use embedded_graphics::mono_font::MonoTextStyle;
use embedded_graphics::mono_font::ascii::FONT_6X10;
use embedded_graphics::pixelcolor::BinaryColor;
use embedded_graphics::pixelcolor::Rgb565;
use embedded_graphics::prelude::Point;
use embedded_graphics::prelude::RgbColor;
use embedded_graphics::text::Text;
use embedded_graphics::{Drawable, geometry::Size, primitives::Rectangle};
use embedded_graphics_core::draw_target::DrawTarget;
use profont::PROFONT_7_POINT;
use profont::PROFONT_18_POINT;
use core::future;
use embedded_graphics::geometry::Dimensions;
use embedded_text::{
TextBox,
alignment::{HorizontalAlignment, VerticalAlignment},
style::{HeightMode, TextBoxStyleBuilder},
};
use crate::{
card::model::{Card, Palette},
@@ -27,7 +38,118 @@ use crate::{
pub struct CardView {
pub card: Card,
}
impl CardView {
fn display_card(
&self,
primary_display: &mut PrimaryDisplay,
) -> Result<(), Box<dyn error::Error>> {
let background = Rectangle::new(Point::new(0, 0), Size::new(240, 320));
let bg_color = self.card.cardtype.clone().into();
let _ = primary_display.fill_solid(&background, bg_color);
// name as header
let text_style_name = MonoTextStyle::new(&PROFONT_18_POINT, Rgb565::WHITE);
let textbox_style = TextBoxStyleBuilder::new()
.height_mode(HeightMode::FitToText)
.alignment(HorizontalAlignment::Center)
.build();
let display_area = primary_display.bounding_box();
let bounds = Rectangle::new(Point::new(0, 10), Size::new(display_area.size.width, 50));
TextBox::with_textbox_style(&self.card.name, bounds, text_style_name, textbox_style)
.draw(primary_display)
.map_err(PrimaryDisplayError::from)?;
// event lower left corner
let text_style_event = MonoTextStyle::new(&PROFONT_7_POINT, Rgb565::WHITE);
let textbox_style = TextBoxStyleBuilder::new()
.height_mode(HeightMode::FitToText)
.alignment(HorizontalAlignment::Right)
.build();
let display_area = primary_display.bounding_box();
let bounds = Rectangle::new(Point::new(0, 310), Size::new(display_area.size.width, 50));
TextBox::with_textbox_style(
&format!("{}", self.card.event),
bounds,
text_style_event,
textbox_style,
)
.draw(primary_display)
.map_err(PrimaryDisplayError::from)?;
// card sprite
let palette: Palette = self.card.cardtype.clone().into();
render_sprite_onto_ili9341(
primary_display,
self.card.sprite.data.as_slice(),
&palette,
Point::new(0, 50),
240,
);
Ok(())
}
fn display_trait(
&self,
secondary_display: &mut SecondaryDisplay,
trait_str: &str,
) -> Result<(), Box<dyn error::Error>> {
let text_style_secondary = MonoTextStyle::new(&PROFONT_18_POINT, BinaryColor::On);
let textbox_style = TextBoxStyleBuilder::new()
.height_mode(HeightMode::FitToText)
.vertical_alignment(VerticalAlignment::Middle)
.alignment(HorizontalAlignment::Justified)
.build();
let display_area = secondary_display.bounding_box();
let bounds = Rectangle::new(Point::new(0, 0), display_area.size);
TextBox::with_textbox_style(
&format!("{}", trait_str),
bounds,
text_style_secondary,
textbox_style,
)
.draw(secondary_display)
.map_err(SecondaryDisplayError::from)?;
secondary_display
.flush()
.map_err(SecondaryDisplayError::from)?;
Ok(())
}
fn display_traits(
&self,
secondary_display_1: &mut SecondaryDisplay,
secondary_display_2: &mut SecondaryDisplay,
secondary_display_3: &mut SecondaryDisplay,
) -> Result<(), Box<dyn error::Error>> {
self.display_trait(secondary_display_1, self.card.trait1.as_str())?;
self.display_trait(secondary_display_2, self.card.trait2.as_str())?;
self.display_trait(secondary_display_3, self.card.trait3.as_str())?;
Ok(())
}
fn display_secret<'a>(
&self,
tertiary_display: &mut TertiaryDisplay<'a>,
) -> Result<(), Box<dyn error::Error + 'a>> {
tertiary_display
.write(&alloc::format!("{}", self.card.secret))
.map_err(TertiaryDisplayWriteError::from)?;
Ok(())
}
}
impl Navigable for CardView {
fn display(
&self,
@@ -35,82 +157,21 @@ impl Navigable for CardView {
_: &Peripherals,
) -> impl core::future::Future<Output = Result<(), Box<dyn error::Error>>> + Send {
async move {
let display = &mut outputs.primary_display;
display
.clear(Rgb565::BLACK)
.map_err(PrimaryDisplayError::from)?;
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 style = MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE);
Text::new(
&format!("Name: {}", self.card.name),
Point::new(20, 240),
style,
)
.draw(display)
.map_err(PrimaryDisplayError::from)?;
Text::new(
&format!("Type: {}", self.card.cardtype),
Point::new(20, 250),
style,
)
.draw(display)
.map_err(PrimaryDisplayError::from)?;
Text::new(
&format!("Event: {}", self.card.event),
Point::new(20, 260),
style,
)
.draw(display)
.map_err(PrimaryDisplayError::from)?;
Text::new(
&format!("UUID: {}", self.card.uuid),
Point::new(20, 270),
style,
)
.draw(display)
.map_err(PrimaryDisplayError::from)?;
Text::new(
&format!("Trait1: {}", self.card.trait1),
Point::new(20, 280),
style,
)
.draw(display)
.map_err(PrimaryDisplayError::from)?;
Text::new(
&format!("Trait2: {}", self.card.trait2),
Point::new(20, 290),
style,
)
.draw(display)
.map_err(PrimaryDisplayError::from)?;
Text::new(
&format!("Trait3: {}", self.card.trait3),
Point::new(20, 300),
style,
)
.draw(display)
.map_err(PrimaryDisplayError::from)?;
Text::new(
outputs.clear_screens()?;
/*Text::new(
&format!("Secret: {}", self.card.secret),
Point::new(20, 310),
style,
)
.draw(display)
.map_err(PrimaryDisplayError::from)?;
let palette: Palette = self.card.cardtype.clone().into();
render_sprite_onto_ili9341(
display,
self.card.sprite.data.as_slice(),
&palette,
Point::new(13, 10),
215,
);
.map_err(PrimaryDisplayError::from)?;*/
self.display_traits(
&mut outputs.secondary_display_1,
&mut outputs.secondary_display_2,
&mut outputs.secondary_display_3,
)?;
self.display_secret(&mut outputs.tertiary_display)?;
self.display_card(&mut outputs.primary_display)?;
Ok(())
}
}