#![no_std]
#![forbid(unsafe_code)]
pub use self::settings::{
DesiredSensorSettings, GasSett, IIRFilterSize, OversamplingSetting, SensorSettings, Settings,
SettingsBuilder, TphSett,
};
mod calc;
mod settings;
use crate::calc::Calc;
use crate::hal::blocking::delay::DelayMs;
use crate::hal::blocking::i2c::{Read, Write};
use core::time::Duration;
use core::{marker::PhantomData, result};
use embedded_hal as hal;
use log::{debug, error, info};
pub const BME680_POLL_PERIOD_MS: u8 = 10;
pub const BME680_CHIP_ID: u8 = 0x61;
const BME680_FIELD_LENGTH: usize = 15;
const BME680_COEFF_ADDR1_LEN: usize = 25;
const BME680_COEFF_ADDR2_LEN: usize = 16;
const BME680_SOFT_RESET_CMD: u8 = 0xb6;
const BME680_ADDR_RES_HEAT_VAL_ADDR: u8 = 0x00;
const BME680_ADDR_RES_HEAT_RANGE_ADDR: u8 = 0x02;
const BME680_ADDR_RANGE_SW_ERR_ADDR: u8 = 0x04;
const BME680_ADDR_SENS_CONF_START: u8 = 0x5A;
const BME680_ADDR_GAS_CONF_START: u8 = 0x64;
const BME680_SOFT_RESET_ADDR: u8 = 0xe0;
const BME680_FIELD0_ADDR: u8 = 0x1d;
const BME680_RES_HEAT0_ADDR: u8 = 0x5a;
const BME680_GAS_WAIT0_ADDR: u8 = 0x64;
const BME680_CONF_HEAT_CTRL_ADDR: u8 = 0x70;
const BME680_CONF_ODR_RUN_GAS_NBC_ADDR: u8 = 0x71;
const BME680_CONF_OS_H_ADDR: u8 = 0x72;
const BME680_CONF_T_P_MODE_ADDR: u8 = 0x74;
const BME680_CONF_ODR_FILT_ADDR: u8 = 0x75;
const BME680_COEFF_ADDR1: u8 = 0x89;
const BME680_COEFF_ADDR2: u8 = 0xe1;
const BME680_CHIP_ID_ADDR: u8 = 0xd0;
const BME680_SLEEP_MODE: u8 = 0;
const BME680_FORCED_MODE: u8 = 1;
const BME680_RESET_PERIOD: u8 = 10;
const BME680_MODE_MSK: u8 = 0x03;
const BME680_RSERROR_MSK: u8 = 0xf0;
const BME680_NEW_DATA_MSK: u8 = 0x80;
const BME680_GAS_INDEX_MSK: u8 = 0x0f;
const BME680_GAS_RANGE_MSK: u8 = 0x0f;
const BME680_GASM_VALID_MSK: u8 = 0x20;
const BME680_HEAT_STAB_MSK: u8 = 0x10;
const BME680_TMP_BUFFER_LENGTH: usize = 40;
const BME680_REG_BUFFER_LENGTH: usize = 6;
#[derive(Debug)]
pub enum Error<R, W> {
I2CWrite(W),
I2CRead(R),
DeviceNotFound,
InvalidLength,
DefinePwrMode,
NoNewData,
BoundaryCheckFailure(&'static str),
}
pub type Result<T, R, W> = result::Result<T, Error<R, W>>;
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum PowerMode {
SleepMode,
ForcedMode,
}
impl PowerMode {
fn from(power_mode: u8) -> Self {
match power_mode {
BME680_SLEEP_MODE => PowerMode::SleepMode,
BME680_FORCED_MODE => PowerMode::ForcedMode,
_ => panic!("Unknown power mode: {}", power_mode),
}
}
fn value(&self) -> u8 {
match self {
PowerMode::SleepMode => BME680_SLEEP_MODE,
PowerMode::ForcedMode => BME680_FORCED_MODE,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum I2CAddress {
Primary,
Secondary,
Other(u8),
}
impl I2CAddress {
pub fn addr(&self) -> u8 {
match &self {
I2CAddress::Primary => 0x76u8,
I2CAddress::Secondary => 0x77u8,
I2CAddress::Other(addr) => *addr,
}
}
}
impl Default for I2CAddress {
fn default() -> I2CAddress {
I2CAddress::Primary
}
}
#[derive(Debug, Default, Copy)]
#[repr(C)]
pub struct CalibData {
pub par_h1: u16,
pub par_h2: u16,
pub par_h3: i8,
pub par_h4: i8,
pub par_h5: i8,
pub par_h6: u8,
pub par_h7: i8,
pub par_gh1: i8,
pub par_gh2: i16,
pub par_gh3: i8,
pub par_t1: u16,
pub par_t2: i16,
pub par_t3: i8,
pub par_p1: u16,
pub par_p2: i16,
pub par_p3: i8,
pub par_p4: i16,
pub par_p5: i16,
pub par_p6: i8,
pub par_p7: i8,
pub par_p8: i16,
pub par_p9: i16,
pub par_p10: u8,
pub res_heat_range: u8,
pub res_heat_val: i8,
pub range_sw_err: u8,
}
impl Clone for CalibData {
fn clone(&self) -> Self {
*self
}
}
#[derive(Debug, Default, Copy)]
#[repr(C)]
pub struct FieldData {
status: u8,
gas_index: u8,
meas_index: u8,
temperature: i16,
pressure: u32,
humidity: u32,
gas_resistance: u32,
}
impl Clone for FieldData {
fn clone(&self) -> Self {
*self
}
}
impl FieldData {
pub fn temperature_celsius(&self) -> f32 {
self.temperature as f32 / 100f32
}
pub fn pressure_hpa(&self) -> f32 {
self.pressure as f32 / 100f32
}
pub fn humidity_percent(&self) -> f32 {
self.humidity as f32 / 1000f32
}
pub fn gas_resistance_ohm(&self) -> u32 {
self.gas_resistance
}
pub fn gas_valid(&self) -> bool {
self.status & BME680_GASM_VALID_MSK != 0
}
pub fn heat_stable(&self) -> bool {
self.status & BME680_HEAT_STAB_MSK != 0
}
}
#[derive(PartialEq, Debug)]
pub enum FieldDataCondition {
NewData,
Unchanged,
}
struct I2CUtil {}
impl I2CUtil {
pub fn read_byte<I2C>(
i2c: &mut I2C,
dev_id: u8,
reg_addr: u8,
) -> Result<u8, <I2C as Read>::Error, <I2C as Write>::Error>
where
I2C: Read + Write,
{
let mut buf = [0; 1];
i2c.write(dev_id, &[reg_addr]).map_err(Error::I2CWrite)?;
match i2c.read(dev_id, &mut buf) {
Ok(()) => Ok(buf[0]),
Err(e) => Err(Error::I2CRead(e)),
}
}
pub fn read_bytes<I2C>(
i2c: &mut I2C,
dev_id: u8,
reg_addr: u8,
buf: &mut [u8],
) -> Result<(), <I2C as Read>::Error, <I2C as Write>::Error>
where
I2C: Read + Write,
{
i2c.write(dev_id, &[reg_addr]).map_err(Error::I2CWrite)?;
match i2c.read(dev_id, buf) {
Ok(()) => Ok(()),
Err(e) => Err(Error::I2CRead(e)),
}
}
}
#[repr(C)]
pub struct Bme680<I2C, D> {
i2c: I2C,
delay: PhantomData<D>,
dev_id: I2CAddress,
calib: CalibData,
tph_sett: TphSett,
gas_sett: GasSett,
power_mode: PowerMode,
}
fn boundary_check<I2C>(
value: Option<u8>,
value_name: &'static str,
min: u8,
max: u8,
) -> Result<u8, <I2C as Read>::Error, <I2C as Write>::Error>
where
I2C: Read + Write,
{
let value = value.ok_or(Error::BoundaryCheckFailure(value_name))?;
if value < min {
const MIN: &str = "Boundary check failure, value exceeds maximum";
error!("{}, value name: {}", MIN, value_name);
return Err(Error::BoundaryCheckFailure(MIN));
}
if value > max {
const MAX: &str = "Boundary check, value exceeds minimum";
error!("{}, value name: {}", MAX, value_name);
return Err(Error::BoundaryCheckFailure(MAX));
}
Ok(value)
}
impl<I2C, D> Bme680<I2C, D>
where
D: DelayMs<u8>,
I2C: Read + Write,
{
pub fn soft_reset(
i2c: &mut I2C,
delay: &mut D,
dev_id: I2CAddress,
) -> Result<(), <I2C as Read>::Error, <I2C as Write>::Error> {
let tmp_buff: [u8; 2] = [BME680_SOFT_RESET_ADDR, BME680_SOFT_RESET_CMD];
i2c.write(dev_id.addr(), &tmp_buff)
.map_err(Error::I2CWrite)?;
delay.delay_ms(BME680_RESET_PERIOD);
Ok(())
}
pub fn init(
mut i2c: I2C,
delay: &mut D,
dev_id: I2CAddress,
) -> Result<Bme680<I2C, D>, <I2C as Read>::Error, <I2C as Write>::Error> {
Bme680::soft_reset(&mut i2c, delay, dev_id)?;
debug!("Reading chip id");
let chip_id = I2CUtil::read_byte::<I2C>(&mut i2c, dev_id.addr(), BME680_CHIP_ID_ADDR)?;
debug!("Chip id: {}", chip_id);
if chip_id == BME680_CHIP_ID {
debug!("Reading calib data");
let calib = Bme680::<I2C, D>::get_calib_data::<I2C>(&mut i2c, dev_id)?;
debug!("Calib data {:?}", calib);
let dev = Bme680 {
i2c,
delay: PhantomData,
dev_id,
calib,
power_mode: PowerMode::ForcedMode,
tph_sett: Default::default(),
gas_sett: Default::default(),
};
info!("Finished device init");
Ok(dev)
} else {
error!("Device does not match chip id {}", BME680_CHIP_ID);
Err(Error::DeviceNotFound)
}
}
fn bme680_set_regs(
&mut self,
reg: &[(u8, u8)],
) -> Result<(), <I2C as Read>::Error, <I2C as Write>::Error> {
if reg.is_empty() || reg.len() > (BME680_TMP_BUFFER_LENGTH / 2) as usize {
return Err(Error::InvalidLength);
}
for (reg_addr, reg_data) in reg {
let tmp_buff: [u8; 2] = [*reg_addr, *reg_data];
debug!(
"Setting register reg: {:?} tmp_buf: {:?}",
reg_addr, tmp_buff
);
self.i2c
.write(self.dev_id.addr(), &tmp_buff)
.map_err(Error::I2CWrite)?;
}
Ok(())
}
pub fn set_sensor_settings(
&mut self,
delay: &mut D,
settings: Settings,
) -> Result<(), <I2C as Read>::Error, <I2C as Write>::Error> {
let (sensor_settings, desired_settings) = settings;
let tph_sett = sensor_settings.tph_sett;
let gas_sett = sensor_settings.gas_sett;
let mut reg: [(u8, u8); BME680_REG_BUFFER_LENGTH] = [(0, 0); BME680_REG_BUFFER_LENGTH];
let intended_power_mode = self.power_mode;
if desired_settings.contains(DesiredSensorSettings::GAS_MEAS_SEL) {
debug!("GAS_MEAS_SEL: true");
self.set_gas_config(gas_sett)?;
}
let power_mode = self.power_mode;
self.set_sensor_mode(delay, power_mode)?;
let mut element_index = 0;
if desired_settings.contains(DesiredSensorSettings::FILTER_SEL) {
let mut data =
I2CUtil::read_byte(&mut self.i2c, self.dev_id.addr(), BME680_CONF_ODR_FILT_ADDR)?;
debug!("FILTER_SEL: true");
data = (data as i32 & !0x1ci32
| (tph_sett.filter.unwrap_or(IIRFilterSize::Size0) as i32) << 2i32 & 0x1ci32)
as u8;
reg[element_index] = (BME680_CONF_ODR_FILT_ADDR, data);
element_index += 1;
}
if desired_settings.contains(DesiredSensorSettings::HCNTRL_SEL) {
debug!("HCNTRL_SEL: true");
let gas_sett_heatr_ctrl =
boundary_check::<I2C>(gas_sett.heatr_ctrl, "GasSett.heatr_ctrl", 0x0u8, 0x8u8)?;
let mut data = I2CUtil::read_byte(
&mut self.i2c,
self.dev_id.addr(),
BME680_CONF_HEAT_CTRL_ADDR,
)?;
data = (data as i32 & !0x8i32 | gas_sett_heatr_ctrl as i32 & 0x8) as u8;
reg[element_index] = (BME680_CONF_HEAT_CTRL_ADDR, data);
element_index += 1;
}
if desired_settings
.contains(DesiredSensorSettings::OST_SEL | DesiredSensorSettings::OSP_SEL)
{
let mut data =
I2CUtil::read_byte(&mut self.i2c, self.dev_id.addr(), BME680_CONF_T_P_MODE_ADDR)?;
if desired_settings.contains(DesiredSensorSettings::OST_SEL) {
debug!("OST_SEL: true");
let tph_sett_os_temp = boundary_check::<I2C>(
tph_sett.os_temp.map(|x| x as u8),
"TphSett.os_temp",
0,
5,
)?;
data = (data as i32 & !0xe0i32 | (tph_sett_os_temp as i32) << 5i32 & 0xe0i32) as u8;
}
if desired_settings.contains(DesiredSensorSettings::OSP_SEL) {
debug!("OSP_SEL: true");
let tph_sett_os_pres = tph_sett.os_temp.expect("OS TEMP");
data = (data as i32 & !0x1ci32 | (tph_sett_os_pres as i32) << 2i32 & 0x1ci32) as u8;
}
reg[element_index] = (BME680_CONF_T_P_MODE_ADDR, data);
element_index += 1;
}
if desired_settings.contains(DesiredSensorSettings::OSH_SEL) {
debug!("OSH_SEL: true");
let tph_sett_os_hum =
boundary_check::<I2C>(tph_sett.os_hum.map(|x| x as u8), "TphSett.os_hum", 0, 5)?;
let mut data =
I2CUtil::read_byte(&mut self.i2c, self.dev_id.addr(), BME680_CONF_OS_H_ADDR)?;
data = (data as i32 & !0x7i32 | tph_sett_os_hum as i32 & 0x7i32) as u8;
reg[element_index] = (BME680_CONF_OS_H_ADDR, data);
element_index += 1;
}
if desired_settings
.contains(DesiredSensorSettings::RUN_GAS_SEL | DesiredSensorSettings::NBCONV_SEL)
{
let mut data = I2CUtil::read_byte(
&mut self.i2c,
self.dev_id.addr(),
BME680_CONF_ODR_RUN_GAS_NBC_ADDR,
)?;
if desired_settings.contains(DesiredSensorSettings::RUN_GAS_SEL) {
debug!("RUN_GAS_SEL: true");
data = (data as i32 & !0x10i32
| (gas_sett.run_gas_measurement as i32) << 4i32 & 0x10i32)
as u8;
}
if desired_settings.contains(DesiredSensorSettings::NBCONV_SEL) {
debug!("NBCONV_SEL: true");
let gas_sett_nb_conv =
boundary_check::<I2C>(Some(gas_sett.nb_conv), "GasSett.nb_conv", 0, 10)?;
data = (data as i32 & !0xfi32 | gas_sett_nb_conv as i32 & 0xfi32) as u8;
}
reg[element_index] = (BME680_CONF_ODR_RUN_GAS_NBC_ADDR, data);
element_index += 1;
}
self.bme680_set_regs(®[0..element_index])?;
self.power_mode = intended_power_mode;
self.tph_sett = tph_sett;
Ok(())
}
pub fn get_sensor_settings(
&mut self,
desired_settings: DesiredSensorSettings,
) -> Result<SensorSettings, <I2C as Read>::Error, <I2C as Write>::Error> {
let reg_addr: u8 = 0x70u8;
let mut data_array: [u8; BME680_REG_BUFFER_LENGTH] = [0; BME680_REG_BUFFER_LENGTH];
let mut sensor_settings: SensorSettings = Default::default();
sensor_settings.tph_sett.temperature_offset = self.tph_sett.temperature_offset;
I2CUtil::read_bytes(&mut self.i2c, self.dev_id.addr(), reg_addr, &mut data_array)?;
if desired_settings.contains(DesiredSensorSettings::GAS_MEAS_SEL) {
sensor_settings.gas_sett = self.get_gas_config()?;
}
if desired_settings.contains(DesiredSensorSettings::FILTER_SEL) {
sensor_settings.tph_sett.filter = Some(IIRFilterSize::from_u8(
((data_array[5usize] as i32 & 0x1ci32) >> 2i32) as u8,
));
}
if desired_settings
.contains(DesiredSensorSettings::OST_SEL | DesiredSensorSettings::OSP_SEL)
{
let os_temp: u8 = ((data_array[4usize] as i32 & 0xe0i32) >> 5i32) as u8;
let os_pres: u8 = ((data_array[4usize] as i32 & 0x1ci32) >> 2i32) as u8;
sensor_settings.tph_sett.os_temp = Some(OversamplingSetting::from_u8(os_temp));
sensor_settings.tph_sett.os_pres = Some(OversamplingSetting::from_u8(os_pres));
}
if desired_settings.contains(DesiredSensorSettings::OSH_SEL) {
let os_hum: u8 = (data_array[2usize] as i32 & 0x7i32) as u8;
sensor_settings.tph_sett.os_hum = Some(OversamplingSetting::from_u8(os_hum));
}
if desired_settings.contains(DesiredSensorSettings::HCNTRL_SEL) {
sensor_settings.gas_sett.heatr_ctrl = Some((data_array[0usize] as i32 & 0x8i32) as u8);
}
if desired_settings
.contains(DesiredSensorSettings::RUN_GAS_SEL | DesiredSensorSettings::NBCONV_SEL)
{
sensor_settings.gas_sett.nb_conv = (data_array[1usize] as i32 & 0xfi32) as u8;
sensor_settings.gas_sett.run_gas_measurement =
((data_array[1usize] as i32 & 0x10i32) >> 4i32) == 0;
}
Ok(sensor_settings)
}
pub fn set_sensor_mode(
&mut self,
delay: &mut D,
target_power_mode: PowerMode,
) -> Result<(), <I2C as Read>::Error, <I2C as Write>::Error> {
let mut tmp_pow_mode: u8;
let mut current_power_mode: PowerMode;
loop {
tmp_pow_mode =
I2CUtil::read_byte(&mut self.i2c, self.dev_id.addr(), BME680_CONF_T_P_MODE_ADDR)?;
current_power_mode = PowerMode::from(tmp_pow_mode & BME680_MODE_MSK);
debug!("Current power mode: {:?}", current_power_mode);
if current_power_mode != PowerMode::SleepMode {
tmp_pow_mode &= !BME680_MODE_MSK;
debug!("Setting to sleep tmp_pow_mode: {}", tmp_pow_mode);
self.bme680_set_regs(&[(BME680_CONF_T_P_MODE_ADDR, tmp_pow_mode)])?;
delay.delay_ms(BME680_POLL_PERIOD_MS);
} else {
break;
}
}
if target_power_mode != PowerMode::SleepMode {
tmp_pow_mode = tmp_pow_mode & !BME680_MODE_MSK | target_power_mode.value();
debug!("Already in sleep Target power mode: {}", tmp_pow_mode);
self.bme680_set_regs(&[(BME680_CONF_T_P_MODE_ADDR, tmp_pow_mode)])?;
}
Ok(())
}
pub fn get_sensor_mode(
&mut self,
) -> Result<PowerMode, <I2C as Read>::Error, <I2C as Write>::Error> {
let regs =
I2CUtil::read_byte(&mut self.i2c, self.dev_id.addr(), BME680_CONF_T_P_MODE_ADDR)?;
let mode = regs & BME680_MODE_MSK;
Ok(PowerMode::from(mode))
}
pub fn bme680_set_profile_dur(&mut self, tph_sett: TphSett, duration: Duration) {
let os_to_meas_cycles: [u8; 6] = [0u8, 1u8, 2u8, 4u8, 8u8, 16u8];
const MILLIS_PER_SEC: u64 = 1_000;
const NANOS_PER_MILLI: u64 = 1_000_000;
let millis = (duration.as_secs() as u64 * MILLIS_PER_SEC)
+ (duration.subsec_nanos() as u64 / NANOS_PER_MILLI);
let mut meas_cycles = os_to_meas_cycles
[tph_sett.os_temp.unwrap_or(OversamplingSetting::OSNone) as usize]
as u64;
meas_cycles = meas_cycles.wrapping_add(
os_to_meas_cycles[tph_sett.os_pres.unwrap_or(OversamplingSetting::OSNone) as usize]
as u64,
);
meas_cycles = meas_cycles.wrapping_add(
os_to_meas_cycles[tph_sett.os_hum.unwrap_or(OversamplingSetting::OSNone) as usize]
as u64,
);
let mut tph_dur = meas_cycles.wrapping_mul(1963u64);
tph_dur = tph_dur.wrapping_add(477u64.wrapping_mul(4u64));
tph_dur = tph_dur.wrapping_add(477u64.wrapping_mul(5u64));
tph_dur = tph_dur.wrapping_add(500u64);
tph_dur = tph_dur.wrapping_div(1000u64);
tph_dur = tph_dur.wrapping_add(1u64);
self.gas_sett.heatr_dur = Some(Duration::from_millis(millis - tph_dur));
}
pub fn get_profile_dur(
&self,
sensor_settings: &SensorSettings,
) -> Result<Duration, <I2C as Read>::Error, <I2C as Write>::Error> {
let os_to_meas_cycles: [u8; 6] = [0u8, 1u8, 2u8, 4u8, 8u8, 16u8];
let mut meas_cycles = os_to_meas_cycles[sensor_settings
.tph_sett
.os_temp
.unwrap_or(OversamplingSetting::OSNone)
as usize] as u32;
meas_cycles = meas_cycles.wrapping_add(
os_to_meas_cycles[sensor_settings
.tph_sett
.os_pres
.unwrap_or(OversamplingSetting::OSNone) as usize] as u32,
);
meas_cycles = meas_cycles.wrapping_add(
os_to_meas_cycles[sensor_settings
.tph_sett
.os_hum
.unwrap_or(OversamplingSetting::OSNone) as usize] as u32,
);
let mut tph_dur = meas_cycles.wrapping_mul(1963u32);
tph_dur = tph_dur.wrapping_add(477u32.wrapping_mul(4u32));
tph_dur = tph_dur.wrapping_add(477u32.wrapping_mul(5u32));
tph_dur = tph_dur.wrapping_add(500u32);
tph_dur = tph_dur.wrapping_div(1000u32);
tph_dur = tph_dur.wrapping_add(1u32);
let mut duration = Duration::from_millis(tph_dur as u64);
if sensor_settings.gas_sett.run_gas_measurement {
duration += sensor_settings.gas_sett.heatr_dur.expect("Heatrdur");
}
Ok(duration)
}
fn get_calib_data<I2CX>(
i2c: &mut I2CX,
dev_id: I2CAddress,
) -> Result<CalibData, <I2CX as Read>::Error, <I2CX as Write>::Error>
where
I2CX: Read + Write,
{
let mut calib: CalibData = Default::default();
let mut coeff_array: [u8; BME680_COEFF_ADDR1_LEN + BME680_COEFF_ADDR2_LEN] =
[0; BME680_COEFF_ADDR1_LEN + BME680_COEFF_ADDR2_LEN];
I2CUtil::read_bytes::<I2CX>(
i2c,
dev_id.addr(),
BME680_COEFF_ADDR1,
&mut coeff_array[0..(BME680_COEFF_ADDR1_LEN - 1)],
)?;
I2CUtil::read_bytes::<I2CX>(
i2c,
dev_id.addr(),
BME680_COEFF_ADDR2,
&mut coeff_array
[BME680_COEFF_ADDR1_LEN..(BME680_COEFF_ADDR1_LEN + BME680_COEFF_ADDR2_LEN - 1)],
)?;
calib.par_t1 = ((coeff_array[34usize] as i32) << 8i32 | coeff_array[33usize] as i32) as u16;
calib.par_t2 = ((coeff_array[2usize] as i32) << 8i32 | coeff_array[1usize] as i32) as i16;
calib.par_t3 = coeff_array[3usize] as i8;
calib.par_p1 = ((coeff_array[6usize] as i32) << 8i32 | coeff_array[5usize] as i32) as u16;
calib.par_p2 = ((coeff_array[8usize] as i32) << 8i32 | coeff_array[7usize] as i32) as i16;
calib.par_p3 = coeff_array[9usize] as i8;
calib.par_p4 = ((coeff_array[12usize] as i32) << 8i32 | coeff_array[11usize] as i32) as i16;
calib.par_p5 = ((coeff_array[14usize] as i32) << 8i32 | coeff_array[13usize] as i32) as i16;
calib.par_p6 = coeff_array[16usize] as i8;
calib.par_p7 = coeff_array[15usize] as i8;
calib.par_p8 = ((coeff_array[20usize] as i32) << 8i32 | coeff_array[19usize] as i32) as i16;
calib.par_p9 = ((coeff_array[22usize] as i32) << 8i32 | coeff_array[21usize] as i32) as i16;
calib.par_p10 = coeff_array[23usize];
calib.par_h1 =
((coeff_array[27usize] as i32) << 4i32 | coeff_array[26usize] as i32 & 0xfi32) as u16;
calib.par_h2 =
((coeff_array[25usize] as i32) << 4i32 | coeff_array[26usize] as i32 >> 4i32) as u16;
calib.par_h3 = coeff_array[28usize] as i8;
calib.par_h4 = coeff_array[29usize] as i8;
calib.par_h5 = coeff_array[30usize] as i8;
calib.par_h6 = coeff_array[31usize];
calib.par_h7 = coeff_array[32usize] as i8;
calib.par_gh1 = coeff_array[37usize] as i8;
calib.par_gh2 =
((coeff_array[36usize] as i32) << 8i32 | coeff_array[35usize] as i32) as i16;
calib.par_gh3 = coeff_array[38usize] as i8;
calib.res_heat_range =
(I2CUtil::read_byte::<I2CX>(i2c, dev_id.addr(), BME680_ADDR_RES_HEAT_RANGE_ADDR)?
& 0x30)
/ 16;
calib.res_heat_val =
I2CUtil::read_byte::<I2CX>(i2c, dev_id.addr(), BME680_ADDR_RES_HEAT_VAL_ADDR)? as i8;
calib.range_sw_err =
(I2CUtil::read_byte::<I2CX>(i2c, dev_id.addr(), BME680_ADDR_RANGE_SW_ERR_ADDR)?
& BME680_RSERROR_MSK)
/ 16;
Ok(calib)
}
fn set_gas_config(
&mut self,
gas_sett: GasSett,
) -> Result<(), <I2C as Read>::Error, <I2C as Write>::Error> {
if self.power_mode != PowerMode::ForcedMode {
return Err(Error::DefinePwrMode);
}
let reg: [(u8, u8); 2] = [
(
BME680_RES_HEAT0_ADDR,
Calc::calc_heater_res(
&self.calib,
gas_sett.ambient_temperature,
gas_sett.heatr_temp.unwrap_or(0),
),
),
(
BME680_GAS_WAIT0_ADDR,
Calc::calc_heater_dur(gas_sett.heatr_dur.unwrap_or_else(|| Duration::from_secs(0))),
),
];
self.gas_sett.nb_conv = 0;
self.bme680_set_regs(®)
}
fn get_gas_config(&mut self) -> Result<GasSett, <I2C as Read>::Error, <I2C as Write>::Error> {
let heatr_temp = Some(I2CUtil::read_byte(
&mut self.i2c,
self.dev_id.addr(),
BME680_ADDR_SENS_CONF_START,
)? as u16);
let heatr_dur_ms = I2CUtil::read_byte(
&mut self.i2c,
self.dev_id.addr(),
BME680_ADDR_GAS_CONF_START,
)? as u64;
let gas_sett = GasSett {
heatr_temp,
heatr_dur: Some(Duration::from_millis(heatr_dur_ms)),
..Default::default()
};
Ok(gas_sett)
}
pub fn get_sensor_data(
&mut self,
delay: &mut D,
) -> Result<(FieldData, FieldDataCondition), <I2C as Read>::Error, <I2C as Write>::Error> {
let mut buff: [u8; BME680_FIELD_LENGTH] = [0; BME680_FIELD_LENGTH];
debug!("Buf {:?}, len: {}", buff, buff.len());
let mut data: FieldData = Default::default();
const TRIES: u8 = 10;
for _ in 0..TRIES {
I2CUtil::read_bytes(
&mut self.i2c,
self.dev_id.addr(),
BME680_FIELD0_ADDR,
&mut buff,
)?;
debug!("Field data read {:?}, len: {}", buff, buff.len());
data.status = buff[0] & BME680_NEW_DATA_MSK;
data.gas_index = buff[0] & BME680_GAS_INDEX_MSK;
data.meas_index = buff[1];
let adc_pres = (buff[2] as u32).wrapping_mul(4096)
| (buff[3] as u32).wrapping_mul(16)
| (buff[4] as u32).wrapping_div(16);
let adc_temp = (buff[5] as u32).wrapping_mul(4096)
| (buff[6] as u32).wrapping_mul(16)
| (buff[7] as u32).wrapping_div(16);
let adc_hum = ((buff[8] as u32).wrapping_mul(256) | buff[9] as u32) as u16;
let adc_gas_res =
((buff[13] as u32).wrapping_mul(4) | (buff[14] as u32).wrapping_div(64)) as u16;
let gas_range = buff[14] & BME680_GAS_RANGE_MSK;
data.status |= buff[14] & BME680_GASM_VALID_MSK;
data.status |= buff[14] & BME680_HEAT_STAB_MSK;
if data.status & BME680_NEW_DATA_MSK != 0 {
let (temp, t_fine) =
Calc::calc_temperature(&self.calib, adc_temp, self.tph_sett.temperature_offset);
debug!(
"adc_temp: {} adc_pres: {} adc_hum: {} adc_gas_res: {}, t_fine: {}",
adc_temp, adc_pres, adc_hum, adc_gas_res, t_fine
);
data.temperature = temp;
data.pressure = Calc::calc_pressure(&self.calib, t_fine, adc_pres);
data.humidity = Calc::calc_humidity(&self.calib, t_fine, adc_hum);
data.gas_resistance =
Calc::calc_gas_resistance(&self.calib, adc_gas_res, gas_range);
return Ok((data, FieldDataCondition::NewData));
}
delay.delay_ms(BME680_POLL_PERIOD_MS);
}
Ok((data, FieldDataCondition::Unchanged))
}
}