Luftdrucksensor Bosch BMP280

Gepostet von am 12. März 2018, 20:21

Der Bosch BMP280 ist quasi der kleine Bruder vom BME280 und besitzt nur einen Luftdruck- und einen Temperatursensor. Einen Luftfeuchtesensor hat er nicht. Trotzdem kann das für viele Anwendungen ausreichen, wo nur die Meereshöhe von Interesse ist. Die Höhensteuerung von Quadrokoptern wäre so ein Anwendungsfall.

Der Sensor kann den Luftdruck im Bereich von 300 – 1100 hPA messen. Das ist ein bisschen weniger als der LPS25H von ST, aber immer noch ausreichend. Damit kann die äquivalente Meereshöhe von -500 bis +9000 Meter gemessen werden und das dürfte für die meisten Anwendungen auf der Erde ausreichen.

Ansteuerung und Anschluss

Der Sensor kann über I2C oder SPI angesteuert und ausgelesen werden. Wir beschränken uns hier auf die Ansteuerung über I2C.

Der elektrische Anschluss ist sehr einfach. Er benötigt nur einen zusätzlichen 100 nF Filterkondensator.

Auslesen über I2C

Der BMP280 hat die I2C-Adresse 0×77. Bevor der Sensor Werte liefert, muss man ihn in den normal mode bringen und das oversampling zum Beispiel auf x1 setzen. Dies geschieht, indem man den Wert 0×27 in das Register 0xf4 schreibt.

Der Sensor liefert die Messwerte leider nicht direkt, sondern man muss sie errechnen. Dazu muss man aber erst einige Konfigurationswerte auslesen.

Bosch Library für den BMP280

Zum Errechnen der Messwerte bietet Bosch eine Library an. Bosch empfiehlt, immer die aktuelle Version zu verwenden, die man in ihrem github-Repository findet: https://github.com/BoschSensortec/BMP280_driver

Die benötigten Funktionen sind aber auch im Datenblatt abgedruckt und können genauso gut verwendet werden. Ich habe die Variante mit floats verwendet, da mein Programm auf einem Raspberry Pi läuft und der genügend Rechenleistung zum Berechnen der floats hat.

Hier die Funktionen:

// Returns temperature in DegC, double precision. Output value of “51.23” equals 51.23 DegC. 
// t_fine carries fine temperature as global value
BMP280_S32_t t_fine;
double bmp280_compensate_T_double(BMP280_S32_t adc_T)
{
	double var1, var2, T;
	var1 = (((double)adc_T)/16384.0 - ((double)dig_T1)/1024.0) * ((double)dig_T2); var2 = ((((double)adc_T)/131072.0 - ((double)dig_T1)/8192.0) * (((double)adc_T)/131072.0 - ((double) dig_T1)/8192.0)) * ((double)dig_T3); 
	t_fine = (BMP280_S32_t)(var1 + var2);
	T = (var1 + var2) / 5120.0;
	return T;
}

// Returns pressure in Pa as double. Output value of “96386.2” equals 96386.2 Pa = 963.862 hPa
double bmp280_compensate_P_double(BMP280_S32_t adc_P) 
{
	double var1, var2, p;
	var1 = ((double)t_fine/2.0) - 64000.0;
	var2 = var1 * var1 * ((double)dig_P6) / 32768.0;
	var2 = var2 + var1 * ((double)dig_P5) * 2.0;
	var2 = (var2/4.0)+(((double)dig_P4) * 65536.0);
	var1 = (((double)dig_P3) * var1 * var1 / 524288.0 + ((double)dig_P2) * var1) / 524288.0; var1 = (1.0 + var1 / 32768.0)*((double)dig_P1);
	if (var1 == 0.0)
	{
		return 0; // avoid exception caused by division by zero 
	}
	p = 1048576.0 - (double)adc_P;
	p = (p - (var2 / 4096.0)) * 6250.0 / var1;
	var1 = ((double)dig_P9) * p * p / 2147483648.0; 
	var2 = p * ((double)dig_P8) / 32768.0;
	p = p + (var1 + var2 + ((double)dig_P7)) / 16.0; 
	return p;
}

Die Variablen wie dig_P1 sind die vorher schon angesprochenen Konfigurationswerte, die vor Aufruf der Funktionen ausgelesen werden müssen und als globale Variablen zur Verfügung gestellt werden müssen. Weiterhin muss man darauf achten, dass man zuerst die Funktion zur Berechnung der Temperatur aufruft, da der ermittelte Wert zur Temperaturkompensation des Luftdrucks verwendet wird.

