Now also exposing the humidity and temperature via web requests.

This commit is contained in:
Yvan 2025-03-17 14:49:58 +00:00
parent 76329ee8ae
commit ce15f3d373

View file

@ -1,4 +1,4 @@
//! Raspberry Pi Pico W meat thermometer //! scd40-rusty-pi: Raspberry Pi Pico W CO2 PPM Monitor with Web Interface
#![no_std] #![no_std]
#![no_main] #![no_main]
@ -66,22 +66,24 @@ async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'sta
// Shared state related code // Shared state related code
// an instance of this will be shared between the sensor read task and the web serve task // an instance of this will be shared between the sensor read task and the web serve task
struct CO2PPM { struct SCD40Data {
value : u16, co2ppm : u16,
humidity : f32,
temperature : f32,
} }
// some sort of mutex wrapper for a CO2PPM instance? // some sort of mutex wrapper for a SCD40Data instance?
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
struct SharedPPM(&'static Mutex<CriticalSectionRawMutex, CO2PPM>); struct SharedSCD40Data(&'static Mutex<CriticalSectionRawMutex, SCD40Data>);
// this then allows us to pass the shared CO2PPM into the picoserve interface // this then allows us to pass the shared SCD40Data into the picoserve interface
struct AppState { struct AppState {
shared_ppm : SharedPPM, shared_scd40data : SharedSCD40Data,
} }
// this is used in the route closure below to extract the SharedPPM struct from the app state // this is used in the route closure below to extract the SharedSCD40Data struct from the app state
impl picoserve::extract::FromRef<AppState> for SharedPPM { impl picoserve::extract::FromRef<AppState> for SharedSCD40Data {
fn from_ref(state: &AppState) -> Self { fn from_ref(state: &AppState) -> Self {
state.shared_ppm state.shared_scd40data
} }
} }
@ -108,10 +110,25 @@ impl AppWithStateBuilder for AppProps {
.route( .route(
"/data/co2", "/data/co2",
get( get(
|State(SharedPPM(co2ppm)): State<SharedPPM>| //newbie note: | delimits a closure |State(SharedSCD40Data(scd40data)): State<SharedSCD40Data>| //newbie note: | delimits a closure
async move { DebugValue( co2ppm.lock().await.value) } async move { DebugValue( scd40data.lock().await.co2ppm) }
), ),
) )
.route(
"/data/temperature",
get(
|State(SharedSCD40Data(scd40data)): State<SharedSCD40Data>|
async move { DebugValue( scd40data.lock().await.temperature) }
),
)
.route(
"/data/humidity",
get(
|State(SharedSCD40Data(scd40data)): State<SharedSCD40Data>|
async move { DebugValue( scd40data.lock().await.humidity) }
),
)
// TODO: is thre a way to genericise the above?
} }
} }
@ -152,13 +169,16 @@ async fn web_task(
#[embassy_executor::task] #[embassy_executor::task]
async fn read_co2( async fn read_co2(
mut scd: Scd4x<i2c::I2c<'static, I2C1, i2c::Blocking>, Delay>, mut scd: Scd4x<i2c::I2c<'static, I2C1, i2c::Blocking>, Delay>,
shared_ppm: SharedPPM shared_scd40data: SharedSCD40Data
) { ) {
log::info!("Enter sensor read loop"); log::info!("Enter sensor read loop");
loop { loop {
if scd.data_ready().unwrap() { if scd.data_ready().unwrap() {
let m = scd.read_measurement().unwrap(); let m = scd.read_measurement().unwrap();
shared_ppm.0.lock().await.value = m.co2; // TODO: is there a way to write this in one block/struct rather than three locks?
shared_scd40data.0.lock().await.co2ppm = m.co2;
shared_scd40data.0.lock().await.temperature = m.temperature;
shared_scd40data.0.lock().await.humidity = m.humidity;
log::info!( log::info!(
"CO2: {}\nHumidity: {}\nTemperature: {}", m.co2, m.humidity, m.temperature "CO2: {}\nHumidity: {}\nTemperature: {}", m.co2, m.humidity, m.temperature
) )
@ -276,9 +296,9 @@ async fn main(spawner: Spawner) {
//////////////////////////////////////////// ////////////////////////////////////////////
// Shared state required by our to main tasks (sensor reader, web server) // Shared state required by our to main tasks (sensor reader, web server)
let co2ppm = CO2PPM { value: 0 }; let scd40data = SCD40Data { co2ppm: 0, humidity: 0.0, temperature: 0.0 };
let shared_ppm = SharedPPM( let shared_scd40data = SharedSCD40Data(
make_static!(Mutex<CriticalSectionRawMutex, CO2PPM>, Mutex::new( co2ppm )), make_static!(Mutex<CriticalSectionRawMutex, SCD40Data>, Mutex::new( scd40data )),
); );
@ -287,6 +307,7 @@ async fn main(spawner: Spawner) {
log::info!("Starting I2C Comms with SCD40"); log::info!("Starting I2C Comms with SCD40");
// this code derived from: https://github.com/SvetlinZarev/libscd/blob/main/examples/embassy-scd4x/src/main.rs // this code derived from: https://github.com/SvetlinZarev/libscd/blob/main/examples/embassy-scd4x/src/main.rs
// TODO: how to make pins configurable?
let sda = p.PIN_26; let sda = p.PIN_26;
let scl = p.PIN_27; let scl = p.PIN_27;
let i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, Config::default()); let i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, Config::default());
@ -304,7 +325,7 @@ async fn main(spawner: Spawner) {
} }
spawner.must_spawn(read_co2(scd, shared_ppm)); spawner.must_spawn(read_co2(scd, shared_scd40data));
//////////////////////////////////////////// ////////////////////////////////////////////
// Set up the HTTP service // Set up the HTTP service
@ -329,7 +350,7 @@ async fn main(spawner: Spawner) {
stack, stack,
app, app,
config, config,
AppState{ shared_ppm }, AppState{ shared_scd40data },
)); ));
} }
} }