Bosch BME280 Luftdruck- und Luftfeuchtigkeitssensor am Raspberry Pi

Gepostet von am 22. Januar 2016, 23:53

Der Bosch BME280 ist ein Sensor, der auf einer Fläche von 2,5 × 2,5 mm einen Luftfeuchtigkeitssensor, einen Luftdrucksensor und einen Temperatursensor vereint. Der Sensor kann per I2C oder SPI ausgelesen werden. Im Laufe des Jahres 2016 soll ein neuer Sensor BME680 erscheinen, der zum BME280 kompatibel ist und zusätzlich noch die Konzentration unterschiedlicher Gase messen kann.

Mehr Infos zum Sensor gibt es auf der Homepage von Bosch Sensortec unter folgendem Link:
http://www.bosch-sensortec.com/en/homepage/products_3/environmental_sensors_1/bme280/bme280_1

Bosch hat auch ein kleines Youtube-Video zum Sensor erstellt:

Anschluss und Beschaltung

Der Sensor befindet sich in einem kleinem SMD-Gehäuse, an das man nicht ohne weiteres Kabel anlöten kann. Zumindest kann man so keine dauerhaft zuverlässige Verbindung schaffen. Deshalb ist es sinnvoll dafür eine kleine Platine zu erstellen oder ein Breakoutboard zu verwenden. So eins gibt es zum Beispiel unter http://www.watterott.com/de/BME280-Breakout-Luftfeuchtigkeits-Druck-Tempertursensor

I2C-Bus am Raspberry Pi

Der BME280 kann direkt mit dem I2C-Bus vom Raspberry Pi oder Beaglebone Black verbunden werden. Der I2C-Bus muss natürlich vorher aktiviert werden. Beim Raspberry Pi passiert das mit dem Programm raspi-config unter Advanced Options. Der Sensor sollte am I2C-Bus mit der Adresse 0×76 erscheinen. Das kann mit dem Programmaufruf “i2cdetect -r 1” überprüft werden.

Ansteuerung mit i2c-dev-Library

Der Sensor kann mit der i2c-dev-Library von Linux ausgelesen werden. Damit das Programm kompiliert werden kann, müssen natürlich die entsprechenden header installiert werden.

Programm

Der BME280 wird bei der Produktion kalibriert und diese Werte werden im Sensor gespeichert. Zur Verwendung müssen diese Werte erst ausgelesen werden. Anschließend müssen die Sensorwerte mit relativ aufwendigen Formeln in die Ausgabewerte umgerechnet werden. Diese Formeln sind aber bereits als C-Quellcode im Datenblatt enthalten. Ich habe sie nur so weit abgewandelt, dass die Kalibrierwerte an die Funktionen übergeben werden. Ich habe die Version mit double-Variablen verwendet, da wir uns hier auf einem Computer befinden, der ausreichend Rechenleistung für die Verarbeitung von Gleitkommazahlen hat. An die Funktionen werden die Sensor- und Kalibrierwerte als int übergeben und sie liefern den umgerechneten Wert als double zurück. Hier der Quellcode:

int t_fine;

double BME280_compensate_P_double(int adc_P,int dig_P1, int dig_P2, int dig_P3, int dig_P4, int dig_P5, int dig_P6, int dig_P7, int dig_P8, int dig_P9)
{
	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;
}
double BME280_compensate_H_double(int adc_H, int dig_H1, int dig_H2, int dig_H3, int dig_H4, int dig_H5, int dig_H6)
{
	double var_H;
	var_H = (((double)t_fine) - 76800.0);
	var_H = (adc_H - (((double)dig_H4) * 64.0 + ((double)dig_H5) / 16384.0 * var_H)) * (((double)dig_H2) / 65536.0 * (1.0 + ((double)dig_H6) / 67108864.0 * var_H * (1.0 + ((double)dig_H3) / 67108864.0 * var_H)));
	var_H = var_H * (1.0 - ((double)dig_H1) * var_H / 524288.0);
	if (var_H > 100.0)
		var_H = 100.0;
	else if (var_H < 0.0)
		var_H = 0.0;
	return var_H;
}
double BME280_compensate_T_double(int adc_T, int dig_T1, int dig_T2, int dig_T3)
{
	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 = (int)(var1 + var2);
	T = (var1 + var2) / 5120.0;
	return T;
}

Bevor die Sensoren Werte liefern, müssen sie durch Schreiben in die Register 0xf2 (Luftfeuchte) und 0xf4 (Luftdruck und Temperatur) aktiviert werden. Dabei muss zuerst in das Register 0xf2 und dann in das Register 0xf4 geschrieben werden, sonst werden die Einstellungen vom Register 0xf2 nicht übernommen.

Leider ist die Byte-Order bei den Kalibrierwerten und den Sensorwerten unterschiedlich, sodass die Sensorwerte mit der Funktion swap_bytes erst in die richtige Reihenfolge gebracht werden müssen.

Das komplette Programm sieht dann so aus:

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

int t_fine;

