Arduino & Barometric Pressure Sensor BMP085

December 5, 2009

in Sensors

In lack of any new projects (which are currently all in an very intermediate state) Interactive Matter presents yet another ‘how to connect a cool I2C sensor to Arduino’ post.

This time it is all about pressure. The BMP085 pressure sensor combines a absolute barometric pressure sensor (aka barometer) with an temperature sensor. It is build by Bosch Sensortec and intended to help GPS navigation units to detect their height above sea level. It combines the advantage of being quite cheap (~5$) and precise.

Arduino & Barometric Pressure Sensor BMP085

But of course there are myriads of other applications as well, like using it in a digital weather station, detecting the force of slammed doors or …

The information about this sensor is very sparse and Bosch is not very keen to give any information to makers. But fortunately Jeelabs offers it as an extension module for their Jeenode and published some very helpful code for it (which helped a lot – check out they are doing really amazing stuff).

Hardware

Arduino & Barometric Pressure Sensor BMP085

Hooking up the BMP085 to the Arduino works just like any other I2C part: Connect VCC to VCC and GND to GND, SCL goes to analogue pin 5, SDA to analogue pin4. Adding some pull up resistors (1K to 20K, most often something like 4.7K) between SDA, SCL and VCC finishes the setup (this was included in my breakout board).

The BMP08 accepts 1.8 to 3.6 Volts – so no chance to connect it directly to 5 Volts. The BMP085 has an additional EOC (end of conversion) pin indicating the successful data capture. This was connected to analogue pin 2 – but not used in the software implementation.

Software

Fortunately the code by Jeenode contained all the functionality, you need, taken directly from the datasheet. The only thing I added was the ability to use all oversampling modes (the BMP085 offers 4 oversampling mode, each on taking longer than the other and using more energy, but delivering more precise results).

First we need the ability to read 16 bit values from the BMP085 registers. Nearly all registers of the BMP085 are 16 bit wide:

int read_int_register(unsigned char r)
{
  unsigned char msb, lsb;
  Wire.beginTransmission(I2C_ADDRESS);
  Wire.send(r);  // register to read
  Wire.endTransmission();

  Wire.requestFrom(I2C_ADDRESS, 2); // read a byte
  while(!Wire.available()) {
    // waiting
  }
  msb = Wire.receive();
  while(!Wire.available()) {
    // waiting
  }
  lsb = Wire.receive();
  return (((int)msb<<8) | ((int)lsb));
}

Second you need a function to write a 8 bit register:

char read_register(unsigned char r)
{
  unsigned char v;
  Wire.beginTransmission(I2C_ADDRESS);
  Wire.send(r);  // register to read
  Wire.endTransmission();

  Wire.requestFrom(I2C_ADDRESS, 1); // read a byte
  while(!Wire.available()) {
    // waiting
  }
  v = Wire.receive();
  return v;
}

Now we can define some global variables to read the Eeprom calibration data:

//just taken from the BMP085 datasheet
int ac1;
int ac2;
int ac3;
unsigned int ac4;
unsigned int ac5;
unsigned int ac6;
int b1;
int b2;
int mb;
int mc;
int md;
void  bmp085_get_cal_data() {
  Serial.println("Reading Calibration Data");
  ac1 = read_int_register(0xAA);
  Serial.print("AC1: ");
  Serial.println(ac1,DEC);
  ac2 = read_int_register(0xAC);
  Serial.print("AC2: ");
  Serial.println(ac2,DEC);
  ac3 = read_int_register(0xAE);
  Serial.print("AC3: ");
  Serial.println(ac3,DEC);
  ac4 = read_int_register(0xB0);
  Serial.print("AC4: ");
  Serial.println(ac4,DEC);
  ac5 = read_int_register(0xB2);
  Serial.print("AC5: ");
  Serial.println(ac5,DEC);
  ac6 = read_int_register(0xB4);
  Serial.print("AC6: ");
  Serial.println(ac6,DEC);
  b1 = read_int_register(0xB6);
  Serial.print("B1: ");
  Serial.println(b1,DEC);
  b2 = read_int_register(0xB8);
  Serial.print("B2: ");
  Serial.println(b1,DEC);
  mb = read_int_register(0xBA);
  Serial.print("MB: ");
  Serial.println(mb,DEC);
  mc = read_int_register(0xBC);
  Serial.print("MC: ");
  Serial.println(mc,DEC);
  md = read_int_register(0xBE);
  Serial.print("MD: ");
  Serial.println(md,DEC);
}

