bme680_driver/
lib.rs

1#![no_main]
2#![no_std]
3
4//! # BME680 Environmental Sensor Driver
5//!
6//! A type-safe, `no_std` driver for the Bosch BME680 environmental sensor.
7//! This driver is designed for high-reliability embedded applications where
8//! floating-point arithmetic is either unavailable or too slow.
9//!
10//! ## Key Features
11//!
12//! - **Typestate Architecture**: The driver uses a compile-time state machine (`Uninitialized` -> `Ready`)
13//!   to ensure methods like `read_new_data` cannot be called before the sensor is properly set up.
14//! - **Fixed-Point Arithmetic**: All calculations (Temperature, Pressure, Humidity, Gas) are performed
15//!   using integer arithmetic. This ensures deterministic behavior and high performance on Cortex-M0/M3/M4 cores.
16//! - **Power Efficiency**: The driver supports granular control over which measurements are enabled
17//!   (e.g., disabling Gas or Pressure sensing to save energy).
18//!
19//! ## Data Units
20//!
21//! To avoid floats, this driver uses scaled integer units:
22//!
23//! - **Temperature**: Centigrade (`°C * 100`). Example: `2350` = 23.50 °C.
24//! - **Humidity**: Milli-percent (`%rH * 1000`). Example: `45123` = 45.123 %rH.
25//! - **Pressure**: Pascal (`Pa`). Example: `101325` = 1013.25 hPa.
26//! - **Gas Resistance**: Ohms (`Ω`). Higher values typically indicate cleaner air.
27
28mod calc;
29pub mod settings;
30
31use core::marker::PhantomData;
32use embedded_hal::{self, delay::DelayNs, i2c};
33
34pub use settings::{BME680Builder, Config, GasProfile, GasProfileIndex, IIRFilter, Oversampling};
35
36/// Internal register addresses for the BME680.
37///
38/// These addresses are derived from the Bosch BME680 Datasheet.
39pub(crate) mod regs {
40    pub const ADDR_RES_HEAT_VAL: u8 = 0x00;
41    pub const ADDR_RES_HEAT_RANGE: u8 = 0x02;
42    pub const ADDR_RANGE_SW_ERR: u8 = 0x04;
43    /// Status register containing the "New Data" bit (Bit 7) and "Measuring" bit (Bit 6).
44    pub const ADDR_EAS_STATUS_0: u8 = 0x1D;
45    /// Start of the measurement data registers (Pressure MSB).
46    pub const ADDR_PRESS_MSB: u8 = 0x1F;
47    pub const ADDR_RES_HEAT_0: u8 = 0x5A;
48    pub const ADDR_GAS_WAIT_0: u8 = 0x64;
49    /// Ctrl Gas 1: Controls RUN_GAS (Bit 4) and NB_CONV (Bits 0-3).
50    pub const ADDR_CTRL_GAS_1: u8 = 0x71;
51    /// Ctrl Hum: Controls Humidity oversampling (Bits 0-2).
52    pub const ADDR_CTRL_HUM: u8 = 0x72;
53    /// Ctrl Meas: Controls Temp/Pres oversampling and Mode selection (Sleep/Forced).
54    pub const ADDR_CTRL_MEAS: u8 = 0x74;
55    /// Config: IIR Filter settings and SPI 3-wire selection.
56    pub const ADDR_CONFIG: u8 = 0x75;
57    /// Start of the first calibration data block (0x89 - 0xA1).
58    pub const ADDR_CALIB_0: u8 = 0x89;
59    /// Chip ID register (should read 0x61).
60    pub const ADDR_ID: u8 = 0xD0;
61    /// Soft Reset register (write 0xB6 to reset).
62    pub const ADDR_RESET: u8 = 0xE0;
63    /// Start of the second calibration data block (0xE1 - 0xF0).
64    pub const ADDR_CALIB_1: u8 = 0xE1;
65}
66
67/// Sizes of various data blocks in memory for burst reads.
68mod reg_sizes {
69    pub const SIZE_CALIB_0: usize = 25;
70    pub const SIZE_CALIB_1: usize = 16;
71    pub const SIZE_CALIB_TOTAL: usize = SIZE_CALIB_0 + SIZE_CALIB_1;
72    pub const SIZE_RAW_DATA: usize = 13;
73}
74
75/// Bit masks for register configuration and bitwise operations.
76mod msks {
77    /// Command to trigger a soft reset.
78    pub const MSK_RESET: u8 = 0xB6;
79    pub const MSK_RES_HEAT_RANGE: u8 = 0x30;
80    pub const MSK_NB_CONV: u8 = 0xF;
81    pub const MSK_PAR_H1_LSB: u8 = 0xF;
82    pub const MSK_GAS_RANGE: u8 = 0x0F;
83    pub const MSK_OSRS_P: u8 = 0x1C;
84    pub const MSK_OSRS_T: u8 = 0xE0;
85    pub const MSK_OSRS_H: u8 = 0x3;
86    pub const MSK_RUN_GAS: u8 = 0x10;
87    pub const MSK_MODE: u8 = 0x3;
88    pub const MSK_FILTER: u8 = 0x1C;
89}
90
91// --- Typestates ---
92
93/// Initial state of the driver. No logical connection established yet.
94pub struct Idle;
95/// Sensor instance created, but hardware not yet initialized (calibration data missing).
96pub struct Uninitialized;
97/// Sensor is fully initialized, calibrated, and ready for measurements.
98pub struct Ready;
99
100/// Error types for the BME680 driver.
101pub mod error {
102    /// Errors that can occur during communication, configuration, or measurement.
103    #[derive(Debug, Clone, Copy)]
104    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
105    pub enum Bme680Error {
106        /// Underlying I2C bus error.
107        I2CError,
108        /// Provided wait time exceeds the 4096ms hardware limit of the sensor.
109        InvalidWaitTime,
110        /// Provided profile index is out of bounds (valid range: 0-9).
111        InvalidProfileIndex,
112        /// Sensor did not complete measurement within the expected time window.
113        Timeout,
114        /// Gas heating plate did not reach the target temperature (check power supply).
115        HeaterNotStable,
116        /// Gas measurement completed, but data is marked as invalid by the sensor.
117        GasDataNotReady,
118    }
119
120    /// Result type alias for BME680 operations.
121    pub type Result<T> = core::result::Result<T, Bme680Error>;
122}
123
124/// Temperature wrapper for type-safety.
125///
126/// **Unit:** Centigrade * 100.
127///
128/// # Example
129/// `Celsius(2350)` represents **23.50 °C**.
130#[derive(Default, Debug, Clone, Copy)]
131#[cfg_attr(feature = "defmt", derive(defmt::Format))]
132pub struct Celsius(pub i32);
133
134impl Celsius {
135    /// Splits the fixed-point value into integral (degrees) and fractional (decimals) parts.
136    ///
137    /// Useful for logging or displaying data without converting to `f32`.
138    ///
139    /// # Returns
140    /// A tuple `(whole, fraction)`.
141    ///
142    /// # Example
143    /// ```rust
144    /// use bme680_driver::Celsius;
145    /// let temp = Celsius(2350);
146    /// assert_eq!(temp.split(), (23, 50)); // Represents 23.50 °C
147    /// ```
148    pub fn split(&self) -> (i32, i32) {
149        (self.0 / 100, self.0 % 100)
150    }
151}
152
153/// Duration wrapper for type-safety. Stored in milliseconds.
154#[derive(Default, Debug, Clone, Copy)]
155pub struct Milliseconds(pub u32);
156
157/// Factory-fused calibration coefficients read from the sensor.
158///
159/// These parameters are unique to every individual chip and are read from
160/// non-volatile memory during `init()`. They are strictly required to compensate
161/// the raw ADC values into physical units.
162#[derive(Debug, Default, Copy, Clone)]
163pub struct CalibData {
164    pub par_h1: u16,
165    pub par_h2: u16,
166    pub par_h3: i8,
167    pub par_h4: i8,
168    pub par_h5: i8,
169    pub par_h6: u8,
170    pub par_h7: i8,
171    pub par_g1: i8,
172    pub par_g2: i16,
173    pub par_g3: i8,
174    pub par_t1: u16,
175    pub par_t2: i16,
176    pub par_t3: i8,
177    pub par_p1: u16,
178    pub par_p2: i16,
179    pub par_p3: i8,
180    pub par_p4: i16,
181    pub par_p5: i16,
182    pub par_p6: i8,
183    pub par_p7: i8,
184    pub par_p8: i16,
185    pub par_p9: i16,
186    pub par_p10: u8,
187    pub res_heat_range: u8,
188    pub res_heat_val: u8,
189    pub range_sw_err: i8,
190}
191
192/// Raw ADC output and status bits read directly from the sensor registers.
193///
194/// This struct holds the uncompensated data read from `regs::ADDR_PRESS_MSB` onwards.
195/// It is used internally by the driver as input for the compensation formulas.
196#[derive(Debug, Copy, Clone)]
197pub struct RawData {
198    pub(crate) temp_adc: u32,
199    pub(crate) hum_adc: u16,
200    pub(crate) press_adc: u32,
201    pub(crate) gas_adc: u16,
202    /// Range switching error used for gas calculation.
203    pub(crate) gas_range: u8,
204    /// Flag: `true` if gas measurement is valid.
205    pub(crate) gas_valid_r: bool,
206    /// Flag: `true` if the target heater temperature was reached.
207    pub(crate) heat_stab_r: bool,
208}
209
210/// Intermediate temperature values used for compensation.
211///
212/// `temp_fine` is a high-resolution temperature value calculated during temperature
213/// compensation. It is carried over to pressure and humidity compensation formulas
214/// to account for temperature dependencies.
215#[derive(Debug, Copy, Clone, Default)]
216pub struct CalcTempData {
217    pub(crate) temp_fine: i32,
218    pub(crate) temp_comp: i32,
219}
220
221/// Represents relative humidity.
222///
223/// **Unit:** Milli-percent (percent * 1000).
224///
225/// # Example
226/// `Humidity(45123)` represents **45.123 %rH**.
227#[derive(Debug, Copy, Clone, Default)]
228#[cfg_attr(feature = "defmt", derive(defmt::Format))]
229pub struct Humidity(pub i32);
230
231impl Humidity {
232    /// Splits the fixed-point value into integral and fractional parts.
233    ///
234    /// # Returns
235    /// A tuple `(whole, fraction)`. The fraction represents 3 decimal places.
236    ///
237    /// # Example
238    /// ```rust
239    /// use bme680_driver::Humidity;
240    /// let hum = Humidity(45123);
241    /// assert_eq!(hum.split(), (45, 123)); // Represents 45.123 %
242    /// ```
243    pub fn split(&self) -> (i32, i32) {
244        (self.0 / 1000, self.0 % 1000)
245    }
246}
247
248/// Represents atmospheric pressure.
249///
250/// **Unit:** Pascal (Pa).
251///
252/// # Example
253/// `Pressure(101325)` represents **101325 Pa** (or 1013.25 hPa).
254#[derive(Debug, Copy, Clone, Default)]
255#[cfg_attr(feature = "defmt", derive(defmt::Format))]
256pub struct Pressure(pub u32);
257
258impl Pressure {
259    /// Converts the raw Pascal value to Hectopascal (hPa) and splits it into parts.
260    ///
261    /// Since 1 hPa = 100 Pa, this effectively splits the integer at the hundreds place.
262    ///
263    /// # Returns
264    /// A tuple `(hPa_integral, hPa_decimal)`.
265    ///
266    /// # Example
267    /// ```rust
268    /// use bme680_driver::Pressure;
269    /// let press = Pressure(101325);
270    /// assert_eq!(press.as_hpa(), (1013, 25)); // Represents 1013.25 hPa
271    /// ```
272    pub fn as_hpa(&self) -> (u32, u32) {
273        (self.0 / 100, self.0 % 100)
274    }
275}
276
277/// Represents gas resistance.
278///
279/// **Unit:** Ohms (Ω).
280///
281/// A higher gas resistance typically indicates cleaner air (fewer VOCs).
282/// A drastic drop in resistance usually indicates the presence of VOCs.
283#[derive(Debug, Copy, Clone, Default)]
284#[cfg_attr(feature = "defmt", derive(defmt::Format))]
285pub struct Gas(pub u32);
286
287/// Compensated measurement result in physical units.
288///
289/// All fields use strong types (`Temperature`, `Humidity`, etc.) to prevent unit confusion.
290/// If a measurement was disabled in the `Config`, the corresponding field will contain `0`.
291#[derive(Debug, Copy, Clone, Default)]
292#[cfg_attr(feature = "defmt", derive(defmt::Format))]
293pub struct Measurement {
294    /// Compensated temperature.
295    pub temp: Celsius,
296    /// Compensated relative humidity.
297    pub hum: Humidity,
298    /// Compensated atmospheric pressure.
299    pub pres: Pressure,
300    /// Compensated gas resistance.
301    pub gas: Gas,
302}
303
304/// The main BME680 driver structure.
305///
306/// Use `Bme680::new(...)` or `Bme680::with_config(...)` to start.
307/// The `STATE` generic uses the Typestate pattern to track initialization status
308/// at compile time, preventing invalid API calls.
309#[derive(Debug, Copy, Clone)]
310pub struct Bme680<I2C, STATE> {
311    i2c: I2C,
312    address: u8,
313    pub(crate) calib_data: CalibData,
314    config: Config,
315    _state: PhantomData<STATE>,
316}
317
318impl<I2C> Bme680<I2C, Idle>
319where
320    I2C: i2c::I2c,
321{
322    /// Creates a new driver instance in the `Uninitialized` state.
323    ///
324    /// This method does not yet communicate with the sensor.
325    ///
326    /// # Arguments
327    /// * `i2c` - The I2C bus object.
328    /// * `address` - The I2C address of the sensor (typically `0x76` or `0x77`).
329    pub fn new(i2c: I2C, address: u8) -> Bme680<I2C, Uninitialized> {
330        Bme680 {
331            i2c,
332            address,
333            calib_data: CalibData::default(),
334            config: Config::default(),
335            _state: PhantomData,
336        }
337    }
338
339    /// Creates a new driver instance with a pre-defined configuration.
340    ///
341    /// Use the `BME680Builder` to create the `config` object.
342    pub fn with_config(i2c: I2C, address: u8, config: Config) -> Bme680<I2C, Uninitialized> {
343        Bme680 {
344            i2c,
345            address,
346            calib_data: CalibData::default(),
347            config,
348            _state: PhantomData,
349        }
350    }
351}
352
353impl<I2C, STATE> Bme680<I2C, STATE>
354where
355    I2C: i2c::I2c,
356{
357    /// Performs a soft-reset of the sensor.
358    ///
359    /// This resets all internal registers to their default values (Power-On-Reset).
360    /// A delay of at least 2ms is required after the reset command.
361    fn reset(&mut self, delay: &mut impl DelayNs) -> error::Result<()> {
362        self.write_reg(&[regs::ADDR_RESET, msks::MSK_RESET])?;
363
364        delay.delay_ms(2);
365
366        Ok(())
367    }
368
369    /// Reads data from a starting register address into a provided buffer.
370    fn read_into(&mut self, reg_address: u8, buffer: &mut [u8]) -> error::Result<()> {
371        self.i2c
372            .write_read(self.address, &[reg_address], buffer)
373            .map_err(|_| error::Bme680Error::I2CError)
374    }
375
376    /// Reads a single byte from a specific register address.
377    fn read_reg_byte(&mut self, reg_address: u8) -> error::Result<u8> {
378        let mut buffer = [0];
379
380        self.i2c
381            .write_read(self.address, &[reg_address], &mut buffer)
382            .map_err(|_| error::Bme680Error::I2CError)?;
383
384        Ok(buffer[0])
385    }
386
387    /// Writes a byte slice (typically `[Register_Address, Value]`) to the sensor.
388    fn write_reg(&mut self, data: &[u8]) -> error::Result<()> {
389        self.i2c
390            .write(self.address, data)
391            .map_err(|_| error::Bme680Error::I2CError)?;
392        Ok(())
393    }
394
395    /// Configures the heater duration and target temperature for the current gas profile.
396    ///
397    /// This function utilizes the current `ambient_temp` to calculate the correct
398    /// heater resistance value needed to reach the target temperature.
399    fn set_gas_heater_profile(&mut self) -> error::Result<()> {
400        self.config_heater_on_time()?;
401        self.config_target_resistance(None)?;
402
403        Ok(())
404    }
405
406    /// Selects one of the 10 available gas heater profiles (0-9) in the sensor.
407    fn select_gas_profile(&mut self, index: GasProfileIndex) -> error::Result<()> {
408        let register = self.read_reg_byte(regs::ADDR_CTRL_GAS_1)?;
409
410        // Clear NB_CONV bits using mask, then set new profile index
411        self.write_reg(&[
412            regs::ADDR_CTRL_GAS_1,
413            (register & !msks::MSK_NB_CONV) | (index as u8),
414        ])?;
415
416        Ok(())
417    }
418
419    /// Enables the gas sensing functionality (sets `RUN_GAS` bit).
420    fn enable_gas_measurement(&mut self) -> error::Result<()> {
421        let register = self.read_reg_byte(regs::ADDR_CTRL_GAS_1)?;
422        // Set RUN_GAS bit (Bit 4)
423        self.write_reg(&[
424            regs::ADDR_CTRL_GAS_1,
425            (register & !msks::MSK_RUN_GAS) | msks::MSK_RUN_GAS,
426        ])?;
427        Ok(())
428    }
429
430    /// Disables the gas sensing functionality to save power.
431    fn disable_gas_measurement(&mut self) -> error::Result<()> {
432        let register = self.read_reg_byte(regs::ADDR_CTRL_GAS_1)?;
433        // Clear RUN_GAS bit (Bit 4)
434        self.write_reg(&[regs::ADDR_CTRL_GAS_1, register & !msks::MSK_RUN_GAS])?;
435        Ok(())
436    }
437}
438
439impl<I2C> Bme680<I2C, Uninitialized>
440where
441    I2C: i2c::I2c,
442{
443    /// Initializes the sensor: performs a soft-reset and loads factory calibration data.
444    ///
445    /// This consumes the `Uninitialized` driver and returns a `Ready` instance.
446    ///
447    /// # Process
448    /// 1. Soft Reset.
449    /// 2. Apply configuration (oversampling, IIR filter, gas settings).
450    /// 3. Read calibration coefficients from ROM.
451    ///
452    /// # Errors
453    /// Returns an error if I2C communication fails or if the sensor is unresponsive.
454    pub fn init(mut self, delay: &mut impl DelayNs) -> error::Result<Bme680<I2C, Ready>> {
455        // Sensor requires time to start up before reset
456        delay.delay_ms(2);
457
458        self.reset(delay)?;
459        self.apply_config()?;
460
461        // Read the factory calibration data (requires ~25ms I2C traffic)
462        let calib_data = self.get_calib_data()?;
463
464        Ok(Bme680 {
465            i2c: self.i2c,
466            address: self.address,
467            calib_data: calib_data,
468            config: self.config,
469            _state: PhantomData,
470        })
471    }
472
473    /// Applies the full sensor configuration defined in `self.config`.
474    ///
475    /// This sets up oversampling rates, IIR filters, and activates the gas sensor
476    /// if a profile is present.
477    fn apply_config(&mut self) -> error::Result<()> {
478        self.config_oversampling()?;
479        self.config_iir_filter()?;
480
481        if let Some(profile) = self.config.gas_profile {
482            self.enable_gas_measurement()?;
483            self.select_gas_profile(profile.index)?;
484            self.set_gas_heater_profile()?;
485        } else {
486            self.disable_gas_measurement()?;
487        }
488
489        Ok(())
490    }
491
492    /// Sets oversampling rates for Humidity, Temperature, and Pressure.
493    ///
494    /// Writes to registers `CTRL_HUM` and `CTRL_MEAS`.
495    fn config_oversampling(&mut self) -> error::Result<()> {
496        // Humidity configuration (Register 0x72)
497        // We must read first to preserve other bits if they existed
498        let ctrl_hum = self.read_reg_byte(regs::ADDR_CTRL_HUM)?;
499        let new_hum = (ctrl_hum & !msks::MSK_OSRS_H) | self.config.osrs_config.hum_osrs as u8;
500        self.write_reg(&[regs::ADDR_CTRL_HUM, new_hum])?;
501
502        // Temperature & Pressure configuration (Register 0x74)
503        let ctrl_meas = self.read_reg_byte(regs::ADDR_CTRL_MEAS)?;
504
505        // Prepare new bits
506        let temp_pres_combined = ((self.config.osrs_config.temp_osrs as u8) << 5)
507            | ((self.config.osrs_config.pres_osrs as u8) << 2);
508
509        // Clear old bits (Using !Mask) and set new ones
510        let new_meas = (ctrl_meas & !(msks::MSK_OSRS_T | msks::MSK_OSRS_P)) | temp_pres_combined;
511
512        self.write_reg(&[regs::ADDR_CTRL_MEAS, new_meas])?;
513
514        Ok(())
515    }
516
517    /// Configures the IIR (Infinite Impulse Response) filter coefficient.
518    ///
519    /// The IIR filter suppresses noise in pressure and temperature measurements.
520    fn config_iir_filter(&mut self) -> error::Result<()> {
521        let register = self.read_reg_byte(regs::ADDR_CONFIG)?;
522        // Clear filter bits and set new value
523        let new_reg_val = (register & !msks::MSK_FILTER) | ((self.config.iir_filter as u8) << 2);
524        self.write_reg(&[regs::ADDR_CONFIG, new_reg_val])?;
525        Ok(())
526    }
527
528    /// Reads factory-fused calibration coefficients from the sensor's ROM.
529    ///
530    /// The BME680 stores calibration data in two non-contiguous memory blocks.
531    /// These bytes are required to compensate the raw ADC values into physical units.
532    fn get_calib_data(&mut self) -> error::Result<CalibData> {
533        let mut calib_data = CalibData::default();
534        let mut buffer = [0u8; reg_sizes::SIZE_CALIB_TOTAL];
535
536        // 1. Read first block (0x89..0xA0)
537        self.read_into(regs::ADDR_CALIB_0, &mut buffer[0..reg_sizes::SIZE_CALIB_0])?;
538        // 2. Read second block (0xE1..0xF0)
539        self.read_into(regs::ADDR_CALIB_1, &mut buffer[reg_sizes::SIZE_CALIB_0..])?;
540
541        // Mapping raw buffer bytes to compensation parameters (Bosch proprietary logic)
542        // See BME680 datasheet, Section 3.11.1
543        calib_data.par_t1 = ((buffer[33] as i32) | ((buffer[34] as i32) << 8)) as u16;
544        calib_data.par_t2 = ((buffer[1] as i32) | ((buffer[2] as i32) << 8)) as i16;
545        calib_data.par_t3 = buffer[3] as i8;
546        calib_data.par_p1 = ((buffer[5] as i32) | ((buffer[6] as i32) << 8)) as u16;
547        calib_data.par_p2 = ((buffer[7] as i32) | ((buffer[8] as i32) << 8)) as i16;
548        calib_data.par_p3 = buffer[9] as i8;
549        calib_data.par_p4 = ((buffer[11] as i32) | ((buffer[12] as i32) << 8)) as i16;
550        calib_data.par_p5 = ((buffer[14] as i32) | ((buffer[13] as i32) << 8)) as i16;
551        calib_data.par_p6 = buffer[16] as i8;
552        calib_data.par_p7 = buffer[15] as i8;
553        calib_data.par_p8 = ((buffer[19] as i32) | ((buffer[20] as i32) << 8)) as i16;
554        calib_data.par_p9 = ((buffer[21] as i32) | ((buffer[22] as i32) << 8)) as i16;
555        calib_data.par_p10 = buffer[23];
556
557        // Use mask constant for bitwise operations
558        calib_data.par_h1 =
559            (((buffer[26] & msks::MSK_PAR_H1_LSB) as i32) | ((buffer[27] as i32) << 4)) as u16;
560        calib_data.par_h2 = (((buffer[26] >> 4) as i32) | ((buffer[25] as i32) << 4)) as u16;
561        calib_data.par_h3 = buffer[28] as i8;
562        calib_data.par_h4 = buffer[29] as i8;
563        calib_data.par_h5 = buffer[30] as i8;
564        calib_data.par_h6 = buffer[31];
565        calib_data.par_h7 = buffer[32] as i8;
566        calib_data.par_g1 = buffer[37] as i8;
567        calib_data.par_g2 = ((buffer[35] as i32) | ((buffer[36] as i32) << 8)) as i16;
568        calib_data.par_g3 = buffer[38] as i8;
569
570        // Additional heater-specific calibration values
571        calib_data.res_heat_val = self.read_reg_byte(regs::ADDR_RES_HEAT_VAL)?;
572
573        // Use mask for range reading (Bits 4,5)
574        calib_data.res_heat_range =
575            (self.read_reg_byte(regs::ADDR_RES_HEAT_RANGE)? & msks::MSK_RES_HEAT_RANGE) >> 4;
576
577        calib_data.range_sw_err = (self.read_reg_byte(regs::ADDR_RANGE_SW_ERR)? as i8) >> 4;
578
579        Ok(calib_data)
580    }
581}
582
583impl<I2C> Bme680<I2C, Ready>
584where
585    I2C: i2c::I2c,
586{
587    /// Triggers a full measurement cycle (Forced Mode), waits for completion, and returns the data.
588    ///
589    /// This is the primary method for retrieving sensor data. It handles the entire sequence:
590    /// 1. Wakes the sensor (Forced Mode).
591    /// 2. Waits for the TPH measurement + Gas Heating duration.
592    /// 3. Reads raw ADC data.
593    /// 4. Compensates raw values using factory calibration data.
594    ///
595    /// # Power Optimization
596    /// If all measurements are set to `Skipped` and gas is disabled in the `Config`,
597    /// this function returns immediately with a default `Measurement`, avoiding I2C traffic.
598    pub fn read_new_data(&mut self, delay: &mut impl DelayNs) -> error::Result<Measurement> {
599        // Optimization: Don't trigger a measurement if nothing is enabled.
600        if self.config.gas_disabled() && self.config.osrs_config.is_all_skipped() {
601            return Ok(Measurement::default());
602        }
603
604        // 1. Wake up sensor and start measurement cycle
605        self.activate_forced_mode()?;
606
607        // 2. Wait for heating phase (if gas is enabled)
608        // The sensor measures T, P, H first, then heats up for gas measurement.
609        if let Some(profile) = self.config.gas_profile {
610            delay.delay_ms(profile.wait_time.0);
611        }
612
613        // 3. Poll for "New Data" bit and read ADC values
614        let raw_data = self.get_raw_data(delay)?;
615
616        let mut temp = CalcTempData::default();
617        let mut hum = 0;
618        let mut pres = 0;
619        let mut gas = 0;
620
621        // 4. Apply mathematical compensation to raw values (if not skipped)
622        if self.config.osrs_config.temp_osrs != Oversampling::Skipped {
623            temp = self.calc_temp(raw_data.temp_adc);
624
625            // Humidity and Pressure compensation depends on "fine temperature" (t_fine)
626            if self.config.osrs_config.hum_osrs != Oversampling::Skipped {
627                hum = self.calc_hum(temp.temp_comp, raw_data.hum_adc);
628            }
629
630            if self.config.osrs_config.pres_osrs != Oversampling::Skipped {
631                pres = self.calc_pres(temp.temp_fine, raw_data.press_adc);
632            }
633        }
634
635        // 5. Check gas validity bits provided by the sensor
636        if self.config.gas_enabled() && !raw_data.gas_valid_r {
637            return Err(error::Bme680Error::GasDataNotReady);
638        } else if self.config.gas_enabled() && !raw_data.heat_stab_r {
639            return Err(error::Bme680Error::HeaterNotStable);
640        }
641
642        if self.config.gas_enabled() {
643            gas = self.calc_gas(raw_data.gas_adc, raw_data.gas_range);
644        }
645
646        Ok(Measurement {
647            temp: Celsius(temp.temp_comp),
648            hum: Humidity(hum),
649            pres: Pressure(pres),
650            gas: Gas(gas),
651        })
652    }
653
654    /// Reads the Chip ID from the sensor.
655    ///
656    /// Used to verify communication. Expected value is usually `0x61`.
657    pub fn read_chip_id(&mut self) -> error::Result<u8> {
658        Ok(self.read_reg_byte(regs::ADDR_ID)?)
659    }
660
661    /// Updates the ambient temperature used for gas heater calculation.
662    ///
663    /// **Important:** To maintain stability, avoid calling this method after every single measurement.
664    /// Only update if the temperature has changed significantly (e.g. > 1°C), as changes
665    /// to the heater resistance can cause the gas sensor values to fluctuate temporarily.
666    pub fn update_ambient_temp(&mut self, temp: Celsius) -> error::Result<()> {
667        self.config_target_resistance(Some(temp))?;
668
669        Ok(())
670    }
671
672    /// Switches the active gas profile to a new one.
673    ///
674    /// This updates the configuration, selects the profile on the sensor, and recalculates
675    /// the heater resistance values.
676    pub fn switch_profile(&mut self, profile: GasProfile) -> error::Result<()> {
677        self.config.gas_profile = Some(profile);
678
679        self.select_gas_profile(profile.index)?;
680        self.set_gas_heater_profile()?;
681
682        Ok(())
683    }
684
685    /// Polls the sensor until new data is available and reads all ADC values.
686    ///
687    /// This method includes a timeout mechanism (max. 50ms) to prevent infinite loops
688    /// if the sensor becomes unresponsive.
689    fn get_raw_data(&mut self, delay: &mut impl DelayNs) -> error::Result<RawData> {
690        let mut new_data = false;
691        let mut timeout_us = 50000; // 50ms Timeout
692
693        while !new_data {
694            if timeout_us <= 0 {
695                return Err(error::Bme680Error::Timeout);
696            }
697            // Check bit 7 (MSB) in EAS_STATUS_0 register
698            // Note: 0x80 is implied for Bit 7
699            new_data = (self.read_reg_byte(regs::ADDR_EAS_STATUS_0)? >> 7) != 0;
700
701            delay.delay_us(500);
702            timeout_us -= 500;
703        }
704
705        let mut buffer = [0u8; reg_sizes::SIZE_RAW_DATA];
706
707        // Burst read starting from ADDR_PRESS_MSB Register (0x1F)
708        self.read_into(regs::ADDR_PRESS_MSB, &mut buffer)?;
709
710        // Reconstruct 20-bit and 16-bit ADC values from register bytes
711        let press_adc =
712            ((buffer[2] as u32) >> 4) | ((buffer[1] as u32) << 4) | ((buffer[0] as u32) << 12);
713        let temp_adc =
714            ((buffer[5] as u32) >> 4) | ((buffer[4] as u32) << 4) | ((buffer[3] as u32) << 12);
715        let hum_adc = ((buffer[7] as u32) | ((buffer[6] as u32) << 8)) as u16;
716        let gas_adc = (((buffer[12] as u32) >> 6) | ((buffer[11] as u32) << 2)) as u16;
717
718        // Use mask for gas range
719        let gas_range = buffer[12] & msks::MSK_GAS_RANGE;
720
721        // Status bits for gas measurement
722        let gas_valid_r = ((buffer[12] >> 5) & 0x1) != 0;
723        let heat_stab_r = ((buffer[12] >> 4) & 0x1) != 0;
724
725        Ok(RawData {
726            temp_adc,
727            hum_adc,
728            press_adc,
729            gas_adc,
730            gas_range,
731            gas_valid_r,
732            heat_stab_r,
733        })
734    }
735
736    /// Activates 'Forced Mode' to trigger a single measurement cycle.
737    ///
738    /// In Forced Mode, the sensor performs one TPHG cycle and then automatically
739    /// returns to Sleep Mode.
740    fn activate_forced_mode(&mut self) -> error::Result<()> {
741        let register = self.read_reg_byte(regs::ADDR_CTRL_MEAS)?;
742        // Clear Mode bits and set to 01 (Forced)
743        self.write_reg(&[regs::ADDR_CTRL_MEAS, (register & !msks::MSK_MODE) | 0b01])?;
744        Ok(())
745    }
746}