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}