Mess­da­ten mit BME680 und ESP32 anzeigen

Lese­zeit: 11 Minu­ten

Seite als PDF

Dar­stel­lung der Daten

Die Mess­da­ten sol­len auf einem LCD und im nächs­ten Schritt zusätz­lich auf einer Web­sei­te dar­ge­stellt wer­den. Die Ver­än­de­rung der Luft­qua­li­tät lässt sich gut beobachten:bei geschlos­se­nen Fens­ter steigt der Wert, sobald für Durch­lüf­tung gesorgt wird, sinkt der Wert kontinuierlich.

Die Hard­ware

Der Bosch Sen­sor BME680 misst Tem­pe­ra­tur, Luft­feuch­tig­keit und den Luft­druck. Außer­dem ent­hält er einen MOX (Metalloxid)-Sensor. Das beheiz­te Metall­oxid ändert sei­nen Wider­stand je nach Kon­zen­tra­ti­on der flüch­ti­gen orga­ni­schen Ver­bin­dun­gen (VOC = Vola­ti­le Orga­nic Com­pounds) in der Luft.

Vie­le Mate­ria­li­en in Innen­räu­men set­zen Gase frei:

  • Rei­ni­gungs- und Pflegemittel
  • Möbel, Ein­rich­tungs­ge­gen­stän­de
  • Tep­pi­che, Tapeten
  • Bau­ma­te­ria­li­en (Far­ben)
  • elek­tro­ni­sche Gerä­te (Com­pu­ter, Drucker)
  • Heiz­ge­rä­te (Her­de, Öfen)
  • mensch­li­che Atem­luft, Schweiß

Die Luft­qua­li­tät wird zunächst als Wider­stand in Ohm gemes­sen. Je höher die Kon­zen­tra­ti­on von VOC, des­to gerin­ger ist der Widerstandswert.

Ein inter­ner Algo­rith­mus der pro­prie­tä­ren Bosch-Soft­ware (BSEC) wan­delt die­sen Roh­wert in einen Wert für die Luft­qua­li­tät (IAQ = Index for Air Qua­li­ty) um.

Die Tabel­le zeigt den IAQ und die Bewer­tung der Luftqualität:

Das Umwelt­bun­des­amt bewer­tet den CO2 Gehalt in der Raum­luft in ver­schie­de­nen Stufen:

Luft­qua­li­tät in Innenräumen

Raum­luft­ka­te­go­rie IDA (indoor Air)Beschrei­bungKon­zen­tra­ti­on CO2 in ppm
IDA 1Hohe Raum­luft­qua­li­tät≤ 800
IDA 2Mitt­le­re Raumluftqualität> 800 - 1000
IDA 3Mäßi­ge Raumluftqualität> 1000 - 1400
IDA 4Nied­ri­ge Raumluftqualität> 1400

Quel­le: https://​www​.umwelt​bun​des​amt​.de/​s​i​t​e​s​/​d​e​f​a​u​l​t​/​f​i​l​e​s​/​m​e​d​i​e​n​/​p​d​f​s​/​k​o​h​l​e​n​d​i​o​x​i​d​_​2​0​0​8​.​pdf

Dar­stel­lung der Messwerte

alle Mess­wer­te zu ver­schie­de­nen Zeit­punk­ten im gut durch­lüf­te­tem Raum

Auf dem LCD

Im Seri­el­len Monitor

Auf einer Webseite

Natür­lich kommt die Genau­ig­keit die­ser Mess­wer­te nicht an die Prä­zi­si­on pro­fes­sio­nel­ler Mess­ge­rä­te her­an. Sie kön­nen aber zuver­läs­si­ge Hin­wei­se auf die Qua­li­tät der Raum­luft geben. Die Daten für Tem­pe­ra­tur, Luft­feuch­tig­keit und Luft­druck ste­hen kurz nach der Akti­vie­rung des Sen­sors zur Ver­fü­gung. Der MOX-Sen­sor benö­tigt bis zu 30 Minu­ten, um ers­te Mess­da­ten anzu­zei­gen. Sta­bi­li­tät und Zuver­läs­sig­keit der Mess­da­ten sind erst nach meh­re­ren Tagen gewähr­leis­tet.
Der Wert für die Genau­ig­keit IAQ muss 3 erreichen.