Außerdem empfiehlt Bosch, dass die aktuellen Sensorwerte für Temperatur und Luftdruck in einem I2C-Block-Read-Zugriff ausgelesen werden, weil nur so verhindert wird, dass die gerade ausgelesenen Werte teilweise von aktuelleren überschrieben werden, und die Datenintegrität gewahrt wird. In meinem unten stehenden Programm habe ich das so umgesetzt.

Programm zum Auslesen des Sensors

Hier ist das komplette Programm, mit dem man den Sensor auf einem Raspberry Pi oder BeagleBone auslesen kann.

#include <stdio.h>
#include <stdlib.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <unistd.h>

//typedefs for bosch library
typedef int BMP280_S32_t;
typedef unsigned int BMP280_U32_t;

//global variables for bosch library
BMP280_U32_t dig_T1=0;
BMP280_S32_t dig_T2=0;
BMP280_S32_t dig_T3=0;
BMP280_U32_t dig_P1=0;
BMP280_S32_t dig_P2=0;
BMP280_S32_t dig_P3=0;
BMP280_S32_t dig_P4=0;
BMP280_S32_t dig_P5=0;
BMP280_S32_t dig_P6=0;
BMP280_S32_t dig_P7=0;
BMP280_S32_t dig_P8=0;
BMP280_S32_t dig_P9=0;

// Returns temperature in DegC, double precision. Output value of “51.23” equals 51.23 DegC. 
// t_fine carries fine temperature as global value
BMP280_S32_t t_fine;
double bmp280_compensate_T_double(BMP280_S32_t adc_T)
{
	double var1, var2, T;
	var1 = (((double)adc_T)/16384.0 - ((double)dig_T1)/1024.0) * ((double)dig_T2); var2 = ((((double)adc_T)/131072.0 - ((double)dig_T1)/8192.0) * (((double)adc_T)/131072.0 - ((double) dig_T1)/8192.0)) * ((double)dig_T3); 
	t_fine = (BMP280_S32_t)(var1 + var2);
	T = (var1 + var2) / 5120.0;
	return T;
}

// Returns pressure in Pa as double. Output value of “96386.2” equals 96386.2 Pa = 963.862 hPa
double bmp280_compensate_P_double(BMP280_S32_t adc_P) 
{
	double var1, var2, p;
	var1 = ((double)t_fine/2.0) - 64000.0;
	var2 = var1 * var1 * ((double)dig_P6) / 32768.0;
	var2 = var2 + var1 * ((double)dig_P5) * 2.0;
	var2 = (var2/4.0)+(((double)dig_P4) * 65536.0);
	var1 = (((double)dig_P3) * var1 * var1 / 524288.0 + ((double)dig_P2) * var1) / 524288.0; var1 = (1.0 + var1 / 32768.0)*((double)dig_P1);
	if (var1 == 0.0)
	{
		return 0; // avoid exception caused by division by zero 
	}
	p = 1048576.0 - (double)adc_P;
	p = (p - (var2 / 4096.0)) * 6250.0 / var1;
	var1 = ((double)dig_P9) * p * p / 2147483648.0; 
	var2 = p * ((double)dig_P8) / 32768.0;
	p = p + (var1 + var2 + ((double)dig_P7)) / 16.0; 
	return p;
}

