bosch_bme680/
async_impl.rs

1use self::i2c_helper::I2CHelper;
2use crate::config::{Configuration, SensorMode, Variant};
3use crate::data::CalibrationData;
4use crate::BmeError;
5use crate::DeviceAddress;
6use crate::MeasurmentData;
7use embedded_hal_async::{
8    delay::DelayNs,
9    i2c::{I2c, SevenBitAddress},
10};
11mod i2c_helper;
12
13/// Asynchronous BME680 sensor driver.
14///
15/// This struct is similar to the [`Bme680`](crate::Bme680) type, except that it
16/// uses the [`embedded_hal_async`] crate's `I2c` and `Delay` traits, rather
17/// than the [`embedded_hal`] versions of those traits.
18///
19/// # Notes
20///
21/// The [`AsyncBme680::new`] constructor is not asynchronous, and therefore ---
22/// unlike the  synchronous [`Bme680::new`] --- it does not initialize the
23/// sensor.  Instead, the sensor must be initialized using the
24/// [`AsyncBme680::initialize`] method before reading sensor data. Otherwise,
25/// the [`AsyncBme680::measure`] method will return [`BmeError::Uninitialized`].
26///
27/// [`Bme680::new`]: crate::Bme680::new
28pub struct AsyncBme680<I2C, D> {
29    // actually communicates with sensor
30    i2c: I2CHelper<I2C, D>,
31    state: Option<State>,
32}
33
34struct State {
35    // calibration data that was saved on the sensor
36    calibration_data: CalibrationData,
37    // used to calculate measurement delay period
38    current_sensor_config: Configuration,
39    // needed to calculate the gas resistance since it differs between bme680 and bme688
40    variant: Variant,
41}
42
43impl<I2C, D> AsyncBme680<I2C, D>
44where
45    I2C: I2c<SevenBitAddress>,
46    D: DelayNs,
47{
48    /// Creates a new instance of the Sensor
49    ///
50    /// # Arguments
51    /// * `delayer` - Used to wait for the triggered measurement to finish
52    /// * `ambient_temperature` - Needed to calculate the heater target
53    ///   temperature
54    ///
55    /// # Notes
56    ///
57    /// This constructor is not asynchronous, and therefore --- unlike the
58    /// synchronous [`Bme680::new`] --- it does not initialize the sensor.
59    /// Instead, the sensor must be initialized using the
60    /// [`AsyncBme680::initialize`] method before reading sensor data.
61    /// Otherwise, the [`AsyncBme680::measure`] method  will return
62    /// [`BmeError::Uninitialized`].
63    ///
64    /// [`Bme680::new`]: crate::Bme680::new
65    pub fn new(
66        i2c_interface: I2C,
67        device_address: DeviceAddress,
68        delayer: D,
69        ambient_temperature: i32,
70    ) -> Self {
71        let i2c = I2CHelper::new(i2c_interface, device_address, delayer, ambient_temperature);
72
73        Self { i2c, state: None }
74    }
75
76    pub async fn initialize(&mut self, sensor_config: &Configuration) -> Result<(), BmeError<I2C>> {
77        self.i2c.init().await?;
78        let calibration_data = self.i2c.get_calibration_data().await?;
79        self.i2c
80            .set_config(sensor_config, &calibration_data)
81            .await?;
82        let variant = self.i2c.get_variant_id().await?;
83        self.state = Some(State {
84            calibration_data,
85            current_sensor_config: sensor_config.clone(),
86            variant,
87        });
88        Ok(())
89    }
90    pub async fn put_to_sleep(&mut self) -> Result<(), BmeError<I2C>> {
91        self.i2c.set_mode(SensorMode::Sleep).await
92    }
93    /// Returns the wrapped i2c interface
94    pub fn into_inner(self) -> I2C {
95        self.i2c.into_inner()
96    }
97
98    pub async fn set_configuration(&mut self, config: &Configuration) -> Result<(), BmeError<I2C>> {
99        let state = self.state.as_mut().ok_or(BmeError::Uninitialized)?;
100        self.i2c.set_mode(SensorMode::Sleep).await?;
101        self.i2c.set_config(config, &state.calibration_data).await?;
102        // current conf is used to calculate measurement delay period
103        state.current_sensor_config = config.clone();
104        Ok(())
105    }
106    /// Trigger a new measurement.
107    /// # Errors
108    /// If no new data is generated in 5 tries a Timeout error is returned.
109    // Sets the sensor mode to forced
110    // Tries to wait 5 times for new data with a delay calculated based on the set sensor config
111    // If no new data could be read in those 5 attempts a Timeout error is returned
112    pub async fn measure(&mut self) -> Result<MeasurmentData, BmeError<I2C>> {
113        let state = self.state.as_mut().ok_or(BmeError::Uninitialized)?;
114        self.i2c.set_mode(SensorMode::Forced).await?;
115        let delay_period = state.current_sensor_config.calculate_delay_period_us();
116
117        self.i2c.delay(delay_period).await;
118        // try read new values 5 times and delay if no new data is available or the sensor is still measuring
119        for _i in 0..5 {
120            let raw_data = self.i2c.get_field_data().await?;
121            match MeasurmentData::from_raw(raw_data, &state.calibration_data, &state.variant) {
122                Some(data) => {
123                    // update the current ambient temperature which is needed to calculate the target heater temp
124                    self.i2c.ambient_temperature = data.temperature as i32;
125                    return Ok(data);
126                }
127                None => self.i2c.delay(delay_period).await,
128            }
129        }
130        // Shouldn't happen
131        Err(BmeError::MeasuringTimeOut)
132    }
133
134    pub fn get_calibration_data(&self) -> Result<&CalibrationData, BmeError<I2C>> {
135        Ok(&self
136            .state
137            .as_ref()
138            .ok_or(BmeError::Uninitialized)?
139            .calibration_data)
140    }
141}
142
143#[cfg(test)]
144mod library_tests {
145    extern crate std;
146
147    use std::vec;
148    use std::vec::Vec;
149
150    use crate::constants::{
151        ADDR_CHIP_ID, ADDR_CONFIG, ADDR_CONTROL_MODE, ADDR_GAS_WAIT_0, ADDR_REG_COEFF1,
152        ADDR_REG_COEFF2, ADDR_REG_COEFF3, ADDR_RES_HEAT_0, ADDR_SOFT_RESET, ADDR_VARIANT_ID,
153        CHIP_ID, CMD_SOFT_RESET, LEN_COEFF1, LEN_COEFF2, LEN_COEFF3,
154    };
155    use crate::i2c_helper::extract_calibration_data;
156
157    const CALIBRATION_DATA: [u8; 42] = [
158        179, 193, 176, 188, 21, 51, 11, 29, 222, 179, 184, 1, 230, 47, 209, 22, 154, 34, 237, 70,
159        148, 134, 44, 13, 204, 61, 206, 69, 18, 43, 124, 164, 92, 132, 19, 63, 29, 28, 201, 140,
160        70, 24,
161    ];
162
163    use super::*;
164    use crate::bitfields::RawConfig;
165    use embedded_hal_mock::eh1::delay::NoopDelay;
166    use embedded_hal_mock::eh1::i2c::{Mock as I2cMock, Transaction as I2cTransaction};
167
168    fn setup_transactions() -> Vec<I2cTransaction> {
169        let mut transactions = vec![];
170        let calibration_data_1 = CALIBRATION_DATA[0..LEN_COEFF1].to_vec();
171        let calibration_data_2 = CALIBRATION_DATA[LEN_COEFF1..LEN_COEFF1 + LEN_COEFF2].to_vec();
172        let calibration_data_3 = CALIBRATION_DATA
173            [LEN_COEFF1 + LEN_COEFF2..LEN_COEFF1 + LEN_COEFF2 + LEN_COEFF3]
174            .to_vec();
175        assert_eq!(calibration_data_1.len(), LEN_COEFF1);
176        assert_eq!(calibration_data_2.len(), LEN_COEFF2);
177        assert_eq!(calibration_data_3.len(), LEN_COEFF3);
178        // soft reset
179        transactions.push(I2cTransaction::write(
180            DeviceAddress::Primary.into(),
181            vec![ADDR_SOFT_RESET, CMD_SOFT_RESET],
182        ));
183        // check device id
184        transactions.push(I2cTransaction::write_read(
185            DeviceAddress::Primary.into(),
186            vec![ADDR_CHIP_ID],
187            vec![CHIP_ID],
188        ));
189        // calibration_data
190        transactions.push(I2cTransaction::write_read(
191            DeviceAddress::Primary.into(),
192            vec![ADDR_REG_COEFF1],
193            calibration_data_1,
194        ));
195        transactions.push(I2cTransaction::write_read(
196            DeviceAddress::Primary.into(),
197            vec![ADDR_REG_COEFF2],
198            calibration_data_2,
199        ));
200        transactions.push(I2cTransaction::write_read(
201            DeviceAddress::Primary.into(),
202            vec![ADDR_REG_COEFF3],
203            calibration_data_3,
204        ));
205        // set config
206        // 1. get current config by writing to register 0x71 with buffer len 5.
207        // 2. apply default user facing config to the default values of the sensor.
208        // 3. write gas_wait_0 and res_heat_0
209        // config defaults to 0s
210        // 1.
211        let default_config = [0u8; 5];
212        transactions.push(I2cTransaction::write_read(
213            DeviceAddress::Primary.into(),
214            vec![ADDR_CONFIG],
215            default_config.into(),
216        ));
217        // 2.
218        let user_config = Configuration::default();
219        let mut raw_config = RawConfig(default_config);
220        raw_config.apply_config(&user_config);
221        // add unique write for each register
222        raw_config
223            .0
224            .into_iter()
225            .enumerate()
226            .for_each(|(register_offset, register_content)| {
227                transactions.push(I2cTransaction::write(
228                    DeviceAddress::Primary.into(),
229                    vec![ADDR_CONFIG + register_offset as u8, register_content],
230                ));
231            });
232        // 3.
233        let gas_config = user_config.gas_config.unwrap();
234        let gas_wait_0 = gas_config.calc_gas_wait();
235        let res_heat_0 = gas_config.calc_res_heat(&extract_calibration_data(CALIBRATION_DATA), 20);
236        transactions.push(I2cTransaction::write(
237            DeviceAddress::Primary.into(),
238            vec![ADDR_GAS_WAIT_0, gas_wait_0],
239        ));
240        transactions.push(I2cTransaction::write(
241            DeviceAddress::Primary.into(),
242            vec![ADDR_RES_HEAT_0, res_heat_0],
243        ));
244        // get chip variant
245        transactions.push(I2cTransaction::write_read(
246            DeviceAddress::Primary.into(),
247            vec![ADDR_VARIANT_ID],
248            vec![0],
249        ));
250        transactions
251    }
252    fn add_sleep_to_sleep_transactions(transactions: &mut Vec<I2cTransaction>) {
253        transactions.push(I2cTransaction::write_read(
254            DeviceAddress::Primary.into(),
255            vec![ADDR_CONTROL_MODE],
256            // sleep mode
257            vec![0b101011_00],
258        ));
259    }
260    #[tokio::test]
261    async fn test_setup() {
262        let transactions = setup_transactions();
263        let i2c_interface = I2cMock::new(&transactions);
264        let mut bme = AsyncBme680::new(i2c_interface, DeviceAddress::Primary, NoopDelay::new(), 20);
265        bme.initialize(&Configuration::default()).await.unwrap();
266        bme.into_inner().done();
267    }
268
269    #[tokio::test]
270    async fn test_set_mode_forced_to_sleep() {
271        let mut transactions = setup_transactions();
272        transactions.push(I2cTransaction::write_read(
273            DeviceAddress::Primary.into(),
274            vec![ADDR_CONTROL_MODE],
275            // sleep mode
276            vec![0b101011_01],
277        ));
278        transactions.push(I2cTransaction::write(
279            DeviceAddress::Primary.into(),
280            vec![ADDR_CONTROL_MODE, 0b101011_00],
281        ));
282        transactions.push(I2cTransaction::write_read(
283            DeviceAddress::Primary.into(),
284            vec![ADDR_CONTROL_MODE],
285            // sleep mode
286            vec![0b101011_00],
287        ));
288        // Transactions: Get(Forced) -> Set(Sleep) -> Get(Sleep)
289        let i2c_interface = I2cMock::new(&transactions);
290        let mut bme = AsyncBme680::new(i2c_interface, DeviceAddress::Primary, NoopDelay::new(), 20);
291        bme.initialize(&Configuration::default()).await.unwrap();
292        bme.put_to_sleep().await.unwrap();
293        bme.into_inner().done();
294    }
295    #[tokio::test]
296    async fn test_set_mode_sleep_to_sleep() {
297        let mut transactions = setup_transactions();
298        add_sleep_to_sleep_transactions(&mut transactions);
299        let i2c_interface = I2cMock::new(&transactions);
300        let mut bme = AsyncBme680::new(i2c_interface, DeviceAddress::Primary, NoopDelay::new(), 20);
301        bme.initialize(&Configuration::default()).await.unwrap();
302        bme.put_to_sleep().await.unwrap();
303        bme.into_inner().done();
304    }
305}