Skip to content

Commit fe52385

Browse files
committed
Added ability to store a reference pressure for altitude calculations
1 parent d35e60e commit fe52385

File tree

1 file changed

+94
-14
lines changed

1 file changed

+94
-14
lines changed

src/lib.rs

+94-14
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
//! # Ok(())
2323
//! # }
2424
//! ```
25+
//!
2526
//! # Datasheet
2627
//! The [BMP390 Datasheet](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp390-ds002.pdf)
2728
//! contains detailed information about the sensor's features, electrical characteristics, and registers. This package
@@ -34,7 +35,7 @@ use embedded_hal_async::{delay::DelayNs, i2c::I2c};
3435
use libm::powf;
3536
use uom::si::f32::{Length, Pressure, ThermodynamicTemperature};
3637
use uom::si::length::{foot, meter};
37-
use uom::si::pressure::{millibar, pascal};
38+
use uom::si::pressure::{hectopascal, pascal};
3839
use uom::si::thermodynamic_temperature::degree_celsius;
3940

4041
mod registers;
@@ -397,7 +398,47 @@ impl Configuration {
397398
}
398399
}
399400

400-
/// BMP390 barometer driver.
401+
/// A driver for the BMP390 pressure sensor over any [`I2c`] implementation.
402+
///
403+
/// This driver utilizes [`uom`] to provide automatic, type-safe, and zero-cost units of measurement. Measurements can
404+
/// be retrieved with [`Bmp390::measure`], which returns a [`Measurement`] struct containing the pressure, temperature,
405+
/// and altitude. The altitude is calculated based on the pressure and a reference pressure, which can be set with
406+
/// [`Bmp390::set_reference_pressure`]. If the reference pressure is not set, the altitude will be calculated based on
407+
/// the standard atmospheric pressure at sea level, 1013.25 hPa.
408+
///
409+
/// # Example
410+
/// ```no_run
411+
/// # use embedded_hal_mock::eh1::{delay::NoopDelay, i2c::Mock};
412+
/// # async fn run() -> Result<(), bmp390::Error<embedded_hal_async::i2c::ErrorKind>> {
413+
/// use bmp390::Bmp390;
414+
/// let config = bmp390::Configuration::default();
415+
/// # let i2c = embedded_hal_mock::eh1::i2c::Mock::new(&[]);
416+
/// # let delay = embedded_hal_mock::eh1::delay::NoopDelay::new();
417+
/// let mut sensor = Bmp390::try_new(i2c, bmp390::Address::Up, delay, &config).await?;
418+
/// let measurement = sensor.measure().await?;
419+
/// defmt::info!("Measurement: {}", measurement);
420+
/// # Ok(())
421+
/// # }
422+
/// ```
423+
///
424+
/// A reference pressure can be set to calculate the altitude relative to a different reference point:
425+
/// ```no_run
426+
/// # use embedded_hal_mock::eh1::{delay::NoopDelay, i2c::Mock};
427+
/// # async fn run() -> Result<(), bmp390::Error<embedded_hal_async::i2c::ErrorKind>> {
428+
/// # use bmp390::Bmp390;
429+
/// # let config = bmp390::Configuration::default();
430+
/// # let i2c = embedded_hal_mock::eh1::i2c::Mock::new(&[]);
431+
/// # let delay = embedded_hal_mock::eh1::delay::NoopDelay::new();
432+
/// # let mut sensor = Bmp390::try_new(i2c, bmp390::Address::Up, delay, &config).await?;
433+
/// let measurement = sensor.measure().await?;
434+
/// sensor.set_reference_pressure(measurement.pressure);
435+
///
436+
/// // Some time later...
437+
/// let measurement = sensor.measure().await?;
438+
/// defmt::info!("Altitude: {}", measurement.altitude.get::<uom::si::length::meter>());
439+
/// # Ok(())
440+
/// # }
441+
/// ```
401442
pub struct Bmp390<I> {
402443
/// The I2C bus the barometer is connected to.
403444
i2c: I,
@@ -407,6 +448,13 @@ pub struct Bmp390<I> {
407448

408449
/// The calibration coefficients for the barometer to compensate temperature and pressure measurements.
409450
coefficients: CalibrationCoefficients,
451+
452+
/// The reference pressure for altitude calculations.
453+
///
454+
/// By default, this is set to the standard atmospheric pressure at sea level, 1013.25 hPa. It can be set to a
455+
/// different value using [`Bmp390::set_reference_pressure`] to calculate the altitude relative to a different
456+
/// reference point.
457+
altitude_reference: Pressure,
410458
}
411459

412460
impl<I, E> Bmp390<I>
@@ -482,6 +530,7 @@ where
482530
i2c,
483531
address,
484532
coefficients,
533+
altitude_reference: Pressure::new::<hectopascal>(1013.25),
485534
}
486535
}
487536

