Skip to main content

embassy_scd41_sensor/
scd41_rp.rs

1use crate::{SCD41Error, SCD41Response};
2use embassy_time::Duration;
3use embedded_hal_async::i2c::I2c;
4
5pub struct SCD41Sensor {
6    address: u8,
7    initialization_step: InitializationStep,
8    last_response: Option<SCD41Response>,
9    next_step_time: embassy_time::Instant,
10}
11
12impl SCD41Sensor {
13    pub fn new(address: u8) -> Self {
14        Self {
15            address,
16            initialization_step: InitializationStep::Initial,
17            last_response: None,
18            next_step_time: embassy_time::Instant::now(),
19        }
20    }
21
22    pub async fn read<'d, I: embassy_rp::i2c::Instance>(
23        &mut self,
24        i2c: &mut embassy_rp::i2c::I2c<'d, I, embassy_rp::i2c::Async>,
25    ) -> Result<SCD41Response, SCD41Error> {
26        let now = embassy_time::Instant::now();
27        if now < self.next_step_time {
28            return if let Some(response) = &self.last_response {
29                Ok(response.clone())
30            } else {
31                Err(SCD41Error::NoData)
32            };
33        }
34        match self.initialization_step {
35            InitializationStep::Initial => {
36                self.initialization_step = InitializationStep::StopMeasurement;
37                self.next_step_time = now + Duration::from_millis(1000);
38                Err(SCD41Error::NoData)
39            }
40            InitializationStep::StopMeasurement => {
41                self.initialization_step = InitializationStep::Reinit;
42                self.next_step_time = now + Duration::from_millis(1000);
43                self.i2c_write(i2c, &[0x3f, 0x86]).await?;
44                Err(SCD41Error::NoData)
45            }
46            InitializationStep::Reinit => {
47                self.initialization_step = InitializationStep::StartMeasurement;
48                self.next_step_time = now + Duration::from_millis(1000);
49                self.i2c_write(i2c, &[0x36, 0x46]).await?;
50                Err(SCD41Error::NoData)
51            }
52            InitializationStep::StartMeasurement => {
53                self.initialization_step = InitializationStep::ReadData;
54                self.next_step_time = now + Duration::from_millis(1000);
55                self.i2c_write(i2c, &[0x21, 0xb1]).await?;
56                Err(SCD41Error::NoData)
57            }
58            InitializationStep::ReadData => {
59                self.next_step_time = now + Duration::from_millis(5000);
60                let mut buf = [0u8; 9];
61                self.i2c_write_read(i2c, &[0xe4, 0xb8], &mut buf).await?;
62                if buf[1] == 0x0 {
63                    self.next_step_time = now + Duration::from_millis(1000);
64                    Err(SCD41Error::NoData)
65                } else {
66                    self.i2c_write_read(i2c, &[0xec, 0x05], &mut buf).await?;
67                    let delimiter = 0xffff as f32;
68                    let co2_ppm = i16::from_be_bytes([buf[0], buf[1]]) as f32;
69                    let temperature_data = i16::from_be_bytes([buf[3], buf[4]]) as f32;
70                    let temperature = -45f32 + 175f32 * temperature_data / delimiter;
71                    let humidity_data = i16::from_be_bytes([buf[6], buf[7]]) as f32;
72                    let humidity = 100f32 * (humidity_data / delimiter);
73                    let response = SCD41Response {
74                        co2: co2_ppm,
75                        temperature,
76                        humidity,
77                    };
78                    self.last_response = Some(response.clone());
79                    Ok(response)
80                }
81            }
82        }
83    }
84
85    async fn i2c_write_read<'d, I : embassy_rp::i2c::Instance>(&mut self,
86        i2c: &mut embassy_rp::i2c::I2c<'d, I, embassy_rp::i2c::Async>,
87        write: &[u8], read: &mut [u8]) -> Result<(), SCD41Error> {
88        match i2c.write_read(self.address, write, read).await {
89            Ok(_) => Ok(()),
90            Err(_) => Err(SCD41Error::I2CError),
91        }
92    }
93
94    async fn i2c_write<'d, I : embassy_rp::i2c::Instance>(&mut self,
95        i2c: &mut embassy_rp::i2c::I2c<'d, I, embassy_rp::i2c::Async>,
96        write: &[u8]) -> Result<(), SCD41Error> {
97        match i2c.write(self.address, write).await {
98            Ok(_) => Ok(()),
99            Err(_) => Err(SCD41Error::I2CError),
100        }
101    }
102}
103
104enum InitializationStep {
105    Initial,
106    StopMeasurement,
107    Reinit,
108    StartMeasurement,
109    ReadData,
110}