embedded_devices/devices/bosch/bme280/
mod.rs

1//! The BME280 is a combined digital humidity, pressure and temperature sensor based on proven
2//! sensing principles. The sensor module is housed in an extremely compact metal-lid LGA package with
3//! a footprint of only 2.5 × 2.5 mm² with a height of 0.93 mm. Its small dimensions and its low power
4//! consumption allow the implementation in battery driven devices such as handsets, GPS modules or
5//! watches. The BME280 is register and performance compatible to the Bosch Sensortec BMP280 digital
6//! pressure sensor.
7//!
8//! The BME280 achieves high performance in all applications requiring humidity and pressure
9//! measurement. These emerging applications of home automation control, in-door navigation, fitness as
10//! well as GPS refinement require a high accuracy and a low TCO at the same time.
11//!
12//! - The humidity sensor provides an extremely fast response time for fast context awareness applications
13//!   and high overall accuracy over a wide temperature range.
14//! - The pressure sensor is an absolute barometric pressure sensor with extremely high accuracy and
15//!   resolution and drastically lower noise than the Bosch Sensortec BMP180.
16//! - The integrated temperature sensor has been optimized for lowest noise and highest resolution. Its
17//!   output is used for temperature compensation of the pressure and humidity sensors and can also be
18//!   used for estimation of the ambient temperature.
19//!
20//! The sensor provides both SPI and I²C interfaces and can be supplied using 1.71 to 3.6 V for the
21//! sensor supply V DD and 1.2 to 3.6 V for the interface supply V DDIO. Measurements can be triggered by
22//! the host or performed in regular intervals. When the sensor is disabled, current consumption drops to
23//! 0.1 μA.
24//!
25//! BME280 can be operated in three power modes:
26//! - sleep mode
27//! - normal mode
28//! - forced mode
29//!
30//! In order to tailor data rate, noise, response time and current consumption to the needs of the user,
31//! a variety of oversampling modes, filter modes and data rates can be selected.
32//!
33//! ## Usage (sync)
34//!
35//! ```rust
36//! # #[cfg(feature = "sync")] mod test {
37//! # fn test<I, D>(mut i2c: I, delay: D) -> Result<(), embedded_devices::devices::bosch::bme280::InitError<I::Error>>
38//! # where
39//! #   I: embedded_hal::i2c::I2c + embedded_hal::i2c::ErrorType,
40//! #   D: embedded_hal::delay::DelayNs
41//! # {
42//! use embedded_devices::devices::bosch::bme280::{BME280Sync, Configuration, address::Address};
43//! use embedded_devices::devices::bosch::bme280::registers::{IIRFilter, Oversampling};
44//! use embedded_devices::sensor::OneshotSensorSync;
45//! use uom::si::pressure::pascal;
46//! use uom::si::ratio::percent;
47//! use uom::si::thermodynamic_temperature::degree_celsius;
48//!
49//! // Create and initialize the device
50//! let mut bme280 = BME280Sync::new_i2c(delay, i2c, Address::Primary);
51//! bme280.init()?;
52//! // Enable sensors
53//! bme280.configure(Configuration {
54//!     temperature_oversampling: Oversampling::X_16,
55//!     pressure_oversampling: Oversampling::X_16,
56//!     humidity_oversampling: Oversampling::X_16,
57//!     iir_filter: IIRFilter::Disabled,
58//! })?;
59//!
60//! // Read measurement
61//! let measurement = bme280.measure().unwrap();
62//! let temp = measurement.temperature.get::<degree_celsius>();
63//! let pressure = measurement.pressure.expect("should be enabled").get::<pascal>();
64//! let humidity = measurement.humidity.expect("should be enabled").get::<percent>();
65//! println!("Current measurement: {:?}°C, {:?} Pa, {:?}%RH", temp, pressure, humidity);
66//! # Ok(())
67//! # }
68//! # }
69//! ```
70//!
71//! ## Usage (async)
72//!
73//! ```rust
74//! # #[cfg(feature = "async")] mod test {
75//! # async fn test<I, D>(mut i2c: I, delay: D) -> Result<(), embedded_devices::devices::bosch::bme280::InitError<I::Error>>
76//! # where
77//! #   I: embedded_hal_async::i2c::I2c + embedded_hal_async::i2c::ErrorType,
78//! #   D: embedded_hal_async::delay::DelayNs
79//! # {
80//! use embedded_devices::devices::bosch::bme280::{BME280Async, Configuration, address::Address};
81//! use embedded_devices::devices::bosch::bme280::registers::{IIRFilter, Oversampling};
82//! use embedded_devices::sensor::OneshotSensorAsync;
83//! use uom::si::pressure::pascal;
84//! use uom::si::ratio::percent;
85//! use uom::si::thermodynamic_temperature::degree_celsius;
86//!
87//! // Create and initialize the device
88//! let mut bme280 = BME280Async::new_i2c(delay, i2c, Address::Primary);
89//! bme280.init().await?;
90//! // Enable sensors
91//! bme280.configure(Configuration {
92//!     temperature_oversampling: Oversampling::X_16,
93//!     pressure_oversampling: Oversampling::X_16,
94//!     humidity_oversampling: Oversampling::X_16,
95//!     iir_filter: IIRFilter::Disabled,
96//! }).await?;
97//!
98//! // Read measurement
99//! let measurement = bme280.measure().await.unwrap();
100//! let temp = measurement.temperature.get::<degree_celsius>();
101//! let pressure = measurement.pressure.expect("should be enabled").get::<pascal>();
102//! let humidity = measurement.humidity.expect("should be enabled").get::<percent>();
103//! println!("Current measurement: {:?}°C, {:?} Pa, {:?}%RH", temp, pressure, humidity);
104//! # Ok(())
105//! # }
106//! # }
107//! ```
108
109use embedded_devices_derive::{forward_register_fns, sensor};
110use embedded_interfaces::TransportError;
111use uom::si::f64::{Pressure, Ratio, ThermodynamicTemperature};
112use uom::si::pressure::pascal;
113use uom::si::ratio::percent;
114use uom::si::thermodynamic_temperature::degree_celsius;
115
116pub mod address;
117pub mod registers;
118
119use self::address::Address;
120use self::registers::{
121    BurstMeasurementsPTH, Config, ControlHumidity, ControlMeasurement, IIRFilter, Id, Oversampling, SensorMode,
122    TrimmingParameters1, TrimmingParameters2,
123};
124
125#[cfg_attr(feature = "defmt", derive(defmt::Format))]
126#[derive(Debug, thiserror::Error)]
127pub enum InitError<BusError> {
128    /// Transport error
129    #[error("transport error")]
130    Transport(#[from] TransportError<(), BusError>),
131    /// Invalid chip id was encountered in `init`
132    #[error("invalid chip id {0:#02x}")]
133    InvalidChip(u8),
134}
135
136#[cfg_attr(feature = "defmt", derive(defmt::Format))]
137#[derive(Debug, thiserror::Error)]
138pub enum MeasurementError<BusError> {
139    /// Transport error
140    #[error("transport error")]
141    Transport(#[from] TransportError<(), BusError>),
142    /// The calibration data was not yet read from the device, but a measurement was requested. Call `init` or `calibrate` first.
143    #[error("not yet calibrated")]
144    NotCalibrated,
145}
146
147/// Measurement data
148#[derive(Debug, embedded_devices_derive::Measurement)]
149pub struct Measurement {
150    /// Current temperature
151    #[measurement(Temperature)]
152    pub temperature: ThermodynamicTemperature,
153    /// Current pressure or None if the sensor reported and invalid value
154    #[measurement(Pressure)]
155    pub pressure: Option<Pressure>,
156    /// Current relative humidity
157    #[measurement(RelativeHumidity)]
158    pub humidity: Option<Ratio>,
159}
160
161/// Fine temperature coefficient calculated when compensating temperature
162/// and required to compensate pressure and humidity
163#[derive(Debug, Clone, Copy)]
164pub(super) struct TFine(i32);
165
166/// The common base for both BME280 and BMP280.
167/// For a full description and usage examples, refer to [BME280](self).
168#[maybe_async_cfg::maybe(
169    idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), RegisterInterface),
170    sync(feature = "sync"),
171    async(feature = "async")
172)]
173pub struct BME280Common<
174    D: hal::delay::DelayNs,
175    I: embedded_interfaces::registers::RegisterInterface,
176    const IS_BME: bool,
177> {
178    /// The delay provider
179    pub(super) delay: D,
180    /// The interface to communicate with the device
181    interface: I,
182    /// Calibration data
183    pub(super) calibration_data: Option<CalibrationData>,
184}
185
186pub trait BME280CommonRegister {}
187
188/// The BME280 is a combined digital humidity, pressure and temperature sensor based on proven
189/// sensing principles. The sensor module is housed in an extremely compact metal-lid LGA package.
190/// a footprint of only 2.5 × 2.5 mm² with a height of 0.93 mm. Its small dimensions and its low power
191/// consumption allow the implementation in battery driven devices such as handsets, GPS modules or
192/// watches.
193#[cfg(feature = "sync")]
194pub type BME280Sync<D, I> = BME280CommonSync<D, I, true>;
195
196/// The BME280 is a combined digital humidity, pressure and temperature sensor based on proven
197/// sensing principles. The sensor module is housed in an extremely compact metal-lid LGA package.
198/// a footprint of only 2.5 × 2.5 mm² with a height of 0.93 mm. Its small dimensions and its low power
199/// consumption allow the implementation in battery driven devices such as handsets, GPS modules or
200/// watches.
201#[cfg(feature = "async")]
202pub type BME280Async<D, I> = BME280CommonAsync<D, I, true>;
203
204/// Common configuration values for the BME280 sensor.
205#[derive(Debug, Clone)]
206pub struct Configuration {
207    /// The oversampling rate for temperature measurements
208    pub temperature_oversampling: Oversampling,
209    /// The oversampling rate for pressure measurements
210    pub pressure_oversampling: Oversampling,
211    /// The oversampling rate for humidity measurements
212    pub humidity_oversampling: Oversampling,
213    /// The iir filter to use
214    pub iir_filter: IIRFilter,
215}
216
217impl Default for Configuration {
218    fn default() -> Self {
219        Self {
220            temperature_oversampling: Oversampling::X_1,
221            pressure_oversampling: Oversampling::X_1,
222            humidity_oversampling: Oversampling::X_1,
223            iir_filter: IIRFilter::Disabled,
224        }
225    }
226}
227
228#[derive(Debug)]
229pub(super) struct CalibrationData {
230    dig_t1: u16,
231    dig_t2: i16,
232    dig_t3: i16,
233    dig_p1: u16,
234    dig_p2: i16,
235    dig_p3: i16,
236    dig_p4: i16,
237    dig_p5: i16,
238    dig_p6: i16,
239    dig_p7: i16,
240    dig_p8: i16,
241    dig_p9: i16,
242    dig_h1: u8,
243    dig_h2: i16,
244    dig_h3: u8,
245    dig_h4: i16,
246    dig_h5: i16,
247    dig_h6: i8,
248}
249
250impl CalibrationData {
251    pub fn new(params1: self::registers::TrimmingParameters1, params2: self::registers::TrimmingParameters2) -> Self {
252        let params1 = params1.unpack();
253        let params2 = params2.unpack();
254        Self {
255            dig_t1: params1.dig_t1,
256            dig_t2: params1.dig_t2,
257            dig_t3: params1.dig_t3,
258            dig_p1: params1.dig_p1,
259            dig_p2: params1.dig_p2,
260            dig_p3: params1.dig_p3,
261            dig_p4: params1.dig_p4,
262            dig_p5: params1.dig_p5,
263            dig_p6: params1.dig_p6,
264            dig_p7: params1.dig_p7,
265            dig_p8: params1.dig_p8,
266            dig_p9: params1.dig_p9,
267            dig_h1: params1.dig_h1,
268            dig_h2: params2.dig_h2,
269            dig_h3: params2.dig_h3,
270            dig_h4: (params2.dig_h4_msb as i16 * 16) | ((params2.dig_h5_lsn_h4_lsn as i16) & 0x0F),
271            dig_h5: (params2.dig_h5_msb as i16 * 16) | (((params2.dig_h5_lsn_h4_lsn as i16) & 0xF0) >> 4),
272            dig_h6: params2.dig_h6,
273        }
274    }
275
276    pub(super) fn compensate_temperature(&self, uncompensated: u32) -> (ThermodynamicTemperature, TFine) {
277        let dig_t1 = self.dig_t1 as i32;
278        let dig_t2 = self.dig_t2 as i32;
279        let dig_t3 = self.dig_t3 as i32;
280
281        let var1 = (uncompensated >> 3) as i32 - (dig_t1 << 1);
282        let var1 = (var1 * dig_t2) >> 11;
283        let var2 = (uncompensated >> 4) as i32 - dig_t1;
284        let var2 = (((var2 * var2) >> 12) * dig_t3) >> 14;
285        let t_fine = var1 + var2;
286        let temperature = (t_fine * 5 + 128) as f64 / (100.0 * 256.0);
287
288        (
289            ThermodynamicTemperature::new::<degree_celsius>(temperature),
290            TFine(t_fine),
291        )
292    }
293
294    pub(super) fn compensate_pressure(&self, uncompensated: u32, t_fine: TFine) -> Option<Pressure> {
295        let t_fine = t_fine.0;
296
297        let dig_p1 = self.dig_p1 as i64;
298        let dig_p2 = self.dig_p2 as i64;
299        let dig_p3 = self.dig_p3 as i64;
300        let dig_p4 = self.dig_p4 as i64;
301        let dig_p5 = self.dig_p5 as i64;
302        let dig_p6 = self.dig_p6 as i64;
303        let dig_p7 = self.dig_p7 as i64;
304        let dig_p8 = self.dig_p8 as i64;
305        let dig_p9 = self.dig_p9 as i64;
306
307        let var1 = t_fine as i64 - 128000;
308        let var2 = var1 * var1 * dig_p6;
309        let var2 = var2 + ((var1 * dig_p5) << 17);
310        let var2 = var2 + (dig_p4 << 35);
311        let var1 = ((var1 * var1 * dig_p3) >> 8) + ((var1 * dig_p2) << 12);
312        let var1 = (((1i64 << 47) + var1) * dig_p1) >> 33;
313        if var1 == 0 {
314            return None;
315        }
316
317        let var4: i64 = 0x100000i64 - uncompensated as i64;
318        let var4 = (((var4 << 31) - var2) * 3125) / var1;
319        let var1 = (dig_p9 * (var4 >> 13) * (var4 >> 13)) >> 25;
320        let var2 = (dig_p8 * var4) >> 19;
321        let var4 = ((var4 + var1 + var2) >> 8) + (dig_p7 << 4);
322        let pressure = (var4 as i32 as f64) / 256.0;
323        Some(Pressure::new::<pascal>(pressure))
324    }
325
326    fn compensate_humidity(&self, uncompensated: u16, t_fine: TFine) -> Ratio {
327        let t_fine = t_fine.0;
328
329        let dig_h1 = self.dig_h1 as i32;
330        let dig_h2 = self.dig_h2 as i32;
331        let dig_h3 = self.dig_h3 as i32;
332        let dig_h4 = self.dig_h4 as i32;
333        let dig_h5 = self.dig_h5 as i32;
334        let dig_h6 = self.dig_h6 as i32;
335
336        let var1 = t_fine - 76800i32;
337        let uncompensated = (uncompensated as i32) << 14;
338        let var5 = (((uncompensated - (dig_h4 << 20)) - (dig_h5 * var1)) + 0x4000) >> 15;
339        let var2 = (var1 * dig_h6) >> 10;
340        let var3 = (var1 * dig_h3) >> 11;
341        let var4 = ((var2 * (var3 + 0x8000)) >> 10) + 0x200000;
342        let var2 = ((var4 * dig_h2) + 0x2000) >> 14;
343        let var3 = var5 * var2;
344        let var4 = ((var3 >> 15) * (var3 >> 15)) >> 7;
345        let var5 = var3 - ((var4 * dig_h1) >> 4);
346        let var5 = var5.clamp(0, 0x19000000);
347        let var5 = var5 >> 12;
348
349        let humidity = var5 as f64 / (1i32 << 10) as f64;
350        Ratio::new::<percent>(humidity)
351    }
352}
353
354#[maybe_async_cfg::maybe(
355    idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), I2cDevice),
356    sync(feature = "sync"),
357    async(feature = "async")
358)]
359impl<D, I, const IS_BME: bool>
360    BME280Common<D, embedded_interfaces::i2c::I2cDevice<I, hal::i2c::SevenBitAddress>, IS_BME>
361where
362    I: hal::i2c::I2c<hal::i2c::SevenBitAddress> + hal::i2c::ErrorType,
363    D: hal::delay::DelayNs,
364{
365    /// Initializes a new device with the given address on the specified bus.
366    /// This consumes the I2C bus `I`.
367    ///
368    /// Before using this device, you must call the [`Self::init`] method which
369    /// initializes the device and ensures that it is working correctly.
370    #[inline]
371    pub fn new_i2c(delay: D, interface: I, address: Address) -> Self {
372        Self {
373            delay,
374            interface: embedded_interfaces::i2c::I2cDevice::new(interface, address.into()),
375            calibration_data: None,
376        }
377    }
378}
379
380#[maybe_async_cfg::maybe(
381    idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), SpiDevice),
382    sync(feature = "sync"),
383    async(feature = "async")
384)]
385impl<D, I, const IS_BME: bool> BME280Common<D, embedded_interfaces::spi::SpiDevice<I>, IS_BME>
386where
387    I: hal::spi::r#SpiDevice,
388    D: hal::delay::DelayNs,
389{
390    /// Initializes a new device from the specified SPI device.
391    /// This consumes the SPI device `I`.
392    ///
393    /// Before using this device, you must call the [`Self::init`] method which
394    /// initializes the device and ensures that it is working correctly.
395    #[inline]
396    pub fn new_spi(delay: D, interface: I) -> Self {
397        Self {
398            delay,
399            interface: embedded_interfaces::spi::SpiDevice::new(interface),
400            calibration_data: None,
401        }
402    }
403}
404
405#[forward_register_fns]
406#[maybe_async_cfg::maybe(
407    idents(
408        hal(sync = "embedded_hal", async = "embedded_hal_async"),
409        RegisterInterface,
410        ResettableDevice
411    ),
412    sync(feature = "sync"),
413    async(feature = "async")
414)]
415impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface, const IS_BME: bool>
416    BME280Common<D, I, IS_BME>
417{
418    /// Initialize the sensor by performing a soft-reset, verifying its chip id
419    /// and reading calibration data.
420    ///
421    /// Beware that by default all internal sensors are disabled. Please
422    /// call [`Self::configure`] after initialization to enable the sensors,
423    /// otherwise measurement may return valid-looking but static values.
424    pub async fn init(&mut self) -> Result<(), InitError<I::BusError>> {
425        use crate::device::ResettableDevice;
426
427        // Soft-reset device
428        self.reset().await?;
429
430        // Verify chip id
431        let chip = self.read_register::<Id>().await?.read_chip();
432        if let self::registers::Chip::Invalid(x) = chip {
433            return Err(InitError::InvalidChip(x));
434        }
435
436        // Read calibration data
437        self.calibrate().await?;
438        Ok(())
439    }
440
441    /// Reads the calibration registers from the device to
442    /// compensate measurements. It is required to call this once
443    /// before taking any measurements. Calling [`Self::init`] will
444    /// automatically do this.
445    pub async fn calibrate(&mut self) -> Result<(), TransportError<(), I::BusError>> {
446        let params1 = self.read_register::<TrimmingParameters1>().await?;
447        let params2 = self.read_register::<TrimmingParameters2>().await?;
448        self.calibration_data = Some(CalibrationData::new(params1, params2));
449
450        Ok(())
451    }
452}
453
454#[maybe_async_cfg::maybe(
455    idents(
456        hal(sync = "embedded_hal", async = "embedded_hal_async"),
457        RegisterInterface,
458        ResettableDevice
459    ),
460    sync(feature = "sync"),
461    async(feature = "async")
462)]
463impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface, const IS_BME: bool>
464    crate::device::ResettableDevice for BME280Common<D, I, IS_BME>
465{
466    type Error = TransportError<(), I::BusError>;
467
468    /// Performs a soft-reset of the device. The datasheet specifies a start-up time
469    /// of 2ms, which is automatically awaited before allowing further communication.
470    async fn reset(&mut self) -> Result<(), Self::Error> {
471        self.write_register(self::registers::Reset::default()).await?;
472        self.delay.delay_ms(2).await;
473        Ok(())
474    }
475}
476
477// BME280 only:
478
479#[sensor(Temperature, Pressure, RelativeHumidity)]
480#[maybe_async_cfg::maybe(
481    idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), RegisterInterface),
482    sync(feature = "sync"),
483    async(feature = "async")
484)]
485impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> BME280Common<D, I, true> {
486    /// Configures common sensor settings. Sensor must be in sleep mode for this to work. To
487    /// configure advanced settings, please directly update the respective registers.
488    pub async fn configure(&mut self, config: Configuration) -> Result<(), TransportError<(), I::BusError>> {
489        self.write_register(ControlHumidity::default().with_oversampling(config.humidity_oversampling))
490            .await?;
491
492        // This must happen after ControlHumidity, otherwise the former will not have any effect
493        let reg_ctrl_m = self.read_register::<ControlMeasurement>().await?;
494        self.write_register(
495            reg_ctrl_m
496                .with_temperature_oversampling(config.temperature_oversampling)
497                .with_pressure_oversampling(config.pressure_oversampling),
498        )
499        .await?;
500
501        let mut reg_config = self.read_register::<Config>().await?;
502        reg_config.write_filter(config.iir_filter);
503        self.write_register(reg_config).await?;
504
505        Ok(())
506    }
507}
508
509#[maybe_async_cfg::maybe(
510    idents(
511        hal(sync = "embedded_hal", async = "embedded_hal_async"),
512        RegisterInterface,
513        OneshotSensor
514    ),
515    sync(feature = "sync"),
516    async(feature = "async")
517)]
518impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::sensor::OneshotSensor
519    for BME280Common<D, I, true>
520{
521    type Error = MeasurementError<I::BusError>;
522    type Measurement = Measurement;
523
524    /// Performs a one-shot measurement. This will transition the device into forced mode,
525    /// which will cause it to take a measurement and return to sleep mode afterwards.
526    ///
527    /// This function will wait until the data is acquired, perform a burst read
528    /// and compensate the returned raw data using the calibration data.
529    ///
530    /// Specific measurements will only be included if they were enabled beforehand by calling
531    /// [`Self::calibrate`]. Pressure and humidity measurement specifically require
532    /// temperature measurements to be enabled.
533    async fn measure(&mut self) -> Result<Self::Measurement, Self::Error> {
534        let reg_ctrl_m = self.read_register::<ControlMeasurement>().await?;
535        self.write_register(reg_ctrl_m.with_sensor_mode(SensorMode::Forced))
536            .await?;
537
538        // Use current oversampling config to determine required measurement delay
539        let reg_ctrl_h = self.read_register::<ControlHumidity>().await?;
540        let o_h = reg_ctrl_h.read_oversampling();
541        let o_t = reg_ctrl_m.read_temperature_oversampling();
542        let o_p = reg_ctrl_m.read_pressure_oversampling();
543
544        // Maximum time required to perform the measurement.
545        // See chapter 9 of the datasheet for more information.
546        let mut max_measurement_delay_us = 1250 + 2300 * o_t.factor();
547        if o_p.factor() > 0 {
548            max_measurement_delay_us += 575 + 2300 * o_p.factor();
549        }
550        if o_h.factor() > 0 {
551            max_measurement_delay_us += 575 + 2300 * o_h.factor();
552        }
553
554        self.delay.delay_us(max_measurement_delay_us).await;
555
556        let raw_data = self.read_register::<BurstMeasurementsPTH>().await?;
557        let Some(ref cal) = self.calibration_data else {
558            return Err(MeasurementError::NotCalibrated);
559        };
560
561        let (temperature, t_fine) = cal.compensate_temperature(raw_data.read_temperature_value());
562        let pressure = (o_p != Oversampling::Disabled)
563            .then(|| cal.compensate_pressure(raw_data.read_pressure_value(), t_fine))
564            .flatten();
565        let humidity =
566            (o_h != Oversampling::Disabled).then(|| cal.compensate_humidity(raw_data.read_humidity_value(), t_fine));
567
568        Ok(Measurement {
569            temperature,
570            pressure,
571            humidity,
572        })
573    }
574}
575
576#[maybe_async_cfg::maybe(
577    idents(
578        hal(sync = "embedded_hal", async = "embedded_hal_async"),
579        RegisterInterface,
580        ContinuousSensor
581    ),
582    sync(feature = "sync"),
583    async(feature = "async")
584)]
585impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::sensor::ContinuousSensor
586    for BME280Common<D, I, true>
587{
588    type Error = MeasurementError<I::BusError>;
589    type Measurement = Measurement;
590
591    /// Starts continuous measurement.
592    async fn start_measuring(&mut self) -> Result<(), Self::Error> {
593        let reg_ctrl_m = self.read_register::<ControlMeasurement>().await?;
594        self.write_register(reg_ctrl_m.with_sensor_mode(SensorMode::Normal))
595            .await?;
596        Ok(())
597    }
598
599    /// Stops continuous measurement.
600    async fn stop_measuring(&mut self) -> Result<(), Self::Error> {
601        let reg_ctrl_m = self.read_register::<ControlMeasurement>().await?;
602        self.write_register(reg_ctrl_m.with_sensor_mode(SensorMode::Sleep))
603            .await?;
604        Ok(())
605    }
606
607    /// Expected amount of time between measurements in microseconds.
608    async fn measurement_interval_us(&mut self) -> Result<u32, Self::Error> {
609        let reg_config = self.read_register::<Config>().await?;
610        let t_standby_us = reg_config.read_standby_time().time_us();
611
612        let reg_ctrl_m = self.read_register::<ControlMeasurement>().await?;
613        let reg_ctrl_h = self.read_register::<ControlHumidity>().await?;
614
615        let o_h = reg_ctrl_h.read_oversampling();
616        let o_t = reg_ctrl_m.read_temperature_oversampling();
617        let o_p = reg_ctrl_m.read_pressure_oversampling();
618
619        // Maximum time required to perform the measurement.
620        // See chapter 9 of the datasheet for more information.
621        let mut t_measure_us = 1250 + 2300 * o_t.factor();
622        if o_p.factor() > 0 {
623            t_measure_us += 575 + 2300 * o_p.factor();
624        }
625        if o_h.factor() > 0 {
626            t_measure_us += 575 + 2300 * o_h.factor();
627        }
628        Ok(t_measure_us + t_standby_us)
629    }
630
631    /// Returns the most recent measurement. Will never return None.
632    async fn current_measurement(&mut self) -> Result<Option<Self::Measurement>, Self::Error> {
633        let reg_ctrl_m = self.read_register::<ControlMeasurement>().await?;
634        let reg_ctrl_h = self.read_register::<ControlHumidity>().await?;
635
636        let o_h = reg_ctrl_h.read_oversampling();
637        let o_p = reg_ctrl_m.read_pressure_oversampling();
638
639        let raw_data = self.read_register::<BurstMeasurementsPTH>().await?;
640        let Some(ref cal) = self.calibration_data else {
641            return Err(MeasurementError::NotCalibrated);
642        };
643
644        let (temperature, t_fine) = cal.compensate_temperature(raw_data.read_temperature_value());
645        let pressure = (o_p != Oversampling::Disabled)
646            .then(|| cal.compensate_pressure(raw_data.read_pressure_value(), t_fine))
647            .flatten();
648        let humidity =
649            (o_h != Oversampling::Disabled).then(|| cal.compensate_humidity(raw_data.read_humidity_value(), t_fine));
650
651        Ok(Some(Measurement {
652            temperature,
653            pressure,
654            humidity,
655        }))
656    }
657
658    /// Not supported, always returns true.
659    async fn is_measurement_ready(&mut self) -> Result<bool, Self::Error> {
660        Ok(true)
661    }
662
663    /// Opportunistically waits one conversion interval and returns the measurement.
664    async fn next_measurement(&mut self) -> Result<Self::Measurement, Self::Error> {
665        let interval = self.measurement_interval_us().await?;
666        self.delay.delay_us(interval).await;
667        self.current_measurement().await?.ok_or_else(|| {
668            MeasurementError::Transport(TransportError::Unexpected(
669                "measurement was not ready even though we expected it to be",
670            ))
671        })
672    }
673}