Skip to main content

pms_7003/
lib.rs

1#![no_std]
2
3use embedded_hal::serial::{Read, Write};
4use nb::block;
5use scroll::{Pread, Pwrite, BE};
6
7mod read_fsm;
8
9const CMD_FRAME_SIZE: usize = 7;
10const OUTPUT_FRAME_SIZE: usize = 32;
11const RESPONSE_FRAME_SIZE: usize = 8;
12const CHECKSUM_SIZE: usize = 2;
13
14type Response = [u8; RESPONSE_FRAME_SIZE];
15
16pub const MN1: u8 = 0x42;
17pub const MN2: u8 = 0x4D;
18const PASSIVE_MODE_RESPONSE: Response = [MN1, MN1, 0x00, 0x04, 0xE1, 0x00, 0x01, 0x74];
19const ACTIVE_MODE_RESPONSE: Response = [MN1, MN2, 0x00, 0x04, 0xE1, 0x01, 0x01, 0x75];
20const SLEEP_RESPONSE: Response = [MN1, MN2, 0x00, 0x04, 0xE4, 0x00, 0x01, 0x77];
21
22#[derive(Debug)]
23pub enum Error {
24    SendFailed,
25    ReadFailed,
26    ChecksumError,
27    IncorrectResponse,
28    NoResponse,
29}
30
31/// Sensor interface
32pub struct Pms7003Sensor<Serial>
33where
34    Serial: Read<u8> + Write<u8>,
35{
36    serial: Serial,
37}
38
39impl<Serial> Pms7003Sensor<Serial>
40where
41    Serial: Read<u8> + Write<u8>,
42{
43    /// Creates a new sensor instance
44    /// * `serial` - single object implementing embedded hal serial traits
45    pub fn new(mut serial: Serial) -> Self {
46        loop {
47            if serial.read().is_err() {
48                break;
49            }
50        }
51
52        Self { serial }
53    }
54
55    fn read_from_device<T: AsMut<[u8]>>(&mut self, mut buffer: T) -> Result<T, Error> {
56        use read_fsm::*;
57
58        let mut read = ReadStateMachine::new(buffer.as_mut(), 10);
59        loop {
60            match read.update(self.serial.read()) {
61                ReadStatus::Failed => return Err(Error::ReadFailed),
62                ReadStatus::Finished => return Ok(buffer),
63                ReadStatus::InProgress => {}
64            }
65        }
66    }
67
68    /// Reads sensor status. Blocks until status is available.
69    pub fn read(&mut self) -> Result<OutputFrame, Error> {
70        OutputFrame::from_buffer(&self.read_from_device([0_u8; OUTPUT_FRAME_SIZE])?)
71    }
72
73    /// Sleep mode. May fail because of incorrect reposnse because of race condition between response and air quality status
74    pub fn sleep(&mut self) -> Result<(), Error> {
75        self.send_cmd(&create_command(0xe4, 0))?;
76        self.receive_response(SLEEP_RESPONSE)
77    }
78
79    pub fn wake(&mut self) -> Result<(), Error> {
80        self.send_cmd(&create_command(0xe4, 1))
81    }
82
83    /// Passive mode - sensor reports air quality on request
84    pub fn passive(&mut self) -> Result<(), Error> {
85        self.send_cmd(&create_command(0xe1, 0))?;
86        self.receive_response(PASSIVE_MODE_RESPONSE)
87    }
88
89    /// Active mode - sensor reports air quality continuously
90    pub fn active(&mut self) -> Result<(), Error> {
91        self.send_cmd(&create_command(0xe1, 1))?;
92        self.receive_response(ACTIVE_MODE_RESPONSE)
93    }
94
95    /// Requests status in passive mode
96    pub fn request(&mut self) -> Result<(), Error> {
97        self.send_cmd(&create_command(0xe2, 0))
98    }
99
100    fn send_cmd(&mut self, cmd: &[u8]) -> Result<(), Error> {
101        for byte in cmd {
102            block!(self.serial.write(*byte)).map_err(|_| Error::SendFailed)?;
103        }
104        Ok(())
105    }
106
107    fn receive_response(&mut self, expected_response: Response) -> Result<(), Error> {
108        if self.read_from_device([0u8; RESPONSE_FRAME_SIZE])? != expected_response {
109            Err(Error::IncorrectResponse)
110        } else {
111            Ok(())
112        }
113    }
114}
115
116fn create_command(cmd: u8, data: u16) -> [u8; CMD_FRAME_SIZE] {
117    let mut buffer = [0_u8; CMD_FRAME_SIZE];
118    let mut offset = 0usize;
119
120    buffer.gwrite::<u8>(MN1, &mut offset).unwrap();
121    buffer.gwrite::<u8>(MN2, &mut offset).unwrap();
122    buffer.gwrite::<u8>(cmd, &mut offset).unwrap();
123    buffer.gwrite_with::<u16>(data, &mut offset, BE).unwrap();
124
125    let checksum = buffer
126        .iter()
127        .take(CMD_FRAME_SIZE - CHECKSUM_SIZE)
128        .map(|b| *b as u16)
129        .sum::<u16>();
130    buffer
131        .gwrite_with::<u16>(checksum, &mut offset, BE)
132        .unwrap();
133
134    buffer
135}
136
137/// Contains data reported by the sensor
138#[derive(Default, Debug)]
139pub struct OutputFrame {
140    pub start1: u8,
141    pub start2: u8,
142    pub frame_length: u16,
143    pub pm1_0: u16,
144    pub pm2_5: u16,
145    pub pm10: u16,
146    pub pm1_0_atm: u16,
147    pub pm2_5_atm: u16,
148    pub pm10_atm: u16,
149    pub beyond_0_3: u16,
150    pub beyond_0_5: u16,
151    pub beyond_1_0: u16,
152    pub beyond_2_5: u16,
153    pub beyond_5_0: u16,
154    pub beyond_10_0: u16,
155    pub reserved: u16,
156    pub check: u16,
157}
158
159impl OutputFrame {
160    pub fn from_buffer(buffer: &[u8; OUTPUT_FRAME_SIZE]) -> Result<Self, Error> {
161        let sum: usize = buffer
162            .iter()
163            .take(OUTPUT_FRAME_SIZE - CHECKSUM_SIZE)
164            .map(|e| *e as usize)
165            .sum();
166
167        let mut frame = OutputFrame::default();
168        let mut offset = 0usize;
169
170        frame.start1 = buffer.gread::<u8>(&mut offset).unwrap();
171        frame.start2 = buffer.gread::<u8>(&mut offset).unwrap();
172        frame.frame_length = buffer.gread_with::<u16>(&mut offset, BE).unwrap();
173        frame.pm1_0 = buffer.gread_with::<u16>(&mut offset, BE).unwrap();
174        frame.pm2_5 = buffer.gread_with::<u16>(&mut offset, BE).unwrap();
175        frame.pm10 = buffer.gread_with::<u16>(&mut offset, BE).unwrap();
176        frame.pm1_0_atm = buffer.gread_with::<u16>(&mut offset, BE).unwrap();
177        frame.pm2_5_atm = buffer.gread_with::<u16>(&mut offset, BE).unwrap();
178        frame.pm10_atm = buffer.gread_with::<u16>(&mut offset, BE).unwrap();
179        frame.beyond_0_3 = buffer.gread_with::<u16>(&mut offset, BE).unwrap();
180        frame.beyond_0_5 = buffer.gread_with::<u16>(&mut offset, BE).unwrap();
181        frame.beyond_1_0 = buffer.gread_with::<u16>(&mut offset, BE).unwrap();
182        frame.beyond_2_5 = buffer.gread_with::<u16>(&mut offset, BE).unwrap();
183        frame.beyond_5_0 = buffer.gread_with::<u16>(&mut offset, BE).unwrap();
184        frame.beyond_10_0 = buffer.gread_with::<u16>(&mut offset, BE).unwrap();
185        frame.reserved = buffer.gread_with::<u16>(&mut offset, BE).unwrap();
186        frame.check = buffer.gread_with::<u16>(&mut offset, BE).unwrap();
187
188        if sum != frame.check as usize {
189            return Err(Error::ChecksumError);
190        }
191
192        Ok(frame)
193    }
194}
195
196impl<TX, RX> Pms7003Sensor<Wrapper<TX, RX>>
197where
198    TX: Write<u8>,
199    RX: Read<u8>,
200{
201    /// Creates a new sensor instance
202    /// * `tx` - embedded hal serial Write
203    /// * `rx` - embedded hal serial Read
204    pub fn new_tx_rx(tx: TX, rx: RX) -> Self {
205        Self::new(Wrapper(tx, rx))
206    }
207}
208
209/// Combines two serial traits objects into one
210pub struct Wrapper<TX, RX>(TX, RX)
211where
212    TX: Write<u8>,
213    RX: Read<u8>;
214
215impl<TX, RX> Read<u8> for Wrapper<TX, RX>
216where
217    TX: Write<u8>,
218    RX: Read<u8>,
219{
220    type Error = RX::Error;
221
222    fn read(&mut self) -> nb::Result<u8, Self::Error> {
223        self.1.read()
224    }
225}
226
227impl<TX, RX> Write<u8> for Wrapper<TX, RX>
228where
229    TX: Write<u8>,
230    RX: Read<u8>,
231{
232    type Error = TX::Error;
233
234    fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
235        self.0.write(word)
236    }
237
238    fn flush(&mut self) -> nb::Result<(), Self::Error> {
239        self.0.flush()
240    }
241}