BME680 und ESP32 Wetterstation für zu Hause

Was ist BME680

Der BME680 ist ein Druck-, Temperatur-, Feuchtigkeits- und Luftqualitätssensor, der von Bosch Sensortec entwickelt wurde. Er liefert präzise Messungen dieser Parameter und wird in verschiedenen Bereichen wie Meteorologie, Industrieanwendungen und IoT (Internet of Things) eingesetzt. Der BMP680 kann zur Messung des Umgebungsdrucks sowie zur Entwicklung verschiedener Arten von Geräten verwendet werden, die präzise Messungen der atmosphärischen Bedingungen erfordern. Heute werden wir sehen, wie wir mit ihm in Kombination mit einem ESP32-Board eine Wetterstation bauen können. Weitere Informationen über den Sensor finden Sie hier. Der Sensor, den wir verwenden werden, ist hier und das ESP32 Board ist das NodeMCU-32S – hier (micro usb version).

Was ist der Plan?

BME680 unterstützt I2C- und SPI-Kommunikation. Der Plan ist, den Sensor an das ESP32-Board anzuschließen und die Messungen in einem bestimmten Intervall zu lesen. Nach dem Auslesen können wir sie auf der seriellen Konsole anzeigen und die Messungen verstehen.

Was ist I2C?

I2C (Inter-Integrated Circuit) ist ein Kommunikationsprotokoll, das zur Verbindung verschiedener Komponenten in elektronischen Geräten verwendet wird. Es ermöglicht diesen Komponenten den Austausch von Daten und Befehlen über eine bidirektionale serielle Verbindung mit zwei Drähten: Daten (SDA – Serial Data) und Tact (SCL – Serial Clock).

I2C ist sehr beliebt für den Anschluss von Mikrocontrollern, Sensoren, Aktoren und anderen Peripheriegeräten in Systemen, in denen eine Kommunikation zwischen verschiedenen Komponenten erforderlich ist. Einer der Vorteile von I2C besteht darin, dass mehrere Geräte an dieselbe Leitung angeschlossen werden können, wobei zur Identifizierung der einzelnen Geräte unterschiedliche Adressen verwendet werden. Dadurch eignet sich I2C für den Aufbau komplexer Systeme mit mehreren Peripheriegeräten.

Verbinden Sie

Überprüfen wir den Sensor und suchen wir die SDA- und SCL-Pins. Normalerweise sind sie auf dem Sensor angegeben. Wie wir auf dem Bild und der Aufschrift auf dem Sensor sehen können, sind dies die Pins 3 und 4 von links nach rechts.

Jetzt müssen wir diese Pins auch auf dem ESP32-Board finden. Wir können dies in der Beschreibung des spezifischen Boards oder in seinem Pin-Diagramm sehen. Lassen Sie uns diese auf diesem Diagramm finden.

Dies sind der Pin 3 und 6 von oben rechts. Wir müssen den Sensor an diese Eingänge anschließen. Damit der BME680  funktioniert, braucht er auch eine Stromversorgung. Diese erhalten wir durch 3,3 V vom Pin 1 von oben links und Masse Pin 1 rechts. Schließen wir sie an.

Konfigurieren der Arduino IDE 1

Um vom Sensor lesen zu können, müssen wir zunächst die Programmierumgebung konfigurieren. Daher müssen wir zunächst einige Bibliotheken installieren.

Gehen wir zu Sketch -> Include Library -> Manage Libraries

Und dort suchen wir nach „adafruit bme680“, wählen aus und installieren.

Als nächstes müssen wir die Sensorbibliotheken installieren.

Gehen wir noch einmal zu Sketch -> Include Library -> Manage Libraries, suchen Sie nach „Adafruit Unified Sensor“ und installieren Sie es.

Wenn Sie diesen Artikel verwendet haben, um die Arduino IDE zu konfigurieren, müssen wir das Board ändern, da wir derzeit das NodeMCU-32S verwenden. Gehen wir zu Tools -> Board -> Boards manager und wählen wir NodeMCU-32S. Dazu müssen wir zuvor esp32 aus der Espressif Systems-Bibliothek installiert haben (siehe nächster Screenshot).

Jetzt sind wir bereit, etwas Code zu schreiben.

Code

Der von uns verwendete Code stammt von hier.

