rktk_drivers_common/
panic_utils.rs1use core::{fmt::Write, mem::MaybeUninit, ptr::write_volatile};
10
11use embassy_time::Timer;
12use embedded_graphics::{
13 mono_font::{MonoFont, MonoTextStyle, ascii::FONT_8X13},
14 pixelcolor::BinaryColor,
15 prelude::*,
16 text::{Baseline, Text},
17};
18use rktk::{
19 drivers::interface::display::DisplayDriver,
20 task::display::utils::{RotatedDrawTarget, Rotation},
21};
22
23#[unsafe(link_section = ".uninit.PANICINFO")]
24static mut PANIC_INFO: MaybeUninit<PanicMessage> = MaybeUninit::uninit();
25const PANIC_INFO_MAGIC: u32 = 0x54_41_43_4B;
26
27#[repr(C)]
28pub struct PanicMessage {
29 magic: u32,
30 len: u16,
31 data: [u8; 1024],
32}
33
34impl Write for PanicMessage {
35 fn write_str(&mut self, s: &str) -> core::fmt::Result {
36 let writable_str = if s.len() + self.len as usize > self.data.len() {
37 let writable_len = self.data.len() - self.len as usize;
38 if writable_len == 0 {
39 return Ok(());
40 }
41 s.get(..writable_len).unwrap_or(s)
42 } else {
43 s
44 };
45
46 let new_len = writable_str.len() as u16 + self.len;
47 self.data[self.len as usize..new_len as usize].copy_from_slice(writable_str.as_bytes());
48 self.len = new_len;
49
50 Ok(())
51 }
52}
53
54impl Default for PanicMessage {
55 fn default() -> Self {
56 Self { magic: PANIC_INFO_MAGIC, len: 0, data: [0; 1024] }
57 }
58}
59
60impl PanicMessage {
61 fn reset() -> Self {
62 Self { magic: 0, len: 0, data: [0; 1024] }
63 }
64}
65
66pub fn save_panic_info(info: &core::panic::PanicInfo) {
70 let mut panic_info = PanicMessage::default();
71 write!(panic_info, "{info}").ok();
72
73 unsafe {
74 write_volatile(&raw mut PANIC_INFO, MaybeUninit::new(panic_info));
75 }
76}
77
78pub fn read_panic_message() -> Option<PanicMessage> {
79 unsafe {
80 let info = core::ptr::read(&raw const PANIC_INFO);
81 let info = info.assume_init();
82 if info.magic == PANIC_INFO_MAGIC {
83 write_volatile(&raw mut PANIC_INFO, MaybeUninit::new(PanicMessage::reset()));
84 Some(info)
85 } else {
86 None
87 }
88 }
89}
90
91pub fn parse_panic_message(panic_info: &PanicMessage) -> &str {
92 match core::str::from_utf8(&panic_info.data[..panic_info.len as usize]) {
96 Ok(str) => str,
97 Err(e) => {
98 let valid_len = e.valid_up_to();
99 core::str::from_utf8(&panic_info.data[..valid_len]).unwrap()
100 }
101 }
102}
103
104const FONT: MonoFont = FONT_8X13;
105
106pub async fn display_message_if_panicked<D: DisplayDriver<Color = BinaryColor>>(display: &mut D) {
114 if let Some(panic_info) = read_panic_message()
115 && display.init().await.is_ok()
116 {
117 let char_width = FONT.character_size.width as usize;
118
119 let str = parse_panic_message(&panic_info);
120
121 rktk_log::error!("Previous panic detected: {:?}", str);
122
123 let str_len = str.lines().map(|line| line.chars().count()).max().unwrap_or(0);
124
125 let orig_display_size = display.draw_target().bounding_box().size;
126 let rotation = if orig_display_size.width > orig_display_size.height {
127 Rotation::Rotate0
128 } else {
129 Rotation::Rotate90
130 };
131
132 let rotated_display = RotatedDrawTarget::new(display.draw_target(), rotation);
133 let display_width = rotated_display.bounding_box().size.width as usize;
134 let overflow_len = if str_len * char_width > display_width {
135 str_len - display_width / char_width + 1
136 } else {
137 0
138 };
139
140 display_mes(display, "Panic!", Point::zero(), rotation).await;
141 Timer::after_millis(400).await;
142
143 if overflow_len > 0 {
144 loop {
145 for i in 0..=overflow_len {
146 display_mes(display, str, Point::new(-((i * char_width) as i32), 0), rotation)
147 .await;
148 Timer::after_millis(200).await;
149 }
150 }
151 } else {
152 display_mes(display, str, Point::zero(), rotation).await;
153 Timer::after_secs(100000000).await;
154 }
155 }
156}
157pub async fn display_mes<D: DisplayDriver<Color = BinaryColor>>(
158 display: &mut D,
159 str: &str,
160 pos: Point,
161 rotation: Rotation,
162) {
163 {
164 let mut rotated_display = RotatedDrawTarget::new(display.draw_target(), rotation);
165
166 let _ = rotated_display.clear(BinaryColor::Off);
167 let _ = Text::with_baseline(
168 str,
169 pos,
170 MonoTextStyle::new(&FONT, BinaryColor::On),
171 Baseline::Top,
172 )
173 .draw(&mut rotated_display);
174 }
175 let _ = display.flush().await;
176}