Benö­tig­te Bauteile

  • BME680
  • LCD-Dis­play I2C 1602
  • Lei­tungs­dräh­te

Board instal­lie­ren

Instal­lie­re mit dem Board­ver­wal­ter das pas­sen­de Board:

Hard­ware anschließen

BME680

VCC ⇒ rot

GND ⇒ schwarz

SCL ⇒ gelb: 22

SDA ⇒ grün: 21

Die Hex-Adres­se ist 0×77 (BME68X_I2C_ADDR_HIGH).
Ein Ver­bin­dung von SDO zu GND ändert die Adres­se auf 0×76 (BME68X_I2C_ADDR_LOW).
Quel­le: https://​joy​-it​.net/​f​i​l​e​s​/​f​i​l​e​s​/​P​r​o​d​u​k​t​e​/​S​E​N​-​B​M​E​6​8​0​/​S​E​N​-​B​M​E​6​8​0​_​A​n​l​e​i​t​u​n​g​_​2​0​2​4​-​0​4​-​1​1​.​pdf

LCD

VCC ⇒ 5 V

GND GND

SDA ⇒ 21

SCL ⇒ 22

Benö­tig­te Bibliotheken

Die Biblio­thek BSEC ist eine pro­prie­tä­re Biblio­thek, der Quell­text steht nicht zur Ver­fü­gung.
Der Com­pi­ler zeigt eine ent­spre­chen­de Meldung:

Biblio­thek BSEC Soft­ware Libra­ry wur­de als vor­kom­pi­liert ange­ge­ben:

Außer­dem steht sie nur für die Archi­tek­tu­ren SAMD (UNO R4/UNO R4 WiFi ) und ESP (ESP32 oder ESP8266) zur Verfügung.

Funk­tio­nen der Biblio­thek BSEC

Schlüs­sel­wortAkti­onAus­ga­be
run­In­Sta­tus()Sta­tus des Sen­sors feststellen0 = noch nicht bereit, 1 = bereit
iaq­Ac­cu­ra­cy()Genau­ig­keit des IAQ-Wertes0 - 3
tem­pe­ra­tu­re()Tem­pe­ra­tur messenMess­be­reich: -40 - 85°C
humi­di­ty()Luft­feuch­tig­keit messen0 - 100%
pres­su­re()Luft­druck messen300 – 1100 hPa
gas­Re­sis­tance()Wider­stands­wert in kOhm des MOX-SensorsJe nied­ri­ger der Wert, des­to höher ist die Kon­zen­tra­ti­on der VOCs
breath­Vo­cE­qui­va­lent()VOC-Kon­zen­tra­ti­on auf Basis des sta­ti­schen IAQs schätzenAnga­be in ppm (parts per million)
0,5 - 15
iaq()Luft­qua­li­tät berechnen0 - 500
sta­ti­cIaq()Sta­ti­sche Luft­qua­li­tät berechnen0 -500
co2Equivalent()CO2 auf Basis des sta­ti­schen IAQs schätzenAnga­be in ppm (parts per million)

Der Wert für sta­ti­cIaq ist für sta­tio­nä­re Gerä­te opti­miert. Er berech­net auf der Basis der bis­he­ri­gen Daten den Mess­wert. iaq() ist für mobi­le Anwen­dun­gen gedacht.

Das Pro­gramm für die Dar­stel­lung der Mess­wer­te auf dem LCD

Biblio­the­ken ein­bin­den und Varia­ble definieren

#include "bsec.h"
#include "LCDIC2.h"

// 4-zeiliges LCD
LCDIC2 lcd(0x27, 20, 4);

// Objekt (iaqSensor) der Klasse Bsec definieren
Bsec iaqSensor;

Der set­up-Teil