/***
  Read Our Complete Guide: https://RandomNerdTutorials.com/esp32-bme680-sensor-arduino/
  Designed specifically to work with the Adafruit BME680 Breakout ----> http://www.adafruit.com/products/3660 These sensors use I2C or SPI to communicate, 2 or 4 pins are required to interface. Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! Written by Limor Fried & Kevin Townsend for Adafruit Industries. BSD license, all text above must be included in any redistribution
***/

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_BME680.h"

/*#define BME_SCK 18
#define BME_MISO 19
#define BME_MOSI 23
#define BME_CS 5*/

#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BME680 bme; // I2C
//Adafruit_BME680 bme(BME_CS); // hardware SPI
//Adafruit_BME680 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK);

void setup() {
  Serial.begin(115200);
  while (!Serial);
  Serial.println(F("BME680 async test"));

  if (!bme.begin()) {
    Serial.println(F("Could not find a valid BME680 sensor, check wiring!"));
    while (1);
  }

  // Set up oversampling and filter initialization
  bme.setTemperatureOversampling(BME680_OS_8X);
  bme.setHumidityOversampling(BME680_OS_2X);
  bme.setPressureOversampling(BME680_OS_4X);
  bme.setIIRFilterSize(BME680_FILTER_SIZE_3);
  bme.setGasHeater(320, 150); // 320*C for 150 ms
}

void loop() {
  // Tell BME680 to begin measurement.
  unsigned long endTime = bme.beginReading();
  if (endTime == 0) {
    Serial.println(F("Failed to begin reading :("));
    return;
  }
  Serial.print(F("Reading started at "));
  Serial.print(millis());
  Serial.print(F(" and will finish at "));
  Serial.println(endTime);

  Serial.println(F("You can do other work during BME680 measurement."));
  delay(50); // This represents parallel work.
  // There's no need to delay() until millis() >= endTime: bme.endReading()
  // takes care of that. It's okay for parallel work to take longer than
  // BME680's measurement time.

  // Obtain measurement results from BME680. Note that this operation isn't
  // instantaneous even if milli() >= endTime due to I2C/SPI latency.
  if (!bme.endReading()) {
    Serial.println(F("Failed to complete reading :("));
    return;
  }
  Serial.print(F("Reading completed at "));
  Serial.println(millis());

  Serial.print(F("Temperature = "));
  Serial.print(bme.temperature);
  Serial.println(F(" *C"));

  Serial.print(F("Pressure = "));
  Serial.print(bme.pressure / 100.0);
  Serial.println(F(" hPa"));

  Serial.print(F("Humidity = "));
  Serial.print(bme.humidity);
  Serial.println(F(" %"));

  Serial.print(F("Gas = "));
  Serial.print(bme.gas_resistance / 1000.0);
  Serial.println(F(" KOhms"));

  Serial.print(F("Approx. Altitude = "));
  Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
  Serial.println(F(" m"));

  Serial.println();
  delay(2000);
}

Nachdem wir den Code kompiliert und auf dem Stich gespeichert haben, können wir die serielle Konsole öffnen und folgendes sehen:

Auf diese Weise können wir die Sensorwerte auslesen. Sie können mehr Details über den Code in dem Originalartikel lesen, aus dem er entnommen wurde, hier. Schauen wir uns aber die Werte etwas genauer an.

Die Werte

Wie wir gesehen haben, kann der Sensor Temperatur, Höhe, Feuchtigkeit und Gas messen.

Der Sensor misst die Temperaturänderung recht genau, aber der Temperaturwert ist nicht exakt. Wenn wir einige der Originaldokumente des Herstellers lesen, zum Beispiel dieses hier, finden wir die folgende Beschreibung:

2.7 Temperaturabweichungen aufgrund von Wärmequellen auf der Leiterplatte

Werfen wir einen Blick auf die Temperatur- und Luftfeuchtigkeitswerte, die wir von der Tafel erhalten. Eine Temperatur von über dreißig Grad und eine so niedrige relative Luftfeuchtigkeit scheinen nicht in Ordnung zu sein. Ein Blick auf ein Referenzthermometer zeigt uns, dass

Unsere Temperatur ist tatsächlich ein paar Grad höher. Bedeutet dies, dass der Temperatursensor im BME680 ungenau ist?

Eigentlich nicht, es misst die Temperatur sehr genau dort, wo sie sich befindet, nämlich auf der Platine. Unsere Platine enthält, wie die meisten Geräte, einige Wärmequellen (z. B. MCU, WiFi-Chip, Display, …). Das bedeutet, dass die Temperatur dort  tatsächlich höher ist als die Umgebungstemperatur. Da die absolute Wassermenge in der Luft annähernd konstant ist, führt dies auch zu einer geringeren relativen Luftfeuchtigkeit auf der Platine als anderswo im Raum.“

