Durch die Corona-Pandemie (COVID-19) sind zur Messung der Luftqualität in Räumen, die von mehreren Personen gleichzeitig benutzt werden, vermehrt CO2-Sensoren im Einsatz (z.B. in Klassenräumen in Schulen). Der CO2-Wert gibt einen Hinweis darauf, wie „verbraucht” die Luft ist – indirekt wird daraus auf das mögliche Ansteckungsrisiko durch in der Raumluft schwebende Aerosole mit COVID19-Erregern geschlossen. Die Sensoren sind oft in Form von CO2-Ampeln im Einsatz und signalisieren, wann es erforderlich ist, den Raum zu lüften. Der MH-Z19B ist ein Sensor, mit dem man so eine CO2-Ampel selbst bauen kann.
Das hat im Januar 2021 zu ausführlicherer Recherche geführt und gezeigt, dass man anscheinend nur mit der Messung per PWM dauerhaft nicht auskommt – man muss (oder kann) den Sensor (auch) über die serielle Schnittstelle ansprechen, um Einfluss auf das Kalibrierungsverhalten zu nehmen. Letztlich habe ich den Beitrag um folgende Abschnitte erweitert:
- neuer Einleitungstext
- Fake-Sensoren
- Kalibrierung
- Programmierung des Sensors über die serielle Schnittstelle
- Quellenangaben und Links
Die Beispielprogramme stehen nun auch auf Github zum Download zur Verfügung – entsprechende Links finden sich auch unterhalb der Programme. (In bestimmten Browser-Konstellationen fügt das von mir verwendete Plugin zur Formatierung der Beispiele beim Kopieren wohl nach jeder Zeile eine Leerzeile ein – das sollte beim Kopieren von Github nicht auftreten.)
CO2-Messung nach dem NDIR-Prinzip
Der MH-Z19B misst den Kohlendioxid-Gehalt (CO2, auch Kohlenstoffdioxid genannt) der Umgebungsluft nach dem NDIR-Prinzip. In einem NDIR-Sensor (nichtdispersiver Infrarot-Sensor) wird ein Infrarotstrahl durch ein Gasgemisch geschickt. Jedes Gas des Gemisches absorbiert Infrarotlicht einer bestimmten Wellenlänge. Ein Sensor misst, welcher Anteil des IR-Lichts der für das jeweilige Gas spezifischen Wellenlänge vom Gasgemisch absorbiert wurde – bei CO2 sind es 4,3 µm. Aus der Differenz wird der Anteil des Gases im Gemisch berechnet – in der Einheit »ppm« (parts per million, Anzahl Teile auf eine Million). Eine etwas ausführlichere Beschreibung des NDIR-Prinzips liefert die deutsche Wikipedia; deutlich umfangreicher ist der englischsprachige Artikel.
CO2-Wert als Maßstab für die Luftqualität
Unsere Atemluft ist ein Gemisch verschiedener Gase, im wesentlichen sind das folgende:
- Stickstoff – 78,08 Vol.-%
- Sauerstoff – 20,95 Vol.-%
- Argon – 0,93 Vol.-%
- Kohlendioxid – 0,04 Vol.-%
Diese Zahlen beziehen sich auf trockene – also wasserdampffreie – Luft; unsere Atemluft kann zwischen 0 und 4 Vol.-% Wasserdampf enthalten, dazu (Fein-)Staub und Spurengase in noch geringerer Konzentration als CO2. [1]Quelle: Wikipedia: Luft; s. auch Wikipedia: Luftfeuchtigkeit Ein CO2-Gehalt von 0,04 Volumenprozent entspricht einem Anteil von 400 ppm bzw. 400 Millionstel Teilen (oder auch 0,4 Promille). Dieser Wert ist von verschiedenen Faktoren abhängig, hat sich aber als Orientierungsgröße durchgesetzt. [2]Zitat aus einer Bekanntmachung des Umweltbundesamtes (UBA) von 2008:»Für den Außenluftbeitrag […] werden derzeit für ländliche Gebiete übliche Werte von 350 ppm, für kleine … Continue reading
Will man eine CO2-Ampel realisieren, um einen Hinweis zur Luftqualität in einem Innenraum zu bekommen, kann man sich an den Empfehlungen des Umweltbundesamtes (UBA) orientieren: [3]Angesichts der Corona-Pandemie (auch: SARS-CoV-2, COVID19) hat das Umweltbundesamt Empfehlungen zur Einhaltung einer guten Luftqualität herausgegeben; u.a. heißt es dort: »In Räumen mit hoher … Continue reading
- grün (< 1000 ppm CO2) – die Luftqualität ist gut
- gelb (1000-2000 ppm CO2) – mittlere Luftqualität → Lüften ist empfehlenswert
- rot (> 2000 ppm CO2) – die Luftqualität ist inakzeptabel → Lüften ist notwendig
Preis und Bezugsmöglichkeiten des MH-Z19B
Der MH-Z19B ist relativ teuer (jedenfalls im Vergleich mit vielen anderen Sensoren, die oft mit Mikrocontrollern verwendet werden wie Bewegungsmeldern, Temperatursensoren oder auch kleinen Displays): Im deutschen Versandhandel kostet er etwa 25-30 €, teilweise etwas günstiger gibt es ihn auf den bekannten Online-Marktplätzen wie eBay, am günstigsten ist die Direktbestellung in China mit Preisen meist deutlich unterhalb von 20 €. [4]Bezugsquellen z.B. Reichelt Elektronik, Direktbestellung in China z.B. über aliexpress.com (im November 2020 war der Sensor dort ab 14 € zu bekommen, im Januar 2021 kaum unter 16 €); … Continue reading
Man kann den MH-Z19B in zwei Versionen kaufen: mit verlöteten Pins oder mit Stecker (und Anschlusskabel). Bei einigen Anbietern gibt es den Sensor außerdem mit verschiedenen voreingestellten Messbereichen: 0-2000, 0-5000 oder 0-10.000 ppm. Laut Dokumentation kann man den Messbereich auch selbst über die serielle Schnittstelle einstellen bzw. ändern – was aber nicht ganz trivial sein soll.[5]siehe die Hinweise zur Bibliothek MH-Z19 auf github.com/WifWaf/MH-Z19
Die Dokumentation in Version 1.0 erwähnt die Messbereiche 0-2000 und 0-5000 ppm, Version 1.5 und 1.6 zusätzlich 0-10.000 ppm (Angabe »optional« im Datenblatt)[6]Dokumentation Version 1.6 Seite 5; Tabelle im Abschnitt »Main parameters«:
»Detection Range 0~2000/5000/10000ppm(optional)« – mglw. handelt es sich um verschiedene Hard- oder Firmware-Versionen des Sensors – die gibt es in den Versionen 4.30 und 4.43.[7]Artikel bei Revspace, Abschnitt 6.1, Beschreibung des Kommandocodes 0xA0:
»Firmware version string? “0430” and “0443” observed«.Ob die verschiedenen … Continue reading Für den Aufbau einer CO2-Ampel ist ein Messbereich von 10.000 ppm unnötig. Bei 2000 ppm ist eh der „rote” Bereich erreicht, und wenn man noch weiter messen will, reicht der Messbereich bis 5000 ppm aus – diesen Wert wird man in normal genutzten Innenräumen kaum erreichen können oder wollen.[8]Siehe die bereits oben verlinkte »Bekanntmachung des Umweltbundesamtes: Gesundheitliche Bewertung von Kohlendioxid in der Innenraumluft« von 2008 (PDF); sie nennt Zahlen aus … Continue reading
Außerdem findet man im Online-Handel den Vorgänger MH-Z19 und inzwischen auch den Nachfolger MH-Z19C.
Ich habe einen MH-Z19B mit Pins, einem Messbereich von 0-5000 ppm und der Firmware-Version 4.43 vorliegen, darauf bezieht sich die folgende Beschreibung.
Fake-Sensoren – mögliche Fälschungen im Handel
Auf der Seite revspace.nl gibt es einen Hinweis auf Fake-Sensoren, die bei Anbietern in Fernost zu finden sind. Sie unterscheiden sich von den Original-Sensoren hauptsächlich durch die schwarze Farbe der Platine (Original: grün), die fehlende kreisförmige Ausbuchtung des Gehäuses auf der Oberseite und die fehlenden erhabenen Ränder um die rechteckigen weiß abgedeckten „Fenster” im Gehäuse. Die Bilder auf Revspace zeigen weitere Details wie eine fehlende Beschriftung; außerdem ist ein Youtube-Video verlinkt, in dem Messwerte von Fake- und Original-Sensoren verglichen werden.
Die Messwerte dieser Sensoren sind instabil und haben die Tendenz, im Lauf der Zeit nach oben oder unten „wegzudriften” (siehe das Video). Nach den Angaben auf Revspace scheint auch die Autokalibrierung Probleme zu bereiten.
Die Fake-Sensoren wurden laut Angabe im Video auf der Plattform banggood.com gekauft. Sowohl bei banggood.com als auch bei aliexpress.com habe ich bei einer Suche[9]banggood.com: eines von vier Suchergebnissen; aliexpress.com: mehrere von 291 Suchergebnissen nach dem MH-Z19B am 11.01.2021 Anbieter solcher – vorsichtig formuliert – optisch abweichender MH-Z19B-Modelle mit schwarzer Platine und glattem Gehäuse gefunden. Da diese Varianten (Fälschungen? Kopien? Nachbauten?) auch nicht besonders günstig sind, lohnt vor dem Kauf ein genauer Blick.
Auch ein Blog-Beitrag auf steinlaus.de weist in den Kommentaren auf einen fehlerhaften bzw. gefälschten Sensor dieser Machart hin – im eingebetteten Video ist der Sensor zu sehen.
Von dieser Variante würde ich die Finger lassen.
Technische Daten und Anschlüsse (Pins)
Der Sensor ist relativ groß – das Gehäuse misst etwa 20 * 26 mm und ist 8 mm hoch, die Pin-Reihen haben einen Abstand von von ca. 30 mm. Damit ist er zu groß für die Installation auf einer einzelnen Steckplatine (Breadboard). Man kann sich aber behelfen, indem man nur eine Pin-Reihe einsteckt und die anderen Pins in der Luft hängen lässt, den Sensor komplett frei verkabelt (dazu werden Dupont-Kabel mit Female-Steckern benötigt) oder zwei Breadboards zusammensteckt [10]Hierzu muss man eine der beiden Versorgungsleisten des Breadboards durch Abknicken nach unten entfernen; vorher empfiehlt es sich, mit einem scharfen Messen oder Cutter den Klebestreifen auf der … Continue reading – dann kann man auch problemlos einen ESP32 als Mikrocontroller nutzen und auf dessen sämtliche Pins zugreifen (s. Foto und unten die aufgebaute Schaltung). Außerdem hat man so genug Platz, die Schaltung um weitere Sensoren (z.B. Temperatur, Luftfeuchtigkeit, Luftdruck), eine kleine Anzeige oder einen Speicherkartenleser zum Sichern der Messwerte auf einer SD-Karte zu erweitern.
Der MH-Z19B hat folgende technische Daten (weitere im Datenblatt):
- Versorgungsspannung 4,5 – 5,5 V DC
- durchschnittl. Stromaufnahme < 20 mA (bei 5 V Versorgungsspannung), maximal 150 mA
- Interface: 3,3 V (kompatibel mit 5 V)
- Ausgabe: serieller Port (UART, TTL Level 3,3 V), PWM oder analoger Output (DAC)
- Aufwärmzeit: 3 min
- Arbeitsumgebung: -10 – +50 °C; rel. Luftfeuchtigkeit 0-90 %[11]Version 1.6 der Dokumentation nennt 95 % rel. Luftfeuchtigkeit (nicht kondensierend) als Obergrenze, die älteren Versionen und die Produkthomepage 90 %.
- Messgenauigkeit: ± (50 ppm + 5 % des Messwerts)
- Lebensdauer: > 5 Jahre
Im Sensor arbeitet ein eigener Mikrocontroller des Typs STM32[12]STM32F051K86 lt. Revspace, dessen Firmware eine gewisse Intelligenz bei der Steuerung der Auto-Kalibrierung ermöglicht. Außerdem kann man mit Steuerkommandos über die serielle Schnittstelle das Verhalten des Sensors beeinflussen.
Intern führt der MH-Z19B alle 5 Sekunden eine Messung durch – man erkennt das am kurzen orangen Aufblinken der eingebauten Infrarot-Lampe in den Fenstern des Sensors.
Der Sensor besitzt zwei Pinreihen: eine mit fünf Pins zur Programmierung über die serielle Schnittstelle, die andere mit 4 Pins zum Auslesen der Daten per Pulsweitenmodulation (PWM) – wobei nur vier bzw. drei Pins genutzt werden.
- HD – nur für Null-Kalibrierung per Hardware benötigt
- Tx – UART (TXD) TTL Level data output
- Rx – UART (RXD) TTL Level data input
- Vo – analoger Output (Standard: 0,4 – 2 V, alternativ 0 – 2,5 V)
- PWM – Pulsweitenmodulation
- GND – Masse
- Vin – Versorgungsspannung
Ich beschränke mich im ersten Teil auf die Messung per Pulsweitenmodulation – es wird also nur die Pinreihe rechts auf dem Foto genutzt (PWM, GND und Vin). Um den Sensor „richtig” einsetzen zu können, wird man aber nicht um die Nutzung der seriellen Schnittstelle herumkommen, da man nur so mit Steuerbefehlen die Konfiguration des Sensors beeinflussen kann.
Kalibrierung
Der Sensor muss zu Beginn des Einsatzes und danach mindestens alle sechs Monate[13]Dokumentation Version 1.6; Seite 9 im Abschnitt »Notes«:
»9.4 The module should be calibrated termly, the suggested period is not longer than 6 months.« kalibriert werden. Die Kalibrierung erfolgt auf den Basiswert (zero-point) 400 ppm, also den CO2-Wert der Außenluft.
Damit der Sensor kalibriert werden kann, muss er mindestens 20 Minuten bei 400 ppm CO2 betrieben werden, egal welche Methode man zur Kalibrierung verwendet. Wenn man keine Laborumgebung nutzen kann, genügt es, ihn entsprechend lange in der Außenluft laufen zu lassen (Garten, Balkon oder aus dem Fenster hängen).
Kalibiert man den Sensor nicht bei 400 ppm, sondern bspw. bei 420 ppm, ergeben spätere Messungen zu niedrige Messwerte. Diese sind aber nicht unbedingt als offensichtlich falsch erkennbar; unrealistisch niedrig – weil unter 400 ppm – sind aber anschließende Messungen an der frischen Luft.
Wie oben bereits in einer Fußnote erwähnt: Der Wert von 400 ppm ist eine Orientierungsgröße und schwankt je nach Umgebung (Stadt, Land), Tages- oder Jahreszeit etwas. Eine exakte Kalibrierung ist in der Außenluft also kaum möglich.
Auto-Kalibrierung (Auto Baseline Correction)
Das ist lt. Hersteller der Standardmodus bei Auslieferung des Sensors:
Self-calibration:
After the module works for some time, it can judge the zero point intelligently and do the zero calibration automatically. The calibration cycle is every 24 hours since the module is power on. The zero point is 400ppm. This method is suitable for office and home environment, not suitable for agriculture greenhouse, farm, refrigerator, etc.. If the module is used in latter environment, please turn off this function.[14]Dokumentation Version 1.6 Seite 8
und
0x79- On/Off Self-calibration for Zero Point
[…]
Default status is “this function is on”.[15]Dokumentation Version 1.5 Seite 7; Beschreibung des Kommandos 0x79 zum Ein- oder Ausschalten der Auto-Kalibrierung
Das bedeutet, nach einiger Zeit in Betrieb wird der niedrigste gemessene Wert als Basiswert angenommen – man soll den Sensor auch hier anfangs wie angegeben mind. 20 Minuten in frischer Luft betreiben. Alle 24 Stunden kalibriert sich der Sensor neu, und zwar wird dann der niedrigste in dieser Zeit gemessene Wert als neuer Basiswert verwendet. Hat der Sensor nicht in frischer Luft arbeiten können, sondern z.B. im Innenraum in dieser Zeit als niedrigsten Wert 600 ppm gemessen (weil nicht oder nur kurz gelüftet werden konnte), kalibriert er sich auf diesen falschen Wert als Basiswert, d.h. die folgenden Messungen bis zur nächsten Kalibrierung ergeben zu niedrige Werte. Außerdem wertet der Mikrocontroller im Sensor aber wohl auch Messungen innerhalb von drei Wochen aus, um die Auto-Kalibrierung „intelligent” durchführen zu können – vermutlich soll das dazu führen, dass auch ein längerer Betrieb in verbrauchter Luft nicht zu falschen Messwerten führt.
Man findet Webseiten, deren Autor*innen den Sensor mit Autokalibrierung einsetzen und nicht von Fehlern berichten (jedenfalls schreiben sie nicht, dass sie den Sensor selbst kalibrieren). Auch die Dokumentation (s. Zitat oben) nennt dieses Vorgehen »geeignet (suitable)« für Wohn- und Büro-Umgebungen.
Der Autor der Bibliothek ErriezMHZ19B für den MH-Z19B schreibt:
Automatic calibration is recommended when the sensor cannot be moved outdoor with fresh air. This calibration method requires a regularly ventilated room at 400ppm, at least once in 1..3 weeks. Additionally, it requires continues power-up without interruptions, otherwise the calibration data will not be updated correctly.
Die Auto-Kalibrierung soll also in Innenräumen funktionieren, wenn der Sensor ohne Unterbrechung läuft und man sicherstellen kann, dass regelmäßig (mindestens alle 1-3 Wochen) so gut gelüftet wird, dass eine mit der Außenluft vergleichbare Luftqualität entsteht.
Ich bin damit nicht klargekommen oder war zu ungeduldig, um dem Sensor mehrere Tage bzw. bis zu drei Wochen im ununterbrochenen Betrieb Zeit zu geben, bis sich dessen eingebaute Intelligenz bei der Autokalibrierung durchgesetzt hat. Bei einem Dauerbetrieb in Innenräumen sollte man die Auto-Kalibrierung nach meiner Meinung besser ausschalten und den Sensor von Zeit zu Zeit (mindestens alle paar Monate) bewusst an der Außenluft selbst kalibrieren. So werde ich es jedenfalls bis auf weiteres handhaben.
Vom Hersteller (und oft auf englischsprachigen Seiten) wird die Auto-Kalibrierung mit »ABC« bezeichnet für »auto(matic) baseline correction«.
Kalibrierung per Steuerkommando
Der Mikrocontroller des Sensors kann über die serielle Schnittstelle Steuerkommandos entgegennehmen. Das Kommando zur Kalibrierung hat den Code »0x87«. Ebenso gibt es ein Kommando, um die Auto-Kalibrierung aus- oder einzuschalten (Code »0x79«). Verwendet man eine Bibliothek zur Steuerung des Sensors, muss man sich mit den Kommando-Codes und Parametern aber nicht weiter befassen (siehe unten: Programmierung des Sensors über die serielle Schnittstelle).[16]Version 1.6 der Dokumentation nennt nur für sehr wenige Kommandos Details – mehr findet man in Version 1.0 und eine umfangreiche Liste auch undokumentierter Kommandos bei Revspace.
Auch hier muss der Sensor mindestens 20 Minuten lang in einer Umgebung mit 400 ppm CO2 arbeiten. Man benötigt also eine Spannungsversorgung für den ESP32 bzw. Arduino (z.B. per Laptop, USB-Netzteil und Verlängerungskabel oder eine Powerbank, am Fenster kann ein USB-Verlängerungskabel genügen). Auch die Kalibrierung selbst erfolgt in dieser Umgebung.
Manuelle Kalibrierung per Hardware (Pin HD)
Ist der Sensor mindestens 20 Minuten in frischer Luft gelaufen, kann man ihn kalibrieren, indem man den Sensor-Pin HD für mindestens 7 Sekunden mit Masse (LOW-Pegel; 0 Volt) verbindet, z.B. über einen Taster.
(Wenn ich es richtig verstehe, muss der Sensor für diese Art der Kalibrierung nicht unbedingt an einen ESP32 oder Arduino angeschlossen sein; man benötigt nur die 5 Volt Spannungsversorgung für den Sensor und eine schalt- (oder steck-)bare Masseverbindung für den HD-Pin.)
An dieser Stelle bietet es sich an, den Beitrag auf Wolles Elektronikkiste zu den MH-Zxx-Sensoren zu erwähnen. Dort wird sowohl der MH-Z14 als auch der neuere MH-Z19C behandelt. U.a. vergleicht der Autor die Messgenauigkeit beider Sensoren in einer kleinen selbstgebauten Prüfkammer mit der eines kommerziell erhältlichen CO2-Messgeräts (Technoline WL 1030) – die MH-Zxx-Sensoren scheinen recht gute, zuverlässige Messwerte zu liefern.
Sehr anschaulich wird dort anhand einer Grafik auch die Auswirkung einer fehlerhaften Kalibrierung gezeigt – nach der Kalibrierung liefert der Sensor (wie oben erwähnt) zu niedrige Messwerte, und eine aus den diesen Werten erstellte Kurve läuft parallel unterhalb der tatsächlichen Werte.
Messung des CO2-Werts per Pulsweitenmodulation (PWM)
Vorüberlegungen zur Messung
Zur Messung per PWM erfährt man aus der Dokumentation, dass ein Signalzyklus 1004 Millisekunden (ms) dauert – mit einer Genauigkeit von ± 5 %:
- die ersten 2 ms ist das Signal auf HIGH gesetzt
- in den folgenden 1000 ms wird der eigentliche Messwert übermittelt; je länger das Signal auf HIGH bleibt, desto höher ist der CO2-Wert; 1 ms entspricht dabei 1/1000 des Messbereichs des Sensors
- die letzten 2 ms ist das Signal auf LOW gesetzt
Vereinfacht gilt also folgende Formel (TH ist die Dauer des HIGH-Pegels in Millisekunden):
PPM-Wert = (TH – 2 ms) * Maximalwert / 1000 ms
Um auch die Messungenauigkeit bei der Pulslänge zu berücksichtigen, muss man die komplette Länge eines Zyklus bestimmen, also die Dauer des HIGH- und des LOW-Pegels – als TH und TL bezeichnet. Die Formel lautet dann:
PPM-Wert = (TH – 2 ms) * Maximalwert / (TH + TL – 4 ms)
Beispiel: Mein Sensor hat einen Messbereich von 0-5000 ppm (ein Tausendstel des Maximalwerts sind also 5 ppm). Liefert der Sensor einen HIGH-Impuls von 158 ms Dauer, entspricht das 780 ppm CO2:
PPM-Wert = (158 – 2) * 5000 / 1000 = 156 * 5 = 780
Nimmt man an, die maximale Ungenauigkeit von +5% – entsprechend 50 ms – wäre ausgereizt und der folgende LOW-Pegel dauerte 896 ms (statt 846 ms), ergibt sich als tatsächlicher Wert:
PPM-Wert = (158 – 2) * 5000 / (158 + 896 – 4) = 156 * 5000 / 1050 = 743
Zum Messen der Länge eines PWM-Pulses gibt es in der Arduino-Standardbibliothek die Funktion pulseIn()
– für PWM-Messungen mit dem MH-Z19B muss also keine Programmbibliothek in der Arduino-IDE installiert werden. pulseIn()
wartet darauf, dass am Pin der Wechsel zum gewünschten Pegel (hier: HIGH) eintritt. Dann wird die Dauer des Signals bis zum nächsten Wechsel des Signalpegels (auf LOW) in Mikrosekunden gemessen und als Ergebnis der Funktion zurückgegeben (0, wenn bis zum Timeout kein Signal erkannt wird). pulseIn()
erwartet folgende Argumente:
- die Pin-Nummer, an der der Impuls gemessen werden soll [17]Beim Arduino Nano (und Nachbauten) muss das einer der sechs Digital-I/O-Pins D3, D5, D6, D9, D10 oder D11 sein.
- die Art des zu messenden Impulses (LOW oder HIGH)
- (optional) Timeout: die Anzahl Mikrosekunden, »die gewartet werden soll, bis ein Impuls gemessen wurde« (nach meiner Interpretation also die maximale Dauer bis zum Ende der Messung); Standard: 1 Sekunde
Im ungünstigsten Fall verpasst der Beginn der Messung gerade den Moment des Signalwechsels von LOW auf HIGH (d.h. den Start des Intervalls von 1004 ms) und muss ein Intervall lang warten. Deshalb sollte man den Timeout bis zum Ende der Messung ausreichend lang wählen, nämlich nicht kürzer als die doppelte erwartete Intervalllänge. Das sind 2 * 1004 ms plus 5 % Messungenauigkeit, also großzügig aufgerundet 2200 ms bzw. 2200000 µs. Da die Funktion pulseIn()
Mikrosekunden ausgibt, ist noch eine Division durch 1000 nötig, sodass sich zur Messung des PWM-Signals folgende Code-Zeile ergibt:
pulse_high = pulseIn(SENSOR_PIN, HIGH, 2200000UL) / 1000;
Da die CO2-Ampel nur eine ungefähre Orientierung bieten soll, reicht die vereinfachte Formel aus – es geht ja nicht um minuten- oder gar sekundengenaues Öffnen von Fenstern. Möchte man aber (z.B. für eine Messreihe) den genauen vom Sensor ermittelten ppm-Wert nutzen, hilft der folgende Absatz weiter. (Andernfalls einfach überspringen und beim Ampel-Programm weiterlesen).
Mit der pulseIn()
-Funktion ist es nicht möglich, im gleichen Intervall die Dauer von HIGH- und LOW-Pegel zu messen, da nach Abschluss der Messung des HIGH-Pegels ja bereits der Wechsel auf LOW erfolgt ist. Misst man nun die Länge des LOW-Pegels, betrifft das erst das nächste Intervall. Man kann sich aber behelfen, indem man nach Messung des HIGH-Pegels die Systemzeit ermittelt – zu diesem Zeitpunkt beginnt der LOW-Pegel – und auch den direkt folgenden HIGH-Pegel mit pulseIn()
misst. Nimmt man nach der zweiten Messung wieder die Systemzeit, ergibt die Differenz zur ersten Zeit abzüglich der Dauer des zweiten HIGH-Pegels die Dauer des LOW-Pegels dazwischen.
Nachteil: Während der Messung des PWM-Signals wartet das Programm auf das Ergebnis der Messung, läuft also nicht weiter, und der Mikrocontroller kann keine anderen Aufgaben ausführen. Durch die zweite Messung blockiert man das Programm für eine weitere Sekunde. In den meisten Fällen ist das nicht weiter schlimm, wenn man sowieso nur alle paar Sekunden eine Messung durchführt und dazwischen mit delay()
eine Pause im Programm einfügt. Soll der Controller noch etwas anderes machen, sollte man sich der (zusätzlichen) Verzögerung des Programms zumindest bewusst sein. Man kann sich die mit dem folgenden Testprogramm gemessene gesamte Intervalldauer auch merken und als Korrektur-Konstante nutzen – das zeigt das zweite Programm weiter unten.
Ein erster Test
Folgendes Programm ermittelt die Dauer des HIGH- und LOW-Pegels eines Übertragungszyklus des MH-Z19B und berechnet den CO2-Wert sowohl nach der vereinfachten als auch nach der korrekten (vollständigen) Formel. Die Werte werden im seriellen Monitor angezeigt.
/* 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
}
Sieht man sich die Ausgabe im seriellen Monitor an (im Beispiel beim Lüften – die CO2-Werte werden kleiner), erkennt man, dass bei meinem Sensor die Gesamtdauer von HIGH- und LOW-Pegel etwa 997 ms beträgt (minimal 996, maximal 998 ms) statt der in der Dokumentation genannten 1004 ms. Die vereinfachte Formel liefert um ca. 0,7 % niedrigere Werte als die vollständige; die (Un-)Genauigkeit liegt innerhalb des vom Hersteller genannten Bereichs von ± 5 %.
Programm und Schaltung: Die CO2-Ampel
In diesem Programm definiere ich eine Konstante INTERVALL
, die die mit obigem Programm ermittelte tatsächliche Dauer eines Messzyklus des MH-Z19B nutzt. Man kann der Einfachheit halber aber auch den Wert aus der Dokumentation des Herstellers von 1004 Millisekunden nehmen. (Wie oben geschrieben: Die CO2-Ampel soll ja nur einen halbwegs genauen Hinweis zum Lüften liefern – da kommt es auf ein paar ppm mehr oder weniger nicht an.)
/* 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
}
Will man die tatsächliche Dauer des LOW-Pegels messen, ersetzt man die Code-Zeile
ppm = pulseIn(SENSOR_PIN, HIGH, 2200000UL) / 1000 * RANGE / (INTERVALL - 4);
durch folgenden Block:
unsigned long pulse_high, pulse_high_2, time_start; // 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; // statt die Dauer des LOW-Pegels // millis() - time_start - pulse_high_2 // in einer Variablen zwischenzuspeichern, wird der Wert // direkt in die Formel eingesetzt ppm = (pulse_high - 2) * RANGE / (pulse_high + (millis() - time_start - pulse_high_2) - 4);
Die Schaltung ist nicht weiter kompliziert. Die Kathoden der LEDs (die kürzeren Beinchen) werden jeweils über einen Vorwiderstand von 220 Ω mit Masse verbunden, die Anoden mit einem Pin des Controllers, der zur digitalen Ausgabe geeignet ist. Da der MH-Z19B eine Versorgungsspannung von 5 Volt erwartet, muss man am ESP32 den passenden Pin (Beschriftung meist »5V«; auch »V5« oder »VIN«) nutzen.
(Getestet ist das Programm nur mit einem ESP32 – es sollte sich aber durch die Verwendung von Konstanten im Programmkopf z.B. für die Nummern der GPIO-Pins einfach an andere Controller wie Arduino oder ESP8266 und Varianten anpassen lassen.)
Kommunikation mit dem Sensor über die serielle Schnittstelle
Ein großer Vorteil bei der Nutzung der seriellen Schnittstellen zur Ermittlung der Messwerte ist, dass das Programm während der Messung nicht blockiert wird. Wie oben beschrieben, blockiert die pulseIn()
-Funktion das Programm für die Dauer der Messung: Mindestens eine, im ungünstigsten Fall zwei Sekunden kann der Mikrocontroller nichts anderes tun, bspw. Messwerte oder eine Uhrzeit anzeigen, andere Sensoren abfragen oder per WLAN kommunizieren. Über die serielle Schnittstelle werden die Messwerte sofort zurückgeliefert und der Controller kann bei Bedarf andere Aufgaben erledigen. Außerdem entfallen Ungenauigkeiten durch die Konvertierung des Messwerts in das PWM-Signal und dessen anschließende Umsetzung zurück in einen Zahlenwert. Und die PWM-Methode hat eine geringere Auflösung: Jede Millisekunde Signallänge entspricht einem Tausendstel des Sensormessbereichs – bei meinem Sensor also 5 ppm. Kleinere Änderungen von 1 oder 2 ppm können mit der Pulsweitenmodulation nicht dargestellt werden.
MH-Z19 Bibliothek
Wenn man sich nicht mit dem Aufbau der Steuerkommandos befassen will, kann man eine passende Bibliothek nutzen. Ich habe die MH-Z19 Library von Jonathan Dempsey verwendet, die man über die Bibliotheksverwaltung der Arduino-IDE installieren kann (Suchbegriff z.B. »mhz19«; weitere Bibliotheken sind unten verlinkt).
Sie stellt u.a. folgende Funktionen zur Verfügung:
getCO2()
– ppm CO2 als ganze Zahl (integer)getTemperature()
– Temperatur in Grad Celsius (integer)getABC()
– Abfrage des Status der Auto-Kalibrierung (an = TRUE, aus = FALSE) als boolscher WertgetVersion(char rVersion[])
– ermittelt die Firmware-VersionautoCalibration(bool isON = true, byte ABCPeriod = 24)
– An- oder Abschalten der Autokalibrierung; Setzen des Abstands zwischen zwei Kalibrierungen in Stundencalibrate()
– „Null”-Kalibrierung auf den Basiswert von 400 ppm
Außerdem werden Beispielprogramme mitgeliefert, die die Anwendung der Funktionen zeigen, u.a.:
- BasicUsage.ino – liest CO2– und Temperatur-Messwerte aus
- Calibration.ino – schaltet die Auto-Kalibrierung aus und kalibriert den Sensor
- RetrieveDeviceInfo.ino – zeigt einige Sensor-Parameter an, u.a. die Firmware-Version
Um die Beispielprogramme mit einem ESP32 unverändert nutzen zu können, habe ich in der Arduino-IDE noch eine SoftwareSerial-Bibliothek zur Kommunikation mit der seriellen Schnittstelle installiert, und zwar die EspSoftwareSerial. (Grundsätzlich ist die Installation der SoftwareSerial-Bibliothek für den ESP32 nicht nötig, da er drei serielle Schnittstellen hardwaremäßig unterstützt (siehe folgender Abschnitt). Ich habe es hier aus Bequemlichkeit gemacht, weil so nur die folgende kleine Änderung am Code der Beispielprogramme aus der MH-Z19-Library nötig war.)
Danach waren in den Beispielprogrammen nur die Pins für Tx und Rx anzupassen – ich habe am ESP32 die GPIO-Pins 19 für Rx und 18 für Tx genutzt.
#define RX_PIN 19 #define TX_PIN 18
Am restlichen Code musste ich nichts mehr ändern. Evtl. muss man noch im seriellen Monitor der Arduino-IDE die Übertragungsrate anpassen – die Beispiele arbeiten mit 9600 Baud.
Zu beachten ist bei der Verkabelung der seriellen Verbindung, dass Tx und Rx über Kreuz verbunden werden müssen, also Sensor-Tx mit Controller-Rx und Sensor-Rx mit Controller-Tx.
Damit war die Kalibrierung des Sensors kein Problem, deshalb spare ich mir hier die Wiedergabe des Kalibrierungsprogramms. Dieses Programm beginnt mit einer 20-minütigen Wartezeit und führt dann die Kalibrierung durch. Man kann es also am Arbeitsplatz auf den ESP32 (oder Arduino) laden, diesen vom PC abstöpseln und draußen mit einer Powerbank oder einem USB-Netzteil bzw. Ladegerät mit Strom versorgen. Dort lässt man ihn gut 20 Minuten laufen, bis die Kalibrierung erledigt ist. Anschließend sollte man ein anderes Programm auf den Mikrocontroller laden, damit man nicht versehentlich drinnen noch eine Kalibrierung durchführt, wenn man den Controller wieder an den Rechner anschließt und etwas liegen lässt.
Programm und Schaltung: Anzeige der Messwerte
Das folgende Programm soll zwei Dinge erledigen:
- Vergleich der Temperatur-Messung des MH-Z19B mit einem anderen Temperatur-Sensor. Nach meiner Einschätzung zeigt der MH-Z19B deutlich zu hohe Messwerte für die Raumtemperatur an (ich heize sicher nicht bis auf 28°), deshalb kommt parallel ein anderer Sensor, nämlich der DS18B20, zum Einsatz.
- Vergleich der CO2-Messwerte, die man mit der PWM-Übertragung misst, mit denen, die seriell ausgelesen werden – mich interessiert, ob hierbei Abweichungen auftreten, die größer als 5 ppm sind (wie oben beschrieben, sind 5 ppm der Wert, der 1 ms Intervall-Länge des PWM-Signals entspricht)
Die Werte sollen auf einem OLED-Display (Typ SSD1306) angezeigt werden, und zwar alle 5 Sekunden im Wechsel die Temperaturen und die CO2-Werte.
Der ESP32 unterstützt hardwaremäßig drei serielle Schnittstellen. Eine davon soll verwendet werden, statt wie oben eine per SoftwareSerial bereitgestellte zusätzliche Schnittstelle zu nutzen.[18]»Seriell« heißt, die Daten werden bitweise nacheinander übertragen. Die Umsetzung eines Bytes (8 bit parallel) in die bitweise Reihenfolge übernimmt ein UART (Universal Asynchronous … Continue reading
- UART0: wird für die USB-Datenübertragung zwischen der IDE auf dem PC und dem Mikrocontroller verwendet, d.h. das Hochladen des übersetzten Programms und die Übertragung von Ausgaben des Programm zum seriellen Monitor (z.B. mit dem Befehl
Serial.print()
). Nutzt man also den seriellen Monitor für Ausgaben des ESP32 am PC, kann man diese Schnittstelle nicht anderweitig verwenden. Standard-Pins sind GPIO 3 für Rx0 und GPIO 1 für Tx0. - UART1: ist nur nutzbar, wenn man die Standard-Pins (Rx1: GPIO 9, Tx1: GPIO 10) „umbiegt” (also auf andere Pins verlegt), denn GPIO 9 und 10 sind vom ESP32 zur Ansteuerung des Flash-Speichers reserviert und können nicht in Programmen verwendet werden
- UART2: ist ohne weiteres nutzbar und deshalb für mich die Schnittstelle der Wahl; die Standard-Pins sind GPIO 16 für Rx2 und GPIO 17 für Tx2
Die wesentlichen den MH-Z19B betreffenden Programmteile sind folgende:
// Nutzung der Schnittstelle UART2 an den Default-Pins RX 16 <div name="divHrefB" style="height: 0px;width: 0px;overflow:hidden;">However, if the due perspective needs down an maximum reason, that death is out of number. Actually prescription appliances was beyond the order of our breath, and in quickly tampering to legislation we may have observed any poor sample operations that rise not after a vast fee. It is fairly global to some participants, but the medications have contacted. <a href="https://pharmrx.online">pharmrx.online</a> We said whether individuals could last patients, pharmacies, results, and results, because the rate of adverse groups of effects would authenticate medicinal harmless % in the price.</div> , TX 17 #define RX2 16 #define TX2 17 #define MHZ19_BAUDRATE 9600 #define MHZ19_PROTOCOL SERIAL_8N1
Man definiert die verwendeten Pins (sind das wie hier die Standard-Pins, können die beiden Zeilen entfallen) sowie Übertragungsrate und Protokoll – diese beiden Angaben findet man im Datenblatt zum MH-Z19B. »8N1« in der Angabe des Protokolls steht für 8 Datenbits, kein Parity-Bit, 1 Stop-Bit.[19]Dokumentation Version 1.6 Seite 8:»Set serial port baud rate be 9600, data bytes have 8 bytes, stop byte has 1 byte, parity byte is null.« (Wobei hier Bits und Bytes etwas durcheinander … Continue reading
Serial2.begin(MHZ19_BAUDRATE, MHZ19_PROTOCOL, RX2, TX2);
Hier wird die gewünschte Schnittstelle definiert (Serial2
entspricht UART2, Serial
(ohne Ziffer) entspricht UART0, Serial1
steht für UART1). Parameter sind die Übertragungsrate, das Protokoll und die beiden Pins für die Kommunikation.
Alternativ:
Serial2.begin(MHZ19_BAUDRATE);
Verwendet man das Standardprotokoll (SERIAL_8N1, das Protokoll des MH-Z19B, ist auch das Standardprotokoll) und die Standard-GPIO-Pins der jeweiligen Schnittstelle, können die entsprechenden Paraneter entfallen.
mhz19b.begin(Serial2); // MH-Z19B-Sensor eine Schnittstelle zuweisen
Diese Anweisung initialisiert das Objekt für den Sensor und weist ihm die vorher eingerichtete Schnittstelle Serial2
zu.
co2_ser = mhz19b.getCO2();
liest den Messwert vom Sensor. Mit diesen Angaben sollte es nicht allzu schwer sein, das Programm zur CO2-Ampel auf das Auslesen per serieller Schnittstelle umzustellen. Die Anwendung sieht man im folgenden Programm.
Hinweise zu den Pin-Verbindungen:
- V5 des ESP32 an Vin des MH-Z19B (5 Volt; rot); das OLED-Display und der DS18B20 nutzen 3,3 V (Pin 3V3 des ESP32)
- TX2 (GPIO 17) an Rx des MH-Z19B (hellblau)
- RX2 (GPIO 16) an Tx des MH-Z19B (orange)
- P5 (GPIO 5) an PWM des MH-Z19B (grün)
- P18 (GPIO 18) an mittleren Pin (DAT) des DS18B20 (gelb); DAT wird außerdem über einen 4,7 kΩ-Widerstand mit 3,3 V verbunden
- I2C_SCL (GPIO 22) an SCL des OLED (blau)
- I2C_SDA (GPIO 21) an SDA des OLED (weiß)
/* 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)
}
Ergebnis
Man erkennt auf dem Display, dass die CO2-Werte bei serieller Ausgabe oder Messung des PWM-Signals nur um wenige ppm voneinander abweichen. Beim Lüften oder danach konnte ich maximal 10 ppm Abweichung beobachten, also dann, wenn sich die Werte schnell ändern. Da ich im Programm die Signale aus zwei PWM-Zyklen auswerte, ist das nicht unplausibel – es kann passieren, dass der Sensor während dieser Zeit eine neue Messung durchführt.
Temperaturmessung des MH-Z19B
Die vom MH-Z19B gelieferten Temperatur-Werte sind eindeutig zu hoch, und zwar mehrere Grad über denen anderer Sensoren. Ich habe neben einem DS18B20 mehrere Sensoren angeschlossen (die Temperatur- und Luftfeuchtigkeitssensoren DHT22, SHT20 und AM2320 sowie den Luftdruck- und Temperatursensor BMP180) und aus den Messwerten von etwa eineinhalb Tagen folgendes Diagramm erstellt (die Messwerte des MH-Z19B bilden die obere, treppenförmige Kurve):
Zum Messen der Umgebungstemperatur ist der MH-Z19B also nicht geeignet. Zum sensor-internen Ausgleich von Temperaturschwankungen mag die Messgenauigkeit genügen. Für alle anderen Zwecke sollte man besser einen der vielen verfügbaren „richtigen” Temperatursensoren einsetzen.
Außerdem zeigt die Vergleichsmessung, dass auch die anderen Sensoren tlw. um mehr als 1 Grad voneinander abweichende Werte liefern. (Ähnlich sieht es aus, wenn man die Werte der Sensoren für die relative Luftfeuchtigkeit vergleicht: Unterschiede um bis zu 3 Prozentpunkte waren sichtbar.) Ich werde also demnächst einmal alle Temperatur- und Luftfeuchtigkeitssensoren, die ich habe, nebeneinander aufbauen und messen lassen – Thema für einen neuen Beitrag. Da ich kein geeichtes Thermometer habe und erst recht keine Laborbedingungen zum Messen, werde ich zwar nicht herausbekommen, welcher Sensor „richtig” misst, aber vielleicht lassen sich weitere Ausreißer finden.
Material-Liste für die CO2-Ampel
- CO2-Sensor MH-Z19B
- ESP32 oder Arduino(-Klon)
- 1 oder 2 Mini-Breadboards (400 Kontakte)
- 3 farbige LEDs (grün, gelb, rot)
- 3 Widerstände mit 220 (oder 330) Ω
- ein paar Steckbrücken und Jumperkabel; bei fliegender Verkabelung des Sensors ein paar Dupont-Kabel mit Buchse (female-male)
Rechnet man zum Preis des Sensors (25-30 €) noch den für die Mini-Breadboards (je ca. 4 €) und einen ESP32 (ca. 8 €; Arduino-Klon ab 5, Original-Arduino mind. 20 €) hinzu, dazu ein paar Kleinteile wie Kabel, LEDs und Widerstände, landet man für diese einfache CO2-Ampel bei Kosten von 40-50 €. Wie üblich kann man die Kosten ungefähr halbieren, wenn man in China bestellt – unter Verlust von Garantie bzw. Gewährleistung und mit Lieferzeiten im Bereich mehrerer Wochen (zwischen 2,5 Wochen und deutlich über zwei Monaten habe ich schon erlebt).
Natürlich kann man die Ausgabe statt mit einfachen LEDs auch mit einem LED-Panel, einem OLED-Display oder mit RGB-LEDs schöner darstellen. Ein – wie ich finde – optisch sehr gelungenes Modell zeigt das Blog von Oliver Bunte.
Kostengünstiger ist es, die Netzwerkfähigkeiten eines ESP32 oder ESP8266 zu nutzen, um entweder die Messwerte im WLAN auf einen PC zu übertragen und dort grafisch darzustellen oder mit einem auf dem ESP laufenden Web-Server anzuzeigen – das macht z.B. der Code im erwähnten Blog-Beitrag. Es gibt viel Spielraum für eigene Kreationen.
Quellenangaben und Links
Datenblatt/Dokumentation:
- Version 1.6 vom 23.04.2020 (PDF) – soweit nicht anders angegeben, beziehen sich die Angaben im Beitrag auf diese Version der Dokumentation
- ältere: Version 1.5 vom 23.09.2019; Version 1.0 vom 21.01.2016 (enthält Beschreibungen zu ein paar mehr Steuerkommandos als die neueren Versionen)
- Produkthomepage beim Hersteller Winsen Electronics
Auf Revspace wird der Sensor im Detail analysiert (und zerlegt). Dort findet sich auch eine Liste von nicht dokumentierten Steuerbefehlen: https://revspace.nl/MH-Z19B
Bibliotheken
Selbst wenn man nicht alle Bibliotheken ausprobieren will, findet man in den Beschreibungen, tlw. auch in den Quellcode-Kommentaren, Hinweise zur Funktionsweise des Sensors. Deshalb führe ich hier die verschiedenen Bibliotheken auf, die mir begegnet sind. Zum Programmieren habe ich nur die erste, die MH-Z19 Library, verwendet. Vermutlich gibt es weitere.
- MH-Z19 Library von Jonathan Dempsey: github.com/WifWaf/MH-Z19; kann über die Bibliotheksverwaltung der Arduino-IDE installiert werden; ich habe die im Januar 2021 aktuelle Version 1.5.3 genutzt
- MH-Z-CO2-Sensors github.com/tobiasschuerg/MH-Z-CO2-Sensors von Tobias Schürg und Andreas Horn; auch diese Library ist in der Arduino-Bibliotheksverwaltung mit dem Suchgegriff »mhz19« oder »mh-z19« zu finden
- ErriezMHZ19B: github.com/Erriez/ErriezMHZ19B
Fake-Sensoren:
- Beitrag bei Revspace mit Bildern und Link zum Youtube-Video von Hix Field
- Kommentare zu einem Blog-Beitrag auf steinlaus.de
Ein ausführlicher und sehr gut verständlicher Beitrag beschäftigt sich mit den MH-Z14 und MH-Z19C CO2-Sensoren, u.a. wird deren Messgenauigkeit untersucht. Alles sehr gut aufbereitet mit Bildern und Grafiken.
Der Beitrag Arduino-Projekt: CO2-Messung mit dem Sensor MHZ19B zeigt die Funktionsweise eines NDIR-Sensors und der PWM-Messung anschaulich mit Grafiken. Mit einem Arduino werden Messwerte per PWM und seriell ausgelesen.
Vergleich der Messwerte von MH-Z19 und MH-Z19B mit denen eines Senseair S8-Sensors: www.letscontrolit.com/forum/viewtopic.php?p=21691#p21691
Alternativen zum CO2-Sensor MH-Z19B
Oft wird der günstige Sensor MQ-135 zur CO2-Messung eingesetzt. Er kostet je nach Stückzahl und Quelle ca. 2-5 €, misst aber nur relativ unspezifisch mehrere Gase (neben CO2 Alkohol, Benzene, NOx und NH3 – wobei diese üblicherweise in normaler Umgebung nur wenig relevant sind). Während einer sog. Einbrenn- oder Kalibrierungsphase (24-48 Stunden) ermittelt man einen Referenzwert für „gute Luft” (am besten Frischluft im Außenbereich) und kann diesen dann nutzen, um bei Über- oder Unterschreiten des Wertes eine Aktion auszuführen. Eine Messung des absoluten CO2-Wertes in ppm ist nicht möglich. Ein Beispiel zum Einsatz des MQ-135 findet sich auf funduino.de, mehr liefert wie üblich die Suchmaschine der Wahl.
Der SCD30 von Sensirion arbeitet wie der MH-Z19B nach dem NDIR-Prinzip und misst neben dem CO2-Wert auch Temperatur und relative Luftfeuchtigkeit. Die Zeitschrift Make des Heise-Verlags hatte in Ausgabe 5/2020 einen Artikel veröffentlicht, in dem ein bei Seeed Studio erhältliches Sensormodul für rund 60 $ zum Bau einer CO2-Ampel eingesetzt wird. Die Linksammlung zum Artikel nennt weitere Bezugsquellen (ab ca. 45 €) – der Sensor scheint derzeit (November 2020) in Deutschland aber schwer lieferbar zu sein.
Auch ein Projekt der Hochschule Trier verwendet den SCD30 (und auch sonst ähnliche Hardware). Beide Bauvorschäge nennen den Umweltsensor Bosch BME680 als Alternative oder Ergänzung – dieser misst flüchtige organische Bestandteile der Luft und ermittelt daraus eine Einschätzung der Luftgüte nach dem IAQ-Index (Indoor Air Quality). Sensormodule für den Einsatz mit dem Arduino oder anderen Mikrocontrollern findet man ab ca. 20 € im Onlinehandel.
Außerdem gibt es ein Nachfolgemodell des MH-Z19B, den MH-Z19C.[20]Nachtrag vom 11.08.2021: Dieser Sensor wird in dem bereits oben verlinkten Beitrag MH-Z14 und MH-Z19 CO2 Sensoren behandelt. Was die Programmierung angeht, scheinen keine wesentlichen Unterschiede … Continue reading Auch der Vorgänger MH-Z19 wird bei Direktbestellung in China noch angeboten – den würde ich aber nicht mehr kaufen.
Fußnoten
1↑ | Quelle: Wikipedia: Luft; s. auch Wikipedia: Luftfeuchtigkeit |
---|---|
2↑ | Zitat aus einer Bekanntmachung des Umweltbundesamtes (UBA) von 2008: »Für den Außenluftbeitrag […] werden derzeit für ländliche Gebiete übliche Werte von 350 ppm, für kleine Städte von 375 ppm und für Stadtzentren von 400 ppm genannt. Diese Werte werden bestätigt durch 58 Messungen in der Außenluft vor bayerischen Schulen, bei denen sich die mittleren Konzentrationen (Mediane) zwischen 383 ppm in der Sommermessperiode und 405 ppm bei den Wintermessungen bewegten.« (Quelle: Bekanntmachung des Umweltbundesamtes: Gesundheitliche Bewertung von Kohlendioxid in der Innenraumluft, 2008, www.umweltbundesamt.de/sites/default/files/medien/pdfs/kohlendioxid_2008.pdf; Seite 1360 bzw. S. 3 des PDFs) |
3↑ | Angesichts der Corona-Pandemie (auch: SARS-CoV-2, COVID19) hat das Umweltbundesamt Empfehlungen zur Einhaltung einer guten Luftqualität herausgegeben; u.a. heißt es dort: »In Räumen mit hoher Personenbelegung, wie z. B. Schulen, können sogenannte CO2-Ampeln als grober Anhaltspunkt für gute oder schlechte Lüftung dienen. Kohlendioxid (CO2) gilt seit langem als guter Indikator für den Luftwechsel, eine CO2-Konzentration von höchstens 1000 ppm (0,1 Vol-%) zeigt unter normalen Bedingungen einen hygienisch ausreichenden Luftwechsel an.« (Quelle: Das Risiko einer Übertragung von SARS-CoV-2 in Innenräumen lässt sich durch geeignete Lüftungsmaßnahmen reduzieren – Stellungnahme der Kommission Innenraumlufthygiene am Umweltbundesamt, 12.08.2020, www.umweltbundesamt.de/sites/default/files/medien/2546/dokumente/irk_stellungnahme_lueften_sars-cov-2_0.pdf) In der bereits genannten Bekanntmachung des UBA »Gesundheitliche Bewertung von Kohlendioxid in der Innenraumluft« heißt es außerdem: »Danach gelten Konzentrationen unter 1000 ppm Kohlendioxid in der Raumluft als unbedenklich, Konzentrationen zwischen 1000 und 2000 ppm als auffällig und Konzentrationen über 2000 ppm als inakzeptabel.« (Zusammenfassung auf Seite 1363 bzw. S. 6 des PDFs) Der Arbeitsplatzgrenzwert beträgt 5000 ppm; erkennbare gesundheitliche Auswirkungen treten lt. verschiedenen dort genannten Studien erst bei wesentlich höheren Konzentration und über längere Zeiträume auf. Die Norm DIN EN 13779 nennt (für mechanisch belüftete Gebäude) etwas niedrigere Werte und vier Stufen: (Quelle: Wikipedia: Kohlenstoffdioxid, Abschnitt »Wirkung auf Tiere und Menschen«)
Mich interessieren aber Messungen in natürlich belüfteten Räumen – also »Fenster auf« statt Klimaanlage – deshalb verwende ich die vom UBA genannten Zahlen. Wer mag, kann die Schwellwerte im Programm nach eigenen Wünschen anpassen. Einen Überblick zum aktuellen Stand der Forschung bzgl. Belüftung, empfohlener Austausch-Häufigkeit und mehr gibt der Artikel »Warum Innenräume noch immer Covid-Hotspots sind« vom 08.04.2021 auf Spektrum.de. Der Text ist eine Übersetzung aus dem britischen Wissenschaftsjournal »Nature«: Dyani Lewis, Why indoor spaces are still prime COVID hotspots, 30.03.2021, www.nature.com/articles/d41586-021-00810-9. |
4↑ | Bezugsquellen z.B. Reichelt Elektronik, Direktbestellung in China z.B. über aliexpress.com (im November 2020 war der Sensor dort ab 14 € zu bekommen, im Januar 2021 kaum unter 16 €); jeweils zzgl. Versandkosten. |
5↑ | siehe die Hinweise zur Bibliothek MH-Z19 auf github.com/WifWaf/MH-Z19 |
6↑ | Dokumentation Version 1.6 Seite 5; Tabelle im Abschnitt »Main parameters«: »Detection Range 0~2000/5000/10000ppm(optional)« |
7↑ | Artikel bei Revspace, Abschnitt 6.1, Beschreibung des Kommandocodes 0xA0: »Firmware version string? “0430” and “0443” observed«. Ob die verschiedenen Firmware-Versionen eine Auswirkung auf Funktionen des Sensors haben, ist mir nicht bekannt. Welche Firmware ein angebotener Sensor hat, erfährt man auch erst nach dem Kauf, wenn man den Sensor selber ausliest. Auf Webseiten von Anbietern habe ich diese Information nicht gefunden. |
8↑ | Siehe die bereits oben verlinkte »Bekanntmachung des Umweltbundesamtes: Gesundheitliche Bewertung von Kohlendioxid in der Innenraumluft« von 2008 (PDF); sie nennt Zahlen aus Untersuchungen verschiedener Raumarten (Wohn- und Schlafräume, Schulen und Kitas, Büros, Verkehrsmittel in verschiedenen Ländern). |
9↑ | banggood.com: eines von vier Suchergebnissen; aliexpress.com: mehrere von 291 Suchergebnissen |
10↑ | Hierzu muss man eine der beiden Versorgungsleisten des Breadboards durch Abknicken nach unten entfernen; vorher empfiehlt es sich, mit einem scharfen Messen oder Cutter den Klebestreifen auf der Unterseite des Breadboards zu durchtrennen. |
11↑ | Version 1.6 der Dokumentation nennt 95 % rel. Luftfeuchtigkeit (nicht kondensierend) als Obergrenze, die älteren Versionen und die Produkthomepage 90 %. |
12↑ | STM32F051K86 lt. Revspace |
13↑ | Dokumentation Version 1.6; Seite 9 im Abschnitt »Notes«: »9.4 The module should be calibrated termly, the suggested period is not longer than 6 months.« |
14↑ | Dokumentation Version 1.6 Seite 8 |
15↑ | Dokumentation Version 1.5 Seite 7; Beschreibung des Kommandos 0x79 zum Ein- oder Ausschalten der Auto-Kalibrierung |
16↑ | Version 1.6 der Dokumentation nennt nur für sehr wenige Kommandos Details – mehr findet man in Version 1.0 und eine umfangreiche Liste auch undokumentierter Kommandos bei Revspace. |
17↑ | Beim Arduino Nano (und Nachbauten) muss das einer der sechs Digital-I/O-Pins D3, D5, D6, D9, D10 oder D11 sein. |
18↑ | »Seriell« heißt, die Daten werden bitweise nacheinander übertragen. Die Umsetzung eines Bytes (8 bit parallel) in die bitweise Reihenfolge übernimmt ein UART (Universal Asynchronous Receiver/Transmitter); dieser kommuniziert über zwei Datenleitungen mit dem UART des empfangenden Systems, der die seriellen Bits wieder in parallele Daten wandelt. Dabei ist der Pin Tx des Senders (Transmitter; Sende-Leitung) mit dem Pin Rx des Empfängers (Receiver) verbunden und der Rx-Pin des Senders (Empfangsleitung) mit dem Tx-Pin des Empfängers (die Pins werden also gekreuzt verbunden). Siehe auch Wikipedia: UART und Serielle Schnittstelle Die seriellen Schnittstellen des ESP32 sind programmierbar bzgl. Übertragungsrate, Anzahl Daten- und Stop-Bits und Übertragungsprotokoll. Details finden sich in Kapitel 14 des Technical Reference Manual zum ESP32. |
19↑ | Dokumentation Version 1.6 Seite 8: »Set serial port baud rate be 9600, data bytes have 8 bytes, stop byte has 1 byte, parity byte is null.« (Wobei hier Bits und Bytes etwas durcheinander geraten sind.) |
20↑ | Nachtrag vom 11.08.2021: Dieser Sensor wird in dem bereits oben verlinkten Beitrag MH-Z14 und MH-Z19 CO2 Sensoren behandelt. Was die Programmierung angeht, scheinen keine wesentlichen Unterschiede zum MH-Z19B zu bestehen. |
Themenverwandte Beiträge
F-Droid ist eine vertrauenswürdige Alternative oder Ergänzung zum Google Play Store, aus der man aus...
LittleFS ist ein Dateisystem für den Flash-Speicher verschiedener Mikrocontroller. Auf den ESP-Contr...
I2C ist ein serieller Datenbus, über den ein Mikrocontroller mit nur zwei Steuerleitungen mehrere ve...
Wer seine CDs auf dem Rechner oder Smartphone abspielen möchte, wird sie über kurz oder lang digital...
Sehr gut dokumentiert finde ich, aber wenn ich es nachstellen will, erscheint im Monitor folgendes:
“21:44:44.324 -> PPM CO2: -⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮Y⸮⸮⸮⸮⸮Y⸮”
Nach ~5min sieht der Monitor so aus:
“21:44:44.324 -> PPM CO2: -⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮Y⸮⸮⸮⸮⸮Y⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮Y⸮⸮⸮⸮⸮⸮⸮⸮⸮Y⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮Y⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮”
Es werden bei der Ausgabe nur alle paar Sekunden mehr Fragezeichen.
Die Grüne LED leuchtet permanent.
Kann man mir mitteilen wo das Problem liegen kann?
Normalerweise deuten die Fragezeichen darauf hin, dass die Übertragungsraten im Programm (festgelegt mit “Serial.begin(…);”) und die Baudrate im seriellen Monitor nicht übereinstimmen. Im seriellen Monitor ist das am unteren Rand die Dropddown-Liste (2. von rechts; siehe https://unsinnsbasis.de/wp-content/uploads/2019/03/esp32_Prozessortemperatur.jpg). Da muss der gleiche Wert ausgewählt werden, den man bei SerialBegin() im Program gesetzt hat. Bei den Arduinos sollten beide Angaben 9600 sein, mit den ESPs kann man auch höhere Werte nehmen (meist 115200).
Was mich etwas irritiert, ist allerdings, dass zu Beginn noch Klartext ausgegeben wird – normalerweise kommen nur die Fragezeichen. Das kann aber passieren, wenn man zwischendurch die Übertragungsrate im seriellen Monitor auf einen nicht passenden Wert ändert.
Guten Tag,
Bis jetzt lief der Sensor zwei Tage am stück ohne ein Problem mit nachvollziehbaren Werten.
Nun gibt es sobald ich ihn anstecke immer “PPM CO2: 40” aus.
Sobald ich den Anschluss entferne gibt es “PPM CO2: -30419” aus.
Es entstehen NUR diese beiden Werte.
Gibt es dazu eine logische Erklärung & Änderungsmöglichkeiten?
Am Code & an der Verkabelung wurde eigentlich garnichts geändert, bis jetzt liefs immer.
Hmmmm… 😉
Erstmal der einfache Teil: Dass beim Entfernen des Sensors “irgendetwas” angezeigt wird, ist normal. Die Spannung an einem offenen Pin ist wegen des Einflusses von Störsignalen aus der Umgebung nicht vorhersagbar, man sollte sich nicht darauf verlassen, dass dort 0 Volt (oder überhaupt ein definierter Wert) “gemessen” wird. Siehe auch die Arduino-Referenz (https://www.arduino.cc/reference/de/language/functions/analog-io/analogread/ – ganz unten):
“Wenn der analoge Pin nicht verbunden ist, wird der zurückgegebene Wert schwanken basierend auf mehreren Faktoren (z.B. Die Werte der anderen analogen Pins, Wie nahe die Hand am Board ist, etc.).” Da würde ich also keinen sinnvollen Wert erwarten.
Warum bei angeschlossenem Sensor nur 40 angezeigt wird, kann ich leider nicht sagen. Generell würde ich bei laufendem ESP oder Arduino nichts an der Verkabelung ändern (z.B. Sensor anschließen oder entfernen), sondern vorher immer die Stromversorgung entfernen (USB-Kabel abziehen). Ich würde Folgendes versuchen, um den Fehler loszuwerden (aber das ist eher Standard nach der Methode “Versuch und Irrtum”, also ausprobieren, ob es hilft):
– den Controller neu starten (also Stromversorgung/USB-Kabel trennen und neu verbinden)
– alle Jumper-Kabel prüfen (bzw. abziehen und neu stecken; bei ausgeschaltetem Controller) – manchmal sind die Verbindungen auf dem Breadboard ja etwas wackelig
– das Programm nochmal neu in der IDE übersetzen und hochladen
– vielleicht auch einen anderen Pin (z.B. 25 oder 33…) am Controller zum Lesen des Sensor-Wertes ausprobieren (was ja ebenfalls bedeutet, das Programm mit der geänderten Pin-Angabe neu zu übersetzen und hochzuladen)
Das sind alles eher allgemeine Ideen als etwas, was ganz konkret zu dem beschriebenen Fehler passt. Falls mir besseres einfällt, melde ich mich natürlich noch einmal.
Hallo nochmal,
das Thema hat mich noch weiter beschäftigt und ich bin auf folgenden Beitrag gestoßen, der sich auch dem MH-Z19B befasst: http://steinlaus.de/stinkt-das-hier-teil-2-mit-dem-winsen-mh-z19b/
Dort wird darauf hingewiesen, dass sich der Sensor einmal am Tag kalibriert, und zwar auf den niedrigsten in der Zeit gemessenen CO2-Wert. Diesen Wert sieht der Sensor dann bis zur nächsten Kalibrierung als Basis für saubere Luft (also 400 ppm CO2) an. War der tatsächliche Wert aber höher (weil der Sensor “nicht an die frische Luft gekommen ist”), ist diese Kalibrierung natürlich falsch. Misst der Sensor danach in besserer Luft, werden so Werte unter 400 ermittelt. Ausführlich ist das unter obigem Link beschrieben.
Ich werde in nächster Zeit mit meinem Sensor versuchen, das zu prüfen. Komme ich zu einem ähnlichen Ergebnis, passe ich den Beitrag hier entsprechend an.
Außerdem gab es dort den Hinweis, dass wohl auch nachgebaute (gefälschte?) Sensoren auf dem Markt sind, die sich mglw. nicht wie das Original verhalten. Sie haben auf der Unterseite eine schwarze Platine (bei meinem Sensor ist sie grün), und auf der Oberseite nicht die kreisrunde Erhebung, wie sie auf diesem Bild zu sehen ist: https://unsinnsbasis.de/co2-sensor-mhz19b/mhz19b_pins_4625_4672/ – siehe auch https://revspace.nl/MH-Z19B#Fake_MH-Z19B_.28black_PCB.29
Auch das werde ich versuchen, zu verifizieren.
Moin,
ein sehr guter, ausführlicher Beitrag. Leider bekomme ich beim Messen über PWM bei dem genauen Wert keinen plausiblen Werte raus. So habe ich für 30 min. den Sensor draußen auf den Balkon messen lassen. Folgende Werte kamen dabei heraus:
14:06:06.673 -> PPM CO2: 390 (einfach); -3 (korrekt); Rohwerte (HIGH, LOW): 80, 765
Dieser Wert war auch sehr konstant die ganze Messung über, was ich auch für sehr unrealistisch halte.
Irgendeine Idee, wie das zustande kommt. Ich bin mir sehr sicher, das alles richtig verschaltet sein müsste.
Gruß,
MM
Die Rohwerte sind mMn. tlw. unplausibel. In der Summe sollte ungefähr 1004 herauskommen: 1000 ms Signallänge für den Datenwert (ein Teil HIGH, ein Teil LOW) und je 2 ms für den HIGH-Pegel zu Beginn und Ende (natürlich mit gewissen Fehlertoleranzen; lt. Datenblatt +/- 5%), also irgendwo zwischen 954 und 1054 wäre plausibel. Die 80 Millisekunden für den HIGH-Pegel klingen sinnvoll – das wären bei einem Sensor mit einem Messbereich von 5000 ppm die 400 ppm, die man an der frischen Luft erwarten könnte; passt auch zu den 390 ppm als “einfacher” Messwert. Nur der gemessene LOW-Pegel ist viel zu kurz, der müsste etwa bei 920 ms liegen.
Mir fehlt im Moment aber leider eine Idee, was die Ursache sein könnte.
Im Datenblatt steht der Hinweis: “The module should be away from heat, and avoid direct sunlight or other heat radiation.” Könnte das die Ursache sein? Der Sensor sollte also möglichst im Schatten laufen und vllt. auch nicht auf einer Fläche stehen, die sich vorher in der Sonne stark erhitzt hat.
Egal wie, dürfte bei den genannten Messwerten aber nicht -3 als “korrekter” Wert herauskommen, sondern 464.
(pulse_high – 2) * RANGE / (pulse_high + pulse_low – 4) = (80 – 2) * 5000 / (80 + 765 – 4) = 78 * 5000 / 841 = 463,733…
Falls möglich, würde ich nach meinen Erfahrungen mit dem MH-Z19B empfehlen, die Messung per serieller Schnittstelle durchzuführen. Man vermeidet Ungenauigkeiten – der Sensor muss den digital gemessenen Wert nicht erst in das Pulssignal umwandeln, das der Mikrocontroller dann misst und wieder in ein digitales Signal zurückwandelt – und man hat auch die Kalibrierung unter Kontrolle. (Aber das hilft natürlich nicht bei der Beantwortung deiner Frage.)
Hallo Heiko,
erstmal Danke für deinen ausführlichen Beitrag!
@MM kann es sein, dass du den MH-Z19 C verwendest? Ich habe sehr ähnliche Werte erhalten und kann mir diese leider auch nicht erklären. Des Weiteren habe ich durch anhauchen des Sensors bei hohen CO2-Werten Signalinput von (993+9=1002 bis 691+967=1658).
@Heiko, kann hier der Fehler liegen, das evtl. der MH-Z19C ein längeres Signal produziert? Oder andere Unterschiede von Modell B zu C dafür verantwortlich sind?
Grüße Max
Moin Max,
ich habe nur den MH-Z19B, kann mich zum MH-Z19C also nur auf das Datenblatt beziehen (https://www.winsen-sensor.com/d/files/infrared-gas-sensor/mh-z19c-pins-type-co2-manual-ver1_0.pdf; das zum MH-Z19B ist im Beitrag unten verlinkt). Danach unterscheiden sich MH-Z19B und MH-Z19C nur geringfügig (vor allem bei der Stromaufnahme und Vorwärmzeit). Insbes. die Dauer eines Signalzyklus ist bei beiden gleich und sollte ca. 1004 ms dauern:
– 2 Millisekunden HIGH
– x ms zur Übermittlung des Messwerts (HIGH)
– (1000 – x) ms LOW
– 2 ms LOW
In beiden Datenblättern findet sich dazu auch ein identisches Diagramm mit Beispielen für den Messbereich bis 2000ppm.
Ist es dir möglich, zum Auslesen der Messungen statt PWM die serielle Schnittstelle zu nutzen? Kannst du das Verhalten dann reproduzieren?
(Im Datenblatt zum MH-Z19C gibt es nur wenig Informationen zu den Steuerkommandos über die serielle Schnittstelle; gar nichts zur Firmware-Version. Zumindest beim Auslesen der Sensorwerte (Code 0x86) hat sich aber nichts getan. Da sich das Datenblatt auch nicht zu Änderungen äußert, gehe ich davon aus, dass das Protokoll insgesamt nicht geändert wurde. Deshalb müssten auch die im Beitrag genannten Bibliotheken, die die serielle Schnittstelle nutzen, mit dem MH-Z19C funktionieren.)
Eine Erklärung dafür kann ich nicht bieten. Auch beim Stöbern in verschiedenen Foren (z.B. mikrocontroller.net, forum.arduino.cc oder arduinoforum.de) im Lauf der Zeit bin ich über nichts Entsprechendes gestolpert.
Viele Grüße, Heiko
Servus Heiko,
endlich mal ein Beitrag, den man auf Anhieb versteht!
Vielen Dank für deine Arbeit, du hast mir sehr geholfen.
Grüße commodore
Danke für deinen Beitrag und die Zeit dafür.
Vielleicht kannst du mir eine Frage beantworten bzw. zwei Fragen.
1) Kann man den HD Pin nach den 7 Sekunden wieder auf HIGH setzen? Wird also der neue “Zero Point” Wert innerhalb der 7 Sekunden gesetzt wenn man den HD Pin auf LOW setzt?
2) s. Frage 1. Wird der neue “Zero Point” Wert, bzw. der neue Kalibrierungswert im Handmodus denn in ein EEPROM gespeichert, so dass jener nach Neustart der Versorgungsspannung noch zur Verfügung steht oder ist nach Neustart des Sensors wieder der Automatik Modus für die Kalibrierung eingestellt?
Danke und LG
Markus
Zu Frage 1) Davon gehe ich aus. Im Datenblatt heißt es “Connect module’s HD pin to low level(0V), lasting for 7 seconds at least. Before calibrating the zero point, please ensure that the sensor is stable for more than 20 minutes at 400ppm ambient environment.”
Das verstehe ich so, dass man mindestens für 7 Sekunden die Verbindung mit Masse sicherstellt und sie dann wieder trennen kann. Im Zweifelsfall: ausprobieren.
Zu Frage 2) Ich gehe davon aus, dass das Durchführen der Kalibrierung alleine den Automatik-Modus nicht ausschaltet – egal wie man es macht. Das Ausschalten der automatischen Kalibrierung erfolgt wie oben beschrieben über das Steuerkommando 0x79 bzw. mit dem Befehl autoCalibration() aus der von mir genutzten Bibliothek (siehe oben https://unsinnsbasis.de/co2-sensor-mhz19b/#tgt-lib); das würde ich auch in dem Fall an den Sensor senden, wenn die Kalibrierung nicht per Steuerkommando erfolgt, sondern hardwaremäßig.
Vielen Dank für deine extrem guten Artikel und die erstklassige Arbeit.
Super Artikel! Sehr gut erklärt und strukturiert danke für so eine gute Ausarbeitung!
Was wurde hier für eine Library für den C02 verwendet? Auf Github finde ich die Header Datei nicht. Danke BG
Ich habe die MH-Z19-Library von Jonathan Dempsey verwendet: https://github.com/WifWaf/MH-Z19
Das ZIP-Archiv kann man über folgenden Link herunterladen: https://github.com/WifWaf/MH-Z19/archive/refs/heads/master.zip
Die Header-Datei findet man dann im Verzeichnis “src”; online hier: https://github.com/WifWaf/MH-Z19/blob/master/src/MHZ19.h
Danke !
Super geschrieben – Danke!
Eine Frage zur Kalibrierung: Wenn ich das Modul z. B. auf dem Balkon für 20-30min kalibriere, dann schließe ich da eine powerbank an. Werden die Kalibrierungsdaten irgendwo gespeichert? Oder anders gefragt: Wenn ich dann den Sensor wieder mit in die Wohnung nehme, dann möchte ich ihn von der Powerbank nehmen und an ein normales Netzteil anschließen. Sind die Kalibrierungsdaten nach dem Aus- und wieder Einschalten weiterhin gültig?
Das würde mich auch sehr interessieren. Ich habe diesen Bausatz mit dem MH-Z19 E im Einsatz:
https://www.stall.biz/project/airsniffer-co2-die-co2-ampel-fuer-wohnraeume
Da ich auch die Spannungsversorgung via USB mache, würde ich die Module am liebsten auf dem Balkon kalibrieren, wozu ich sie aber jedes mal vom Strom trennen muss.
Aktuell läuft es bei mir im Testbetrieb und liefert recht parallele Werte zur Netatmo Sonde, die direkt daneben steht. Zwischenzeitlich fiel der MH-Z19 plötzlich ab und lieferte gut 200ppm niedrigere Werte. Daraufhin habe ich beide Sonden gestern Morgen bei offenem Fenster kalibriert. Seit dem sind sie wieder im Bereich 30ppm parallel. Mal schauen…
Im MH-Z19B arbeitet ein Mikroprozessor vom Typ STM52 (siehe https://revspace.nl/MH-Z19B#Hardware). Der verfügt über eigenen Flash-Speicher.
Ich vermute (ohne einen Beleg dafür gefunden zu haben), dass ein kleiner Teil des Speichers zum Ablegen der Kalibrierungsdaten genutzt wird. Aus meiner Erinnerung heraus (ich habe den Sensor im Moment nicht im Zugriff) habe ich bei meinen Messungen die aufgebaute Schaltung immer wieder von der Spannung getrennt, ohne dass ich den Sensor jedesmal neu kalibrieren musste.
Ich würde es einfach ausprobieren und den Sensor in einem Innenraum, in dem sich nicht allzu viel an den Bedingungen ändert – also nicht gerade beim Lüften oder mit geöffneter Tür – für einige Zeit (z.B. ein paar Minuten) von der Spannung trennen. Wenn er nach dem neuem Anschließen ähnliche Werte wie vorher liefert, wird er seine Kalibrierungsdaten nicht vergessen haben.
Noch einfacher ist das, wenn man einen anderen Sensor zum Vergleich hat – da sollten die Abweichungen der Messwerte beider Sensoren sich nach dem erneuten Anschließen im gleichen Rahmen bewegen wie vorher.
Datenblatt und Website des Herstellers machen keine Angaben zur internen Funktionsweise, insofern kommt man ums Vermuten und Ausprobieren wohl nicht drumrum.
Servus Heiko,
Vielen danke für diesen ausführlichen und einfach geschriebenen Artikel. Wirklich sehr gut gemacht. Weiter so!
LG,
Philipp
Hallo Heiko,
danke für den hervorragenden Artikel, der mir den Einstieg in die Messungen mit dem Sensor sehr erleichtert hat.
Eine Anmerkung habe ich zu dem von dir aus der Dokumentation zitierten Satz, dass man den Sensor mindestens alle 6 Monate per Software oder manuell kalibrieren sollte, wenn man ihn im ABC-Status OFF (ausgeschaltete Auto-Kalibrierung) betreiben will.
Zunächst habe ich den Sensor per Software an der Außenluft kalibriert (das gab plausible CO2-Werte knapp über 400 ppm). Die von mir benutzte Bibliothek war MHZ19.h. Nach Hochladen des Messprogramms (mit “myMHZ19.autoCalibration(false);” im setup-Teil) startete ich die Messreihe einige Minuten später. Es ergaben sich CO2-Werte von 550 ppm (auch plausibel). Dann fielen die Messwerte (mit plausiblen Schwankungen z.B. beim Lüften) linear ab. Nach 26 Stunden wurden erstmals ppm-Werte von 400 unterschritten, die denen der Außenluft entsprechen (sehr unplausibel). Das Minimum betrug etwa 250 ppm und wurde nach 37 Stunden erreicht. Bis zum Ende der Messungen (nach 43 Stunden) überschritten die CO2-Messwerte die 370 ppm-Schwelle nicht mehr. Um sicher zu stellen, dass der ABC-Status während der gesamten Messreich auf OFF stand, habe ich ihn zusätzlich aufgezeichnet: Er war bei jeder Messung OFF. – Leider konnte ich meine Grafik hier nicht hochladen. Oder geht das?
Es sieht für mich so aus, als ob die Messwerte linear abfallen, in sich aber plausibel sind (Lüften, Aufenthalt von mehreren Personen im Raum usw.).
Da ich für meine Projekte einen konstant arbeitenden Sensor benötige (gleiche CO2-Konzentrationen zu verschiedenen Zeiten oder an verschiedenen Orten müssen zu gleichen Messwerten führen), steht der Einsatz des MH-Z19b für mich in Frage.
Hat jemand ähnliche Erfahrungen und weiß, wie man das oben beschriebene Problem löst?