Update(2): I’ve fixed some reported bugs in this code. Please use the newer version here.
Update: If the LM Sensors website is still down, you can get smbus.c and smbus.h from here.
I needed a way to measure air pressure as part of my Raspberry Pi controlled weather station.
I decided to use the Bosch BMP085 as it is very sensitive (down to 0.03hPa, or 3Pa) and SparkFun Electronics offer it already soldered to a break out board making it relatively easy to interface.
The breakout board includes pull up resistors on the Data and Clock lines, so it’s a simple four wire connection to the Raspberry Pi.
Breakout Board pin | Raspberry Po GPIO Pin |
---|---|
SDA | P1-03 / IC20-SDA |
SCL | P1-05 / IC20_SCL |
XCLR | Not Connected |
EOC | Not Connected |
GND | P1-06 / GND |
VCC | P1-01 / 3.3V |
I had real trouble talking to the sensor using the standard file write and file read commands. I was having to do multiple reads to get usable data, but I noticed that the i2cget and i2cset commands worked perfectly every time.
I took a look at the i2cget and i2cset source code and noticed it was using smbus to talk to the sensor. A little further delving and I had a working solution.
Click here for the source (testBMP085.c).
This code is a derivative of the Arduino code by Jim Lindblom and is released under the same “CC BY-SA v3.0” license.
You can find smbus.c and smbus.h at http://www.lm-sensors.org/browser/i2c-tools/trunk/lib/smbus.c and http://www.lm-sensors.org/browser/i2c-tools/trunk/include/i2c/smbus.h/ – You will probably want to edit smbus.c to change the path to smbus.h.
/* Raspberry Pi Bosch BMP085 communication code. By: John Burns (www.john.geek.nz) Date: 01 August 2012 License: CC BY-SA v3.0 - http://creativecommons.org/licenses/by-sa/3.0/ This is a derivative work based on: BMP085 Extended Example Code by: Jim Lindblom SparkFun Electronics date: 1/18/11 license: CC BY-SA v3.0 - http://creativecommons.org/licenses/by-sa/3.0/ Source: http://www.sparkfun.com/tutorial/Barometric/BMP085_Example_Code.pde Compile with: gcc -Wall -o testBMP085 ./smbus.c ./testBMP085.c Circuit detail: Using a Spark Fun Barometric Pressure Sensor - BMP085 breakout board link: https://www.sparkfun.com/products/9694 This comes with pull up resistors already on the i2c lines. BMP085 pins below are as marked on the Sparkfun BMP085 Breakout board SDA - P1-03 / IC20-SDA SCL - P1-05 / IC20_SCL XCLR - Not Connected EOC - Not Connected GND - P1-06 / GND VCC - P1-01 / 3.3V Note: Make sure you use P1-01 / 3.3V NOT the 5V pin. */ #include#include #include #include #include #include #include #include #include "smbus.h" #define BMP085_I2C_ADDRESS 0x77 const unsigned char BMP085_OVERSAMPLING_SETTING = 3; // Calibration values - These are stored in the BMP085 short int ac1; short int ac2; short int ac3; unsigned short int ac4; unsigned short int ac5; unsigned short int ac6; short int b1; short int b2; short int mb; short int mc; short int md; int b5; unsigned int temperature, pressure; // Open a connection to the bmp085 // Returns a file id int bmp085_i2c_Begin() { int fd; char *fileName = "/dev/i2c-0"; // Open port for reading and writing if ((fd = open(fileName, O_RDWR)) < 0) exit(1); // Set the port options and set the address of the device if (ioctl(fd, I2C_SLAVE, BMP085_I2C_ADDRESS) < 0) { close(fd); exit(1); } return fd; } // Read two words from the BMP085 and supply it as a 16 bit integer __s32 bmp085_i2c_Read_Int(int fd, __u8 address) { __s32 res = i2c_smbus_read_word_data(fd, address); if (res < 0) { close(fd); exit(1); } // Convert result to 16 bits and swap bytes res = ((res<<8) & 0xFF00) | ((res>>8) & 0xFF); return res; } //Write a byte to the BMP085 void bmp085_i2c_Write_Byte(int fd, __u8 address, __u8 value) { if (i2c_smbus_write_byte_data(fd, address, value) < 0) { close(fd); exit(1); } } // Read a block of data BMP085 void bmp085_i2c_Read_Block(int fd, __u8 address, __u8 length, __u8 *values) { if(i2c_smbus_read_i2c_block_data(fd, address,length,values)<0) { close(fd); exit(1); } } void bmp085_Calibration() { int fd = bmp085_i2c_Begin(); ac1 = bmp085_i2c_Read_Int(fd,0xAA); ac2 = bmp085_i2c_Read_Int(fd,0xAC); ac3 = bmp085_i2c_Read_Int(fd,0xAE); ac4 = bmp085_i2c_Read_Int(fd,0xB0); ac5 = bmp085_i2c_Read_Int(fd,0xB2); ac6 = bmp085_i2c_Read_Int(fd,0xB4); b1 = bmp085_i2c_Read_Int(fd,0xB6); b2 = bmp085_i2c_Read_Int(fd,0xB8); mb = bmp085_i2c_Read_Int(fd,0xBA); mc = bmp085_i2c_Read_Int(fd,0xBC); md = bmp085_i2c_Read_Int(fd,0xBE); close(fd); } // Read the uncompensated temperature value unsigned int bmp085_ReadUT() { unsigned int ut = 0; int fd = bmp085_i2c_Begin(); // Write 0x2E into Register 0xF4 // This requests a temperature reading bmp085_i2c_Write_Byte(fd,0xF4,0x2E); // Wait at least 4.5ms usleep(5000); // Read the two byte result from address 0xF6 ut = bmp085_i2c_Read_Int(fd,0xF6); // Close the i2c file close (fd); return ut; } // Read the uncompensated pressure value unsigned int bmp085_ReadUP() { unsigned int up = 0; int fd = bmp085_i2c_Begin(); // Write 0x34+(BMP085_OVERSAMPLING_SETTING<<6) into register 0xF4 // Request a pressure reading w/ oversampling setting bmp085_i2c_Write_Byte(fd,0xF4,0x34 + (BMP085_OVERSAMPLING_SETTING<<6)); // Wait for conversion, delay time dependent on oversampling setting usleep((2 + (3< > (8-BMP085_OVERSAMPLING_SETTING); return up; } // Calculate pressure given uncalibrated pressure // Value returned will be in units of XXXXX unsigned int bmp085_GetPressure(unsigned int up) { int x1, x2, x3, b3, b6, p; unsigned int b4, b7; b6 = b5 - 4000; // Calculate B3 x1 = (b2 * (b6 * b6)>>12)>>11; x2 = (ac2 * b6)>>11; x3 = x1 + x2; b3 = (((((int)ac1)*4 + x3)< >2; // Calculate B4 x1 = (ac3 * b6)>>13; x2 = (b1 * ((b6 * b6)>>12))>>16; x3 = ((x1 + x2) + 2)>>2; b4 = (ac4 * (unsigned int)(x3 + 32768))>>15; b7 = ((unsigned int)(up - b3) * (50000>>BMP085_OVERSAMPLING_SETTING)); if (b7 < 0x80000000) p = (b7<<1)/b4; else p = (b7/b4)<<1; x1 = (p>>8) * (p>>8); x1 = (x1 * 3038)>>16; x2 = (-7357 * p)>>16; p += (x1 + x2 + 3791)>>4; return p; } // Calculate temperature given uncalibrated temperature // Value returned will be in units of 0.1 deg C unsigned int bmp085_GetTemperature(unsigned int ut) { int x1, x2; x1 = (((int)ut - (int)ac6)*(int)ac5) >> 15; x2 = ((int)mc << 11)/(x1 + md); b5 = x1 + x2; unsigned int result = ((b5 + 8)>>4); return result; } int main(int argc, char **argv) { bmp085_Calibration(); temperature = bmp085_GetTemperature(bmp085_ReadUT()); pressure = bmp085_GetPressure(bmp085_ReadUP()); printf("Temperature\t%0.1f%cC\n", ((double)temperature)/10,0x00B0); printf("Pressure\t%0.2fhPa\n", ((double)pressure)/100); return 0; }
Hi! Your links for smbus.c & smbus.h are dead, ive search for libraries on inet but no luck. Can U pls share them somewhere or just send them to my mail…? I need them bcouse i would like use bmp085 with php web…
Hi, Please visit http://www.john.geek.nz/2012/12/update-reading-data-from-a-bosch-bmp085-with-a-raspberry-pi/ for smbus.c and smbus.h
Hello. Do you know where I can find smbus.c/smbus.h now? Lm-sensors site is unfortunately completely empty :/ Or maybe You still have those files and You will be so kind do send me them?
See this post.
Hi,
I have version 2 of rpi (512MB) and I needed to change I2C bus in code from
char *fileName = “/dev/i2c-0”;
to
char *fileName = “/dev/i2c-1”;
to make it work. Beside that it works great.
Regards
Hey, thanks for the code, works far better than the python code I got from Adafruit, not sure why such a difference, but your C code produces temperature results far better.
Any code to calculate altitude based on the pressure readings? I’ve tried a couple of things, but get spurious results 🙂
Thanks for the code. I spotted a problem after modifying the main routine to take a sample a minute and output the results to a log file. The process was mysteriously dying after just over 17 hours (fully repeatable). After some investigation and noting that my log files contained exactly 1022 samples, I found the reason. The process was running out of file descriptors. The bmp085_ReadUP function omits to “close (fd);” just before returning from the function.
This doesn’t matter if you compile the code as supplied, as you call each function once. However, if you modify it to sit in a loop, each reading opens another channel to /dev/i2c-0 and you gradually use up file descriptors until you run out and fall over.
Once I’d fixed that issue, the code worked great.
Thanks for the code. Works perfectly.
I tried the Adafruit python code and for some reason it kept giving a error. Your code works first time.
I get
gcc -Wall -o testBMP085 ./smbus.c ./testBMP085.c
./smbus.c: In function ‘i2c_smbus_access’:
./smbus.c:47:30: error: storage size of ‘args’ isn’t known
./smbus.c:55:20: error: ‘I2C_SMBUS’ undeclared (first use in this function)
./smbus.c:55:20: note: each undeclared identifier is reported only once for each function it appears in
./smbus.c:47:30: warning: unused variable ‘args’ [-Wunused-variable]
./testBMP085.c: In function ‘bmp085_i2c_Begin’:
./testBMP085.c:78:16: error: ‘I2C_SLAVE’ undeclared (first use in this function)
./testBMP085.c:78:16: note: each undeclared identifier is reported only once for each function it appears in
distcc[6315] ERROR: compile ./smbus.c on localhost failed
make: *** [all] Fehler 1
Hi, I started of using your code but kept getting corrupted results, about 5-15 per 6 hour period while sampling every 5 minutes. I think it was due to overclocking messing with ‘usleep’. So I re-wrote the code to do away with SMBUS and added a polling feature to check if the conversion is complete.
code can be found here:
https://github.com/maasdoel/bmp180 and HTU21D-F
PS
I started testing this morning with the pi running on a 12v battery and lighter adapter USB PSU and ‘turbo’ overclock enabled, so far so good.
Thanks.