@@ -508,6 +557,7 @@ where
508557
Ok(measurement.pressure)
509558
}
510559

560+
/// Measures the pressure and temperature from the barometer.
511561
pub async fn measure(&mut self) -> Result<Measurement, Error<E>> {
512562
// Burst read: only address DATA_0 (pressure XLSB) and BMP390 auto-increments through DATA_5 (temperature MSB)
513563
let write = &[Register::DATA_0.into()];
@@ -529,22 +579,32 @@ where
529579
Ok(Measurement {
530580
temperature,
531581
pressure,
532-
altitude: Self::calculate_altitude(pressure, Pressure::new::<millibar>(1013.25)),
582+
altitude: self.calculate_altitude(pressure),
533583
})
534584
}
535585

536-
/// Calculate the altitude based on the pressure and calibrated pressure.
586+
/// Set the reference pressure for altitude calculations.
587+
///
588+
/// Following this, the altitude can be calculated using [`Bmp390::altitude`]. If the current pressure matches
589+
/// the reference pressure, the altitude will be 0.
590+
pub fn set_reference_pressure(&mut self, pressure: Pressure) {
591+
self.altitude_reference = pressure;
592+
}
593+
594+
/// Retrieve the latest pressure measurement and calculate the altitude based on this and the reference pressure.
537595
///
538596
/// The altitude is calculating following the [NOAA formula](https://www.weather.gov/media/epz/wxcalc/pressureAltitude.pdf).
539-
pub async fn altitude(&mut self, sea_level_pressure: Pressure) -> Result<Length, Error<E>> {
597+
pub async fn altitude(&mut self) -> Result<Length, Error<E>> {
540598
let pressure = self.pressure().await?;
541-
Ok(Self::calculate_altitude(pressure, sea_level_pressure))
599+
Ok(self.calculate_altitude(pressure))
542600
}
543601

544-
/// Calculate the altitude based on the pressure and calibrated pressure.
545-
fn calculate_altitude(pressure: Pressure, sea_level_pressure: Pressure) -> Length {
602+
/// Calculate the altitude based on the pressure and reference pressure.
603+
///
604+
/// The altitude is calculating following the [NOAA formula](https://www.weather.gov/media/epz/wxcalc/pressureAltitude.pdf).
605+
fn calculate_altitude(&self, pressure: Pressure) -> Length {
546606
Length::new::<foot>(
547-
145366.45 * (1.0 - powf((pressure / sea_level_pressure).value, 0.190284)),
607+
145366.45 * (1.0 - powf((pressure / self.altitude_reference).value, 0.190284)),
548608
)
549609
}
550610
}
@@ -553,6 +613,7 @@ where
553613
mod tests {
554614
use embedded_hal_mock::eh1::delay::{CheckedDelay, NoopDelay, Transaction as DelayTransaction};
555615
use embedded_hal_mock::eh1::i2c::{Mock, Transaction as I2cTransaction};
616+
use uom::ConstZero;
556617

557618
use super::*;
558619

@@ -572,6 +633,7 @@ mod tests {
572633
ThermodynamicTemperature::new::<degree_celsius>(25.770_746)
573634
}
574635

636+
/// The [`Measurement::altitude`] value for [`expected_pressure()`] and a reference pressure of 1013.25 hPa.
575637
fn expected_altitude() -> Length {
576638
Length::new::<meter>(248.78754)
577639
}
@@ -714,11 +776,29 @@ mod tests {
714776
let mut i2c = Mock::new(&expectations);
715777
let mut bmp390 =
716778
Bmp390::new_with_coefficients(i2c.clone(), addr, CalibrationCoefficients::default());
717-
let altitude = bmp390
718-
.altitude(Pressure::new::<millibar>(1013.25))
719-
.await
720-
.unwrap();
721-
assert_eq!(altitude.get::<meter>(), expected_altitude().get::<meter>());
779+
let altitude = bmp390.altitude().await.unwrap();
780+
assert_eq!(altitude, expected_altitude());
781+
i2c.done();
782+
}
783+
784+
#[tokio::test]
785+
async fn test_altitude_custom_reference() {
786+
let addr = Address::Up;
787+
788+
// NOTE: a pressure read requires a temperature read, so response is 6 bytes
789+
let expectations = [I2cTransaction::write_read(
790+
addr.into(),
791+
vec![Register::DATA_0.into()],
792+
PRESSURE_TEMPERATURE_BYTES.to_vec(),
793+
)];
794+
795+
let mut i2c = Mock::new(&expectations);
796+
let mut bmp390 =
797+
Bmp390::new_with_coefficients(i2c.clone(), addr, CalibrationCoefficients::default());
798+
799+
bmp390.set_reference_pressure(expected_pressure());
800+
let altitude = bmp390.altitude().await.unwrap();
801+
assert_eq!(altitude, Length::ZERO);
722802
i2c.done();
723803
}
724804

0 commit comments

Comments
 (0)