Temperatur- und Drucksensoren BMP280 und BME280 an Microcontrollern per SPI oder I2C betreiben

https://bildung.social/deck/@oerinformatik/114120517692659575

https://oer-informatik.de/esp_bme280

tl/dr; (ca. 5 min Lesezeit): Breakoutsboards mit den günstige Temperatur- und Drucksensoren BMP280 und BME280 sind weit verbreitet und lassen sich mithilfe vieler Bibliotheken an Microcontrollern anbinden. Es werden in diesem Artikel die Unterschiede der Sensoren, deren Anschluss per SPI und I2C und deren Anbindung per C++ Code gezeigt. Dazu gibt es Beispielcodes, Hinweise auf mögliche Fehlerquellen alternativen zur Vorgegebenen Pin-/Adress-Belegung. (Zuletzt geändert am 11.11.2025)

Teilabschnitte dieses Artikels sind:

  • Welcher Sensor liegt vor?

  • Generelle Überlegungen zu Installation und Inbetriebnahme von Devices am Microcontrollerboard

  • Aufbau und Beispielcode mit der I2C-Schnittstelle

  • Individuelle Konfiguration per I2C-Schnittstelle

  • Aufbau und Beispielcode mit der SPI-Schnittstelle

  • Individuelle Konfiguration per SPI-Schnittstelle

Welcher Sensor liegt vor Dir? BME280 oder BMP280?

Es gibt unzählige Breakout-Boards für zwei sehr ähnlichen Temperatur/Druck Sensoren von Bosch: der BME280 und der BMP280 sind sich in Aussehen und Funktion zum Verwechseln ähnlich. Zunächst müssen wir erst einmal herausfinden, welcher Sensor auf dem jeweiligen Board verbaut wurde:

Temperatur- und Drucksensoren BME280 (links, kann auch Luftfeuchtigkeit) und BMP280 (rechts)
Temperatur- und Drucksensoren BME280 (links, kann auch Luftfeuchtigkeit) und BMP280 (rechts)

Auf den Breakout-Boards findet sich der eigentliche Sensor in einem kleinen Metallgehäuse - an diesem können die beiden Varianten voneinander abgegrenzt werden:

  • Den etwas funktionsreicheren BME280-Sensor (im Bild oben links) erkennt man bei genauem Hinsehen am eher quadratischeren Metallgehäuse, das eine winzige Öffnung fast mittig zu linken Seitenkante hat (gut zu erkennen auf den Datenblattseiten des Herstellers Bosch). Der BME280 kann neben Temperatur und Luftdruck auch noch die Luftfeuchtigkeit messen.

  • Demgegenüber hat der BMP280-Sensor (im Bild oben rechts) einen rechteckigen Sensor mit unterschiedlichen langen Kanten. Er ist etwas günstiger und kann nicht die Luftfeuchtigkeit messen. Das kleine Metallgehäuse hat eine Öffnung, die eher in der Ecke liegt (gut zu erkennen z.B. auch auf den Datenblattseiten des Herstellers Bosch).

Generelle Überlegungen zu Installation und Inbetriebnahme von Devices am Microcontrollerboard

