6 Commits
9 changed files with 100 additions and 53 deletions
+2 -4
View File
@@ -2,13 +2,11 @@
runner = "espflash flash --monitor --chip esp32s3" runner = "espflash flash --monitor --chip esp32s3"
[env] [env]
ESP_LOG="info" ESP_LOG = "debug"
WM_CONN = '{"ssid": "Wokwi-GUEST", "psk": "", "data": {}}' WM_CONN = '{"ssid": "Wokwi-GUEST", "psk": "", "data": {}}'
[build] [build]
rustflags = [ rustflags = ["-C", "link-arg=-nostartfiles"]
"-C", "link-arg=-nostartfiles",
]
target = "xtensa-esp32s3-none-elf" target = "xtensa-esp32s3-none-elf"
+3
View File
@@ -83,3 +83,6 @@ opt-level = 's'
[features] [features]
default = [] default = []
wokwi = [] wokwi = []
[lints.clippy]
manual_async_fn = "allow"
+16 -2
View File
@@ -1,4 +1,9 @@
use crate::peripherals::Peripherals; use crate::navigation::navigation::display_error;
use crate::{
navigation::{navigation::Mutex, outputs::Outputs, state::NavigationState},
peripherals::Peripherals,
};
use alloc::string::ToString;
use embedded_hal_bus::i2c::AtomicDevice; use embedded_hal_bus::i2c::AtomicDevice;
use esp_hal::{Blocking, i2c::master::I2c, time::Instant}; use esp_hal::{Blocking, i2c::master::I2c, time::Instant};
@@ -15,7 +20,9 @@ use crate::{
)] )]
pub async fn nfc_scanner( pub async fn nfc_scanner(
mut nfc_driver: NfcPn532Driver<AtomicDevice<'static, I2c<'static, Blocking>>>, mut nfc_driver: NfcPn532Driver<AtomicDevice<'static, I2c<'static, Blocking>>>,
outputs: &'static Mutex<Outputs>,
peripherals: &'static Peripherals, peripherals: &'static Peripherals,
navigation_state: &'static Mutex<NavigationState>,
) { ) {
loop { loop {
while CARD_DATA.lock().await.is_none() { while CARD_DATA.lock().await.is_none() {
@@ -45,7 +52,14 @@ pub async fn nfc_scanner(
{ {
Ok(()) => {} Ok(()) => {}
Err(error) => { Err(error) => {
log::error!("Error storing card in flash: {:?}", error) log::error!("Error storing card in flash: {:?}", error);
display_error(
error.to_string(),
navigation_state,
outputs,
peripherals,
)
.await;
} }
} }
+7 -4
View File
@@ -20,6 +20,7 @@ use creaturedex::drivers::tertiary_lcd::{
use creaturedex::navigation::navigation::Mutex; use creaturedex::navigation::navigation::Mutex;
use creaturedex::navigation::inputs::Inputs; use creaturedex::navigation::inputs::Inputs;
use creaturedex::navigation::navigation::{self}; use creaturedex::navigation::navigation::{self};
use creaturedex::navigation::state::NavigationState;
use creaturedex::navigation::outputs::Outputs; use creaturedex::navigation::outputs::Outputs;
use creaturedex::peripherals::Peripherals; use creaturedex::peripherals::Peripherals;
use creaturedex::peripherals::storage::cardstore::CardStore; use creaturedex::peripherals::storage::cardstore::CardStore;
@@ -163,7 +164,7 @@ async fn main(spawner: Spawner) {
display_shit(&mut secondaries[2], "baz 1"); display_shit(&mut secondaries[2], "baz 1");
display_shit_prim(&mut primary, "Hello World"); display_shit_prim(&mut primary, "Hello World");
let outputs = { let outputs = Box::leak(Box::new(Mutex::new({
let [sec_1, sec_2, sec_3] = secondaries; let [sec_1, sec_2, sec_3] = secondaries;
Outputs { Outputs {
primary_display: primary, primary_display: primary,
@@ -173,12 +174,14 @@ async fn main(spawner: Spawner) {
tertiary_display: lcd, tertiary_display: lcd,
_led_a: (), _led_a: (),
} }
}; })));
let navigation_state = Box::leak(Box::new(Mutex::new(NavigationState::new())));
log::info!("Setup complete, entering main loop"); log::info!("Setup complete, entering main loop");
spawner.spawn(navigation::run(inputs, outputs, peripherals).expect("run task failed")); spawner.spawn(navigation::run(inputs, outputs, peripherals, navigation_state).expect("run task failed"));
spawner.spawn( spawner.spawn(
background_tasks::nfc_scanner(nfc_driver, peripherals).expect("nfc scanner task failed"), background_tasks::nfc_scanner(nfc_driver, outputs, peripherals, navigation_state).expect("nfc scanner task failed"),
); );
core::future::pending::<()>().await core::future::pending::<()>().await
} }
+46 -34
View File
@@ -41,43 +41,56 @@ pub trait Navigable {
fn handle_input(&self, input: Action) -> impl core::future::Future<Output = NewState> + Send; fn handle_input(&self, input: Action) -> impl core::future::Future<Output = NewState> + Send;
} }
#[embassy_executor::task] pub async fn display_error(
pub async fn run(mut inputs: Inputs, mut outputs: Outputs, peripherals: &'static Peripherals) { error: String,
async fn display_error( navigation_state: &Mutex<NavigationState>,
error: String, outputs: &Mutex<Outputs>,
state: &mut NavigationState, peripherals: &Peripherals,
outputs: &mut Outputs, ) -> () {
peripherals: &Peripherals, let error_view = ErrorView {
) -> () { error: error.to_string(),
let error_view = ErrorView { };
error: error.to_string(), let mut state = navigation_state.lock().await;
}; state.screens.push(View::Error(error_view));
state.screens.push(View::Error(error_view)); let mut outs = outputs.lock().await;
state state
.screens
.last()
// Unwrap: we just pushed the state
.unwrap()
.display(outputs, peripherals)
.await
// Unwrap: display will panic on error in error view
.unwrap();
}
log::info!("starting navigation");
let mut state = NavigationState::new();
if let Err(error) = state
.screens .screens
.last() .last()
// Unwrap: we just pushed the state
.unwrap() .unwrap()
.display(&mut outputs, peripherals) .display(&mut outs, peripherals)
.await .await
// Unwrap: display will panic on error in error view
.unwrap();
}
#[embassy_executor::task]
pub async fn run(
mut inputs: Inputs,
outputs: &'static Mutex<Outputs>,
peripherals: &'static Peripherals,
navigation_state: &'static Mutex<NavigationState>,
) {
log::info!("starting navigation");
{ {
display_error(error.to_string(), &mut state, &mut outputs, peripherals).await; let mut outs = outputs.lock().await;
}; if let Err(error) = navigation_state
.lock()
.await
.screens
.last()
.unwrap()
.display(&mut outs, peripherals)
.await
{
display_error(error.to_string(), navigation_state, outputs, peripherals).await;
};
}
loop { loop {
Timer::after(Duration::from_millis(DEBOUNCE_DURATION_MILLIS)).await;
let mut state = navigation_state.lock().await;
let selection = select( let selection = select(
inputs.wait_for_press(), inputs.wait_for_press(),
Timer::after(Duration::from_millis(DEBOUNCE_DURATION_MILLIS)), Timer::after(Duration::from_millis(DEBOUNCE_DURATION_MILLIS)),
@@ -120,17 +133,16 @@ pub async fn run(mut inputs: Inputs, mut outputs: Outputs, peripherals: &'static
} }
state.screens.push(new_state.view); state.screens.push(new_state.view);
let mut outs = outputs.lock().await;
if (action != Action::Timer || new_state.redraw) if (action != Action::Timer || new_state.redraw)
&& let Err(error) = state && let Err(error) = state
.screens .screens
.last() .last()
.unwrap() .unwrap()
.display(&mut outputs, peripherals) .display(&mut outs, peripherals)
.await .await
{ {
display_error(error.to_string(), &mut state, &mut outputs, peripherals).await; display_error(error.to_string(), navigation_state, outputs, peripherals).await;
} }
Timer::after(Duration::from_millis(DEBOUNCE_DURATION_MILLIS)).await;
} }
} }
+5 -3
View File
@@ -21,8 +21,7 @@ impl Error for FlashStoreError {}
impl Display for FlashStoreError { impl Display for FlashStoreError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
use core::fmt::Debug; write!(f, "FlashStoreError:\n{:?}", self.0)
self.0.fmt(f)
} }
} }
@@ -75,7 +74,10 @@ impl FlashStore {
bytes.len() bytes.len()
); );
let mut lock = self.storage.lock().await; let mut lock = self.storage.lock().await;
lock.erase(FLASH_ADDR + offset, FLASH_ADDR + bytes.len() as u32)?; lock.erase(
FLASH_ADDR + offset,
FLASH_ADDR + offset + bytes.len() as u32,
)?;
lock.write(FLASH_ADDR + offset, bytes)?; lock.write(FLASH_ADDR + offset, bytes)?;
Ok(()) Ok(())
} }
+12 -3
View File
@@ -34,6 +34,15 @@ impl Navigable for ErrorView {
_: &Peripherals, _: &Peripherals,
) -> impl core::future::Future<Output = Result<(), Box<dyn error::Error>>> + Send { ) -> impl core::future::Future<Output = Result<(), Box<dyn error::Error>>> + Send {
async move { async move {
outputs
.primary_display
.clear(Rgb565::BLACK)
.unwrap_or_else(|error| {
panic!(
"Error: {error:?}\nDraw error while rendering ErrorView: {}",
self.error
)
});
let style = MonoTextStyle::new(&FONT_10X20, Rgb565::RED); let style = MonoTextStyle::new(&FONT_10X20, Rgb565::RED);
let display_area = outputs.primary_display.bounding_box(); let display_area = outputs.primary_display.bounding_box();
Text::new( Text::new(
@@ -44,7 +53,7 @@ impl Navigable for ErrorView {
.draw(&mut outputs.primary_display) .draw(&mut outputs.primary_display)
.unwrap_or_else(|error| { .unwrap_or_else(|error| {
panic!( panic!(
"Error: {error:?}\nDraw error while rendering error: {}", "Error: {error:?}\nDraw error while rendering ErrorView: {}",
self.error self.error
) )
}); });
@@ -55,13 +64,13 @@ impl Navigable for ErrorView {
.alignment(HorizontalAlignment::Center) .alignment(HorizontalAlignment::Center)
.build(); .build();
let bounds = Rectangle::new(Point::new(0, 30), display_area.size); let bounds = Rectangle::new(Point::new(0, 50), display_area.size);
TextBox::with_textbox_style(&self.error.to_string(), bounds, style, textbox_style) TextBox::with_textbox_style(&self.error.to_string(), bounds, style, textbox_style)
.draw(&mut outputs.primary_display) .draw(&mut outputs.primary_display)
.unwrap_or_else(|error| { .unwrap_or_else(|error| {
panic!( panic!(
"Error: {error:?}\nDraw error while rendering error: {}", "Error: {error:?}\nDraw error while rendering ErrorView: {}",
self.error self.error
) )
}); });
+5 -1
View File
@@ -34,6 +34,10 @@ impl Navigable for FlashInfoView {
peripherals: &Peripherals, peripherals: &Peripherals,
) -> impl core::future::Future<Output = Result<(), Box<dyn error::Error>>> + Send { ) -> impl core::future::Future<Output = Result<(), Box<dyn error::Error>>> + Send {
async move { async move {
outputs
.primary_display
.clear(Rgb565::BLACK)
.map_err(PrimaryDisplayError::from)?;
let _style = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE); let _style = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE);
let mut magic_bytes: [u8; MAGIC_REGION.size] = [0; MAGIC_REGION.size]; let mut magic_bytes: [u8; MAGIC_REGION.size] = [0; MAGIC_REGION.size];
peripherals peripherals
@@ -55,7 +59,7 @@ impl Navigable for FlashInfoView {
let style = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE); let style = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE);
let textbox_style = TextBoxStyleBuilder::new() let textbox_style = TextBoxStyleBuilder::new()
.height_mode(HeightMode::FitToText) .height_mode(HeightMode::FitToText)
.alignment(HorizontalAlignment::Center) .alignment(HorizontalAlignment::Justified)
.build(); .build();
let display_area = outputs.primary_display.bounding_box(); let display_area = outputs.primary_display.bounding_box();
+4 -2
View File
@@ -12,7 +12,7 @@ use embedded_graphics::pixelcolor::Rgb565;
use embedded_graphics::prelude::RgbColor; use embedded_graphics::prelude::RgbColor;
use embedded_graphics_core::draw_target::DrawTarget; use embedded_graphics_core::draw_target::DrawTarget;
pub const MAX_SELECTED: i32 = 2; pub const MAX_SELECTED: i32 = 3;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SettingsMenu { pub struct SettingsMenu {
@@ -27,7 +27,9 @@ impl Navigable for SettingsMenu {
) -> impl core::future::Future<Output = Result<(), Box<dyn error::Error>>> + Send { ) -> impl core::future::Future<Output = Result<(), Box<dyn error::Error>>> + Send {
async move { async move {
let display = &mut outputs.primary_display; let display = &mut outputs.primary_display;
display.clear(Rgb565::BLACK).unwrap(); display
.clear(Rgb565::BLACK)
.map_err(PrimaryDisplayError::from)?;
menu_item::show(display, "System Inforation", 0, self.selected) menu_item::show(display, "System Inforation", 0, self.selected)
.map_err(PrimaryDisplayError::from)?; .map_err(PrimaryDisplayError::from)?;
menu_item::show(display, "Wifi Information", 1, self.selected) menu_item::show(display, "Wifi Information", 1, self.selected)