Skip to main content

pamoja_sensors/
bmp280.rs

1//! Bosch BMP280 digital pressure sensor.
2//!
3//! The BMP280 is the BME280's sibling without the humidity element: a barometric
4//! pressure and temperature sensor that ships 20-bit raw ADC codes plus a block of
5//! per-chip trimming coefficients, and leaves the compensation to the host. The
6//! integer arithmetic here is a line for line port of the `bmp280_compensate_T_int32`
7//! and `bmp280_compensate_P_int64` reference code in section 3.11.3 of the data
8//! sheet (BST-BMP280-DS001), and the tests cross-check it against the data sheet's
9//! floating-point form in appendix 8.1.
10//!
11//! A caller reads the 24 calibration bytes once with [`Calibration::parse`], then
12//! burst-reads the six data registers each cycle into a [`Measurement`] and calls
13//! [`Calibration::compensate`] for a [`Reading`]. [`CtrlMeas`] and [`Config`] build
14//! and decode the two control registers, and every decode has a matching builder so a
15//! node can be exercised with nothing wired. The measurement-time functions are here
16//! too, and the [`Bmp280`] driver (the `embedded-hal` feature) runs the whole
17//! sequence, reset to reading, over an I2C or SPI bus.
18
19#[cfg(feature = "embedded-hal")]
20mod driver;
21#[cfg(feature = "embedded-hal")]
22pub mod sim;
23
24#[cfg(feature = "embedded-hal")]
25pub use driver::{Bmp280, STATUS_POLLS};
26
27/// The I2C address with the SDO pin tied to ground.
28pub const I2C_ADDRESS_PRIMARY: u8 = 0x76;
29/// The I2C address with the SDO pin tied to VDDIO.
30pub const I2C_ADDRESS_SECONDARY: u8 = 0x77;
31/// The value the chip-id register (0xD0) returns for a BMP280.
32pub const CHIP_ID: u8 = 0x58;
33/// The word written to the reset register (0xE0) to run a full power-on reset.
34pub const RESET_WORD: u8 = 0xB6;
35/// The raw code a data register holds when its measurement is skipped.
36pub const SKIPPED_OUTPUT: u32 = 0x80000;
37/// The number of calibration bytes at [`register::CALIBRATION`].
38pub const CALIBRATION_LEN: usize = 24;
39/// The number of data bytes at [`register::DATA`].
40pub const DATA_LEN: usize = 6;
41/// The start-up time from power-on or reset until the part accepts a transfer, in
42/// microseconds: Table 2's `t_startup`, 2 ms at most, which the soft reset of section
43/// 4.3.2 incurs again since it runs the complete power-on-reset procedure.
44pub const STARTUP_MICROS: u32 = 2_000;
45
46/// The BMP280 register addresses.
47pub mod register {
48    /// First of the 24 trimming bytes, `calib00..=calib23` (0x88..=0x9F).
49    pub const CALIBRATION: u8 = 0x88;
50    /// Chip-id register; reads [`super::CHIP_ID`] for a BMP280.
51    pub const CHIP_ID: u8 = 0xD0;
52    /// Soft-reset register; write [`super::RESET_WORD`].
53    pub const RESET: u8 = 0xE0;
54    /// Status register: `measuring` in bit 3, `im_update` in bit 0.
55    pub const STATUS: u8 = 0xF3;
56    /// Measurement control register: oversampling and power mode.
57    pub const CTRL_MEAS: u8 = 0xF4;
58    /// Configuration register: standby time, IIR filter, and 3-wire SPI.
59    pub const CONFIG: u8 = 0xF5;
60    /// First of the 6 burst-read data bytes: pressure then temperature (0xF7..=0xFC).
61    pub const DATA: u8 = 0xF7;
62}
63
64/// Returns whether the status register reports a conversion in progress.
65///
66/// # Arguments
67///
68/// * `status` - the byte read from [`register::STATUS`].
69///
70/// # Returns
71///
72/// `true` while a conversion runs, `false` once its results have reached the data
73/// registers.
74pub fn measuring(status: u8) -> bool {
75    status & 0x08 != 0
76}
77
78/// Returns whether the status register reports the NVM image being copied.
79///
80/// # Arguments
81///
82/// * `status` - the byte read from [`register::STATUS`].
83///
84/// # Returns
85///
86/// `true` while the trimming data is being copied to the image registers, which
87/// happens at power-on reset and before every conversion.
88pub fn image_updating(status: u8) -> bool {
89    status & 0x01 != 0
90}
91
92/// An oversampling setting for the pressure or temperature measurement.
93///
94/// Each step adds one bit of output resolution, stored in the XLSB data register.
95#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
96pub enum Oversampling {
97    /// The measurement is skipped and its data registers read [`SKIPPED_OUTPUT`].
98    #[default]
99    Skipped,
100    /// A single sample, 16-bit output.
101    X1,
102    /// Two samples, 17-bit output.
103    X2,
104    /// Four samples, 18-bit output.
105    X4,
106    /// Eight samples, 19-bit output.
107    X8,
108    /// Sixteen samples, 20-bit output.
109    X16,
110}
111
112impl Oversampling {
113    /// Returns the three-bit `osrs_t` / `osrs_p` field value.
114    pub fn code(self) -> u8 {
115        match self {
116            Oversampling::Skipped => 0b000,
117            Oversampling::X1 => 0b001,
118            Oversampling::X2 => 0b010,
119            Oversampling::X4 => 0b011,
120            Oversampling::X8 => 0b100,
121            Oversampling::X16 => 0b101,
122        }
123    }
124
125    /// Decodes a three-bit `osrs_t` / `osrs_p` field value.
126    ///
127    /// The codes `0b101`, `0b110`, and `0b111` all select 16x oversampling.
128    ///
129    /// # Arguments
130    ///
131    /// * `code` - the field value; only the low three bits are used.
132    ///
133    /// # Returns
134    ///
135    /// The oversampling setting.
136    pub fn from_code(code: u8) -> Oversampling {
137        match code & 0b111 {
138            0b000 => Oversampling::Skipped,
139            0b001 => Oversampling::X1,
140            0b010 => Oversampling::X2,
141            0b011 => Oversampling::X4,
142            0b100 => Oversampling::X8,
143            _ => Oversampling::X16,
144        }
145    }
146
147    /// Returns the number of samples averaged, or `0` when skipped.
148    pub fn factor(self) -> u8 {
149        match self {
150            Oversampling::Skipped => 0,
151            Oversampling::X1 => 1,
152            Oversampling::X2 => 2,
153            Oversampling::X4 => 4,
154            Oversampling::X8 => 8,
155            Oversampling::X16 => 16,
156        }
157    }
158}
159
160/// The power mode selected by the `mode[1:0]` field.
161#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
162pub enum Mode {
163    /// No measurements; the power-on default. Registers stay readable.
164    #[default]
165    Sleep,
166    /// One measurement, then back to sleep.
167    Forced,
168    /// Continuous cycling between measurement and a standby period.
169    Normal,
170}
171
172impl Mode {
173    /// Returns the two-bit `mode` field value.
174    pub fn code(self) -> u8 {
175        match self {
176            Mode::Sleep => 0b00,
177            Mode::Forced => 0b01,
178            Mode::Normal => 0b11,
179        }
180    }
181
182    /// Decodes a two-bit `mode` field value; both `0b01` and `0b10` mean forced.
183    ///
184    /// # Arguments
185    ///
186    /// * `code` - the field value; only the low two bits are used.
187    ///
188    /// # Returns
189    ///
190    /// The power mode.
191    pub fn from_code(code: u8) -> Mode {
192        match code & 0b11 {
193            0b00 => Mode::Sleep,
194            0b11 => Mode::Normal,
195            _ => Mode::Forced,
196        }
197    }
198}
199
200/// The inactive period between measurements in normal mode, the `t_sb[2:0]` field.
201#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
202pub enum Standby {
203    /// 0.5 ms.
204    #[default]
205    Ms0_5,
206    /// 62.5 ms.
207    Ms62_5,
208    /// 125 ms.
209    Ms125,
210    /// 250 ms.
211    Ms250,
212    /// 500 ms.
213    Ms500,
214    /// 1000 ms.
215    Ms1000,
216    /// 2000 ms.
217    Ms2000,
218    /// 4000 ms.
219    Ms4000,
220}
221
222impl Standby {
223    /// Returns the three-bit `t_sb` field value.
224    pub fn code(self) -> u8 {
225        match self {
226            Standby::Ms0_5 => 0b000,
227            Standby::Ms62_5 => 0b001,
228            Standby::Ms125 => 0b010,
229            Standby::Ms250 => 0b011,
230            Standby::Ms500 => 0b100,
231            Standby::Ms1000 => 0b101,
232            Standby::Ms2000 => 0b110,
233            Standby::Ms4000 => 0b111,
234        }
235    }
236
237    /// Decodes a three-bit `t_sb` field value.
238    ///
239    /// # Arguments
240    ///
241    /// * `code` - the field value; only the low three bits are used.
242    ///
243    /// # Returns
244    ///
245    /// The standby period.
246    pub fn from_code(code: u8) -> Standby {
247        match code & 0b111 {
248            0b000 => Standby::Ms0_5,
249            0b001 => Standby::Ms62_5,
250            0b010 => Standby::Ms125,
251            0b011 => Standby::Ms250,
252            0b100 => Standby::Ms500,
253            0b101 => Standby::Ms1000,
254            0b110 => Standby::Ms2000,
255            _ => Standby::Ms4000,
256        }
257    }
258
259    /// Returns the standby period in microseconds.
260    pub fn microseconds(self) -> u32 {
261        match self {
262            Standby::Ms0_5 => 500,
263            Standby::Ms62_5 => 62_500,
264            Standby::Ms125 => 125_000,
265            Standby::Ms250 => 250_000,
266            Standby::Ms500 => 500_000,
267            Standby::Ms1000 => 1_000_000,
268            Standby::Ms2000 => 2_000_000,
269            Standby::Ms4000 => 4_000_000,
270        }
271    }
272}
273
274/// The measurement control register (0xF4, `ctrl_meas`).
275///
276/// The default is the register's reset state, `0x00`: both measurements skipped and
277/// the device asleep.
278#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
279pub struct CtrlMeas {
280    /// Temperature oversampling, `osrs_t[2:0]` in bits 7:5.
281    pub temperature: Oversampling,
282    /// Pressure oversampling, `osrs_p[2:0]` in bits 4:2.
283    pub pressure: Oversampling,
284    /// Power mode, `mode[1:0]` in bits 1:0.
285    pub mode: Mode,
286}
287
288impl CtrlMeas {
289    /// Packs the settings into the register byte.
290    ///
291    /// # Returns
292    ///
293    /// The byte to write to [`register::CTRL_MEAS`].
294    pub fn bits(self) -> u8 {
295        (self.temperature.code() << 5) | (self.pressure.code() << 2) | self.mode.code()
296    }
297
298    /// Decodes a register byte.
299    ///
300    /// # Arguments
301    ///
302    /// * `bits` - the byte read from [`register::CTRL_MEAS`].
303    ///
304    /// # Returns
305    ///
306    /// The decoded settings.
307    pub fn from_bits(bits: u8) -> CtrlMeas {
308        CtrlMeas {
309            temperature: Oversampling::from_code(bits >> 5),
310            pressure: Oversampling::from_code(bits >> 2),
311            mode: Mode::from_code(bits),
312        }
313    }
314}
315
316/// The configuration register (0xF5, `config`).
317///
318/// The data sheet lists the IIR filter coefficients (off, 2, 4, 8, 16) and places the
319/// field in bits 4:2, but does not publish which three-bit code selects which
320/// coefficient, so the field is carried here as its raw code. Writes to this register
321/// may be ignored in normal mode; write it in sleep mode.
322///
323/// The default is the register's reset state, `0x00`.
324#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
325pub struct Config {
326    /// Normal-mode standby period, `t_sb[2:0]` in bits 7:5.
327    pub standby: Standby,
328    /// IIR filter setting, the raw `filter[2:0]` code in bits 4:2.
329    pub filter: u8,
330    /// Enables the 3-wire SPI interface, `spi3w_en` in bit 0.
331    pub spi_3wire: bool,
332}
333
334impl Config {
335    /// Packs the settings into the register byte.
336    ///
337    /// # Returns
338    ///
339    /// The byte to write to [`register::CONFIG`]. Only the low three bits of `filter`
340    /// are used.
341    pub fn bits(self) -> u8 {
342        (self.standby.code() << 5) | ((self.filter & 0b111) << 2) | u8::from(self.spi_3wire)
343    }
344
345    /// Decodes a register byte.
346    ///
347    /// # Arguments
348    ///
349    /// * `bits` - the byte read from [`register::CONFIG`].
350    ///
351    /// # Returns
352    ///
353    /// The decoded settings.
354    pub fn from_bits(bits: u8) -> Config {
355        Config {
356            standby: Standby::from_code(bits >> 5),
357            filter: (bits >> 2) & 0b111,
358            spi_3wire: bits & 0x01 != 0,
359        }
360    }
361}
362
363/// The longest one measurement cycle can take, in microseconds.
364///
365/// 1.25 ms, plus 2.3 ms per temperature sample, plus 2.3 ms per pressure sample and
366/// 0.575 ms, each term only for a measurement that is not skipped. Section 3.8.1 of
367/// the data sheet publishes these maxima as Table 13, one row per recommended
368/// setting, rather than as a formula; the terms are the BME280 data sheet's formula
369/// without its humidity term, and they reproduce every row of the table, including
370/// the minimum-rate column, which is computed from the maxima before the time column
371/// rounds them (6.425 ms gives the row's 155.6 Hz where 6.4 ms would give 156.3 Hz).
372/// A driver waits this long after forcing a measurement before it polls the status
373/// register, which is how section 3.9 asks forced-mode readout to be timed.
374///
375/// # Arguments
376///
377/// * `temperature` - temperature oversampling.
378/// * `pressure` - pressure oversampling.
379///
380/// # Returns
381///
382/// The maximum measurement time in microseconds.
383///
384/// # Examples
385///
386/// ```
387/// use pamoja_sensors::bmp280::{max_measurement_micros, Oversampling};
388///
389/// // Table 13's standard resolution row: pressure x4, temperature x1, 13.3 ms.
390/// let micros = max_measurement_micros(Oversampling::X1, Oversampling::X4);
391/// assert_eq!(micros, 13_325);
392/// ```
393pub fn max_measurement_micros(temperature: Oversampling, pressure: Oversampling) -> u32 {
394    let mut micros = 1_250;
395    if temperature != Oversampling::Skipped {
396        micros += 2_300 * u32::from(temperature.factor());
397    }
398    if pressure != Oversampling::Skipped {
399        micros += 2_300 * u32::from(pressure.factor()) + 575;
400    }
401    micros
402}
403
404/// The typical time one measurement cycle takes, in microseconds.
405///
406/// 1 ms, plus 2 ms per temperature sample, plus 2 ms per pressure sample and 0.5 ms,
407/// each term only for a measurement that is not skipped: the typical column of
408/// Table 13, row for row.
409///
410/// # Arguments
411///
412/// * `temperature` - temperature oversampling.
413/// * `pressure` - pressure oversampling.
414///
415/// # Returns
416///
417/// The typical measurement time in microseconds.
418///
419/// # Examples
420///
421/// ```
422/// use pamoja_sensors::bmp280::{typical_measurement_micros, Oversampling};
423///
424/// // Table 13's ultra low power row: pressure x1, temperature x1, 5.5 ms.
425/// assert_eq!(typical_measurement_micros(Oversampling::X1, Oversampling::X1), 5_500);
426/// ```
427pub fn typical_measurement_micros(temperature: Oversampling, pressure: Oversampling) -> u32 {
428    let mut micros = 1_000;
429    if temperature != Oversampling::Skipped {
430        micros += 2_000 * u32::from(temperature.factor());
431    }
432    if pressure != Oversampling::Skipped {
433        micros += 2_000 * u32::from(pressure.factor()) + 500;
434    }
435    micros
436}
437
438/// The per-chip trimming coefficients read from `0x88..=0x9F`.
439///
440/// These are programmed into non-volatile memory during production and are constant
441/// for a given device, so they are read once at start-up and reused for every
442/// measurement.
443#[derive(Clone, Copy, Debug, PartialEq, Eq)]
444pub struct Calibration {
445    /// `dig_T1`, unsigned, at 0x88/0x89.
446    pub dig_t1: u16,
447    /// `dig_T2`, signed, at 0x8A/0x8B.
448    pub dig_t2: i16,
449    /// `dig_T3`, signed, at 0x8C/0x8D.
450    pub dig_t3: i16,
451    /// `dig_P1`, unsigned, at 0x8E/0x8F.
452    pub dig_p1: u16,
453    /// `dig_P2`, signed, at 0x90/0x91.
454    pub dig_p2: i16,
455    /// `dig_P3`, signed, at 0x92/0x93.
456    pub dig_p3: i16,
457    /// `dig_P4`, signed, at 0x94/0x95.
458    pub dig_p4: i16,
459    /// `dig_P5`, signed, at 0x96/0x97.
460    pub dig_p5: i16,
461    /// `dig_P6`, signed, at 0x98/0x99.
462    pub dig_p6: i16,
463    /// `dig_P7`, signed, at 0x9A/0x9B.
464    pub dig_p7: i16,
465    /// `dig_P8`, signed, at 0x9C/0x9D.
466    pub dig_p8: i16,
467    /// `dig_P9`, signed, at 0x9E/0x9F.
468    pub dig_p9: i16,
469}
470
471impl Calibration {
472    /// Parses the coefficients from the calibration block, each word LSB first.
473    ///
474    /// # Arguments
475    ///
476    /// * `bytes` - the 24 bytes read from `0x88..=0x9F`.
477    ///
478    /// # Returns
479    ///
480    /// The decoded calibration.
481    pub fn parse(bytes: &[u8; CALIBRATION_LEN]) -> Calibration {
482        let b = bytes;
483        Calibration {
484            dig_t1: u16::from_le_bytes([b[0], b[1]]),
485            dig_t2: i16::from_le_bytes([b[2], b[3]]),
486            dig_t3: i16::from_le_bytes([b[4], b[5]]),
487            dig_p1: u16::from_le_bytes([b[6], b[7]]),
488            dig_p2: i16::from_le_bytes([b[8], b[9]]),
489            dig_p3: i16::from_le_bytes([b[10], b[11]]),
490            dig_p4: i16::from_le_bytes([b[12], b[13]]),
491            dig_p5: i16::from_le_bytes([b[14], b[15]]),
492            dig_p6: i16::from_le_bytes([b[16], b[17]]),
493            dig_p7: i16::from_le_bytes([b[18], b[19]]),
494            dig_p8: i16::from_le_bytes([b[20], b[21]]),
495            dig_p9: i16::from_le_bytes([b[22], b[23]]),
496        }
497    }
498
499    /// Builds the calibration block a device holding these coefficients would return.
500    ///
501    /// # Returns
502    ///
503    /// The 24 bytes as they sit at `0x88..=0x9F`.
504    pub fn to_bytes(&self) -> [u8; CALIBRATION_LEN] {
505        let mut out = [0u8; CALIBRATION_LEN];
506        let words = [
507            self.dig_t1,
508            self.dig_t2 as u16,
509            self.dig_t3 as u16,
510            self.dig_p1,
511            self.dig_p2 as u16,
512            self.dig_p3 as u16,
513            self.dig_p4 as u16,
514            self.dig_p5 as u16,
515            self.dig_p6 as u16,
516            self.dig_p7 as u16,
517            self.dig_p8 as u16,
518            self.dig_p9 as u16,
519        ];
520        for (chunk, word) in out.as_chunks_mut::<2>().0.iter_mut().zip(words) {
521            *chunk = word.to_le_bytes();
522        }
523        out
524    }
525
526    /// Compensates a raw measurement into temperature and pressure.
527    ///
528    /// Temperature is computed first because its `t_fine` intermediate feeds the
529    /// pressure formula, exactly as the reference code shares it.
530    ///
531    /// # Arguments
532    ///
533    /// * `raw` - the uncompensated 20-bit codes from the data registers.
534    ///
535    /// # Returns
536    ///
537    /// The compensated [`Reading`].
538    pub fn compensate(&self, raw: &Measurement) -> Reading {
539        let t_fine = self.t_fine(raw.temperature);
540        Reading {
541            temperature_centi_celsius: compensate_temperature(t_fine),
542            pressure_q24_8: self.compensate_pressure(raw.pressure, t_fine),
543        }
544    }
545
546    // bmp280_compensate_T_int32 up to t_fine. The products are formed in 64 bits so
547    // no 20-bit code and coefficient pair can overflow; the sum always fits 32 bits.
548    fn t_fine(&self, adc_t: u32) -> i32 {
549        let adc_t = i64::from(adc_t);
550        let dig_t1 = i64::from(self.dig_t1);
551        let var1 = (((adc_t >> 3) - (dig_t1 << 1)) * i64::from(self.dig_t2)) >> 11;
552        let near = (adc_t >> 4) - dig_t1;
553        let var2 = (((near * near) >> 12) * i64::from(self.dig_t3)) >> 14;
554        (var1 + var2) as i32
555    }
556
557    // bmp280_compensate_P_int64: pressure in Q24.8 pascals.
558    fn compensate_pressure(&self, adc_p: u32, t_fine: i32) -> u32 {
559        let mut var1 = i64::from(t_fine) - 128000;
560        let mut var2 = var1 * var1 * i64::from(self.dig_p6);
561        var2 += (var1 * i64::from(self.dig_p5)) << 17;
562        var2 += i64::from(self.dig_p4) << 35;
563        var1 =
564            ((var1 * var1 * i64::from(self.dig_p3)) >> 8) + ((var1 * i64::from(self.dig_p2)) << 12);
565        var1 = (((1i64 << 47) + var1) * i64::from(self.dig_p1)) >> 33;
566        if var1 == 0 {
567            return 0;
568        }
569        let mut p = 1048576 - i64::from(adc_p);
570        p = (((p << 31) - var2) * 3125) / var1;
571        var1 = (i64::from(self.dig_p9) * (p >> 13) * (p >> 13)) >> 25;
572        var2 = (i64::from(self.dig_p8) * p) >> 19;
573        p = ((p + var1 + var2) >> 8) + (i64::from(self.dig_p7) << 4);
574        p as u32
575    }
576}
577
578// The tail of bmp280_compensate_T_int32: hundredths of a degree Celsius.
579fn compensate_temperature(t_fine: i32) -> i32 {
580    ((i64::from(t_fine) * 5 + 128) >> 8) as i32
581}
582
583/// The raw, uncompensated 20-bit codes from the BMP280 data registers.
584#[derive(Clone, Copy, Debug, PartialEq, Eq)]
585pub struct Measurement {
586    /// The 20-bit uncompensated pressure, `up[19:0]`.
587    pub pressure: u32,
588    /// The 20-bit uncompensated temperature, `ut[19:0]`.
589    pub temperature: u32,
590}
591
592impl Measurement {
593    /// Parses the six data bytes burst-read from `0xF7..=0xFC`.
594    ///
595    /// The order on the wire is pressure then temperature, each as MSB, LSB, and an
596    /// XLSB byte whose upper nibble holds the last four bits.
597    ///
598    /// # Arguments
599    ///
600    /// * `data` - the six bytes read from the data registers.
601    ///
602    /// # Returns
603    ///
604    /// The unpacked raw measurement.
605    pub fn parse(data: &[u8; DATA_LEN]) -> Measurement {
606        let unpack = |msb: u8, lsb: u8, xlsb: u8| {
607            (u32::from(msb) << 12) | (u32::from(lsb) << 4) | (u32::from(xlsb) >> 4)
608        };
609        Measurement {
610            pressure: unpack(data[0], data[1], data[2]),
611            temperature: unpack(data[3], data[4], data[5]),
612        }
613    }
614
615    /// Builds the six data bytes a device holding these codes would return.
616    ///
617    /// # Returns
618    ///
619    /// The bytes as they sit at `0xF7..=0xFC`. Only the low 20 bits of each code are
620    /// carried.
621    pub fn to_bytes(&self) -> [u8; DATA_LEN] {
622        let pack = |code: u32| {
623            [
624                ((code >> 12) & 0xFF) as u8,
625                ((code >> 4) & 0xFF) as u8,
626                ((code & 0x0F) << 4) as u8,
627            ]
628        };
629        let p = pack(self.pressure);
630        let t = pack(self.temperature);
631        [p[0], p[1], p[2], t[0], t[1], t[2]]
632    }
633
634    /// Returns whether the pressure measurement was skipped (`osrs_p = 0`).
635    pub fn pressure_skipped(&self) -> bool {
636        self.pressure == SKIPPED_OUTPUT
637    }
638
639    /// Returns whether the temperature measurement was skipped (`osrs_t = 0`).
640    pub fn temperature_skipped(&self) -> bool {
641        self.temperature == SKIPPED_OUTPUT
642    }
643}
644
645/// A compensated BMP280 reading.
646///
647/// The fields are the exact integer outputs of the reference compensation code; the
648/// methods present them in conventional units.
649#[derive(Clone, Copy, Debug, PartialEq, Eq)]
650pub struct Reading {
651    /// Temperature in hundredths of a degree Celsius.
652    pub temperature_centi_celsius: i32,
653    /// Pressure in pascals as Q24.8 fixed point, that is units of 1/256 Pa.
654    pub pressure_q24_8: u32,
655}
656
657impl Reading {
658    /// Returns the temperature in degrees Celsius.
659    pub fn celsius(&self) -> f32 {
660        self.temperature_centi_celsius as f32 / 100.0
661    }
662
663    /// Returns the pressure in whole pascals.
664    pub fn pascals(&self) -> u32 {
665        self.pressure_q24_8 >> 8
666    }
667
668    /// Returns the pressure in hectopascals (millibars).
669    pub fn hectopascals(&self) -> f32 {
670        (f64::from(self.pressure_q24_8) / 25_600.0) as f32
671    }
672}
673
674#[cfg(test)]
675mod tests {
676    use super::*;
677
678    // The temperature and pressure coefficients of the BME280 module's sample set; the
679    // two parts share the dig_T and dig_P formulas verbatim, so the set exercises the
680    // same arithmetic here.
681    fn sample_calibration() -> Calibration {
682        Calibration {
683            dig_t1: 28485,
684            dig_t2: 26735,
685            dig_t3: 50,
686            dig_p1: 37190,
687            dig_p2: -10646,
688            dig_p3: 3024,
689            dig_p4: 7758,
690            dig_p5: -120,
691            dig_p6: -7,
692            dig_p7: 9900,
693            dig_p8: -10230,
694            dig_p9: 4285,
695        }
696    }
697
698    // The worked temperature example carried by the BME280 module: dig_T1 = 27504,
699    // dig_T2 = 26435, dig_T3 = -1000 with adc_T = 519888 gives 25.08 degrees C. The
700    // temperature formula is identical between the two parts.
701    fn worked_example_calibration() -> Calibration {
702        Calibration {
703            dig_t1: 27504,
704            dig_t2: 26435,
705            dig_t3: -1000,
706            ..sample_calibration()
707        }
708    }
709
710    #[test]
711    fn register_map_matches_table_18() {
712        assert_eq!(register::CALIBRATION, 0x88);
713        assert_eq!(register::CHIP_ID, 0xD0);
714        assert_eq!(register::RESET, 0xE0);
715        assert_eq!(register::STATUS, 0xF3);
716        assert_eq!(register::CTRL_MEAS, 0xF4);
717        assert_eq!(register::CONFIG, 0xF5);
718        assert_eq!(register::DATA, 0xF7);
719        // Table 18 reset state of the id register, and section 4.3.2's reset word.
720        assert_eq!(CHIP_ID, 0x58);
721        assert_eq!(RESET_WORD, 0xB6);
722        // Table 17 spans calib00..calib23; section 3.9 burst-reads 0xF7 to 0xFC.
723        assert_eq!(CALIBRATION_LEN, 0x9F - 0x88 + 1);
724        assert_eq!(DATA_LEN, 0xFC - 0xF7 + 1);
725    }
726
727    #[test]
728    fn i2c_address_follows_the_sdo_pin() {
729        // Section 5.2: 1110110 with SDO to GND, 1110111 with SDO to VDDIO.
730        assert_eq!(I2C_ADDRESS_PRIMARY, 0b1110110);
731        assert_eq!(I2C_ADDRESS_SECONDARY, 0b1110111);
732    }
733
734    #[test]
735    fn start_up_time_matches_table_2() {
736        // Table 2: t_startup, time to first communication, 2 ms at most.
737        assert_eq!(STARTUP_MICROS, 2_000);
738    }
739
740    #[test]
741    fn measurement_times_reproduce_table_13() {
742        // Table 13, one row per oversampling setting: pressure and temperature
743        // oversampling, then typical and maximum measurement time in tenths of a
744        // millisecond and typical and minimum rate in tenths of a hertz. The rate
745        // columns pin the maxima before the time column rounds them: 6.425 ms is the
746        // row's 155.6 Hz, where 6.4 ms would be 156.3 Hz.
747        let rows = [
748            (Oversampling::X1, Oversampling::X1, 55, 64, 1818, 1556),
749            (Oversampling::X2, Oversampling::X1, 75, 87, 1333, 1146),
750            (Oversampling::X4, Oversampling::X1, 115, 133, 870, 750),
751            (Oversampling::X8, Oversampling::X1, 195, 225, 513, 444),
752            (Oversampling::X16, Oversampling::X2, 375, 432, 267, 231),
753        ];
754        let tenths_of_ms = |micros: u32| (micros + 50) / 100;
755        let tenths_of_hz = |micros: u32| (10_000_000 + micros / 2) / micros;
756        for (pressure, temperature, typ_ms, max_ms, typ_hz, min_hz) in rows {
757            let typical = typical_measurement_micros(temperature, pressure);
758            let maximum = max_measurement_micros(temperature, pressure);
759            assert_eq!(
760                tenths_of_ms(typical),
761                typ_ms,
762                "{pressure:?} {temperature:?}"
763            );
764            assert_eq!(
765                tenths_of_ms(maximum),
766                max_ms,
767                "{pressure:?} {temperature:?}"
768            );
769            assert_eq!(
770                tenths_of_hz(typical),
771                typ_hz,
772                "{pressure:?} {temperature:?}"
773            );
774            assert_eq!(
775                tenths_of_hz(maximum),
776                min_hz,
777                "{pressure:?} {temperature:?}"
778            );
779        }
780        // The BME280 data sheet's worked example of the same terms, x1 temperature and
781        // x4 pressure, is Table 13's standard resolution row.
782        assert_eq!(
783            typical_measurement_micros(Oversampling::X1, Oversampling::X4),
784            11_500
785        );
786        assert_eq!(
787            max_measurement_micros(Oversampling::X1, Oversampling::X4),
788            13_325
789        );
790        let none = Oversampling::Skipped;
791        assert_eq!(typical_measurement_micros(none, none), 1_000);
792        assert_eq!(max_measurement_micros(none, none), 1_250);
793        assert_eq!(
794            max_measurement_micros(Oversampling::X1, none),
795            1_250 + 2_300
796        );
797        assert_eq!(
798            max_measurement_micros(none, Oversampling::X1),
799            1_250 + 2_300 + 575
800        );
801    }
802
803    #[test]
804    fn status_flags_sit_in_bits_3_and_0() {
805        // Table 19: measuring[0] is bit 3, im_update[0] is bit 0.
806        assert!(measuring(0x08));
807        assert!(!measuring(0x01));
808        assert!(image_updating(0x01));
809        assert!(!image_updating(0x08));
810        assert!(!measuring(0x00) && !image_updating(0x00));
811    }
812
813    #[test]
814    fn oversampling_codes_and_factors_match_tables_21_and_22() {
815        let table = [
816            (0b000, Oversampling::Skipped, 0),
817            (0b001, Oversampling::X1, 1),
818            (0b010, Oversampling::X2, 2),
819            (0b011, Oversampling::X4, 4),
820            (0b100, Oversampling::X8, 8),
821            (0b101, Oversampling::X16, 16),
822        ];
823        for (code, setting, factor) in table {
824            assert_eq!(setting.code(), code);
825            assert_eq!(Oversampling::from_code(code), setting);
826            assert_eq!(setting.factor(), factor);
827        }
828        // "101, 110, 111" (Table 22) and "101, Others" (Table 21) all mean x16.
829        assert_eq!(Oversampling::from_code(0b110), Oversampling::X16);
830        assert_eq!(Oversampling::from_code(0b111), Oversampling::X16);
831    }
832
833    #[test]
834    fn mode_codes_match_table_10() {
835        assert_eq!(Mode::Sleep.code(), 0b00);
836        assert_eq!(Mode::Forced.code(), 0b01);
837        assert_eq!(Mode::Normal.code(), 0b11);
838        assert_eq!(Mode::from_code(0b00), Mode::Sleep);
839        // "01 and 10" are both forced mode.
840        assert_eq!(Mode::from_code(0b01), Mode::Forced);
841        assert_eq!(Mode::from_code(0b10), Mode::Forced);
842        assert_eq!(Mode::from_code(0b11), Mode::Normal);
843    }
844
845    #[test]
846    fn standby_codes_and_periods_match_table_11() {
847        let table = [
848            (0b000, Standby::Ms0_5, 500),
849            (0b001, Standby::Ms62_5, 62_500),
850            (0b010, Standby::Ms125, 125_000),
851            (0b011, Standby::Ms250, 250_000),
852            (0b100, Standby::Ms500, 500_000),
853            (0b101, Standby::Ms1000, 1_000_000),
854            (0b110, Standby::Ms2000, 2_000_000),
855            (0b111, Standby::Ms4000, 4_000_000),
856        ];
857        for (code, standby, micros) in table {
858            assert_eq!(standby.code(), code);
859            assert_eq!(Standby::from_code(code), standby);
860            assert_eq!(standby.microseconds(), micros);
861        }
862    }
863
864    #[test]
865    fn ctrl_meas_packs_the_fields_of_table_20() {
866        // osrs_t in bits 7:5, osrs_p in bits 4:2, mode in bits 1:0. Table 7's
867        // "indoor navigation" row: x2 temperature, x16 pressure, normal mode.
868        let ctrl = CtrlMeas {
869            temperature: Oversampling::X2,
870            pressure: Oversampling::X16,
871            mode: Mode::Normal,
872        };
873        assert_eq!(ctrl.bits(), 0b0101_0111);
874        assert_eq!(CtrlMeas::from_bits(0x57), ctrl);
875        // Table 18 reset state is 0x00.
876        assert_eq!(CtrlMeas::default().bits(), 0x00);
877        assert_eq!(CtrlMeas::from_bits(0x00), CtrlMeas::default());
878        for bits in 0..=u8::MAX {
879            let decoded = CtrlMeas::from_bits(bits);
880            assert_eq!(CtrlMeas::from_bits(decoded.bits()), decoded);
881        }
882    }
883
884    #[test]
885    fn config_packs_the_fields_of_table_23() {
886        // t_sb in bits 7:5, filter in bits 4:2, spi3w_en in bit 0.
887        let config = Config {
888            standby: Standby::Ms62_5,
889            filter: 0b100,
890            spi_3wire: true,
891        };
892        assert_eq!(config.bits(), 0b0011_0001);
893        assert_eq!(Config::from_bits(0x31), config);
894        assert_eq!(Config::default().bits(), 0x00);
895        // Bit 1 is reserved and is dropped on decode.
896        assert_eq!(Config::from_bits(0x02), Config::default());
897        for bits in 0..=u8::MAX {
898            let decoded = Config::from_bits(bits);
899            assert_eq!(Config::from_bits(decoded.bits()), decoded);
900        }
901    }
902
903    #[test]
904    fn calibration_parses_the_table_17_layout_and_round_trips() {
905        // Table 17: twelve little-endian words, dig_T1..dig_T3 then dig_P1..dig_P9,
906        // dig_T1 and dig_P1 unsigned and the rest signed.
907        let bytes: [u8; 24] = [
908            0x70, 0x6B, 0x43, 0x67, 0x18, 0xFC, 0x7D, 0x8E, 0x43, 0xD6, 0xD0, 0x0B, 0x27, 0x0B,
909            0x8C, 0x00, 0xF9, 0xFF, 0x8C, 0x3C, 0xF8, 0xC6, 0x70, 0x17,
910        ];
911        let calib = Calibration::parse(&bytes);
912        assert_eq!(
913            calib,
914            Calibration {
915                dig_t1: 27504,
916                dig_t2: 26435,
917                dig_t3: -1000,
918                dig_p1: 36477,
919                dig_p2: -10685,
920                dig_p3: 3024,
921                dig_p4: 2855,
922                dig_p5: 140,
923                dig_p6: -7,
924                dig_p7: 15500,
925                dig_p8: -14600,
926                dig_p9: 6000,
927            }
928        );
929        assert_eq!(calib.to_bytes(), bytes);
930        let sample = sample_calibration();
931        assert_eq!(Calibration::parse(&sample.to_bytes()), sample);
932    }
933
934    #[test]
935    fn measurement_parses_the_burst_read_and_round_trips() {
936        // Tables 24 and 25: up[19:12], up[11:4], up[3:0] in the upper nibble, then the
937        // same for ut. Pressure 0x655AC = 415148, temperature 0x7EED0 = 519888.
938        let data = [0x65, 0x5A, 0xC0, 0x7E, 0xED, 0x00];
939        let raw = Measurement::parse(&data);
940        assert_eq!(raw.pressure, 415_148);
941        assert_eq!(raw.temperature, 519_888);
942        assert_eq!(raw.to_bytes(), data);
943        // The low nibble of each XLSB byte is not part of the code.
944        assert_eq!(
945            Measurement::parse(&[0x65, 0x5A, 0xCF, 0x7E, 0xED, 0x0F]),
946            raw
947        );
948        assert_eq!(
949            Measurement::parse(&[0xFF; 6]).to_bytes(),
950            [0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xF0]
951        );
952    }
953
954    #[test]
955    fn skipped_measurements_read_as_0x80000() {
956        // Tables 21 and 22: a skipped measurement sets its output to 0x80000, which
957        // is also the Table 18 reset state of the data registers.
958        let raw = Measurement::parse(&[0x80, 0x00, 0x00, 0x80, 0x00, 0x00]);
959        assert_eq!(raw.pressure, SKIPPED_OUTPUT);
960        assert!(raw.pressure_skipped() && raw.temperature_skipped());
961        assert!(!Measurement::parse(&[0x65, 0x5A, 0xC0, 0x7E, 0xED, 0x00]).pressure_skipped());
962    }
963
964    #[test]
965    fn temperature_matches_the_worked_example() {
966        // 25.08 degrees C for adc_T = 519888, as in the BME280 module's anchor; the
967        // shift-based integer path lands exactly on the 128422 t_fine that example
968        // quotes.
969        let calib = worked_example_calibration();
970        let t_fine = calib.t_fine(519_888);
971        assert_eq!(t_fine, 128_422);
972        assert_eq!(compensate_temperature(t_fine), 2508);
973        let reading = calib.compensate(&Measurement {
974            pressure: 415_148,
975            temperature: 519_888,
976        });
977        assert_eq!(reading.temperature_centi_celsius, 2508);
978        assert!((reading.celsius() - 25.08).abs() < 0.001);
979    }
980
981    #[test]
982    fn reference_vector_pins_the_integer_output() {
983        // The exact output of the section 3.11.3 code for the Table 17 layout bytes in
984        // calibration_parses_the_table_17_layout_and_round_trips and the burst read
985        // 65 5A C0 7E ED 00. Both temperature functions store t_fine = 128422 for this
986        // input, so the appendix 8.1 double form is a like-for-like check here: it
987        // gives 100653.26 Pa, and the Q24.8 value below is within 0.01 Pa of it.
988        let calib = Calibration {
989            dig_t1: 27504,
990            dig_t2: 26435,
991            dig_t3: -1000,
992            dig_p1: 36477,
993            dig_p2: -10685,
994            dig_p3: 3024,
995            dig_p4: 2855,
996            dig_p5: 140,
997            dig_p6: -7,
998            dig_p7: 15500,
999            dig_p8: -14600,
1000            dig_p9: 6000,
1001        };
1002        let raw = Measurement::parse(&[0x65, 0x5A, 0xC0, 0x7E, 0xED, 0x00]);
1003        let reading = calib.compensate(&raw);
1004        assert_eq!(reading.temperature_centi_celsius, 2508);
1005        assert_eq!(reading.pressure_q24_8, 25_767_233);
1006        assert_eq!(reading.pascals(), 100_653);
1007        let (_, t_fine_ref) = reference_temperature(&calib, raw.temperature);
1008        assert_eq!(calib.t_fine(raw.temperature), 128_422);
1009        assert_eq!(t_fine_ref, 128_422);
1010        let p_ref = reference_pressure(&calib, raw.pressure, t_fine_ref);
1011        assert!((f64::from(reading.pressure_q24_8) / 256.0 - p_ref).abs() < 0.01);
1012    }
1013
1014    #[test]
1015    fn integer_compensation_tracks_the_floating_point_reference() {
1016        // Sweep raw codes across several coefficient sets and require the section
1017        // 3.11.3 integer path to agree with the appendix 8.1 double path to within
1018        // rounding. The two temperature functions round t_fine differently (the int32
1019        // path floors three shifts, the double casts once), at most three counts for
1020        // these coefficients, and the pressure formula moves about 0.03 Pa per count;
1021        // so the pressure functions are compared on the same signed 32-bit t_fine, the
1022        // carry-over the appendix defines them to share.
1023        for calib in [sample_calibration(), worked_example_calibration()] {
1024            for adc_t in [400_000, 450_000, 500_000, 519_888, 540_000, 600_000] {
1025                let t_fine = calib.t_fine(adc_t);
1026                let t_int = compensate_temperature(t_fine);
1027                let (t_ref, t_fine_ref) = reference_temperature(&calib, adc_t);
1028                assert!(
1029                    (t_fine - t_fine_ref).abs() <= 3,
1030                    "t_fine {t_fine} vs {t_fine_ref}"
1031                );
1032                assert!(
1033                    (f64::from(t_int) / 100.0 - t_ref).abs() < 0.02,
1034                    "temperature {t_int} vs {t_ref}"
1035                );
1036                for adc_p in [300_000, 350_000, 415_148, 450_000, 512_000] {
1037                    let p_int = calib.compensate_pressure(adc_p, t_fine);
1038                    let p_ref = reference_pressure(&calib, adc_p, t_fine);
1039                    assert!(
1040                        (f64::from(p_int) / 256.0 - p_ref).abs() < 0.05,
1041                        "pressure {p_int} (Q24.8) vs {p_ref} Pa"
1042                    );
1043                }
1044            }
1045        }
1046    }
1047
1048    #[test]
1049    fn any_20_bit_code_pair_compensates_without_overflow() {
1050        let calib = sample_calibration();
1051        for adc_t in (0..=0xF_FFFF).step_by(0x1_0000).chain([0xF_FFFF]) {
1052            for adc_p in (0..=0xF_FFFF).step_by(0x1_0000).chain([0xF_FFFF]) {
1053                let _ = calib.compensate(&Measurement {
1054                    pressure: adc_p,
1055                    temperature: adc_t,
1056                });
1057            }
1058        }
1059    }
1060
1061    #[test]
1062    fn zero_dig_p1_takes_the_division_guard() {
1063        // Section 3.11.3 returns 0 from bmp280_compensate_P_int64 when its var1 is
1064        // zero, "to avoid exception caused by division by zero", and appendix 8.1 does
1065        // the same; a dig_P1 of 0 reaches that branch for any raw code.
1066        let base = sample_calibration();
1067        let calib = Calibration { dig_p1: 0, ..base };
1068        let raw = Measurement::parse(&[0x65, 0x5A, 0xC0, 0x7E, 0xED, 0x00]);
1069        let reading = calib.compensate(&raw);
1070        assert_eq!(reading.pressure_q24_8, 0);
1071        assert_eq!(reading.pascals(), 0);
1072        let expected = base.compensate(&raw).temperature_centi_celsius;
1073        assert_eq!(reading.temperature_centi_celsius, expected);
1074        let t_fine = calib.t_fine(raw.temperature);
1075        assert_eq!(reference_pressure(&calib, raw.pressure, t_fine), 0.0);
1076    }
1077
1078    #[test]
1079    fn reading_unit_helpers_match_the_reference_code_comments() {
1080        // Section 3.11.3: "24674867" in Q24.8 is 96386.2 Pa = 963.862 hPa, and a
1081        // temperature output of "5123" equals 51.23 degrees C.
1082        let reading = Reading {
1083            temperature_centi_celsius: 5123,
1084            pressure_q24_8: 24_674_867,
1085        };
1086        assert!((reading.celsius() - 51.23).abs() < 0.001);
1087        assert_eq!(reading.pascals(), 96_386);
1088        assert!((reading.hectopascals() - 963.862).abs() < 0.001);
1089    }
1090
1091    // bmp280_compensate_T_double from appendix 8.1, used only to validate the integer
1092    // path above. It returns the Celsius output and the signed 32-bit t_fine the
1093    // appendix stores for its pressure function.
1094    fn reference_temperature(c: &Calibration, adc_t: u32) -> (f64, i32) {
1095        let adc_t = f64::from(adc_t);
1096        let var1 = (adc_t / 16384.0 - f64::from(c.dig_t1) / 1024.0) * f64::from(c.dig_t2);
1097        let var2 = {
1098            let v = adc_t / 131072.0 - f64::from(c.dig_t1) / 8192.0;
1099            v * v * f64::from(c.dig_t3)
1100        };
1101        ((var1 + var2) / 5120.0, (var1 + var2) as i32)
1102    }
1103
1104    // bmp280_compensate_P_double from appendix 8.1, reading the signed 32-bit t_fine
1105    // its temperature function stored.
1106    fn reference_pressure(c: &Calibration, adc_p: u32, t_fine: i32) -> f64 {
1107        let mut var1 = (f64::from(t_fine) / 2.0) - 64000.0;
1108        let mut var2 = var1 * var1 * f64::from(c.dig_p6) / 32768.0;
1109        var2 += var1 * f64::from(c.dig_p5) * 2.0;
1110        var2 = (var2 / 4.0) + (f64::from(c.dig_p4) * 65536.0);
1111        var1 =
1112            (f64::from(c.dig_p3) * var1 * var1 / 524288.0 + f64::from(c.dig_p2) * var1) / 524288.0;
1113        var1 = (1.0 + var1 / 32768.0) * f64::from(c.dig_p1);
1114        if var1 == 0.0 {
1115            return 0.0;
1116        }
1117        let mut p = 1048576.0 - f64::from(adc_p);
1118        p = (p - (var2 / 4096.0)) * 6250.0 / var1;
1119        var1 = f64::from(c.dig_p9) * p * p / 2147483648.0;
1120        var2 = p * f64::from(c.dig_p8) / 32768.0;
1121        p + (var1 + var2 + f64::from(c.dig_p7)) / 16.0
1122    }
1123}