1#![no_std]
34#![forbid(unsafe_code)]
35#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
36#![allow(clippy::excessive_precision)]
37#![allow(clippy::unusual_byte_groupings)]
38
39use self::config::{SensorMode, Variant};
40
41use data::CalibrationData;
42use embedded_hal::delay::DelayNs;
43use embedded_hal::i2c::{I2c, SevenBitAddress};
44use i2c_helper::I2CHelper;
45
46pub use self::config::{Configuration, DeviceAddress, GasConfig, IIRFilter, Oversampling};
47use crate::data::{calculate_humidity, calculate_pressure, calculate_temperature};
48pub use data::MeasurmentData;
49pub use error::BmeError;
50
51#[cfg(feature = "embedded-hal-async")]
52mod async_impl;
53#[cfg(feature = "embedded-hal-async")]
54pub use async_impl::AsyncBme680;
55mod bitfields;
56mod calculations;
57mod config;
58mod constants;
59mod data;
60mod error;
61mod i2c_helper;
62
63pub struct Bme680<I2C, D> {
65 i2c: I2CHelper<I2C, D>,
67 calibration_data: CalibrationData,
69 current_sensor_config: Configuration,
71 variant: Variant,
73}
74impl<I2C, D> Bme680<I2C, D>
75where
76 I2C: I2c<SevenBitAddress>,
77 D: DelayNs,
80{
81 pub fn new(
87 i2c_interface: I2C,
88 device_address: DeviceAddress,
89 delayer: D,
90 sensor_config: &Configuration,
91 ambient_temperature: i32,
92 ) -> Result<Self, BmeError<I2C>> {
93 let mut i2c = I2CHelper::new(i2c_interface, device_address, delayer, ambient_temperature)?;
94
95 let calibration_data = i2c.get_calibration_data()?;
96 i2c.set_config(sensor_config, &calibration_data)?;
97 let variant = i2c.get_variant_id()?;
98 let bme = Self {
99 i2c,
100 calibration_data,
101 current_sensor_config: sensor_config.clone(),
102 variant,
103 };
104
105 Ok(bme)
106 }
107 pub fn into_inner(self) -> I2C {
109 self.i2c.into_inner()
110 }
111
112 fn put_to_sleep(&mut self) -> Result<(), BmeError<I2C>> {
113 self.i2c.set_mode(SensorMode::Sleep)
114 }
115 pub fn set_configuration(&mut self, config: &Configuration) -> Result<(), BmeError<I2C>> {
116 self.put_to_sleep()?;
117 self.i2c.set_config(config, &self.calibration_data)?;
118 self.current_sensor_config = config.clone();
120 Ok(())
121 }
122 pub fn measure(&mut self) -> Result<MeasurmentData, BmeError<I2C>> {
129 self.i2c.set_mode(SensorMode::Forced)?;
130 let delay_period = self.current_sensor_config.calculate_delay_period_us();
131 self.i2c.delay(delay_period);
132 for _i in 0..5 {
134 let raw_data = self.i2c.get_field_data()?;
135 match MeasurmentData::from_raw(raw_data, &self.calibration_data, &self.variant) {
136 Some(data) => {
137 self.i2c.ambient_temperature = data.temperature as i32;
139 return Ok(data);
140 }
141 None => self.i2c.delay(delay_period),
142 }
143 }
144 Err(BmeError::MeasuringTimeOut)
146 }
147
148 pub fn get_calibration_data(&self) -> &CalibrationData {
149 &self.calibration_data
150 }
151}
152
153#[cfg(test)]
154mod library_tests {
155 extern crate std;
156
157 use std::vec;
158 use std::vec::Vec;
159
160 use crate::constants::{
161 ADDR_CHIP_ID, ADDR_CONFIG, ADDR_CONTROL_MODE, ADDR_GAS_WAIT_0, ADDR_REG_COEFF1,
162 ADDR_REG_COEFF2, ADDR_REG_COEFF3, ADDR_RES_HEAT_0, ADDR_SOFT_RESET, ADDR_VARIANT_ID,
163 CHIP_ID, CMD_SOFT_RESET, LEN_COEFF1, LEN_COEFF2, LEN_COEFF3,
164 };
165 use crate::i2c_helper::extract_calibration_data;
166
167 const CALIBRATION_DATA: [u8; 42] = [
168 179, 193, 176, 188, 21, 51, 11, 29, 222, 179, 184, 1, 230, 47, 209, 22, 154, 34, 237, 70,
169 148, 134, 44, 13, 204, 61, 206, 69, 18, 43, 124, 164, 92, 132, 19, 63, 29, 28, 201, 140,
170 70, 24,
171 ];
172
173 use super::*;
174 use crate::bitfields::RawConfig;
175 use embedded_hal_mock::eh1::delay::NoopDelay;
176 use embedded_hal_mock::eh1::i2c::{Mock as I2cMock, Transaction as I2cTransaction};
177
178 fn setup_transactions() -> Vec<I2cTransaction> {
179 let mut transactions = vec![];
180 let calibration_data_1 = CALIBRATION_DATA[0..LEN_COEFF1].to_vec();
181 let calibration_data_2 = CALIBRATION_DATA[LEN_COEFF1..LEN_COEFF1 + LEN_COEFF2].to_vec();
182 let calibration_data_3 = CALIBRATION_DATA
183 [LEN_COEFF1 + LEN_COEFF2..LEN_COEFF1 + LEN_COEFF2 + LEN_COEFF3]
184 .to_vec();
185 assert_eq!(calibration_data_1.len(), LEN_COEFF1);
186 assert_eq!(calibration_data_2.len(), LEN_COEFF2);
187 assert_eq!(calibration_data_3.len(), LEN_COEFF3);
188 transactions.push(I2cTransaction::write(
190 DeviceAddress::Primary.into(),
191 vec![ADDR_SOFT_RESET, CMD_SOFT_RESET],
192 ));
193 transactions.push(I2cTransaction::write_read(
195 DeviceAddress::Primary.into(),
196 vec![ADDR_CHIP_ID],
197 vec![CHIP_ID],
198 ));
199 transactions.push(I2cTransaction::write_read(
201 DeviceAddress::Primary.into(),
202 vec![ADDR_REG_COEFF1],
203 calibration_data_1,
204 ));
205 transactions.push(I2cTransaction::write_read(
206 DeviceAddress::Primary.into(),
207 vec![ADDR_REG_COEFF2],
208 calibration_data_2,
209 ));
210 transactions.push(I2cTransaction::write_read(
211 DeviceAddress::Primary.into(),
212 vec![ADDR_REG_COEFF3],
213 calibration_data_3,
214 ));
215 let default_config = [0u8; 5];
222 transactions.push(I2cTransaction::write_read(
223 DeviceAddress::Primary.into(),
224 vec![ADDR_CONFIG],
225 default_config.into(),
226 ));
227 let user_config = Configuration::default();
229 let mut raw_config = RawConfig(default_config);
230 raw_config.apply_config(&user_config);
231 raw_config
233 .0
234 .into_iter()
235 .enumerate()
236 .for_each(|(register_offset, register_content)| {
237 transactions.push(I2cTransaction::write(
238 DeviceAddress::Primary.into(),
239 vec![ADDR_CONFIG + register_offset as u8, register_content],
240 ));
241 });
242 let gas_config = user_config.gas_config.unwrap();
244 let gas_wait_0 = gas_config.calc_gas_wait();
245 let res_heat_0 = gas_config.calc_res_heat(&extract_calibration_data(CALIBRATION_DATA), 20);
246 transactions.push(I2cTransaction::write(
247 DeviceAddress::Primary.into(),
248 vec![ADDR_GAS_WAIT_0, gas_wait_0],
249 ));
250 transactions.push(I2cTransaction::write(
251 DeviceAddress::Primary.into(),
252 vec![ADDR_RES_HEAT_0, res_heat_0],
253 ));
254 transactions.push(I2cTransaction::write_read(
256 DeviceAddress::Primary.into(),
257 vec![ADDR_VARIANT_ID],
258 vec![0],
259 ));
260 transactions
261 }
262 fn add_sleep_to_sleep_transactions(transactions: &mut Vec<I2cTransaction>) {
263 transactions.push(I2cTransaction::write_read(
264 DeviceAddress::Primary.into(),
265 vec![ADDR_CONTROL_MODE],
266 vec![0b101011_00],
268 ));
269 }
270 #[test]
271 fn test_setup() {
272 let transactions = setup_transactions();
273 let i2c_interface = I2cMock::new(&transactions);
274 let bme = Bme680::new(
275 i2c_interface,
276 DeviceAddress::Primary,
277 NoopDelay::new(),
278 &Configuration::default(),
279 20,
280 )
281 .unwrap();
282 bme.into_inner().done();
283 }
284
285 #[test]
286 fn test_set_mode_forced_to_sleep() {
287 let mut transactions = setup_transactions();
288 transactions.push(I2cTransaction::write_read(
289 DeviceAddress::Primary.into(),
290 vec![ADDR_CONTROL_MODE],
291 vec![0b101011_01],
293 ));
294 transactions.push(I2cTransaction::write(
295 DeviceAddress::Primary.into(),
296 vec![ADDR_CONTROL_MODE, 0b101011_00],
297 ));
298 transactions.push(I2cTransaction::write_read(
299 DeviceAddress::Primary.into(),
300 vec![ADDR_CONTROL_MODE],
301 vec![0b101011_00],
303 ));
304 let i2c_interface = I2cMock::new(&transactions);
306 let mut bme = Bme680::new(
307 i2c_interface,
308 DeviceAddress::Primary,
309 NoopDelay::new(),
310 &Configuration::default(),
311 20,
312 )
313 .unwrap();
314 bme.put_to_sleep().unwrap();
315 bme.into_inner().done();
316 }
317 #[test]
318 fn test_set_mode_sleep_to_sleep() {
319 let mut transactions = setup_transactions();
320 add_sleep_to_sleep_transactions(&mut transactions);
321 let i2c_interface = I2cMock::new(&transactions);
322 let mut bme = Bme680::new(
323 i2c_interface,
324 DeviceAddress::Primary,
325 NoopDelay::new(),
326 &Configuration::default(),
327 20,
328 )
329 .unwrap();
330 bme.put_to_sleep().unwrap();
331 bme.into_inner().done();
332 }
333}