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:
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.
Kommentare
Kommentarfunktion für diesen Artikel geschlossen.