The Eeprom readout can be done more efficiently by reading all the values with just one register write. But it is more comprehensible like this and in the initialization phase we have good amount of time.
The raw temperature (ut) and pressure (up) data can be readout as 16 bit and 24 bit value:

unsigned int bmp085_read_ut() {
  write_register(0xf4,0x2e);
  delay(5); //longer than 4.5 ms
  return read_int_register(0xf6);
}
long bmp085_read_up() {
  write_register(0xf4,0x34+(oversampling_setting<<6));
  delay(pressure_waittime[oversampling_setting]);

  unsigned char msb, lsb, xlsb;
  Wire.beginTransmission(I2C_ADDRESS);
  Wire.send(0xf6);  // register to read
  Wire.endTransmission();

  Wire.requestFrom(I2C_ADDRESS, 3); // read a byte
  while(!Wire.available()) {
    // waiting
  }
  msb = Wire.receive();
  while(!Wire.available()) {
    // waiting
  }
  lsb |= Wire.receive();
  while(!Wire.available()) {
    // waiting
  }
  xlsb |= Wire.receive();
  return (((long)msb<<16) | ((long)lsb<<8) | ((long)xlsb)) >>(8-oversampling_setting);
}

The conversion between the raw data and the real temperature data in 0.1 °C an Pascal is taken from the datasheet (combined with the corrections of Jeenodes):

