1#![no_std]
112#![warn(clippy::pedantic)]
113#![warn(clippy::cargo)]
114
115use core::error::Error;
116use core::fmt::{Debug, Display, Formatter};
117use core::marker::PhantomData;
118#[cfg(feature = "sync")]
119use embedded_hal::delay::DelayNs;
120#[cfg(not(feature = "sync"))]
121use embedded_hal_async::delay::DelayNs;
122#[cfg(feature = "sync")]
123use embedded_io::{Read, ReadExactError, Write};
124#[cfg(not(feature = "sync"))]
125use embedded_io_async::{Read, ReadExactError, Write};
126use maybe_async::maybe_async;
127pub use message::{FirmwareVersion, Measurement};
128use message::{
129 Kind, Message, ParseError, Reporting, ReportingMode, Sleep, SleepMode, WorkingPeriod,
130 RECV_BUF_SIZE,
131};
132
133mod message;
134
135#[derive(Debug, Clone)]
140pub struct Config {
141 sleep_delay: u32,
142 measure_delay: u32,
143}
144
145impl Default for Config {
146 fn default() -> Self {
147 Self {
148 sleep_delay: 500,
149 measure_delay: 30_000,
150 }
151 }
152}
153
154impl Config {
155 #[must_use]
159 pub fn set_measure_delay(mut self, measure_delay: u32) -> Self {
160 self.measure_delay = measure_delay;
161 self
162 }
163
164 #[must_use]
167 pub fn set_sleep_delay(mut self, sleep_delay: u32) -> Self {
168 self.sleep_delay = sleep_delay;
169 self
170 }
171}
172
173pub enum SDS011Error<E> {
175 ParseError(ParseError),
177 ReadError(E),
179 WriteError(E),
181 UnexpectedEof,
183 UnexpectedType,
185 OperationFailed,
187 Invalid,
189}
190
191impl<E> Display for SDS011Error<E> {
192 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
193 match self {
194 SDS011Error::ParseError(e) => {
195 f.write_fmt(format_args!("message could not be decoded: {e}"))
196 }
197 SDS011Error::ReadError(_) => f.write_str("serial read error"),
198 SDS011Error::WriteError(_) => f.write_str("serial write error"),
199 SDS011Error::UnexpectedEof => f.write_str("unexpected EOF"),
200 SDS011Error::UnexpectedType => f.write_str("unexpected message type"),
201 SDS011Error::OperationFailed => f.write_str("requested operation failed"),
202 SDS011Error::Invalid => f.write_str("given parameters were invalid"),
203 }
204 }
205}
206
207impl<E> Debug for SDS011Error<E> {
208 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
209 Display::fmt(self, f)
210 }
211}
212
213impl<E> Error for SDS011Error<E> {}
214
215pub mod sensor_state {
216 mod private {
217 pub trait Sealed {}
218 }
219
220 pub trait SensorState: private::Sealed {}
225
226 pub struct Periodic;
228 impl private::Sealed for Periodic {}
229 impl SensorState for Periodic {}
230
231 pub struct Polling;
233 impl private::Sealed for Polling {}
234 impl SensorState for Polling {}
235
236 pub struct Uninitialized;
238 impl private::Sealed for Uninitialized {}
239 impl SensorState for Uninitialized {}
240}
241
242pub use sensor_state::SensorState;
243use sensor_state::{Periodic, Polling, Uninitialized};
244
245pub struct SDS011<RW, S: SensorState> {
251 serial: RW,
252 config: Config,
253 sensor_id: Option<u16>,
254 firmware: Option<FirmwareVersion>,
255 _state: PhantomData<S>,
256}
257
258impl<RW, S> SDS011<RW, S>
259where
260 RW: Read + Write,
261 S: SensorState,
262{
263 #[maybe_async]
264 async fn get_reply(&mut self) -> Result<Message, SDS011Error<RW::Error>> {
265 let mut buf = [0u8; RECV_BUF_SIZE];
266
267 match self.serial.read_exact(&mut buf).await {
268 Ok(()) => Message::parse_reply(&buf).map_err(SDS011Error::ParseError),
269 Err(ReadExactError::UnexpectedEof) => Err(SDS011Error::UnexpectedEof),
270 Err(ReadExactError::Other(e)) => Err(SDS011Error::ReadError(e)),
271 }
272 }
273
274 #[maybe_async]
275 async fn send_message(&mut self, kind: Kind) -> Result<(), SDS011Error<RW::Error>> {
276 let msg = Message::new(kind, self.sensor_id);
277 let out_buf = msg.create_query();
278
279 self.serial
280 .write_all(&out_buf)
281 .await
282 .map_err(SDS011Error::WriteError)
283 }
284
285 #[maybe_async]
286 async fn read_sensor(&mut self, query: bool) -> Result<Measurement, SDS011Error<RW::Error>> {
287 if query {
288 self.send_message(Kind::Query(None)).await?;
289 }
290
291 match self.get_reply().await?.kind {
292 Kind::Query(data) => Ok(data.expect("replies always contain data")),
293 _ => Err(SDS011Error::UnexpectedType),
294 }
295 }
296
297 #[maybe_async]
298 async fn get_firmware(&mut self) -> Result<(u16, FirmwareVersion), SDS011Error<RW::Error>> {
299 self.send_message(Kind::FWVersion(None)).await?;
300
301 let reply = self.get_reply().await?;
302 let id = reply.sensor_id.expect("replies always contain data");
303 match reply.kind {
304 Kind::FWVersion(data) => Ok((id, data.expect("replies always contain data"))),
305 _ => Err(SDS011Error::UnexpectedType),
306 }
307 }
308
309 #[maybe_async]
310 async fn _get_runmode(&mut self) -> Result<ReportingMode, SDS011Error<RW::Error>> {
311 let r = Reporting::new_query();
312 self.send_message(Kind::ReportingMode(r)).await?;
313
314 match self.get_reply().await?.kind {
315 Kind::ReportingMode(data) => Ok(data.mode()),
316 _ => Err(SDS011Error::UnexpectedType),
317 }
318 }
319
320 #[maybe_async]
321 async fn set_runmode_query(&mut self) -> Result<(), SDS011Error<RW::Error>> {
322 let r = Reporting::new_set(ReportingMode::Query);
323 self.send_message(Kind::ReportingMode(r)).await?;
324
325 match self.get_reply().await?.kind {
326 Kind::ReportingMode(r) => match r.mode() {
327 ReportingMode::Query => Ok(()),
328 ReportingMode::Active => Err(SDS011Error::OperationFailed),
329 },
330 _ => Err(SDS011Error::UnexpectedType),
331 }
332 }
333
334 #[maybe_async]
335 async fn set_runmode_active(&mut self) -> Result<(), SDS011Error<RW::Error>> {
336 let r = Reporting::new_set(ReportingMode::Active);
337 self.send_message(Kind::ReportingMode(r)).await?;
338
339 match self.get_reply().await?.kind {
340 Kind::ReportingMode(r) => match r.mode() {
341 ReportingMode::Active => Ok(()),
342 ReportingMode::Query => Err(SDS011Error::OperationFailed),
343 },
344 _ => Err(SDS011Error::UnexpectedType),
345 }
346 }
347
348 #[maybe_async]
349 async fn _get_period(&mut self) -> Result<u8, SDS011Error<RW::Error>> {
350 let w = WorkingPeriod::new_query();
351 self.send_message(Kind::WorkingPeriod(w)).await?;
352
353 match self.get_reply().await?.kind {
354 Kind::WorkingPeriod(data) => Ok(data.period()),
355 _ => Err(SDS011Error::UnexpectedType),
356 }
357 }
358
359 #[maybe_async]
360 async fn set_period(&mut self, minutes: u8) -> Result<(), SDS011Error<RW::Error>> {
361 let w = WorkingPeriod::new_set(minutes);
362 self.send_message(Kind::WorkingPeriod(w)).await?;
363
364 match self.get_reply().await?.kind {
365 Kind::WorkingPeriod(data) if data.period() == minutes => Ok(()),
366 Kind::WorkingPeriod(_) => Err(SDS011Error::OperationFailed),
367 _ => Err(SDS011Error::UnexpectedType),
368 }
369 }
370
371 #[maybe_async]
372 async fn _get_sleep(&mut self) -> Result<SleepMode, SDS011Error<RW::Error>> {
373 let s = Sleep::new_query();
374 self.send_message(Kind::Sleep(s)).await?;
375
376 match self.get_reply().await?.kind {
377 Kind::Sleep(data) => Ok(data.sleep_mode()),
378 _ => Err(SDS011Error::UnexpectedType),
379 }
380 }
381
382 #[maybe_async]
383 async fn sleep(&mut self) -> Result<(), SDS011Error<RW::Error>> {
384 let s = Sleep::new_set(SleepMode::Sleep);
385 self.send_message(Kind::Sleep(s)).await?;
386
387 match self.get_reply().await?.kind {
388 Kind::Sleep(s) => match s.sleep_mode() {
389 SleepMode::Sleep => Ok(()),
390 SleepMode::Work => Err(SDS011Error::OperationFailed),
391 },
392 _ => Err(SDS011Error::UnexpectedType),
393 }
394 }
395
396 #[maybe_async]
397 async fn wake(&mut self) -> Result<(), SDS011Error<RW::Error>> {
398 let s = Sleep::new_set(SleepMode::Work);
399 self.send_message(Kind::Sleep(s)).await?;
400
401 match self.get_reply().await?.kind {
402 Kind::Sleep(s) => match s.sleep_mode() {
403 SleepMode::Work => Ok(()),
404 SleepMode::Sleep => Err(SDS011Error::OperationFailed),
405 },
406 _ => Err(SDS011Error::UnexpectedType),
407 }
408 }
409}
410
411impl<RW> SDS011<RW, Uninitialized>
412where
413 RW: Read + Write,
414{
415 pub fn new(serial: RW, config: Config) -> Self {
418 SDS011::<RW, Uninitialized> {
419 serial,
420 config,
421 sensor_id: None,
422 firmware: None,
423 _state: PhantomData,
424 }
425 }
426
427 #[maybe_async]
433 pub async fn init<D: DelayNs>(
434 mut self,
435 delay: &mut D,
436 ) -> Result<SDS011<RW, Polling>, SDS011Error<RW::Error>> {
437 delay.delay_ms(self.config.sleep_delay).await;
439 self.wake().await?;
440
441 self.set_runmode_query().await?;
442
443 let (id, firmware) = self.get_firmware().await?;
445 self.sleep().await?;
446
447 Ok(SDS011::<RW, Polling> {
448 serial: self.serial,
449 config: self.config,
450 sensor_id: Some(id),
451 firmware: Some(firmware),
452 _state: PhantomData,
453 })
454 }
455}
456
457impl<RW> SDS011<RW, Periodic>
458where
459 RW: Read + Write,
460{
461 #[maybe_async]
469 pub async fn measure(&mut self) -> Result<Measurement, SDS011Error<RW::Error>> {
470 self.read_sensor(false).await
471 }
472
473 #[allow(clippy::missing_panics_doc)] pub fn id(&self) -> u16 {
476 self.sensor_id.expect("sensor is initialized")
477 }
478
479 #[allow(clippy::missing_panics_doc)] pub fn version(&self) -> FirmwareVersion {
482 self.firmware.clone().expect("sensor is initialized")
483 }
484}
485
486impl<RW> SDS011<RW, Polling>
487where
488 RW: Read + Write,
489{
490 #[maybe_async]
498 pub async fn measure<D: DelayNs>(
499 &mut self,
500 delay: &mut D,
501 ) -> Result<Measurement, SDS011Error<RW::Error>> {
502 delay.delay_ms(self.config.sleep_delay).await;
504 self.wake().await?;
505
506 _ = self.read_sensor(true).await?;
508 delay.delay_ms(self.config.measure_delay).await;
509 let res = self.read_sensor(true).await?;
510 self.sleep().await?;
511
512 Ok(res)
513 }
514
515 #[maybe_async]
523 pub async fn make_periodic<D: DelayNs>(
524 mut self,
525 delay: &mut D,
526 minutes: u8,
527 ) -> Result<SDS011<RW, Periodic>, SDS011Error<RW::Error>> {
528 if minutes > 30 {
529 return Err(SDS011Error::Invalid);
530 }
531
532 delay.delay_ms(self.config.sleep_delay).await;
534 self.wake().await?;
535
536 self.set_period(minutes).await?;
537 self.set_runmode_active().await?;
538
539 Ok(SDS011::<RW, Periodic> {
540 serial: self.serial,
541 config: self.config,
542 sensor_id: self.sensor_id,
543 firmware: self.firmware,
544 _state: PhantomData,
545 })
546 }
547
548 #[allow(clippy::missing_panics_doc)] pub fn id(&self) -> u16 {
551 self.sensor_id.expect("sensor is initialized")
552 }
553
554 #[allow(clippy::missing_panics_doc)] pub fn version(&self) -> FirmwareVersion {
557 self.firmware.clone().expect("sensor is initialized")
558 }
559}