Das Vorgehen ist eigentlich bei allen Geräten im Arduino-Kosmos identisch, unabhängig davon, ob es ein Sensor, ein Aktor, ein Display oder etwas völlig anderes ist:

  1. Typenbezeichnung suchen: Zuerst muss die Art und Typ des neuen Geräts herausgefunden werden. Manchmal steht dies auf der Platine, manchmal muss man im Netz nach Fotos/Pinouts suchen (siehe unten).

  2. Bibliothek wählen und installieren: Wird mit der Arduino-IDE entwickelt, so muss im Menü Werkzeuge / Bibliotheken verwalten eine passende Bibliothek für das Gerät gesucht werden. Häufig sind die Bibliotheken von Adafruit relativ gut. Wer eine andere IDE nutzt, ist hoffentlich fortgeschritten genug, die Bibliotheken selbständig einzubinden. Falls nicht, wäre der Wechsel zur Arduino-IDE eine Option.

  3. Pins des Geräts recherchieren und Logiklevel prüfen: Zum Anschließen des Geräts benötigen wir die Pinbezeichnungen der jeweils genutzten Platine. Nicht immer ist der Aufdruck auf den Platinen komplett, häufig wirkt ein zusätzliches Pinout (Bildersuche nach “Pinout Gerätebezeichnung”) Wunder. Dabei sollte im Datenblatt auch geprüft werden, ob das Logiklevel der Platine mit dem des Microcontrollers kompatibel ist (Arduino Uno: 5V, ESPs, Arduino Mega: 3,3V).

  4. Freie Pins des Microcontrollers wählen: Bevor wir verdrahten können, sollten wir uns noch entscheiden, welche Schnittstelle wir verwenden wollen (wenn das Gerät mehrere anbietet - z.B. SPI oder I2C). Dazu benötigen wir eine Liste der freien Pins unseres Microcontrollers, der für diese Schnittstelle geeignet ist (für einige ESPs beispielsweise in diesem Artikel). Ggf. hilft auch ein Blick in die Beispielquelltexte mit Hinweisen (s.u.).

  5. Verdrahten und mit Beispielcode erkunden: Dann kann das Gerät verdrahtet werden und über Beispiel-Quelltexte (Datei/ Beispiele häufig ganz unten: “Beispiele angepasster Bibliotheken”) erkundet werden.

In unserem konkreten Fall:

Zu (1) Typenbezeichnung suchen: BME280 - siehe oben

Zu (2) Bibliothek wählen und installieren: Werkzeuge / Bibliotheken verwalten

Die Suche nach “BME280” liefert u.a. die Bibliothek “Adafruit BME280 Library”, die ich in dem Beispiel nutze - andere Bibliotheken können aber ebenso eingesetzt werden:

Installieren der Bibliothek “Adafruit BME280 Library”
Installieren der Bibliothek “Adafruit BME280 Library”

Die Bibliothek benötigt eine Reihe von weiteren Abhängigkeiten, die direkt mit installiert werden können:

BME280 ist abhängig von BusIO und Unified Sensor
BME280 ist abhängig von BusIO und Unified Sensor

Aufbau und Beispielcode mit der I2C-Schnittstelle

Zu (3) Pins des Geräts recherchieren

Ich schließe den BME280/BMP280 zunächst per I2C-Schnittstelle an - hierfür sind neben der Spannungsversorgung (3V3, GND) noch die Signale Clock (SCL) und Data (SDA) nötig. Die beiden übrigen Pins (CSB, SDO) sind nur für den Anschluss per SPI-Schnittstelle nötig (oder zur Änderung der I2C-Adresse), können daher unbeschaltet bleiben.

Zu (4) Freie Pins des Microcontrollers wählen

Da wir den BME280/BMP280 per I2C-Schnittstelle anschließen wollen, nutzen wir einfach die vorgesehenen Pins:

  • bei ESP32 ist dies: GPIO21 für SDA und GPIO22 für SCL.

  • bei ESP8266 ist dies: D2 für SDA und D1 für SCL.

  • bei Arduino Uno ist dies: A4 für SDA und A5 für SCL.

Wenn bereits andere I2C Geräte an diesen Leitungen angeschlossen sind, dann kann der Sensor zusätzlich parallel angeschlossen werden - schließlich handelt es sich hierbei um ein Bus-System, dass genau für den Anschluss mehrere Geräte gedacht ist.

Es wären auch andere Pins möglich (siehe unten), aber so sparen wir uns etwas Konfigurationsarbeit.

Zu (5) Verdrahten und mit Beispielcode erkunden:

Der Aufbau sieht bei mir mit einem ESP32 Dev V1 so aus (vorsicht: ggf. ist die Pinleiste andersherum aufgelötet und alle Pins des Sensors gespiegelt):

Beispiel der Verdrahtung des BME280 per I2C an einem ESP32 Dev V1(erstellt mit Fritzing, weitere Quellen hier)
Beispiel der Verdrahtung des BME280 per I2C an einem ESP32 Dev V1(erstellt mit Fritzing, weitere Quellen hier)

Natürlich müssen bei einem anderen Microcontroller auch andere Pins verwendet werden, da die Adafruit_BMx280-Bibliothek die I2C-Standardpins der jeweiligen Boards verwendet. (Wie man von den Standardports abweichen kann erkläre ich weiter unten).