void bmp085_read_temperature_and_pressure(int* temperature, long* pressure) {
  int  ut= bmp085_read_ut();
  long up = bmp085_read_up();
  long x1, x2, x3, b3, b5, b6, p;
  unsigned long b4, b7;

  //calculate the temperature
  x1 = ((long)ut - ac6) * ac5 >> 15;
  x2 = ((long) mc << 11) / (x1 + md);
  b5 = x1 + x2;
  *temperature = (b5 + 8) >> 4;

  //calculate the pressure
  b6 = b5 - 4000;
  x1 = (b2 * (b6 * b6 >> 12)) >> 11;
  x2 = ac2 * b6 >> 11;
  x3 = x1 + x2;
  b3 = (((int32_t) ac1 * 4 + x3)<> 2;
  x1 = ac3 * b6 >> 13;
  x2 = (b1 * (b6 * b6 >> 12)) >> 16;
  x3 = ((x1 + x2) + 2) >> 2;
  b4 = (ac4 * (uint32_t) (x3 + 32768)) >> 15;
  b7 = ((uint32_t) up - b3) * (50000 >> oversampling_setting);
  p = b7 < 0x80000000 ? (b7 * 2) / b4 : (b7 / b4) * 2;

  x1 = (p >> 8) * (p >> 8);
  x1 = (x1 * 3038) >> 16;
  x2 = (-7357 * p) >> 16;
  *pressure = p + ((x1 + x2 + 3791) >> 4);

}

Unfortunately temperature and pressure had to be computed at the same time (the temperature value is used for the pressure calculation). int* means a pointer to an integer value, which is written like *temperature = (b5 + 8 ) >> 4;. the function is called like

  int  temperature = 0;
  long pressure = 0;

  bmp085_read_temperature_and_pressure(&temperature,&pressure);

It is very simple and gives very good results.
If you want to try it yourself get the BMP085 Arduino Sketch. If there is any interest in the breakout board (assembled or unassembled) just drop me a note.

And big thanks to Jeelabs. Their code helped me a lot they real awesome stuff!

{ 38 comments… read them below or add one }

tz December 7, 2009 at 04:05

It looks like a fairly nice breakout board. When can I get one? It would compliment GPS altitude nicely, and there are mountains nearby.

Reply

Marcus December 7, 2009 at 10:53

There are still some boards left. So there is a slight chance that it is ‘in stock’.
The board will cost 20€ or 29$.
I will send you an email.

Reply

Jim Frazer December 18, 2009 at 03:23

If you have anymore I would like to buy a couple (or three).

Reply

Marcus March 11, 2010 at 15:38

Currently there is quite a good discussion over at RCgroups.com.
As soon as I find enough time I will incorporate the results – until then I just reference this here for further reference.

Reply

Jason April 18, 2010 at 09:42

Are you using the BMP085 with a 3.3v arduino or with a 5v arduino supplying 3.3v to the BMP085?

I thought the latter would cause problems…

Thanks!

Reply

Marcus April 18, 2010 at 13:32

I allways power my Arduino from 3.3V. This is positively out of specification, but works well.

Reply

Jason April 22, 2010 at 21:37

From my understanding of the data sheet, temperature is returned in .1 degrees celsius and pressure in pascals.

I am getting something like:
209 26612

which would be 20.9 C which is probably correct. I don’t understand the 26612 (266.12 mbars) number though. The pressure in millibars should be around 1015…

Any ideas?

Thanks!!

Reply

Marcus April 22, 2010 at 22:24

Obey, the data sheet is incorrect.
And my sketch is incorrect too.
I currently have the correct sketch at hand. I wil post it at the weekend. So check for the update!

Reply

Sin Shimozono April 25, 2010 at 16:38

Seems that the 5th line after “//calculate the pressure” is missing shifts with over sampling setting value:
b3 = ((((long) ac1 * 4 + x3)<>2;
Anyway, your code brought to me a lot of help.
Thanks.

Reply

SolamenteDoug May 12, 2010 at 03:38

My output from an 8 MHz Arduino Pro:

Setting up BMP085
Reading Calibration Data
AC1: 6301
AC2: -1248
AC3: -14399
AC4: 33614
AC5: 24426
AC6: 25445
B1: 5498
B2: 5498
MB: -32768
MC: -11075
MD: 2432
-2622 116140
-2622 116124
-2622 116138

Indoor temp is currently 23C and the outside pressure is ~101.6 kPa. I have several chips and will be trying all of them. I just want to be sure that it isn’t a software thing I’m completely overlooking. Thanks!

Reply

Marcus May 12, 2010 at 11:31

Yes, there is an error in my code. Check the RC Group discussion (link above). I am still trying to find time to fix the source.

Reply

JonT October 20, 2010 at 15:30

Hi

I battled with this for a while. This works: get code from http://mitat.tuu.fi/?p=78 and then around line 58, change
int ut= bmp085_read_ut();
to:
long ut= bmp085_read_ut();
This was originally posted by Scott at Sparkfun, all thanks to him.

Reply

SolamenteDoug May 12, 2010 at 15:38

The RC site is on the right track for sure. I think I have the code where it ought to be. Great work on this chip! When my project has finished, I’ll tell you how they’re to be used…

Reply

SolamenteDoug May 18, 2010 at 21:58

My supervisors said they needed precise barometers to better calculate the scattering due to air of x-rays at our new beamline. Commercial devices that could interface with our serial-over-ethernet sensor network were quoted at over a thousand dollars each. I told them I could make one just as precise and customized to our needs for under 1/10th the cost. I made a prototype with the chip, an Arduino Pro, a level shifter and a hard case. It worked better than I had expected!

So, I’m happy to report that we now have three devices installed and humming along nicely! Thanks for all your hard work!

Reply

Marcus May 18, 2010 at 22:15

I am happy that it was usefull. But most of the credit goes to jeelabs. Their wireless Arduino stuff is awesome.
Thanks for your kind words and I am looking forward to your project description. Blog this ;)

Pierce June 4, 2010 at 01:30

I am about to go mad trying to figure this one out. I looked through everything I could find online and can not get anything different form what SolamenteDoug said that he was getting. What I’m getting looks like this:

Setting up BMP085
Reading Calibration Data
AC1: 6697
AC2: -1139
AC3: -14293
AC4: 34056
AC5: 25243
AC6: 24811
B1: 5498
B2: 5498
MB: -32768
MC: -11075
MD: 2432
Temp -2695 Pressure 42558
Temp -2694 Pressure 42557

If someone could throw me a bone I would greatly appreciate it. Thanks.

bert May 12, 2010 at 17:22

Hey guys,

I’m also trying to get the BMP085 to work on my arduino duemilanove.
The above Zip-file is not working. Could someone post or mail the code?
What I don’t get: The BMP is powered (I connected VCC to 3.3V and GND to GND). Where should I connect SDA & SCL and WHY?

On the internet I have found several examples. One working woth onewire, the other with I2C. What should I use?

grtz,

Bert

Reply

Marcus May 12, 2010 at 17:40

Which problem do you have with the ZIP File?

Regarding communication: The BMP085 supprts only I2C, not one wire. SCL goes to analogue pin 5 of the Arduino, SDA to analogue pin 4. And don’t forget the I2C pull up resistors (4.7K to VCC).

Reply

bert May 13, 2010 at 09:43

it’s corrupt. I can dowbload it, but not unzip it!

Reply

bert May 13, 2010 at 10:14

I tried downloading winrar. But I stil can’t open the sketch!

Reply

bert May 13, 2010 at 10:24

I got it. Thx for the advice.

Reply

gigs July 1, 2010 at 12:51

will this code work for an arduino deicemila ATmega168V??

Reply

Marcus July 1, 2010 at 14:27

Yes, if you are running the Arduino Firmware. The basic algorithm works on any AVR – I think

Reply

gigs July 2, 2010 at 08:03

ahhh ok, but i’m still having problems getting the calibration data. it just prints Setting BMP085 /n Reading Calibration Data nothing else. I’ve been troubleshooting the hardware earlier but i cant seem to find anything wrong. do you have any idea of some common problems encountered in bmp085 like this one.

gigs July 1, 2010 at 17:40

ahhh ok, but i’m still having problems getting the calibration data. it just prints Setting BMP085 /n Reading Calibration Data nothing else. I’ve been troubleshooting the hardware earlier but i cant seem to find anything wrong. do you have any idea of some common problems encountered in bmp085 like this one.

Reply

Richard July 2, 2010 at 11:02

I to had troubles opening the PDE file then i found the solution.

It looks like the file is double zipped.

Download the file and extract the contents.

Rename the extracted file BMP085.zip

Extract the contents and hay presto you have the demo code you are looking for.

Richard

Reply

Marcus July 2, 2010 at 21:16

Strange, I cannot reproduce this on my mac. It works like a charm. But perhaps if you got the same problem as Richard, it may help. Leave a comment if this problem applies to you too – I will then investigate it further.

Reply

Richard July 7, 2010 at 17:46

Hi i am running a 5v arduino is there a way to hook this sensor up to my arduino. I dont want to damage my sensor

Reply

Marcus July 7, 2010 at 18:27

Be sure to power the BMP085 from 3V or 3.3V. Sparkfun has some great writeup of how to interface 3 with 5V.
But it breaks down to some basic rules:

If you really want to get sure that nothing goes wrong use a real level translation chip – search over at digikey, they got a lot.
Most of the cases some 10K resistors in your datalines, between the arduino & the BMP085 are enough. It still exihibts some danger but it should be ok.
Sometimes I just connect them without any protection at all, but I feel unsafe all the time. But untill now nothing bad happened.

Reply

Kelsey July 28, 2010 at 23:02

I had same issue as others above, with values returned like this:
-2703 138596
-2703 138594
-2703 138592
-2703 138596

The solution for me was that ut should be type “long” in line 44:

long ut= bmp085_read_ut();

I suspect that it doesn’t come up for all values of the calibration constants (or raw pressure), so some people don’t see this issue.

As others have mentioned, the b3 in line 60 should be changed as well. As seen on the rcgroups discussion listed above, I used:

b3 = (((int32_t) ac1 * 4 + x3)<> 2;

These two changes give reasonable values for me with the source code listed here.

e.g.,
287 99684
287 99681
287 99690
287 99684

Reply

Sofian Audry October 12, 2010 at 18:26

There’s no taking into account of the oversample_setting in your example. I used the following replacement for line 60 and it works fine:

int32_t tmp = ac1;
tmp = (tmp*4 + x3)<> 2;

(taken from

Reply

st March 9, 2011 at 18:04

Will it work with 5V-powered Arduino directly via I2C? Or this sensor is not 5v-tolerant, and I’ll need level shifter?

Reply

Marcus March 9, 2011 at 18:28

The BMP085 datasheet clearly states: +4.25V (page 7) as absolute maximum for any pin – so you need a level shifter.

Reply

st March 9, 2011 at 18:59

thanks

Reply

Niko S. June 26, 2012 at 09:34

After poking around for a day, I couldn’t find a single functional demo of the BMP085 for Arduino 1.0+

I was finally able to successfully debug some code on bildr.org, and here is the working result: http://ilabbali.com/code/Arduino_BMP085.cpp

Reply

Marcus June 28, 2012 at 14:14

Thanks for sharing.
Somehow the BMP085 seems a bit cursed – there are errors in the datasheet, there are errors in any code available – so thanks for this working example!

Reply

mahin2010 June 4, 2013 at 21:34

hello.excuseme i want sens high with bmp085,can you help me? I want example of bascom with bmp085 altimetr for 3 meter in room, can you help me? tank alot before.

Reply

Matt Rebel January 11, 2014 at 14:48

When you use this code on the Due the int is a 32-bit value, instead of 16-bits on the Uno, so the calibration values are wrong. When you change the int to short when declaring the variables at the beginning of the code, this code will also work on the Due. But nevertheless thanks to the topic starter for the code.

Reply

Leave a Comment

{ 3 trackbacks }