Aug
02
2012

Reading data from a Bosch BMP085 with a Raspberry Pi

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 <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <sys/ioctl.h>
#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<<BMP085_OVERSAMPLING_SETTING)) * 1000);

	// Read the three byte result from 0xF6
	// 0xF6 = MSB, 0xF7 = LSB and 0xF8 = XLSB
	__u8 values[3];
	bmp085_i2c_Read_Block(fd, 0xF6, 3, values);

	up = (((unsigned int) values[0] << 16) | ((unsigned int) values[1] << 8) | (unsigned int) values[2]) >> (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)<<BMP085_OVERSAMPLING_SETTING) + 2)>>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;
}
Written by John in: Electronics,Raspberry Pi,Weather Station |

11 Comments »

  • Darek

    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…

    Comment | 4 December 2012
  • [...] Python port of John Burn’s C Code in Reading data from a Bosch BMP085 with a Raspberry Pi. [...]

    Pingback | 8 December 2012
  • Bartek

    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?

    Comment | 14 December 2012
  • See this post.

    Comment | 17 December 2012
  • 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

    Comment | 17 December 2012
  • [...] Python port of John Burn’s C Code in Reading data from a Bosch BMP085 with a Raspberry Pi [...]

    Pingback | 28 December 2012
  • Smarky

    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

    Comment | 19 April 2013
  • m0atz

    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 :-)

    Comment | 1 May 2013
  • 42jon

    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.

    Comment | 10 May 2013
  • Greg

    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.

    Comment | 26 August 2013
  • lano

    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

    Comment | 31 January 2014

RSS feed for comments on this post. TrackBack URL

Leave a comment