Für uns bedeutet das, dass wir einen Referenzwert mit einem anderen Thermometer nehmen und den Temperatur-Offset relativ zu diesem Wert anpassen müssen. Wir können dies tun, indem wir den Offset zu den Gradzahlen im Programm subtrahieren oder addieren. Das Gleiche gilt für die Luftfeuchtigkeit.

Gas und VOC

Wenn wir den Gaswert ablesen, erhalten wir einen Sensorwiderstandswert, aber was bedeutet das und was ist der VOC.

VOC steht für Volatile Organic Compounds (flüchtige organische Verbindungen) und bezeichnet eine Vielzahl von chemischen Verbindungen, die eine hohe Dampfdurchlässigkeit aufweisen und unter normalen Temperatur- und Druckbedingungen leicht verdampfen. Sie werden häufig mit industriellen Prozessen, Lackier-, Klebe- und Reinigungsarbeiten und anderen Tätigkeiten in Verbindung gebracht, die Luftverschmutzung verursachen und schädliche Auswirkungen auf die menschliche Gesundheit und die Umwelt haben können.

Wenn sich flüchtige organische Verbindungen mit Gasen verbinden, erhöht sich der Widerstand des Sensors, was die elektrische Leitfähigkeit verringert, und wenn die flüchtigen organischen Verbindungen abnehmen, verringert sich der Widerstand, was die elektrische Leitfähigkeit erhöht. Durch Messung des Widerstandes können wir also die VOC-Gase nachweisen. Kurz gesagt, wenn der Widerstand zunimmt, nimmt das Vorhandensein von Gasen zu, und wenn er abnimmt, nehmen die Gase ab.

Um jedoch verständliche Werte anzeigen zu können, werden wir die bsec-Bibliothek verwenden.

BSEC

Installieren wir es. Gehen wir noch einmal zu Sketch -> Include Library -> Manage Librariessuchen Sie nach „bsec“ und installieren Sie es.

Kopieren wir den folgenden Code, den Sie hier mit weiterer Beschreibung finden.

#include <EEPROM.h>
#include "bsec.h"
/* Configure the BSEC library with information about the sensor
    18v/33v = Voltage at Vdd. 1.8V or 3.3V
    3s/300s = BSEC operating mode, BSEC_SAMPLE_RATE_LP or BSEC_SAMPLE_RATE_ULP
    4d/28d = Operating age of the sensor in days
    generic_18v_3s_4d
    generic_18v_3s_28d
    generic_18v_300s_4d
    generic_18v_300s_28d
    generic_33v_3s_4d
    generic_33v_3s_28d
    generic_33v_300s_4d
    generic_33v_300s_28d
*/
const uint8_t bsec_config_iaq[] = {
#include "config/generic_33v_3s_4d/bsec_iaq.txt"
};

#define BME68X_I2C_ADDR 0x77

#define STATE_SAVE_PERIOD  UINT32_C(360 * 60 * 1000) // 360 minutes - 4 times a day

// Helper functions declarations
void checkIaqSensorStatus(void);
void errLeds(void);
void loadState(void);
void updateState(void);

// Create an object of the class Bsec
Bsec iaqSensor;
uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE] = {0};
uint16_t stateUpdateCounter = 0;

String output;

// Entry point for the example
void setup(void)
{
  EEPROM.begin(BSEC_MAX_STATE_BLOB_SIZE + 1);
  Serial.begin(115200);
  delay(1000);
  pinMode(LED_BUILTIN, OUTPUT);
  iaqSensor.begin(BME68X_I2C_ADDR, Wire);
  output = "\nBSEC library version " + String(iaqSensor.version.major) + "." + String(iaqSensor.version.minor) + "." + String(iaqSensor.version.major_bugfix) + "." + String(iaqSensor.version.minor_bugfix);
  Serial.println(output);
  checkIaqSensorStatus();

  iaqSensor.setConfig(bsec_config_iaq);
  checkIaqSensorStatus();

  loadState();

  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
  };

  iaqSensor.updateSubscription(sensorList, 13, BSEC_SAMPLE_RATE_LP);
  checkIaqSensorStatus();

 }