void setup() 
{
  Serial.begin(9600);

  // auf Serielle Verbindung warten
  while (!Serial);
  delay(1000);

  // LCD starten
  lcd.begin();

  // Cursor "verstecken"
  lcd.setCursor(false);

  // Hex-Adresse des BME680
  // BME68X_I2C_ADDR_HIGH = 0x77 (Standard)
  // BME68X_I2C_ADDR_LOW = 0x76 (Verbindung SDO zu GND)
  iaqSensor.begin(BME68X_I2C_ADDR_HIGH, Wire);
  
  // Array der verfügbaren Sensoren
  bsec_virtual_sensor_t sensorList[13] = 
  {
    BSEC_OUTPUT_IAQ,
    BSEC_OUTPUT_STATIC_IAQ,
    BSEC_OUTPUT_CO2_EQUIVALENT,
    BSEC_OUTPUT_BREATH_VOC_EQUIVALENT,
    BSEC_OUTPUT_RAW_TEMPERATURE,
    BSEC_OUTPUT_RAW_PRESSURE,
    BSEC_OUTPUT_RAW_HUMIDITY,
    BSEC_OUTPUT_RAW_GAS,
    BSEC_OUTPUT_STABILIZATION_STATUS,
    BSEC_OUTPUT_RUN_IN_STATUS,
    BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
    BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
    BSEC_OUTPUT_GAS_PERCENTAGE
  };

  // BSEC_SAMPLE_RATE_ULP: Ultra Low Powermode -> Messung alle 5 Minuten
  // BSEC_SAMPLE_RATE_LP: Low Powermode -> Messung alle 3 Sekunden
  iaqSensor.updateSubscription(sensorList, 13, BSEC_SAMPLE_RATE_LP);
}

Der loop-Teil

void loop() 
{
  // wenn der Sensor arbeitet ...
  if (iaqSensor.run()) 
  {
    // Messwerte ermitteln, in Strings umwandeln, . durch , ersetzen
    String Temperatur = String(iaqSensor.temperature);
    Temperatur.replace(".", ",");

    String Luftfeuchtigkeit = String(iaqSensor.humidity);
    Luftfeuchtigkeit.replace(".", ",");

    String Luftdruck = String(iaqSensor.pressure / 100);
    Luftdruck.replace(".", ",");

    String IAQ = String(iaqSensor.iaq);
    IAQ.replace(".", ",");

    String StatischerIAQ = String(iaqSensor.staticIaq);
    StatischerIAQ.replace(".", ",");

    String CO2 = String(iaqSensor.co2Equivalent);
    CO2.replace(".", ",");

    String VOC = String(iaqSensor.breathVocEquivalent);
    VOC.replace(".", ",");

    String GasInProzent = String(iaqSensor.gasPercentage);
    GasInProzent.replace(".", ",");

    String WiderstandMOXSensor = String(iaqSensor.gasResistance / 1000);
    WiderstandMOXSensor.replace(".", ",");
    // Ausgabe Serieller Monitor
    Serial.println("Statusmeldungen:");
    Serial.println("-----------------------------");
    Serial.println("Sensor bereit (0-1): " + String(int(iaqSensor.runInStatus)));
    Serial.println("Genauigkeit IAQ (0-3): " + String(int(iaqSensor.iaqAccuracy)));
    Serial.println("-----------------------------");
    Serial.println("Messwerte:");
    Serial.println("-----------------------------");
    Serial.println("Temperatur: " + Temperatur + " °C");
    Serial.println("Feuchtigkeit: " + Luftfeuchtigkeit + " %");
    Serial.println("Luftdruck: " + Luftdruck + " hPa");
    Serial.println("IAQ: " + IAQ);
    Serial.println("Statischer IAQ: " + StatischerIAQ);
    Serial.println("Schätzung CO2: " + CO2 + " ppm");
    Serial.println("Gas (0-100): " + GasInProzent + " %");
    Serial.println("Widerstandswert MOX-Sensor: " + WiderstandMOXSensor + " kOhm");
    Serial.println("VOC: " + VOC);
    Serial.println("-----------------------------");

    // Ausgabe LCD
    lcd.setCursor(0, 0);
    lcd.print("Temperatur: " + Temperatur + "\337C");
    lcd.setCursor(0, 1);
    lcd.print("Feuchtigkeit: " + Luftfeuchtigkeit + "%");
    lcd.setCursor(0, 2);
    lcd.print("IAQ: " + IAQ);
    lcd.setCursor(0, 3);
    lcd.print("CO2: " + CO2 + " ppm");
  }
}

Das Pro­gramm mit der Dar­stel­lung der Mess­wer­te auf einer Webseite

Biblio­the­ken ein­bin­den und Varia­ble definieren

#include "bsec.h"
#include "WiFi.h"
#include "WebServer.h"
#include "time.h"

// Objekt (iaqSensor) der Klasse Bsec definieren
Bsec iaqSensor;

char Router[] = "Router_SSID";
char Passwort[] = "xxxxxxxx";

// ip und gateway müssen an das lokale Netz angepasst werden
IPAddress ip(192, 168, 1, 100);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);

/*
  öffentliche DNS-Server
  -----------------------------------------------
  OpenDNS 208, 67, 222, 222 (USA)
  Google 8, 8, 8, 8 (USA)
  Cloudfare 1, 1, 1, 1 (USA)
  DNSWWatch 84, 200, 69, 80 (Deutschland)
  Quad9 9, 9, 9, 9 (Schweiz)
  Neustar UltraDNS 56, 154, 70, 3 (USA, gefiltert)
  Deutsche Telekom 217, 5,100, 185
  ------------------------------------------------
  oder die im Router eingetragene IP
  im Beispiel: 192, 168, 1, 20
*/
IPAddress primaryDNS(192, 168, 1, 20);
IPAddress secondaryDNS(9, 9, 9, 9);

// NTP-Server aus dem Pool
#define Zeitserver "de.pool.ntp.org"

/*
  Liste der Zeitzonen
  https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
  Zeitzone CET = Central European Time -1 -> 1 Stunde zurück
  CEST = Central European Summer Time von
  M3 = März, 5.0 = Sonntag 5. Woche, 02 = 2 Uhr
  bis M10 = Oktober, 5.0 = Sonntag 5. Woche 03 = 3 Uhr
*/
#define Zeitzone "CET-1CEST,M3.5.0/02,M10.5.0/03"

// time_t enthält die Anzahl der Sekunden seit dem 1.1.1970 0 Uhr
time_t aktuelleZeit;

/* 
  Struktur tm
  tm_hour -> Stunde: 0 bis 23
  tm_min -> Minuten: 0 bis 59
  tm_sec -> Sekunden 0 bis 59
  tm_mday -> Tag 1 bis 31
  tm_wday -> Wochentag (0 = Sonntag, 6 = Samstag)
  tm_mon -> Monat: 0 (Januar) bis 11 (Dezember)
  tm_year -> Jahre seit 1900
  tm_yday -> vergangene Tage seit 1. Januar des Jahres
  tm_isdst -> Wert > 0 = Sommerzeit (dst = daylight saving time)
*/
tm Zeit;

WebServer server(80);

// benötigte Strings
String Nachricht;
String Temperatur;
String Luftfeuchtigkeit;
String Luftdruck;
String CO2;
String IAQ;
String StatischerIAQ;

Der set­up-Teil

void setup() 
{
  // Zeitzone: Parameter für die zu ermittelnde Zeit
  configTzTime(Zeitzone, Zeitserver);

  // localtime_r -> Zeit in die lokale Zeitzone setzen
  localtime_r(&aktuelleZeit, &Zeit);
  Serial.begin(9600);

  // auf Serielle Verbindung warten
  while (!Serial);
  delay(1000);

  // WiFi starten
  WiFi.begin(Router, Passwort);

  WiFi.config(ip, gateway, subnet, primaryDNS, secondaryDNS);
  Serial.print("Verbunden mit ");
  Serial.println(Router);

  // IP anzeigen
  Serial.print("Statische IP: ");
  Serial.println(ip);

  server.begin();
  server.on("/", handleRoot);

  // Hex-Adresse des BME680
  // BME68X_I2C_ADDR_HIGH = 0x77
  // BME68X_I2C_ADDR_LOW = 0x76
  iaqSensor.begin(BME68X_I2C_ADDR_HIGH, Wire);

  // Array der verfügbaren Sensoren
  bsec_virtual_sensor_t sensorList[13] = 
  {
    BSEC_OUTPUT_IAQ,
    BSEC_OUTPUT_STATIC_IAQ,
    BSEC_OUTPUT_CO2_EQUIVALENT,
    BSEC_OUTPUT_BREATH_VOC_EQUIVALENT,
    BSEC_OUTPUT_RAW_TEMPERATURE,
    BSEC_OUTPUT_RAW_PRESSURE,
    BSEC_OUTPUT_RAW_HUMIDITY,
    BSEC_OUTPUT_RAW_GAS,
    BSEC_OUTPUT_STABILIZATION_STATUS,
    BSEC_OUTPUT_RUN_IN_STATUS,
    BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
    BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
    BSEC_OUTPUT_GAS_PERCENTAGE
  };

  // BSEC_SAMPLE_RATE_ULP: Ultra Low Powermode -> Messung alle 5 Minuten
  // BSEC_SAMPLE_RATE_LP: Low Powermode -> Messung alle 3 Sekunden
  iaqSensor.updateSubscription(sensorList, 13, BSEC_SAMPLE_RATE_LP);
}

Der loop-Teil

void loop() 
{
  // wenn der Sensor arbeitet ...
  if (iaqSensor.run()) 
  {
    // aktuelle Zeit holen
    time(&aktuelleZeit);

    // localtime_r -> Zeit in die lokale Zeitzone setzen
    localtime_r(&aktuelleZeit, &Zeit);

    // es kann bis zu 30 Sekunden dauern
    // bis die Zeit ermittelt wird
    // Name des Wochentages 0-6
    switch (Zeit.tm_wday) {
      case 0:
        Serial.print("Sonntag");
        break;
      case 1:
        Serial.print("Montag");
        break;
      case 2:
        Serial.print("Dienstag");
        break;
      case 3:
        Serial.print("Mittwoch");
        break;
      case 4:
        Serial.print("Donnerstag");
        break;
      case 5:
        Serial.print("Freitag");
        break;
      case 6:
        Serial.print("Samstag");
        break;
    }

    Serial.print(",");
    if (Zeit.tm_mday < 10) Serial.print("0");
    Serial.print(Zeit.tm_mday);
    Serial.print(".");

    // Monat: führende 0 ergänzen
    if (Zeit.tm_mon < 10) Serial.print("0");

    // Zählung beginnt mit 0 -> + 1
    Serial.print(Zeit.tm_mon + 1);
    Serial.print(".");

    // Anzahl Jahre seit 1900
    Serial.print(Zeit.tm_year + 1900);
    Serial.print(" ");

    // Stunde: wenn Stunde  10 -> 0 davor setzen
    if (Zeit.tm_hour < 10) Serial.print("0");
    Serial.print(Zeit.tm_hour);
    Serial.print(":");

    // Minuten
    if (Zeit.tm_min < 10) Serial.print("0"); 
    Serial.println(Zeit.tm_min);

    server.handleClient();

    // Messwerte ermitteln, in Strings umwandeln, . durch , ersetzen
    Temperatur = String(iaqSensor.temperature);
    Temperatur.replace(".", ",");

    Luftfeuchtigkeit = String(iaqSensor.humidity);
    Luftfeuchtigkeit.replace(".", ",");

    Luftdruck = String(iaqSensor.pressure / 100);
    Luftdruck.replace(".", ",");

    IAQ = String(iaqSensor.iaq);
    IAQ.replace(".", ",");

    StatischerIAQ = String(iaqSensor.staticIaq);
    StatischerIAQ.replace(".", ",");

    CO2 = String(iaqSensor.co2Equivalent);
    CO2.replace(".", ",");

    String VOC = String(iaqSensor.breathVocEquivalent);
    VOC.replace(".", ",");

    String GasInProzent = String(iaqSensor.gasPercentage);
    GasInProzent.replace(".", ",");

    String WiderstandMOXSensor = String(iaqSensor.gasResistance / 1000);
    WiderstandMOXSensor.replace(".", ",");

    // Ausgabe Serieller Monitor
    Serial.println("Statusmeldungen:");
    Serial.println("-----------------------------");
    Serial.println("Sensor bereit (0-1): " + String(int(iaqSensor.runInStatus)));
    Serial.println("Genauigkeit IAQ (0-3): " + String(int(iaqSensor.iaqAccuracy)));
    Serial.println("-----------------------------");
    Serial.println("Messwerte:");
    Serial.println("-----------------------------");
    Serial.println("Temperatur: " + Temperatur + " °C");
    Serial.println("Feuchtigkeit: " + Luftfeuchtigkeit + " %");
    Serial.println("Luftdruck: " + Luftdruck + " hPa");
    Serial.println("IAQ: " + IAQ);
    Serial.println("Statischer IAQ: " + StatischerIAQ);
    Serial.println("Schätzung CO2: " + CO2 + " ppm");
    Serial.println("Gas (0-100): " + GasInProzent + " %");
    Serial.println("Widerstandswert MOX-Sensor: " + WiderstandMOXSensor + " kOhm");
    Serial.println("VOC: " + VOC);
    Serial.println("-----------------------------");
  }
}

