bmp390_rs/
lib.rs

1#![no_std]
2
3pub mod config;
4pub mod register;
5pub mod bus;
6mod calibration;
7
8pub mod testing;
9
10use core::fmt::{Debug};
11use embedded_hal_async::delay::DelayNs;
12use embedded_hal_async::i2c::SevenBitAddress;
13use crate::bus::{Bus, I2c, Spi};
14use crate::calibration::CalibrationData;
15use crate::config::Configuration;
16use crate::register::{chip_id, data, err_reg, odr, osr, pwr_ctrl, status, InvalidRegisterField, Readable, Writable};
17
18const BMP390_CHIP_ID:u8 = 0x60;
19
20#[derive(Debug)]
21pub enum Bmp390Error<BusError> {
22    Bus(BusError),
23    NotConnected,
24    UnexpectedRegisterData(InvalidRegisterField),
25}
26
27type Bmp390Result<T, BusError> = Result<T, Bmp390Error<BusError>>;
28
29type Bmp390I2c<T> = Bmp390<I2c<T>>;
30type Bmp390Spi<T> = Bmp390<Spi<T>>;
31
32pub struct Bmp390<B> {
33    bus: B,
34    calibration_data: CalibrationData,
35}
36
37impl<T> Bmp390I2c<T>
38where
39    T: embedded_hal_async::i2c::I2c,
40    I2c<T>: Bus,
41{
42    pub async fn new_i2c<D: DelayNs>(
43        i2c: T,
44        address: SevenBitAddress,
45        config: Configuration,
46        delay: &mut D) -> Bmp390Result<Self, <I2c<T> as Bus>::Error> {
47        Self::new(I2c::new(i2c, address), config, delay).await
48    }
49}
50
51impl<T> Bmp390Spi<T>
52where
53    T: embedded_hal_async::spi::SpiDevice,
54    Spi<T>: Bus,
55{
56    pub async fn new_spi<D: DelayNs>(
57        spi: T,
58        config: Configuration,
59        delay: &mut D) -> Bmp390Result<Self, <Spi<T> as Bus>::Error> {
60        Self::new(Spi::new(spi), config, delay).await
61    }
62}
63
64impl<B> Bmp390<B>
65where
66    B: Bus,
67{
68    async fn probe_ready<D: DelayNs>(bus: &mut B, delay: &mut D, attempts: u32) -> Bmp390Result<(), B::Error> {
69        for _ in 0..attempts {
70            if let Ok(id) = bus.read::<register::chip_id::ChipId>().await {
71                if id == BMP390_CHIP_ID {
72                    return Ok(())
73                }
74            }
75
76            delay.delay_ms(1).await;
77        }
78
79        Err(Bmp390Error::NotConnected)
80    }
81    async fn new<D: DelayNs>(mut bus: B, config: Configuration, delay: &mut D) -> Bmp390Result<Self, B::Error>  {
82        // The datasheet (Section 1, table 2) specifies 2ms start-up time after VDD/VDDIO > 1.8V
83        Self::probe_ready(&mut bus, delay, 5).await?;
84
85        let calibration_data = CalibrationData::new(&mut bus).await?;
86
87        bus.write::<pwr_ctrl::PwrCtrl>(&pwr_ctrl::PwrCtrlCfg {
88            press_en: config.enable_pressure,
89            temp_en: config.enable_temperature,
90            mode: config.mode,
91        }).await?;
92
93        bus.write::<osr::Osr>(&osr::OsrCfg {
94            osr_p: config.pressure_oversampling,
95            osr_t: config.temperature_oversampling,
96        }).await?;
97
98        bus.write::<odr::Odr>(&odr::OdrCfg {
99            odr_sel: config.output_data_rate
100        }).await?;
101
102        bus.write::<register::config::Config>(&register::config::ConfigFields {
103            iir_filter: config.iir_filter_coefficient
104        }).await?;
105
106        Ok(Bmp390 { bus, calibration_data })
107    }
108
109    pub async fn read<R: Readable>(&mut self) -> Result<R::Out, Bmp390Error<B::Error>> {
110        Ok(self.bus.read::<R>().await?)
111    }
112    
113    pub async fn write<W: Writable>(&mut self, v: &W::In) ->  Result<(), Bmp390Error<B::Error>> {
114        Ok(self.bus.write::<W>(v).await?)
115    }
116
117    pub async fn is_connected(&mut self) -> Bmp390Result<bool, B::Error> {
118        let id = self.bus.read::<chip_id::ChipId>().await?;
119
120        Ok(id == BMP390_CHIP_ID)
121    }
122
123    /// Returns the error flags from the ERR_REG (0x02) register.
124    ///
125    /// These flags are
126    pub async fn error_flags(&mut self) -> Bmp390Result<err_reg::ErrorFlags, B::Error> {
127        Ok(self.bus.read::<err_reg::ErrReg>().await?)
128    }
129
130    /// Returns the status from the STATUS (0x03) register.
131    pub async fn status(&mut self) -> Bmp390Result<status::StatusFlags, B::Error> {
132        Ok(self.bus.read::<status::Status>().await?)
133    }
134
135    /// Sets the power mode of the device by writing to the PwrCtrl (0x1B) register
136    ///
137    /// As described in section 3.3.4 of the datasheet, these are the valid state transitions:
138    ///
139    /// Sleep => Normal
140    ///
141    /// Normal => Sleep
142    ///
143    /// Sleep => Forced => Sleep (Forced is a transient state and the device will return to Sleep when the measurement is finished)
144    ///
145    /// The device ignores any attempt to perform an invalid state transition.
146    ///
147    /// # Examples
148    ///
149    /// ```rust, no_run
150    /// # tokio_test::block_on(async {
151    /// use bmp390_rs::register::pwr_ctrl::PowerMode;
152    /// # let mut device = bmp390_rs::testing::dummy_device().await;
153    /// device.set_mode(PowerMode::Normal).await;
154    /// # });
155    pub async fn set_mode(&mut self, mode: pwr_ctrl::PowerMode) -> Bmp390Result<(), B::Error> {
156        let mut pwr_ctrl = self.bus.read::<pwr_ctrl::PwrCtrl>().await?;
157        pwr_ctrl.mode = mode;
158        self.bus.write::<pwr_ctrl::PwrCtrl>(&pwr_ctrl).await?;
159        Ok(())
160    }
161    /// Reads the current power mode from the PwrCtrl (0x1B) register
162    ///
163    /// # Examples
164    ///
165    /// ```rust, no_run
166    /// # tokio_test::block_on(async {
167    /// # let mut device = bmp390_rs::testing::dummy_device().await;
168    /// device.mode().await;
169    /// # });
170    pub async fn mode(&mut self) -> Bmp390Result<pwr_ctrl::PowerMode, B::Error> {
171        Ok(self.bus.read::<pwr_ctrl::PwrCtrl>().await?.mode)
172    }
173
174    /// Reads the **calibrated** pressure and temperature from the data (0x04 - 0x09) registers.
175    /// This method will read data from these registers and calibrate them using the NVM-stored calibration coefficients
176    /// before returning them to the caller.
177    ///
178    /// # Examples
179    ///
180    /// ```rust, no_run
181    /// # tokio_test::block_on(async {
182    /// # let mut device = bmp390_rs::testing::dummy_device().await;
183    /// let data = device.read_sensor_data().await.unwrap();
184    /// println!("The current pressure and temperature is {} and {}", data.pressure, data.temperature);
185    /// # });
186    pub async fn read_sensor_data(&mut self) -> Bmp390Result<Measurement, B::Error> {
187        let measurement = self.bus.read::<data::Data>().await?;
188
189        let compensated_temperature =
190            self.calibration_data.compensate_temperature(measurement.temperature);
191        let compensated_pressure =
192            self.calibration_data.compensate_pressure(measurement.pressure);
193
194        Ok(Measurement {
195            pressure: compensated_pressure,
196            temperature: compensated_temperature,
197        })
198    }
199
200
201}
202
203/// Holds calibrated pressure and temperature samples.
204pub struct Measurement {
205    pub pressure: f32,
206    pub temperature: f32,
207}