bosch_bme680/
async_impl.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use self::i2c_helper::I2CHelper;
use crate::config::{Configuration, SensorMode, Variant};
use crate::data::CalibrationData;
use crate::BmeError;
use crate::DeviceAddress;
use crate::MeasurmentData;
use embedded_hal_async::{
    delay::DelayNs,
    i2c::{I2c, SevenBitAddress},
};
mod i2c_helper;

/// Asynchronous BME680 sensor driver.
///
/// This struct is similar to the [`Bme680`](crate::Bme680) type, except that it
/// uses the [`embedded_hal_async`] crate's `I2c` and `Delay` traits, rather
/// than the [`embedded_hal`] versions of those traits.
///
/// # Notes
///
/// The [`AsyncBme680::new`] constructor is not asynchronous, and therefore ---
/// unlike the  synchronous [`Bme680::new`] --- it does not initialize the
/// sensor.  Instead, the sensor must be initialized using the
/// [`AsyncBme680::initialize`] method before reading sensor data. Otherwise,
/// the [`AsyncBme680::measure`] method will return [`BmeError::Uninitialized`].
///
/// [`Bme680::new`]: crate::Bme680::new
pub struct AsyncBme680<I2C, D> {
    // actually communicates with sensor
    i2c: I2CHelper<I2C, D>,
    state: Option<State>,
}

struct State {
    // calibration data that was saved on the sensor
    calibration_data: CalibrationData,
    // used to calculate measurement delay period
    current_sensor_config: Configuration,
    // needed to calculate the gas resistance since it differs between bme680 and bme688
    variant: Variant,
}

impl<I2C, D> AsyncBme680<I2C, D>
where
    I2C: I2c<SevenBitAddress>,
    D: DelayNs,
{
    /// Creates a new instance of the Sensor
    ///
    /// # Arguments
    /// * `delayer` - Used to wait for the triggered measurement to finish
    /// * `ambient_temperature` - Needed to calculate the heater target
    ///   temperature
    ///
    /// # Notes
    ///
    /// This constructor is not asynchronous, and therefore --- unlike the
    /// synchronous [`Bme680::new`] --- it does not initialize the sensor.
    /// Instead, the sensor must be initialized using the
    /// [`AsyncBme680::initialize`] method before reading sensor data.
    /// Otherwise, the [`AsyncBme680::measure`] method  will return
    /// [`BmeError::Uninitialized`].
    ///
    /// [`Bme680::new`]: crate::Bme680::new
    pub fn new(
        i2c_interface: I2C,
        device_address: DeviceAddress,
        delayer: D,
        ambient_temperature: i32,
    ) -> Self {
        let i2c = I2CHelper::new(i2c_interface, device_address, delayer, ambient_temperature);

        Self { i2c, state: None }
    }

    pub async fn initialize(&mut self, sensor_config: &Configuration) -> Result<(), BmeError<I2C>> {
        self.i2c.init().await?;
        let calibration_data = self.i2c.get_calibration_data().await?;
        self
            .i2c
            .set_config(sensor_config, &calibration_data)
            .await?;
        let variant = self.i2c.get_variant_id().await?;
        self.state = Some(State {
            calibration_data,
            current_sensor_config: sensor_config.clone(),
            variant,
        });
        Ok(())
    }

    /// Returns the wrapped i2c interface
    pub fn into_inner(self) -> I2C {
        self.i2c.into_inner()
    }

    pub async fn set_configuration(&mut self, config: &Configuration) -> Result<(), BmeError<I2C>> {
        let state = self.state.as_mut().ok_or(BmeError::Uninitialized)?;
        self.i2c.set_mode(SensorMode::Sleep).await?;
        self.i2c.set_config(config, &state.calibration_data).await?;
        // current conf is used to calculate measurement delay period
        state.current_sensor_config = config.clone();
        Ok(())
    }
    /// Trigger a new measurement.
    /// # Errors
    /// If no new data is generated in 5 tries a Timeout error is returned.
    // Sets the sensor mode to forced
    // Tries to wait 5 times for new data with a delay calculated based on the set sensor config
    // If no new data could be read in those 5 attempts a Timeout error is returned
    pub async fn measure(&mut self) -> Result<MeasurmentData, BmeError<I2C>> {
        let state = self.state.as_mut().ok_or(BmeError::Uninitialized)?;
        self.i2c.set_mode(SensorMode::Forced).await?;
        let delay_period = state.current_sensor_config.calculate_delay_period_us();


        self.i2c.delay(delay_period).await;
        // try read new values 5 times and delay if no new data is available or the sensor is still measuring
        for _i in 0..5 {
            let raw_data = self.i2c.get_field_data().await?;
            match MeasurmentData::from_raw(raw_data, &state.calibration_data, &state.variant) {
                Some(data) => {
                    // update the current ambient temperature which is needed to calculate the target heater temp
                    self.i2c.ambient_temperature = data.temperature as i32;
                    return Ok(data);
                }
                None => self.i2c.delay(delay_period).await,
            }
        }
        // Shouldn't happen
        Err(BmeError::MeasuringTimeOut)
    }

    pub fn get_calibration_data(&self) -> Result<&CalibrationData, BmeError<I2C>> {
        Ok(&self
            .state
            .as_ref()
            .ok_or(BmeError::Uninitialized)?
            .calibration_data)
    }
}

#[cfg(test)]
mod library_tests {
    // TODO: embedded_hal_mock doesn't currently have support for the async I2C
    // trait. When that's added, we should add tests here.
}