Skip to content

Commit

Permalink
Merge branch 'random-greetings'
Browse files Browse the repository at this point in the history
  • Loading branch information
zargony committed Nov 9, 2024
2 parents c40780f + 9382d1c commit feb55e4
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 138 deletions.
2 changes: 2 additions & 0 deletions firmware/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

- Show random greetings to user

## 0.1.0 - 2024-10-30

- Showcased on general meeting
Expand Down
1 change: 1 addition & 0 deletions firmware/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ async fn main(spawner: Spawner) {

// Create UI
let mut ui = ui::Ui::new(
rng,
&mut display,
&mut keypad,
&mut nfc,
Expand Down
247 changes: 113 additions & 134 deletions firmware/src/screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ use embedded_graphics::draw_target::DrawTarget;
use embedded_graphics::image::{Image, ImageRaw};
use embedded_graphics::pixelcolor::BinaryColor;
use embedded_graphics::prelude::*;
use rand_core::RngCore;
use u8g2_fonts::types::{FontColor, HorizontalAlignment, VerticalPosition};
use u8g2_fonts::{fonts, FontRenderer};
use u8g2_fonts::{fonts, Content, FontRenderer};

/// Touch 'n Drink bi-color logo
#[rustfmt::skip]
Expand Down Expand Up @@ -39,6 +40,18 @@ const MEDIUM_FONT: FontRenderer = FontRenderer::new::<fonts::u8g2_font_6x10_tf>(
const SMALL_FONT: FontRenderer = FontRenderer::new::<fonts::u8g2_font_5x7_tf>();
const FOOTER_FONT: FontRenderer = FontRenderer::new::<fonts::u8g2_font_5x7_tf>();

/// Screen width
const WIDTH: i32 = 128;

/// Horizontal position for centering
const HCENTER: i32 = WIDTH / 2;

/// Screen height
const HEIGHT: i32 = 64;

/// Number of characters that fit in a line
const MEDIUM_CHARS_PER_LINE: i32 = WIDTH / 6;

/// Screen display error
pub type Error<E> = u8g2_fonts::Error<E>;

Expand All @@ -50,6 +63,53 @@ pub trait Screen {
) -> Result<(), Error<D::Error>>;
}

/// Draw centered text on given line
fn centered<D: DrawTarget<Color = BinaryColor>>(
font: &FontRenderer,
y: i32,
content: impl Content,
target: &mut D,
) -> Result<(), Error<D::Error>> {
font.render_aligned(
content,
Point::new(HCENTER, y),
VerticalPosition::Baseline,
HorizontalAlignment::Center,
FontColor::Transparent(BinaryColor::On),
target,
)?;
Ok(())
}

/// Draw common footer (bottom 7 lines, 57..64)
fn footer<D: DrawTarget<Color = BinaryColor>>(
left: &str,
right: &str,
target: &mut D,
) -> Result<(), Error<D::Error>> {
if !left.is_empty() {
FOOTER_FONT.render_aligned(
left,
Point::new(0, HEIGHT - 1),
VerticalPosition::Baseline,
HorizontalAlignment::Left,
FontColor::Transparent(BinaryColor::On),
target,
)?;
}
if !right.is_empty() {
FOOTER_FONT.render_aligned(
right,
Point::new(WIDTH - 1, HEIGHT - 1),
VerticalPosition::Baseline,
HorizontalAlignment::Right,
FontColor::Transparent(BinaryColor::On),
target,
)?;
}
Ok(())
}

/// Splash screen
pub struct Splash;

Expand All @@ -61,18 +121,11 @@ impl Screen for Splash {
Image::new(&LOGO, Point::new(0, 13))
.draw(target)
.map_err(Error::DisplayError)?;
SPLASH_VERSION_FONT.render_aligned(
VERSION_STR,
Point::new(63, 30 + 12),
VerticalPosition::Baseline,
HorizontalAlignment::Center,
FontColor::Transparent(BinaryColor::On),
target,
)?;
centered(&SPLASH_VERSION_FONT, 13 + 29, VERSION_STR, target)?;
#[cfg(not(debug_assertions))]
Footer::new("", GIT_SHA_STR).draw(target)?;
footer("", GIT_SHA_STR, target)?;
#[cfg(debug_assertions)]
Footer::new("(DEBUG)", GIT_SHA_STR).draw(target)?;
footer("(DEBUG)", GIT_SHA_STR, target)?;
Ok(())
}
}
Expand All @@ -93,23 +146,14 @@ impl<M: fmt::Display> Screen for Failure<M> {
&self,
target: &mut D,
) -> Result<(), Error<D::Error>> {
TITLE_FONT.render_aligned(
"FEHLER!",
Point::new(63, 26),
VerticalPosition::Baseline,
HorizontalAlignment::Center,
FontColor::Transparent(BinaryColor::On),
target,
)?;
SMALL_FONT.render_aligned(
centered(&TITLE_FONT, 26, "FEHLER!", target)?;
centered(
&SMALL_FONT,
26 + 12,
format_args!("{}", self.message),
Point::new(63, 26 + 12),
VerticalPosition::Baseline,
HorizontalAlignment::Center,
FontColor::Transparent(BinaryColor::On),
target,
)?;
Footer::new("* Abbruch", "").draw(target)?;
footer("* Abbruch", "", target)?;
Ok(())
}
}
Expand All @@ -125,27 +169,18 @@ impl Screen for PleaseWait {
&self,
target: &mut D,
) -> Result<(), Error<D::Error>> {
TITLE_FONT.render_aligned(
"Stand By...",
Point::new(63, 26),
VerticalPosition::Baseline,
HorizontalAlignment::Center,
FontColor::Transparent(BinaryColor::On),
target,
)?;
MEDIUM_FONT.render_aligned(
centered(&TITLE_FONT, 26, "Stand By...", target)?;
centered(
&MEDIUM_FONT,
26 + 12,
match self {
Self::WifiConnecting => "WLAN Verbindung\nwird aufgebaut",
Self::ApiQuerying => "Vereinsflieger\nAbfrage",
},
Point::new(63, 26 + 12),
VerticalPosition::Baseline,
HorizontalAlignment::Center,
FontColor::Transparent(BinaryColor::On),
target,
)?;
if let Self::WifiConnecting = self {
Footer::new("* Abbruch", "").draw(target)?;
footer("* Abbruch", "", target)?;
}
Ok(())
}
Expand All @@ -159,51 +194,47 @@ impl Screen for ScanId {
&self,
target: &mut D,
) -> Result<(), Error<D::Error>> {
TITLE_FONT.render_aligned(
"Mitgliedsausweis\nscannen",
Point::new(63, 26),
VerticalPosition::Baseline,
HorizontalAlignment::Center,
FontColor::Transparent(BinaryColor::On),
target,
)?;
centered(&TITLE_FONT, 26, "Mitgliedsausweis\nscannen", target)?;
Ok(())
}
}

/// Prompt to ask for number of drinks
pub struct NumberOfDrinks<N> {
name: N,
pub struct NumberOfDrinks<'a> {
greeting: u32,
name: &'a str,
}

impl<N: fmt::Display> NumberOfDrinks<N> {
pub fn new(name: N) -> Self {
Self { name }
impl<'a> NumberOfDrinks<'a> {
pub fn new<RNG: RngCore>(rng: &mut RNG, name: &'a str) -> Self {
Self {
greeting: rng.next_u32(),
name,
}
}
}

impl<N: fmt::Display> Screen for NumberOfDrinks<N> {
impl<'a> Screen for NumberOfDrinks<'a> {
fn draw<D: DrawTarget<Color = BinaryColor>>(
&self,
target: &mut D,
) -> Result<(), Error<D::Error>> {
MEDIUM_FONT.render_aligned(
format_args!("Hallo {}", self.name),
Point::new(63, 8),
VerticalPosition::Baseline,
HorizontalAlignment::Center,
FontColor::Transparent(BinaryColor::On),
target,
)?;
TITLE_FONT.render_aligned(
"Anzahl Getränke\nwählen",
Point::new(63, 26),
VerticalPosition::Baseline,
HorizontalAlignment::Center,
FontColor::Transparent(BinaryColor::On),
target,
)?;
Footer::new("* Abbruch", "1-9 Weiter").draw(target)?;
static GREETINGS: [&str; 9] = [
"Hi", "Hallo", "Hey", "Tach", "Servus", "Moin", "Hej", "Olá", "Ciao",
];

// Trim name if it's too long to display
let greeting = GREETINGS[self.greeting as usize % GREETINGS.len()];
let greeting_len = greeting.len() + 1;
let name = if self.name.len() + greeting_len > MEDIUM_CHARS_PER_LINE as usize {
&self.name[..(MEDIUM_CHARS_PER_LINE as usize - greeting_len)]
} else {
self.name
};

centered(&MEDIUM_FONT, 8, format_args!("{greeting} {name}"), target)?;
centered(&TITLE_FONT, 8 + 22, "Anzahl Getränke\nwählen", target)?;
footer("* Abbruch", "1-9 Weiter", target)?;
Ok(())
}
}
Expand All @@ -228,7 +259,9 @@ impl Screen for Checkout {
&self,
target: &mut D,
) -> Result<(), Error<D::Error>> {
MEDIUM_FONT.render_aligned(
centered(
&MEDIUM_FONT,
26,
format_args!(
"{} {}",
self.num_drinks,
Expand All @@ -238,21 +271,15 @@ impl Screen for Checkout {
"Getränke"
}
),
Point::new(63, 26),
VerticalPosition::Baseline,
HorizontalAlignment::Center,
FontColor::Transparent(BinaryColor::On),
target,
)?;
TITLE_FONT.render_aligned(
centered(
&TITLE_FONT,
26 + 16,
format_args!("{:.02} EUR", self.total_price),
Point::new(63, 26 + 16),
VerticalPosition::Baseline,
HorizontalAlignment::Center,
FontColor::Transparent(BinaryColor::On),
target,
)?;
Footer::new("* Abbruch", "# BEZAHLEN").draw(target)?;
footer("* Abbruch", "# BEZAHLEN", target)?;
Ok(())
}
}
Expand All @@ -273,62 +300,14 @@ impl Screen for Success {
&self,
target: &mut D,
) -> Result<(), Error<D::Error>> {
TITLE_FONT.render_aligned(
"Affirm!",
Point::new(63, 26),
VerticalPosition::Baseline,
HorizontalAlignment::Center,
FontColor::Transparent(BinaryColor::On),
target,
)?;
SMALL_FONT.render_aligned(
centered(&TITLE_FONT, 26, "Affirm!", target)?;
centered(
&SMALL_FONT,
26 + 12,
format_args!("{} Getränke genehmigt", self.num_drinks),
Point::new(63, 26 + 12),
VerticalPosition::Baseline,
HorizontalAlignment::Center,
FontColor::Transparent(BinaryColor::On),
target,
)?;
Footer::new("", "# Ok").draw(target)?;
Ok(())
}
}

/// Common footer (bottom 7 lines 57..64)
struct Footer<'a> {
left: &'a str,
right: &'a str,
}

impl<'a> Footer<'a> {
fn new(left: &'a str, right: &'a str) -> Self {
Self { left, right }
}

fn draw<D: DrawTarget<Color = BinaryColor>>(
&self,
target: &mut D,
) -> Result<(), Error<D::Error>> {
if !self.left.is_empty() {
FOOTER_FONT.render_aligned(
self.left,
Point::new(0, 63),
VerticalPosition::Baseline,
HorizontalAlignment::Left,
FontColor::Transparent(BinaryColor::On),
target,
)?;
}
if !self.right.is_empty() {
FOOTER_FONT.render_aligned(
self.right,
Point::new(127, 63),
VerticalPosition::Baseline,
HorizontalAlignment::Right,
FontColor::Transparent(BinaryColor::On),
target,
)?;
}
footer("", "# Ok", target)?;
Ok(())
}
}
Loading

0 comments on commit feb55e4

Please sign in to comment.