Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 76 additions & 24 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,50 @@ where
Ok(temperature)
}

/// Measures the temperature and pressure from the barometer.
///
/// # Example
/// ```no_run
/// # use embedded_hal_mock::eh1::{delay::NoopDelay, i2c::Mock};
/// # use bmp390::Bmp390;
/// use uom::si::{pressure::hectopascal, thermodynamic_temperature::degree_celsius};
/// # async fn run() -> Result<(), bmp390::Error<embedded_hal_async::i2c::ErrorKind>> {
/// # let config = bmp390::Configuration::default();
/// # let i2c = embedded_hal_mock::eh1::i2c::Mock::new(&[]);
/// # let delay = embedded_hal_mock::eh1::delay::NoopDelay::new();
/// # let mut sensor = Bmp390::try_new(i2c, bmp390::Address::Up, delay, &config).await?;
/// let (temperature, pressure) = sensor.temperature_pressure().await?;
/// defmt::info!(
/// "Temperature: {} °C, Pressure: {} hPa",
/// temperature.get::<degree_celsius>(),
/// pressure.get::<hectopascal>()
/// );
/// # Ok(())
/// # }
/// ```
pub async fn temperature_pressure(
&mut self,
) -> Result<(ThermodynamicTemperature, Pressure), Error<E>> {
// Burst read: only address DATA_0 (pressure XLSB) and BMP390 auto-increments through DATA_5 (temperature MSB)
let write = &[Register::DATA_0.into()];
let mut read = [0; 6];
self.i2c
.write_read(self.address.into(), write, &mut read)
.await
.map_err(Error::I2c)?;

trace!("DATA = {=[u8]:#04x}", read);

// pressure is 0:2 (XLSB, LSB, MSB), temperature is 3:5 (XLSB, LSB, MSB)
let temperature = u32::from(read[3]) | u32::from(read[4]) << 8 | u32::from(read[5]) << 16;
let temperature = self.coefficients.compensate_temperature(temperature);

let pressure = u32::from(read[0]) | u32::from(read[1]) << 8 | u32::from(read[2]) << 16;
let pressure = self.coefficients.compensate_pressure(temperature, pressure);

Ok((temperature, pressure))
}

/// Reads the pressure from the barometer as a [`Pressure`].
///
/// # Example
Expand All @@ -605,12 +649,16 @@ where
/// # }
/// ```
pub async fn pressure(&mut self) -> Result<Pressure, Error<E>> {
// pressure requires temperature to compensate, so just measure both
let measurement = self.measure().await?;
Ok(measurement.pressure)
// pressure requires temperature to compensate, so we have to measure both
let (_, pressure) = self.temperature_pressure().await?;
Ok(pressure)
}

