Just cleaning up the code, no functional changes.

This commit is contained in:
Yvan 2025-03-17 12:56:49 +00:00
parent a77576b26a
commit 07959bf1b7

View file

@ -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<PIO0>;
});
bind_interrupts!(struct UsbIrqs {
USBCTRL_IRQ => USBInterruptHandler<USB>;
});
// 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<W: Write>(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<CriticalSectionRawMutex, CO2PPM>);
// 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<AppState> 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<AppState>;
fn build_app(self) -> picoserve::Router<Self::PathRouter, Self::State> {
//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<SharedPPM>| async move { DebugValue( co2ppm.lock().await.co2ppm ) }),
get(
|State(SharedPPM(co2ppm)): State<SharedPPM>| //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<i2c::I2c<'static, I2C1, i2c::Blocking>, 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<cyw43::State> = 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;
////////////////////////////////////////////
// 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<CriticalSectionRawMutex, CO2PPM>, 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<CriticalSectionRawMutex, CO2PPM>, 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>, 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<i2c::I2c<'static, I2C1, i2c::Blocking>, 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;
}
}