use embedded_hal::i2c::I2c;
const REG_ID: u8 = 0xD0;
const REG_RESET: u8 = 0xE0;
const REG_STATUS: u8 = 0xF3;
const REG_CTRL_MEAS: u8 = 0xF4;
const REG_CONFIG: u8 = 0xF5;
const REG_CAL_START: u8 = 0x88;
const REG_DATA: u8 = 0xF7;
const CHIP_ID: u8 = 0x58;
const RESET_CMD: u8 = 0xB6;
const CTRL_MEAS_DEFAULT: u8 = 0x25;
fn delay_ms(ms: u32) {
#[cfg(feature = "std")]
std::thread::sleep(std::time::Duration::from_millis(ms as u64));
#[cfg(not(feature = "std"))]
let _ = ms;
}
fn write_reg_u8<I2C: I2c>(i2c: &mut I2C, addr: u8, reg: u8, value: u8) -> Result<(), I2C::Error> {
i2c.write(addr, &[reg, value])
}
fn read_reg<I2C: I2c>(i2c: &mut I2C, addr: u8, reg: u8, buf: &mut [u8]) -> Result<(), I2C::Error> {
i2c.write_read(addr, &[reg], buf)
}
fn read_reg_u8<I2C: I2c>(i2c: &mut I2C, addr: u8, reg: u8) -> Result<u8, I2C::Error> {
let mut buf = [0u8; 1];
i2c.write_read(addr, &[reg], &mut buf)?;
Ok(buf[0])
}
pub const OSRS_SKIP: u8 = 0;
pub const OSRS_X1: u8 = 1;
pub const OSRS_X2: u8 = 2;
pub const OSRS_X4: u8 = 3;
pub const OSRS_X8: u8 = 4;
pub const OSRS_X16: u8 = 5;
pub const MODE_SLEEP: u8 = 0;
pub const MODE_FORCED: u8 = 1;
pub const MODE_NORMAL: u8 = 3;
pub const FILTER_OFF: u8 = 0;
pub const FILTER_2: u8 = 1;
pub const FILTER_4: u8 = 2;
pub const FILTER_8: u8 = 3;
pub const FILTER_16: u8 = 4;
pub const T_SB_0_5_MS: u8 = 0;
pub const T_SB_62_5_MS: u8 = 1;
pub const T_SB_125_MS: u8 = 2;
pub const T_SB_250_MS: u8 = 3;
pub const T_SB_500_MS: u8 = 4;
pub const T_SB_1000_MS: u8 = 5;
pub const T_SB_2000_MS: u8 = 6;
pub const T_SB_4000_MS: u8 = 7;
pub const STATUS_MEASURING: u8 = 0x08;
pub const STATUS_IM_UPDATE: u8 = 0x01;
pub struct Bmp280Minimal<I2C> {
i2c: I2C,
addr: u8,
t_fine: i32,
ctrl_meas_cache: u8,
dig_T1: u16, dig_T2: i16, dig_T3: i16,
dig_P1: u16, dig_P2: i16, dig_P3: i16,
dig_P4: i16, dig_P5: i16, dig_P6: i16,
dig_P7: i16, dig_P8: i16, dig_P9: i16,
}
impl<I2C: I2c> Bmp280Minimal<I2C> {
pub fn new(mut i2c: I2C, addr: u8) -> Result<Self, I2C::Error> {
let mut s = Self { i2c, addr, t_fine: 0, ctrl_meas_cache: CTRL_MEAS_DEFAULT,
dig_T1: 0, dig_T2: 0, dig_T3: 0,
dig_P1: 0, dig_P2: 0, dig_P3: 0,
dig_P4: 0, dig_P5: 0, dig_P6: 0,
dig_P7: 0, dig_P8: 0, dig_P9: 0,
};
s.load_calibration()?;
write_reg_u8(&mut s.i2c, s.addr, REG_CTRL_MEAS, CTRL_MEAS_DEFAULT)?;
write_reg_u8(&mut s.i2c, s.addr, REG_CONFIG, 0)?;
Ok(s)
}
fn load_calibration(&mut self) -> Result<(), I2C::Error> {
let mut raw = [0u8; 24];
read_reg(&mut self.i2c, self.addr, REG_CAL_START, &mut raw)?;
self.dig_T1 = u16::from_le_bytes([raw[0], raw[1]]);
self.dig_T2 = i16::from_le_bytes([raw[2], raw[3]]);
self.dig_T3 = i16::from_le_bytes([raw[4], raw[5]]);
self.dig_P1 = u16::from_le_bytes([raw[6], raw[7]]);
self.dig_P2 = i16::from_le_bytes([raw[8], raw[9]]);
self.dig_P3 = i16::from_le_bytes([raw[10], raw[11]]);
self.dig_P4 = i16::from_le_bytes([raw[12], raw[13]]);
self.dig_P5 = i16::from_le_bytes([raw[14], raw[15]]);
self.dig_P6 = i16::from_le_bytes([raw[16], raw[17]]);
self.dig_P7 = i16::from_le_bytes([raw[18], raw[19]]);
self.dig_P8 = i16::from_le_bytes([raw[20], raw[21]]);
self.dig_P9 = i16::from_le_bytes([raw[22], raw[23]]);
Ok(())
}
fn trigger_read_burst(&mut self) -> Result<(i32, i32), I2C::Error> {
let mut raw = [0u8; 6];
read_reg(&mut self.i2c, self.addr, REG_DATA, &mut raw)?;
let adc_P = ((raw[0] as i32) << 12) | ((raw[1] as i32) << 4) | ((raw[2] as i32) >> 4);
let adc_T = ((raw[3] as i32) << 12) | ((raw[4] as i32) << 4) | ((raw[5] as i32) >> 4);
Ok((adc_T, adc_P))
}
fn trigger_measurement(&mut self) -> Result<(), I2C::Error> {
write_reg_u8(&mut self.i2c, self.addr, REG_CTRL_MEAS, self.ctrl_meas_cache)?;
delay_ms(7);
Ok(())
}
fn compensate_temp(&mut self, adc_T: i32) -> f32 {
let T1 = self.dig_T1 as i64;
let T2 = self.dig_T2 as i64;
let T3 = self.dig_T3 as i64;
let t = adc_T as i64;
let var1 = (((t >> 3) - (T1 << 1)) * T2) >> 11;
let var2 = (((((t >> 4) - T1) * ((t >> 4) - T1)) >> 12) * T3) >> 14;
self.t_fine = (var1 + var2) as i32;
((self.t_fine as i64 * 5 + 128) >> 8) as f32 / 100.0
}
fn compensate_pressure(&mut self, adc_P: i32) -> f32 {
let tf = self.t_fine as i64;
let P1 = self.dig_P1 as i64;
let P2 = self.dig_P2 as i64;
let P3 = self.dig_P3 as i64;
let P4 = self.dig_P4 as i64;
let P5 = self.dig_P5 as i64;
let P6 = self.dig_P6 as i64;
let P7 = self.dig_P7 as i64;
let P8 = self.dig_P8 as i64;
let P9 = self.dig_P9 as i64;
let mut var1 = tf - 128000;
let mut var2 = var1 * var1 * P6;
var2 = var2 + ((var1 * P5) << 17);
var2 = var2 + (P4 << 35);
var1 = ((var1 * var1 * P3) >> 8) + ((var1 * P2) << 12);
var1 = ((((1 << 47) + var1) * P1) >> 33);
if var1 == 0 { return 0.0; }
let mut p = 1048576 - adc_P as i64;
p = (((p << 31) - var2) * 3125) / var1;
var1 = (P9 * (p >> 13) * (p >> 13)) >> 25;
var2 = (P8 * p) >> 19;
p = ((p + var1 + var2) >> 8) + (P7 << 4);
(p as f32 / 256.0) / 100.0
}
pub fn temperature(&mut self) -> Result<f32, I2C::Error> {
self.trigger_measurement()?;
let (adc_T, adc_P) = self.trigger_read_burst()?;
let _ = adc_P;
Ok(self.compensate_temp(adc_T))
}
pub fn pressure(&mut self) -> Result<f32, I2C::Error> {
self.trigger_measurement()?;
let (adc_T, adc_P) = self.trigger_read_burst()?;
self.compensate_temp(adc_T);
Ok(self.compensate_pressure(adc_P))
}
}
pub struct Bmp280Full<I2C> {
inner: Bmp280Minimal<I2C>,
osrs_t: u8,
osrs_p: u8,
mode: u8,
filter: u8,
t_sb: u8,
}
impl<I2C: I2c> Bmp280Full<I2C> {
pub fn new(i2c: I2C, addr: u8, osrs_t: u8, osrs_p: u8, mode: u8, filter: u8, t_sb: u8) -> Result<Self, I2C::Error> {
let mut inner = Bmp280Minimal::new(i2c, addr)?;
let ctrl = (osrs_t << 5) | (osrs_p << 2) | mode;
inner.ctrl_meas_cache = ctrl;
write_reg_u8(&mut inner.i2c, addr, REG_CTRL_MEAS, ctrl)?;
write_reg_u8(&mut inner.i2c, addr, REG_CONFIG, (t_sb << 5) | (filter << 2))?;
Ok(Self { inner, osrs_t, osrs_p, mode, filter, t_sb })
}
fn ctrl_meas_value(&self) -> u8 {
(self.osrs_t << 5) | (self.osrs_p << 2) | self.mode
}
fn config_value(&self) -> u8 {
(self.t_sb << 5) | (self.filter << 2)
}
pub fn configure(&mut self, osrs_t: u8, osrs_p: u8, mode: u8, filter: u8, t_sb: u8) -> Result<(), I2C::Error> {
self.osrs_t = osrs_t;
self.osrs_p = osrs_p;
self.mode = mode;
self.filter = filter;
self.t_sb = t_sb;
let ctrl = self.ctrl_meas_value();
self.inner.ctrl_meas_cache = ctrl;
let cfg = self.config_value();
write_reg_u8(&mut self.inner.i2c, self.inner.addr, REG_CTRL_MEAS, ctrl)?;
write_reg_u8(&mut self.inner.i2c, self.inner.addr, REG_CONFIG, cfg)?;
Ok(())
}
pub fn set_oversampling(&mut self, osrs_t: u8, osrs_p: u8) -> Result<(), I2C::Error> {
self.osrs_t = osrs_t;
self.osrs_p = osrs_p;
let ctrl = self.ctrl_meas_value();
self.inner.ctrl_meas_cache = ctrl;
write_reg_u8(&mut self.inner.i2c, self.inner.addr, REG_CTRL_MEAS, ctrl)
}
pub fn set_mode(&mut self, mode: u8) -> Result<(), I2C::Error> {
self.mode = mode;
let ctrl = self.ctrl_meas_value();
self.inner.ctrl_meas_cache = ctrl;
write_reg_u8(&mut self.inner.i2c, self.inner.addr, REG_CTRL_MEAS, ctrl)
}
pub fn set_filter(&mut self, coeff: u8) -> Result<(), I2C::Error> {
self.filter = coeff;
let cfg = self.config_value();
write_reg_u8(&mut self.inner.i2c, self.inner.addr, REG_CONFIG, cfg)
}
pub fn set_standby(&mut self, t_sb: u8) -> Result<(), I2C::Error> {
self.t_sb = t_sb;
let cfg = self.config_value();
write_reg_u8(&mut self.inner.i2c, self.inner.addr, REG_CONFIG, cfg)
}
pub fn status(&mut self) -> Result<u8, I2C::Error> {
read_reg_u8(&mut self.inner.i2c, self.inner.addr, REG_STATUS)
}
pub fn altitude(&mut self, sea_level_hpa: f32) -> Result<f32, I2C::Error> {
let p = self.inner.pressure()?;
Ok(44330.0 * (1.0 - libm::powf(p / sea_level_hpa, 1.0 / 5.255)))
}
pub fn sea_level_pressure(&mut self, altitude_m: f32) -> Result<f32, I2C::Error> {
let p = self.inner.pressure()?;
Ok(p / libm::powf(1.0 - altitude_m / 44330.0, 5.255))
}
pub fn chip_id(&mut self) -> Result<u8, I2C::Error> {
read_reg_u8(&mut self.inner.i2c, self.inner.addr, REG_ID)
}
pub fn reset(&mut self) -> Result<(), I2C::Error> {
write_reg_u8(&mut self.inner.i2c, self.inner.addr, REG_RESET, RESET_CMD)?;
delay_ms(2);
self.inner.load_calibration()?;
let ctrl = self.ctrl_meas_value();
let cfg = self.config_value();
write_reg_u8(&mut self.inner.i2c, self.inner.addr, REG_CTRL_MEAS, ctrl)?;
write_reg_u8(&mut self.inner.i2c, self.inner.addr, REG_CONFIG, cfg)?;
Ok(())
}
pub fn temperature(&mut self) -> Result<f32, I2C::Error> {
self.inner.temperature()
}
pub fn pressure(&mut self) -> Result<f32, I2C::Error> {
self.inner.pressure()
}
}