From d87215bebd8aaaf99d51c2853f2c0af33d6a7887 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Wed, 11 Jun 2025 13:26:29 +0700 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9F=20pmme-device:=20PM2.5=20is=20now?= =?UTF-8?q?=20being=20read=20and=20written=20to=20an=20ArcMutex?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pmme-device/rust-version/src/main.rs | 78 +++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 12 deletions(-) diff --git a/pmme-device/rust-version/src/main.rs b/pmme-device/rust-version/src/main.rs index f192b6e..eb430dc 100644 --- a/pmme-device/rust-version/src/main.rs +++ b/pmme-device/rust-version/src/main.rs @@ -22,9 +22,31 @@ use ssd1306::{I2CDisplayInterface, Ssd1306, size::*, rotation::*}; use ssd1306::mode::DisplayConfig; use std::thread; -use log::info; +use std::sync::{Arc, Mutex}; +use log::{info, warn}; -fn oled_task(i2c: I2cDriver) -> Result<()> { +#[derive(Debug, Clone, Default)] +pub struct Pms7003Data { + // PM concentrations (CF=1, standard particle) in μg/m³ + pub pm1_0_cf1: u16, + pub pm2_5_cf1: u16, + pub pm10_cf1: u16, + + // PM concentrations (atmospheric environment) in μg/m³ + pub pm1_0_atm: u16, + pub pm2_5_atm: u16, + pub pm10_atm: u16, + + // Particle counts (particles per 0.1L air) + pub particles_0_3um: u16, + pub particles_0_5um: u16, + pub particles_1_0um: u16, + pub particles_2_5um: u16, + pub particles_5_0um: u16, + pub particles_10um: u16, +} + +fn oled_task(pm_data: Arc>, i2c: I2cDriver) -> Result<()> { info!("Staring OLED Task"); let interface = I2CDisplayInterface::new(i2c); @@ -39,11 +61,12 @@ fn oled_task(i2c: I2cDriver) -> Result<()> { im.draw(&mut display).unwrap(); display.flush().map_err(|e| anyhow!("Could not flush display! {:?}", e))?; - FreeRtos::delay_ms(3000); + FreeRtos::delay_ms(2000); loop { + let current_pm_data = pm_data.lock().unwrap().clone(); display.clear(BinaryColor::Off).map_err(|e| anyhow!("Could not clear display! {:?}", e))?; Text::new( - "PM 2.5: 4", + &format!("PM 2.5: {}", current_pm_data.pm2_5_atm), Point::new(20, 40), MonoTextStyle::new(&FONT_10X20, BinaryColor::On), ).draw(&mut display).map_err(|e| anyhow!("Could not draw text! {:?}", e))?; @@ -52,6 +75,32 @@ fn oled_task(i2c: I2cDriver) -> Result<()> { } } +// u16::from_be_bytes is too verbose +fn get_u16(buf: &[u8]) -> u16 { ((buf[0] as u16) << 8) | buf[1] as u16 } + +fn read_pm_data(buf: &[u8; 32]) -> Option { + let checksum: u16 = buf.iter().take(29).copied().map(u16::from).sum(); + let csum_target = get_u16(&buf[30..32]); + if checksum != csum_target { + warn!("Checksum from PMS7003 READ does not match, skipping read"); + return None + } + Some(Pms7003Data { + pm1_0_cf1: get_u16(&buf[ 4..6 ]), + pm2_5_cf1: get_u16(&buf[ 6..8 ]), + pm10_cf1: get_u16(&buf[ 8..10]), + pm1_0_atm: get_u16(&buf[10..12]), + pm2_5_atm: get_u16(&buf[12..14]), + pm10_atm: get_u16(&buf[14..16]), + particles_0_3um: get_u16(&buf[16..18]), + particles_0_5um: get_u16(&buf[18..20]), + particles_1_0um: get_u16(&buf[20..22]), + particles_2_5um: get_u16(&buf[22..24]), + particles_5_0um: get_u16(&buf[24..26]), + particles_10um: get_u16(&buf[26..28]), + }) +} + fn send_pms7003_command(uart: &UartDriver, cmd: u8, datah: u8, datal: u8) -> Result<()> { let mut command = [0x42u8, 0x4D, cmd, datah, datal, 0, 0]; @@ -66,23 +115,23 @@ fn send_pms7003_command(uart: &UartDriver, cmd: u8, datah: u8, datal: u8) -> Res Ok(()) } -fn pm_sensor_task(uart: &UartDriver) -> Result<()> { +fn pm_sensor_task(pm_data: Arc>, uart: &UartDriver) -> Result<()> { info!("Staring UART PM Sensor Task"); send_pms7003_command(uart, 0xE1, 0x00, 0x00)?; - let mut buf = [0_u8; 32]; loop { send_pms7003_command(uart, 0xE2, 0x00, 0x00)?; + let mut buf = [0_u8; 32]; let len = uart.read(&mut buf, BLOCK)?; if len > 0 { - info!("Read some bytes! {}", len); - for i in 0..len { - info!("0x{:02x}", buf[i]); + if let Some(data) = read_pm_data(&buf) { + let mut pm_data = pm_data.lock().unwrap(); + *pm_data = data; } } else { - info!("No bytes read"); + // info!("No bytes read"); } uart.clear_rx()?; FreeRtos::delay_ms(5000); @@ -120,14 +169,19 @@ fn main() -> Result<()> { &config, )?; + let pm_data = Arc::new(Mutex::new(Pms7003Data::default())); + + let pm_data1 = Arc::clone(&pm_data); + let pm_data2 = Arc::clone(&pm_data); + let handles = vec! [ thread::spawn(move || { - if let Err(e) = oled_task(i2c) { + if let Err(e) = oled_task(pm_data1, i2c) { log::error!("OLED Task Error: {:?}", e); } }), thread::spawn(move || { - if let Err(e) = pm_sensor_task(&uart) { + if let Err(e) = pm_sensor_task(pm_data2, &uart) { log::error!("PM Sensor Task Error: {:?}", e); } }),