22
22
//! # Ok(())
23
23
//! # }
24
24
//! ```
25
+ //!
25
26
//! # Datasheet
26
27
//! The [BMP390 Datasheet](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp390-ds002.pdf)
27
28
//! 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};
34
35
use libm:: powf;
35
36
use uom:: si:: f32:: { Length , Pressure , ThermodynamicTemperature } ;
36
37
use uom:: si:: length:: { foot, meter} ;
37
- use uom:: si:: pressure:: { millibar , pascal} ;
38
+ use uom:: si:: pressure:: { hectopascal , pascal} ;
38
39
use uom:: si:: thermodynamic_temperature:: degree_celsius;
39
40
40
41
mod registers;
@@ -397,7 +398,47 @@ impl Configuration {
397
398
}
398
399
}
399
400
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
+ /// ```
401
442
pub struct Bmp390 < I > {
402
443
/// The I2C bus the barometer is connected to.
403
444
i2c : I ,
@@ -407,6 +448,13 @@ pub struct Bmp390<I> {
407
448
408
449
/// The calibration coefficients for the barometer to compensate temperature and pressure measurements.
409
450
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 ,
410
458
}
411
459
412
460
impl < I , E > Bmp390 < I >
@@ -482,6 +530,7 @@ where
482
530
i2c,
483
531
address,
484
532
coefficients,
533
+ altitude_reference : Pressure :: new :: < hectopascal > ( 1013.25 ) ,
485
534
}
486
535
}
487
536
@@ -508,6 +557,7 @@ where
508
557
Ok ( measurement. pressure )
509
558
}
510
559
560
+ /// Measures the pressure and temperature from the barometer.
511
561
pub async fn measure ( & mut self ) -> Result < Measurement , Error < E > > {
512
562
// Burst read: only address DATA_0 (pressure XLSB) and BMP390 auto-increments through DATA_5 (temperature MSB)
513
563
let write = & [ Register :: DATA_0 . into ( ) ] ;
@@ -529,22 +579,32 @@ where
529
579
Ok ( Measurement {
530
580
temperature,
531
581
pressure,
532
- altitude : Self :: calculate_altitude ( pressure, Pressure :: new :: < millibar > ( 1013.25 ) ) ,
582
+ altitude : self . calculate_altitude ( pressure) ,
533
583
} )
534
584
}
535
585
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.
537
595
///
538
596
/// 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 > > {
540
598
let pressure = self . pressure ( ) . await ?;
541
- Ok ( Self :: calculate_altitude ( pressure, sea_level_pressure ) )
599
+ Ok ( self . calculate_altitude ( pressure) )
542
600
}
543
601
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 {
546
606
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 ) ) ,
548
608
)
549
609
}
550
610
}
@@ -553,6 +613,7 @@ where
553
613
mod tests {
554
614
use embedded_hal_mock:: eh1:: delay:: { CheckedDelay , NoopDelay , Transaction as DelayTransaction } ;
555
615
use embedded_hal_mock:: eh1:: i2c:: { Mock , Transaction as I2cTransaction } ;
616
+ use uom:: ConstZero ;
556
617
557
618
use super :: * ;
558
619
@@ -572,6 +633,7 @@ mod tests {
572
633
ThermodynamicTemperature :: new :: < degree_celsius > ( 25.770_746 )
573
634
}
574
635
636
+ /// The [`Measurement::altitude`] value for [`expected_pressure()`] and a reference pressure of 1013.25 hPa.
575
637
fn expected_altitude ( ) -> Length {
576
638
Length :: new :: < meter > ( 248.78754 )
577
639
}
@@ -714,11 +776,29 @@ mod tests {
714
776
let mut i2c = Mock :: new ( & expectations) ;
715
777
let mut bmp390 =
716
778
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 ) ;
722
802
i2c. done ( ) ;
723
803
}
724
804
0 commit comments