// Function that is looped forever
void loop(void)
{
  unsigned long time_trigger = millis();
  if (iaqSensor.run()) { // If new data is available
    digitalWrite(LED_BUILTIN, HIGH);
    output = "Timestamp [ms]: " + String(time_trigger) + "\n";
    output += "Raw temperature [°C]: " + String(iaqSensor.rawTemperature) + "\n";
    output += "Pressure [hPa]: " + String(iaqSensor.pressure/100) + "\n";
    output += "Raw relative humidity [%]: " + String(iaqSensor.rawHumidity) + "\n";
    output += "Gas resistance [Ohm]: " + String(iaqSensor.gasResistance/1000) +"\n";
    output += "IAQ: " + String(iaqSensor.iaq) + "\n";
    output += "IAQ accuracy: " + String(iaqSensor.iaqAccuracy) + "\n";
    output += "Temperature [°C]: " + String(iaqSensor.temperature) + "\n";
    output += "Relative humidity [%]: " + String(iaqSensor.humidity) + "\n";
    output += "Static IAQ: " + String(iaqSensor.staticIaq) + "\n";
    output += "CO2 equivalent: " + String(iaqSensor.co2Equivalent) + "\n";
    output += "Breath VOC equivalent: " + String(iaqSensor.breathVocEquivalent) + "\n";
    Serial.println(output);

    digitalWrite(LED_BUILTIN, LOW);
    updateState();
  } else {
    checkIaqSensorStatus();
  }
}

// Helper function definitions
void checkIaqSensorStatus(void)
{
  if (iaqSensor.bsecStatus != BSEC_OK) {
    if (iaqSensor.bsecStatus < BSEC_OK) {
      output = "BSEC error code : " + String(iaqSensor.bsecStatus);
      Serial.println(output);
      for (;;)
        errLeds(); /* Halt in case of failure */
    } else {
      output = "BSEC warning code : " + String(iaqSensor.bsecStatus);
      Serial.println(output);
    }
  }

  if (iaqSensor.bme68xStatus != BME68X_OK) {
    if (iaqSensor.bme68xStatus < BME68X_OK) {
      output = "BME68X error code : " + String(iaqSensor.bme68xStatus);
      Serial.println(output);
      for (;;)
        errLeds(); /* Halt in case of failure */
    } else {
      output = "BME68X warning code : " + String(iaqSensor.bme68xStatus);
      Serial.println(output);
    }
  }
}

void errLeds(void)
{
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  delay(100);
  digitalWrite(LED_BUILTIN, LOW);
  delay(100);
}

void loadState(void)
{
  if (EEPROM.read(0) == BSEC_MAX_STATE_BLOB_SIZE) {
    // Existing state in EEPROM
    Serial.println("Reading state from EEPROM");

    for (uint8_t i = 0; i < BSEC_MAX_STATE_BLOB_SIZE; i++) {
      bsecState[i] = EEPROM.read(i + 1);
      Serial.println(bsecState[i], HEX);
    }

    iaqSensor.setState(bsecState);
    checkIaqSensorStatus();
  } else {
    // Erase the EEPROM with zeroes
    Serial.println("Erasing EEPROM");

    for (uint8_t i = 0; i < BSEC_MAX_STATE_BLOB_SIZE + 1; i++)
      EEPROM.write(i, 0);

    EEPROM.commit();
  }
}

void updateState(void)
{
  bool update = false;
  if (stateUpdateCounter == 0) {
    /* First state update when IAQ accuracy is >= 3 */
    if (iaqSensor.iaqAccuracy >= 3) {
      update = true;
      stateUpdateCounter++;
    }
  } else {
    /* Update every STATE_SAVE_PERIOD minutes */
    if ((stateUpdateCounter * STATE_SAVE_PERIOD) < millis()) {
      update = true;
      stateUpdateCounter++;
    }
  }

  if (update) {
    iaqSensor.getState(bsecState);
    checkIaqSensorStatus();

    Serial.println("Writing state to EEPROM");

    for (uint8_t i = 0; i < BSEC_MAX_STATE_BLOB_SIZE ; i++) {
      EEPROM.write(i + 1, bsecState[i]);
      Serial.println(bsecState[i], HEX);
    }

    EEPROM.write(0, BSEC_MAX_STATE_BLOB_SIZE);
    EEPROM.commit();
  }
}

Im Code müssen wir BME68X_I2C_ADDR konfigurieren. In diesem Fall ist es 0x77. Wir können auch die Konfiguration der BSEC-Bibliothek anpassen. Im Code ist sie mit generic_33v_3s_4d konfiguriert. Weitere Informationen über die Bibliothek finden Sie hier.

