embassy-scd41-sensor 0.1.4

SCD41 sensor driver for the Embassy async runtime
Documentation
use crate::{SCD41Error, SCD41Response};
use embassy_time::Duration;
use embedded_hal_async::i2c::I2c;

pub struct SCD41Sensor {
    address: u8,
    initialization_step: InitializationStep,
    last_response: Option<SCD41Response>,
    next_step_time: embassy_time::Instant,
}

impl SCD41Sensor {
    pub fn new(address: u8) -> Self {
        Self {
            address,
            initialization_step: InitializationStep::Initial,
            last_response: None,
            next_step_time: embassy_time::Instant::now(),
        }
    }

    pub async fn read<'d, I: embassy_rp::i2c::Instance>(
        &mut self,
        i2c: &mut embassy_rp::i2c::I2c<'d, I, embassy_rp::i2c::Async>,
    ) -> Result<SCD41Response, SCD41Error> {
        let now = embassy_time::Instant::now();
        if now < self.next_step_time {
            return if let Some(response) = &self.last_response {
                Ok(response.clone())
            } else {
                Err(SCD41Error::NoData)
            };
        }
        match self.initialization_step {
            InitializationStep::Initial => {
                self.initialization_step = InitializationStep::StopMeasurement;
                self.next_step_time = now + Duration::from_millis(1000);
                Err(SCD41Error::NoData)
            }
            InitializationStep::StopMeasurement => {
                self.initialization_step = InitializationStep::Reinit;
                self.next_step_time = now + Duration::from_millis(1000);
                self.i2c_write(i2c, &[0x3f, 0x86]).await?;
                Err(SCD41Error::NoData)
            }
            InitializationStep::Reinit => {
                self.initialization_step = InitializationStep::StartMeasurement;
                self.next_step_time = now + Duration::from_millis(1000);
                self.i2c_write(i2c, &[0x36, 0x46]).await?;
                Err(SCD41Error::NoData)
            }
            InitializationStep::StartMeasurement => {
                self.initialization_step = InitializationStep::ReadData;
                self.next_step_time = now + Duration::from_millis(1000);
                self.i2c_write(i2c, &[0x21, 0xb1]).await?;
                Err(SCD41Error::NoData)
            }
            InitializationStep::ReadData => {
                self.next_step_time = now + Duration::from_millis(5000);
                let mut buf = [0u8; 9];
                self.i2c_write_read(i2c, &[0xe4, 0xb8], &mut buf).await?;
                if buf[1] == 0x0 {
                    self.next_step_time = now + Duration::from_millis(1000);
                    Err(SCD41Error::NoData)
                } else {
                    self.i2c_write_read(i2c, &[0xec, 0x05], &mut buf).await?;
                    let delimiter = 0xffff as f32;
                    let co2_ppm = i16::from_be_bytes([buf[0], buf[1]]) as f32;
                    let temperature_data = i16::from_be_bytes([buf[3], buf[4]]) as f32;
                    let temperature = -45f32 + 175f32 * temperature_data / delimiter;
                    let humidity_data = i16::from_be_bytes([buf[6], buf[7]]) as f32;
                    let humidity = 100f32 * (humidity_data / delimiter);
                    let response = SCD41Response {
                        co2: co2_ppm,
                        temperature,
                        humidity,
                    };
                    self.last_response = Some(response.clone());
                    Ok(response)
                }
            }
        }
    }

    async fn i2c_write_read<'d, I : embassy_rp::i2c::Instance>(&mut self,
        i2c: &mut embassy_rp::i2c::I2c<'d, I, embassy_rp::i2c::Async>,
        write: &[u8], read: &mut [u8]) -> Result<(), SCD41Error> {
        match i2c.write_read(self.address, write, read).await {
            Ok(_) => Ok(()),
            Err(_) => Err(SCD41Error::I2CError),
        }
    }

    async fn i2c_write<'d, I : embassy_rp::i2c::Instance>(&mut self,
        i2c: &mut embassy_rp::i2c::I2c<'d, I, embassy_rp::i2c::Async>,
        write: &[u8]) -> Result<(), SCD41Error> {
        match i2c.write(self.address, write).await {
            Ok(_) => Ok(()),
            Err(_) => Err(SCD41Error::I2CError),
        }
    }
}

enum InitializationStep {
    Initial,
    StopMeasurement,
    Reinit,
    StartMeasurement,
    ReadData,
}