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