diff --git a/firmware/src/main.rs b/firmware/src/main.rs index 0fa39c7..d92c3ff 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -64,7 +64,7 @@ use esp_hal::system::SystemControl; use esp_hal::timer::systimer::{SystemTimer, Target}; use esp_hal::timer::timg::TimerGroup; use esp_println::println; -use log::info; +use log::{error, info}; static VERSION_STR: &str = concat!("v", env!("CARGO_PKG_VERSION")); static GIT_SHA_STR: &str = env!("GIT_SHORT_SHA"); @@ -179,7 +179,7 @@ async fn main(spawner: Spawner) { let mut ui = ui::Ui::new(display, keypad, nfc, buzzer, wifi); // Show splash screen for a while, ignore any error - let _ = ui.show_splash_screen().await; + let _ = ui.show_splash().await; // Wait for network to become available (if not already), ignore any error let _ = ui.wait_network_up().await; @@ -192,8 +192,11 @@ async fn main(spawner: Spawner) { Err(error::Error::Cancel) => info!("User cancelled, starting over..."), // Timeout: start over again Err(error::Error::UserTimeout) => info!("Timeout waiting for user, starting over..."), - // TODO: Display error to user and start over again - Err(err) => panic!("Unhandled Error: {:?}", err), + // Display error to user and start over again + Err(err) => { + error!("Unhandled Error: {:?}", err); + let _ = ui.show_error(&err).await; + } } } } diff --git a/firmware/src/screen.rs b/firmware/src/screen.rs index 72bc77a..612fd22 100644 --- a/firmware/src/screen.rs +++ b/firmware/src/screen.rs @@ -1,4 +1,5 @@ use crate::{GIT_SHA_STR, VERSION_STR}; +use core::fmt; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::image::{Image, ImageRaw}; use embedded_graphics::pixelcolor::BinaryColor; @@ -76,6 +77,43 @@ impl Screen for Splash { } } +/// Failure screen +pub struct Failure { + message: M, +} + +impl Failure { + pub fn new(message: M) -> Self { + Self { message } + } +} + +impl Screen for Failure { + fn draw>( + &self, + target: &mut D, + ) -> Result<(), Error> { + TITLE_FONT.render_aligned( + "FEHLER!", + Point::new(63, 27), + VerticalPosition::Baseline, + HorizontalAlignment::Center, + FontColor::Transparent(BinaryColor::On), + target, + )?; + SMALL_FONT.render_aligned( + format_args!("{}", self.message), + Point::new(63, 27 + 12), + VerticalPosition::Baseline, + HorizontalAlignment::Center, + FontColor::Transparent(BinaryColor::On), + target, + )?; + Footer::new("* Abbruch", "").draw(target)?; + Ok(()) + } +} + /// Wait while a lengthy action is in progress pub enum PleaseWait { WifiConnecting, @@ -224,7 +262,7 @@ impl Screen for Success { target, )?; SMALL_FONT.render_aligned( - format_args!("{} Getränke entnehmen", self.num_drinks), + format_args!("{} Getränke genehmigt", self.num_drinks), Point::new(63, 27 + 12), VerticalPosition::Baseline, HorizontalAlignment::Center, @@ -236,43 +274,6 @@ impl Screen for Success { } } -/// Error screen -pub struct Failure<'a> { - message: &'a str, -} - -impl<'a> Failure<'a> { - pub fn new(message: &'a str) -> Self { - Self { message } - } -} - -impl<'a> Screen for Failure<'a> { - fn draw>( - &self, - target: &mut D, - ) -> Result<(), Error> { - TITLE_FONT.render_aligned( - "FEHLER!", - Point::new(63, 27), - VerticalPosition::Baseline, - HorizontalAlignment::Center, - FontColor::Transparent(BinaryColor::On), - target, - )?; - SMALL_FONT.render_aligned( - self.message, - Point::new(63, 27 + 12), - VerticalPosition::Baseline, - HorizontalAlignment::Center, - FontColor::Transparent(BinaryColor::On), - target, - )?; - Footer::new("* Abbruch", "").draw(target)?; - Ok(()) - } -} - /// Common footer (bottom 7 lines 57..64) struct Footer<'a> { left: &'a str, diff --git a/firmware/src/ui.rs b/firmware/src/ui.rs index 95f1241..94f133b 100644 --- a/firmware/src/ui.rs +++ b/firmware/src/ui.rs @@ -6,6 +6,7 @@ use crate::nfc::{Nfc, Uid}; use crate::screen; use crate::wifi::Wifi; use core::convert::Infallible; +use core::fmt; use embassy_futures::select::{select, Either}; use embassy_time::{with_timeout, Duration, TimeoutError}; use embedded_hal_async::digital::Wait; @@ -69,8 +70,11 @@ impl<'a, I2C: I2c, IRQ: Wait> Ui<'a, I2C, IRQ> { } /// Show splash screen and wait for keypress or timeout - pub async fn show_splash_screen(&mut self) -> Result<(), Error> { + pub async fn show_splash(&mut self) -> Result<(), Error> { + info!("UI: Displaying splash screen"); + self.display.screen(&screen::Splash).await?; + match with_timeout(SPLASH_TIMEOUT, self.keypad.read()).await { // Key pressed Ok(_key) => Ok(()), @@ -79,6 +83,22 @@ impl<'a, I2C: I2c, IRQ: Wait> Ui<'a, I2C, IRQ> { } } + /// Show error screen and wait for keypress or timeout + pub async fn show_error(&mut self, message: M) -> Result<(), Error> { + info!("UI: Displaying error: {}", message); + + self.display.screen(&screen::Failure::new(message)).await?; + let _ = self.buzzer.error().await; + + let wait_cancel = async { while self.keypad.read().await != Key::Cancel {} }; + match with_timeout(USER_TIMEOUT, wait_cancel).await { + // Cancel key cancels + Ok(()) => Ok(()), + // User interaction timeout + Err(TimeoutError) => Err(Error::UserTimeout), + } + } + /// Wait for network to become available (if not already). Show a waiting screen and allow to /// cancel pub async fn wait_network_up(&mut self) -> Result<(), Error> { @@ -86,6 +106,8 @@ impl<'a, I2C: I2c, IRQ: Wait> Ui<'a, I2C, IRQ> { return Ok(()); } + info!("UI: Waiting for network to become available..."); + self.display .screen(&screen::PleaseWait::WifiConnecting) .await?; @@ -117,10 +139,7 @@ impl<'a, I2C: I2c, IRQ: Wait> Ui<'a, I2C, IRQ> { // TODO: Process payment let _ = screen::Success::new(num_drinks); - self.display - .screen(&screen::Failure::new("Not implemented yet")) - .await?; - let _ = self.buzzer.error().await; + let _ = self.show_error("Not implemented yet").await; let _key = self.keypad.read().await; Ok(()) }