Skip to main content

pamoja_sensors/bmp280/
driver.rs

1//! The BMP280 driven over a bus: reset, identify, calibrate, and measure on demand.
2
3use embedded_hal::delay::DelayNs;
4use embedded_hal::i2c::I2c;
5use embedded_hal::spi::SpiDevice;
6use pamoja_core::Sensor;
7
8use super::{
9    image_updating, max_measurement_micros, measuring, register, Calibration, Config, CtrlMeas,
10    Measurement, Mode, Oversampling, Reading, CALIBRATION_LEN, CHIP_ID, DATA_LEN, RESET_WORD,
11    STARTUP_MICROS,
12};
13use crate::driver::{I2cRegisters, RegisterBus, SpiRegisters};
14use crate::error::{DriverError, SensorError};
15
16/// How many times the status register is polled, 1 ms apart, before a conversion or
17/// a reset is called overdue.
18pub const STATUS_POLLS: u8 = 20;
19
20/// A BMP280 on an I2C or SPI bus, measuring on demand in forced mode.
21///
22/// [`Bmp280::i2c`] or [`Bmp280::spi`] wraps the bus. [`init`](Bmp280::init) resets the
23/// part, checks its chip id, reads its trimming coefficients, and writes the
24/// oversampling and filter settings while the part is asleep, the one state in which
25/// the datasheet promises a `config` write is not ignored. [`measure`](Bmp280::measure)
26/// runs one forced-mode conversion, waits the datasheet's maximum measurement time
27/// for the settings in use, confirms the part is idle, and returns the compensated
28/// [`Reading`]. As a [`Sensor`] its reading is the whole measurement; one channel is
29/// selected with [`Sensor::map`].
30///
31/// # Examples
32///
33/// A drone's barometer, read from a BMP280 that is not plugged in yet: the simulated part
34/// holds 25 °C and 1006.5 hPa, and the driver compensates its raw readings with the
35/// trimming the part carries.
36///
37/// ```
38/// use pamoja_core::Sensor;
39/// use pamoja_hal::script::{block_on, DelayLog};
40/// use pamoja_sensors::bmp280::{sim, Bmp280, I2C_ADDRESS_PRIMARY};
41///
42/// let part = sim::reporting(I2C_ADDRESS_PRIMARY, 25.0, 1006.5);
43/// let mut barometer = Bmp280::i2c(part, I2C_ADDRESS_PRIMARY, DelayLog::new());
44/// let reading = block_on(barometer.read())?;
45/// assert!((reading.celsius() - 25.0).abs() < 0.01);
46/// assert!((reading.hectopascals() - 1006.5).abs() < 0.1);
47/// # Ok::<(), pamoja_core::Error>(())
48/// ```
49#[derive(Debug)]
50pub struct Bmp280<B, D> {
51    bus: B,
52    delay: D,
53    temperature: Oversampling,
54    pressure: Oversampling,
55    filter: u8,
56    calibration: Option<Calibration>,
57}
58
59impl<I2C: I2c, D> Bmp280<I2cRegisters<I2C>, D> {
60    /// Wraps an I2C bus and the part's address.
61    ///
62    /// # Arguments
63    ///
64    /// * `bus` - the I2C bus the part is on.
65    /// * `address` - [`I2C_ADDRESS_PRIMARY`](super::I2C_ADDRESS_PRIMARY) with SDO
66    ///   to ground, [`I2C_ADDRESS_SECONDARY`](super::I2C_ADDRESS_SECONDARY) with SDO
67    ///   to VDDIO.
68    /// * `delay` - the timer that paces the reset and the conversions.
69    ///
70    /// # Returns
71    ///
72    /// The driver, with both measurements at oversampling x1 and the filter off.
73    pub fn i2c(bus: I2C, address: u8, delay: D) -> Self {
74        Bmp280::new(I2cRegisters::new(bus, address), delay)
75    }
76}
77
78impl<SPI: SpiDevice, D> Bmp280<SpiRegisters<SPI>, D> {
79    /// Wraps an SPI device, the bus plus the part's chip-select line.
80    ///
81    /// # Arguments
82    ///
83    /// * `device` - the SPI device, in mode 0 or 3.
84    /// * `delay` - the timer that paces the reset and the conversions.
85    ///
86    /// # Returns
87    ///
88    /// The driver, with both measurements at oversampling x1 and the filter off.
89    pub fn spi(device: SPI, delay: D) -> Self {
90        Bmp280::new(SpiRegisters::new(device), delay)
91    }
92}
93
94impl<B, D> Bmp280<B, D> {
95    /// Wraps any register bus.
96    ///
97    /// # Arguments
98    ///
99    /// * `bus` - the part's register map, over whichever bus reaches it.
100    /// * `delay` - the timer that paces the reset and the conversions.
101    ///
102    /// # Returns
103    ///
104    /// The driver, with both measurements at oversampling x1 and the filter off.
105    pub fn new(bus: B, delay: D) -> Self {
106        Bmp280 {
107            bus,
108            delay,
109            temperature: Oversampling::X1,
110            pressure: Oversampling::X1,
111            filter: 0,
112            calibration: None,
113        }
114    }
115
116    /// Sets the oversampling of each measurement; `Skipped` leaves one out.
117    ///
118    /// The settings are written to the part by [`init`](Bmp280::init).
119    ///
120    /// # Arguments
121    ///
122    /// * `temperature` - temperature oversampling.
123    /// * `pressure` - pressure oversampling.
124    ///
125    /// # Returns
126    ///
127    /// The driver.
128    pub fn with_oversampling(mut self, temperature: Oversampling, pressure: Oversampling) -> Self {
129        self.temperature = temperature;
130        self.pressure = pressure;
131        self
132    }
133
134    /// Sets the IIR filter that smooths pressure and temperature.
135    ///
136    /// The setting is written to the part by [`init`](Bmp280::init).
137    ///
138    /// # Arguments
139    ///
140    /// * `code` - the raw three-bit `filter[2:0]` code, `0` for the filter off. The
141    ///   datasheet lists the coefficients without publishing which code selects
142    ///   which, so the code is written as given; only its low three bits are used.
143    ///
144    /// # Returns
145    ///
146    /// The driver.
147    pub fn with_filter(mut self, code: u8) -> Self {
148        self.filter = code;
149        self
150    }
151
152    /// Returns the trimming coefficients read at initialization, if it has happened.
153    pub fn calibration(&self) -> Option<&Calibration> {
154        self.calibration.as_ref()
155    }
156
157    /// Gives back the bus and the delay.
158    ///
159    /// # Returns
160    ///
161    /// The bus and delay the driver was built from.
162    pub fn release(self) -> (B, D) {
163        (self.bus, self.delay)
164    }
165}
166
167impl<B: RegisterBus, D: DelayNs> Bmp280<B, D> {
168    /// Resets the part, checks its identity, reads its trimming coefficients, and
169    /// writes the settings.
170    ///
171    /// The sequence is the datasheet's: the soft-reset word of section 4.3.2, which
172    /// runs the complete power-on-reset procedure, the start-up time of Table 2, a
173    /// wait for the `im_update` flag of Table 19 to clear, the chip id of section
174    /// 4.3.1, the trimming block of Table 17, then `config` and `ctrl_meas` (sections
175    /// 4.3.5 and 4.3.4) written while the part is asleep, leaving it asleep.
176    ///
177    /// # Errors
178    ///
179    /// Returns [`DriverError::Bus`] if the bus fails, [`DriverError::Timeout`] if the
180    /// trimming image never finishes loading, and [`DriverError::Sensor`] with
181    /// [`SensorError::Identity`] if the chip id is not the 0x58 section 4.3.1 fixes
182    /// for a BMP280; the datasheet names no other value.
183    pub fn init(&mut self) -> Result<(), DriverError<B::Error>> {
184        self.write(register::RESET, RESET_WORD)?;
185        self.delay.delay_us(STARTUP_MICROS);
186        self.wait(image_updating)?;
187
188        if self.read(register::CHIP_ID)? != CHIP_ID {
189            return Err(SensorError::Identity.into());
190        }
191
192        let mut trimming = [0u8; CALIBRATION_LEN];
193        self.bus
194            .read_registers(register::CALIBRATION, &mut trimming)
195            .map_err(DriverError::Bus)?;
196        self.calibration = Some(Calibration::parse(&trimming));
197
198        let config = Config {
199            filter: self.filter,
200            ..Config::default()
201        };
202        self.write(register::CONFIG, config.bits())?;
203        self.write(register::CTRL_MEAS, self.ctrl_meas(Mode::Sleep).bits())?;
204        Ok(())
205    }
206
207    /// Runs one forced-mode conversion and returns the compensated reading.
208    ///
209    /// Initializes the part first if [`init`](Bmp280::init) has not run. The sequence
210    /// is the datasheet's forced mode (section 3.6.2): `ctrl_meas` with the forced
211    /// code of Table 10, a wait of the maximum measurement time of section 3.8.1,
212    /// which is how section 3.9 asks forced-mode readout to be timed, the `measuring`
213    /// flag of Table 19 polled until it clears, then the single burst read of 0xF7 to
214    /// 0xFC that section 3.9 prescribes.
215    ///
216    /// # Returns
217    ///
218    /// The compensated reading.
219    ///
220    /// # Errors
221    ///
222    /// Returns [`DriverError::Bus`] if the bus fails, [`DriverError::Timeout`] if the
223    /// part is still measuring after the polls, or any error of
224    /// [`init`](Bmp280::init).
225    pub fn measure(&mut self) -> Result<Reading, DriverError<B::Error>> {
226        if self.calibration.is_none() {
227            self.init()?;
228        }
229        self.write(register::CTRL_MEAS, self.ctrl_meas(Mode::Forced).bits())?;
230        self.delay
231            .delay_us(max_measurement_micros(self.temperature, self.pressure));
232        self.wait(measuring)?;
233
234        let mut data = [0u8; DATA_LEN];
235        self.bus
236            .read_registers(register::DATA, &mut data)
237            .map_err(DriverError::Bus)?;
238        let raw = Measurement::parse(&data);
239        let calibration = self.calibration.as_ref().expect("read at initialization");
240        Ok(calibration.compensate(&raw))
241    }
242
243    fn ctrl_meas(&self, mode: Mode) -> CtrlMeas {
244        CtrlMeas {
245            temperature: self.temperature,
246            pressure: self.pressure,
247            mode,
248        }
249    }
250
251    fn read(&mut self, register: u8) -> Result<u8, DriverError<B::Error>> {
252        self.bus.read_register(register).map_err(DriverError::Bus)
253    }
254
255    fn write(&mut self, register: u8, value: u8) -> Result<(), DriverError<B::Error>> {
256        self.bus
257            .write_register(register, value)
258            .map_err(DriverError::Bus)
259    }
260
261    fn wait(&mut self, busy: fn(u8) -> bool) -> Result<(), DriverError<B::Error>> {
262        for _ in 0..STATUS_POLLS {
263            if !busy(self.read(register::STATUS)?) {
264                return Ok(());
265            }
266            self.delay.delay_ms(1);
267        }
268        Err(DriverError::Timeout)
269    }
270}
271
272impl<B, D> Sensor for Bmp280<B, D>
273where
274    B: RegisterBus + Send,
275    B::Error: core::fmt::Debug,
276    D: DelayNs + Send,
277{
278    type Reading = Reading;
279
280    async fn read(&mut self) -> pamoja_core::Result<Reading> {
281        self.measure().map_err(pamoja_core::Error::from)
282    }
283}
284
285#[cfg(test)]
286mod tests {
287    use super::*;
288    use crate::bmp280::{Standby, I2C_ADDRESS_PRIMARY, I2C_ADDRESS_SECONDARY};
289    use crate::driver::{SPI_ADDRESS_MASK, SPI_READ};
290    use alloc::vec;
291    use alloc::vec::Vec;
292    use embedded_hal::i2c::{ErrorKind, NoAcknowledgeSource};
293    use pamoja_hal::script::{block_on, DelayLog, I2cScript, I2cStep, SpiScript, SpiStep};
294
295    // The Table 17 layout bytes and the burst read the decode module pins its
296    // reference vector to: 25.08 degrees C and 100653 Pa.
297    const CALIBRATION: [u8; CALIBRATION_LEN] = [
298        0x70, 0x6B, 0x43, 0x67, 0x18, 0xFC, 0x7D, 0x8E, 0x43, 0xD6, 0xD0, 0x0B, 0x27, 0x0B, 0x8C,
299        0x00, 0xF9, 0xFF, 0x8C, 0x3C, 0xF8, 0xC6, 0x70, 0x17,
300    ];
301    const DATA: [u8; DATA_LEN] = [0x65, 0x5A, 0xC0, 0x7E, 0xED, 0x00];
302
303    fn init_steps(address: u8, config: u8, ctrl_meas: u8) -> Vec<I2cStep> {
304        vec![
305            I2cStep::write(address, [register::RESET, RESET_WORD]),
306            I2cStep::write_read(address, [register::STATUS], [0x00]),
307            I2cStep::write_read(address, [register::CHIP_ID], [CHIP_ID]),
308            I2cStep::write_read(address, [register::CALIBRATION], CALIBRATION),
309            I2cStep::write(address, [register::CONFIG, config]),
310            I2cStep::write(address, [register::CTRL_MEAS, ctrl_meas]),
311        ]
312    }
313
314    fn measure_steps(address: u8, ctrl_meas: u8) -> Vec<I2cStep> {
315        vec![
316            I2cStep::write(address, [register::CTRL_MEAS, ctrl_meas]),
317            I2cStep::write_read(address, [register::STATUS], [0x00]),
318            I2cStep::write_read(address, [register::DATA], DATA),
319        ]
320    }
321
322    #[test]
323    fn init_follows_the_datasheet_sequence_and_keeps_the_calibration() {
324        let mut steps = init_steps(I2C_ADDRESS_PRIMARY, 0x00, 0x24);
325        steps.extend(measure_steps(I2C_ADDRESS_PRIMARY, 0x25));
326        let mut sensor = Bmp280::i2c(I2cScript::new(steps), I2C_ADDRESS_PRIMARY, DelayLog::new());
327        sensor.init().unwrap();
328        let expected = Calibration::parse(&CALIBRATION);
329        assert_eq!(sensor.calibration(), Some(&expected));
330
331        let reading = sensor.measure().unwrap();
332        assert_eq!(reading, expected.compensate(&Measurement::parse(&DATA)));
333        assert_eq!(reading.temperature_centi_celsius, 2508);
334        assert_eq!(reading.pascals(), 100_653);
335
336        let (bus, delay) = sensor.release();
337        assert!(bus.release().done());
338        assert_eq!(delay.waits_ns()[0], STARTUP_MICROS * 1_000);
339        assert_eq!(
340            delay.waits_ns()[1],
341            6_425 * 1_000,
342            "x1, x1 is 6.425 ms at most"
343        );
344    }
345
346    #[test]
347    fn oversampling_and_filter_settings_reach_the_control_registers() {
348        let config = Config {
349            standby: Standby::Ms0_5,
350            filter: 0b100,
351            spi_3wire: false,
352        }
353        .bits();
354        let ctrl_meas_sleep = CtrlMeas {
355            temperature: Oversampling::X2,
356            pressure: Oversampling::X16,
357            mode: Mode::Sleep,
358        }
359        .bits();
360        let mut steps = init_steps(I2C_ADDRESS_SECONDARY, config, ctrl_meas_sleep);
361        steps.extend(measure_steps(I2C_ADDRESS_SECONDARY, ctrl_meas_sleep | 0x01));
362        let mut sensor = Bmp280::i2c(
363            I2cScript::new(steps),
364            I2C_ADDRESS_SECONDARY,
365            DelayLog::new(),
366        )
367        .with_oversampling(Oversampling::X2, Oversampling::X16)
368        .with_filter(0b100);
369        sensor.measure().unwrap();
370        let (bus, delay) = sensor.release();
371        assert!(bus.release().done());
372        let expected = max_measurement_micros(Oversampling::X2, Oversampling::X16);
373        assert_eq!(delay.waits_ns()[1], expected * 1_000);
374        assert_eq!(
375            expected, 43_225,
376            "the ultra high resolution row of Table 13, 43.2 ms"
377        );
378    }
379
380    #[test]
381    fn init_waits_while_the_trimming_image_is_still_loading() {
382        let mut steps = vec![
383            I2cStep::write(0x76, [register::RESET, RESET_WORD]),
384            I2cStep::write_read(0x76, [register::STATUS], [0x01]),
385            I2cStep::write_read(0x76, [register::STATUS], [0x01]),
386        ];
387        steps.extend(init_steps(0x76, 0x00, 0x24).into_iter().skip(1));
388        let mut sensor = Bmp280::i2c(I2cScript::new(steps), 0x76, DelayLog::new());
389        sensor.init().unwrap();
390        let (bus, delay) = sensor.release();
391        assert!(bus.release().done());
392        assert_eq!(
393            delay.waits_ns(),
394            [STARTUP_MICROS * 1_000, 1_000_000, 1_000_000]
395        );
396
397        let mut steps = vec![I2cStep::write(0x76, [register::RESET, RESET_WORD])];
398        for _ in 0..STATUS_POLLS {
399            steps.push(I2cStep::write_read(0x76, [register::STATUS], [0x01]));
400        }
401        let mut sensor = Bmp280::i2c(I2cScript::new(steps), 0x76, DelayLog::new());
402        assert_eq!(sensor.init(), Err(DriverError::Timeout));
403        assert!(sensor.calibration().is_none());
404        assert!(sensor.release().0.release().done());
405    }
406
407    #[test]
408    fn a_part_that_is_not_a_bmp280_is_refused_before_anything_else() {
409        let steps = [
410            I2cStep::write(0x76, [register::RESET, RESET_WORD]),
411            I2cStep::write_read(0x76, [register::STATUS], [0x00]),
412            I2cStep::write_read(0x76, [register::CHIP_ID], [0x60]),
413        ];
414        let mut sensor = Bmp280::i2c(I2cScript::new(steps), 0x76, DelayLog::new());
415        assert_eq!(
416            sensor.init(),
417            Err(DriverError::Sensor(SensorError::Identity))
418        );
419        assert!(sensor.calibration().is_none());
420        assert!(sensor.release().0.release().done());
421    }
422
423    #[test]
424    fn a_conversion_that_never_finishes_is_a_timeout() {
425        let mut steps = init_steps(0x76, 0x00, 0x24);
426        steps.push(I2cStep::write(0x76, [register::CTRL_MEAS, 0x25]));
427        for _ in 0..STATUS_POLLS {
428            steps.push(I2cStep::write_read(0x76, [register::STATUS], [0x08]));
429        }
430        let mut sensor = Bmp280::i2c(I2cScript::new(steps), 0x76, DelayLog::new());
431        assert_eq!(sensor.measure(), Err(DriverError::Timeout));
432        assert!(sensor.release().0.release().done());
433    }
434
435    #[test]
436    fn a_bus_fault_surfaces_as_a_bus_error_and_maps_to_the_core_io_error() {
437        let kind = ErrorKind::NoAcknowledge(NoAcknowledgeSource::Address);
438        let steps = [I2cStep::fault(0x76, kind)];
439        let mut sensor = Bmp280::i2c(I2cScript::new(steps), 0x76, DelayLog::new());
440        let error = sensor.init().unwrap_err();
441        assert!(matches!(error, DriverError::Bus(_)));
442        let core: pamoja_core::Error = error.into();
443        assert!(matches!(core, pamoja_core::Error::Io(_)));
444    }
445
446    #[test]
447    fn as_a_sensor_it_reads_and_maps_to_one_channel() {
448        let mut steps = init_steps(0x76, 0x00, 0x24);
449        steps.extend(measure_steps(0x76, 0x25));
450        let sensor = Bmp280::i2c(I2cScript::new(steps), 0x76, DelayLog::new());
451        let mut pascals = sensor.map(|reading| reading.pascals());
452        assert_eq!(block_on(pascals.read()).unwrap(), 100_653);
453        assert!(pascals.into_inner().release().0.release().done());
454    }
455
456    #[test]
457    fn over_spi_the_same_sequence_uses_the_bosch_control_bytes() {
458        // Section 5.3: 0xF7 is written through control byte 0x77 and read through
459        // 0xF7; Figure 10 writes ctrl_meas through 0x74.
460        assert_eq!(register::DATA & SPI_ADDRESS_MASK, 0x77);
461        assert_eq!(register::DATA | SPI_READ, 0xF7);
462        assert_eq!(register::CTRL_MEAS & SPI_ADDRESS_MASK, 0x74);
463        let steps = [
464            SpiStep::write([register::RESET & SPI_ADDRESS_MASK, RESET_WORD]),
465            SpiStep::write([register::STATUS | SPI_READ]),
466            SpiStep::read([0x00]),
467            SpiStep::write([register::CHIP_ID | SPI_READ]),
468            SpiStep::read([CHIP_ID]),
469            SpiStep::write([register::CALIBRATION | SPI_READ]),
470            SpiStep::read(CALIBRATION),
471            SpiStep::write([register::CONFIG & SPI_ADDRESS_MASK, 0x00]),
472            SpiStep::write([register::CTRL_MEAS & SPI_ADDRESS_MASK, 0x24]),
473            SpiStep::write([register::CTRL_MEAS & SPI_ADDRESS_MASK, 0x25]),
474            SpiStep::write([register::STATUS | SPI_READ]),
475            SpiStep::read([0x00]),
476            SpiStep::write([register::DATA | SPI_READ]),
477            SpiStep::read(DATA),
478        ];
479        let mut sensor = Bmp280::spi(SpiScript::new(steps), DelayLog::new());
480        let reading = sensor.measure().unwrap();
481        assert_eq!(reading.temperature_centi_celsius, 2508);
482        assert!(sensor.release().0.release().done());
483    }
484
485    #[test]
486    fn init_and_a_forced_measurement_make_the_transfers_the_datasheet_gives() {
487        const PART: u8 = crate::bmp280::I2C_ADDRESS_PRIMARY;
488        let trimming = [
489            0x70, 0x6B, 0x43, 0x67, 0x18, 0xFC, 0x7D, 0x8E, 0x43, 0xD6, 0xD0, 0x0B, 0x27, 0x0B,
490            0x8C, 0x00, 0xF9, 0xFF, 0x8C, 0x3C, 0xF8, 0xC6, 0x70, 0x17,
491        ];
492        let bus = I2cScript::new([
493            I2cStep::write(PART, [0xE0, 0xB6]),
494            I2cStep::write_read(PART, [0xF3], [0x00]),
495            I2cStep::write_read(PART, [0xD0], [0x58]),
496            I2cStep::write_read(PART, [0x88], trimming),
497            I2cStep::write(PART, [0xF5, 0x00]),
498            I2cStep::write(PART, [0xF4, 0x24]),
499            I2cStep::write(PART, [0xF4, 0x25]),
500            I2cStep::write_read(PART, [0xF3], [0x00]),
501            I2cStep::write_read(PART, [0xF7], [0x65, 0x5A, 0xC0, 0x7E, 0xED, 0x00]),
502        ]);
503
504        let mut sensor = Bmp280::i2c(bus, PART, DelayLog::new());
505        let reading = block_on(Sensor::read(&mut sensor)).expect("the scripted part answers");
506        assert_eq!(reading.temperature_centi_celsius, 2508);
507        assert_eq!(reading.pascals(), 100_653);
508
509        let (registers, delay) = sensor.release();
510        assert!(registers.release().done());
511        assert!(delay.total_micros() >= 2_000 + 6_425);
512    }
513}