/// Measures the pressure and temperature from the barometer.
/// Measures the temperature and pressure from the barometer.
/// Altitude is then calculated using the [NOAA formula](https://www.weather.gov/media/epz/wxcalc/pressureAltitude.pdf).
///
/// This altitude calculation can be expensive on devices without floating point hardware. In this case, consider
/// calling [`temperature_pressure()`] instead and using an approximation or lookup table.
///
/// # Example
/// ```no_run
Expand All @@ -627,22 +675,7 @@ where
/// # }
/// ```
pub async fn measure(&mut self) -> Result<Measurement, Error<E>> {
// Burst read: only address DATA_0 (pressure XLSB) and BMP390 auto-increments through DATA_5 (temperature MSB)
let write = &[Register::DATA_0.into()];
let mut read = [0; 6];
self.i2c
.write_read(self.address.into(), write, &mut read)
.await
.map_err(Error::I2c)?;

trace!("DATA = {=[u8]:#04x}", read);

// pressure is 0:2 (XLSB, LSB, MSB), temperature is 3:5 (XLSB, LSB, MSB)
let temperature = u32::from(read[3]) | u32::from(read[4]) << 8 | u32::from(read[5]) << 16;
let temperature = self.coefficients.compensate_temperature(temperature);

let pressure = u32::from(read[0]) | u32::from(read[1]) << 8 | u32::from(read[2]) << 16;
let pressure = self.coefficients.compensate_pressure(temperature, pressure);
let (temperature, pressure) = self.temperature_pressure().await?;

Ok(Measurement {
temperature,
Expand Down Expand Up @@ -854,7 +887,7 @@ mod tests {
}

#[tokio::test]
async fn test_measure_reads_temperature_and_pressure() {
async fn test_reads_temperature_pressure() {
let addr = Address::Up;
let expectations = [I2cTransaction::write_read(
addr.into(),
Expand All @@ -865,9 +898,9 @@ mod tests {
let mut i2c = Mock::new(&expectations);
let mut bmp390 =
Bmp390::new_with_coefficients(i2c.clone(), addr, CalibrationCoefficients::default());
let measurement = bmp390.measure().await.unwrap();
assert_eq!(measurement.temperature, expected_temperature());
assert_eq!(measurement.pressure, expected_pressure());
let measurement = bmp390.temperature_pressure().await.unwrap();
assert_eq!(measurement.0, expected_temperature());
assert_eq!(measurement.1, expected_pressure());
i2c.done();
}

Expand All @@ -890,6 +923,25 @@ mod tests {
i2c.done();
}

#[tokio::test]
async fn test_measure_reads_temperature_pressure_altitude() {
let addr = Address::Up;
let expectations = [I2cTransaction::write_read(
addr.into(),
vec![Register::DATA_0.into()],
PRESSURE_TEMPERATURE_BYTES.to_vec(),
)];

let mut i2c = Mock::new(&expectations);
let mut bmp390 =
Bmp390::new_with_coefficients(i2c.clone(), addr, CalibrationCoefficients::default());
let measurement = bmp390.measure().await.unwrap();
assert_eq!(measurement.temperature, expected_temperature());
assert_eq!(measurement.pressure, expected_pressure());
assert_eq!(measurement.altitude, expected_altitude());
i2c.done();
}

#[tokio::test]
async fn test_altitude_custom_reference() {
let addr = Address::Up;
Expand Down
100 changes: 77 additions & 23 deletions src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,52 @@ where
Ok(temperature)
}

/// Reads temperature and pressure from the barometer.
/// # Example
/// ```no_run
/// # use embedded_hal_mock::eh1::{delay::NoopDelay, i2c::Mock};
/// # use bmp390::sync::Bmp390;
/// use uom::si::{pressure::hectopascal, thermodynamic_temperature::degree_celsius};
/// # fn run() -> Result<(), bmp390::Error<embedded_hal::i2c::ErrorKind>> {
/// # let config = bmp390::Configuration::default();
/// # let i2c = embedded_hal_mock::eh1::i2c::Mock::new(&[]);
/// # let delay = embedded_hal_mock::eh1::delay::NoopDelay::new();
/// # let mut sensor = Bmp390::try_new(i2c, bmp390::Address::Up, delay, &config)?;
/// let (temperature, pressure) = sensor.temperature_pressure()?;
/// defmt::info!(
/// "Temperature: {} °C, Pressure: {} hPa",
/// temperature.get::<degree_celsius>(),
/// pressure.get::<hectopascal>()
/// );
/// # Ok(())
/// # }
/// ```
pub fn temperature_pressure(
&mut self,
) -> Result<(ThermodynamicTemperature, Pressure), Error<E>> {
// pressure requires temperature to compensate, so just measure both
// pressure requires temperature to compensate, so just measure both
let write = &[Register::DATA_0.into()];
let mut read = [0; 6];
self.i2c
.write_read(self.address.into(), write, &mut read)
.map_err(Error::I2c)?;

trace!("DATA = {=[u8]:#04x}", read);

// pressure is 0:2 (XLSB, LSB, MSB), temperature is 3:5 (XLSB, LSB, MSB)
let temperature_raw =
u32::from(read[3]) | u32::from(read[4]) << 8 | u32::from(read[5]) << 16;
let pressure_raw = u32::from(read[0]) | u32::from(read[1]) << 8 | u32::from(read[2]) << 16;

let temperature = self.coefficients.compensate_temperature(temperature_raw);
let pressure = self
.coefficients
.compensate_pressure(temperature, pressure_raw);

Ok((temperature, pressure))
}

/// Reads the pressure from the barometer as a [`Pressure`].
///
/// # Example
Expand All @@ -238,12 +284,15 @@ where
/// # }
/// ```
pub fn pressure(&mut self) -> Result<Pressure, Error<E>> {
// pressure requires temperature to compensate, so just measure both
let measurement = self.measure()?;
Ok(measurement.pressure)
// pressure requires temperature to compensate, so we have to measure both
self.temperature_pressure().map(|(_, pressure)| pressure)
}

/// Measures the pressure and temperature from the barometer.
/// Measures the temperature and pressure from the barometer.
/// Altitude is then calculated using the [NOAA formula](https://www.weather.gov/media/epz/wxcalc/pressureAltitude.pdf).
///
/// This altitude calculation can be expensive on devices without floating point hardware. In this case, consider
/// calling [`temperature_pressure()`] instead and using an approximation or lookup table.
///
/// # Example
/// ```no_run
Expand All @@ -260,21 +309,7 @@ where
/// # }
/// ```
pub fn measure(&mut self) -> Result<Measurement, Error<E>> {
// Burst read: only address DATA_0 (pressure XLSB) and BMP390 auto-increments through DATA_5 (temperature MSB)
let write = &[Register::DATA_0.into()];
let mut read = [0; 6];
self.i2c
.write_read(self.address.into(), write, &mut read)
.map_err(Error::I2c)?;

trace!("DATA = {=[u8]:#04x}", read);

// pressure is 0:2 (XLSB, LSB, MSB), temperature is 3:5 (XLSB, LSB, MSB)
let temperature = u32::from(read[3]) | u32::from(read[4]) << 8 | u32::from(read[5]) << 16;
let temperature = self.coefficients.compensate_temperature(temperature);

let pressure = u32::from(read[0]) | u32::from(read[1]) << 8 | u32::from(read[2]) << 16;
let pressure = self.coefficients.compensate_pressure(temperature, pressure);
let (temperature, pressure) = self.temperature_pressure()?;

Ok(Measurement {
temperature,
Expand Down Expand Up @@ -468,7 +503,7 @@ mod tests {
}

#[test]
fn test_measure_reads_temperature_and_pressure() {
fn test_reads_temperature_pressure() {
let addr = Address::Up;
let expectations = [I2cTransaction::write_read(
addr.into(),
Expand All @@ -479,9 +514,9 @@ mod tests {
let mut i2c = Mock::new(&expectations);
let mut bmp390 =
Bmp390::new_with_coefficients(i2c.clone(), addr, CalibrationCoefficients::default());
let measurement = bmp390.measure().unwrap();
assert_eq!(measurement.temperature, expected_temperature());
assert_eq!(measurement.pressure, expected_pressure());
let measurement = bmp390.temperature_pressure().unwrap();
assert_eq!(measurement.0, expected_temperature());
assert_eq!(measurement.1, expected_pressure());
i2c.done();
}

Expand All @@ -504,6 +539,25 @@ mod tests {
i2c.done();
}

#[test]
fn test_measure_reads_temperature_pressure_altitude() {
let addr = Address::Up;
let expectations = [I2cTransaction::write_read(
addr.into(),
vec![Register::DATA_0.into()],
PRESSURE_TEMPERATURE_BYTES.to_vec(),
)];

let mut i2c = Mock::new(&expectations);
let mut bmp390 =
Bmp390::new_with_coefficients(i2c.clone(), addr, CalibrationCoefficients::default());
let measurement = bmp390.measure().unwrap();
assert_eq!(measurement.temperature, expected_temperature());
assert_eq!(measurement.pressure, expected_pressure());
assert_eq!(measurement.altitude, expected_altitude());
i2c.done();
}

#[test]
fn test_altitude_custom_reference() {
let addr = Address::Up;
Expand Down