1/***************************************************************************
2 This is a library for the BMP085 pressure sensor
3
4 Designed specifically to work with the Adafruit BMP085 or BMP180 Breakout
5 ----> http://www.adafruit.com/products/391
6 ----> http://www.adafruit.com/products/1603
7
8 These displays use I2C to communicate, 2 pins are required to interface.
9
10 Adafruit invests time and resources providing this open source code,
11 please support Adafruit andopen-source hardware by purchasing products
12 from Adafruit!
13
14 Written by Kevin Townsend for Adafruit Industries.
15 BSD license, all text above must be included in any redistribution
16 ***************************************************************************/
17#if ARDUINO >= 100
18 #include "Arduino.h"
19#else
20 #include "WProgram.h"
21#endif
22
23#ifdef __AVR_ATtiny85__
24 #include "TinyWireM.h"
25 #define Wire TinyWireM
26#else
27 #include <Wire.h>
28#endif
29
30#include <math.h>
31#include <limits.h>
32
33#include "Adafruit_BMP085_U.h"
34
35static bmp085_calib_data _bmp085_coeffs; // Last read accelerometer data will be available here
36static uint8_t _bmp085Mode;
37
38#define BMP085_USE_DATASHEET_VALS (0) /* Set to 1 for sanity check */
39
40/***************************************************************************
41 PRIVATE FUNCTIONS
42 ***************************************************************************/
43
44/**************************************************************************/
45/*!
46 @brief Writes an 8 bit value over I2C
47*/
48/**************************************************************************/
49static void writeCommand(byte reg, byte value)
50{
51 Wire.beginTransmission((uint8_t)BMP085_ADDRESS);
52 #if ARDUINO >= 100
53 Wire.write((uint8_t)reg);
54 Wire.write((uint8_t)value);
55 #else
56 Wire.send(reg);
57 Wire.send(value);
58 #endif
59 Wire.endTransmission();
60}
61
62/**************************************************************************/
63/*!
64 @brief Reads an 8 bit value over I2C
65*/
66/**************************************************************************/
67static void read8(byte reg, uint8_t *value)
68{
69 Wire.beginTransmission((uint8_t)BMP085_ADDRESS);
70 #if ARDUINO >= 100
71 Wire.write((uint8_t)reg);
72 #else
73 Wire.send(reg);
74 #endif
75 Wire.endTransmission();
76 Wire.requestFrom((uint8_t)BMP085_ADDRESS, (byte)1);
77 #if ARDUINO >= 100
78 *value = Wire.read();
79 #else
80 *value = Wire.receive();
81 #endif
82 Wire.endTransmission();
83}
84
85/**************************************************************************/
86/*!
87 @brief Reads a 16 bit value over I2C
88*/
89/**************************************************************************/
90static void read16(byte reg, uint16_t *value)
91{
92 Wire.beginTransmission((uint8_t)BMP085_ADDRESS);
93 #if ARDUINO >= 100
94 Wire.write((uint8_t)reg);
95 #else
96 Wire.send(reg);
97 #endif
98 Wire.endTransmission();
99 Wire.requestFrom((uint8_t)BMP085_ADDRESS, (byte)2);
100 #if ARDUINO >= 100
101 *value = (Wire.read() << 8) | Wire.read();
102 #else
103 *value = (Wire.receive() << 8) | Wire.receive();
104 #endif
105 Wire.endTransmission();
106}
107
108/**************************************************************************/
109/*!
110 @brief Reads a signed 16 bit value over I2C
111*/
112/**************************************************************************/
113static void readS16(byte reg, int16_t *value)
114{
115 uint16_t i;
116 read16(reg, &i);
117 *value = (int16_t)i;
118}
119
120/**************************************************************************/
121/*!
122 @brief Reads the factory-set coefficients
123*/
124/**************************************************************************/
125static void readCoefficients(void)
126{
127 #if BMP085_USE_DATASHEET_VALS
128 _bmp085_coeffs.ac1 = 408;
129 _bmp085_coeffs.ac2 = -72;
130 _bmp085_coeffs.ac3 = -14383;
131 _bmp085_coeffs.ac4 = 32741;
132 _bmp085_coeffs.ac5 = 32757;
133 _bmp085_coeffs.ac6 = 23153;
134 _bmp085_coeffs.b1 = 6190;
135 _bmp085_coeffs.b2 = 4;
136 _bmp085_coeffs.mb = -32768;
137 _bmp085_coeffs.mc = -8711;
138 _bmp085_coeffs.md = 2868;
139 _bmp085Mode = 0;
140 #else
141 readS16(BMP085_REGISTER_CAL_AC1, &_bmp085_coeffs.ac1);
142 readS16(BMP085_REGISTER_CAL_AC2, &_bmp085_coeffs.ac2);
143 readS16(BMP085_REGISTER_CAL_AC3, &_bmp085_coeffs.ac3);
144 read16(BMP085_REGISTER_CAL_AC4, &_bmp085_coeffs.ac4);
145 read16(BMP085_REGISTER_CAL_AC5, &_bmp085_coeffs.ac5);
146 read16(BMP085_REGISTER_CAL_AC6, &_bmp085_coeffs.ac6);
147 readS16(BMP085_REGISTER_CAL_B1, &_bmp085_coeffs.b1);
148 readS16(BMP085_REGISTER_CAL_B2, &_bmp085_coeffs.b2);
149 readS16(BMP085_REGISTER_CAL_MB, &_bmp085_coeffs.mb);
150 readS16(BMP085_REGISTER_CAL_MC, &_bmp085_coeffs.mc);
151 readS16(BMP085_REGISTER_CAL_MD, &_bmp085_coeffs.md);
152 #endif
153}
154
155/**************************************************************************/
156/*!
157
158*/
159/**************************************************************************/
160static void readRawTemperature(int32_t *temperature)
161{
162 #if BMP085_USE_DATASHEET_VALS
163 *temperature = 27898;
164 #else
165 uint16_t t;
166 writeCommand(BMP085_REGISTER_CONTROL, BMP085_REGISTER_READTEMPCMD);
167 delay(5);
168 read16(BMP085_REGISTER_TEMPDATA, &t);
169 *temperature = t;
170 #endif
171}
172
173/**************************************************************************/
174/*!
175
176*/
177/**************************************************************************/
178static void readRawPressure(int32_t *pressure)
179{
180 #if BMP085_USE_DATASHEET_VALS
181 *pressure = 23843;
182 #else
183 uint8_t p8;
184 uint16_t p16;
185 int32_t p32;
186
187 writeCommand(BMP085_REGISTER_CONTROL, BMP085_REGISTER_READPRESSURECMD + (_bmp085Mode << 6));
188 switch(_bmp085Mode)
189 {
190 case BMP085_MODE_ULTRALOWPOWER:
191 delay(5);
192 break;
193 case BMP085_MODE_STANDARD:
194 delay(8);
195 break;
196 case BMP085_MODE_HIGHRES:
197 delay(14);
198 break;
199 case BMP085_MODE_ULTRAHIGHRES:
200 default:
201 delay(26);
202 break;
203 }
204
205 read16(BMP085_REGISTER_PRESSUREDATA, &p16);
206 p32 = (uint32_t)p16 << 8;
207 read8(BMP085_REGISTER_PRESSUREDATA+2, &p8);
208 p32 += p8;
209 p32 >>= (8 - _bmp085Mode);
210
211 *pressure = p32;
212 #endif
213}
214
215/**************************************************************************/
216/*!
217 @brief Compute B5 coefficient used in temperature & pressure calcs.
218*/
219/**************************************************************************/
220int32_t Adafruit_BMP085_Unified::computeB5(int32_t ut) {
221 int32_t X1 = (ut - (int32_t)_bmp085_coeffs.ac6) * ((int32_t)_bmp085_coeffs.ac5) >> 15;
222 int32_t X2 = ((int32_t)_bmp085_coeffs.mc << 11) / (X1+(int32_t)_bmp085_coeffs.md);
223 return X1 + X2;
224}
225
226
227/***************************************************************************
228 CONSTRUCTOR
229 ***************************************************************************/
230
231/**************************************************************************/
232/*!
233 @brief Instantiates a new Adafruit_BMP085_Unified class
234*/
235/**************************************************************************/
236Adafruit_BMP085_Unified::Adafruit_BMP085_Unified(int32_t sensorID) {
237 _sensorID = sensorID;
238}
239
240/***************************************************************************
241 PUBLIC FUNCTIONS
242 ***************************************************************************/
243
244/**************************************************************************/
245/*!
246 @brief Setups the HW
247*/
248/**************************************************************************/
249bool Adafruit_BMP085_Unified::begin(bmp085_mode_t mode)
250{
251 // Enable I2C
252 Wire.begin();
253
254 /* Mode boundary check */
255 if ((mode > BMP085_MODE_ULTRAHIGHRES) || (mode < 0))
256 {
257 mode = BMP085_MODE_ULTRAHIGHRES;
258 }
259
260 /* Make sure we have the right device */
261 uint8_t id;
262 read8(BMP085_REGISTER_CHIPID, &id);
263 if(id != 0x55)
264 {
265 return false;
266 }
267
268 /* Set the mode indicator */
269 _bmp085Mode = mode;
270
271 /* Coefficients need to be read once */
272 readCoefficients();
273
274 return true;
275}
276
277/**************************************************************************/
278/*!
279 @brief Gets the compensated pressure level in kPa
280*/
281/**************************************************************************/
282void Adafruit_BMP085_Unified::getPressure(float *pressure)
283{
284 int32_t ut = 0, up = 0, compp = 0;
285 int32_t x1, x2, b5, b6, x3, b3, p;
286 uint32_t b4, b7;
287
288 /* Get the raw pressure and temperature values */
289 readRawTemperature(&ut);
290 readRawPressure(&up);
291
292 /* Temperature compensation */
293 b5 = computeB5(ut);
294
295 /* Pressure compensation */
296 b6 = b5 - 4000;
297 x1 = (_bmp085_coeffs.b2 * ((b6 * b6) >> 12)) >> 11;
298 x2 = (_bmp085_coeffs.ac2 * b6) >> 11;
299 x3 = x1 + x2;
300 b3 = (((((int32_t) _bmp085_coeffs.ac1) * 4 + x3) << _bmp085Mode) + 2) >> 2;
301 x1 = (_bmp085_coeffs.ac3 * b6) >> 13;
302 x2 = (_bmp085_coeffs.b1 * ((b6 * b6) >> 12)) >> 16;
303 x3 = ((x1 + x2) + 2) >> 2;
304 b4 = (_bmp085_coeffs.ac4 * (uint32_t) (x3 + 32768)) >> 15;
305 b7 = ((uint32_t) (up - b3) * (50000 >> _bmp085Mode));
306
307 if (b7 < 0x80000000)
308 {
309 p = (b7 << 1) / b4;
310 }
311 else
312 {
313 p = (b7 / b4) << 1;
314 }
315
316 x1 = (p >> 8) * (p >> 8);
317 x1 = (x1 * 3038) >> 16;
318 x2 = (-7357 * p) >> 16;
319 compp = p + ((x1 + x2 + 3791) >> 4);
320
321 /* Assign compensated pressure value */
322 *pressure = compp;
323}
324
325/**************************************************************************/
326/*!
327 @brief Reads the temperatures in degrees Celsius
328*/
329/**************************************************************************/
330void Adafruit_BMP085_Unified::getTemperature(float *temp)
331{
332 int32_t UT, X1, X2, B5; // following ds convention
333 float t;
334
335 readRawTemperature(&UT);
336
337 #if BMP085_USE_DATASHEET_VALS
338 // use datasheet numbers!
339 UT = 27898;
340 _bmp085_coeffs.ac6 = 23153;
341 _bmp085_coeffs.ac5 = 32757;
342 _bmp085_coeffs.mc = -8711;
343 _bmp085_coeffs.md = 2868;
344 #endif
345
346 B5 = computeB5(UT);
347 t = (B5+8) >> 4;
348 t /= 10;
349
350 *temp = t;
351}
352
353/**************************************************************************/
354/*!
355 Calculates the altitude (in meters) from the specified atmospheric
356 pressure (in hPa), and sea-level pressure (in hPa).
357
358 @param seaLevel Sea-level pressure in hPa
359 @param atmospheric Atmospheric pressure in hPa
360*/
361/**************************************************************************/
362float Adafruit_BMP085_Unified::pressureToAltitude(float seaLevel, float atmospheric)
363{
364 // Equation taken from BMP180 datasheet (page 16):
365 // http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf
366
367 // Note that using the equation from wikipedia can give bad results
368 // at high altitude. See this thread for more information:
369 // http://forums.adafruit.com/viewtopic.php?f=22&t=58064
370
371 return 44330.0 * (1.0 - pow(atmospheric / seaLevel, 0.1903));
372}
373
374/**************************************************************************/
375/*!
376 Calculates the altitude (in meters) from the specified atmospheric
377 pressure (in hPa), and sea-level pressure (in hPa). Note that this
378 function just calls the overload of pressureToAltitude which takes
379 seaLevel and atmospheric pressure--temperature is ignored. The original
380 implementation of this function was based on calculations from Wikipedia
381 which are not accurate at higher altitudes. To keep compatibility with
382 old code this function remains with the same interface, but it calls the
383 more accurate calculation.
384
385 @param seaLevel Sea-level pressure in hPa
386 @param atmospheric Atmospheric pressure in hPa
387 @param temp Temperature in degrees Celsius
388*/
389/**************************************************************************/
390float Adafruit_BMP085_Unified::pressureToAltitude(float seaLevel, float atmospheric, float temp)
391{
392 return pressureToAltitude(seaLevel, atmospheric);
393}
394
395/**************************************************************************/
396/*!
397 Calculates the pressure at sea level (in hPa) from the specified altitude
398 (in meters), and atmospheric pressure (in hPa).
399
400 @param altitude Altitude in meters
401 @param atmospheric Atmospheric pressure in hPa
402*/
403/**************************************************************************/
404float Adafruit_BMP085_Unified::seaLevelForAltitude(float altitude, float atmospheric)
405{
406 // Equation taken from BMP180 datasheet (page 17):
407 // http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf
408
409 // Note that using the equation from wikipedia can give bad results
410 // at high altitude. See this thread for more information:
411 // http://forums.adafruit.com/viewtopic.php?f=22&t=58064
412
413 return atmospheric / pow(1.0 - (altitude/44330.0), 5.255);
414}
415
416/**************************************************************************/
417/*!
418 Calculates the pressure at sea level (in hPa) from the specified altitude
419 (in meters), and atmospheric pressure (in hPa). Note that this
420 function just calls the overload of seaLevelForAltitude which takes
421 altitude and atmospheric pressure--temperature is ignored. The original
422 implementation of this function was based on calculations from Wikipedia
423 which are not accurate at higher altitudes. To keep compatibility with
424 old code this function remains with the same interface, but it calls the
425 more accurate calculation.
426
427 @param altitude Altitude in meters
428 @param atmospheric Atmospheric pressure in hPa
429 @param temp Temperature in degrees Celsius
430*/
431/**************************************************************************/
432float Adafruit_BMP085_Unified::seaLevelForAltitude(float altitude, float atmospheric, float temp)
433{
434 return seaLevelForAltitude(altitude, atmospheric);
435}
436
437
438
439/**************************************************************************/
440/*!
441 @brief Provides the sensor_t data for this sensor
442*/
443/**************************************************************************/
444void Adafruit_BMP085_Unified::getSensor(sensor_t *sensor)
445{
446 /* Clear the sensor_t object */
447 memset(sensor, 0, sizeof(sensor_t));
448
449 /* Insert the sensor name in the fixed length char array */
450 strncpy (sensor->name, "BMP085", sizeof(sensor->name) - 1);
451 sensor->name[sizeof(sensor->name)- 1] = 0;
452 sensor->version = 1;
453 sensor->sensor_id = _sensorID;
454 sensor->type = SENSOR_TYPE_PRESSURE;
455 sensor->min_delay = 0;
456 sensor->max_value = 1100.0F; // 300..1100 hPa
457 sensor->min_value = 300.0F;
458 sensor->resolution = 0.01F; // Datasheet states 0.01 hPa resolution
459}
460
461/**************************************************************************/
462/*!
463 @brief Reads the sensor and returns the data as a sensors_event_t
464*/
465/**************************************************************************/
466bool Adafruit_BMP085_Unified::getEvent(sensors_event_t *event)
467{
468 float pressure_kPa;
469
470 /* Clear the event */
471 memset(event, 0, sizeof(sensors_event_t));
472
473 event->version = sizeof(sensors_event_t);
474 event->sensor_id = _sensorID;
475 event->type = SENSOR_TYPE_PRESSURE;
476 event->timestamp = 0;
477 getPressure(&pressure_kPa);
478 event->pressure = pressure_kPa / 100.0F;
479
480 return true;
481}