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.
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
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!



{ 34 comments… read them below or add one }
It looks like a fairly nice breakout board. When can I get one? It would compliment GPS altitude nicely, and there are mountains nearby.
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.
If you have anymore I would like to buy a couple (or three).
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.
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!
I allways power my Arduino from 3.3V. This is positively out of specification, but works well.
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!!
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!
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.
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!
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.
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.
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…
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!
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 ;)
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.
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
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).
it’s corrupt. I can dowbload it, but not unzip it!
I tried downloading winrar. But I stil can’t open the sketch!
I got it. Thx for the advice.
will this code work for an arduino deicemila ATmega168V??
Yes, if you are running the Arduino Firmware. The basic algorithm works on any AVR – I think
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.
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.
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
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.
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
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.
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
…
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 https://code.google.com/p/arducopter/source/browse/trunk/libraries/APM_BMP085/APM_BMP085.cpp
Will it work with 5V-powered Arduino directly via I2C? Or this sensor is not 5v-tolerant, and I’ll need level shifter?
The BMP085 datasheet clearly states: +4.25V (page 7) as absolute maximum for any pin – so you need a level shifter.
thanks
{ 3 trackbacks }