bmp280_ehal/
lib.rs

1//! A platform agnostic driver to interface with the BMP280 (pressure sensor)
2//!
3//! This driver is built using [`embedded-hal`] traits.
4
5#![deny(missing_docs)]
6#![deny(warnings)]
7#![no_std]
8
9extern crate embedded_hal as ehal;
10
11use core::fmt;
12
13/// The default address for the BMP280
14const DEFAULT_ADDRESS: u8 = 0x76;
15
16/// BMP280 driver
17pub struct BMP280<I2C: ehal::blocking::i2c::WriteRead> {
18    com: I2C,
19    addr: u8,
20    // Temperature compensation
21    dig_t1: u16,
22    dig_t2: i16,
23    dig_t3: i16,
24    t_fine: i32,
25    // Pressure calibration
26    dig_p1: u16,
27    dig_p2: i16,
28    dig_p3: i16,
29    dig_p4: i16,
30    dig_p5: i16,
31    dig_p6: i16,
32    dig_p7: i16,
33    dig_p8: i16,
34    dig_p9: i16,
35}
36
37impl<I2C: ehal::blocking::i2c::WriteRead> BMP280<I2C> {
38    /// Creates new BMP280 driver with the specified address
39    pub fn new_with_address<E>(i2c: I2C, addr: u8) -> Result<BMP280<I2C>, E>
40    where
41        I2C: ehal::blocking::i2c::WriteRead<Error = E>,
42    {
43        let mut chip = BMP280 {
44            com: i2c,
45            addr,
46            dig_t1: 0,
47            dig_t2: 0,
48            dig_t3: 0,
49            t_fine: 0,
50            dig_p1: 0,
51            dig_p2: 0,
52            dig_p3: 0,
53            dig_p4: 0,
54            dig_p5: 0,
55            dig_p6: 0,
56            dig_p7: 0,
57            dig_p8: 0,
58            dig_p9: 0,
59        };
60
61        if chip.id() == 0x58 {
62            chip.read_calibration();
63        }
64
65        Ok(chip)
66    }
67
68    /// Create a new BMP280 driver with the default address
69    pub fn new<E>(i2c: I2C) -> Result<BMP280<I2C>, E>
70    where
71        I2C: ehal::blocking::i2c::WriteRead<Error = E>
72    {
73        Self::new_with_address(i2c, DEFAULT_ADDRESS)
74    }
75}
76
77impl<I2C: ehal::blocking::i2c::WriteRead> BMP280<I2C> {
78    fn read_calibration(&mut self) {
79        let mut data: [u8; 24] = [0; 24];
80        let _ = self
81            .com
82            .write_read(self.addr, &[Register::calib00 as u8], &mut data);
83
84        self.dig_t1 = ((data[1] as u16) << 8) | (data[0] as u16);
85        self.dig_t2 = ((data[3] as i16) << 8) | (data[2] as i16);
86        self.dig_t3 = ((data[5] as i16) << 8) | (data[4] as i16);
87
88        self.dig_p1 = ((data[7] as u16) << 8) | (data[6] as u16);
89        self.dig_p2 = ((data[9] as i16) << 8) | (data[8] as i16);
90        self.dig_p3 = ((data[11] as i16) << 8) | (data[10] as i16);
91        self.dig_p4 = ((data[13] as i16) << 8) | (data[12] as i16);
92        self.dig_p5 = ((data[15] as i16) << 8) | (data[14] as i16);
93        self.dig_p6 = ((data[17] as i16) << 8) | (data[16] as i16);
94        self.dig_p7 = ((data[19] as i16) << 8) | (data[18] as i16);
95        self.dig_p8 = ((data[21] as i16) << 8) | (data[20] as i16);
96        self.dig_p9 = ((data[23] as i16) << 8) | (data[22] as i16);
97    }
98
99    /// Reads and returns pressure
100    pub fn pressure(&mut self) -> f64 {
101        let mut data: [u8; 6] = [0, 0, 0, 0, 0, 0];
102        let _ = self
103            .com
104            .write_read(self.addr, &[Register::press as u8], &mut data);
105        let press = (data[0] as u32) << 12 | (data[1] as u32) << 4 | (data[2] as u32) >> 4;
106
107        let mut var1 = ((self.t_fine as f64) / 2.0) - 64000.0;
108        let mut var2 = var1 * var1 * (self.dig_p6 as f64) / 32768.0;
109        var2 += var1 * (self.dig_p5 as f64) * 2.0;
110        var2 = (var2 / 4.0) + ((self.dig_p4 as f64) * 65536.0);
111        var1 = ((self.dig_p3 as f64) * var1 * var1 / 524288.0 + (self.dig_p2 as f64) * var1)
112            / 524288.0;
113        var1 = (1.0 + var1 / 32768.0) * (self.dig_p1 as f64);
114        let mut pressure = 1048576.0 - (press as f64);
115        if var1 != 0.0 {
116            pressure = (pressure - (var2 / 4096.0)) * 6250.0 / var1;
117            var1 = (self.dig_p9 as f64) * pressure * pressure / 2147483648.0;
118            var2 = pressure * (self.dig_p8 as f64) / 32768.0;
119            pressure += (var1 + var2 + (self.dig_p7 as f64)) / 16.0;
120        }
121        pressure
122    }
123
124    /// Reads and returns pressure and resets con
125    pub fn pressure_one_shot(&mut self) -> f64 {
126        let pressure = self.pressure();
127        self.set_control(Control {
128            osrs_t: Oversampling::x2,
129            osrs_p: Oversampling::x16,
130            mode: PowerMode::Forced,
131        });
132
133        pressure
134    }
135
136    /// Reads and returns temperature
137    pub fn temp(&mut self) -> f64 {
138        let mut data: [u8; 6] = [0, 0, 0, 0, 0, 0];
139        let _ = self
140            .com
141            .write_read(self.addr, &[Register::press as u8], &mut data);
142        let _pres = (data[0] as u32) << 12 | (data[1] as u32) << 4 | (data[2] as u32) >> 4;
143        let temp = (data[3] as u32) << 12 | (data[4] as u32) << 4 | (data[5] as u32) >> 4;
144
145        let v1 = ((temp as f64) / 16384.0 - (self.dig_t1 as f64) / 1024.0) * (self.dig_t2 as f64);
146        let v2 = (((temp as f64) / 131072.0 - (self.dig_t1 as f64) / 8192.0)
147            * ((temp as f64) / 131072.0 - (self.dig_t1 as f64) / 8192.0))
148            * (self.dig_t3 as f64);
149        self.t_fine = (v1 + v2) as i32;
150
151        (v1 + v2) / 5120.0
152    }
153
154    /// Returns current config
155    pub fn config(&mut self) -> Config {
156        let config = self.read_byte(Register::config);
157        let t_sb = match (config & (0b111 << 5)) >> 5 {
158            x if x == Standby::ms0_5 as u8 => Standby::ms0_5,
159            x if x == Standby::ms62_5 as u8 => Standby::ms62_5,
160            x if x == Standby::ms125 as u8 => Standby::ms125,
161            x if x == Standby::ms250 as u8 => Standby::ms250,
162            x if x == Standby::ms500 as u8 => Standby::ms500,
163            x if x == Standby::ms1000 as u8 => Standby::ms1000,
164            x if x == Standby::ms2000 as u8 => Standby::ms2000,
165            x if x == Standby::ms4000 as u8 => Standby::ms4000,
166            _ => Standby::unknown,
167        };
168        let filter = match (config & (0b111 << 2)) >> 2 {
169            x if x == Filter::off as u8 => Filter::off,
170            x if x == Filter::c2 as u8 => Filter::c2,
171            x if x == Filter::c4 as u8 => Filter::c4,
172            x if x == Filter::c8 as u8 => Filter::c8,
173            x if x == Filter::c16 as u8 => Filter::c16,
174            _ => Filter::unknown,
175        };
176        Config {
177            t_sb,
178            filter,
179        }
180    }
181
182    /// Sets configuration
183    pub fn set_config(&mut self, new: Config) {
184        let config: u8 = 0x00;
185        let t_sb = (new.t_sb as u8) << 5;
186        let filter = (new.filter as u8) << 2;
187        self.write_byte(Register::config, config | t_sb | filter);
188    }
189
190    /// Sets control
191    pub fn set_control(&mut self, new: Control) {
192        let osrs_t: u8 = (new.osrs_t as u8) << 5;
193        let osrs_p: u8 = (new.osrs_p as u8) << 2;
194        let control: u8 = osrs_t | osrs_p | (new.mode as u8);
195        self.write_byte(Register::ctrl_meas, control);
196    }
197
198    /// Returns control
199    pub fn control(&mut self) -> Control {
200        let config = self.read_byte(Register::ctrl_meas);
201        let osrs_t = match (config & (0b111 << 5)) >> 5 {
202            x if x == Oversampling::skipped as u8 => Oversampling::skipped,
203            x if x == Oversampling::x1 as u8 => Oversampling::x1,
204            x if x == Oversampling::x2 as u8 => Oversampling::x2,
205            x if x == Oversampling::x4 as u8 => Oversampling::x4,
206            x if x == Oversampling::x8 as u8 => Oversampling::x8,
207            _ => Oversampling::x16,
208        };
209        let osrs_p = match (config & (0b111 << 2)) >> 2 {
210            x if x == Oversampling::skipped as u8 => Oversampling::skipped,
211            x if x == Oversampling::x1 as u8 => Oversampling::x1,
212            x if x == Oversampling::x2 as u8 => Oversampling::x2,
213            x if x == Oversampling::x4 as u8 => Oversampling::x4,
214            x if x == Oversampling::x8 as u8 => Oversampling::x8,
215            _ => Oversampling::x16,
216        };
217        let mode = match config & 0b11 {
218            x if x == PowerMode::Sleep as u8 => PowerMode::Sleep,
219            x if x == PowerMode::Forced as u8 => PowerMode::Forced,
220            x if x == PowerMode::Normal as u8 => PowerMode::Normal,
221            _ => PowerMode::Forced,
222        };
223
224        Control {
225            osrs_t,
226            osrs_p,
227            mode,
228        }
229    }
230
231    /// Returns device status
232    pub fn status(&mut self) -> Status {
233        let status = self.read_byte(Register::status);
234        Status {
235            measuring: 0 != (status & 0b00001000),
236            im_update: 0 != (status & 0b00000001),
237        }
238    }
239
240    /// Returns device id
241    pub fn id(&mut self) -> u8 {
242        self.read_byte(Register::id)
243    }
244
245    /// Software reset, emulates POR
246    pub fn reset(&mut self) {
247        self.write_byte(Register::reset, 0xB6); // Magic from documentation
248    }
249
250    fn write_byte(&mut self, reg: Register, byte: u8) {
251        let mut buffer = [0];
252        let _ = self
253            .com
254            .write_read(self.addr, &[reg as u8, byte], &mut buffer);
255    }
256
257    fn read_byte(&mut self, reg: Register) -> u8 {
258        let mut data: [u8; 1] = [0];
259        let _ = self.com.write_read(self.addr, &[reg as u8], &mut data);
260        data[0]
261    }
262}
263
264#[derive(Debug, Copy, Clone)]
265/// Control
266pub struct Control {
267    /// Temperature oversampling
268    pub osrs_t: Oversampling,
269    /// Pressure oversampling
270    pub osrs_p: Oversampling,
271    /// Powermode
272    pub mode: PowerMode,
273}
274
275#[derive(Debug, Copy, Clone)]
276#[allow(non_camel_case_types)]
277/// Standby time in ms
278pub enum Standby {
279    /// ms0_5
280    ms0_5 = 0b000,
281    /// ms62_5
282    ms62_5 = 0b001,
283    /// ms125_5
284    ms125 = 0b010,
285    /// ms250
286    ms250 = 0b011,
287    /// ms500
288    ms500 = 0b100,
289    /// ms1000
290    ms1000 = 0b101,
291    /// ms2000
292    ms2000 = 0b110,
293    /// ms4000
294    ms4000 = 0b111,
295    /// unknown
296    unknown,
297}
298
299#[derive(Debug, Copy, Clone)]
300#[allow(non_camel_case_types)]
301/// The time constant of IIR filter
302pub enum Filter {
303    /// off
304    off = 0x00,
305    /// c2
306    c2 = 0x01,
307    /// c4
308    c4 = 0x02,
309    /// c8
310    c8 = 0x03,
311    /// c16
312    c16 = 0x04,
313    /// unknown
314    unknown,
315}
316
317/// Configuration register, sets the rate, filter and interface options
318/// of the device. Note that writing to this register while device in normal
319/// mode may be ignored. Writes in sleep mode are not ignored.
320///
321/// spi3w_en is intentionally left out of this implementation.
322#[derive(Debug, Copy, Clone)]
323pub struct Config {
324    /// Controls inactive duration in normal mode
325    pub t_sb: Standby,
326    /// Controls the time constant of IIR filter
327    pub filter: Filter,
328}
329
330/// Status
331#[derive(Debug, Copy, Clone)]
332pub struct Status {
333    /// measuring
334    measuring: bool,
335    /// im update
336    im_update: bool,
337}
338
339impl fmt::Display for Status {
340    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
341        write!(
342            f,
343            "conversion is running: {}, NVM data being copied: {}",
344            self.measuring, self.im_update
345        )
346    }
347}
348
349#[derive(Debug, Copy, Clone)]
350#[allow(non_camel_case_types)]
351/// Oversampling
352pub enum Oversampling {
353    /// skipped
354    skipped = 0b000,
355    /// x1
356    x1 = 0b001,
357    /// x2
358    x2 = 0b010,
359    /// x4
360    x4 = 0b011,
361    /// x8
362    x8 = 0b100,
363    /// x16
364    x16 = 0b101,
365}
366
367#[derive(Debug, Copy, Clone)]
368/// PowerMode
369pub enum PowerMode {
370    /// Sleep
371    Sleep = 0b00,
372    /// Forced
373    Forced = 0b01,
374    /// Normal
375    Normal = 0b11,
376}
377
378#[allow(non_camel_case_types)]
379enum Register {
380    id = 0xD0,
381    reset = 0xE0,
382    status = 0xF3,
383    ctrl_meas = 0xF4,
384    config = 0xF5,
385    press = 0xF7,
386    calib00 = 0x88,
387}