1#![allow(dead_code)]
13
14extern crate i2cdev;
15extern crate i2csensors;
16extern crate byteorder;
17
18use i2csensors::{Barometer, Thermometer};
19use std::thread;
20use std::time::Duration;
21use std::error::Error;
22use i2cdev::core::I2CDevice;
23#[cfg(any(target_os = "linux", target_os = "android"))]
24use i2cdev::linux::{LinuxI2CDevice,LinuxI2CError};
25use byteorder::{ByteOrder, BigEndian, LittleEndian};
26
27pub const BMP280_I2C_ADDR: u16 = 0x77;
28
29const BMP280_PRESS_MSB: u8 = 0xF7;
30const BMP280_PRESS_LSB: u8 = 0xF8;
31const BMP280_PRESS_XLSB: u8 = 0xF9;
32
33const BMP280_TEMP_MSB: u8 = 0xFA;
34const BMP280_TEMP_LSB: u8 = 0xFB;
35const BMP280_TEMP_XLSB: u8 = 0xFC;
36
37#[derive(Copy,Clone)]
40pub enum BMP280CompensationAlgorithm{
41 B32,
42 B64,
43 Float
44}
45
46#[derive(Copy,Clone)]
47struct BMP280CalibrationCoefficients {
48 pub dig_t1: u16,
49 pub dig_t2: i16,
50 pub dig_t3: i16,
51 pub dig_p1: u16,
52 pub dig_p2: i16,
53 pub dig_p3: i16,
54 pub dig_p4: i16,
55 pub dig_p5: i16,
56 pub dig_p6: i16,
57 pub dig_p7: i16,
58 pub dig_p8: i16,
59 pub dig_p9: i16,
60}
61
62impl BMP280CalibrationCoefficients {
63 pub fn new<E: Error>(i2cdev: &mut I2CDevice<Error = E>) -> Result<BMP280CalibrationCoefficients, E> {
64let mut buf = [0_u8; 26];
66 let mut register: u8 = 0x88;
67 try!(i2cdev.write(&[register]));
68 try!(i2cdev.read(&mut buf));
69
70 Ok(BMP280CalibrationCoefficients {
71 dig_t1: LittleEndian::read_u16(&buf[0..2]),
72 dig_t2: LittleEndian::read_i16(&buf[2..4]),
73 dig_t3: LittleEndian::read_i16(&buf[4..6]),
74 dig_p1: LittleEndian::read_u16(&buf[6..8]),
75 dig_p2: LittleEndian::read_i16(&buf[8..10]),
76 dig_p3: LittleEndian::read_i16(&buf[10..12]),
77 dig_p4: LittleEndian::read_i16(&buf[12..14]),
78 dig_p5: LittleEndian::read_i16(&buf[14..16]),
79 dig_p6: LittleEndian::read_i16(&buf[16..18]),
80 dig_p7: LittleEndian::read_i16(&buf[18..20]),
81 dig_p8: LittleEndian::read_i16(&buf[20..22]),
82 dig_p9: LittleEndian::read_i16(&buf[22..24])
83 })
84}
86}
87
88#[derive(Copy,Clone)]
89pub enum BMP280PowerMode {
90 SleepMode = 0b00000000,
91 NormalMode = 0b00000011,
92 ForcedMode = 0b00000001
93}
94
95#[derive(Copy,Clone)]
101pub enum BMP280PressureOversampling {
102 Off = 0b00000000,
103 UltraLowPower = 0b00000100,
104 LowPower= 0b00001000,
105 StandardResolution = 0b00001100,
106 HighResolution = 0b00010000,
107 UltraHighResolution = 0b00010100
108}
109
110#[derive(Copy,Clone)]
120pub enum BMP280TemperatureOversampling {
121 Off,
122 x1 = 0b00100000,
123 x2 = 0b01000000,
124 x4 = 0b01100000,
125 x8 = 0b10000000,
126 x16 = 0b10100000
127}
128
129#[derive(Copy,Clone)]
131pub enum BMP280FilterCoefficient {
132 Off = 0b00000100,
133 Low = 0b00001000,
134 Medium = 0b00001100,
135 High = 0b00010000,
136 UltraHigh = 0b00010100
137}
138
139#[derive(Copy,Clone)]
140pub enum BMP280Timing {
141 ms0_5 = 0b00000000,
142 ms62_5 = 0b00100000,
143 ms125 = 0b01000000,
144 ms250 = 0b01100000,
145 ms500 = 0b10000000,
146 ms1000 = 0b10100000,
147 ms2000 = 0b11000000,
148 ms4000 = 0b11100000
149}
150
151#[derive(Copy,Clone)]
153pub struct BMP280Settings {
154 pub compensation: BMP280CompensationAlgorithm,
155 pub t_sb: BMP280Timing,
156 pub iir_filter_coeff: BMP280FilterCoefficient,
157 pub osrs_t: BMP280TemperatureOversampling,
158 pub osrs_p: BMP280PressureOversampling,
159 pub power_mode: BMP280PowerMode
160}
161
162#[cfg(any(target_os = "linux", target_os = "android"))]
163pub fn get_linux_bmp280_i2c_device() -> Result<LinuxI2CDevice, LinuxI2CError> {
164 match LinuxI2CDevice::new("/dev/i2c-1", BMP280_I2C_ADDR) {
165 Ok(device) => Ok(device),
166 Err(e) => Err(e)
167 }
168}
169
170#[derive(Copy,Clone)]
171pub struct BMP280<T: I2CDevice + Sized> {
172 pub barometer: T,
173 coeff: BMP280CalibrationCoefficients,
174 t_fine: i32,
175 algorithm: BMP280CompensationAlgorithm
176}
177
178impl<T> BMP280<T>
181 where T: I2CDevice + Sized
182{
183 #[cfg(any(target_os = "linux", target_os = "android"))]
184 pub fn new(mut i2cdev: T, settings: BMP280Settings) -> Result<BMP280<T>, T::Error> {
185 let id = try!(i2cdev.smbus_read_byte_data(0xD0));
186 assert!(id == 0x58);
187
188 let measurement_control = 0_u8 | settings.osrs_t as u8 | settings.osrs_p as u8 | settings.power_mode as u8;
189 let config = 0_u8 | settings.t_sb as u8 | settings.iir_filter_coeff as u8;
190
191 try!(i2cdev.smbus_write_byte_data(0xF4, measurement_control));
192 try!(i2cdev.smbus_write_byte_data(0xF5, config));
193
194 let coefficients = try!(BMP280CalibrationCoefficients::new(&mut i2cdev));
195
196 Ok(BMP280 {
197 barometer: i2cdev,
198 coeff: coefficients,
199 t_fine: 0,
200 algorithm: settings.compensation
201 })
202 }
203
204 #[cfg(any(target_os = "linux", target_os = "android"))]
205 pub fn reset(&mut self) -> Result<(), T::Error> {
206 try!(self.barometer.smbus_write_byte_data(0xE0, 0xB6));
207 Ok(())
208 }
209
210 #[cfg(any(target_os = "linux", target_os = "android"))]
211 pub fn set_mode(&mut self, mode: BMP280PowerMode) -> Result<(), T::Error> {
212 let mut ctrl_meas = try!(self.barometer.smbus_read_byte_data(0xF4));
213 ctrl_meas = ctrl_meas & 0b11111100;
214 ctrl_meas = ctrl_meas | mode as u8;
215 try!(self.barometer.smbus_write_byte_data(0xF4, ctrl_meas));
216 Ok(())
217 }
218
219 fn compensate_temperature_32b(&mut self, adc_t: i32) -> i32 {
220 let (mut var1, mut var2, mut t): (i32, i32, i32);
221 var1 = ((((adc_t >> 3) - ((self.coeff.dig_t1 as i32) <<1))) * (self.coeff.dig_t2 as i32)) >> 11;
222 var2 = (((((adc_t>>4) - (self.coeff.dig_t1 as i32)) * ((adc_t>>4) - (self.coeff.dig_t1 as i32))) >> 12) *
223 (self.coeff.dig_t3 as i32)) >> 14;
224 self.t_fine = var1 + var2;
225 t = (self.t_fine * 5 + 128) >> 8;
226 t
227 }
228
229 fn compensate_pressure_32b(&mut self, adc_p: i32) -> u32 {
230 let (mut var1, mut var2, mut p): (i32, i32, u32);
231
232 var1 = ((self.t_fine as i32) >> 1) - 64000;
233 var2 = (((var1 >> 2) * (var1 >> 2)) >> 11) * (self.coeff.dig_p6 as i32);
234 var2 = var2 + ((var1 * (self.coeff.dig_p5 as i32)) << 1);
235 var2 = (var2 >> 2) + ((self.coeff.dig_p4 as i32) << 16);
236 var1 = ((((self.coeff.dig_p3 as i32) * (((var1 >> 2) * (var1 >> 2)) >> 13)) >> 3) + (((self.coeff.dig_p2 as i32) * var1) >> 1)) >> 18;
237 var1 = ((((32768 + var1)) * (self.coeff.dig_p1 as i32)) >> 15);
238 if var1 == 0 {
239 return 0; }
241 p = (((1048576 - adc_p) - (var2 >> 12)) as u32) * 3125;
242 if p < 0x80000000 {
243 p = (p << 1) / (var1 as u32);
244 } else {
245 p = (p / var1 as u32) * 2;
246 }
247 var1 = ((self.coeff.dig_p9 as i32) * ((((p >> 3) * (p >> 3)) >> 13) as i32)) >> 12;
248 var2 = (((p >> 2) as i32) * (self.coeff.dig_p8) as i32) >> 13;
249 p = ((p as i32) + ((var1 + var2 + (self.coeff.dig_p7 as i32)) >> 4)) as u32;
250 p
251 }
252
253 fn compensate_pressure_64b(&mut self, adc_p: i32) -> u32 {
254 let (mut var1, mut var2, mut p): (i64, i64, i64);
255 var1 = (self.t_fine as i64) - 128000;
256 var2 = var1 * var1 * (self.coeff.dig_p6 as i64);
257 var2 = var2 + ((var1 * (self.coeff.dig_p5 as i64)) << 17);
258 var2 = var2 + ((self.coeff.dig_p4 as i64) << 35);
259 var1 = ((var1 * var1 * (self.coeff.dig_p3 as i64))>>8) + ((var1 * (self.coeff.dig_p2 as i64)) << 12);
260 var1 = ((1_i64 << 47) + var1)*(self.coeff.dig_p1 as i64)>>33;
261 if var1 == 0 {
262 return 0; }
264 p = 1048576 - adc_p as i64;
265 p = (((p << 31)-var2) * 3125) / var1;
266 var1 = ((self.coeff.dig_p9 as i64) * (p >> 13) * (p >> 13)) >> 25;
267 var2 = ((self.coeff.dig_p8 as i64) * p) >> 19;
268 p = ((p + var1 + var2) >> 8) + ((self.coeff.dig_p7 as i64) << 4);
269 p as u32
270 }
271
272 fn compensate_temperature_float(&mut self, adc_t: i32) -> f64 {
273 let (mut var1, mut var2, mut t): (f64, f64, f64);
274 var1 = ((adc_t as f64)/16384.0 - (self.coeff.dig_t1 as f64)/1024.0) * (self.coeff.dig_t2 as f64);
275 var2 = (((adc_t as f64)/131072.0 - (self.coeff.dig_t1 as f64)/8192.0) *
276 ((adc_t as f64)/131072.0 - (self.coeff.dig_t1 as f64)/8192.0)) * (self.coeff.dig_t3 as f64);
277 self.t_fine = (var1 + var2) as i32;
278 t = (var1 + var2) / 5120.0;
279 t
280 }
281
282 fn compensate_pressure_float(&mut self, adc_p: i32) -> f64 {
283 let (mut var1, mut var2, mut p): (f64, f64, f64);
284
285 var1 = ((self.t_fine as f64)/2.0) - 64000.0_f64;
286 var2 = var1 * var1 * (self.coeff.dig_p6 as f64) / 32768.0_f64;
287 var2 = var2 + var1 * (self.coeff.dig_p5 as f64) * 2.0_f64;
288 var2 = (var2 / 4.0_f64) + ((self.coeff.dig_p4 as f64) * 65536.0_f64);
289 var1 = ((self.coeff.dig_p3 as f64) * var1 * var1 / 524288.0_f64 + (self.coeff.dig_p2 as f64) * var1) / 524288.0_f64;
290 var1 = (1.0 + var1 / 32768.0_f64) * (self.coeff.dig_p1 as f64);
291 if var1 == 0.0_f64 {
292 return 0.0; }
294 p = 1048576.0_f64 - (adc_p as f64);
295 p = (p - (var2 / 4096.0_f64)) * 6250.0_f64 / var1;
296 var1 = (self.coeff.dig_p9 as f64) * p * p / 2147483648.0_f64;
297 var2 = p * (self.coeff.dig_p8 as f64) / 32768.0_f64;
298 p = p + (var1 + var2 + (self.coeff.dig_p7 as f64)) / 16.0_f64;
299 p
300 }
301
302 fn compensate_temperature(&mut self, adc_t: i32) -> f32 {
303 match self.algorithm {
304 BMP280CompensationAlgorithm::B32 => {
305 let result = self.compensate_temperature_32b(adc_t);
306 return (result as f32) / 100.0
307 },
308 BMP280CompensationAlgorithm::B64 => {
309 let result = self.compensate_temperature_32b(adc_t);
310 return (result as f32) / 100.0
311 },
312 BMP280CompensationAlgorithm::Float => self.compensate_temperature_float(adc_t) as f32
313 }
314 }
315
316 fn compensate_pressure(&mut self, adc_p: i32) -> f32 {
318 match self.algorithm {
319 BMP280CompensationAlgorithm::B32 => {
320 let result = self.compensate_pressure_64b(adc_p);
321 return result as f32;
322 },
323 BMP280CompensationAlgorithm::B64 => {
324 let result = self.compensate_pressure_64b(adc_p);
325 return (result as f32) / 256.0;
326 },
327 BMP280CompensationAlgorithm::Float => {
328 self.compensate_pressure_float(adc_p) as f32
329 }
330 }
331 }
332
333 #[cfg(any(target_os = "linux", target_os = "android"))]
334 fn read_temp_raw(&mut self) -> Result<i32, T::Error> {
335 let mut buf = [0_u8; 3];
336 try!(self.barometer.write(&[BMP280_TEMP_MSB]));
337 try!(self.barometer.read(&mut buf));
338 let mut raw_temp: i32 = ((buf[0] as i32) << 12) + ((buf[1] as i32) << 4) + ((buf[2] as i32) >> 4);
339
340 Ok(raw_temp)
341 }
342
343 #[cfg(any(target_os = "linux", target_os = "android"))]
344 fn read_press_raw(&mut self) -> Result<i32, T::Error> {
345 let mut buf = [0_u8; 3];
346 try!(self.barometer.write(&[BMP280_PRESS_MSB]));
347 try!(self.barometer.read(&mut buf));
348 let raw_press = ((buf[0] as i32) << 12) | ((buf[1] as i32) << 4) | ((buf[2] as i32) >> 4);
349
350 Ok(raw_press)
351 }
352
353 #[doc(hidden)]
354 pub fn test_calculate_real_pressure(&mut self) {
355 self.coeff = BMP280CalibrationCoefficients {
356 dig_t1: 27504,
357 dig_t2: 26435,
358 dig_t3: -1000,
359 dig_p1: 36477,
360 dig_p2: -10685,
361 dig_p3: 3024,
362 dig_p4: 2855,
363 dig_p5: 140,
364 dig_p6: -7,
365 dig_p7: 15500,
366 dig_p8: -14600,
367 dig_p9: 6000,
368 };
369
370 let temp_reading = 519888;
371 let pressure_reading = 415148;
372
373 let tdiff_f = self.compensate_temperature_float(temp_reading) - 25.08;
374 assert!(tdiff_f.abs() < 1.0 && tdiff_f > -1.0);
375 println!("TempFloat PASS");
376 let pdiff_f = self.compensate_pressure_float(pressure_reading) - 100653.26;
377 assert!(pdiff_f.abs() < 1.0 || pdiff_f > -1.0);
378 println!("PressFloat PASS");
379
380 assert!(self.compensate_temperature_32b(temp_reading) == 2508);
381 println!("Temp32b PASS");
382
383 println!("{}", self.compensate_pressure_32b(pressure_reading));
384 println!("Press32b PASS");
387
388 println!("{}", self.compensate_pressure_64b(pressure_reading));
389 println!("Press64b PASS");
392
393 println!("Passed calibration test");
394 }
395}
396
397impl<T> Thermometer for BMP280<T>
398 where T: I2CDevice + Sized
399{
400 type Error = T::Error;
401
402 #[cfg(not(any(target_os = "linux", target_os = "android")))]
403 fn temperature_celsius(&mut self) -> Result<f32, T::Error> {
404 Ok(0.0)
405 }
406
407 #[cfg(any(target_os = "linux", target_os = "android"))]
408 fn temperature_celsius(&mut self) -> Result<f32, T::Error> {
409 match self.read_temp_raw() {
410 Ok(adc_t) => Ok(self.compensate_temperature(adc_t)),
411 Err(e) => Err(e)
412 }
413 }
414}
415
416impl<T> Barometer for BMP280<T>
417 where T: I2CDevice + Sized
418{
419 type Error = T::Error;
420
421 #[cfg(not(any(target_os = "linux", target_os = "android")))]
422 fn pressure_kpa(&mut self) -> Result<f32, T::Error> {
423 Ok(0.0)
424 }
425
426 #[cfg(any(target_os = "linux", target_os = "android"))]
427 fn pressure_kpa(&mut self) -> Result<f32, T::Error> {
428 self.temperature_celsius();
429
430 match self.read_press_raw() {
431 Ok(adc_p) => Ok(self.compensate_pressure(adc_p) / 1000.0),
432 Err(e) => Err(e)
433 }
434 }
435}
436
437#[cfg(test)]
438mod tests {
439 use super::*;
440 #[test]
441 fn test_algorithms() {
442 println!("BMP280 Barometer Thermometer.");
443 match get_linux_bmp280_i2c_device() {
444 Ok(device) => {
445 let settings = BMP280Settings {
446 compensation: BMP280CompensationAlgorithm::B64,
447 t_sb: BMP280Timing::ms0_5,
448 iir_filter_coeff: BMP280FilterCoefficient::Medium,
449 osrs_t: BMP280TemperatureOversampling::x1,
450 osrs_p: BMP280PressureOversampling::StandardResolution,
451 power_mode: BMP280PowerMode::NormalMode
452 };
453
454 match BMP280::new(device, settings) {
455 Ok(mut bmp280) => {
456 bmp280.test_calculate_real_pressure();
457 },
458 Err(e) => {}
459 }
460 },
461 Err(e) => {}
462 }
463 }
464}