double BME280_compensate_P_double(int adc_P,int dig_P1, int dig_P2, int dig_P3, int dig_P4, int dig_P5, int dig_P6, int dig_P7, int dig_P8, int dig_P9)
{
	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;
}
double BME280_compensate_H_double(int adc_H, int dig_H1, int dig_H2, int dig_H3, int dig_H4, int dig_H5, int dig_H6)
{
	double var_H;
	var_H = (((double)t_fine) - 76800.0);
	var_H = (adc_H - (((double)dig_H4) * 64.0 + ((double)dig_H5) / 16384.0 * var_H)) * (((double)dig_H2) / 65536.0 * (1.0 + ((double)dig_H6) / 67108864.0 * var_H * (1.0 + ((double)dig_H3) / 67108864.0 * var_H)));
	var_H = var_H * (1.0 - ((double)dig_H1) * var_H / 524288.0);
	if (var_H > 100.0)
		var_H = 100.0;
	else if (var_H < 0.0)
		var_H = 0.0;
	return var_H;
}
double BME280_compensate_T_double(int adc_T, int dig_T1, int dig_T2, int dig_T3)
{
	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 = (int)(var1 + var2);
	T = (var1 + var2) / 5120.0;
	return T;
}

unsigned short int swap_bytes(unsigned short int input)
{
	return (((input & 0xff) << 8) | ((input >> 8) & 0xff));
}

int main(void)
{
	int file;
	int addr=0x76;		// address of BME280
	int x;
	int adc_T,dig_T1,dig_T2,dig_T3;
	int adc_H,dig_H1,dig_H2,dig_H3,dig_H4,dig_H5,dig_H6;
	int adc_P,dig_P1,dig_P2,dig_P3,dig_P4,dig_P5,dig_P6,dig_P7,dig_P8,dig_P9;
	float temperature,humidity,pressure;

	printf("BME280\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);
	}
	i2c_smbus_write_byte_data(file,0xf2,0x01);	// activate humidity measurement
	i2c_smbus_write_byte_data(file,0xf4,0x27);	// activate temperature measurement

	dig_P1=i2c_smbus_read_word_data(file, 0x8e);
	dig_P2=i2c_smbus_read_word_data(file, 0x90);
	dig_P3=i2c_smbus_read_word_data(file, 0x92);
	dig_P4=i2c_smbus_read_word_data(file, 0x94);
	dig_P5=i2c_smbus_read_word_data(file, 0x96);
	dig_P6=i2c_smbus_read_word_data(file, 0x98);
	dig_P7=i2c_smbus_read_word_data(file, 0x9a);
	dig_P8=i2c_smbus_read_word_data(file, 0x9c);
	dig_P9=i2c_smbus_read_word_data(file, 0x9f);
	dig_T1=i2c_smbus_read_word_data(file, 0x88);
	dig_T2=i2c_smbus_read_word_data(file, 0x8a);
	dig_T3=i2c_smbus_read_word_data(file, 0x8c);
	dig_H1=i2c_smbus_read_byte_data(file, 0xa1);
	dig_H2=i2c_smbus_read_word_data(file, 0xe1);
	dig_H3=i2c_smbus_read_byte_data(file, 0xe3);
	dig_H4=((i2c_smbus_read_byte_data(file, 0xe4))<<4)+(i2c_smbus_read_byte_data(file, 0xe5)&0x0f);
	dig_H5=((i2c_smbus_read_byte_data(file, 0xe5)&0xf0)>>4)+(i2c_smbus_read_byte_data(file, 0xe6)>>4);
	dig_H6=i2c_smbus_read_byte_data(file, 0xe7);
	printf("dig_H1=%i, dig_H2=%i, dig_H3=%i, dig_H4=%i, dig_H5=%i, dig_H6=%i\n",dig_H1,dig_H2,dig_H3,dig_H4,dig_H5,dig_H6);
	printf("dig_P1=%i, dig_P2=%i, dig_P3=%i, dig_P4=%i, dig_P5=%i, dig_P6=%i, dig_P7=%i, dig_P8=%i, dig_P=%i\n",dig_P1,dig_P2,dig_P3,dig_P4,dig_P5,dig_P6,dig_P7,dig_P8,dig_P9);
	printf("dig_T1=%i, dig_T2=%i, dig_T3=%i\n",dig_T1,dig_T2,dig_T3);

	adc_P=((swap_bytes(i2c_smbus_read_word_data(file, 0xF7)))<<4)+(((i2c_smbus_read_byte_data(file,0xF9))>>4)&0x0F);
	adc_T=((swap_bytes(i2c_smbus_read_word_data(file, 0xFA)))<<4)+(((i2c_smbus_read_byte_data(file,0xFC))>>4)&0x0F);
	adc_H=swap_bytes(i2c_smbus_read_word_data(file, 0xFD));

	printf("0x%0x\n",adc_T);
	printf("0x%0x\n",adc_H);
	printf("0x%0x\n",adc_P);

	temperature=BME280_compensate_T_double(adc_T,dig_T1,dig_T2,dig_T3);
	humidity=BME280_compensate_H_double(adc_H,dig_H1,dig_H2,dig_H3,dig_H4,dig_H5,dig_H6);
	pressure=BME280_compensate_P_double(adc_P,dig_P1,dig_P2,dig_P3,dig_P4,dig_P5,dig_P6,dig_P7,dig_P8,dig_P9);

	printf("Temperatur:  %f C\n",temperature);
	printf("Luftfeuchte: %f %%rH\n",humidity);
	printf("Luftdruck:   %f hPa\n",pressure/100.0);

	close(file);

	return 0;
}

Kategorien ,

Stichworte I2C,Bosch,BME280,Raspberry Pi,Beaglebone Black,Temperature,Humidity


Kommentare

Kommentarfunktion für diesen Artikel geschlossen.