Skip to content

Commit

Permalink
Refactor display screens
Browse files Browse the repository at this point in the history
  • Loading branch information
zargony committed Aug 12, 2024
1 parent 259c641 commit 83cba4f
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 77 deletions.
81 changes: 8 additions & 73 deletions firmware/src/display.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{GIT_SHA_STR, VERSION_STR};
use crate::screen::{self, Screen};
use core::any;
use embedded_graphics::pixelcolor::BinaryColor;
use embedded_graphics::prelude::*;
use embedded_hal::i2c::I2c;
Expand All @@ -8,41 +9,13 @@ use ssd1306::prelude::I2CInterface;
use ssd1306::rotation::DisplayRotation;
use ssd1306::size::DisplaySize128x64;
use ssd1306::Ssd1306;
use u8g2_fonts::types::{FontColor, HorizontalAlignment, VerticalPosition};
use u8g2_fonts::{fonts, FontRenderer};

// The `ssd1306` crate unfortunately doesn't support async yet (though `display-interface`,
// `display-interface-i2c` and `embedded-hal-bus` do), so we can't use async here yet.
// See also https://github.com/rust-embedded-community/ssd1306/pull/189

const SPLASH_TITLE_FONT: FontRenderer = FontRenderer::new::<fonts::u8g2_font_logisoso16_tr>();
const SPLASH_VERSION_FONT: FontRenderer = FontRenderer::new::<fonts::u8g2_font_profont12_tr>();
const SPLASH_FOOTER_FONT: FontRenderer = FontRenderer::new::<fonts::u8g2_font_profont11_tr>();
const BIG_CHAR_FONT: FontRenderer = FontRenderer::new::<fonts::u8g2_font_luBS24_tr>();

/// Display error
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
/// Display driver error
#[allow(dead_code)]
DriverError(display_interface::DisplayError),
/// Font render error
#[allow(dead_code)]
FontRenderError(u8g2_fonts::Error<display_interface::DisplayError>),
}

impl From<display_interface::DisplayError> for Error {
fn from(err: display_interface::DisplayError) -> Self {
Self::DriverError(err)
}
}

impl From<u8g2_fonts::Error<display_interface::DisplayError>> for Error {
fn from(err: u8g2_fonts::Error<display_interface::DisplayError>) -> Self {
Self::FontRenderError(err)
}
}
pub type Error = screen::Error<display_interface::DisplayError>;

