diff --git a/src/lib.rs b/src/lib.rs index 5f779a1..bc95ed1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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> { + /// # 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::(), + /// pressure.get::() + /// ); + /// # Ok(()) + /// # } + /// ``` + pub async fn temperature_pressure( + &mut self, + ) -> Result<(ThermodynamicTemperature, Pressure), Error> { + // 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 @@ -605,12 +649,16 @@ where /// # } /// ``` pub async fn pressure(&mut self) -> Result> { - // 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 @@ -627,22 +675,7 @@ where /// # } /// ``` pub async fn measure(&mut self) -> Result> { - // 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, @@ -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(), @@ -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(); } @@ -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; diff --git a/src/sync.rs b/src/sync.rs index 0729b25..7f631e1 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -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> { + /// # 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::(), + /// pressure.get::() + /// ); + /// # Ok(()) + /// # } + /// ``` + pub fn temperature_pressure( + &mut self, + ) -> Result<(ThermodynamicTemperature, Pressure), Error> { + // 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 @@ -238,12 +284,15 @@ where /// # } /// ``` pub fn pressure(&mut self) -> Result> { - // 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 @@ -260,21 +309,7 @@ where /// # } /// ``` pub fn measure(&mut self) -> Result> { - // 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, @@ -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(), @@ -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(); } @@ -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;