embedded_qmp6988/
lib.rs

1#![doc = include_str!("../README.md")]
2#![deny(unsafe_code, missing_docs)]
3#![no_std]
4
5use embedded_hal::i2c::{Operation, SevenBitAddress};
6#[allow(unused_imports)]
7use micromath::F32Ext;
8
9/// The I2C address when the SDO pin is connected to logic low
10pub const I2C_ADDRESS_LOGIC_LOW: SevenBitAddress = 0x70;
11/// The I2C address when the SDO pin is connected to logic high
12pub const I2C_ADDRESS_LOGIC_HIGH: SevenBitAddress = 0x56;
13/// The default I2C address (SDO pin connected to low)
14pub const DEFAULT_I2C_ADDRESS: SevenBitAddress = I2C_ADDRESS_LOGIC_LOW;
15
16const CHIP_ID_REGISTER: &[u8] = &[0xd1];
17const COE_B00_1_REGISTER: &[u8] = &[0xa0];
18const CTRL_MEAS_REGISTER: &[u8] = &[0xf4];
19const IIR_CNT_REGISTER: &[u8] = &[0xf1];
20const PRESS_TXD2: &[u8] = &[0xf7];
21const RESET_REGISTER: &[u8] = &[0xe0];
22
23/// All possible errors generated when using the Qmp6988 struct
24#[derive(Debug)]
25pub enum Error<I2cE>
26where
27    I2cE: embedded_hal::i2c::Error,
28{
29    /// I²C bus error
30    I2c(I2cE),
31    /// The QMP6988 chip has not been detected
32    ChipNotDetected,
33    /// The computed CRC and the one sent by the device mismatch
34    BadCrc,
35}
36
37impl<I2cE> From<I2cE> for Error<I2cE>
38where
39    I2cE: embedded_hal::i2c::Error,
40{
41    fn from(value: I2cE) -> Self {
42        Error::I2c(value)
43    }
44}
45
46/// IIR (Infinite Impulse Response) filter.
47///
48/// It chooses the amount of noise reduction being performed on the pressure
49/// measurement. The greater the coeff, the higher the noise reduction.
50#[derive(Clone, Copy, Debug, Default)]
51#[repr(u8)]
52pub enum IirFilter {
53    /// No filter is applied
54    Off = 0x00,
55    /// A coefficient 2 filter is applied
56    Coeff2 = 0x01,
57    /// A coefficient 4 filter is applied
58    #[default]
59    Coeff4 = 0x02,
60    /// A coefficient 8 filter is applied
61    Coeff8 = 0x03,
62    /// A coefficient 16 filter is applied
63    Coeff16 = 0x04,
64    /// A coefficient 32 filter is applied
65    Coeff32 = 0x05,
66}
67
68/// The oversampling setting.
69///
70/// It chooses the accuracy of the measurement, with an impact on the
71/// duration of the measurement. The greater the accuracy, the longer the
72/// duration of the measurement, and the higher the current consumption.
73#[derive(Clone, Copy, Debug, Default)]
74#[repr(u8)]
75pub enum OverSamplingSetting {
76    /// The shorter measurement, with the lowest accuracy. This is typically
77    /// used for weather monitoring.
78    HighSpeed,
79    /// A measurement with a litle more accuracy, but still a low current
80    /// consumption. This might be used for drop detection.
81    LowPower,
82    /// The standard setting, providing a compromise between the accuracy
83    /// of the measurement and its duration. This might be used for elevator
84    /// detection.
85    #[default]
86    Standard,
87    /// A high accuracy measurement, with a quite long duration. This might be
88    /// used for stair detection.
89    HighAccuracy,
90    /// The best accuracy measurement, with the longer duration and higher
91    /// current consumption. This is typically used for indoor navigation.
92    UltraHighAccuracy,
93}
94
95#[derive(Clone, Copy, Debug, Default)]
96#[repr(u8)]
97enum PowerMode {
98    #[default]
99    Sleep = 0x00,
100    Forced = 0x01,
101    #[allow(dead_code)]
102    Normal = 0x03,
103}
104
105#[derive(Clone, Copy, Debug, Default)]
106#[repr(u8)]
107enum OverSampling {
108    // Skipped = 0x00,
109    #[default]
110    X1 = 0x01,
111    X2 = 0x02,
112    X4 = 0x03,
113    X8 = 0x04,
114    X16 = 0x05,
115    X32 = 0x06,
116    // X64 = 0x07,
117}
118
119#[derive(Debug, Default)]
120struct Coe {
121    a0: i32,
122    a1: i16,
123    a2: i16,
124    b00: i32,
125    bt1: i16,
126    bt2: i16,
127    bp1: i16,
128    b11: i16,
129    bp2: i16,
130    b12: i16,
131    b21: i16,
132    bp3: i16,
133}
134
135impl From<&[u8; 25]> for Coe {
136    fn from(value: &[u8; 25]) -> Self {
137        Coe {
138            a0: ((((value[18] as u32) << 12 | (value[19] as u32) << 4 | (value[24] as u32) & 0x0f)
139                << 12) as i32)
140                >> 12,
141            a1: ((value[20] as u16) << 8 | (value[21] as u16)) as i16,
142            a2: ((value[22] as u16) << 8 | (value[23] as u16)) as i16,
143            b00: ((((value[0] as u32) << 12
144                | (value[1] as u32) << 4
145                | ((value[24] as u32) & 0xf0) >> 4)
146                << 12) as i32)
147                >> 12,
148            bt1: ((value[2] as u16) << 8 | (value[3] as u16)) as i16,
149            bt2: ((value[4] as u16) << 8 | (value[5] as u16)) as i16,
150            bp1: ((value[6] as u16) << 8 | (value[7] as u16)) as i16,
151            b11: ((value[8] as u16) << 8 | (value[9] as u16)) as i16,
152            bp2: ((value[10] as u16) << 8 | (value[11] as u16)) as i16,
153            b12: ((value[12] as u16) << 8 | (value[13] as u16)) as i16,
154            b21: ((value[14] as u16) << 8 | (value[15] as u16)) as i16,
155            bp3: ((value[16] as u16) << 8 | (value[17] as u16)) as i16,
156        }
157    }
158}
159
160#[derive(Debug, Default)]
161struct K {
162    a0: f32,
163    a1: f32,
164    a2: f32,
165    b00: f32,
166    bt1: f32,
167    bt2: f32,
168    bp1: f32,
169    b11: f32,
170    bp2: f32,
171    b12: f32,
172    b21: f32,
173    bp3: f32,
174}
175
176impl From<&Coe> for K {
177    fn from(value: &Coe) -> Self {
178        K {
179            a0: value.a0 as f32 / 16.0,
180            a1: -6.30E-03 + ((4.30E-04 * value.a1 as f32) / 32_767.0),
181            a2: -1.90E-11 + ((1.20E-10 * value.a2 as f32) / 32_767.0),
182            b00: value.b00 as f32 / 16.0,
183            bt1: 1.00E-01 + ((9.10E-02 * value.bt1 as f32) / 32_767.0),
184            bt2: 1.20E-08 + ((1.20E-06 * value.bt2 as f32) / 32_767.0),
185            bp1: 3.30E-02 + ((1.90E-02 * value.bp1 as f32) / 32_767.0),
186            b11: 2.10E-07 + ((1.40E-07 * value.b11 as f32) / 32_767.0),
187            bp2: -6.30E-10 + ((3.50E-10 * value.bp2 as f32) / 32_767.0),
188            b12: 2.90E-13 + ((7.60E-13 * value.b12 as f32) / 32_767.0),
189            b21: 2.10E-15 + ((1.20E-14 * value.b21 as f32) / 32_767.0),
190            bp3: 1.30E-16 + ((7.90E-17 * value.bp3 as f32) / 32_767.0),
191        }
192    }
193}
194
195/// The result of a measurement.
196///
197/// Such a measurement can be obtained using [`Qmp6988::measure()`].
198#[derive(Clone, Copy, Debug, Default)]
199pub struct Measurement {
200    /// The measured barometric pressure (in hPa).
201    pub pressure: f32,
202    /// The measured temperature (in °C).
203    pub temperature: f32,
204}
205
206/// QMP6988 device driver
207#[derive(Debug)]
208pub struct Qmp6988<I2C, D> {
209    address: SevenBitAddress,
210    coe: Coe,
211    delay: D,
212    filter: IirFilter,
213    i2c: I2C,
214    k: K,
215    oversampling_setting: OverSamplingSetting,
216}
217
218impl<I2C, D> Qmp6988<I2C, D>
219where
220    I2C: embedded_hal::i2c::I2c,
221    D: embedded_hal::delay::DelayNs,
222{
223    /// Perform a measurement of pressure and temperature.
224    ///
225    /// This uses the forced power mode to perform a single measurement and
226    /// automatically go back to the sleep power mode where the sensor has the
227    /// lowest current consumption.
228    pub fn measure(&mut self) -> Result<Measurement, Error<I2C::Error>> {
229        self.apply_power_mode(PowerMode::Forced)?;
230        self.delay.delay_ms(self.get_measurement_duration());
231        let mut dp = [0u8; 3];
232        let mut dt = [0u8; 3];
233        let mut operations = [
234            Operation::Write(PRESS_TXD2),
235            Operation::Read(&mut dp),
236            Operation::Read(&mut dt),
237        ];
238        self.i2c.transaction(self.address, &mut operations)?;
239        let dp = Self::get_i32_value(&dp) - 8_388_608;
240        let dt = Self::get_i32_value(&dt) - 8_388_608;
241        let temperature = self.compensate_temperature(dt);
242        let pressure = self.compensate_pressure(dp, temperature);
243        Ok(Measurement {
244            pressure: pressure / 100.0,
245            temperature: temperature / 256.0,
246        })
247    }
248
249    /// Create a new instance of the QMP6988 device.
250    pub fn new(i2c: I2C, address: SevenBitAddress, delay: D) -> Result<Self, Error<I2C::Error>> {
251        let mut device = Self {
252            address,
253            coe: Coe::default(),
254            delay,
255            filter: IirFilter::default(),
256            i2c,
257            k: K::default(),
258            oversampling_setting: OverSamplingSetting::default(),
259        };
260        device.check_device()?;
261        device.get_calibration_data()?;
262        device.apply_filter()?;
263        device.apply_measure_control_parameters()?;
264        Ok(device)
265    }
266
267    /// Perform a soft reset.
268    pub fn reset(&mut self) -> Result<(), Error<I2C::Error>> {
269        self.i2c.write(self.address, RESET_REGISTER)?;
270        self.delay.delay_ms(10);
271        Ok(())
272    }
273
274    /// Define the IIR (Infinite Impulse Response) filter to use during the
275    /// measurements.
276    pub fn set_filter(&mut self, filter: IirFilter) -> Result<(), Error<I2C::Error>> {
277        self.filter = filter;
278        self.apply_filter()
279    }
280
281    /// Define the oversampling setting to use during the measurements.
282    pub fn set_oversampling_setting(
283        &mut self,
284        oversampling_setting: OverSamplingSetting,
285    ) -> Result<(), Error<I2C::Error>> {
286        self.oversampling_setting = oversampling_setting;
287        self.apply_measure_control_parameters()
288    }
289
290    fn apply_filter(&mut self) -> Result<(), Error<I2C::Error>> {
291        let filter = [self.filter as u8];
292        let mut operations = [
293            Operation::Write(IIR_CNT_REGISTER),
294            Operation::Write(&filter),
295        ];
296        self.i2c.transaction(self.address, &mut operations)?;
297        self.delay.delay_ms(20);
298        Ok(())
299    }
300
301    fn apply_measure_control_parameters(&mut self) -> Result<(), Error<I2C::Error>> {
302        let (pressure_oversampling, temperature_oversampling) = self.get_oversamplings();
303        let data = [(temperature_oversampling as u8) << 5
304            | (pressure_oversampling as u8) << 2
305            | (PowerMode::Sleep as u8)];
306        let mut operations = [
307            Operation::Write(CTRL_MEAS_REGISTER),
308            Operation::Write(&data),
309        ];
310        self.i2c.transaction(self.address, &mut operations)?;
311        self.delay.delay_ms(20);
312        Ok(())
313    }
314
315    fn apply_power_mode(&mut self, power_mode: PowerMode) -> Result<(), Error<I2C::Error>> {
316        let mut data = [0u8; 1];
317        let mut operations = [
318            Operation::Write(CTRL_MEAS_REGISTER),
319            Operation::Read(&mut data),
320        ];
321        self.i2c.transaction(self.address, &mut operations)?;
322        data[0] = (data[0] & 0xfc) | power_mode as u8;
323        let mut operations = [
324            Operation::Write(CTRL_MEAS_REGISTER),
325            Operation::Write(&data),
326        ];
327        self.i2c.transaction(self.address, &mut operations)?;
328        self.delay.delay_ms(20);
329        Ok(())
330    }
331
332    fn check_device(&mut self) -> Result<(), Error<I2C::Error>> {
333        let mut chip_id = [0u8; 1];
334        let mut operations = [
335            Operation::Write(CHIP_ID_REGISTER),
336            Operation::Read(&mut chip_id),
337        ];
338        self.i2c.transaction(self.address, &mut operations)?;
339        if chip_id[0] == 0x5c {
340            Ok(())
341        } else {
342            Err(Error::ChipNotDetected)
343        }
344    }
345
346    fn compensate_pressure(&self, dp: i32, temperature: f32) -> f32 {
347        let dp = dp as f32;
348        self.k.b00
349            + self.k.bt1 * temperature
350            + self.k.bp1 * dp
351            + self.k.b11 * temperature * dp
352            + self.k.bt2 * temperature.powf(2.0)
353            + self.k.bp2 * dp.powf(2.0)
354            + self.k.b12 * dp * temperature.powf(2.0)
355            + self.k.b21 * dp.powf(2.0) * temperature
356            + self.k.bp3 * dp.powf(3.0)
357    }
358
359    fn compensate_temperature(&self, dt: i32) -> f32 {
360        let dt = dt as f32;
361        self.k.a0 + self.k.a1 * dt + self.k.a2 * dt.powf(2.0)
362    }
363
364    fn get_calibration_data(&mut self) -> Result<(), Error<I2C::Error>> {
365        let mut coe = [0u8; 25];
366        let mut operations = [
367            Operation::Write(COE_B00_1_REGISTER),
368            Operation::Read(&mut coe),
369        ];
370        self.i2c.transaction(self.address, &mut operations)?;
371        self.coe = (&coe).into();
372        self.k = (&self.coe).into();
373        Ok(())
374    }
375
376    fn get_measurement_duration(&self) -> u32 {
377        match self.oversampling_setting {
378            OverSamplingSetting::HighSpeed => 6,
379            OverSamplingSetting::LowPower => 8,
380            OverSamplingSetting::Standard => 11,
381            OverSamplingSetting::HighAccuracy => 19,
382            OverSamplingSetting::UltraHighAccuracy => 34,
383        }
384    }
385
386    fn get_oversamplings(&self) -> (OverSampling, OverSampling) {
387        match self.oversampling_setting {
388            OverSamplingSetting::HighSpeed => (OverSampling::X2, OverSampling::X1),
389            OverSamplingSetting::LowPower => (OverSampling::X4, OverSampling::X1),
390            OverSamplingSetting::Standard => (OverSampling::X8, OverSampling::X1),
391            OverSamplingSetting::HighAccuracy => (OverSampling::X16, OverSampling::X2),
392            OverSamplingSetting::UltraHighAccuracy => (OverSampling::X32, OverSampling::X4),
393        }
394    }
395
396    #[inline]
397    fn get_i32_value(data: &[u8; 3]) -> i32 {
398        ((data[0] as u32) << 16 | (data[1] as u32) << 8 | (data[2] as u32)) as i32
399    }
400}
401
402/// Calculate the altitude (in m) from a measurement.
403pub fn calculate_altitude(measurement: Measurement) -> f32 {
404    ((1_013.25 / measurement.pressure).powf(1.0 / 5.257) - 1.0) * (measurement.temperature + 273.15)
405        / 0.0065
406}
407
408#[cfg(test)]
409mod tests {
410    use crate::*;
411    use embedded_hal_mock::eh1::delay::StdSleep as Delay;
412    use embedded_hal_mock::eh1::i2c::{Mock as I2cMock, Transaction as I2cTransaction};
413
414    fn create_device() -> Qmp6988<I2cMock, Delay> {
415        let expectations = [
416            I2cTransaction::transaction_start(DEFAULT_I2C_ADDRESS),
417            I2cTransaction::write(DEFAULT_I2C_ADDRESS, CHIP_ID_REGISTER.to_vec()),
418            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x5c].to_vec()),
419            I2cTransaction::transaction_end(DEFAULT_I2C_ADDRESS),
420            I2cTransaction::transaction_start(DEFAULT_I2C_ADDRESS),
421            I2cTransaction::write(DEFAULT_I2C_ADDRESS, COE_B00_1_REGISTER.to_vec()),
422            I2cTransaction::read(
423                DEFAULT_I2C_ADDRESS,
424                [
425                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
426                    0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
427                ]
428                .to_vec(),
429            ),
430            I2cTransaction::transaction_end(DEFAULT_I2C_ADDRESS),
431            I2cTransaction::transaction_start(DEFAULT_I2C_ADDRESS),
432            I2cTransaction::write(DEFAULT_I2C_ADDRESS, IIR_CNT_REGISTER.to_vec()),
433            I2cTransaction::write(DEFAULT_I2C_ADDRESS, [0x02].to_vec()),
434            I2cTransaction::transaction_end(DEFAULT_I2C_ADDRESS),
435            I2cTransaction::transaction_start(DEFAULT_I2C_ADDRESS),
436            I2cTransaction::write(DEFAULT_I2C_ADDRESS, CTRL_MEAS_REGISTER.to_vec()),
437            I2cTransaction::write(DEFAULT_I2C_ADDRESS, [0x30].to_vec()),
438            I2cTransaction::transaction_end(DEFAULT_I2C_ADDRESS),
439        ];
440        let i2c = I2cMock::new(&expectations);
441        let mut device = Qmp6988::new(i2c, DEFAULT_I2C_ADDRESS, Delay {}).unwrap();
442        device.i2c.done();
443        device
444    }
445
446    #[test]
447    fn calculate_altitude() {
448        assert!(
449            (crate::calculate_altitude(Measurement {
450                pressure: 991.32,
451                temperature: 20.55,
452            }) - 188.46)
453                .abs()
454                < 0.01
455        );
456        assert!(
457            (crate::calculate_altitude(Measurement {
458                pressure: 1013.25,
459                temperature: 17.93,
460            }) - 0.0)
461                .abs()
462                < 0.01
463        );
464        assert!(
465            (crate::calculate_altitude(Measurement {
466                pressure: 1013.25,
467                temperature: 37.5,
468            }) - 0.0)
469                .abs()
470                < 0.01
471        );
472        assert!(
473            (crate::calculate_altitude(Measurement {
474                pressure: 962.81,
475                temperature: 19.37,
476            }) - 439.25)
477                .abs()
478                < 0.01
479        );
480    }
481
482    #[test]
483    fn measure() {
484        let expectations = [
485            I2cTransaction::transaction_start(DEFAULT_I2C_ADDRESS),
486            I2cTransaction::write(DEFAULT_I2C_ADDRESS, CTRL_MEAS_REGISTER.to_vec()),
487            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x30].to_vec()),
488            I2cTransaction::transaction_end(DEFAULT_I2C_ADDRESS),
489            I2cTransaction::transaction_start(DEFAULT_I2C_ADDRESS),
490            I2cTransaction::write(DEFAULT_I2C_ADDRESS, CTRL_MEAS_REGISTER.to_vec()),
491            I2cTransaction::write(DEFAULT_I2C_ADDRESS, [0x31].to_vec()),
492            I2cTransaction::transaction_end(DEFAULT_I2C_ADDRESS),
493            I2cTransaction::transaction_start(DEFAULT_I2C_ADDRESS),
494            I2cTransaction::write(DEFAULT_I2C_ADDRESS, PRESS_TXD2.to_vec()),
495            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x00, 0x01, 0x02].to_vec()),
496            I2cTransaction::read(DEFAULT_I2C_ADDRESS, [0x00, 0x01, 0x02].to_vec()),
497            I2cTransaction::transaction_end(DEFAULT_I2C_ADDRESS),
498        ];
499        let mut device = create_device();
500        device.i2c.update_expectations(&expectations);
501        device.measure().unwrap();
502        device.i2c.done();
503    }
504
505    #[test]
506    fn reset() {
507        let expectations = [I2cTransaction::write(
508            DEFAULT_I2C_ADDRESS,
509            RESET_REGISTER.to_vec(),
510        )];
511        let mut device = create_device();
512        device.i2c.update_expectations(&expectations);
513        device.reset().unwrap();
514        device.i2c.done();
515    }
516
517    #[test]
518    fn set_filter() {
519        let expectations = [
520            I2cTransaction::transaction_start(DEFAULT_I2C_ADDRESS),
521            I2cTransaction::write(DEFAULT_I2C_ADDRESS, IIR_CNT_REGISTER.to_vec()),
522            I2cTransaction::write(DEFAULT_I2C_ADDRESS, [0x05].to_vec()),
523            I2cTransaction::transaction_end(DEFAULT_I2C_ADDRESS),
524        ];
525        let mut device = create_device();
526        device.i2c.update_expectations(&expectations);
527        device.set_filter(IirFilter::Coeff32).unwrap();
528        device.i2c.done();
529    }
530
531    #[test]
532    fn set_oversampling_setting() {
533        let expectations = [
534            I2cTransaction::transaction_start(DEFAULT_I2C_ADDRESS),
535            I2cTransaction::write(DEFAULT_I2C_ADDRESS, CTRL_MEAS_REGISTER.to_vec()),
536            I2cTransaction::write(DEFAULT_I2C_ADDRESS, [0x28].to_vec()),
537            I2cTransaction::transaction_end(DEFAULT_I2C_ADDRESS),
538        ];
539        let mut device = create_device();
540        device.i2c.update_expectations(&expectations);
541        device
542            .set_oversampling_setting(OverSamplingSetting::HighSpeed)
543            .unwrap();
544        device.i2c.done();
545    }
546}