/// Convenient hardware-agnostic display driver
pub struct Display<I2C> {
Expand Down Expand Up @@ -80,56 +53,18 @@ impl<I2C: I2c> Display<I2C> {
/// Clear display
#[allow(dead_code)]
pub fn clear(&mut self) -> Result<(), Error> {
debug!("Display: Clearing");
self.driver.clear(BinaryColor::Off)?;
self.driver.flush()?;
self.driver.set_display_on(true)?;
Ok(())
}

/// Display splash screen
pub fn splash(&mut self) -> Result<(), Error> {
self.driver.clear(BinaryColor::Off)?;
// TODO: Temporary title, replace with proper bitmap logo
SPLASH_TITLE_FONT.render_aligned(
"Touch'n Drink",
Point::new(63, 28),
VerticalPosition::Baseline,
HorizontalAlignment::Center,
FontColor::Transparent(BinaryColor::On),
&mut self.driver,
)?;
SPLASH_VERSION_FONT.render_aligned(
VERSION_STR,
Point::new(63, 28 + 12),
VerticalPosition::Baseline,
HorizontalAlignment::Center,
FontColor::Transparent(BinaryColor::On),
&mut self.driver,
)?;
SPLASH_FOOTER_FONT.render_aligned(
GIT_SHA_STR,
Point::new(127, 63),
VerticalPosition::Baseline,
HorizontalAlignment::Right,
FontColor::Transparent(BinaryColor::On),
&mut self.driver,
)?;
self.driver.flush()?;
self.driver.set_display_on(true)?;
Ok(())
}

/// Display big centered text
pub fn big_centered_char(&mut self, ch: char) -> Result<(), Error> {
/// Show screen
pub fn screen<S: Screen>(&mut self, screen: S) -> Result<(), Error> {
debug!("Display: Showing screen `{}`", any::type_name::<S>());
self.driver.clear(BinaryColor::Off)?;
BIG_CHAR_FONT.render_aligned(
ch,
Point::new(63, 42),
VerticalPosition::Baseline,
HorizontalAlignment::Center,
FontColor::Transparent(BinaryColor::On),
&mut self.driver,
)?;
screen.draw(&mut self.driver)?;
self.driver.flush()?;
self.driver.set_display_on(true)?;
Ok(())
Expand Down
1 change: 1 addition & 0 deletions firmware/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

mod display;
mod keypad;
mod screen;
mod ui;
mod wifi;

Expand Down
100 changes: 100 additions & 0 deletions firmware/src/screen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use crate::{GIT_SHA_STR, VERSION_STR};
use embedded_graphics::draw_target::DrawTarget;
use embedded_graphics::pixelcolor::BinaryColor;
use embedded_graphics::prelude::*;
use u8g2_fonts::types::{FontColor, HorizontalAlignment, VerticalPosition};
use u8g2_fonts::{fonts, FontRenderer};

const SPLASH_TITLE_FONT: FontRenderer = FontRenderer::new::<fonts::u8g2_font_logisoso16_tr>();
const SPLASH_VERSION_FONT: FontRenderer = FontRenderer::new::<fonts::u8g2_font_profont12_tr>();
const SPLASH_FOOTER_FONT: FontRenderer = FontRenderer::new::<fonts::u8g2_font_profont11_tr>();
const BIG_CHAR_FONT: FontRenderer = FontRenderer::new::<fonts::u8g2_font_luBS24_tr>();

/// Screen display error
#[derive(Debug)]
#[non_exhaustive]
pub enum Error<E> {
/// Display error
#[allow(dead_code)]
DisplayError(E),
/// Font render error
#[allow(dead_code)]
FontRenderError(u8g2_fonts::Error<E>),
}

impl<E> From<E> for Error<E> {
fn from(err: E) -> Self {
Self::DisplayError(err)
}
}

impl<E> From<u8g2_fonts::Error<E>> for Error<E> {
fn from(err: u8g2_fonts::Error<E>) -> Self {
Self::FontRenderError(err)
}
}

/// Generic screen that can be displayed
pub trait Screen {
fn draw<D: DrawTarget<Color = BinaryColor>>(
&self,
target: &mut D,
) -> Result<(), Error<D::Error>>;
}

/// Splash screen
pub struct Splash;

impl Screen for Splash {
fn draw<D: DrawTarget<Color = BinaryColor>>(
&self,
target: &mut D,
) -> Result<(), Error<D::Error>> {
// TODO: Temporary title, replace with proper bitmap logo
SPLASH_TITLE_FONT.render_aligned(
"Touch'n Drink",
Point::new(63, 28),
VerticalPosition::Baseline,
HorizontalAlignment::Center,
FontColor::Transparent(BinaryColor::On),
target,
)?;
SPLASH_VERSION_FONT.render_aligned(
VERSION_STR,
Point::new(63, 28 + 12),
VerticalPosition::Baseline,
HorizontalAlignment::Center,
FontColor::Transparent(BinaryColor::On),
target,
)?;
SPLASH_FOOTER_FONT.render_aligned(
GIT_SHA_STR,
Point::new(127, 63),
VerticalPosition::Baseline,
HorizontalAlignment::Right,
FontColor::Transparent(BinaryColor::On),
target,
)?;
Ok(())
}
}

/// Big centered character
pub struct BigCenteredChar(pub char);

impl Screen for BigCenteredChar {
fn draw<D: DrawTarget<Color = BinaryColor>>(
&self,
target: &mut D,
) -> Result<(), Error<D::Error>> {
BIG_CHAR_FONT.render_aligned(
self.0,
Point::new(63, 44),
VerticalPosition::Baseline,
HorizontalAlignment::Center,
FontColor::Transparent(BinaryColor::On),
target,
)?;
Ok(())
}
}
10 changes: 6 additions & 4 deletions firmware/src/ui.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::display::{self, Display};
use crate::keypad::{self, Key, Keypad};
use crate::screen;
use embassy_time::{with_timeout, Duration, TimeoutError};
use embedded_hal::digital::{InputPin, OutputPin};
use embedded_hal::i2c::I2c;
Expand Down Expand Up @@ -65,17 +66,18 @@ where
Ok(())
}

/// Show splash screen
/// Show splash screen and wait for keypress or timeout
pub async fn show_splash_screen(&mut self) -> Result<(), Error<IN, OUT>> {
self.display.splash()?;
self.display.screen(screen::Splash)?;
let _key = with_timeout(SPLASH_TIMEOUT, self.keypad.read()).await??;
Ok(())
}

/// Wait for input of a single digit
/// Wait for input of a single digit and show it on screen
pub async fn get_single_digit(&mut self) -> Result<Key, Error<IN, OUT>> {
let key = with_timeout(USER_TIMEOUT, self.keypad.read()).await??;
self.display.big_centered_char(key.as_char())?;
let ch = key.as_char();
self.display.screen(screen::BigCenteredChar(ch))?;
Ok(key)
}

Expand Down

0 comments on commit 83cba4f

Please sign in to comment.