Wichtig ist zu beachten, dass die Pinanordnung des Sensors auch spiegelverkehrt sein kann, wenn die Pin-Leiste auf der anderen Seite angelötet wurde. Außerdem sind die Spannungsversorgungs-Schienen (rote und blaue Pinleisten außen) am Breadboard manchmal umgekehrt - also bitte immer alle Kontakte des eigenen Aufbaus prüfen und nicht einfach nach optischer Ähnlichkeit zusammenstecken!

Die I2C-Schnittstelle identifiziert Geräte am Bus über eine eindeutige Adresse. Jedes Gerät erkennt anhand dieser Adresse, wer Empfänger der Nachrichten am Bus sein soll. Die Adresse umfasst sieben Bit, es gibt also 2^7 = 128 verschiedene Adressen ( also 0 und 127 dezimal, 0x00-0x7F hexadezimal, 0b0000000 - 0b1111111 binär). Damit mehrere Geräte des gleichen Typs angeschlossen werden können, wird häufig nur ein Teil dieser Adresse durch den Hersteller fest vorgegeben, einige Bit können manuell konfiguriert werden. Bei den BMx280-Sensoren wird das letzte Bit angepasst - es können die I2C-Adressen 0x76 (binär: 0b1110110) oder 0x77 (binär: 0b1110111) konfiguriert werden. Im Fall der oben abgebildeten BMx280-Breakout-Boards wird das letzte Bit über den Pin SDO bestimmt: liegt hier ein HIGH an, reagiert der Sensor auf die Adresse 0x77, ist der Pin nicht verdrahtet oder LOW, dann regiert der Sensor auf 0x76.

Die genutzte Bibliothek spricht standardmäßig die Adresse 0x77 an. Wir müssen also entweder ein HIGH an SDO anlegen - oder aber die Software an die neue Adresse (0x76) anpassen (wie unten geschehen). Wir verlassen uns in dem Beispielcode nicht darauf, dass die IDE die Standardports der I2C-Schnittstelle erkennt und geben diese sicherheitshalber explizit an.Dadurch können wir die Pins auch individuell konfigurieren.

Intern nutzt der Adafruit_BMx280-Treiber eine Arduino-Bibliothek namens Wire, um die I2C-Kommunikation abzuwickeln. Mit Hilfe dieser Bibliothek können wir die I2C-Verbindung auch auf andere Pins legen, indem wir zu Beginn unseres Programms die Pin-Bezeichnungen über den Aufruf Wire.begin(MCU_SDA, MCU_SCL); übergeben. Natürlich müssen die beiden Variablen, die ich hier mal MCU_SDA und MCU_SCL genannt habe, vorher deklariert und initialisiert werden. Alternativ können auch direkt die Pinbezeichnungen übergeben werden (Wire.begin(18, 19);), was aber den Quelltext schlecht wartbar machen würde.

Ein minimales Programm, das den BMx280-Sensor mit der Adafruit-Bibliothek abfragt, könnte also so aussehen:

#include <Adafruit_BME280.h>

//-------------------------------------------------------------------------------------
// List of Input- and Output-devices and Pins
//-------------------------------------------------------------------------------------
// Datatype | Name of Variable    | Pin No. connected | Name, Behaviour*/
const int     MCU_SDA             = 21;               // GPIO of MCU connected to SDA-Pin of Sensor
const int     MCU_SCL             = 22;               // GPIO of MCU connected to SDCL-Pin of Sensor
const int     I2C_ADDR            = 0x76;             // if SD0 is floating or LOW 0x76, else 0x77

//-------------------------------------------------------------------------------------
// App-Settings
//-------------------------------------------------------------------------------------
#define SEALEVELPRESSURE_HPA (1013.25)
unsigned long delayTime             = 1000;
unsigned long serialMonitorBaudrate = 115200;

Adafruit_BME280 bme; 


void setup(){
  Serial.begin(serialMonitorBaudrate);        // Activate debugging via serial monitor
  
  Wire.begin(MCU_SDA, MCU_SCL);               // connection with individual I2C-Pins
  int status = bme.begin(I2C_ADDR, &Wire);    //status = bme.begin();
  
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring, i2c address");
    while (1) delay(10);
  }
  Serial.println("|Temperature | Pressure   | Altitude | Humidity |");
}