Wenn wir den Code auf das Board hochladen, sehen wir folgende Werte in der seriellen Konsole

Schauen wir uns die Werte genauer an

Zeitstempel [ms]: 343045 – Dies ist die Zeit, zu der die Messung begann

Rohtemperatur [°C]: 25.82 – Dies ist die vom Sensor gemessene Temperatur

Druck [hPa]: 949.18 – Dies ist der vom Sensor gemessene Höhendruck (nicht auf Meereshöhe, sondern auf der tatsächlichen Höhe)

Relative Rohfeuchte [%]: 49.55 – Die vom Sensor gemessene relative Feuchte. Oder es handelt sich um das in Prozent ausgedrückte Verhältnis zwischen der in der Atmosphäre verfügbaren Feuchtigkeitsmenge und der Menge, die verfügbar wäre, wenn die Luft gesättigt wäre.

Gaswiderstand [Ohm]: 84.00 – Der Widerstand, der bei der Messung des VOC-Wertes ermittelt wird.

IAQ: 50.00 – Raumluftqualität oder Luftqualitätsindex

IAQ-Genauigkeit: 1 – Diese Kennzeichnung ist interessant und wir können uns hier darüber informieren

Die IAQ-Genauigkeit spiegelt den aktuellen Stand des Hintergrundkalibrierungsprozesses wider, z. B:

  • IAQ Accuracy=0 kann bedeuten: das BSEC ist gerade gestartet und der Sensor stabilisiert sich (dies dauert in der Regel 5 min im LP-Modus oder 20 min im ULP-Modus), es gab eine Zeitüberschreitung (d.h. das BSEC wurde zu früh oder zu spät aufgerufen), die vom BSEC durch eine Warnung/Fehlerflagge signalisiert werden sollte.
  • IAQ-Genauigkeit=1 bedeutet, dass der BSEC-Hintergrundverlauf unsicher ist. Dies bedeutet in der Regel, dass die Daten des Gassensors zu stabil waren, als dass das BSEC seine Referenzwerte eindeutig hätte bestimmen können.
  • IAQ Accuracy=2 bedeutet, dass BSEC neue Kalibrierungsdaten gefunden hat und diese gerade kalibriert werden.
  • IAQ Accuracy=3 bedeutet, dass das BSEC erfolgreich kalibriert wurde.

Temperatur [°C]: 25.71 – Geschätzte Lufttemperatur

Relative Feuchtigkeit [%]: 49.87 – Geschätzte relative Feuchtigkeit

Statischer IAQ: 50,00 – Statischer IAQ: Der Hauptunterschied zwischen IAQ und statischem IAQ (sIAQ) liegt im Skalierungsfaktor, der auf der Grundlage der jüngsten Vergangenheit des Sensors berechnet wird. Die Ausgabe von sIAQ ist für stationäre Anwendungen (z. B. stationäre Geräte in Innenräumen) optimiert, während die Ausgabe von IAQ ideal für mobile Anwendungen (z. B. Handheld-Geräte) ist.

CO2-Äquivalent: 600.00 – Die äquivalente Konzentration von CO2 (CO2eq) [ppm] in der Umwelt. Sie wird ebenfalls aus der Ausgabe des sIAQ berechnet und aus VOC-Messungen und Korrelationen aus Feldstudien abgeleitet.

VOC-Äquivalent in der Atemluft: 0,50 – Das VOC-Äquivalent in der Atemluft (bVOCeq) schätzt die Gesamtkonzentration von VOCs [ppm] in der Umwelt. Er wird auf der Grundlage der Ergebnisse des sIAQ berechnet und aus Labortests abgeleitet.

Eine recht ausführliche Erklärung der Werte und der Möglichkeiten des Sensors finden Sie hier.

Schlussfolgerung

In diesem Artikel haben wir gesehen, wie der BMP680-Sensor programmiert und seine Daten an ein ESP32-Prozessorboard übertragen werden können. Die auf diese Weise gesammelten Informationen geben uns ein ziemlich genaues Bild von unserem Zuhause. Auf diese Weise können wir Faktoren erkennen, die unsere Gesundheit bedrohen, die angenehme Temperatur und Luftfeuchtigkeit verstehen, eine Historie der gemessenen Daten führen und auf diese Veränderungen reagieren.

Viel Spaß beim Programmieren!

Comments

No comments yet. Why don’t you start the discussion?

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert