From 07959bf1b735f4acdc2a7f31dbaa148cb9736b62 Mon Sep 17 00:00:00 2001 From: Yvan Date: Mon, 17 Mar 2025 12:56:49 +0000 Subject: [PATCH] Just cleaning up the code, no functional changes. --- src/main.rs | 235 ++++++++++++++++++++-------------------------------- 1 file changed, 89 insertions(+), 146 deletions(-) diff --git a/src/main.rs b/src/main.rs index 8b79696..896beb1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,103 +14,81 @@ use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Level, Output}; use embassy_rp::i2c::{self, Config}; use embassy_rp::peripherals::{DMA_CH0, PIO0}; -use embassy_rp::peripherals::USB; use embassy_rp::peripherals::I2C1; +use embassy_rp::peripherals::USB; use embassy_rp::pio::{InterruptHandler, Pio}; -use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex}; use embassy_rp::usb::{Driver, InterruptHandler as USBInterruptHandler}; +use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex}; use embassy_time::{Duration, Timer, Delay}; use heapless::Vec; use libscd::synchronous::scd4x::Scd4x; -use picoserve::{ - make_static, - routing::{get, get_service, PathRouter}, - AppWithStateBuilder, AppRouter, - response::DebugValue -}; -use picoserve::response::File; use picoserve::extract::State; +use picoserve::{ make_static, routing::{get, get_service, PathRouter}, AppWithStateBuilder, AppRouter, response::DebugValue }; +use picoserve::response::File; use rand::RngCore; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; +// oh no, exposing my IoT WiFi credentials to the world ;) const WIFI_NETWORK: &str = "bendybogalow"; const WIFI_PASSWORD: &str = "parsnipcabbageonion"; const INDEX: &str = include_str!("html/index.html"); +const CSS: &str = include_str!("html/main.css"); +// TODO: I think these calls can be combined? bind_interrupts!(struct Irqs { PIO0_IRQ_0 => InterruptHandler; }); - bind_interrupts!(struct UsbIrqs { USBCTRL_IRQ => USBInterruptHandler; }); +// from example code #[embassy_executor::task] async fn logger_task(driver: Driver<'static, USB>) { embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver); } +// from example code #[embassy_executor::task] async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { runner.run().await } +// from example code #[embassy_executor::task] async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! { runner.run().await } -/* -async fn co2ppm_to_str { - let mut buff : [u8; 20] = [0u8; 20]; - unsafe { - CO2PPM.numtoa(10, &mut buff); - } - let buffstr: &str = core::str::from_utf8(&mut buff).unwrap(); - log::info!("CO2PPM from build_app: {}", buffstr); - "1234" // works - // buffstr // fails for the obvious reason of lifetime -} -*/ -/* -impl Content for AtomicU16 { - fn content_type(&self) -> &'static str { - "text/plain; charset=utf-8" - } - fn content_length(&self) -> usize { - 5; - } - async fn write_content(self, writer: W) -> Result<(), W::Error> { - "fooo".as_bytes().write_content(writer).await - } -} -*/ -/* -struct Number { - value: u16; -} -async fn get_number(Number { value }: Number) -> impl IntoResponse { - picoserve::response::DebugValue(value) -} -*/ +////////////////////////////////////////////////////////////////////// +// Shared state related code +// an instance of this will be shared between the sensor read task and the web serve task struct CO2PPM { - co2ppm : u16, + value : u16, } + +// some sort of mutex wrapper for a CO2PPM instance? #[derive(Clone, Copy)] struct SharedPPM(&'static Mutex); + +// this then allows us to pass the shared CO2PPM into the picoserve interface struct AppState { shared_ppm : SharedPPM, } +// this is used in the route closure below to extract the SharedPPM struct from the app state impl picoserve::extract::FromRef for SharedPPM { fn from_ref(state: &AppState) -> Self { state.shared_ppm } } -// picoserve HTTP code kicked off using: https://github.com/sammhicks/picoserve/blob/main/examples/embassy/hello_world/src/main.rs + +////////////////////////////////////////////////////////////////////// +// HTTP handler code + struct AppProps; impl AppWithStateBuilder for AppProps { @@ -118,27 +96,21 @@ impl AppWithStateBuilder for AppProps { type PathRouter = impl PathRouter; fn build_app(self) -> picoserve::Router { - //let Self { } = self; - - /*let mut buff : [u8; 20] = [0u8; 20]; - unsafe { - let _ = CO2PPM.numtoa(10, &mut buff); - } - let buffstr: &str = core::str::from_utf8(&mut buff).unwrap(); - log::info!("CO2PPM from build_app: {}", buffstr);*/ - picoserve::Router::new() .route( "/", - get_service(File::html(INDEX)) // .replace("%{CO2}%", CO2PPM.to_string()))) + get_service(File::html(INDEX)), ) .route( "/main.css", - get_service(File::css(include_str!("html/main.css"))) + get_service(File::css(CSS)), ) .route( "/data/co2", - get(|State(SharedPPM(co2ppm)): State| async move { DebugValue( co2ppm.lock().await.co2ppm ) }), + get( + |State(SharedPPM(co2ppm)): State| //newbie note: | delimits a closure + async move { DebugValue( co2ppm.lock().await.value) } + ), ) } } @@ -173,20 +145,46 @@ async fn web_task( .await } + +////////////////////////////////////////////////////////////////////// +// CO2 Sensor Code + +#[embassy_executor::task] +async fn read_co2( + mut scd: Scd4x, Delay>, + shared_ppm: SharedPPM +) { + log::info!("Enter sensor read loop"); + loop { + if scd.data_ready().unwrap() { + let m = scd.read_measurement().unwrap(); + shared_ppm.0.lock().await.value = m.co2; + log::info!( + "CO2: {}\nHumidity: {}\nTemperature: {}", m.co2, m.humidity, m.temperature + ) + } + Timer::after_secs(1).await; + } +} + + +////////////////////////////////////////////////////////////////////// +// main! + #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); let driver = Driver::new(p.USB, UsbIrqs); + spawner.spawn(logger_task(driver)).unwrap(); - let mut rng = RoscRng; + log::info!("main: entry"); // TODO: this was required to make log entries before the loop actually reach the TTY - so I am // guessing there is some setup happening in the background and wonder if there is a better way - // to wait for that than a sleep... + // to wait for TTY to be ready that than a sleep... (the sleep here is not in fact a 100% + // reliable way to ensure the TTY is ready it seems, but works most of the time.) Timer::after_secs(1).await; - log::info!("main: entry"); - let fw = include_bytes!("../cyw43-firmware/43439A0.bin"); let clm = include_bytes!("../cyw43-firmware/43439A0_clm.bin"); @@ -212,6 +210,10 @@ async fn main(spawner: Spawner) { p.DMA_CH0, ); + + //////////////////////////////////////////// + // get the WiFi up + log::info!("main: init wifi"); static STATE: StaticCell = StaticCell::new(); let state = STATE.init(cyw43::State::new()); @@ -224,7 +226,13 @@ async fn main(spawner: Spawner) { .set_power_management(cyw43::PowerManagementMode::PowerSave) .await; - //let config = Config::dhcpv4(Default::default()); + + //////////////////////////////////////////// + // get an IP sorted + + // if DHCP then use this code: + // let config = Config::dhcpv4(Default::default()); + // if static IP then use this code: log::info!("main: configure static IP"); let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 3, 15), 24), @@ -233,6 +241,7 @@ async fn main(spawner: Spawner) { }); // Generate random seed + let mut rng = RoscRng; let seed = rng.next_u64(); // Init network stack @@ -264,37 +273,43 @@ async fn main(spawner: Spawner) { log::info!("DHCP is now up!");*/ + //////////////////////////////////////////// + // Shared state required by our to main tasks (sensor reader, web server) + + let co2ppm = CO2PPM { value: 0 }; + let shared_ppm = SharedPPM( + make_static!(Mutex, Mutex::new( co2ppm )), + ); + + + //////////////////////////////////////////// + // Set up the SCD40 I2C sensor + log::info!("Starting I2C Comms with SCD40"); - Timer::after_secs(1).await; // this code derived from: https://github.com/SvetlinZarev/libscd/blob/main/examples/embassy-scd4x/src/main.rs let sda = p.PIN_26; let scl = p.PIN_27; let i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, Config::default()); log::info!("Initialise Scd4x"); - Timer::after_secs(1).await; let mut scd = Scd4x::new(i2c, Delay); - // When re-programming, the controller will be restarted, - // but not the sensor. We try to stop it in order to - // prevent the rest of the commands failing. + // When re-programming, the controller will be restarted, but not the sensor. We try to stop it + // in order to prevent the rest of the commands failing. log::info!("Stop periodic measurements"); - Timer::after_secs(1).await; _ = scd.stop_periodic_measurement(); log::info!("Sensor serial number: {:?}", scd.serial_number()); - Timer::after_secs(1).await; if let Err(e) = scd.start_periodic_measurement() { log::error!("Failed to start periodic measurement: {:?}", e ); + } - let co2ppm = 69; - let shared_ppm = SharedPPM( - make_static!(Mutex, Mutex::new(CO2PPM { co2ppm })), - ); spawner.must_spawn(read_co2(scd, shared_ppm)); + //////////////////////////////////////////// + // Set up the HTTP service + log::info!("Commence HTTP service"); - Timer::after_secs(1).await; let app = make_static!(AppRouter, AppProps.build_app()); @@ -317,77 +332,5 @@ async fn main(spawner: Spawner) { AppState{ shared_ppm }, )); } -/* - let mut rx_buffer = [0; 4096]; - let mut tx_buffer = [0; 4096]; - let mut buf = [0; 4096]; - - let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - - //let delay = Duration::from_secs(1); - log::info!("main: pre-loop"); - loop { - let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(Duration::from_secs(10))); - - control.gpio_set(0, false).await; - log::info!("Listening on TCP:00..."); - if let Err(e) = socket.accept(1234).await { - log::warn!("accept error: {:?}", e); - continue; - } - - log::info!("Received connection from {:?}", socket.remote_endpoint()); - - control.gpio_set(0, true).await; - - loop { - let n = match socket.read(&mut buf).await { - Ok(0) => { - log::warn!("read EOF"); - break; - } - Ok(n) => n, - Err(e) => { - log::warn!("read error: {:?}", e); - break; - } - }; - - log::info!("rxd {}", from_utf8(&buf[..n]).unwrap()); - - match socket.write_all(&buf[..n]).await { - Ok(()) => {} - Err(e) => { - log::warn!("write error: {:?}", e); - break; - } - }; - - } - - log::info!("LED off!"); - control.gpio_set(0, false).await; - } - */ -} - -#[embassy_executor::task] -async fn read_co2( - mut scd: Scd4x, Delay>, - shared_ppm: SharedPPM -) { - log::info!("Enter sensor read loop"); - Timer::after_secs(1).await; - loop { - if scd.data_ready().unwrap() { - let m = scd.read_measurement().unwrap(); - shared_ppm.0.lock().await.co2ppm = m.co2; - log::info!( - "CO2: {}\nHumidity: {}\nTemperature: {}", m.co2, m.humidity, m.temperature - ) - } - Timer::after_secs(1).await; - } }