bosch_bme680/
data.rs

1#[derive(Debug)]
2pub struct CalibrationData {
3    // Temperature coefficients
4    pub par_t1: u16,
5    pub par_t2: i16,
6    pub par_t3: i8,
7    // Pressure coefficients
8    pub par_p1: u16,
9    pub par_p2: i16,
10    pub par_p3: i8,
11    pub par_p4: i16,
12    pub par_p5: i16,
13    pub par_p6: i8,
14    pub par_p7: i8,
15    pub par_p8: i16,
16    pub par_p9: i16,
17    pub par_p10: u8,
18    // Humidity coefficients
19    pub par_h1: u16,
20    pub par_h2: u16,
21    pub par_h3: i8,
22    pub par_h4: i8,
23    pub par_h5: i8,
24    pub par_h6: u8,
25    pub par_h7: i8,
26    // Gas heater
27    pub par_gh1: i8,
28    pub par_gh2: i16,
29    pub par_gh3: i8,
30
31    // Other
32    pub res_heat_range: u8,
33    pub res_heat_val: i8,
34    pub range_sw_err: i8,
35}
36
37/// Measurment data returned from the sensor
38#[derive(Debug)]
39#[cfg_attr(feature = "serde", derive(serde::Serialize))]
40pub struct MeasurmentData {
41    /// Temperature in °C
42    pub temperature: f32,
43    /// Relative humidity in %
44    pub humidity: f32,
45    /// Pressure in hPa
46    pub pressure: f32,
47    /// Gas resistance in Ohms
48    /// None if gas measurment is disabled or gas measurment hasn't finished in time according to the gas_measuring bit.
49    pub gas_resistance: Option<f32>,
50}
51
52impl MeasurmentData {
53    pub(crate) fn from_raw(
54        raw_data: crate::bitfields::RawData<[u8; 15]>,
55        calibration_data: &CalibrationData,
56        variant: &crate::config::Variant,
57    ) -> Option<Self> {
58        // First, check to make sure a measurement is ready. If not, bail.
59        if raw_data.measuring() && !raw_data.new_data() {
60            return None;
61        }
62        let (temperature, t_fine) =
63            crate::calculate_temperature(raw_data.temperature_adc().0, calibration_data);
64        let pressure =
65            crate::calculate_pressure(raw_data.pressure_adc().0, calibration_data, t_fine);
66        let humidity =
67            crate::calculate_humidity(raw_data.humidity_adc().0, calibration_data, t_fine);
68        let gas_resistance = if raw_data.gas_valid() && !raw_data.gas_measuring() {
69            let gas_resistance = variant.calc_gas_resistance(
70                raw_data.gas_adc().0,
71                calibration_data.range_sw_err,
72                raw_data.gas_range() as usize,
73            );
74            Some(gas_resistance)
75        } else {
76            None
77        };
78
79        Some(MeasurmentData {
80            temperature,
81            gas_resistance,
82            humidity,
83            pressure,
84        })
85    }
86}
87
88pub fn calculate_temperature(adc_temp: u32, calibration_data: &CalibrationData) -> (f32, f32) {
89    let temp_adc = adc_temp as f32;
90    let var_1 = ((temp_adc / 16384.) - (calibration_data.par_t1 as f32 / 1024.))
91        * calibration_data.par_t2 as f32;
92    let var_2 = (((temp_adc / 131072.) - (calibration_data.par_t1 as f32 / 8192.))
93        * ((temp_adc / 131072.) - (calibration_data.par_t1 as f32 / 8192.)))
94        * (calibration_data.par_t3 as f32 * 16.);
95    // store for use in pressure and hummidity calculation
96    let t_fine = var_1 + var_2;
97    let calc_temp = t_fine / 5120.;
98    (calc_temp, t_fine)
99}
100
101pub fn calculate_pressure(adc_press: u32, calibration_data: &CalibrationData, t_fine: f32) -> f32 {
102    let adc_press = adc_press as f32;
103    let var1 = (t_fine / 2.) - 64000.;
104    let var2 = var1 * var1 * (calibration_data.par_p6 as f32 / 131072.);
105    let var2 = var2 + (var1 * calibration_data.par_p5 as f32 * 2.);
106    let var2 = (var2 / 4.) + (calibration_data.par_p4 as f32 * 65536.);
107    let var1 = (((calibration_data.par_p3 as f32 * var1 * var1) / 16384.)
108        + (calibration_data.par_p2 as f32 * var1))
109        / 524288.;
110    let var1 = (1. + (var1 / 32768.)) * calibration_data.par_p1 as f32;
111    let mut calc_pres = 1048576. - adc_press;
112    if var1 as i16 != 0 {
113        calc_pres = ((calc_pres - (var2 / 4096.)) * 6250.) / var1;
114        let var1 = (calibration_data.par_p9 as f32 * calc_pres * calc_pres) / 2147483648.;
115        let var2 = calc_pres * (calibration_data.par_p8 as f32 / 32768.);
116        let var3 = (calc_pres / 256.)
117            * (calc_pres / 256.)
118            * (calc_pres / 256.)
119            * (calibration_data.par_p10 as f32 / 131072.);
120        calc_pres += (var1 + var2 + var3 + (calibration_data.par_p7 as f32 * 128.)) / 16.;
121    } else {
122        calc_pres = 0.;
123    }
124    calc_pres
125}
126
127pub fn calculate_humidity(adc_hum: u16, calibration_data: &CalibrationData, t_fine: f32) -> f32 {
128    let adc_hum = adc_hum as f32;
129    let temp_comp = t_fine / 5120.;
130    let var1 = (adc_hum)
131        - ((calibration_data.par_h1 as f32 * 16.)
132            + ((calibration_data.par_h3 as f32 / 2.) * temp_comp));
133    let var2 = var1
134        * ((calibration_data.par_h2 as f32 / 262144.)
135            * (1.
136                + ((calibration_data.par_h4 as f32 / 16384.) * temp_comp)
137                + ((calibration_data.par_h5 as f32 / 1048576.) * temp_comp * temp_comp)));
138    let var3 = calibration_data.par_h6 as f32 / 16384.;
139    let var4 = calibration_data.par_h7 as f32 / 2097152.;
140    let mut calc_hum = var2 + ((var3 + (var4 * temp_comp)) * var2 * var2);
141    calc_hum = calc_hum.clamp(0., 100.);
142    // Reference implemetation uses this.
143    // if calc_hum > 100. {
144    //     calc_hum = 100.;
145    // } else if calc_hum < 0. {
146    //     calc_hum = 0.
147    // }
148    calc_hum
149}
150
151#[cfg(test)]
152mod tests {
153    use crate::data::{
154        calculate_humidity, calculate_pressure, calculate_temperature, CalibrationData,
155    };
156    use approx::assert_abs_diff_eq;
157
158    static CALIBRATION_DATA: CalibrationData = CalibrationData {
159        par_t1: 25942,
160        par_t2: 26664,
161        par_t3: 3,
162        par_p1: 37439,
163        par_p2: -10316,
164        par_p3: 88,
165        par_p4: 10477,
166        par_p5: -308,
167        par_p6: 30,
168        par_p7: 62,
169        par_p8: -5160,
170        par_p9: -1568,
171        par_p10: 30,
172        par_h1: 881,
173        par_h2: 989,
174        par_h3: 0,
175        par_h4: 45,
176        par_h5: 20,
177        par_h6: 120,
178        par_h7: -100,
179        par_gh1: -69,
180        par_gh2: -9092,
181        par_gh3: 18,
182        res_heat_range: 1,
183        res_heat_val: 30,
184        range_sw_err: 0,
185    };
186
187    #[test]
188    fn test_calc_temp() {
189        // Calc_temp: temp_adc: 482062, calc_temp: 21.295866, tfine: 109034.835938
190        // Calc_temp: temp_adc: 482452, calc_temp: 21.419861, tfine: 109669.687500
191        // Calc_temp: temp_adc: 482060, calc_temp: 21.295231, tfine: 109031.585938
192        // Calc_temp: temp_adc: 482453, calc_temp: 21.420179, tfine: 109671.320312
193        // Calc_temp: temp_adc: 482058, calc_temp: 21.294596, tfine: 109028.328125
194
195        let data = [
196            (482062, 21.295866, 109034.835938),
197            (482452, 21.419861, 109669.687500),
198            (482060, 21.295231, 109031.585938),
199            (482453, 21.420179, 109671.320312),
200            (482058, 21.294596, 109028.328125),
201        ];
202        for (temp_adc, actual_temp, actual_tfine) in data {
203            let (temp, calc_tfine) = calculate_temperature(temp_adc, &CALIBRATION_DATA);
204
205            assert_abs_diff_eq!(actual_temp, temp);
206            assert_abs_diff_eq!(actual_tfine, calc_tfine);
207        }
208    }
209    #[test]
210    fn test_calc_humidity() {
211        // hum_adc: 25537, calc_hum: 59.469585, tfine: 109842.234375
212        // hum_adc: 25531, calc_hum: 59.410557, tfine: 109090.187500
213        // hum_adc: 25545, calc_hum: 59.515030, tfine: 109643.640625
214        // hum_adc: 25535, calc_hum: 59.431923, tfine: 108942.054688
215        // hum_adc: 25549, calc_hum: 59.537392, tfine: 109531.328125
216
217        let pairs = [
218            (25537, 59.469585, 109842.234375),
219            (25531, 59.410557, 109090.187500),
220            (25545, 59.515030, 109643.640625),
221            (25535, 59.431923, 108942.054688),
222            (25549, 59.537392, 109531.328125),
223        ];
224        for (hum_adc, actual_hum, tfine) in pairs {
225            let calc_hum = calculate_humidity(hum_adc, &CALIBRATION_DATA, tfine);
226            assert_abs_diff_eq!(calc_hum, actual_hum);
227        }
228    }
229    #[test]
230    fn test_calc_pressure() {
231        // pres_adc: 307582, calc_pres: 95058.664062, tfine: 111095.656250
232        // pres_adc: 307395, calc_pres: 95058.992188, tfine: 110130.359375
233        // pres_adc: 307469, calc_pres: 95059.296875, tfine: 110525.921875
234        // pres_adc: 307313, calc_pres: 95058.773438, tfine: 109695.726562
235        // pres_adc: 307254, calc_pres: 95060.328125, tfine: 109436.914062
236        let data = [
237            (307582, 95058.664062, 111095.656250),
238            (307395, 95058.992188, 110130.359375),
239            (307469, 95059.296875, 110525.921875),
240            (307313, 95058.773438, 109695.726562),
241            (307254, 95060.328125, 109436.914062),
242        ];
243        for (press_adc, actual_press, tfine) in data {
244            let calc_press = calculate_pressure(press_adc, &CALIBRATION_DATA, tfine);
245            assert_abs_diff_eq!(calc_press, actual_press);
246        }
247    }
248}