embedded_drivers/
bme280.rs

1//! BME280, BMP280 driver.
2//!
3//! via https://github.com/uber-foo/bme280-rs
4//!
5//! Humidity resolution: 0.008 %RH
6//! Pressure resolution: 0.18 Pa
7//! Temperature resolution: 0.01 C
8
9use core::marker::PhantomData;
10use embedded_hal::blocking::delay::DelayMs;
11use embedded_hal::blocking::i2c::{Read, Write, WriteRead};
12
13#[cfg(feature = "serde")]
14use serde::Serialize;
15
16const BME280_I2C_ADDR_PRIMARY: u8 = 0x76;
17const BME280_I2C_ADDR_SECONDARY: u8 = 0x77;
18
19const BME280_PWR_CTRL_ADDR: u8 = 0xF4;
20const BME280_CTRL_HUM_ADDR: u8 = 0xF2;
21const BME280_CTRL_MEAS_ADDR: u8 = 0xF4;
22const BME280_CONFIG_ADDR: u8 = 0xF5;
23
24const BME280_RESET_ADDR: u8 = 0xE0;
25const BME280_SOFT_RESET_CMD: u8 = 0xB6;
26
27const BME280_CHIP_ID: u8 = 0x60;
28const BMP280_CHIP_ID: u8 = 0x58;
29const BME280_CHIP_ID_ADDR: u8 = 0xD0;
30
31const BME280_DATA_ADDR: u8 = 0xF7;
32const BME280_P_T_H_DATA_LEN: usize = 8;
33
34const BME280_P_T_CALIB_DATA_ADDR: u8 = 0x88;
35const BME280_P_T_CALIB_DATA_LEN: usize = 26;
36
37const BME280_H_CALIB_DATA_ADDR: u8 = 0xE1;
38const BME280_H_CALIB_DATA_LEN: usize = 7;
39
40const BME280_TEMP_MIN: f32 = -40.0;
41const BME280_TEMP_MAX: f32 = 85.0;
42
43const BME280_PRESSURE_MIN: f32 = 30000.0;
44const BME280_PRESSURE_MAX: f32 = 110000.0;
45
46const BME280_HUMIDITY_MIN: f32 = 0.0;
47const BME280_HUMIDITY_MAX: f32 = 100.0;
48
49const BME280_SLEEP_MODE: u8 = 0x00;
50const BME280_FORCED_MODE: u8 = 0x01;
51const BME280_NORMAL_MODE: u8 = 0x03;
52
53const BME280_SENSOR_MODE_MSK: u8 = 0x03;
54
55const BME280_CTRL_HUM_MSK: u8 = 0x07;
56
57const BME280_CTRL_PRESS_MSK: u8 = 0x1C;
58const BME280_CTRL_PRESS_POS: u8 = 0x02;
59
60const BME280_CTRL_TEMP_MSK: u8 = 0xE0;
61const BME280_CTRL_TEMP_POS: u8 = 0x05;
62
63const BME280_FILTER_MSK: u8 = 0x1C;
64const BME280_FILTER_POS: u8 = 0x02;
65const BME280_FILTER_COEFF_16: u8 = 0x04;
66
67const BME280_OVERSAMPLING_1X: u8 = 0x01;
68const BME280_OVERSAMPLING_2X: u8 = 0x02;
69const BME280_OVERSAMPLING_16X: u8 = 0x05;
70
71macro_rules! concat_bytes {
72    ($msb:expr, $lsb:expr) => {
73        (($msb as u16) << 8) | ($lsb as u16)
74    };
75}
76
77macro_rules! set_bits {
78    ($reg_data:expr, $mask:expr, $pos:expr, $data:expr) => {
79        ($reg_data & !$mask) | (($data << $pos) & $mask)
80    };
81}
82
83/// BME280 errors
84#[derive(Debug)]
85pub enum Error<E> {
86    /// Failed to compensate a raw measurement
87    CompensationFailed,
88    /// I²C bus error
89    I2c(E),
90    /// Failed to parse sensor data
91    InvalidData,
92    /// No calibration data is available (probably forgot to call or check BME280::init for failure)
93    NoCalibrationData,
94    /// Chip ID doesn't match expected value
95    UnsupportedChip,
96}
97
98/// BME280 operating mode
99#[derive(Debug, Copy, Clone)]
100pub enum SensorMode {
101    /// Sleep mode
102    Sleep,
103    /// Forced mode
104    Forced,
105    /// Normal mode
106    Normal,
107}
108
109#[derive(Debug)]
110struct CalibrationData {
111    dig_t1: u16,
112    dig_t2: i16,
113    dig_t3: i16,
114    dig_p1: u16,
115    dig_p2: i16,
116    dig_p3: i16,
117    dig_p4: i16,
118    dig_p5: i16,
119    dig_p6: i16,
120    dig_p7: i16,
121    dig_p8: i16,
122    dig_p9: i16,
123    dig_h1: u8,
124    dig_h2: i16,
125    dig_h3: u8,
126    dig_h4: i16,
127    dig_h5: i16,
128    dig_h6: i8,
129    t_fine: i32,
130}
131
132/// Measurement data
133#[cfg_attr(feature = "serde", derive(Serialize))]
134#[cfg_attr(feature = "defmt", derive(defmt::Format))]
135pub struct Measurements {
136    /// temperature in degrees celsius
137    pub temperature: f32,
138    /// pressure in pascals
139    pub pressure: f32,
140    /// percent relative humidity (`0` with BMP280)
141    pub humidity: f32,
142}
143
144impl Measurements {
145    fn parse<E>(
146        data: [u8; BME280_P_T_H_DATA_LEN],
147        calibration: &mut CalibrationData,
148    ) -> Result<Self, Error<E>> {
149        let data_msb: u32 = (data[0] as u32) << 12;
150        let data_lsb: u32 = (data[1] as u32) << 4;
151        let data_xlsb: u32 = (data[2] as u32) >> 4;
152        let pressure = data_msb | data_lsb | data_xlsb;
153
154        let data_msb: u32 = (data[3] as u32) << 12;
155        let data_lsb: u32 = (data[4] as u32) << 4;
156        let data_xlsb: u32 = (data[5] as u32) >> 4;
157        let temperature = data_msb | data_lsb | data_xlsb;
158
159        let data_msb: u32 = (data[6] as u32) << 8;
160        let data_lsb: u32 = data[7] as u32;
161        let humidity = data_msb | data_lsb;
162
163        let temperature = Measurements::compensate_temperature(temperature, calibration)?;
164        let pressure = Measurements::compensate_pressure(pressure, calibration)?;
165        let humidity = Measurements::compensate_humidity(humidity, calibration)?;
166
167        Ok(Measurements {
168            temperature,
169            pressure,
170            humidity,
171        })
172    }
173
174    fn compensate_temperature<E>(
175        uncompensated: u32,
176        calibration: &mut CalibrationData,
177    ) -> Result<f32, Error<E>> {
178        let var1: f32 = uncompensated as f32 / 16384.0 - calibration.dig_t1 as f32 / 1024.0;
179        let var1 = var1 * calibration.dig_t2 as f32;
180        let var2 = uncompensated as f32 / 131072.0 - calibration.dig_t1 as f32 / 8192.0;
181        let var2 = var2 * var2 * calibration.dig_t3 as f32;
182
183        calibration.t_fine = (var1 + var2) as i32;
184
185        let temperature = (var1 + var2) / 5120.0;
186        let temperature = if temperature < BME280_TEMP_MIN {
187            BME280_TEMP_MIN
188        } else if temperature > BME280_TEMP_MAX {
189            BME280_TEMP_MAX
190        } else {
191            temperature
192        };
193        Ok(temperature)
194    }
195
196    fn compensate_pressure<E>(
197        uncompensated: u32,
198        calibration: &mut CalibrationData,
199    ) -> Result<f32, Error<E>> {
200        let var1: f32 = calibration.t_fine as f32 / 2.0 - 64000.0;
201        let var2: f32 = var1 * var1 * calibration.dig_p6 as f32 / 32768.0;
202        let var2: f32 = var2 + var1 * calibration.dig_p5 as f32 * 2.0;
203        let var2: f32 = var2 / 4.0 + calibration.dig_p4 as f32 * 65536.0;
204        let var3: f32 = calibration.dig_p3 as f32 * var1 * var1 / 524288.0;
205        let var1: f32 = (var3 + calibration.dig_p2 as f32 * var1) / 524288.0;
206        let var1: f32 = (1.0 + var1 / 32768.0) * calibration.dig_p1 as f32;
207
208        let pressure = if var1 > 0.0 {
209            let pressure: f32 = 1048576.0 - uncompensated as f32;
210            let pressure: f32 = (pressure - (var2 / 4096.0)) * 6250.0 / var1;
211            let var1: f32 = calibration.dig_p9 as f32 * pressure * pressure / 2147483648.0;
212            let var2: f32 = pressure * calibration.dig_p8 as f32 / 32768.0;
213            let pressure: f32 = pressure + (var1 + var2 + calibration.dig_p7 as f32) / 16.0;
214            if pressure < BME280_PRESSURE_MIN {
215                BME280_PRESSURE_MIN
216            } else if pressure > BME280_PRESSURE_MAX {
217                BME280_PRESSURE_MAX
218            } else {
219                pressure
220            }
221        } else {
222            return Err(Error::InvalidData);
223        };
224        Ok(pressure)
225    }
226
227    fn compensate_humidity<E>(
228        uncompensated: u32,
229        calibration: &mut CalibrationData,
230    ) -> Result<f32, Error<E>> {
231        let var1: f32 = calibration.t_fine as f32 - 76800.0;
232        let var2: f32 =
233            calibration.dig_h4 as f32 * 64.0 + (calibration.dig_h5 as f32 / 16384.0) * var1;
234        let var3: f32 = uncompensated as f32 - var2;
235        let var4: f32 = calibration.dig_h2 as f32 / 65536.0;
236        let var5: f32 = 1.0 + (calibration.dig_h3 as f32 / 67108864.0) * var1;
237        let var6: f32 = 1.0 + (calibration.dig_h6 as f32 / 67108864.0) * var1 * var5;
238        let var6: f32 = var3 * var4 * (var5 * var6);
239
240        let humidity: f32 = var6 * (1.0 - calibration.dig_h1 as f32 * var6 / 524288.0);
241        let humidity = if humidity < BME280_HUMIDITY_MIN {
242            BME280_HUMIDITY_MIN
243        } else if humidity > BME280_HUMIDITY_MAX {
244            BME280_HUMIDITY_MAX
245        } else {
246            humidity
247        };
248        Ok(humidity)
249    }
250}
251
252/// Representation of a BME280
253#[derive(Debug, Default)]
254pub struct BME280<I2C> {
255    /// concrete I²C device implementation
256    i2c: I2C,
257    /// I²C device address
258    address: u8,
259    /// calibration data
260    calibration: Option<CalibrationData>,
261}
262
263impl<I2C, E> BME280<I2C>
264where
265    I2C: Read<Error = E> + Write<Error = E> + WriteRead<Error = E>,
266{
267    /// Create a new BME280 struct using the primary I²C address `0x76`
268    pub fn new_primary(i2c: I2C) -> Self {
269        Self::new(i2c, BME280_I2C_ADDR_PRIMARY)
270    }
271
272    /// Create a new BME280 struct using the secondary I²C address `0x77`
273    pub fn new_secondary(i2c: I2C) -> Self {
274        Self::new(i2c, BME280_I2C_ADDR_SECONDARY)
275    }
276
277    /// Create a new BME280 struct using a custom I²C address
278    pub fn new(i2c: I2C, address: u8) -> Self {
279        BME280 {
280            i2c,
281            address,
282            calibration: None,
283        }
284    }
285
286    /// Initializes the BME280
287    pub fn init<D: DelayMs<u8>>(&mut self, delay: &mut D) -> Result<(), Error<E>> {
288        self.verify_chip_id()?;
289        self.soft_reset(delay)?;
290        self.calibrate()?;
291        self.configure(delay)
292    }
293
294    pub fn release(self) -> I2C {
295        self.i2c
296    }
297
298    fn verify_chip_id(&mut self) -> Result<(), Error<E>> {
299        let chip_id = self.read_register(BME280_CHIP_ID_ADDR)?;
300        if chip_id == BME280_CHIP_ID || chip_id == BMP280_CHIP_ID {
301            Ok(())
302        } else {
303            Err(Error::UnsupportedChip)
304        }
305    }
306
307    fn soft_reset<D: DelayMs<u8>>(&mut self, delay: &mut D) -> Result<(), Error<E>> {
308        self.write_register(BME280_RESET_ADDR, BME280_SOFT_RESET_CMD)?;
309        delay.delay_ms(2); // startup time is 2ms
310        Ok(())
311    }
312
313    fn calibrate(&mut self) -> Result<(), Error<E>> {
314        let pt_calib_data = self.read_pt_calib_data(BME280_P_T_CALIB_DATA_ADDR)?;
315        let h_calib_data = self.read_h_calib_data(BME280_H_CALIB_DATA_ADDR)?;
316        self.calibration = Some(parse_calib_data(&pt_calib_data, &h_calib_data));
317        Ok(())
318    }
319
320    fn configure<D: DelayMs<u8>>(&mut self, delay: &mut D) -> Result<(), Error<E>> {
321        match self.mode()? {
322            SensorMode::Sleep => {}
323            _ => self.soft_reset(delay)?,
324        };
325
326        self.write_register(
327            BME280_CTRL_HUM_ADDR,
328            BME280_OVERSAMPLING_1X & BME280_CTRL_HUM_MSK,
329        )?;
330        let ctrl_meas = self.read_register(BME280_CTRL_MEAS_ADDR)?;
331        self.write_register(BME280_CTRL_MEAS_ADDR, ctrl_meas)?;
332
333        let data = self.read_register(BME280_CTRL_MEAS_ADDR)?;
334        let data = set_bits!(
335            data,
336            BME280_CTRL_PRESS_MSK,
337            BME280_CTRL_PRESS_POS,
338            BME280_OVERSAMPLING_16X
339        );
340        let data = set_bits!(
341            data,
342            BME280_CTRL_TEMP_MSK,
343            BME280_CTRL_TEMP_POS,
344            BME280_OVERSAMPLING_2X
345        );
346        self.write_register(BME280_CTRL_MEAS_ADDR, data)?;
347
348        let data = self.read_register(BME280_CONFIG_ADDR)?;
349        let data = set_bits!(
350            data,
351            BME280_FILTER_MSK,
352            BME280_FILTER_POS,
353            BME280_FILTER_COEFF_16
354        );
355        self.write_register(BME280_CONFIG_ADDR, data)
356    }
357
358    fn mode(&mut self) -> Result<SensorMode, Error<E>> {
359        let mut data: [u8; 1] = [0];
360        self.i2c
361            .write_read(self.address, &[BME280_PWR_CTRL_ADDR], &mut data)
362            .map_err(Error::I2c)?;
363        match data[0] & BME280_SENSOR_MODE_MSK {
364            BME280_SLEEP_MODE => Ok(SensorMode::Sleep),
365            BME280_FORCED_MODE => Ok(SensorMode::Forced),
366            BME280_NORMAL_MODE => Ok(SensorMode::Normal),
367            _ => Err(Error::InvalidData),
368        }
369    }
370
371    fn forced<D: DelayMs<u8>>(&mut self, delay: &mut D) -> Result<(), Error<E>> {
372        self.set_mode(BME280_FORCED_MODE, delay)
373    }
374
375    fn set_mode<D: DelayMs<u8>>(&mut self, mode: u8, delay: &mut D) -> Result<(), Error<E>> {
376        match self.mode()? {
377            SensorMode::Sleep => {}
378            _ => self.soft_reset(delay)?,
379        };
380        let data = self.read_register(BME280_PWR_CTRL_ADDR)?;
381        let data = set_bits!(data, BME280_SENSOR_MODE_MSK, 0, mode);
382        self.write_register(BME280_PWR_CTRL_ADDR, data)
383    }
384
385    /// Captures and processes sensor data for temperature, pressure, and humidity
386    pub fn measure<D: DelayMs<u8>>(&mut self, delay: &mut D) -> Result<Measurements, Error<E>> {
387        self.forced(delay)?;
388        delay.delay_ms(40); // await measurement
389        let measurements = self.read_data(BME280_DATA_ADDR)?;
390        match self.calibration.as_mut() {
391            Some(calibration) => {
392                let measurements = Measurements::parse(measurements, &mut *calibration)?;
393                Ok(measurements)
394            }
395            None => Err(Error::NoCalibrationData),
396        }
397    }
398
399    fn read_register(&mut self, register: u8) -> Result<u8, Error<E>> {
400        let mut data: [u8; 1] = [0];
401        self.i2c
402            .write_read(self.address, &[register], &mut data)
403            .map_err(Error::I2c)?;
404        Ok(data[0])
405    }
406
407    fn read_data(&mut self, register: u8) -> Result<[u8; BME280_P_T_H_DATA_LEN], Error<E>> {
408        let mut data: [u8; BME280_P_T_H_DATA_LEN] = [0; BME280_P_T_H_DATA_LEN];
409        self.i2c
410            .write_read(self.address, &[register], &mut data)
411            .map_err(Error::I2c)?;
412        Ok(data)
413    }
414
415    fn read_pt_calib_data(
416        &mut self,
417        register: u8,
418    ) -> Result<[u8; BME280_P_T_CALIB_DATA_LEN], Error<E>> {
419        let mut data: [u8; BME280_P_T_CALIB_DATA_LEN] = [0; BME280_P_T_CALIB_DATA_LEN];
420        self.i2c
421            .write_read(self.address, &[register], &mut data)
422            .map_err(Error::I2c)?;
423        Ok(data)
424    }
425
426    fn read_h_calib_data(
427        &mut self,
428        register: u8,
429    ) -> Result<[u8; BME280_H_CALIB_DATA_LEN], Error<E>> {
430        let mut data: [u8; BME280_H_CALIB_DATA_LEN] = [0; BME280_H_CALIB_DATA_LEN];
431        self.i2c
432            .write_read(self.address, &[register], &mut data)
433            .map_err(Error::I2c)?;
434        Ok(data)
435    }
436
437    fn write_register(&mut self, register: u8, payload: u8) -> Result<(), Error<E>> {
438        self.i2c
439            .write(self.address, &[register, payload])
440            .map_err(Error::I2c)
441    }
442}
443
444fn parse_calib_data(
445    pt_data: &[u8; BME280_P_T_CALIB_DATA_LEN],
446    h_data: &[u8; BME280_H_CALIB_DATA_LEN],
447) -> CalibrationData {
448    let dig_t1 = concat_bytes!(pt_data[1], pt_data[0]);
449    let dig_t2 = concat_bytes!(pt_data[3], pt_data[2]) as i16;
450    let dig_t3 = concat_bytes!(pt_data[5], pt_data[4]) as i16;
451    let dig_p1 = concat_bytes!(pt_data[7], pt_data[6]);
452    let dig_p2 = concat_bytes!(pt_data[9], pt_data[8]) as i16;
453    let dig_p3 = concat_bytes!(pt_data[11], pt_data[10]) as i16;
454    let dig_p4 = concat_bytes!(pt_data[13], pt_data[12]) as i16;
455    let dig_p5 = concat_bytes!(pt_data[15], pt_data[14]) as i16;
456    let dig_p6 = concat_bytes!(pt_data[17], pt_data[16]) as i16;
457    let dig_p7 = concat_bytes!(pt_data[19], pt_data[18]) as i16;
458    let dig_p8 = concat_bytes!(pt_data[21], pt_data[20]) as i16;
459    let dig_p9 = concat_bytes!(pt_data[23], pt_data[22]) as i16;
460    let dig_h1 = pt_data[25];
461    let dig_h2 = concat_bytes!(h_data[1], h_data[0]) as i16;
462    let dig_h3 = h_data[2];
463    let dig_h4 = (h_data[3] as i16 * 16) | ((h_data[4] as i16) & 0x0F);
464    let dig_h5 = (h_data[5] as i16 * 16) | ((h_data[4] as i16) >> 4);
465    let dig_h6 = h_data[6] as i8;
466
467    CalibrationData {
468        dig_t1,
469        dig_t2,
470        dig_t3,
471        dig_p1,
472        dig_p2,
473        dig_p3,
474        dig_p4,
475        dig_p5,
476        dig_p6,
477        dig_p7,
478        dig_p8,
479        dig_p9,
480        dig_h1,
481        dig_h2,
482        dig_h3,
483        dig_h4,
484        dig_h5,
485        dig_h6,
486        t_fine: 0,
487    }
488}