Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
ESP32-Beispielprogramme zur CO2-Messung mit dem Sensor MH-Z19B per Pulsweitenmodulation (PWM) und serieller Schnittstelle (https://unsinnsbasis.de/co2-sensor-mhz19b/)
/* CO2-Messung mit dem Sensor MH-Z19B
* per Pulsweitenmodulation (PWM)
*
* Anzeige der Messwerte als CO2-Ampel
*
* 2020-11-08 Heiko (unsinnsbasis.de)
*/
// Definition der GPIO-Pins
// (für die LEDs beim Arduino jeweils einen freien Digitalpin Dx verwenden)
#define LED_GREEN 32
#define LED_YELLOW 33
#define LED_RED 25
#define SENSOR_PIN 14 // beim Arduino z.B. D3 (oder 5, 6, 9, 10, 11)
// Bitrate für die Datenübertragung zum seriellen Monitor
// (ESP: z.B. 115200, Arduino: zwingend 9600)
#define BITRATE 115200 // Arduino: 9600
// tatsächliche Dauer eines Messzyklus des MH-Z19B
// (wenn nicht bekannt, den Standardwert von 1004 ms verwenden)
#define INTERVALL 997 // Standard: 1004
// Obergrenze des Messbereichs des Sensors
// (0-2000. 0-5000, 0-10000 ppm CO2)
#define RANGE 5000
// Grenzen des "grünen" und "gelben" Bereichs
#define LIMIT_GREEN 1000
#define LIMIT_YELLOW 2000
void setup() {
// Sensor-Pin als Eingang verwenden
pinMode(SENSOR_PIN, INPUT);
// GPIO-Pins der LEDs sind Ausgänge
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_YELLOW, OUTPUT);
pinMode(LED_RED, OUTPUT);
// Übertragungsrate zum seriellen Monitor setzen
Serial.begin(BITRATE);
// alle LEDs aus
digitalWrite(LED_GREEN, LOW);
digitalWrite(LED_YELLOW, LOW);
digitalWrite(LED_RED, LOW);
}
void loop() {
int ppm;
// Verwendung der einfachen Formel; allerdings wird - sofern
// bekannt - mit dem Wert von INTERVALL die tatsächliche
// Intervalllänge des Sensors berücksichtigt
ppm = (pulseIn(SENSOR_PIN, HIGH, 2200000UL) / 1000 - 2) * RANGE / (INTERVALL - 4);
// Ausgabe des CO2-Werts in ppm im seriellen Monitor
Serial.print("PPM CO2: ");
Serial.println(ppm);
// je nach Wert die passende LED an- und die anderen
// beiden ausschalten
if (ppm < LIMIT_GREEN) {
digitalWrite(LED_GREEN, HIGH);
digitalWrite(LED_YELLOW, LOW);
digitalWrite(LED_RED, LOW);
} else if (ppm < LIMIT_YELLOW) {
digitalWrite(LED_GREEN, LOW);
digitalWrite(LED_YELLOW, HIGH);
digitalWrite(LED_RED, LOW);
} else {
digitalWrite(LED_GREEN, LOW);
digitalWrite(LED_YELLOW, LOW);
digitalWrite(LED_RED, HIGH);
}
delay(2500); // vor der nächsten Messung etwas warten
}
/* Abfrage von Sensoren und Anzeige auf OLED
* - MH-Z19B: CO2 und Temperatur
* - DS18B20: Temperatur
*
* Die OLED-Anzeige ändert sich alle 5 Sekunden:
* - CO2-Wert: Abfrage des MH-Z19B seriell und per PWM
* - Temperatur: Messwerte beider Sensoren
*
* 2021-01-14 Heiko (unsinnsbasis.de)
*/
/* ------------------------------------------------------------
* Einbinden der benötigten Bibliotheken,
* Defintion der GPIO-Pins und anderer Konstanten,
* Anlegen der Datenobjekte
* ------------------------------------------------------------ */
// Übertragungsrate für Ausgabe zum seriellen Monitor
#define SERIAL_BAUDRATE 115200 // Arduino: 9600, ESP32: z.B. 115200
/* ------------------------------------------------------------
* DS18B20
* ------------------------------------------------------------ */
#include <OneWire.h>
#include <DallasTemperature.h>
#define DS18B20_PIN 18 // GPIO-Pin für Daten des DS18B20
// OneWire-Objekt erstellen ...
OneWire ow(DS18B20_PIN);
// ... und einen Zeiger darauf an ein Sensor-Objekt übergeben
DallasTemperature ds18b20(&ow);
DeviceAddress ds18b20_id; // ID des DS18B20 (Adresse)
/* ------------------------------------------------------------
* MH-Z19B
* ------------------------------------------------------------ */
#include <Arduino.h>
#include <MHZ19.h>
// Nutzung der Schnittstelle UART2 an den Default-Pins RX 16, TX 17
#define RX2 16
#define TX2 17
#define MHZ19_BAUDRATE 9600
#define MHZ19_PROTOCOL SERIAL_8N1
#define MHZ19_RANGE 5000 // Obergrenze des Messbereichs des Sensors
#define MHZ19_PWM_PIN 5
MHZ19 mhz19b; // Sensor-Objekt
/* ------------------------------------------------------------
* SSD1306-OLED-Display (I2C)
* ------------------------------------------------------------ */
// Bibliothek bindet auch <Adafruit_GFX.h>, <SPI.h>, <Wire.h> ein
#include <Adafruit_SSD1306.h>
// I2C-Adresse des Displays (0x3C oder 0x3D)
#define DISPLAY_I2C_ADDRESS 0x3C
// Auflösung des SSD1306-OLED-Displays
#define DISPLAY_WIDTH 128 // Breite in Pixeln
#define DISPLAY_HEIGHT 64 // Höhe in Pixeln
// Datenobjekt für das Display
// - Verbindung per I2C (Standard-Pins SCL, SDA)
// - Display hat keinen Reset-Pin
Adafruit_SSD1306 display(DISPLAY_WIDTH, DISPLAY_HEIGHT, &Wire, -1);
/* Timer für die Ausgabe:
* - alle 10 Sekunden die Temperatur des CO2-Sensors und
* des DS18B20 anzeigen
* -> immer wenn die Sekundenzahl auf 0 endet
* - 5 Sekunden später die CO2-Werte (seriell und PWM) anzeigen
* -> immer wenn die Sekundenzahl auf 5 endet
*/
unsigned long timer_output_0 = 0, timer_output_5 = 5000;
// CO2-Werte bei serieller und PWM-Messung
int co2_ser, co2_pwm;
float temp_ds; //Temperatur des DS18B20
int temp_mh; //Temperatur des MH-Z19B
/* ------------------------------------------------------------ */
void setup(){
int i;
bool error = false;
char mhz19_version[4];
Serial.begin(SERIAL_BAUDRATE);
delay(500); // kurz warten, bis die ser. Schnittstelle bereit ist
Serial.println("\n\n"); // Abstand zur vorigen Ausgabe
// Display initialisieren
// im Fehlerfall Meldung ausgeben
if(!display.begin(SSD1306_SWITCHCAPVCC, DISPLAY_I2C_ADDRESS)) {
Serial.println("SSD1306 nicht gefunden");
error = true;
} else {
display.clearDisplay(); // Display löschen
display.display();
}
// Sensoren initialisieren
// ROM-Adresse (ID) des DS18B20 ermitteln und anzeigen
if (!ow.search(ds18b20_id)) {
Serial.println("Kein DS18B20-Sensor gefunden");
error = true;
} else {
Serial.print("DS18B20 ID (Sensor-Adresse):");
// Adress-Bytes als Hexadezimalwerte ausgeben
for (i = 0; i < sizeof(DeviceAddress); i++) {
Serial.print(' ');
Serial.print(ds18b20_id[i], HEX);
}
Serial.print(' ');
}
Serial.println();
Serial2.begin(MHZ19_BAUDRATE, MHZ19_PROTOCOL, RX2, TX2);
mhz19b.begin(Serial2); // MH-Z19B-Sensor eine Schnittstelle zuweisen
// ein paar Daten der Sensor-Konfiguration ausgeben
mhz19b.getVersion(mhz19_version);
Serial.print("--------------------\nMH-Z19B Firmware Version: ");
for(i = 0; i < 4; i++) {
Serial.print(mhz19_version[i]);
if(i == 1)
Serial.print(".");
}
Serial.print("\nMH-Z19B Messbereich: ");
Serial.println(mhz19b.getRange());
Serial.print("MH-Z19B Autokalibrierung (ABC): ");
mhz19b.getABC() ? Serial.println("AN") : Serial.println("AUS");
Serial.println("--------------------");
// im Fehlerfall Programm nicht fortsetzen (leere Dauerschleife))
if(error) {
for(;;);
}
}
/* ------------------------------------------------------------ */
void loop(){
// Timer und Intervalllängen für die PWM-Messung des CO2-Werts
unsigned long pulse_high, pulse_high_2, time_start, timer;
timer = millis();
if ((timer / 1000) % 10 == 0 && timer > timer_output_0) {
timer_output_0 += 10000; // nächste Anzeige in 10 Sekunden
ds18b20.requestTemperatures(); // DS18B20-Abfrage starten
temp_ds = ds18b20.getTempC(ds18b20_id);
temp_mh = mhz19b.getTemperature();
display.clearDisplay();
display.setTextColor(WHITE); // helle Schrift, dunkler Grund
display.setTextSize(2); // doppelt hohe Schrift
display.setCursor(0, 0);
display.print("Temperatur");
display.setCursor(12, 20);
display.print("MH: ");
display.print(temp_mh);
display.setCursor(12, 48);
display.print("DS: ");
display.print(temp_ds, 1);
display.display();
// im Anschluss die PWM-Messung durchführen (dauert bis zu
// zwei Sekunden), um die anschließende Ausgabe nicht
// zu verzögern;
// mit der vollständigen Formel und der
// tatsächlich gemessenen Intervalllänge arbeiten.
// Länge des HIGH-Pegels ermitteln
pulse_high = pulseIn(MHZ19_PWM_PIN, HIGH, 2200000UL) / 1000;
time_start = millis(); // jetzt beginnt der LOW-Pegel
// ... dann die Länge des folgenden HIGH-Pegels messen
pulse_high_2 = pulseIn(MHZ19_PWM_PIN, HIGH, 1100000UL) / 1000;
// die Dauer des LOW-Pegels ist die Zeit seit Beginn der
// Messung minus die Dauer des zweiten HIGH-Pegels;
// statt die Dauer des LOW-Pegels
// millis() - time_start - pulse_high_2
// in einer Variablen zwischenzuspeichern, wird der Wert
// direkt in die Formel eingesetzt
co2_pwm = (pulse_high - 2) * MHZ19_RANGE / (pulse_high + (millis() - time_start - pulse_high_2) - 4);
co2_ser = mhz19b.getCO2();
} else if ((timer / 1000) % 10 == 5 && timer > timer_output_5) {
timer_output_5 += 10000;
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(2);
display.setCursor(0, 0);
display.print("CO2 (ppm)");
display.setCursor(0, 20);
display.print("ser.: ");
display.print(co2_ser);
display.setCursor(0, 48);
display.print("PWM: ");
display.print(co2_pwm);
display.display();
}
delay(100); // kurz warten (1/10 Sekunde; kann entfallen)
}
/* CO2-Messung mit dem Sensor MH-Z19B
* per Pulsweitenmodulation (PWM)
*
* 2020-11-08 Heiko (unsinnsbasis.de)
*/
// Definition der GPIO-Pins
#define SENSOR_PIN 14 // beim Arduino z.B. D3 (oder 5, 6, 9, 10, 11)
// Bitrate für die Datenübertragung zum seriellen Monitor
// (ESP: z.B. 115200, Arduino: zwingend 9600)
#define BITRATE 115200 // Arduino: 9600
// Obergrenze des Messbereichs (0-2000. 0-5000, 0-10000 ppm CO2)
#define RANGE 5000
void setup() {
// Sensor-Pin als Eingang verwenden
pinMode(SENSOR_PIN, INPUT);
// Übertragungsrate zum seriellen Monitor setzen
Serial.begin(BITRATE);
}
void loop() {
int pulse_high, pulse_high_2, pulse_low;
unsigned long time_start;
// pulseIn() arbeitet mit Mikro- (nicht Milli-)Sekunden
// erst einmal Länge des HIGH-Pegels ermitteln ...
pulse_high = pulseIn(SENSOR_PIN, HIGH, 2200000UL) / 1000;
time_start = millis(); // jetzt beginnt der LOW-Pegel
// ... dann die Länge des folgenden HIGH-Pegels messen
pulse_high_2 = pulseIn(SENSOR_PIN, HIGH, 1100000UL) / 1000;
// die Dauer des LOW-Pegels ist die Zeit seit Beginn der
// Messung minus die Dauer des zweiten HIGH-Pegels;
pulse_low = millis() - time_start - pulse_high_2;
// Ausgabe im seriellen Monitor:
// - CO2-Wert nach einfacher Formel
// - Wert nach vollständiger Formel
// - Roh-Werte der Messung
Serial.print("PPM CO2: ");
Serial.print((pulse_high - 2) * RANGE / 1000);
Serial.print(" (einfach); ");
Serial.print((pulse_high - 2) * RANGE / (pulse_high + pulse_low -4));
Serial.print(" (korrekt); ");
Serial.print("Rohwerte (HIGH, LOW): ");
Serial.print(pulse_high);
Serial.print(", ");
Serial.println(pulse_low);
delay(2500); // vor der nächsten Messung etwas warten
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment