From 00019284a01e14b26d76588a44f5466d1b917890 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 7 Mar 2026 16:32:34 +1300 Subject: [PATCH 1/4] Add method to get just temperature and pressure Altitude calculations accounted for ~66% of runtime on my device. Add a temperature_pressure() method to allow the user to e.g. potentially calculate altitude using a cheaper approximation --- src/lib.rs | 70 ++++++++++++++++++++++++++++++++++++++--------------- src/sync.rs | 68 ++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 99 insertions(+), 39 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5f779a1..67062a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -587,6 +587,47 @@ 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; + /// # 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 measurement = 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 +646,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 +672,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, diff --git a/src/sync.rs b/src/sync.rs index 0729b25..cfefd3e 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -220,6 +220,47 @@ 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; + /// # 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 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 +279,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 +304,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, From 8f54cfdc222c76f67d06dcbfff829596d5cf80b7 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sun, 8 Mar 2026 19:18:33 +1300 Subject: [PATCH 2/4] Add temperature_pressure() tests --- src/lib.rs | 27 +++++++++++++++++++++++---- src/sync.rs | 27 +++++++++++++++++++++++---- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 67062a4..59a3483 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -884,7 +884,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(), @@ -895,9 +895,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(); } @@ -920,6 +920,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 cfefd3e..47023a5 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -498,7 +498,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(), @@ -509,9 +509,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(); } @@ -534,6 +534,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; From 4a98ddbe10f8765b6f1fb229312dc7a9341e2884 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Mon, 9 Mar 2026 10:23:36 +1300 Subject: [PATCH 3/4] Fix doctests --- src/lib.rs | 3 ++- src/sync.rs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 59a3483..708c107 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -593,12 +593,13 @@ where /// ```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 measurement = sensor.temperature_pressure().await?; + /// let (temperature, pressure) = sensor.temperature_pressure().await?; /// defmt::info!( /// "Temperature: {} °C, Pressure: {} hPa", /// temperature.get::(), diff --git a/src/sync.rs b/src/sync.rs index 47023a5..312916c 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -225,13 +225,13 @@ where /// ```no_run /// # use embedded_hal_mock::eh1::{delay::NoopDelay, i2c::Mock}; /// # use bmp390::sync::Bmp390; - /// use uom::si::pressure::hectopascal; + /// 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 pressure = sensor.temperature_pressure()?; + /// let (temperature, pressure) = sensor.temperature_pressure()?; /// defmt::info!( /// "Temperature: {} °C, Pressure: {} hPa", /// temperature.get::(), From 2cae121dd9edb640f7c4d5897a1d7862860fe6a7 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Mon, 9 Mar 2026 10:24:06 +1300 Subject: [PATCH 4/4] cargo fmt --- src/lib.rs | 12 +++++++----- src/sync.rs | 21 +++++++++++++-------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 708c107..bc95ed1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -601,14 +601,16 @@ where /// # 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::(), + /// "Temperature: {} °C, Pressure: {} hPa", + /// temperature.get::(), /// pressure.get::() /// ); /// # Ok(()) /// # } /// ``` - pub async fn temperature_pressure(&mut self) -> Result<(ThermodynamicTemperature, Pressure), Error> { + 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]; @@ -626,7 +628,7 @@ where 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)) + Ok((temperature, pressure)) } /// Reads the pressure from the barometer as a [`Pressure`]. @@ -654,7 +656,7 @@ where /// 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. /// diff --git a/src/sync.rs b/src/sync.rs index 312916c..7f631e1 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -233,14 +233,16 @@ where /// # 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::(), + /// "Temperature: {} °C, Pressure: {} hPa", + /// temperature.get::(), /// pressure.get::() /// ); /// # Ok(()) /// # } /// ``` - pub fn temperature_pressure(&mut self) -> Result<(ThermodynamicTemperature, Pressure), Error> { + 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()]; @@ -252,11 +254,14 @@ where 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_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); + let pressure = self + .coefficients + .compensate_pressure(temperature, pressure_raw); Ok((temperature, pressure)) } @@ -285,7 +290,7 @@ where /// 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. ///