messinwu
Posts: 17
Joined: Wed Aug 08, 2012 1:33 pm

C Code for using BMP085 with Raspberry Pi

Mon Sep 10, 2012 6:15 pm

I realize there's already the "Adafruit" way of interfacing with the BMP085 sensor, but I wasn't able to get that to work, and I think it's because it uses python-smbus and that's not available with Arch Linux for some reason, and I got tired of trying to figure out how to convert a DEB (seemingly the only way you can get python-smbus) for installation on my Arch Linux Pi system. Also, I prefer a more "direct" access using a lower level programming language anyway. I'm not proficient with C by any means, but I'm OK at hacking together things to make them work based on examples.

Anyway, I found this guy's C-Code here: http://www.john.geek.nz/2012/08/reading ... pberry-pi/

As he mentions, you have to edit the location of the smbus.h file, and he conveniently provides direct links to those two files for you to wget as well. However, his code does not compile as written, at least it didn't on my setup. GCC complains about NULL not being defined. So, just add '#define NULL 0' toward the top of smbus.c and you should be set.

Below is my version of his code (which was originally derived from a couple other sources as well), which includes calculation and reporting of Altitude and I also changed it to report the temperature and altitude in Imperial measurements. You can easily remove the additional formula on the printf lines back to Metric if you so wish. Anyway, here's my version of his code:

Code: Select all