void loop() { 
    serialPrintMeasurement();
    delay(delayTime);
}


void serialPrintMeasurement() {
  char prBuffer[51]; // Print buffer
  float temp     = bme.readTemperature();
  float pressure = bme.readPressure() / 100.0F;
  float altitude = bme.readAltitude(SEALEVELPRESSURE_HPA);
  float humidity = bme.readHumidity();

  sprintf(prBuffer, "|    %2.1f °C | %4.1f hPa |   %3.1f m |   %2.1f %% |",temp, pressure, altitude, humidity);
  Serial.println(prBuffer);
}

Der seriellen Monitor (Strg+ Shift+M) sollte bei 115200 Baud eingestellt jetzt folgendes ausgeben (beim BME zusätzlich noch die Spalte “Humidity”):

Temperature | Pressure   | Altitude |
    24.0 °C | 1004.1 hPa |   76.7 m |
    23.9 °C | 1004.1 hPa |   76.8 m |
    23.9 °C | 1004.1 hPa |   76.7 m |
    23.8 °C | 1004.1 hPa |   76.2 m |
    23.7 °C | 1004.1 hPa |   76.5 m |

Wenn dieser Programmcode oder die vom Treiber mitgelieferten Beispielsketche (Datei/ Beispiele / ganz unten: BME280 bzw. BMP280 ) nicht gehen, dann hat das häufig die folgenden Ursachen:

  • Could not find a valid BME280 sensor, check wiring, i2c address: Die I2C-Adresse ist falsch konfiguriert oder wird nicht erkannt. Welche Adresse hat der Sensor überhaupt und ist alles richtig verdrahtet? Hier hilft ein Scan des I2C-Bus’ über das Beispielprogramm: Datei/ Beispiele / Wire/ WireScan. Im Normalfall reicht es, sowohl die 0x76 als auch die 0x77 als Adresse zu versuchen.

  • Ausgabe der Werte ist unrealistisch, z.B. 0.0 °C | 0.0 hPa | 44330.0 m |: Es handelt sich um den falschen Treiber für den Sensor - bitte nochmals prüfen, ob es ein BME280 oder BMP280 ist (siehe oben).

  • Die Pins wurden vertauscht - das passiert insbesondere, wenn die Beschriftung auf der Unterseite ist und man sich alles “auf dem Kopf” vorstellen muss.

Aufbau und Beispielcode mit der SPI-Schnittstelle

Wenn wir anstelle der I2C-Schnittstelle die SPI-Schnittstelle nutzen wollen, können wir die Überlegungen (1) Sensorart bestimmen und (2) Treiber wählen und installieren von oben übernehmen. Bei der Pin-Belegung gibt es aber Unterschiede:

Zu (3) Pins des Geräts recherchieren

SPI nutzt neben der Spannungsversorgung (3V3, GND) vier weitere Pins: die Clock (SCL), Datenausgang des Microcontrollers (MOSI, SDA), Dateneingang des Microcontrollers (MISO, SDO) und einen Pin zur Sensorauswahl (Chip Select, CSB). Wesentliche Unterschiede zu I2C sind also, dass wir über unterschiedliche Leitungen lesen und schreiben (“Multiplex”) und anstelle einer Adressierung dem Sensor über eine eigene digitale Leitung mitgeteilt wird, dass die Nachricht am Bus für ihn bestimmt wird (CSB).

Zu (4) Freie Pins des Microcontrollers wählen

Auch die SPI-Kommunikation mit dem BME280/BMP280 wollen wir zunächst über die vorgesehenen Pins lösen:

  • bei ESP32 ist dies: GPIO18 für SCL, GPIO19 für MISO/SDO, GPIO23 für MOSI/SDA und GPIO5 für CSB

  • bei ESP8266 ist dies: GPIO15/D5 für SCL, GPIO12/D6 für MISO/SDO, GPIO13/D7 für MOSI/SDA und GPIO15/D8 für CSB

  • bei Arduino Uno ist dies: D13 für SCL, D12 für MISO/SDO, D11 für MOSI/SDA und D10 für CSB

