From 8f3d1c23153f8b442ed872a62d2c2a1b85a2b24a Mon Sep 17 00:00:00 2001 From: zenonet Date: Mon, 22 Sep 2025 22:25:18 +0200 Subject: [PATCH] Implemented catching panics and displaying them on the display. Also implemented a deep-sleep based loop --- src/main.rs | 100 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 90 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index c2d706b..1a50da7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,11 @@ +use core::panic; use std::borrow::Cow; use std::str::FromStr; use chrono::{format::Fixed, DateTime, Datelike, Days, FixedOffset, NaiveTime}; use embedded_graphics::{ - mono_font::{MonoTextStyle, MonoTextStyleBuilder}, prelude::{Dimensions, Point}, - text::{Baseline, Text, TextStyleBuilder}, + text::{Text}, Drawable, }; use epd_waveshare::{color::Color, epd7in5_v2::Epd7in5, prelude::WaveshareDisplay}; @@ -22,13 +22,20 @@ use esp_idf_svc::{ nvs::EspDefaultNvsPartition, wifi::Configuration, }; +use esp_idf_sys::esp_deep_sleep; use u8g2_fonts::U8g2TextStyle; use untis::{Date, Lesson}; -use embedded_layout::{layout::linear::{spacing::DistributeFill, ElementSpacing, FixedMargin, LinearLayout}, prelude::*}; +use embedded_layout::{layout::linear::{spacing::DistributeFill, FixedMargin, LinearLayout}, prelude::*}; type Timetable = [[Option;8]; 5]; +#[link_section = ".rtc.data"] +static mut WAKEUP_COUNTER: u64 = 0; + +#[link_section = ".rtc.data"] +static mut ERROR_MSG: Option<[u8; 2048]> = None; + fn main() -> anyhow::Result<()> { // It is necessary to call this function once. Otherwise some patches to the runtime // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 @@ -49,6 +56,32 @@ fn main() -> anyhow::Result<()> { let dc = PinDriver::output(miso).unwrap(); + + std::panic::set_hook(Box::new(|panic|{ + let msg: String = format!("{}", panic);/* if let Some(s) = panic.payload().downcast_ref::<&str>() { + format!("{s:?}") + } else if let Some(s) = panic.payload().downcast_ref::() { + format!("{s:?}") + } else { + format!("Failed to downcast panic msg") + }; */ + let msg_bytes = msg.into_bytes(); + + // Copy the string buffer into the array + let mut buffer = [0u8; 2048]; + for i in 0..(buffer.len().min(msg_bytes.len())){ + buffer[i] = msg_bytes[i]; + } + + unsafe{ + ERROR_MSG = Some(buffer); + + // Sleep for a second + esp_deep_sleep(1_000_000) + } + })); + + log::info!("Initialized pins"); let config = config::Config::new(); @@ -66,21 +99,54 @@ fn main() -> anyhow::Result<()> { log::info!("Initialized spi interface"); + // Initialize the draw buffer // Allocating on the stack makes the chip crash. We gotta put it on the heap let mut display = Box::new(epd_waveshare::epd7in5_v2::Display7in5::default()); display.set_rotation(epd_waveshare::prelude::DisplayRotation::Rotate0); + unsafe { + WAKEUP_COUNTER += 1; + } + let mut delay = Ets; - let mut epd7in5 = Epd7in5::new(&mut spi, busy, dc, rst, &mut delay, None).unwrap(); + unsafe{ + if let Some(error) = ERROR_MSG{ + + // Draw to the display + let text_style = U8g2TextStyle::new(u8g2_fonts::fonts::u8g2_font_7x14_mr, Color::White); + + let error_label = Text::new("An error occured:", Point::zero(), &text_style); + + let error_message = String::from_utf8_lossy(&error); + let error_message = match &error_message{ + Cow::Borrowed(c) => *c, + Cow::Owned(c) => &c + } ; + + + let error_message_label = Text::new(error_message, Point::zero(), &text_style); + let mut vg = [error_label, error_message_label]; + LinearLayout::vertical(Views::new(&mut vg)) + .arrange() + .draw(&mut *display).unwrap(); + + let mut epd7in5 = Epd7in5::new(&mut spi, busy, dc, rst, &mut delay, None).unwrap(); + epd7in5.update_and_display_frame(&mut spi, &*display.buffer(), &mut delay)?; + epd7in5.sleep(&mut spi, &mut delay)?; + + // Sleep for 12 hours + esp_deep_sleep(12*60*60*60*1000_000) + } + } // Create a file called wifi-creds.txt and write SSID and password into it separated by a line break let mut creds = include_str!("../wifi-creds.txt").split('\n'); let ssid: heapless::String<32> = - heapless::String::<32>::from_str(creds.next().unwrap()).unwrap(); + heapless::String::<32>::from_str(creds.next().unwrap()).unwrap(); let password: heapless::String<64> = - heapless::String::<64>::from_str(creds.next().unwrap()).unwrap(); - + heapless::String::<64>::from_str(creds.next().unwrap()).unwrap(); + // Connecting to wifi let sysloop = EspSystemEventLoop::take().unwrap(); let nvs = EspDefaultNvsPartition::take().unwrap(); @@ -115,13 +181,26 @@ fn main() -> anyhow::Result<()> { let (tt, now) = fetch_timetable(httpconnection); + + // Render the layout draw_layout(&mut display, &tt, now); - //draw_text(&mut display, &tt[3][5].clone().unwrap().subjects.first().as_ref().unwrap().name, 5, 5); + + // Actually push changes to the display + let mut epd7in5 = Epd7in5::new(&mut spi, busy, dc, rst, &mut delay, None).unwrap(); epd7in5.update_and_display_frame(&mut spi, &*display.buffer(), &mut delay)?; epd7in5.sleep(&mut spi, &mut delay)?; log::info!("Completed"); - Ok(()) + wifi.disconnect().unwrap(); + wifi.stop().unwrap(); + + drop(epd7in5); + drop(wifi); + drop(spi); + + unsafe{ + esp_deep_sleep(10 * 60 * 1000_000); + } } fn draw_layout(display: &mut epd_waveshare::epd7in5_v2::Display7in5, timetable: &Timetable, now: DateTime){ @@ -166,7 +245,8 @@ fn draw_layout(display: &mut epd_waveshare::epd7in5_v2::Display7in5, timetable: let columns = Views::new(&mut columns); - let time_str = format!("{}", now.format("%Y-%m-%d %H:%M")); + // Unsafe is needed to access static mutable variable + let time_str = format!("{} ({})", now.format("%Y-%m-%d %H:%M"), unsafe {WAKEUP_COUNTER}); let time_text = Text::new(&time_str, Point::zero(), &text_style_small); let time_table_grid = LinearLayout::horizontal(columns)