bme680/
lib.rs

1//! This crate is a pure Rust implementation for the BME680 environmental sensor.
2//! The library can be used to read the gas, pressure, humidity and temperature sensors via I²C.
3//!
4//! The library uses the embedded-hal crate to abstract reading and writing via I²C.
5//! In the examples you can find a demo how to use the library in Linux using the linux-embedded-hal crate (e.g. on a RPI).
6//! ```no_run
7
8//! extern crate bme680;
9//! extern crate embedded_hal;
10//! // Note that you'll have to import your board crates types corresponding to
11//! // Delay and I2cdev.
12//!
13//! use bme680::*;
14//! use embedded_hal::blocking::i2c;
15//! use hal::*;
16//! use std::result;
17//! use std::time::Duration;
18//! use std::thread::sleep;
19//!
20//! # mod hal {
21//! #   use super::*;
22//! #   use embedded_hal::blocking::delay;
23//! #
24//! #   #[derive(Debug)]
25//! #   pub struct Delay {}
26//! #
27//! #   impl delay::DelayMs<u8> for Delay {
28//! #       fn delay_ms(&mut self, _ms: u8) {}
29//! #   }
30//! #
31//! #   #[derive(Debug)]
32//! #   pub enum I2CError {}
33//! #
34//! #   pub struct I2cdev {}
35//! #
36//! #   impl i2c::Write for I2cdev {
37//! #       type Error = I2CError;
38//! #
39//! #       fn write<'w>(&mut self, addr: u8, bytes: &'w [u8]) -> result::Result<(), Self::Error> {
40//! #           Ok(())
41//! #       }
42//! #   }
43//! #
44//! #   impl i2c::Read for I2cdev {
45//! #       type Error = I2CError;
46//! #
47//! #       fn read<'w>(&mut self, addr: u8, bytes: &'w mut [u8]) -> result::Result<(), Self::Error> {
48//! #           Ok(())
49//! #       }
50//! #   }
51//! # }
52//!
53//! fn main() -> result::Result<(), Error<<hal::I2cdev as i2c::Read>::Error, <hal::I2cdev as i2c::Write>::Error>>
54//! {
55//!     // Initialize device
56//!     let i2c = I2cdev {};        // Your I2C device construction will look different, perhaps using I2cdev::new(..)
57//!     let mut delayer = Delay {}; // Your Delay construction will look different, perhaps using Delay::new(..)
58//!     let mut dev = Bme680::init(i2c, &mut delayer, I2CAddress::Primary)?;
59//!     let settings = SettingsBuilder::new()
60//!         .with_humidity_oversampling(OversamplingSetting::OS2x)
61//!         .with_pressure_oversampling(OversamplingSetting::OS4x)
62//!         .with_temperature_oversampling(OversamplingSetting::OS8x)
63//!         .with_temperature_filter(IIRFilterSize::Size3)
64//!         .with_gas_measurement(Duration::from_millis(1500), 320, 25)
65//!         .with_run_gas(true)
66//!         .build();
67//!     dev.set_sensor_settings(&mut delayer, settings)?;
68//!     let profile_duration = dev.get_profile_dur(&settings.0)?;
69//!
70//!     // Read sensor data
71//!     dev.set_sensor_mode(&mut delayer, PowerMode::ForcedMode)?;
72//!     sleep(profile_duration);
73//!     let (data, _state) = dev.get_sensor_data(&mut delayer)?;
74//!
75//!     println!("Temperature {}°C", data.temperature_celsius());
76//!     println!("Pressure {}hPa", data.pressure_hpa());
77//!     println!("Humidity {}%", data.humidity_percent());
78//!     println!("Gas Resistence {}Ω", data.gas_resistance_ohm());
79//!
80//!     Ok(())
81//! }
82//! ```
83
84#![no_std]
85#![forbid(unsafe_code)]
86
87pub use self::settings::{
88    DesiredSensorSettings, GasSett, IIRFilterSize, OversamplingSetting, SensorSettings, Settings,
89    SettingsBuilder, TphSett,
90};
91
92mod calc;
93mod settings;
94
95use crate::calc::Calc;
96use crate::hal::blocking::delay::DelayMs;
97use crate::hal::blocking::i2c::{Read, Write};
98
99use core::time::Duration;
100use core::{marker::PhantomData, result};
101use embedded_hal as hal;
102use log::{debug, error, info};
103
104/// BME680 General config
105pub const BME680_POLL_PERIOD_MS: u8 = 10;
106
107/// BME680 unique chip identifier
108pub const BME680_CHIP_ID: u8 = 0x61;
109
110/// BME680 field_x related defines
111const BME680_FIELD_LENGTH: usize = 15;
112
113/// BME680 coefficients related defines
114const BME680_COEFF_ADDR1_LEN: usize = 25;
115const BME680_COEFF_ADDR2_LEN: usize = 16;
116
117const BME680_SOFT_RESET_CMD: u8 = 0xb6;
118
119/// Register map
120/// Other coefficient's address
121const BME680_ADDR_RES_HEAT_VAL_ADDR: u8 = 0x00;
122const BME680_ADDR_RES_HEAT_RANGE_ADDR: u8 = 0x02;
123const BME680_ADDR_RANGE_SW_ERR_ADDR: u8 = 0x04;
124const BME680_ADDR_SENS_CONF_START: u8 = 0x5A;
125const BME680_ADDR_GAS_CONF_START: u8 = 0x64;
126
127const BME680_SOFT_RESET_ADDR: u8 = 0xe0;
128
129/// Field settings
130const BME680_FIELD0_ADDR: u8 = 0x1d;
131
132/// Heater settings
133const BME680_RES_HEAT0_ADDR: u8 = 0x5a;
134const BME680_GAS_WAIT0_ADDR: u8 = 0x64;
135
136/// Sensor configuration registers
137const BME680_CONF_HEAT_CTRL_ADDR: u8 = 0x70;
138const BME680_CONF_ODR_RUN_GAS_NBC_ADDR: u8 = 0x71;
139const BME680_CONF_OS_H_ADDR: u8 = 0x72;
140const BME680_CONF_T_P_MODE_ADDR: u8 = 0x74;
141const BME680_CONF_ODR_FILT_ADDR: u8 = 0x75;
142
143/// Coefficient's address
144const BME680_COEFF_ADDR1: u8 = 0x89;
145const BME680_COEFF_ADDR2: u8 = 0xe1;
146
147/// Chip identifier
148const BME680_CHIP_ID_ADDR: u8 = 0xd0;
149
150const BME680_SLEEP_MODE: u8 = 0;
151const BME680_FORCED_MODE: u8 = 1;
152
153const BME680_RESET_PERIOD: u8 = 10;
154
155const BME680_MODE_MSK: u8 = 0x03;
156const BME680_RSERROR_MSK: u8 = 0xf0;
157const BME680_NEW_DATA_MSK: u8 = 0x80;
158const BME680_GAS_INDEX_MSK: u8 = 0x0f;
159const BME680_GAS_RANGE_MSK: u8 = 0x0f;
160const BME680_GASM_VALID_MSK: u8 = 0x20;
161const BME680_HEAT_STAB_MSK: u8 = 0x10;
162
163/// Buffer length macro declaration
164const BME680_TMP_BUFFER_LENGTH: usize = 40;
165const BME680_REG_BUFFER_LENGTH: usize = 6;
166
167/// All possible errors in this crate
168#[derive(Debug)]
169pub enum Error<R, W> {
170    ///
171    /// aka BME680_E_COM_FAIL
172    ///
173    I2CWrite(W),
174    I2CRead(R),
175    ///
176    /// aka BME680_E_DEV_NOT_FOUND
177    ///
178    DeviceNotFound,
179    ///
180    /// aka BME680_E_INVALID_LENGTH
181    ///
182    InvalidLength,
183    ///
184    /// Warning aka BME680_W_DEFINE_PWR_MODE
185    ///
186    DefinePwrMode,
187    ///
188    /// Warning aka BME680_W_DEFINE_PWR_MODE
189    ///
190    NoNewData,
191    ///
192    /// Warning Boundary Check
193    ///
194    BoundaryCheckFailure(&'static str),
195}
196
197/// Abbreviates `std::result::Result` type
198pub type Result<T, R, W> = result::Result<T, Error<R, W>>;
199
200///
201/// Power mode settings
202///
203#[derive(Debug, PartialEq, Clone, Copy)]
204pub enum PowerMode {
205    SleepMode,
206    ForcedMode,
207}
208
209impl PowerMode {
210    // TODO replace with TryFrom once stabilized
211    fn from(power_mode: u8) -> Self {
212        match power_mode {
213            BME680_SLEEP_MODE => PowerMode::SleepMode,
214            BME680_FORCED_MODE => PowerMode::ForcedMode,
215            _ => panic!("Unknown power mode: {}", power_mode),
216        }
217    }
218
219    fn value(&self) -> u8 {
220        match self {
221            PowerMode::SleepMode => BME680_SLEEP_MODE,
222            PowerMode::ForcedMode => BME680_FORCED_MODE,
223        }
224    }
225}
226
227///
228/// I2C Slave Address
229/// To determine the slave address of your device you can use `i2cdetect -y 1` on linux.
230/// The 7-bit device address is 111011x. The 6 MSB bits are fixed.
231/// The last bit is changeable by SDO value and can be changed during operation.
232/// Connecting SDO to GND results in slave address 1110110 (0x76); connecting it to V DDIO results in slave
233/// address 1110111 (0x77), which is the same as BMP280’s I2C address.
234///
235#[derive(Debug, Clone, Copy)]
236pub enum I2CAddress {
237    /// Primary Slave Address 0x76
238    Primary,
239    /// Secondary Slave Address 0x77
240    Secondary,
241    /// Alternative address
242    Other(u8),
243}
244
245impl I2CAddress {
246    pub fn addr(&self) -> u8 {
247        match &self {
248            I2CAddress::Primary => 0x76u8,
249            I2CAddress::Secondary => 0x77u8,
250            I2CAddress::Other(addr) => *addr,
251        }
252    }
253}
254
255impl Default for I2CAddress {
256    fn default() -> I2CAddress {
257        I2CAddress::Primary
258    }
259}
260
261/// Calibration data used during initalization
262#[derive(Debug, Default, Copy)]
263#[repr(C)]
264pub struct CalibData {
265    pub par_h1: u16,
266    pub par_h2: u16,
267    pub par_h3: i8,
268    pub par_h4: i8,
269    pub par_h5: i8,
270    pub par_h6: u8,
271    pub par_h7: i8,
272    pub par_gh1: i8,
273    pub par_gh2: i16,
274    pub par_gh3: i8,
275    pub par_t1: u16,
276    pub par_t2: i16,
277    pub par_t3: i8,
278    pub par_p1: u16,
279    pub par_p2: i16,
280    pub par_p3: i8,
281    pub par_p4: i16,
282    pub par_p5: i16,
283    pub par_p6: i8,
284    pub par_p7: i8,
285    pub par_p8: i16,
286    pub par_p9: i16,
287    pub par_p10: u8,
288    pub res_heat_range: u8,
289    pub res_heat_val: i8,
290    pub range_sw_err: u8,
291}
292
293impl Clone for CalibData {
294    fn clone(&self) -> Self {
295        *self
296    }
297}
298
299/// Contains read sensors values  e.g. temperature, pressure, humidity etc.
300#[derive(Debug, Default, Copy)]
301#[repr(C)]
302pub struct FieldData {
303    /// Contains new_data, gasm_valid & heat_stab
304    status: u8,
305    /// Index of heater profile used
306    gas_index: u8,
307    /// Measurement index
308    meas_index: u8,
309    temperature: i16,
310    pressure: u32,
311    humidity: u32,
312    gas_resistance: u32,
313}
314
315impl Clone for FieldData {
316    fn clone(&self) -> Self {
317        *self
318    }
319}
320
321impl FieldData {
322    /// Temperature in degree celsius (°C)
323    pub fn temperature_celsius(&self) -> f32 {
324        self.temperature as f32 / 100f32
325    }
326
327    /// Pressure in hectopascal (hPA)
328    pub fn pressure_hpa(&self) -> f32 {
329        self.pressure as f32 / 100f32
330    }
331
332    /// Humidity in % relative humidity
333    pub fn humidity_percent(&self) -> f32 {
334        self.humidity as f32 / 1000f32
335    }
336
337    pub fn gas_resistance_ohm(&self) -> u32 {
338        self.gas_resistance
339    }
340
341    /// Whether a real (and not a dummy) gas reading was performed.
342    pub fn gas_valid(&self) -> bool {
343        self.status & BME680_GASM_VALID_MSK != 0
344    }
345
346    /// Whether the heater target temperature for the gas reading was reached.
347    ///
348    /// If this values is `false`, the heating duration was likely too short or
349    /// the target temperature too high.
350    pub fn heat_stable(&self) -> bool {
351        self.status & BME680_HEAT_STAB_MSK != 0
352    }
353}
354
355/// Shows if new data is available
356#[derive(PartialEq, Debug)]
357pub enum FieldDataCondition {
358    ///
359    /// Data changed since last read
360    ///
361    NewData,
362    ///
363    /// Data has not changed since last read
364    ///
365    Unchanged,
366}
367
368struct I2CUtil {}
369
370impl I2CUtil {
371    pub fn read_byte<I2C>(
372        i2c: &mut I2C,
373        dev_id: u8,
374        reg_addr: u8,
375    ) -> Result<u8, <I2C as Read>::Error, <I2C as Write>::Error>
376    where
377        I2C: Read + Write,
378    {
379        let mut buf = [0; 1];
380
381        i2c.write(dev_id, &[reg_addr]).map_err(Error::I2CWrite)?;
382
383        match i2c.read(dev_id, &mut buf) {
384            Ok(()) => Ok(buf[0]),
385            Err(e) => Err(Error::I2CRead(e)),
386        }
387    }
388
389    pub fn read_bytes<I2C>(
390        i2c: &mut I2C,
391        dev_id: u8,
392        reg_addr: u8,
393        buf: &mut [u8],
394    ) -> Result<(), <I2C as Read>::Error, <I2C as Write>::Error>
395    where
396        I2C: Read + Write,
397    {
398        i2c.write(dev_id, &[reg_addr]).map_err(Error::I2CWrite)?;
399
400        match i2c.read(dev_id, buf) {
401            Ok(()) => Ok(()),
402            Err(e) => Err(Error::I2CRead(e)),
403        }
404    }
405}
406
407/// Driver for the BME680 environmental sensor
408#[repr(C)]
409pub struct Bme680<I2C, D> {
410    i2c: I2C,
411    delay: PhantomData<D>,
412    dev_id: I2CAddress,
413    calib: CalibData,
414    // TODO remove ? as it may not reflect the state of the device
415    tph_sett: TphSett,
416    // TODO remove ? as it may not reflect the state of the device
417    gas_sett: GasSett,
418    // TODO remove ? as it may not reflect the state of the device
419    power_mode: PowerMode,
420}
421
422fn boundary_check<I2C>(
423    value: Option<u8>,
424    value_name: &'static str,
425    min: u8,
426    max: u8,
427) -> Result<u8, <I2C as Read>::Error, <I2C as Write>::Error>
428where
429    I2C: Read + Write,
430{
431    let value = value.ok_or(Error::BoundaryCheckFailure(value_name))?;
432
433    if value < min {
434        const MIN: &str = "Boundary check failure, value exceeds maximum";
435        error!("{}, value name: {}", MIN, value_name);
436        return Err(Error::BoundaryCheckFailure(MIN));
437    }
438
439    if value > max {
440        const MAX: &str = "Boundary check, value exceeds minimum";
441        error!("{}, value name: {}", MAX, value_name);
442        return Err(Error::BoundaryCheckFailure(MAX));
443    }
444    Ok(value)
445}
446
447impl<I2C, D> Bme680<I2C, D>
448where
449    D: DelayMs<u8>,
450    I2C: Read + Write,
451{
452    pub fn soft_reset(
453        i2c: &mut I2C,
454        delay: &mut D,
455        dev_id: I2CAddress,
456    ) -> Result<(), <I2C as Read>::Error, <I2C as Write>::Error> {
457        let tmp_buff: [u8; 2] = [BME680_SOFT_RESET_ADDR, BME680_SOFT_RESET_CMD];
458
459        i2c.write(dev_id.addr(), &tmp_buff)
460            .map_err(Error::I2CWrite)?;
461
462        delay.delay_ms(BME680_RESET_PERIOD);
463        Ok(())
464    }
465
466    pub fn init(
467        mut i2c: I2C,
468        delay: &mut D,
469        dev_id: I2CAddress,
470    ) -> Result<Bme680<I2C, D>, <I2C as Read>::Error, <I2C as Write>::Error> {
471        Bme680::soft_reset(&mut i2c, delay, dev_id)?;
472
473        debug!("Reading chip id");
474        /* Soft reset to restore it to default values*/
475        let chip_id = I2CUtil::read_byte::<I2C>(&mut i2c, dev_id.addr(), BME680_CHIP_ID_ADDR)?;
476        debug!("Chip id: {}", chip_id);
477
478        if chip_id == BME680_CHIP_ID {
479            debug!("Reading calib data");
480            let calib = Bme680::<I2C, D>::get_calib_data::<I2C>(&mut i2c, dev_id)?;
481            debug!("Calib data {:?}", calib);
482            let dev = Bme680 {
483                i2c,
484                delay: PhantomData,
485                dev_id,
486                calib,
487                power_mode: PowerMode::ForcedMode,
488                tph_sett: Default::default(),
489                gas_sett: Default::default(),
490            };
491            info!("Finished device init");
492            Ok(dev)
493        } else {
494            error!("Device does not match chip id {}", BME680_CHIP_ID);
495            Err(Error::DeviceNotFound)
496        }
497    }
498
499    fn bme680_set_regs(
500        &mut self,
501        reg: &[(u8, u8)],
502    ) -> Result<(), <I2C as Read>::Error, <I2C as Write>::Error> {
503        if reg.is_empty() || reg.len() > (BME680_TMP_BUFFER_LENGTH / 2) as usize {
504            return Err(Error::InvalidLength);
505        }
506
507        for (reg_addr, reg_data) in reg {
508            let tmp_buff: [u8; 2] = [*reg_addr, *reg_data];
509            debug!(
510                "Setting register reg: {:?} tmp_buf: {:?}",
511                reg_addr, tmp_buff
512            );
513            self.i2c
514                .write(self.dev_id.addr(), &tmp_buff)
515                .map_err(Error::I2CWrite)?;
516        }
517
518        Ok(())
519    }
520
521    /// Set the settings to be used during the sensor measurements
522    pub fn set_sensor_settings(
523        &mut self,
524        delay: &mut D,
525        settings: Settings,
526    ) -> Result<(), <I2C as Read>::Error, <I2C as Write>::Error> {
527        let (sensor_settings, desired_settings) = settings;
528        let tph_sett = sensor_settings.tph_sett;
529        let gas_sett = sensor_settings.gas_sett;
530
531        let mut reg: [(u8, u8); BME680_REG_BUFFER_LENGTH] = [(0, 0); BME680_REG_BUFFER_LENGTH];
532        let intended_power_mode = self.power_mode;
533
534        if desired_settings.contains(DesiredSensorSettings::GAS_MEAS_SEL) {
535            debug!("GAS_MEAS_SEL: true");
536            self.set_gas_config(gas_sett)?;
537        }
538
539        let power_mode = self.power_mode;
540        self.set_sensor_mode(delay, power_mode)?;
541
542        let mut element_index = 0;
543        // Selecting the filter
544        if desired_settings.contains(DesiredSensorSettings::FILTER_SEL) {
545            let mut data =
546                I2CUtil::read_byte(&mut self.i2c, self.dev_id.addr(), BME680_CONF_ODR_FILT_ADDR)?;
547
548            debug!("FILTER_SEL: true");
549            data = (data as i32 & !0x1ci32
550                | (tph_sett.filter.unwrap_or(IIRFilterSize::Size0) as i32) << 2i32 & 0x1ci32)
551                as u8;
552            reg[element_index] = (BME680_CONF_ODR_FILT_ADDR, data);
553            element_index += 1;
554        }
555
556        if desired_settings.contains(DesiredSensorSettings::HCNTRL_SEL) {
557            debug!("HCNTRL_SEL: true");
558            let gas_sett_heatr_ctrl =
559                boundary_check::<I2C>(gas_sett.heatr_ctrl, "GasSett.heatr_ctrl", 0x0u8, 0x8u8)?;
560            let mut data = I2CUtil::read_byte(
561                &mut self.i2c,
562                self.dev_id.addr(),
563                BME680_CONF_HEAT_CTRL_ADDR,
564            )?;
565            data = (data as i32 & !0x8i32 | gas_sett_heatr_ctrl as i32 & 0x8) as u8;
566            reg[element_index] = (BME680_CONF_HEAT_CTRL_ADDR, data);
567            element_index += 1;
568        }
569
570        // Selecting heater T,P oversampling for the sensor
571        if desired_settings
572            .contains(DesiredSensorSettings::OST_SEL | DesiredSensorSettings::OSP_SEL)
573        {
574            let mut data =
575                I2CUtil::read_byte(&mut self.i2c, self.dev_id.addr(), BME680_CONF_T_P_MODE_ADDR)?;
576
577            if desired_settings.contains(DesiredSensorSettings::OST_SEL) {
578                debug!("OST_SEL: true");
579                let tph_sett_os_temp = boundary_check::<I2C>(
580                    tph_sett.os_temp.map(|x| x as u8),
581                    "TphSett.os_temp",
582                    0,
583                    5,
584                )?;
585                data = (data as i32 & !0xe0i32 | (tph_sett_os_temp as i32) << 5i32 & 0xe0i32) as u8;
586            }
587
588            if desired_settings.contains(DesiredSensorSettings::OSP_SEL) {
589                debug!("OSP_SEL: true");
590                let tph_sett_os_pres = tph_sett.os_temp.expect("OS TEMP");
591                data = (data as i32 & !0x1ci32 | (tph_sett_os_pres as i32) << 2i32 & 0x1ci32) as u8;
592            }
593            reg[element_index] = (BME680_CONF_T_P_MODE_ADDR, data);
594            element_index += 1;
595        }
596
597        // Selecting humidity oversampling for the sensor
598        if desired_settings.contains(DesiredSensorSettings::OSH_SEL) {
599            debug!("OSH_SEL: true");
600            let tph_sett_os_hum =
601                boundary_check::<I2C>(tph_sett.os_hum.map(|x| x as u8), "TphSett.os_hum", 0, 5)?;
602            let mut data =
603                I2CUtil::read_byte(&mut self.i2c, self.dev_id.addr(), BME680_CONF_OS_H_ADDR)?;
604            data = (data as i32 & !0x7i32 | tph_sett_os_hum as i32 & 0x7i32) as u8;
605            reg[element_index] = (BME680_CONF_OS_H_ADDR, data);
606            element_index += 1;
607        }
608
609        // Selecting the runGas and NB conversion settings for the sensor
610        if desired_settings
611            .contains(DesiredSensorSettings::RUN_GAS_SEL | DesiredSensorSettings::NBCONV_SEL)
612        {
613            let mut data = I2CUtil::read_byte(
614                &mut self.i2c,
615                self.dev_id.addr(),
616                BME680_CONF_ODR_RUN_GAS_NBC_ADDR,
617            )?;
618
619            if desired_settings.contains(DesiredSensorSettings::RUN_GAS_SEL) {
620                debug!("RUN_GAS_SEL: true");
621                data = (data as i32 & !0x10i32
622                    | (gas_sett.run_gas_measurement as i32) << 4i32 & 0x10i32)
623                    as u8;
624            }
625
626            if desired_settings.contains(DesiredSensorSettings::NBCONV_SEL) {
627                debug!("NBCONV_SEL: true");
628                let gas_sett_nb_conv =
629                    boundary_check::<I2C>(Some(gas_sett.nb_conv), "GasSett.nb_conv", 0, 10)?;
630                data = (data as i32 & !0xfi32 | gas_sett_nb_conv as i32 & 0xfi32) as u8;
631            }
632
633            reg[element_index] = (BME680_CONF_ODR_RUN_GAS_NBC_ADDR, data);
634            element_index += 1;
635        }
636
637        self.bme680_set_regs(&reg[0..element_index])?;
638
639        // Restore previous intended power mode
640        self.power_mode = intended_power_mode;
641        self.tph_sett = tph_sett;
642        Ok(())
643    }
644
645    /// Retrieve settings from sensor registers
646    ///
647    /// # Arguments
648    ///
649    /// * `desired_settings` - Settings to be retrieved. Setting values may stay `None` if not retrieved.
650    pub fn get_sensor_settings(
651        &mut self,
652        desired_settings: DesiredSensorSettings,
653    ) -> Result<SensorSettings, <I2C as Read>::Error, <I2C as Write>::Error> {
654        let reg_addr: u8 = 0x70u8;
655        let mut data_array: [u8; BME680_REG_BUFFER_LENGTH] = [0; BME680_REG_BUFFER_LENGTH];
656        let mut sensor_settings: SensorSettings = Default::default();
657        sensor_settings.tph_sett.temperature_offset = self.tph_sett.temperature_offset;
658
659        I2CUtil::read_bytes(&mut self.i2c, self.dev_id.addr(), reg_addr, &mut data_array)?;
660
661        if desired_settings.contains(DesiredSensorSettings::GAS_MEAS_SEL) {
662            sensor_settings.gas_sett = self.get_gas_config()?;
663        }
664
665        if desired_settings.contains(DesiredSensorSettings::FILTER_SEL) {
666            sensor_settings.tph_sett.filter = Some(IIRFilterSize::from_u8(
667                ((data_array[5usize] as i32 & 0x1ci32) >> 2i32) as u8,
668            ));
669        }
670
671        if desired_settings
672            .contains(DesiredSensorSettings::OST_SEL | DesiredSensorSettings::OSP_SEL)
673        {
674            let os_temp: u8 = ((data_array[4usize] as i32 & 0xe0i32) >> 5i32) as u8;
675            let os_pres: u8 = ((data_array[4usize] as i32 & 0x1ci32) >> 2i32) as u8;
676            sensor_settings.tph_sett.os_temp = Some(OversamplingSetting::from_u8(os_temp));
677            sensor_settings.tph_sett.os_pres = Some(OversamplingSetting::from_u8(os_pres));
678        }
679
680        if desired_settings.contains(DesiredSensorSettings::OSH_SEL) {
681            let os_hum: u8 = (data_array[2usize] as i32 & 0x7i32) as u8;
682            sensor_settings.tph_sett.os_hum = Some(OversamplingSetting::from_u8(os_hum));
683        }
684
685        if desired_settings.contains(DesiredSensorSettings::HCNTRL_SEL) {
686            sensor_settings.gas_sett.heatr_ctrl = Some((data_array[0usize] as i32 & 0x8i32) as u8);
687        }
688
689        if desired_settings
690            .contains(DesiredSensorSettings::RUN_GAS_SEL | DesiredSensorSettings::NBCONV_SEL)
691        {
692            sensor_settings.gas_sett.nb_conv = (data_array[1usize] as i32 & 0xfi32) as u8;
693            sensor_settings.gas_sett.run_gas_measurement =
694                ((data_array[1usize] as i32 & 0x10i32) >> 4i32) == 0;
695        }
696
697        Ok(sensor_settings)
698    }
699
700    /// Set the sensor into a certain power mode
701    ///
702    /// # Arguments
703    ///
704    /// * `target_power_mode` - Desired target power mode
705    pub fn set_sensor_mode(
706        &mut self,
707        delay: &mut D,
708        target_power_mode: PowerMode,
709    ) -> Result<(), <I2C as Read>::Error, <I2C as Write>::Error> {
710        let mut tmp_pow_mode: u8;
711        let mut current_power_mode: PowerMode;
712
713        // Call repeatedly until in sleep
714        loop {
715            tmp_pow_mode =
716                I2CUtil::read_byte(&mut self.i2c, self.dev_id.addr(), BME680_CONF_T_P_MODE_ADDR)?;
717
718            // Put to sleep before changing mode
719            current_power_mode = PowerMode::from(tmp_pow_mode & BME680_MODE_MSK);
720
721            debug!("Current power mode: {:?}", current_power_mode);
722
723            if current_power_mode != PowerMode::SleepMode {
724                // Set to sleep
725                tmp_pow_mode &= !BME680_MODE_MSK;
726                debug!("Setting to sleep tmp_pow_mode: {}", tmp_pow_mode);
727                self.bme680_set_regs(&[(BME680_CONF_T_P_MODE_ADDR, tmp_pow_mode)])?;
728                delay.delay_ms(BME680_POLL_PERIOD_MS);
729            } else {
730                // TODO do while in Rust?
731                break;
732            }
733        }
734
735        // Already in sleep
736        if target_power_mode != PowerMode::SleepMode {
737            tmp_pow_mode = tmp_pow_mode & !BME680_MODE_MSK | target_power_mode.value();
738            debug!("Already in sleep Target power mode: {}", tmp_pow_mode);
739            self.bme680_set_regs(&[(BME680_CONF_T_P_MODE_ADDR, tmp_pow_mode)])?;
740        }
741        Ok(())
742    }
743
744    /// Retrieve current sensor power mode via registers
745    pub fn get_sensor_mode(
746        &mut self,
747    ) -> Result<PowerMode, <I2C as Read>::Error, <I2C as Write>::Error> {
748        let regs =
749            I2CUtil::read_byte(&mut self.i2c, self.dev_id.addr(), BME680_CONF_T_P_MODE_ADDR)?;
750        let mode = regs & BME680_MODE_MSK;
751        Ok(PowerMode::from(mode))
752    }
753
754    pub fn bme680_set_profile_dur(&mut self, tph_sett: TphSett, duration: Duration) {
755        let os_to_meas_cycles: [u8; 6] = [0u8, 1u8, 2u8, 4u8, 8u8, 16u8];
756        // TODO check if the following unwrap_ors do not change behaviour
757        // TODO replace once https://github.com/rust-lang/rust/pull/50167 has been merged
758        const MILLIS_PER_SEC: u64 = 1_000;
759        const NANOS_PER_MILLI: u64 = 1_000_000;
760        let millis = (duration.as_secs() as u64 * MILLIS_PER_SEC)
761            + (duration.subsec_nanos() as u64 / NANOS_PER_MILLI);
762
763        let mut meas_cycles = os_to_meas_cycles
764            [tph_sett.os_temp.unwrap_or(OversamplingSetting::OSNone) as usize]
765            as u64;
766        meas_cycles = meas_cycles.wrapping_add(
767            os_to_meas_cycles[tph_sett.os_pres.unwrap_or(OversamplingSetting::OSNone) as usize]
768                as u64,
769        );
770        meas_cycles = meas_cycles.wrapping_add(
771            os_to_meas_cycles[tph_sett.os_hum.unwrap_or(OversamplingSetting::OSNone) as usize]
772                as u64,
773        );
774        let mut tph_dur = meas_cycles.wrapping_mul(1963u64);
775        tph_dur = tph_dur.wrapping_add(477u64.wrapping_mul(4u64));
776        tph_dur = tph_dur.wrapping_add(477u64.wrapping_mul(5u64));
777        tph_dur = tph_dur.wrapping_add(500u64);
778        tph_dur = tph_dur.wrapping_div(1000u64);
779        tph_dur = tph_dur.wrapping_add(1u64);
780        self.gas_sett.heatr_dur = Some(Duration::from_millis(millis - tph_dur));
781    }
782
783    pub fn get_profile_dur(
784        &self,
785        sensor_settings: &SensorSettings,
786    ) -> Result<Duration, <I2C as Read>::Error, <I2C as Write>::Error> {
787        let os_to_meas_cycles: [u8; 6] = [0u8, 1u8, 2u8, 4u8, 8u8, 16u8];
788        // TODO check if the following unwrap_ors do not change behaviour
789        let mut meas_cycles = os_to_meas_cycles[sensor_settings
790            .tph_sett
791            .os_temp
792            .unwrap_or(OversamplingSetting::OSNone)
793            as usize] as u32;
794        meas_cycles = meas_cycles.wrapping_add(
795            os_to_meas_cycles[sensor_settings
796                .tph_sett
797                .os_pres
798                .unwrap_or(OversamplingSetting::OSNone) as usize] as u32,
799        );
800        meas_cycles = meas_cycles.wrapping_add(
801            os_to_meas_cycles[sensor_settings
802                .tph_sett
803                .os_hum
804                .unwrap_or(OversamplingSetting::OSNone) as usize] as u32,
805        );
806        let mut tph_dur = meas_cycles.wrapping_mul(1963u32);
807        tph_dur = tph_dur.wrapping_add(477u32.wrapping_mul(4u32));
808        tph_dur = tph_dur.wrapping_add(477u32.wrapping_mul(5u32));
809        tph_dur = tph_dur.wrapping_add(500u32);
810        tph_dur = tph_dur.wrapping_div(1000u32);
811        tph_dur = tph_dur.wrapping_add(1u32);
812        let mut duration = Duration::from_millis(tph_dur as u64);
813        if sensor_settings.gas_sett.run_gas_measurement {
814            duration += sensor_settings.gas_sett.heatr_dur.expect("Heatrdur");
815        }
816        Ok(duration)
817    }
818
819    fn get_calib_data<I2CX>(
820        i2c: &mut I2CX,
821        dev_id: I2CAddress,
822    ) -> Result<CalibData, <I2CX as Read>::Error, <I2CX as Write>::Error>
823    where
824        I2CX: Read + Write,
825    {
826        let mut calib: CalibData = Default::default();
827
828        let mut coeff_array: [u8; BME680_COEFF_ADDR1_LEN + BME680_COEFF_ADDR2_LEN] =
829            [0; BME680_COEFF_ADDR1_LEN + BME680_COEFF_ADDR2_LEN];
830
831        I2CUtil::read_bytes::<I2CX>(
832            i2c,
833            dev_id.addr(),
834            BME680_COEFF_ADDR1,
835            &mut coeff_array[0..(BME680_COEFF_ADDR1_LEN - 1)],
836        )?;
837
838        I2CUtil::read_bytes::<I2CX>(
839            i2c,
840            dev_id.addr(),
841            BME680_COEFF_ADDR2,
842            &mut coeff_array
843                [BME680_COEFF_ADDR1_LEN..(BME680_COEFF_ADDR1_LEN + BME680_COEFF_ADDR2_LEN - 1)],
844        )?;
845
846        calib.par_t1 = ((coeff_array[34usize] as i32) << 8i32 | coeff_array[33usize] as i32) as u16;
847        calib.par_t2 = ((coeff_array[2usize] as i32) << 8i32 | coeff_array[1usize] as i32) as i16;
848        calib.par_t3 = coeff_array[3usize] as i8;
849        calib.par_p1 = ((coeff_array[6usize] as i32) << 8i32 | coeff_array[5usize] as i32) as u16;
850        calib.par_p2 = ((coeff_array[8usize] as i32) << 8i32 | coeff_array[7usize] as i32) as i16;
851        calib.par_p3 = coeff_array[9usize] as i8;
852        calib.par_p4 = ((coeff_array[12usize] as i32) << 8i32 | coeff_array[11usize] as i32) as i16;
853        calib.par_p5 = ((coeff_array[14usize] as i32) << 8i32 | coeff_array[13usize] as i32) as i16;
854        calib.par_p6 = coeff_array[16usize] as i8;
855        calib.par_p7 = coeff_array[15usize] as i8;
856        calib.par_p8 = ((coeff_array[20usize] as i32) << 8i32 | coeff_array[19usize] as i32) as i16;
857        calib.par_p9 = ((coeff_array[22usize] as i32) << 8i32 | coeff_array[21usize] as i32) as i16;
858        calib.par_p10 = coeff_array[23usize];
859        calib.par_h1 =
860            ((coeff_array[27usize] as i32) << 4i32 | coeff_array[26usize] as i32 & 0xfi32) as u16;
861        calib.par_h2 =
862            ((coeff_array[25usize] as i32) << 4i32 | coeff_array[26usize] as i32 >> 4i32) as u16;
863        calib.par_h3 = coeff_array[28usize] as i8;
864        calib.par_h4 = coeff_array[29usize] as i8;
865        calib.par_h5 = coeff_array[30usize] as i8;
866        calib.par_h6 = coeff_array[31usize];
867        calib.par_h7 = coeff_array[32usize] as i8;
868        calib.par_gh1 = coeff_array[37usize] as i8;
869        calib.par_gh2 =
870            ((coeff_array[36usize] as i32) << 8i32 | coeff_array[35usize] as i32) as i16;
871        calib.par_gh3 = coeff_array[38usize] as i8;
872
873        calib.res_heat_range =
874            (I2CUtil::read_byte::<I2CX>(i2c, dev_id.addr(), BME680_ADDR_RES_HEAT_RANGE_ADDR)?
875                & 0x30)
876                / 16;
877
878        calib.res_heat_val =
879            I2CUtil::read_byte::<I2CX>(i2c, dev_id.addr(), BME680_ADDR_RES_HEAT_VAL_ADDR)? as i8;
880
881        calib.range_sw_err =
882            (I2CUtil::read_byte::<I2CX>(i2c, dev_id.addr(), BME680_ADDR_RANGE_SW_ERR_ADDR)?
883                & BME680_RSERROR_MSK)
884                / 16;
885
886        Ok(calib)
887    }
888
889    fn set_gas_config(
890        &mut self,
891        gas_sett: GasSett,
892    ) -> Result<(), <I2C as Read>::Error, <I2C as Write>::Error> {
893        if self.power_mode != PowerMode::ForcedMode {
894            return Err(Error::DefinePwrMode);
895        }
896
897        // TODO check whether unwrap_or changes behaviour
898        let reg: [(u8, u8); 2] = [
899            (
900                BME680_RES_HEAT0_ADDR,
901                Calc::calc_heater_res(
902                    &self.calib,
903                    gas_sett.ambient_temperature,
904                    gas_sett.heatr_temp.unwrap_or(0),
905                ),
906            ),
907            (
908                BME680_GAS_WAIT0_ADDR,
909                Calc::calc_heater_dur(gas_sett.heatr_dur.unwrap_or_else(|| Duration::from_secs(0))),
910            ),
911        ];
912
913        self.gas_sett.nb_conv = 0;
914        self.bme680_set_regs(&reg)
915    }
916
917    fn get_gas_config(&mut self) -> Result<GasSett, <I2C as Read>::Error, <I2C as Write>::Error> {
918        let heatr_temp = Some(I2CUtil::read_byte(
919            &mut self.i2c,
920            self.dev_id.addr(),
921            BME680_ADDR_SENS_CONF_START,
922        )? as u16);
923
924        let heatr_dur_ms = I2CUtil::read_byte(
925            &mut self.i2c,
926            self.dev_id.addr(),
927            BME680_ADDR_GAS_CONF_START,
928        )? as u64;
929
930        let gas_sett = GasSett {
931            heatr_temp,
932            heatr_dur: Some(Duration::from_millis(heatr_dur_ms)),
933            ..Default::default()
934        };
935
936        Ok(gas_sett)
937    }
938
939    /// Retrieve the current sensor informations
940    pub fn get_sensor_data(
941        &mut self,
942        delay: &mut D,
943    ) -> Result<(FieldData, FieldDataCondition), <I2C as Read>::Error, <I2C as Write>::Error> {
944        let mut buff: [u8; BME680_FIELD_LENGTH] = [0; BME680_FIELD_LENGTH];
945
946        debug!("Buf {:?}, len: {}", buff, buff.len());
947        let mut data: FieldData = Default::default();
948
949        const TRIES: u8 = 10;
950        for _ in 0..TRIES {
951            I2CUtil::read_bytes(
952                &mut self.i2c,
953                self.dev_id.addr(),
954                BME680_FIELD0_ADDR,
955                &mut buff,
956            )?;
957
958            debug!("Field data read {:?}, len: {}", buff, buff.len());
959
960            data.status = buff[0] & BME680_NEW_DATA_MSK;
961            data.gas_index = buff[0] & BME680_GAS_INDEX_MSK;
962            data.meas_index = buff[1];
963
964            let adc_pres = (buff[2] as u32).wrapping_mul(4096)
965                | (buff[3] as u32).wrapping_mul(16)
966                | (buff[4] as u32).wrapping_div(16);
967            let adc_temp = (buff[5] as u32).wrapping_mul(4096)
968                | (buff[6] as u32).wrapping_mul(16)
969                | (buff[7] as u32).wrapping_div(16);
970            let adc_hum = ((buff[8] as u32).wrapping_mul(256) | buff[9] as u32) as u16;
971            let adc_gas_res =
972                ((buff[13] as u32).wrapping_mul(4) | (buff[14] as u32).wrapping_div(64)) as u16;
973            let gas_range = buff[14] & BME680_GAS_RANGE_MSK;
974
975            data.status |= buff[14] & BME680_GASM_VALID_MSK;
976            data.status |= buff[14] & BME680_HEAT_STAB_MSK;
977
978            if data.status & BME680_NEW_DATA_MSK != 0 {
979                let (temp, t_fine) =
980                    Calc::calc_temperature(&self.calib, adc_temp, self.tph_sett.temperature_offset);
981                debug!(
982                    "adc_temp: {} adc_pres: {} adc_hum: {} adc_gas_res: {}, t_fine: {}",
983                    adc_temp, adc_pres, adc_hum, adc_gas_res, t_fine
984                );
985                data.temperature = temp;
986                data.pressure = Calc::calc_pressure(&self.calib, t_fine, adc_pres);
987                data.humidity = Calc::calc_humidity(&self.calib, t_fine, adc_hum);
988                data.gas_resistance =
989                    Calc::calc_gas_resistance(&self.calib, adc_gas_res, gas_range);
990                return Ok((data, FieldDataCondition::NewData));
991            }
992
993            delay.delay_ms(BME680_POLL_PERIOD_MS);
994        }
995        Ok((data, FieldDataCondition::Unchanged))
996    }
997}