int main(void)
{
	int file;
	int addr=0x77;		// adress of BMP280
	char a=0;

	__u8 buf[6];	// buffer for block read
	int temp=0,press=0; // output registers
	double temp_out=0.0,press_out=0.0; // output registers for calculated values

	printf("Bosch BMP280\n");
	if((file=open("/dev/i2c-1",O_RDWR))<0)	// open i2c-bus
	{
		perror("cannot open i2c-1");
		exit(1);
	}
	if(ioctl(file, I2C_SLAVE, addr)<0)	// open slave
	{
		perror("cannot open slave address");
		exit(1);
	}

	a=i2c_smbus_read_byte_data(file,0xd0);	// read id
	printf("id:        %02X\n",a);
	a=i2c_smbus_read_byte_data(file,0xf3);	// read status
	printf("status:    %02X\n",a);
	a=i2c_smbus_read_byte_data(file,0xf4);	// read ctrl_meas
	printf("ctrl_meas: %02X\n",a);
	if(a!=0x27)	// check if device active
	{
		printf("setting device to normal mode and oversampling x1\n");
		i2c_smbus_write_byte_data(file,0xf4,0x27);	// setting device to normal mode and oversampling x1
		a=i2c_smbus_read_byte_data(file,0xf4);	// read ctrl_meas
		printf("ctrl_meas: %02X\n",a);
	}
	a=i2c_smbus_read_byte_data(file,0xf5);	// read config
	printf("config:    %02X\n",a);

	dig_T1=i2c_smbus_read_word_data(file, 0x88);	// read dig_T1
	printf("dig_T1:    0x%04X = %i\n",(unsigned short)dig_T1,dig_T1);
	dig_T2=(signed short)i2c_smbus_read_word_data(file, 0x8a);	// read dig_T2
	printf("dig_T2:    0x%04X = %i\n",(unsigned short)dig_T2,dig_T2);
	dig_T3=(signed short)i2c_smbus_read_word_data(file, 0x8c);	// read dig_T3
	printf("dig_T3:    0x%04X = %i\n",(unsigned short)dig_T3,dig_T3);
	dig_P1=i2c_smbus_read_word_data(file, 0x8e);	// read dig_P1
	printf("dig_P1:    0x%04X = %i\n",(unsigned short)dig_P1,dig_P1);
	dig_P2=(signed short)i2c_smbus_read_word_data(file, 0x90);	// read dig_P2
	printf("dig_P2:    0x%04X = %i\n",(unsigned short)dig_P2,dig_P2);
	dig_P3=(signed short)i2c_smbus_read_word_data(file, 0x92);	// read dig_P3
	printf("dig_P3:    0x%04X = %i\n",(unsigned short)dig_P3,dig_P3);
	dig_P4=(signed short)i2c_smbus_read_word_data(file, 0x94);	// read dig_P4
	printf("dig_P4:    0x%04X = %i\n",(unsigned short)dig_P4,dig_P4);
	dig_P5=(signed short)i2c_smbus_read_word_data(file, 0x96);	// read dig_P5
	printf("dig_P5:    0x%04X = %i\n",(unsigned short)dig_P5,dig_P5);
	dig_P6=(signed short)i2c_smbus_read_word_data(file, 0x98);	// read dig_P6
	printf("dig_P6:    0x%04X = %i\n",(unsigned short)dig_P6,dig_P6);
	dig_P7=(signed short)i2c_smbus_read_word_data(file, 0x9a);	// read dig_P7
	printf("dig_P7:    0x%04X = %i\n",(unsigned short)dig_P7,dig_P7);
	dig_P8=(signed short)i2c_smbus_read_word_data(file, 0x9c);	// read dig_P8
	printf("dig_P8:    0x%04X = %i\n",(unsigned short)dig_P8,dig_P8);
	dig_P9=(signed short)i2c_smbus_read_word_data(file, 0x9e);	// read dig_P9
	printf("dig_P9:    0x%04X = %i\n",(unsigned short)dig_P9,dig_P9);

	a=i2c_smbus_read_i2c_block_data(file,0xf7,6,buf);	// read block beginning at 0xf7 as recommended in datasheet
	printf("read %i bytes\n",a);
	temp=(buf[3]<<12)+(buf[4]<<4)+(buf[5]>>4);	// generate temperature value from buffer
	printf("temp:    0x%08X = %i\n",(unsigned)temp,temp);
	press=(buf[0]<<12)+(buf[1]<<4)+(buf[2]>>4);	// generate pressure value from buffer
	printf("press:   0x%08X = %i\n",(unsigned)press,press);

	temp_out=bmp280_compensate_T_double(temp);	// call bosch library function for temperature
	printf("calculated temperature: %f C\n",temp_out);

	press_out=bmp280_compensate_P_double(press);	// call bosch library function for pressure
	printf("calculated pressure: %f Pa = %f hPa\n",press_out,press_out/100.0);

	close(file);

	return 0;
}

Bei mir wurden dann folgende Werte ausgelesen:

Bosch BMP280

Verfügbarkeit und Evalboards.

Der BMP280 ist auf dem Enviro pHAT von Pimoroni verbaut. Dadurch ist ein einfacher Testaufbau mit einem Raspberry Pi möglich.

Der Sensor hat bei mir auf Anhieb problemlos funktioniert und liefert Luftdruckwerte, die mit der Wettervorhersage sehr gut übereinstimmen.


Kategorien ,

Stichworte Bosch,BMP280,Luftdruck,Temperatur,Raspberry Pi,I2C,MEMS,Sensor


Kommentare

Kommentarfunktion für diesen Artikel geschlossen.