/*
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, altitude;


// 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;
}

// This Altitude part is stolen from some some unknown
// Arduino library.  The number divided into pressure for
// float A is derived from the local pressure as explained
// at http://learn.adafruit.com/bmp085/using-the-bmp085.
unsigned int bmp085_Altitude(float pressure)
{
	float A = pressure/101794.58;
	float B = 1/5.25588;
	float C = pow(A,B);
	C = 1 - C;
	C = C / 0.0000225577;

	return C;
}



int main(int argc, char **argv)

{

	bmp085_Calibration();

	temperature = bmp085_GetTemperature(bmp085_ReadUT());
	pressure = bmp085_GetPressure(bmp085_ReadUP());
        altitude = bmp085_Altitude(pressure);

	printf("Temperature\t%0.1f *F\n", ((double)temperature)/10 * 1.8 + 32);
	printf("Pressure\t%0.2f hPa\n", ((double)pressure)/100);
        printf("Altitude\t%0.1f Feet\n", ((double)altitude)*3.280839895);

	return 0;
}
To compile, make sure you downloaded the smbus.c and smbus.h files, changed smbus.c to correctly link to smbus.h, and added the DEFINE line mentioned above to smbus.c so the compiler doesn't complain. Compile it like this:

Code: Select all

gcc -Wall -o testBMP085 ./smbus.c ./testBMP085.c
Then of course, run your compiled ./testBMP085 file and you'll see something like this:

Code: Select all

./testBMP085
Temperature	72.3 *F
Pressure	974.60 hPa
Altitude	1197.5 Feet

ChrisKempson
Posts: 4
Joined: Mon May 28, 2012 10:57 am

Re: C Code for using BMP085 with Raspberry Pi

Wed Sep 19, 2012 8:29 pm

You can install "python-smbus" on ArchLinux with

Code: Select all

sudo pacman -S i2c-tools
.

User avatar
PieTrzaK
Posts: 2
Joined: Thu Dec 13, 2012 7:15 pm

Re: C Code for using BMP085 with Raspberry Pi

Thu Dec 13, 2012 7:17 pm

Hello.
Could You tell me where can I find smbus.c/smubs.h? Unfortunately links on the lm-sensors site are dead - whole site just disappeared.

kroonen
Posts: 5
Joined: Sat Jan 19, 2013 2:23 pm

Re: C Code for using BMP085 with Raspberry Pi

Sat Jan 19, 2013 2:25 pm

Hi I used the obove code but get an error when compiling this, any idea?

root@raspberrypi:~# gcc -Wall -o testBMP085 ./smbus.c ./testBMP085.c
./testBMP085.c: In function 'bmp085_Altitude':
./testBMP085.c:239:4: warning: implicit declaration of function 'pow' [-Wimplicit-function-declaration]
./testBMP085.c:239:14: warning: incompatible implicit declaration of built-in function 'pow' [enabled by default]
/tmp/ccIVzEEf.o: In function `bmp085_Altitude':
testBMP085.c:(.text+0x780): undefined reference to `pow'
collect2: ld returned 1 exit status

gcb
Posts: 3
Joined: Sun Jul 29, 2012 3:12 pm

Re: C Code for using BMP085 with Raspberry Pi

Sat Jan 19, 2013 4:09 pm

try with the -lm option, that links the math library.

Code: Select all

gcc -lm -Wall -o testBMP085 ./smbus.c ./testBMP085.c

kroonen
Posts: 5
Joined: Sat Jan 19, 2013 2:23 pm

Re: C Code for using BMP085 with Raspberry Pi

Sat Jan 19, 2013 8:31 pm

It compiles but I get nothing as result??

root@raspberrypi:~# gcc -lm -Wall -o testBMP085 ./smbus.c ./testBMP085.c
./testBMP085.c: In function 'bmp085_Altitude':
./testBMP085.c:239:8: warning: implicit declaration of function 'pow' [-Wimplicit-function-declaration]
./testBMP085.c:239:18: warning: incompatible implicit declaration of built-in function 'pow' [enabled by default]
root@raspberrypi:~# ./testBMP085
root@raspberrypi:~#

kroonen
Posts: 5
Joined: Sat Jan 19, 2013 2:23 pm

Re: C Code for using BMP085 with Raspberry Pi

Sat Jan 19, 2013 8:59 pm

I found on adafruit that the b-version (512) is different, and is this the problem, and what should I change?

If you're using a version 2 Pi (512 M) then you'll have to change the I2C bus as it flipped from #0 to #1 in the version 2.

itsisme
Posts: 20
Joined: Sat Mar 09, 2013 2:16 am

Re: C Code for using BMP085 with Raspberry Pi

Sat Mar 09, 2013 2:22 am

Hello,

I am running a RPi V2 what should I change exactly due to the changed I2C (0 -> 1)?

I have it compiled (no errors) but runing it provides no info:

root@pi:~/projects/BMP085# ./testBMP085
root@pi:~/projects/BMP085#

The python script works fine.. But I would like to see if I can get the corrected sea level mod so Altitude is correct as mentioned : http://learn.adafruit.com/bmp085/using-the-bmp085 and using with this C project.

My PI at http://pi.gids.nl/

Chrs.

sanspi
Posts: 1
Joined: Sun Mar 10, 2013 4:52 pm

Re: C Code for using BMP085 with Raspberry Pi

Sun Mar 10, 2013 4:57 pm

if you get no output try this;
change char *fileName = "/dev/i2c-0";
to /dev/i2c-1

itsisme
Posts: 20
Joined: Sat Mar 09, 2013 2:16 am

Re: C Code for using BMP085 with Raspberry Pi

Sun Mar 10, 2013 8:41 pm

Indeed that worked fine...

Now for the altitude thats way off on my tool:

Temperature 17.4 C
Pressure 1003.24 hPa
Altitude 122.0 Meter

I noticed on th ewebsite some calculations with correction but how to adapt this:

//
// http://forums.netduino.com/index.php?/t ... re-sensor/
//
// altimeter_setting = (float)101325 * pow(((288-0.0065*known_altitude)/288),5.256);
// long pressureASL = (101325 + pressure) - altimeter_setting;
//

Ps my metric output:

printf("Temperature\t%0.1f C\n", ((double)temperature)/10);
printf("Pressure\t%0.2f hPa\n", ((double)pressure)/100);
printf("Altitude\t%0.1f Meter\n", ((double)altitude));

Our property is at 12 M I am in the edic so add 7 meter should result in about 19 a 20 m

Chrs...

Return to “Interfacing (DSI, CSI, I2C, etc.)”