Es wären auch andere Pins möglich (siehe unten), aber so sparen wir uns etwas Konfigurationsarbeit.

Zu (5) Verdrahten und mit Beispielcode erkunden:

Der Aufbau sieht bei mir mit einem ESP32 Dev V1 so aus (vorsicht: ggf. ist die Pinleiste andersherum aufgelötet und alle Pins gespiegelt):

Beispiel der Verdrahtung des BME280 per SPI an einem ESP32 Dev V1 (erstellt mit Fritzing, weitere Quellen hier)
Beispiel der Verdrahtung des BME280 per SPI an einem ESP32 Dev V1 (erstellt mit Fritzing, weitere Quellen hier)

Der Adafruit-Treiber ist so generisch aufgebaut, dass sich nur wenige Codezeilen ändern, wenn statt der I2C-Schnittstelle die SPI-Schnittstelle verwendet wird.

  • Beim Aufruf des Treibers wird der Chip-Select-Pin übergeben - damit erkennt der Treiber automatisch, dass SPI verwendet werden soll: Adafruit_BMP280 bmp(BMP_CS);

  • Beim Starten der Messung wird keine I2C-Adresse übergeben: bmp.begin()

Sicherheitshalber verlassen wir uns nicht darauf, dass die Standard-SPI-Ports des Microcontrollers korrekt gefunden werden und nutzen die Möglichkeit, alle SPI-Pins (MISO, MOSI) explizit zu konfigurieren. Damit sollte das Auslesen per SPI in jedem Fall klappen. Mit unterschiedlichen CS-Pins können so auch einfach mehrere Sensoren/Aktoren an den SPI-Bus angeschlossen werden. Ein minimales Codebeispiel sieht so aus:

#include <Adafruit_BME280.h>

//-------------------------------------------------------------------------------------
// List of Input- and Output-devices and Pins
//-------------------------------------------------------------------------------------
// Datatype | Name of Variable    | Pin No. connected | Name, Behaviour*/
const int     BMP_CS              = 5;                // GPIO of MCU connected to SPI-Chip-Select-(CSB)-Pin
const int     BMP_MOSI            = 23;               // GPIO of MCU connected to SDA/SPI-MOSI-Pin
const int     BMP_MISO            = 19;               // GPIO of MCU connected to SDO/SPI-MISO-Pin
const int     BMP_CLK             = 18;               // GPIO of MCU connected to SCL/SPI-Clock-Pin

//-------------------------------------------------------------------------------------
// App-Settings
//-------------------------------------------------------------------------------------
#define SEALEVELPRESSURE_HPA (1013.25)
unsigned long delayTime             = 1000;
unsigned long serialMonitorBaudrate = 115200;

Adafruit_BME280 bme(BMP_CS, BMP_MOSI, BMP_MISO, BMP_CLK); // indivudual SPI-Pins

void setup(){
  Serial.begin(serialMonitorBaudrate);        // Activate debugging via serial monitor

  int status = bme.begin();
  
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring");
    while (1) delay(10);
  }
  Serial.println("|Temperature | Pressure   | Altitude | Humidity |");
}


void loop() { 
    serialPrintMeasurement();
    delay(delayTime);
}


void serialPrintMeasurement() {
  char prBuffer[51]; // Print buffer
  float temp     = bme.readTemperature();
  float pressure = bme.readPressure() / 100.0F;
  float altitude = bme.readAltitude(SEALEVELPRESSURE_HPA);
  float humidity = bme.readHumidity();

  sprintf(prBuffer, "|    %2.1f °C | %4.1f hPa |   %3.1f m |   %2.1f %% |",temp, pressure, altitude, humidity);
  Serial.println(prBuffer);
}

Quellen und weitere Informationen


Hinweis zur Nachnutzung als Open Educational Resource (OER)

Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: Temperatur- und Drucksensoren BMP280 und BME280 an Microcontrollern per SPI oder I2C betreiben” von oer-informatik.de (H. Stein), Lizenz: CC BY 4.0. Der Artikel wurde unter https://oer-informatik.de/esp_bme280 veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/mcu/arduino-esp. Stand: 11.11.2025.

[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: