bosch_bme680/
bitfields.rs

1use crate::config::{Configuration, HeaterProfile, IIRFilter, Oversampling, SensorMode};
2use bitfield::bitfield;
3use core::time::Duration;
4
5bitfield! {
6    pub struct RawConfig([u8]);
7    impl Debug;
8    u8;
9    // 0x75<4:2>
10    pub from into IIRFilter, filter, set_filter : calc_position(4, 4), calc_position(2, 4);
11    // 0x74<1:0>
12    pub mode, set_mode: calc_position(1, 3), calc_position(0, 3);
13    // 0x74<4:2>
14    pub from into Oversampling, pressure_oversampling, set_pressure_oversampling : calc_position(4, 3), calc_position(2, 3);
15    // 0x74<7:5>
16    pub from into Oversampling, temperature_oversampling, set_temperature_oversampling: calc_position(7, 3), calc_position(5, 3);
17    // 0x72<2:0>
18    pub from into Oversampling, humidity_oversampling, set_humidity_oversampling: calc_position(2, 1), calc_position(0, 1);
19    // 0x71<3:0>
20    pub from into HeaterProfile, heater_profile, set_heater_profile: calc_position(3, 0), calc_position(0, 0);
21    // 0x71<4>
22    pub run_gas, set_run_gas: calc_position(4, 0);
23}
24
25impl RawConfig<[u8; 5]> {
26    /// Applies all present settings in the config.
27    /// None values will be ignored and left as they were before.
28    /// That means leaving in the default values before configuring and leaving prior set values as they were are.
29    /// Does not check for nonsensical configuration settings, such as trying to read the gas meas without specifying a gas config
30    pub fn apply_config(&mut self, config: &Configuration) {
31        // maybe consume config here
32        let config = config.clone();
33        if let Some(temperature_oversampling) = config.temperature_oversampling {
34            self.set_temperature_oversampling(temperature_oversampling);
35        }
36        if let Some(pressure_oversampling) = config.pressure_oversampling {
37            self.set_pressure_oversampling(pressure_oversampling);
38        }
39        if let Some(humidity_oversampling) = config.humidity_oversampling {
40            self.set_humidity_oversampling(humidity_oversampling);
41        }
42        if let Some(filter) = config.filter {
43            self.set_filter(filter);
44        }
45        if let Some(_gas_config) = config.gas_config {
46            self.set_run_gas(true);
47            // Only heater profile0 is needed for forced mode.
48            // Sequential mode is not implemented and only available in bme688
49            self.set_heater_profile(HeaterProfile::Profile0);
50        }
51    }
52    
53}
54
55bitfield! {
56    pub struct RawGasConfig([u8]);
57    impl Debug;
58    u8;
59    pub res_heat, _: 7, 0;
60    pub from into GasWaitDuration, gas_wait, _: calc_position(7, 1), calc_position(0, 1);
61}
62#[derive(Debug)]
63pub struct GasWaitDuration(Duration);
64impl From<u8> for GasWaitDuration {
65    fn from(val: u8) -> Self {
66        Self(Duration::from_millis(val as u64))
67    }
68}
69
70impl From<GasWaitDuration> for Duration {
71    fn from(value: GasWaitDuration) -> Self {
72        value.0
73    }
74}
75
76// Ctrl_meas content. Register 0x74
77bitfield! {
78    pub struct CtrlMeasurment(u8);
79    impl Debug;
80    u8;
81    pub from into Oversampling, temperature_os, set_temperature_os: 7, 5;
82    pub from into Oversampling, pressure_os, set_pressure_os: 4, 2;
83    pub from into SensorMode, mode, set_mode: 1, 0;
84}
85
86bitfield! {
87    pub struct MeasurmentStatus(u8);
88    impl Debug;
89    u8;
90    pub bool, new_data, _: 7;
91    pub bool, gas_measuring, _: 6;
92    pub bool, measuring, _: 5;
93    pub gas_meas_index, _: 3, 0;
94}
95
96// 15 long
97// 0: meas_status_0 0x1D
98// 1: _             0x1E
99// 2: press_msb     0x2F
100// 3: press_lsb     0x20
101// 4: press_xlsb    0x21
102// 5: temp_msb      0x22
103// 6: temp_lsb      0x23
104// 7: temp_xlsb     0x24
105// 8: hum_msb       0x25
106// 9: hum_lsb       0x26
107// 10: _            0x27
108// 11: _            0x28
109// 12: _            0x29
110// 13: gas_r_msb    0x2A
111// 14: gas_r_lsb    0x2B
112bitfield! {
113    pub struct RawData([u8]);
114    impl Debug;
115
116    pub u8, gas_range, _: calc_position(3, 14), calc_position(0, 14);
117    // Each measuring cycle contains a  gas measurment slot, either a real one or a dummy one.
118    // gas_valid indicates wether a real gas conversion (i.e. not a dummy one) is returned.
119    pub bool, gas_valid, _: calc_position(5, 14);
120    // Indicates if the heater target temperature was reached
121    pub bool, heater_sable, _: calc_position(4, 14);
122    pub u16, from into GasADC, gas_adc, _: calc_position(7, 14), calc_position(0, 13);
123    pub u16, from into Humidity, humidity_adc, _: calc_position(7, 9), calc_position(0, 8);
124    pub u32, from into Measurment, temperature_adc, _: calc_position(7, 7), calc_position(0, 5);
125    pub u32, from into Measurment, pressure_adc, _: calc_position(7, 4), calc_position(0, 2);
126    // measurment status
127    // up to 10 conversions numbered from 0 to 9
128    pub u8, gas_meas_index, _: calc_position(3, 0), calc_position(0, 0);
129    // true if measuring is not done yet
130    pub bool, measuring, _: calc_position(5, 0);
131    // true if gas measuring is not done yet
132    pub bool, gas_measuring, _: calc_position(6, 0);
133    // true if data is available
134    pub bool, new_data, _: calc_position(7, 0);
135}
136
137/// Temperature/Pressure adc values. 20 bits consisting of msb, lsb, xlsb
138#[derive(Debug)]
139pub struct Measurment(pub u32);
140impl From<u32> for Measurment {
141    fn from(value: u32) -> Self {
142        let measurment_value = u32::from_be(value) >> 12;
143        Measurment(measurment_value)
144    }
145}
146
147/// Humidity adc value. 16 bits
148#[derive(Debug)]
149pub struct Humidity(pub u16);
150impl From<u16> for Humidity {
151    fn from(value: u16) -> Self {
152        // switch bytes around
153        let humidity = u16::from_be(value);
154        Humidity(humidity)
155    }
156}
157
158/// gas adc value. 10 bits
159#[derive(Debug)]
160pub struct GasADC(pub u16);
161impl From<u16> for GasADC {
162    fn from(value: u16) -> Self {
163        // bit 7/6 from
164        let [gas_r_msb, gas_r_lsb] = value.to_le_bytes();
165        let mut gas_adc: u16 = gas_r_msb.into();
166        // make space for 2 low bits
167        gas_adc <<= 2;
168        gas_adc |= (gas_r_lsb >> 6) as u16;
169        GasADC(gas_adc)
170    }
171}
172
173// used to calculate the actual bit position in bitfields with multiple bytes
174fn calc_position(bit_position: usize, byte_offset: usize) -> usize {
175    byte_offset * 8 + bit_position
176}
177#[cfg(test)]
178mod tests {
179    extern crate std;
180    use crate::config::Configuration;
181    use std::println;
182
183    use super::{calc_position, Humidity, Measurment, RawConfig, RawData};
184    use bitfield::bitfield;
185
186    bitfield! {
187        pub struct SampleData([u8]);
188        impl Debug;
189        pub u32, from into Measurment, m, _: calc_position(7, 2), calc_position(0, 0);
190        pub u16, from into Humidity, h, _: calc_position(7,4), calc_position(0, 3);
191    }
192
193    #[test]
194    fn test_raw_data() {
195        let data = [
196            // new_data, gas_measuring, measuring, _, gas_meas_index
197            0b1_0_0_0_0000u8,
198            // placeholder
199            0,
200            // p_msb
201            0b00101001,
202            // p_lsb
203            0b10110011,
204            // p_xlsb
205            0b1111_0000,
206            // t_msb
207            0b00101001,
208            // t_lsb
209            0b10110011,
210            // t_xlsb
211            0b1111_0000,
212            // h_msb
213            0b10110011,
214            // h_lsb
215            0b00101001,
216            // 3 placeholders
217            0,
218            0,
219            0,
220            // gas_r_msb gas_adc<9:2>
221            0b10000001,
222            // gas_r_lsb gas_adc<1:0>, gas_valid, heater_stable, gas_range
223            0b11_1_1_0011,
224        ];
225        let expected_new_data = true;
226        let expected_gas_measuring = false;
227        let expected_measuring = false;
228        let expected_gas_meas_index = 0u8;
229        let expected_pressure = 0b0000_00000000_00101001_10110011_1111_u32;
230        let expected_temperature = 0b0000_00000000_00101001_10110011_1111_u32;
231        let expected_humidity = 0b10110011_00101001_u16;
232        let expected_gas_adc = 0b10000001_11u16;
233        let expected_gas_range = 0b011u8;
234        let expected_gas_valid = true;
235        let expected_heater_stable = true;
236        let raw_data = RawData(data);
237        assert!(raw_data.new_data() == expected_new_data);
238        assert!(raw_data.gas_measuring() == expected_gas_measuring);
239        assert!(raw_data.measuring() == expected_measuring);
240        assert!(raw_data.gas_meas_index() == expected_gas_meas_index);
241        assert!(raw_data.pressure_adc().0 == expected_pressure);
242        assert!(raw_data.temperature_adc().0 == expected_temperature);
243        assert!(raw_data.humidity_adc().0 == expected_humidity);
244        assert!(raw_data.gas_adc().0 == expected_gas_adc);
245        assert!(raw_data.gas_valid() == expected_gas_valid);
246        assert!(raw_data.heater_sable() == expected_heater_stable);
247        assert!(raw_data.gas_range() == expected_gas_range);
248    }
249
250    #[test]
251    fn test_measurment_and_humidty() {
252        // 2.5 bytes msb, lsb, xlsb 2 bytes msb, lsb
253        let data = [0b00101001, 0b10110011, 0b1111_0000, 0b10110011, 0b00101001];
254        let expected_measurment_value = 0b0000_00000000_00101001_10110011_1111_u32;
255        let expected_humidity = 0b10110011_00101001_u16;
256        let measurment = SampleData(data);
257        assert!(expected_measurment_value == measurment.m().0);
258        assert!(expected_humidity == measurment.h().0);
259    }
260    #[test]
261    fn test_assemble() {
262        let mut result = 0;
263        let xlsb: u32 = 0b1111_0000;
264        let lsb = 0b10110011;
265        let msb = 0b00101001;
266        let expected_value = 0b101001_10110011_1111;
267        result |= xlsb >> 4;
268        println!("Adding xlsb {xlsb:b} results in: {result:b}");
269        result |= lsb << 4;
270        println!("Adding lsb {lsb:b} results in: {result:b}");
271        result |= msb << 12;
272        println!("Adding msb {msb:b} results in: {result:b}");
273        assert!(result == expected_value);
274    }
275    #[test]
276    fn test_raw_config() {
277        let mut raw_config = RawConfig([0u8; 5]);
278        let default_user_config = Configuration::default();
279        raw_config.apply_config(&default_user_config);
280        let raw_data = raw_config.0;
281        let expected_raw_data = [
282            // 0x71 run_gas/nb_conv
283            0b_000_1_0000u8,
284            // 0x72 humidity oversampling by 2
285            0b_0_0_000_001,
286            // 0x73 placeholder
287            0b0,
288            // 0x74 temperature oversampling by 16 / pressure oversampling by 1 / mode sleep
289            0b_010_101_00,
290            // 0x75 filter coeff 1
291            0b000_001_00,
292        ];
293        println!("Expeced data: {expected_raw_data:?}");
294        println!("Actual raw data: {raw_data:?}");
295        assert!(expected_raw_data == raw_data);
296    }
297}