Skip to content

Instantly share code, notes, and snippets.

@jianingy
Created February 21, 2017 02:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jianingy/2af23ee0591ecac4373b8834ef28dcf2 to your computer and use it in GitHub Desktop.
Save jianingy/2af23ee0591ecac4373b8834ef28dcf2 to your computer and use it in GitHub Desktop.
Read PM5003 PM2.5 Sensor
// Jianing Yang @ 19 Feb, 2017
use chrono::offset::local::Local;
use errors::*;
use errors::ErrorKind::*;
use models::AirQualityRecord;
use serial;
use serial::prelude::*;
use std::io::prelude::*;
use std::time::Duration;
use std::thread::sleep;
pub struct PlantowerG5 {
uart: serial::posix::TTYPort
}
impl PlantowerG5 {
pub fn open(uart_port: &str, timeout: Duration) -> Result<Self> {
let mut uart = serial::open(uart_port)?;
uart.reconfigure(&|us| {
us.set_baud_rate(serial::Baud9600)?;
us.set_char_size(serial::Bits8);
us.set_parity(serial::ParityNone);
us.set_stop_bits(serial::Stop1);
us.set_flow_control(serial::FlowNone);
Ok(())
})?;
uart.set_timeout(timeout)?;
Ok(Self { uart: uart })
}
pub fn read(&mut self) -> Result<AirQualityRecord> {
let bs = self.read_from_device()?;
self.translate(&bs)
}
fn read_from_device(&mut self) -> Result<Vec<u8>> {
let mut hd = [0u8; 64];
// searching for header 'B'
for _ in 0..64 {
self.uart.read_exact(&mut hd[0..1])?;
if hd[0] == 0x42 { break };
}
if hd[0] != 0x42 {
return Err(InvalidPacketData("cannot find header".to_string())
.into())
}
// searching for header 'M'
for _ in 0..64 {
self.uart.read_exact(&mut hd[1..2])?;
if hd[1] == 0x4d { break };
}
if hd[1] != 0x4d {
return Err(InvalidPacketData("cannot find header".to_string())
.into())
}
// read packet size
self.uart.read_exact(&mut hd[2..4])?;
let bs_len = ((hd[2] as usize) << 8) + hd[3] as usize;
// read packet, ("bs_len - 2" skips checksum bytes)
let mut bs: Vec<u8> = vec![0; bs_len - 2];
self.uart.read_exact(&mut bs)?;
// read checksum
let mut cs = [0u8; 2];
self.uart.read_exact(&mut cs)?;
let calc = hd.iter().map(|x| *x as u16).sum::<u16>()
+ bs.iter().map(|x| *x as u16).sum::<u16>();
if calc != ((cs[0] as u16) << 8) + cs[1] as u16 {
// checksum failed
return Err(InvalidPacketData("checksum failed".to_string())
.into())
}
Ok(bs)
}
fn translate(&self, bs: &[u8]) -> Result<AirQualityRecord> {
let vals = bs.chunks(2)
.map(|b| (((b[0] as u16) << 8) + b[1] as u16) as i32)
.collect::<Vec<i32>>();
Ok(AirQualityRecord {
pm010_std: vals[0],
pm025_std: vals[1],
pm100_std: vals[2],
pm010_atm: vals[3],
pm025_atm: vals[4],
pm100_atm: vals[5],
created_at: Local::now().naive_local()
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment