A personal rust firmware for the Badger 2040 W
at e067a8bbb321e9ba185c2ecad08becedf7d7eeec 515 lines 18 kB view raw
1#![no_std] 2#![no_main] 3 4use badge_display::display_image::DisplayImage; 5use badge_display::{ 6 CHANGE_IMAGE, CURRENT_IMAGE, DISPLAY_CHANGED, FORCE_SCREEN_REFRESH, RECENT_WIFI_NETWORKS, 7 RTC_TIME_STRING, RecentWifiNetworksVec, SCREEN_TO_SHOW, Screen, WIFI_COUNT, run_the_display, 8}; 9use core::cell::RefCell; 10use core::fmt::Write; 11use core::str::from_utf8; 12use cyw43::JoinOptions; 13use cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi}; 14use defmt::info; 15use defmt::*; 16use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice; 17use embassy_executor::Spawner; 18use embassy_net::StackResources; 19use embassy_net::dns::DnsSocket; 20use embassy_net::tcp::client::{TcpClient, TcpClientState}; 21use embassy_rp::clocks::RoscRng; 22use embassy_rp::flash::Async; 23use embassy_rp::gpio::Input; 24use embassy_rp::i2c::I2c; 25use embassy_rp::peripherals::{DMA_CH0, I2C0, PIO0, SPI0}; 26use embassy_rp::pio::{InterruptHandler, Pio}; 27use embassy_rp::rtc::{DateTime, DayOfWeek}; 28use embassy_rp::spi::Spi; 29use embassy_rp::spi::{self}; 30use embassy_rp::{bind_interrupts, gpio, i2c}; 31use embassy_sync::blocking_mutex::NoopMutex; 32use embassy_sync::blocking_mutex::raw::NoopRawMutex; 33use embassy_sync::mutex::Mutex; 34use embassy_time::{Duration, Timer}; 35use env::env_value; 36use gpio::{Level, Output, Pull}; 37use heapless::{String, Vec}; 38use helpers::easy_format; 39use pcf85063a::PCF85063; 40use reqwless::client::{HttpClient, TlsConfig, TlsVerify}; 41use reqwless::request::Method; 42use save::{Save, read_postcard_from_flash, save_postcard_to_flash}; 43use serde::Deserialize; 44use static_cell::StaticCell; 45use temp_sensor::run_the_temp_sensor; 46use {defmt_rtt as _, panic_probe as _}; 47 48mod badge_display; 49mod env; 50mod helpers; 51mod pcf85063a; 52mod save; 53mod temp_sensor; 54 55type Spi0Bus = Mutex<NoopRawMutex, Spi<'static, SPI0, spi::Async>>; 56type I2c0Bus = NoopMutex<RefCell<I2c<'static, I2C0, i2c::Blocking>>>; 57 58const BSSID_LEN: usize = 1_000; 59const ADDR_OFFSET: u32 = 0x100000; 60const SAVE_OFFSET: u32 = 0x00; 61 62const FLASH_SIZE: usize = 2 * 1024 * 1024; 63 64bind_interrupts!(struct Irqs { 65 PIO0_IRQ_0 => InterruptHandler<PIO0>; 66}); 67 68#[embassy_executor::main] 69async fn main(spawner: Spawner) { 70 let p = embassy_rp::init(Default::default()); 71 let mut user_led = Output::new(p.PIN_22, Level::High); 72 user_led.set_high(); 73 74 //Wifi driver and cyw43 setup 75 let fw = include_bytes!("../cyw43-firmware/43439A0.bin"); 76 let clm = include_bytes!("../cyw43-firmware/43439A0_clm.bin"); 77 78 let pwr = Output::new(p.PIN_23, Level::Low); 79 let cs = Output::new(p.PIN_25, Level::High); 80 let mut pio = Pio::new(p.PIO0, Irqs); 81 let spi = PioSpi::new( 82 &mut pio.common, 83 pio.sm0, 84 DEFAULT_CLOCK_DIVIDER, 85 pio.irq0, 86 cs, 87 p.PIN_24, 88 p.PIN_29, 89 p.DMA_CH0, 90 ); 91 static STATE: StaticCell<cyw43::State> = StaticCell::new(); 92 let state = STATE.init(cyw43::State::new()); 93 let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; 94 spawner.must_spawn(cyw43_task(runner)); 95 96 control.init(clm).await; 97 control 98 .set_power_management(cyw43::PowerManagementMode::PowerSave) 99 .await; 100 101 let miso = p.PIN_16; 102 let mosi = p.PIN_19; 103 let clk = p.PIN_18; 104 let dc = p.PIN_20; 105 let cs = p.PIN_17; 106 let busy = p.PIN_26; 107 let reset = p.PIN_21; 108 let power = p.PIN_10; 109 110 let reset = Output::new(reset, Level::Low); 111 let mut power = Output::new(power, Level::Low); 112 power.set_high(); 113 114 let dc = Output::new(dc, Level::Low); 115 let cs = Output::new(cs, Level::High); 116 let busy = Input::new(busy, Pull::Up); 117 118 let btn_up = Input::new(p.PIN_15, Pull::Down); 119 let btn_down = Input::new(p.PIN_11, Pull::Down); 120 let btn_a = Input::new(p.PIN_12, Pull::Down); 121 let btn_b = Input::new(p.PIN_13, Pull::Down); 122 let btn_c = Input::new(p.PIN_14, Pull::Down); 123 124 let spi = Spi::new( 125 p.SPI0, 126 clk, 127 mosi, 128 miso, 129 p.DMA_CH1, 130 p.DMA_CH2, 131 spi::Config::default(), 132 ); 133 134 //SPI Bus setup to run the e-ink display 135 static SPI_BUS: StaticCell<Spi0Bus> = StaticCell::new(); 136 let spi_bus = SPI_BUS.init(Mutex::new(spi)); 137 138 info!("led on!"); 139 // control.gpio_set(0, true).await; 140 141 //wifi setup 142 let mut rng = RoscRng; 143 144 let config = embassy_net::Config::dhcpv4(Default::default()); 145 let seed = rng.next_u64(); 146 147 // Init network stack 148 static RESOURCES: StaticCell<StackResources<5>> = StaticCell::new(); 149 let (stack, runner) = embassy_net::new( 150 net_device, 151 config, 152 RESOURCES.init(StackResources::new()), 153 seed, 154 ); 155 156 //rtc setup 157 let mut rtc = embassy_rp::rtc::Rtc::new(p.RTC); 158 159 spawner.must_spawn(net_task(runner)); 160 //Attempt to connect to wifi to get RTC time loop for 2 minutes 161 let mut wifi_connection_attempts = 0; 162 let mut connected_to_wifi = false; 163 164 let wifi_ssid = env_value("WIFI_SSID"); 165 let wifi_password = env_value("WIFI_PASSWORD"); 166 while wifi_connection_attempts < 30 { 167 match control 168 .join(wifi_ssid, JoinOptions::new(wifi_password.as_bytes())) 169 .await 170 { 171 Ok(_) => { 172 connected_to_wifi = true; 173 info!("join successful"); 174 break; 175 } 176 Err(err) => { 177 info!("join failed with status={}", err.status); 178 } 179 } 180 Timer::after(Duration::from_secs(1)).await; 181 wifi_connection_attempts += 1; 182 } 183 184 let mut time_was_set = false; 185 if connected_to_wifi { 186 info!("waiting for DHCP..."); 187 while !stack.is_config_up() { 188 Timer::after_millis(100).await; 189 } 190 info!("DHCP is now up!"); 191 192 info!("waiting for link up..."); 193 while !stack.is_link_up() { 194 Timer::after_millis(500).await; 195 } 196 info!("Link is up!"); 197 198 info!("waiting for stack to be up..."); 199 stack.wait_config_up().await; 200 info!("Stack is up!"); 201 202 //RTC Web request 203 let mut rx_buffer = [0; 8192]; 204 let mut tls_read_buffer = [0; 16640]; 205 let mut tls_write_buffer = [0; 16640]; 206 let client_state = TcpClientState::<1, 1024, 1024>::new(); 207 let tcp_client = TcpClient::new(stack, &client_state); 208 let dns_client = DnsSocket::new(stack); 209 let tls_config = TlsConfig::new( 210 seed, 211 &mut tls_read_buffer, 212 &mut tls_write_buffer, 213 TlsVerify::None, 214 ); 215 // let mut http_client = HttpClient::new(&tcp_client, &dns_client); 216 let mut http_client = HttpClient::new_with_tls(&tcp_client, &dns_client, tls_config); 217 218 let url = env_value("TIME_API"); 219 info!("connecting to {}", &url); 220 221 //If the call goes through set the rtc 222 match http_client.request(Method::GET, &url).await { 223 Ok(mut request) => { 224 let response = match request.send(&mut rx_buffer).await { 225 Ok(resp) => resp, 226 Err(e) => { 227 error!("Failed to send HTTP request: {:?}", e); 228 // error!("Failed to send HTTP request"); 229 return; // handle the error; 230 } 231 }; 232 233 let body = match from_utf8(response.body().read_to_end().await.unwrap()) { 234 Ok(b) => b, 235 Err(_e) => { 236 error!("Failed to read response body"); 237 return; // handle the error 238 } 239 }; 240 info!("Response body: {:?}", &body); 241 242 let bytes = body.as_bytes(); 243 match serde_json_core::de::from_slice::<TimeApiResponse>(bytes) { 244 Ok((output, _used)) => { 245 //Deadlines am i right? 246 info!("Datetime: {:?}", output.datetime); 247 //split at T 248 let datetime = output.datetime.split('T').collect::<Vec<&str, 2>>(); 249 //split at - 250 let date = datetime[0].split('-').collect::<Vec<&str, 3>>(); 251 let year = date[0].parse::<u16>().unwrap(); 252 let month = date[1].parse::<u8>().unwrap(); 253 let day = date[2].parse::<u8>().unwrap(); 254 //split at : 255 let time = datetime[1].split(':').collect::<Vec<&str, 4>>(); 256 let hour = time[0].parse::<u8>().unwrap(); 257 let minute = time[1].parse::<u8>().unwrap(); 258 //split at . 259 let second_split = time[2].split('.').collect::<Vec<&str, 2>>(); 260 let second = second_split[0].parse::<f64>().unwrap(); 261 let rtc_time = DateTime { 262 year: year, 263 month: month, 264 day: day, 265 day_of_week: match output.day_of_week { 266 0 => DayOfWeek::Sunday, 267 1 => DayOfWeek::Monday, 268 2 => DayOfWeek::Tuesday, 269 3 => DayOfWeek::Wednesday, 270 4 => DayOfWeek::Thursday, 271 5 => DayOfWeek::Friday, 272 6 => DayOfWeek::Saturday, 273 _ => DayOfWeek::Sunday, 274 }, 275 hour, 276 minute, 277 second: second as u8, 278 }; 279 rtc.set_datetime(rtc_time).unwrap(); 280 time_was_set = true; 281 let _ = control.leave().await; 282 } 283 Err(_e) => { 284 error!("Failed to parse response body"); 285 // return; // handle the error 286 } 287 } 288 } 289 Err(e) => { 290 error!("Failed to make HTTP request: {:?}", e); 291 // return; // handle the error 292 } 293 }; 294 } 295 296 //Set up saving 297 let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH3); 298 let mut save: Save = read_postcard_from_flash(ADDR_OFFSET, &mut flash, SAVE_OFFSET).unwrap(); 299 WIFI_COUNT.store(save.wifi_counted, core::sync::atomic::Ordering::Relaxed); 300 301 //Setup i2c bus 302 let config = embassy_rp::i2c::Config::default(); 303 let i2c = i2c::I2c::new_blocking(p.I2C0, p.PIN_5, p.PIN_4, config); 304 static I2C_BUS: StaticCell<I2c0Bus> = StaticCell::new(); 305 let i2c_bus = NoopMutex::new(RefCell::new(i2c)); 306 let i2c_bus = I2C_BUS.init(i2c_bus); 307 308 let i2c_dev = I2cDevice::new(i2c_bus); 309 let mut rtc_device = PCF85063::new(i2c_dev); 310 311 //Task spawning 312 spawner.must_spawn(run_the_temp_sensor(i2c_bus)); 313 spawner.must_spawn(run_the_display(spi_bus, cs, dc, busy, reset)); 314 315 //Input loop 316 let cycle = Duration::from_millis(100); 317 let mut current_cycle = 0; 318 let mut time_to_scan = true; 319 //5 minutes(ish) idk it's late and my math is so bad rn 320 let reset_cycle = 3_000; 321 //Turn off led to signify that the badge is ready 322 user_led.set_low(); 323 324 loop { 325 //Change Image Button 326 if btn_c.is_high() { 327 info!("Button C pressed"); 328 let current_image = CURRENT_IMAGE.load(core::sync::atomic::Ordering::Relaxed); 329 let new_image = DisplayImage::from_u8(current_image).unwrap().next(); 330 CURRENT_IMAGE.store(new_image.as_u8(), core::sync::atomic::Ordering::Relaxed); 331 CHANGE_IMAGE.store(true, core::sync::atomic::Ordering::Relaxed); 332 Timer::after(Duration::from_millis(500)).await; 333 continue; 334 } 335 336 if btn_a.is_high() { 337 println!("{:?}", current_cycle); 338 info!("Button A pressed"); 339 user_led.toggle(); 340 Timer::after(Duration::from_millis(500)).await; 341 continue; 342 } 343 344 if btn_down.is_high() { 345 info!("Button Down pressed"); 346 SCREEN_TO_SHOW.lock(|screen| { 347 screen.replace(Screen::WifiList); 348 }); 349 DISPLAY_CHANGED.store(true, core::sync::atomic::Ordering::Relaxed); 350 Timer::after(Duration::from_millis(500)).await; 351 continue; 352 } 353 354 if btn_up.is_high() { 355 info!("Button Up pressed"); 356 SCREEN_TO_SHOW.lock(|screen| { 357 screen.replace(Screen::Badge); 358 }); 359 DISPLAY_CHANGED.store(true, core::sync::atomic::Ordering::Relaxed); 360 Timer::after(Duration::from_millis(500)).await; 361 continue; 362 } 363 364 if btn_b.is_high() { 365 info!("Button B pressed"); 366 367 SCREEN_TO_SHOW.lock(|screen| { 368 if *screen.borrow() == Screen::Badge { 369 //IF on badge screen and b pressed reset wifi count 370 save.wifi_counted = 0; 371 save.bssid.clear(); 372 WIFI_COUNT.store(0, core::sync::atomic::Ordering::Relaxed); 373 current_cycle = 0; 374 } 375 }); 376 377 let mut recent_networks = RecentWifiNetworksVec::new(); 378 let mut scanner = control.scan(Default::default()).await; 379 380 while let Some(bss) = scanner.next().await { 381 process_bssid(bss.bssid, &mut save.wifi_counted, &mut save.bssid); 382 if recent_networks.len() < 8 { 383 let possible_ssid = core::str::from_utf8(&bss.ssid); 384 match possible_ssid { 385 Ok(ssid) => { 386 let removed_zeros = ssid.trim_end_matches(char::from(0)); 387 let ssid_string: String<32> = 388 easy_format::<32>(format_args!("{}", removed_zeros)); 389 390 if recent_networks.contains(&ssid_string) { 391 continue; 392 } 393 if ssid_string != "" { 394 let _ = recent_networks.push(ssid_string); 395 } 396 } 397 Err(_) => { 398 continue; 399 } 400 } 401 } 402 } 403 RECENT_WIFI_NETWORKS.lock(|recent_networks_vec| { 404 recent_networks_vec.replace(recent_networks); 405 }); 406 407 FORCE_SCREEN_REFRESH.store(true, core::sync::atomic::Ordering::Relaxed); 408 Timer::after(Duration::from_millis(500)).await; 409 410 continue; 411 } 412 413 if time_was_set { 414 let now = rtc.now(); 415 match now { 416 Ok(time) => set_display_time(time), 417 Err(_) => { 418 info!("Error getting time"); 419 } 420 } 421 } else { 422 RTC_TIME_STRING.lock(|rtc_time_string| { 423 rtc_time_string.borrow_mut().clear(); 424 rtc_time_string.borrow_mut().push_str("No Wifi").unwrap(); 425 }); 426 } 427 if time_to_scan { 428 info!("Scanning for wifi networks"); 429 time_to_scan = false; 430 let mut scanner = control.scan(Default::default()).await; 431 while let Some(bss) = scanner.next().await { 432 process_bssid(bss.bssid, &mut save.wifi_counted, &mut save.bssid); 433 } 434 WIFI_COUNT.store(save.wifi_counted, core::sync::atomic::Ordering::Relaxed); 435 save_postcard_to_flash(ADDR_OFFSET, &mut flash, SAVE_OFFSET, &save).unwrap(); 436 info!("wifi_counted: {}", save.wifi_counted); 437 } 438 if current_cycle >= reset_cycle { 439 current_cycle = 0; 440 time_to_scan = true; 441 } 442 current_cycle += 1; 443 Timer::after(cycle).await; 444 } 445} 446 447fn set_display_time(time: DateTime) { 448 let mut am = true; 449 let twelve_hour = if time.hour > 12 { 450 am = false; 451 time.hour - 12 452 } else if time.hour == 0 { 453 12 454 } else { 455 time.hour 456 }; 457 458 let am_pm = if am { "AM" } else { "PM" }; 459 460 let formatted_time = easy_format::<8>(format_args!( 461 "{:02}:{:02} {}", 462 twelve_hour, time.minute, am_pm 463 )); 464 465 RTC_TIME_STRING.lock(|rtc_time_string| { 466 rtc_time_string.borrow_mut().clear(); 467 rtc_time_string 468 .borrow_mut() 469 .push_str(formatted_time.as_str()) 470 .unwrap(); 471 }); 472} 473 474#[embassy_executor::task] 475async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! { 476 runner.run().await 477} 478 479#[embassy_executor::task] 480async fn cyw43_task( 481 runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>, 482) -> ! { 483 runner.run().await 484} 485 486#[derive(Deserialize)] 487struct TimeApiResponse<'a> { 488 datetime: &'a str, 489 day_of_week: u8, 490} 491 492fn process_bssid(bssid: [u8; 6], wifi_counted: &mut u32, bssids: &mut Vec<String<17>, BSSID_LEN>) { 493 let bssid_str = format_bssid(bssid); 494 if !bssids.contains(&bssid_str) { 495 *wifi_counted += 1; 496 WIFI_COUNT.store(*wifi_counted, core::sync::atomic::Ordering::Relaxed); 497 // info!("bssid: {:x}", bssid_str); 498 let result = bssids.push(bssid_str); 499 if result.is_err() { 500 info!("bssid list full"); 501 bssids.clear(); 502 } 503 } 504} 505 506fn format_bssid(bssid: [u8; 6]) -> String<17> { 507 let mut s = String::new(); 508 for (i, byte) in bssid.iter().enumerate() { 509 if i != 0 { 510 let _ = s.write_char(':'); 511 } 512 core::fmt::write(&mut s, format_args!("{:02x}", byte)).unwrap(); 513 } 514 s 515}