A personal rust firmware for the Badger 2040 W

WIp of multiple screens

Changed files
+181 -97
src
badge_display
+141 -92
src/badge_display/mod.rs
··· 27 27 use gpio::Output; 28 28 use heapless::String; 29 29 use tinybmp::Bmp; 30 - use uc8151::asynch::Uc8151; 31 30 use uc8151::LUT; 32 31 use uc8151::WIDTH; 32 + use uc8151::{asynch::Uc8151, HEIGHT}; 33 33 use {defmt_rtt as _, panic_probe as _}; 34 34 35 35 use crate::{env::env_value, helpers::easy_format, Spi0Bus}; 36 36 37 37 //Display state 38 + pub static SCREEN_TO_SHOW: blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<Screen>> = 39 + blocking_mutex::Mutex::new(RefCell::new(Screen::Badge)); 40 + pub static DISPLAY_CHANGED: AtomicBool = AtomicBool::new(false); 38 41 pub static CURRENT_IMAGE: AtomicU8 = AtomicU8::new(0); 39 42 pub static CHANGE_IMAGE: AtomicBool = AtomicBool::new(true); 40 43 pub static WIFI_COUNT: AtomicU32 = AtomicU32::new(0); ··· 42 45 blocking_mutex::Mutex::new(RefCell::new(String::<8>::new())); 43 46 pub static TEMP: AtomicU8 = AtomicU8::new(0); 44 47 pub static HUMIDITY: AtomicU8 = AtomicU8::new(0); 48 + 49 + #[derive(Debug, Clone, Copy, PartialEq, defmt::Format)] 50 + pub enum Screen { 51 + Badge, 52 + WifiList, 53 + } 45 54 46 55 #[embassy_executor::task] 47 56 pub async fn run_the_display( ··· 102 111 //New start every 120 cycles or 60 seconds 103 112 let cycles_to_clear_at: i32 = 120; 104 113 let mut cycles_since_last_clear = 0; 105 - 114 + let mut current_screen = Screen::Badge; 106 115 loop { 107 116 //Timed based display events 108 - 109 - //Runs every second; 110 - if cycles_since_last_clear % 2 == 0 { 111 - // update_time_values_from_cycles(); 117 + if DISPLAY_CHANGED.load(core::sync::atomic::Ordering::Relaxed) { 118 + let clear_rectangle = Rectangle::new(Point::new(0, 0), Size::new(WIDTH, HEIGHT)); 119 + clear_rectangle 120 + .into_styled(PrimitiveStyle::with_fill(BinaryColor::On)) 121 + .draw(&mut display) 122 + .unwrap(); 123 + let _ = display.update().await; 124 + DISPLAY_CHANGED.store(false, core::sync::atomic::Ordering::Relaxed); 125 + first_run = true; 112 126 } 113 127 114 - //Updates the top bar 115 - //Runs every 60 cycles/30 seconds and first run 116 - if cycles_since_last_clear % 60 == 0 || first_run { 117 - let count = WIFI_COUNT.load(core::sync::atomic::Ordering::Relaxed); 118 - let temp = TEMP.load(core::sync::atomic::Ordering::Relaxed); 119 - let humidity = HUMIDITY.load(core::sync::atomic::Ordering::Relaxed); 120 - let top_text: String<64> = easy_format::<64>(format_args!( 121 - "{}F {}% Wifi found: {}", 122 - temp, humidity, count 123 - )); 124 - let top_bounds = Rectangle::new(Point::new(0, 0), Size::new(WIDTH, 24)); 125 - top_bounds 126 - .into_styled( 127 - PrimitiveStyleBuilder::default() 128 - .stroke_color(BinaryColor::Off) 129 - .fill_color(BinaryColor::On) 130 - .stroke_width(1) 131 - .build(), 132 - ) 133 - .draw(&mut display) 134 - .unwrap(); 128 + SCREEN_TO_SHOW.lock(|x| current_screen = *x.borrow()); 129 + info!("Current Screen: {:?}", current_screen); 130 + if current_screen == Screen::Badge { 131 + //Updates the top bar 132 + //Runs every 60 cycles/30 seconds and first run 133 + if cycles_since_last_clear % 60 == 0 || first_run { 134 + let count = WIFI_COUNT.load(core::sync::atomic::Ordering::Relaxed); 135 + let temp = TEMP.load(core::sync::atomic::Ordering::Relaxed); 136 + let humidity = HUMIDITY.load(core::sync::atomic::Ordering::Relaxed); 137 + let top_text: String<64> = easy_format::<64>(format_args!( 138 + "{}F {}% Wifi found: {}", 139 + temp, humidity, count 140 + )); 141 + let top_bounds = Rectangle::new(Point::new(0, 0), Size::new(WIDTH, 24)); 142 + top_bounds 143 + .into_styled( 144 + PrimitiveStyleBuilder::default() 145 + .stroke_color(BinaryColor::Off) 146 + .fill_color(BinaryColor::On) 147 + .stroke_width(1) 148 + .build(), 149 + ) 150 + .draw(&mut display) 151 + .unwrap(); 135 152 136 - Text::new(top_text.as_str(), Point::new(8, 16), character_style) 137 - .draw(&mut display) 138 - .unwrap(); 153 + Text::new(top_text.as_str(), Point::new(8, 16), character_style) 154 + .draw(&mut display) 155 + .unwrap(); 139 156 140 - // Draw the text box. 141 - let result = display.partial_update(top_bounds.try_into().unwrap()).await; 142 - match result { 143 - Ok(_) => {} 144 - Err(_) => { 145 - info!("Error updating display"); 157 + // Draw the text box. 158 + let result = display.partial_update(top_bounds.try_into().unwrap()).await; 159 + match result { 160 + Ok(_) => {} 161 + Err(_) => { 162 + info!("Error updating display"); 163 + } 146 164 } 147 165 } 148 - } 149 166 150 - //Runs every 120 cycles/60 seconds and first run 151 - if cycles_since_last_clear == 0 { 152 - let mut time_text: String<8> = String::<8>::new(); 167 + //Runs every 120 cycles/60 seconds and first run 168 + if cycles_since_last_clear == 0 || first_run { 169 + let mut time_text: String<8> = String::<8>::new(); 170 + 171 + let time_box_rectangle_location = Point::new(0, 96); 172 + RTC_TIME_STRING.lock(|x| { 173 + time_text.push_str(x.borrow().as_str()).unwrap(); 174 + }); 153 175 154 - let time_box_rectangle_location = Point::new(0, 96); 155 - RTC_TIME_STRING.lock(|x| { 156 - time_text.push_str(x.borrow().as_str()).unwrap(); 157 - }); 176 + //The bounds of the box for time and refresh area 177 + let time_bounds = Rectangle::new(time_box_rectangle_location, Size::new(88, 24)); 178 + time_bounds 179 + .into_styled( 180 + PrimitiveStyleBuilder::default() 181 + .stroke_color(BinaryColor::Off) 182 + .fill_color(BinaryColor::On) 183 + .stroke_width(1) 184 + .build(), 185 + ) 186 + .draw(&mut display) 187 + .unwrap(); 158 188 159 - //The bounds of the box for time and refresh area 160 - let time_bounds = Rectangle::new(time_box_rectangle_location, Size::new(88, 24)); 161 - time_bounds 162 - .into_styled( 163 - PrimitiveStyleBuilder::default() 164 - .stroke_color(BinaryColor::Off) 165 - .fill_color(BinaryColor::On) 166 - .stroke_width(1) 167 - .build(), 189 + //Adding a y offset to the box location to fit inside the box 190 + Text::new( 191 + time_text.as_str(), 192 + ( 193 + time_box_rectangle_location.x + 8, 194 + time_box_rectangle_location.y + 16, 195 + ) 196 + .into(), 197 + character_style, 168 198 ) 169 199 .draw(&mut display) 170 200 .unwrap(); 171 201 172 - //Adding a y offset to the box location to fit inside the box 173 - Text::new( 174 - time_text.as_str(), 175 - ( 176 - time_box_rectangle_location.x + 8, 177 - time_box_rectangle_location.y + 16, 178 - ) 179 - .into(), 180 - character_style, 181 - ) 182 - .draw(&mut display) 183 - .unwrap(); 184 - 185 - let result = display 186 - .partial_update(time_bounds.try_into().unwrap()) 187 - .await; 188 - match result { 189 - Ok(_) => {} 190 - Err(_) => { 191 - info!("Error updating display"); 202 + let result = display 203 + .partial_update(time_bounds.try_into().unwrap()) 204 + .await; 205 + match result { 206 + Ok(_) => {} 207 + Err(_) => { 208 + info!("Error updating display"); 209 + } 192 210 } 193 211 } 194 - } 195 212 196 - //Manually triggered display events 213 + //Manually triggered display events 197 214 198 - if CHANGE_IMAGE.load(core::sync::atomic::Ordering::Relaxed) { 199 - let current_image = get_current_image(); 200 - let tga: Bmp<BinaryColor> = Bmp::from_slice(&current_image.image()).unwrap(); 201 - let image = Image::new(&tga, current_image.image_location()); 202 - //clear image location by writing a white rectangle over previous image location 203 - let clear_rectangle = Rectangle::new( 204 - current_image.previous().image_location(), 205 - Size::new(157, 101), 206 - ); 207 - clear_rectangle 208 - .into_styled(PrimitiveStyle::with_fill(BinaryColor::On)) 209 - .draw(&mut display) 210 - .unwrap(); 215 + if CHANGE_IMAGE.load(core::sync::atomic::Ordering::Relaxed) || first_run { 216 + let current_image = get_current_image(); 217 + let tga: Bmp<BinaryColor> = Bmp::from_slice(&current_image.image()).unwrap(); 218 + let image = Image::new(&tga, current_image.image_location()); 219 + //clear image location by writing a white rectangle over previous image location 220 + let clear_rectangle = Rectangle::new( 221 + current_image.previous().image_location(), 222 + Size::new(157, 101), 223 + ); 224 + clear_rectangle 225 + .into_styled(PrimitiveStyle::with_fill(BinaryColor::On)) 226 + .draw(&mut display) 227 + .unwrap(); 211 228 212 - let _ = image.draw(&mut display); 213 - //TODO need to look up the reginal area display 214 - let _ = display.update().await; 215 - CHANGE_IMAGE.store(false, core::sync::atomic::Ordering::Relaxed); 229 + let _ = image.draw(&mut display); 230 + //TODO need to look up the reginal area display 231 + let _ = display.update().await; 232 + CHANGE_IMAGE.store(false, core::sync::atomic::Ordering::Relaxed); 233 + } 234 + } else { 235 + if cycles_since_last_clear % 60 == 0 || first_run { 236 + let top_bounds = Rectangle::new(Point::new(0, 0), Size::new(WIDTH, 24)); 237 + top_bounds 238 + .into_styled( 239 + PrimitiveStyleBuilder::default() 240 + .stroke_color(BinaryColor::Off) 241 + .fill_color(BinaryColor::On) 242 + .stroke_width(1) 243 + .build(), 244 + ) 245 + .draw(&mut display) 246 + .unwrap(); 247 + 248 + let top_text: String<64> = easy_format::<64>(format_args!( 249 + "Wifi found: {}", 250 + WIFI_COUNT.load(core::sync::atomic::Ordering::Relaxed) 251 + )); 252 + 253 + Text::new(top_text.as_str(), Point::new(8, 16), character_style) 254 + .draw(&mut display) 255 + .unwrap(); 256 + 257 + let result = display.partial_update(top_bounds.try_into().unwrap()).await; 258 + match result { 259 + Ok(_) => {} 260 + Err(_) => { 261 + info!("Error updating display"); 262 + } 263 + } 264 + } 216 265 } 217 266 218 267 cycles_since_last_clear += 1;
+40 -5
src/main.rs
··· 5 5 #![no_std] 6 6 #![no_main] 7 7 use badge_display::display_image::DisplayImage; 8 - use badge_display::{run_the_display, CHANGE_IMAGE, CURRENT_IMAGE, RTC_TIME_STRING, WIFI_COUNT}; 8 + use badge_display::{ 9 + run_the_display, Screen, CHANGE_IMAGE, CURRENT_IMAGE, DISPLAY_CHANGED, RTC_TIME_STRING, 10 + SCREEN_TO_SHOW, WIFI_COUNT, 11 + }; 9 12 use core::fmt::Write; 10 13 use core::str::from_utf8; 11 14 use cyw43_driver::setup_cyw43; ··· 81 84 let cs = Output::new(cs, Level::High); 82 85 let busy = Input::new(busy, Pull::Up); 83 86 84 - let _btn_up = Input::new(p.PIN_15, Pull::Down); 85 - let _btn_down = Input::new(p.PIN_11, Pull::Down); 86 - let _btn_a = Input::new(p.PIN_12, Pull::Down); 87 + let btn_up = Input::new(p.PIN_15, Pull::Down); 88 + let btn_down = Input::new(p.PIN_11, Pull::Down); 89 + let btn_a = Input::new(p.PIN_12, Pull::Down); 87 90 let btn_b = Input::new(p.PIN_13, Pull::Down); 88 91 let btn_c = Input::new(p.PIN_14, Pull::Down); 89 92 ··· 281 284 continue; 282 285 } 283 286 287 + if btn_a.is_high() { 288 + info!("Button A pressed"); 289 + user_led.toggle(); 290 + Timer::after(Duration::from_millis(500)).await; 291 + current_cycle += 500; 292 + continue; 293 + } 294 + 295 + if btn_down.is_high() { 296 + info!("Button Down pressed"); 297 + SCREEN_TO_SHOW.lock(|screen| { 298 + screen.replace(Screen::WifiList); 299 + }); 300 + DISPLAY_CHANGED.store(true, core::sync::atomic::Ordering::Relaxed); 301 + Timer::after(Duration::from_millis(500)).await; 302 + current_cycle += 500; 303 + continue; 304 + } 305 + 306 + if btn_up.is_high() { 307 + info!("Button Up pressed"); 308 + SCREEN_TO_SHOW.lock(|screen| { 309 + screen.replace(Screen::Badge); 310 + }); 311 + DISPLAY_CHANGED.store(true, core::sync::atomic::Ordering::Relaxed); 312 + Timer::after(Duration::from_millis(500)).await; 313 + current_cycle += 500; 314 + continue; 315 + } 316 + 284 317 if btn_b.is_high() { 285 318 info!("Button B pressed"); 286 - user_led.toggle(); 319 + save.wifi_counted = 0; 320 + save.bssid.clear(); 321 + WIFI_COUNT.store(0, core::sync::atomic::Ordering::Relaxed); 287 322 Timer::after(Duration::from_millis(500)).await; 288 323 current_cycle += 500; 289 324 continue;