HTML-Sei­te erstel­len und senden

void handleRoot() 
{
  // Seite zusammenbauen
  // Kopf der HTML-Seite: aktualisierung alle 60 Sekunden
  // kann angepasst werden
  Nachricht = "<head><meta http-equiv=\"refresh\" content=\"60\"></head>";
  Nachricht += "<h1>Messwerte BME680</h1><hr>";

  // Datum anzeigen
  // formatieren: Schrift 14pt, ohne Serifen, Zeilenhöhe
  Nachricht += "<div style = \"font-size:14pt; font-family:sans-serif; line-height:1.8em\">";
  Nachricht += "Letzte Aktualisierung: ";

  // wenn der Tag < 10
  if (Zeit.tm_mday < 10) Nachricht += "0";
  Nachricht += String(Zeit.tm_mday) + ".";

  // wen der Monat < 10, Zählung beginnt mit 0
  if ((Zeit.tm_mon + 1) < 10) Nachricht += "0";
  Nachricht += String(Zeit.tm_mon + 1) + ". ";

  // Zeit anzeigen
  // wenn die Stunde < 10
  if (Zeit.tm_hour < 10) Nachricht += "0";
  Nachricht += String(Zeit.tm_hour) + ":";

  // wenn die Minute < 10
  if (Zeit.tm_min < 10) Nachricht += "0";
  Nachricht+= String(Zeit.tm_min) + " Uhr<br><hr>";
  
  Nachricht += "Temperatur: " + Temperatur + " &degC<br>";
  Nachricht += "Luftfeuchtigkeit: " + Luftfeuchtigkeit + " %<br>";
  Nachricht += "Luftdruck: " + Luftdruck + " hPa<br>";
  Nachricht += "IAQ: " + IAQ + "<br>";
  Nachricht += "Statischer IAQ: " + StatischerIAQ + "<br>";
  Nachricht += "Sch&auml;tzung CO2: " + CO2 + " ppm<br>";
  Nachricht += "Genauigkeit IAQ (0-3): " + String(int(iaqSensor.iaqAccuracy)) + "</div>";

  // Button aktualisieren
  Nachricht += "<hr><input style=\"font-size:16pt; font-weight:bold;";
  Nachricht += "background-color:#55A96B;";
  Nachricht += "display:block; cursor:pointer;\"type=\"button\"";

  // IP für den Button aktualisieren (location.href)
  // muss mit dem Wert für IPAdress übereinstimmen (. statt ,)
  Nachricht += " onClick=\"location.href='http://192.168.1.100'\" value=\"aktualisieren\">";
  Nachricht += "<hr>";

  // Nachricht senden -> Seite anzeigen
  server.send(200, "text/html", Nachricht);
}

Startseite
Aufgaben A-Z
Suchen
Downloads
Fehlermeldungen
Seite als PDF

Ver­wand­te Anleitungen:


letz­te Aktua­li­sie­rung: Nov 16, 2024 @ 10:38