embedded_devices/devices/bosch/
bmp280.rs

1//! The BMP280 is a combined digital 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 BMP280 is register and performance compatible to the Bosch Sensortec BMP280 digital
6//! pressure sensor.
7//!
8//! The BMP280 achieves high performance in all applications requiring temperature 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 pressure sensor is an absolute barometric pressure sensor with extremely high accuracy and
13//!   resolution and drastically lower noise than the Bosch Sensortec BMP180.
14//! - The integrated temperature sensor has been optimized for lowest noise and highest resolution. Its
15//!   output is used for temperature compensation of the pressure sensor and can also be
16//!   used for estimation of the ambient temperature.
17//!
18//! The sensor provides both SPI and I²C interfaces and can be supplied using 1.71 to 3.6 V for the
19//! sensor supply V DD and 1.2 to 3.6 V for the interface supply V DDIO. Measurements can be triggered by
20//! the host or performed in regular intervals. When the sensor is disabled, current consumption drops to
21//! 0.1 μA.
22//!
23//! BMP280 can be operated in three power modes:
24//! - sleep mode
25//! - normal mode
26//! - forced mode
27//!
28//! In order to tailor data rate, noise, response time and current consumption to the needs of the user, a
29//! variety of oversampling modes, filter modes and data rates can be selected.
30//!
31//! ## Usage (sync)
32//!
33//! ```rust
34//! # #[cfg(feature = "sync")] mod test {
35//! # fn test<I, D>(mut i2c: I, delay: D) -> Result<(), embedded_devices::devices::bosch::bme280::InitError<I::Error>>
36//! # where
37//! #   I: embedded_hal::i2c::I2c + embedded_hal::i2c::ErrorType,
38//! #   D: embedded_hal::delay::DelayNs
39//! # {
40//! use embedded_devices::devices::bosch::bmp280::{BMP280Sync, Configuration};
41//! use embedded_devices::devices::bosch::bme280::address::Address;
42//! use embedded_devices::devices::bosch::bme280::registers::{IIRFilter, Oversampling};
43//! use embedded_devices::sensor::OneshotSensorSync;
44//! use uom::si::pressure::pascal;
45//! use uom::si::thermodynamic_temperature::degree_celsius;
46//!
47//! // Create and initialize the device
48//! let mut bmp280 = BMP280Sync::new_i2c(delay, i2c, Address::Primary);
49//! bmp280.init()?;
50//! // Enable sensors
51//! bmp280.configure(Configuration {
52//!     temperature_oversampling: Oversampling::X_16,
53//!     pressure_oversampling: Oversampling::X_16,
54//!     iir_filter: IIRFilter::Disabled,
55//! })?;
56//!
57//! // Read measurement
58//! let measurement = bmp280.measure().unwrap();
59//! let temp = measurement.temperature.get::<degree_celsius>();
60//! let pressure = measurement.pressure.expect("should be enabled").get::<pascal>();
61//! println!("Current measurement: {:?}°C, {:?} Pa", temp, pressure);
62//! # Ok(())
63//! # }
64//! # }
65//! ```
66//!
67//! ## Usage (async)
68//!
69//! ```rust
70//! # #[cfg(feature = "async")] mod test {
71//! # async fn test<I, D>(mut i2c: I, delay: D) -> Result<(), embedded_devices::devices::bosch::bme280::InitError<I::Error>>
72//! # where
73//! #   I: embedded_hal_async::i2c::I2c + embedded_hal_async::i2c::ErrorType,
74//! #   D: embedded_hal_async::delay::DelayNs
75//! # {
76//! use embedded_devices::devices::bosch::bmp280::{BMP280Async, Configuration};
77//! use embedded_devices::devices::bosch::bme280::address::Address;
78//! use embedded_devices::devices::bosch::bme280::registers::{IIRFilter, Oversampling};
79//! use embedded_devices::sensor::OneshotSensorAsync;
80//! use uom::si::pressure::pascal;
81//! use uom::si::thermodynamic_temperature::degree_celsius;
82//!
83//! // Create and initialize the device
84//! let mut bmp280 = BMP280Async::new_i2c(delay, i2c, Address::Primary);
85//! bmp280.init().await?;
86//! // Enable sensors
87//! bmp280.configure(Configuration {
88//!     temperature_oversampling: Oversampling::X_16,
89//!     pressure_oversampling: Oversampling::X_16,
90//!     iir_filter: IIRFilter::Disabled,
91//! }).await?;
92//!
93//! // Read measurement
94//! let measurement = bmp280.measure().await.unwrap();
95//! let temp = measurement.temperature.get::<degree_celsius>();
96//! let pressure = measurement.pressure.expect("should be enabled").get::<pascal>();
97//! println!("Current measurement: {:?}°C, {:?} Pa", temp, pressure);
98//! # Ok(())
99//! # }
100//! # }
101//! ```
102
103use embedded_devices_derive::sensor;
104use embedded_interfaces::TransportError;
105use uom::si::f64::{Pressure, ThermodynamicTemperature};
106
107use super::bme280::{
108    MeasurementError,
109    registers::{BurstMeasurementsPT, Config, ControlMeasurement, IIRFilter, Oversampling, SensorMode},
110};
111
112#[cfg(feature = "async")]
113use super::bme280::BME280CommonAsync;
114#[cfg(feature = "sync")]
115use super::bme280::BME280CommonSync;
116
117/// Measurement data
118#[derive(Debug, embedded_devices_derive::Measurement)]
119pub struct Measurement {
120    /// Current temperature
121    #[measurement(Temperature)]
122    pub temperature: ThermodynamicTemperature,
123    /// Current pressure or None if the sensor reported and invalid value
124    #[measurement(Pressure)]
125    pub pressure: Option<Pressure>,
126}
127
128/// The BMP280 is a combined digital pressure and temperature sensor based on proven
129/// sensing principles. The sensor module is housed in an extremely compact metal-lid LGA package.
130/// a footprint of only 2.5 × 2.5 mm² with a height of 0.93 mm. Its small dimensions and its low power
131/// consumption allow the implementation in battery driven devices such as handsets, GPS modules or
132/// watches.
133#[cfg(feature = "sync")]
134pub type BMP280Sync<D, I> = BME280CommonSync<D, I, false>;
135
136/// The BMP280 is a combined digital pressure and temperature sensor based on proven
137/// sensing principles. The sensor module is housed in an extremely compact metal-lid LGA package.
138/// a footprint of only 2.5 × 2.5 mm² with a height of 0.93 mm. Its small dimensions and its low power
139/// consumption allow the implementation in battery driven devices such as handsets, GPS modules or
140/// watches.
141#[cfg(feature = "async")]
142pub type BMP280Async<D, I> = BME280CommonAsync<D, I, false>;
143
144/// Common configuration values for the BMP280 sensor.
145#[derive(Debug, Clone)]
146pub struct Configuration {
147    /// The oversampling rate for temperature mesurements
148    pub temperature_oversampling: Oversampling,
149    /// The oversampling rate for pressure mesurements
150    pub pressure_oversampling: Oversampling,
151    /// The iir filter to use
152    pub iir_filter: IIRFilter,
153}
154
155impl Default for Configuration {
156    fn default() -> Self {
157        Self {
158            temperature_oversampling: Oversampling::X_1,
159            pressure_oversampling: Oversampling::X_1,
160            iir_filter: IIRFilter::Disabled,
161        }
162    }
163}
164
165#[sensor(Temperature, Pressure)]
166#[maybe_async_cfg::maybe(
167    idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), RegisterInterface),
168    sync(feature = "sync"),
169    async(feature = "async")
170)]
171impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> BME280Common<D, I, false> {
172    /// Configures common sensor settings. Sensor must be in sleep mode for this to work. To
173    /// configure advanced settings, please directly update the respective registers.
174    pub async fn configure(&mut self, config: Configuration) -> Result<(), TransportError<(), I::BusError>> {
175        let reg_ctrl_m = self.read_register::<ControlMeasurement>().await?;
176        self.write_register(
177            reg_ctrl_m
178                .with_temperature_oversampling(config.temperature_oversampling)
179                .with_pressure_oversampling(config.pressure_oversampling),
180        )
181        .await?;
182
183        let mut reg_config = self.read_register::<Config>().await?;
184        reg_config.write_filter(config.iir_filter);
185        self.write_register(reg_config).await?;
186
187        Ok(())
188    }
189}
190
191#[maybe_async_cfg::maybe(
192    idents(
193        hal(sync = "embedded_hal", async = "embedded_hal_async"),
194        RegisterInterface,
195        OneshotSensor
196    ),
197    sync(feature = "sync"),
198    async(feature = "async")
199)]
200impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::sensor::OneshotSensor
201    for BME280Common<D, I, false>
202{
203    type Error = MeasurementError<I::BusError>;
204    type Measurement = Measurement;
205
206    /// Performs a one-shot measurement. This will transition the device into forced mode,
207    /// which will cause it to take a measurement and return to sleep mode afterwards.
208    ///
209    /// This function will wait until the data is acquired, perform a burst read and compensate the
210    /// returned raw data using the calibration data.
211    async fn measure(&mut self) -> Result<Self::Measurement, Self::Error> {
212        let reg_ctrl_m = self.read_register::<ControlMeasurement>().await?;
213        self.write_register(reg_ctrl_m.with_sensor_mode(SensorMode::Forced))
214            .await?;
215
216        // Use current oversampling config to determine required measurement delay
217        let o_t = reg_ctrl_m.read_temperature_oversampling();
218        let o_p = reg_ctrl_m.read_pressure_oversampling();
219
220        // Maximum time required to perform the measurement.
221        // See chapter 9 of the datasheet for more information.
222        let mut max_measurement_delay_us = 1250 + 2300 * o_t.factor();
223        if o_p.factor() > 0 {
224            max_measurement_delay_us += 575 + 2300 * o_p.factor();
225        }
226
227        self.delay.delay_us(max_measurement_delay_us).await;
228
229        let raw_data = self.read_register::<BurstMeasurementsPT>().await?;
230        let Some(ref cal) = self.calibration_data else {
231            return Err(MeasurementError::NotCalibrated);
232        };
233
234        let (temperature, t_fine) = cal.compensate_temperature(raw_data.read_temperature_value());
235        let pressure = cal.compensate_pressure(raw_data.read_pressure_value(), t_fine);
236
237        Ok(Measurement { temperature, pressure })
238    }
239}
240
241#[maybe_async_cfg::maybe(
242    idents(
243        hal(sync = "embedded_hal", async = "embedded_hal_async"),
244        RegisterInterface,
245        ContinuousSensor
246    ),
247    sync(feature = "sync"),
248    async(feature = "async")
249)]
250impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::sensor::ContinuousSensor
251    for BME280Common<D, I, false>
252{
253    type Error = MeasurementError<I::BusError>;
254    type Measurement = Measurement;
255
256    /// Starts continuous measurement.
257    async fn start_measuring(&mut self) -> Result<(), Self::Error> {
258        let reg_ctrl_m = self.read_register::<ControlMeasurement>().await?;
259        self.write_register(reg_ctrl_m.with_sensor_mode(SensorMode::Normal))
260            .await?;
261        Ok(())
262    }
263
264    /// Stops continuous measurement.
265    async fn stop_measuring(&mut self) -> Result<(), Self::Error> {
266        let reg_ctrl_m = self.read_register::<ControlMeasurement>().await?;
267        self.write_register(reg_ctrl_m.with_sensor_mode(SensorMode::Sleep))
268            .await?;
269        Ok(())
270    }
271
272    /// Expected amount of time between measurements in microseconds.
273    async fn measurement_interval_us(&mut self) -> Result<u32, Self::Error> {
274        let reg_config = self.read_register::<Config>().await?;
275        let t_standby_us = reg_config.read_standby_time().time_us();
276
277        let reg_ctrl_m = self.read_register::<ControlMeasurement>().await?;
278        let o_t = reg_ctrl_m.read_temperature_oversampling();
279        let o_p = reg_ctrl_m.read_pressure_oversampling();
280
281        // Maximum time required to perform the measurement.
282        // See chapter 9 of the datasheet for more information.
283        let mut t_measure_us = 1250 + 2300 * o_t.factor();
284        if o_p.factor() > 0 {
285            t_measure_us += 575 + 2300 * o_p.factor();
286        }
287        Ok(t_measure_us + t_standby_us)
288    }
289
290    /// Returns the most recent measurement. Will never return None.
291    async fn current_measurement(&mut self) -> Result<Option<Self::Measurement>, Self::Error> {
292        let reg_ctrl_m = self.read_register::<ControlMeasurement>().await?;
293        let o_p = reg_ctrl_m.read_pressure_oversampling();
294
295        let raw_data = self.read_register::<BurstMeasurementsPT>().await?;
296        let Some(ref cal) = self.calibration_data else {
297            return Err(MeasurementError::NotCalibrated);
298        };
299
300        let (temperature, t_fine) = cal.compensate_temperature(raw_data.read_temperature_value());
301        let pressure = (o_p != Oversampling::Disabled)
302            .then(|| cal.compensate_pressure(raw_data.read_pressure_value(), t_fine))
303            .flatten();
304
305        Ok(Some(Measurement { temperature, pressure }))
306    }
307
308    /// Not supported, always returns true.
309    async fn is_measurement_ready(&mut self) -> Result<bool, Self::Error> {
310        Ok(true)
311    }
312
313    /// Opportunistically waits one conversion interval and returns the measurement.
314    async fn next_measurement(&mut self) -> Result<Self::Measurement, Self::Error> {
315        let interval = self.measurement_interval_us().await?;
316        self.delay.delay_us(interval).await;
317        self.current_measurement().await?.ok_or_else(|| {
318            MeasurementError::Transport(TransportError::Unexpected(
319                "measurement was not ready even though we expected it to be",
320            ))
321        })
322    }
323}