#![cfg_attr(not(test), no_std)]
#[cfg(feature = "sync")]
use embedded_hal::delay::DelayNs;
#[cfg(feature="sync")]
use embedded_hal::i2c::I2c;
#[cfg(not(feature="sync"))]
use embedded_hal_async::delay::DelayNs;
#[cfg(not(feature="sync"))]
use embedded_hal_async::i2c::I2c;
use maybe_async::maybe_async;
pub const DEFAULT_SENSOR_ADDRESS: u8 = 0x76;
pub const SECONDARY_SENSOR_ADDRESS: u8 = 0x77;
const RESET_COMMAND: u8 = 0x1E;
const CONVERT_D1: u8 = 0x48;
const CONVERT_D2: u8 = 0x58;
const ADC_READ: u8 = 0x00;
const PROM_READ: u8 = 0xA0;
macro_rules! defmt {
($body:expr) => {
#[cfg(feature = "logging")]
{
use defmt::*;
$body;
}
#[cfg(not(feature = "logging"))]
{
}
};
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Measurement {
pub temperature: i64,
pub pressure: i64,
}
impl Measurement {
pub fn mbar(&self) -> f32 {
self.pressure as f32 / 10.0
}
pub fn bar(&self) -> f32 {
self.mbar() / 1000.0
}
pub fn celsius(&self) -> f32 {
self.temperature as f32 / 100.0
}
pub fn fahrenheit(&self) -> f32 {
self.celsius() * 9.0 / 5.0 + 32.0
}
}
pub struct PressureSensorDriver<I2cImpl: I2c, DelayImpl: DelayNs> {
i2c: I2cImpl,
delay: DelayImpl,
calibration: [u16; 8],
sensor_address: u8,
}
#[maybe_async]
impl<I2cImpl: I2c, DelayImpl: DelayNs> PressureSensorDriver<I2cImpl, DelayImpl> {
pub fn new(i2c: I2cImpl, delay: DelayImpl, sensor_address: u8) -> Self {
PressureSensorDriver {
i2c,
delay,
calibration: [0u16; 8],
sensor_address,
}
}
pub async fn init(&mut self) -> Result<(), I2cImpl::Error> {
defmt!(trace!("Resetting Sensor"));
self.i2c
.write(self.sensor_address, &[RESET_COMMAND])
.await?;
for i in 0..8 {
self.delay.delay_ms(3).await;
let mut buffer = [0u8; 2];
let addr = PROM_READ + i * 2;
self.i2c
.write_read(self.sensor_address, &[addr], &mut buffer)
.await?;
let v = u16::from_be_bytes(buffer);
self.calibration[i as usize] = v;
}
defmt!(trace!(
"Received Calibration Coefficients {}",
self.calibration
));
Ok(())
}
pub async fn read(&mut self) -> Result<Measurement, I2cImpl::Error> {
self.delay.delay_ms(10).await;
self.i2c.write(self.sensor_address, &[CONVERT_D1]).await?;
self.delay.delay_ms(10).await;
let mut pressure_raw = [0u8; 3];
self.i2c
.write_read(self.sensor_address, &[ADC_READ], &mut pressure_raw)
.await?;
let d1 = u32::from_be_bytes([0, pressure_raw[0], pressure_raw[1], pressure_raw[2]]);
self.delay.delay_ms(10).await;
self.i2c.write(self.sensor_address, &[CONVERT_D2]).await?;
self.delay.delay_ms(10).await;
let mut temperature_raw = [0u8; 3];
self.i2c
.write_read(self.sensor_address, &[ADC_READ], &mut temperature_raw)
.await?;
let d2: u32 = u32::from_be_bytes([
0,
temperature_raw[0],
temperature_raw[1],
temperature_raw[2],
]);
let _reserved = self.calibration[0];
let c1 = self.calibration[1];
let c2 = self.calibration[2];
let c3 = self.calibration[3];
let c4 = self.calibration[4];
let c5 = self.calibration[5];
let c6 = self.calibration[6];
let _serial_code_and_crc = self.calibration[7];
let d_t = (d2 as i64) - c5 as i64 * 2i64.pow(8);
let temperature: i64 = 2_000 + (d_t * c6 as i64) / 2i64.pow(23);
let off = c2 as i64 * 2i64.pow(16) + (c4 as i64 * d_t) / 2i64.pow(7);
let sens = c1 as i64 * 2i64.pow(15) + (c3 as i64 * d_t) / 2i64.pow(8);
let mut off2;
let mut sens2;
let t2;
if temperature < 2000 {
t2 = 3 * (d_t).pow(2) / 2i64.pow(33);
off2 = 3 * (temperature - 2000).pow(2) / 2;
sens2 = 5 * (temperature - 2000).pow(2) / 2i64.pow(3);
if temperature < -1500 {
off2 -= 7 * (temperature + 1500).pow(2);
sens2 += 4 * (temperature + 1500).pow(2);
}
} else {
t2 = 7 * (d_t).pow(2) / 2i64.pow(37);
off2 = (temperature - 2000).pow(2) / 2i64.pow(4);
sens2 = 0;
}
defmt!(trace!(
"Without second order values {} {} {}",
temperature,
off,
sens
));
let temperature = temperature - t2;
let off = off - off2;
let sens = sens - sens2;
defmt!(trace!(
"Corrected with second order values {} {} {}",
temperature,
off,
sens
));
let pressure: i64 = (d1 as i64 * sens / 2i64.pow(21) - off) / 2i64.pow(15);
defmt!(trace!(
"Got Temperature {} and Pressure {}",
temperature,
pressure
));
Ok(Measurement {
temperature,
pressure,
})
}
}
#[cfg(test)]
mod test {
use super::*;
use embedded_hal_mock::eh1::i2c::{Mock as I2cMock, Transaction as I2cTransaction};
#[tokio::test]
async fn i2c_example_from_datasheet() -> Result<(), ()> {
let expectations = [
I2cTransaction::write(0x76, vec![0x1e]),
I2cTransaction::write_read(0x76, vec![0xa0], vec![0x13, 0x37]),
I2cTransaction::write_read(0x76, vec![0xa2], 46546u16.to_be_bytes().to_vec()),
I2cTransaction::write_read(0x76, vec![0xa4], 42845u16.to_be_bytes().to_vec()),
I2cTransaction::write_read(0x76, vec![0xa6], 29751u16.to_be_bytes().to_vec()),
I2cTransaction::write_read(0x76, vec![0xa8], 29457u16.to_be_bytes().to_vec()),
I2cTransaction::write_read(0x76, vec![0xaa], 32745u16.to_be_bytes().to_vec()),
I2cTransaction::write_read(0x76, vec![0xac], 29059u16.to_be_bytes().to_vec()),
I2cTransaction::write_read(0x76, vec![0xae], 0x1337u16.to_be_bytes().to_vec()),
];
let i2c = I2cMock::new(&expectations);
let mut sensor = PressureSensorDriver::new(
i2c,
embedded_hal_mock::eh1::delay::StdSleep,
DEFAULT_SENSOR_ADDRESS,
);
sensor.init().await.unwrap();
sensor.i2c.update_expectations(&[
I2cTransaction::write(0x76, vec![0x48]),
I2cTransaction::write_read(0x76, vec![0x00], 4311550u32.to_be_bytes()[1..].to_vec()),
I2cTransaction::write(0x76, vec![0x58]),
I2cTransaction::write_read(0x76, vec![0x00], 8387300u32.to_be_bytes()[1..].to_vec()),
]);
let measurement = sensor.read().await.unwrap();
assert_eq!(measurement.temperature, 2015);
assert_eq!(measurement.pressure, 10005);
sensor.i2c.done();
Ok(())
}
#[test]
fn measurement_conversions_are_correct() {
let measurement = Measurement { temperature: 2015, pressure: 10005 };
assert_eq!(measurement.bar(), 1.0005);
assert_eq!(measurement.mbar(), 1000.5);
assert_eq!(measurement.celsius(), 20.15);
assert_eq!(measurement.fahrenheit(), 68.27);
}
}