Soda, hier geht es also um die CO2 Messung mit dem Arduino und dem MH-Z19.
Warum CO2 Messen?
Gestellt hat sich die Frage, wie wir unser neues Haus geplant haben. Wir standen vor der Entscheidung, ob wir eine zusätzliche Lüftung einplanen sollen oder nicht. Bei der Recherche zu diesem Thema stieß ich auf die CO2 Thematik. CO2 kann recht gut als Maß verwendet werden, wie verbraucht die Raumluft ist. Das ist doch interessant, oder? Aber wie hoch sind die CO2 Werte eigentlich derzeit im alten Haus? (ohne Lüftung aber mit „undichten“ Fenstern) – keine Ahnung! Also Messen.
Welchen Sensor nehmen?
Messprinzip bei diesen Sensoren ist NDIR (= „nondispersive infrared“). Dabei wird ausgenutzt, dass bestimmte IR-Wellenlängen (4.26 µm) von CO2 absorbiert werden. Für die Messung braucht man eine IR Quelle, einen Filter, der nur die relevanten Wellenlängen durchlässt, ein „Stück Luft“ mit CO2 drinnen, das mehr oder wenige absorbiert und einen Detektor, der die durchgelassene IR-Strahlung misst. Je mehr CO2 in der Luft ist, desto weniger misst der Detektor. Gut wäre auch noch ein Referenzdetektor, der die IR-Strahlung direkt misst (damit Schwankungen in der Quelle herausgerechnet werden können). Dann brauchen wir noch ein wenig Mathematik und haben ein CO2-Signal.
Das alles gibt es fix fertig als CO2-Detektor zu kaufen! Brauchen wir also nur einen Sensor kaufen, den mit dem Arduino auslesen und dann können wir die Daten verarbeiten wie wir wollen!
So einfach?
Na schauen wir mal.
Die Sensoren die es da gibt sind zum Teil nicht ganz billig. Da ich aber später gerne dauernd in mehreren Räumen messen möchte sollte die Lösung nicht allzu teuer sein. Nach einiger Recherche bin ich auf den MH-Z19 von Winsen gestoßen (Die Seite des Herstellers mit dem entsprechenden Datenblatt ist hier).
Die Daten des Sensor schauen bei erster Betrachtung recht brauchbar aus und die Kosten für den Sensor liegen im Internet bei ca. € 20. Nicht extrem billig, aber auch nicht arg teuer.
Bei genauerer Betrachtung fällt auf, dass die Informationen im Datenblatt ein wenig dürftig sind. Es wird also ein wenig „Reverse-Engineering“ gefragt sein, aber das ist ja auch genau die Richtige Aufgabe für den DIYDonkey!!! Eine wenig Reverse-Engineering wurde auch schon hier und hier durchgeführt (letzteres ist in russisch – aber Google Translate hilft etwas). Aber so ganz verstanden scheinen die Funktion noch nicht zu sein. Es bleiben also noch Aufgaben für mich.
Für den Anfang habe ich mir mal 2 Stück von diesen Sensoren bestellt, die auch gestern (05.04.2017) gekommen sind.
Also frisch ans Werk!
Als erstes habe ich mir mal einen kleinen Testaufbau auf einer Lochrasterplatine gelötet, mit der ich beide Sensoren mit einem Arduino Mega auslesen kann. Für den Anfang verwende ich mal nur die serielle Schnittstelle (UART), d.h. man muß die Sensoren nur an 5V, GND anschließen und TXD vom Sensor mit RX am Arduine und RXD mit TX am Arduino verbinden.

UART funktioniert am Sensor mit 9600 Baud, 8 Datenbits, 1 Stopbit und ohne Parity Check (im Datenblatt sind hier wohl ein wenig die Bits und Bytes durcheinander gekommen – aber da das Stanardeinstellungen sind, ist klar was gemeint ist).
Ein Befehl für diesen Sensor besteht aus 9 Byte. Zuerst kommt ein Startbyte 0xFF dann die Nummer des Sensors, die standardmäßig 0x01 ist. Das dritte Byte enthält den Befehl, dann 5 Bytes für Parameter und ein Byte Prüfsumme.
Der Befehl um die CO2 Gaskonzentration auszulesen ist 0x86. Der Ganze Befehl sieht also so aus:
Byte0 |
Byte1 | Byte2 | Byte3 | Byte4 | Byte5 | Byte6 | Byte7 | Byte8 |
Startbyte | Sensornummer | Befehl | Parameter | Prüfsumme | ||||
0xFF |
0x01 | 0x86 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 |
0x79 |
Der Sensor antwortet dann mit neun Bytes:
Byte0 |
Byte1 | Byte2 | Byte3 | Byte4 | Byte5 | Byte6 | Byte7 | Byte8 |
Startbyte | Befehl | CO2-high | CO2-low | Temp | Status | Min-high | Min-low | Prüfsumme |
Das Byte0 ist hier wieder das Starbyte 0xFF gefolgt von der Wiederholung das Befehls 0x85. Byte2 und Byte3 enthalten den aktuellen Co2 Wert. D.h. der aktuell gemessene CO2 Wert berechnet sich aus (CO2-high*256+CO2-low).
Die Bytes 4-7 sind im Datenblatt nicht dokumentiert. In weiser Voraussicht habe ich hier mal die Namen Temp, Status, Min-high und Min-low vergeben. Was es damit für eine Bewandtnis hat, werden wir später sehen. Der Wert Temp enthält die vom Sensor gemessene Temperatur plus 40°C. Um die wahre Temperatur zu ermitteln müssen wir also (Byte4-40) rechnen (das haben schon viele herausgefunden – siehe die beiden Links oben).
Jetzt brauchen wir noch ein kleines Programm für den Arduino, der die Werte der beiden Sensoren ausließt und an den Computer weitergibt. Ich habe das mal aus ausgeführt:
/* Programm zum Auslesen der CO2-Konzentration aus 2 Sensoren MH-Z19 an einem Arduino Mega * Die Sensoren befinden sich an den seriellen Schnittstellen 1 und 2. * Die Werte werden dann an im Klartext an den Computer übergeben. * Geschrieben von: Norbert Huber * Datum: 08.04.2017 */ byte com[]={0xff,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79}; //Befehl zum Auslesen der CO2 Konzentration byte return1[9]; //hier kommt der zurückgegeben Wert des ersten Sensors rein int concentration1; //CO2-Konzentration des ersten Sensors int minimum1; //Minimum-Wert des ersten Sensors int temp1; //Temperatur-Wert des ersten Sensors byte status1; //Status des ersten Sensors byte return2[9]; int concentration2; int minimum2; int temp2; byte status2; void setup() { Serial.begin(9600); //Initialisierung Serielle Schnittstelle zum Computer Serial1.begin(9600); //Initialisierung der seriellen Schnittstelle für den ersten Sensor Serial2.begin(9600); //Initialisierung der seriellen Schnittstelle für den zweiten Sensor } void loop() { while (Serial1.available() >0) { //Hier wird der Buffer der Seriellen Schnittstelle gelehrt Serial1.read(); } while (Serial2.available() >0) { Serial2.read(); } //Sensor 1: Serial1.write(com,9); //Befehl zum Auslesen der CO2 Konzentration Serial1.readBytes(return1,9); //Auslesen der Antwort concentration1 = return1[2]*256+return1[3]; //Berechnung der Konzentration temp1 = return1[4]-40; //Berechnung der Temperatur minimum1 = return1[6]*256+return1[7]; //Berechnung des Minimums status1 = return1[5]; //Berechnung des Status Serial.print("Zeit = "); //Senden der Werte an den PC Serial.print(millis()); //Am Angfang wird die Zeit in ms gesendet, die der Arduino in Betrieb ist Serial.print(" ; Sensor 1: CO2 = "); Serial.print(concentration1); Serial.print(" ; Temp = "); Serial.print(temp1); Serial.print(" ; Minimum = "); Serial.print(minimum1); Serial.print(" ; Status = "); Serial.print(status1); //Sensor2: siehe oben Serial2.write(com,9); Serial2.readBytes(return2,9); concentration2 = return2[2]*256+return2[3]; temp2 = return2[4]-40; minimum2 = return2[6]*256+return2[7]; status2=return2[5]; Serial.print(" ; Sensor 2: CO2 = "); Serial.print(concentration2); Serial.print(" ; Temp = "); Serial.print(temp2); Serial.print(" ; Minimum = "); Serial.print(minimum2); Serial.print(" ; Status = "); Serial.println(status2); delay(10000); //10s warten }
Das Programm auf den Arduino hochladen und schon werden die CO2-Werte gemessen und angezeigt. Bei mir hat das auf Anhieb funktioniert.
Die nicht dokumentierten Werte sehen wir jetzt auch – aber mit denen beschäftigen wir uns im nächsten Artikel – da geht es dann mit dem Reverse-Engineering richtig los!
Wenn es euch das interessiert, dann müsst ihr etwas Geduld haben.
Ich hoffe, der Artikel hat euch gefallen, und vielleicht auch ein wenig geholfen. Über einen Kommentar würde ich mich freuen.
Liebe Grüße
Euer DIYDonkey
Hi, think you for this article. But I have a problem, I tried 5 MH-Z19 sensors and I aloways get the output 0. I am using Arduino uno R3 with 3.3V (when I use 5V the arduino disconnect) and A0 branched to RX and A1 to TX.
Thank you
Normally the sensor should be powered with 5V – it reduces this itself to 3.3 V.
If you are using an Uno you have to change the program, because the Uno has no additional serial interface – you have to use something like software serial – did you do this?
thank you for your response. Yes I changed the code but always I get 0. for the 5V I don’t know why when I powered my arduino with 5V it disconnect it is only working with 3.3V. This is my code I tried to use the PWM and UART methods.
#include
#define pwmPin 10
SoftwareSerial Serial1(A0, A1); // TX, RX
unsigned long th, tl,ppm, ppm2, ppm3 = 0.0;
byte com[]={0xff,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79}; //Befehl zum Auslesen der CO2 Konzentration
byte return1[9]; //hier kommt der zurückgegeben Wert des ersten Sensors rein
int concentration; //CO2-Konzentration des ersten Sensors
int minimum; //Minimum-Wert des ersten Sensors
int temp; //Temperatur-Wert des ersten Sensors
byte status; //Status des ersten Sensors
void setup() {
Serial.begin(9600); //Initialisierung Serielle Schnittstelle zum Computer
Serial1.begin(9600); //Initialisierung der seriellen Schnittstelle für den ersten Sensor
pinMode(pwmPin, INPUT);
}
void loop() {
while (Serial1.available() >0) { //Hier wird der Buffer der Seriellen Schnittstelle gelehrt
Serial1.read();
}
//Sensor 1:
Serial1.write(com,9); //Befehl zum Auslesen der CO2 Konzentration
Serial1.readBytes(return1,9); //Auslesen der Antwort
concentration = return1[2]*256+return1[3]; //Berechnung der Konzentration
temp= return1[4]-40; //Berechnung der Temperatur
minimum = return1[6]*256+return1[7]; //Berechnung des Minimums
status = return1[5]; //Berechnung des Status
Serial.print(„Zeit = „); //Senden der Werte an den PC
Serial.println(millis()); //Am Angfang wird die Zeit in ms gesendet, die der Arduino in Betrieb ist
Serial.print(“ ; Sensor : CO2 = „);
Serial.println(concentration);
Serial.print(“ ; Temp = „);
Serial.println(temp);
Serial.print(“ ; Minimum = „);
Serial.println(minimum);
Serial.print(“ ; Status = „);
Serial.println(status);
//Sensor2: siehe oben
delay(10000); //10s warten
}
Hi, great article!
One question – did you connect the RX/TX directly to the Arduino (5v)? Just wonder because according to the datasheet the interface voltage is 3.3v so I wonder if you used a level shifter.
Thank you!
Thanks for the compliment.
Yes I connected the RX/TX directly to the Arduino. With some 3.3 V devices this can cause problems but not with the MH-Z19 (in the User-Manual from 2016-01-21 – it is stated that the levels are 3.3 V but compatible to 5V – this means the can withstand also the 5V – wich is not the case for all 3.3 V devices!)
Hi, thank you very much for the quick response!
Cheers!
Hallo,
ich möchte den MHZ-19 Co2 Sensor gerne mit dem LoLin Nodemcu1.0 (ESP-12E Modul) betreiben.
Wo müssen die TX/RX Verbindungen angeschlosen werden ?
Ich bekomme zur Zeit nur den CO2 Wert 0 und Temperatur Wert -40 zurück
Würdest du den Sensor empfehlen (natürlich in dieser Preisklasse, schon klar)? Sind die Messwerte plausibel?
Hallo Stefan,
Gute Frage: Ja, ich denke, ich würde den Sensor empfehlen – in der Preisklasse gibt es sonst nicht viel anderes – und die Werte sind halbwegs plausibel. Besonders genau ist er sicher nicht, liefert aber einen guten Überblick. Allerdings zeigt er im Spezialfall ein seltsames Verhalten, er versucht nämlich einmal pro Tag einen „Nullpunkt“ zu finden – wenn das nicht gelingt, dann mißt er Hausnummern (ich wollte das im Blog genauer beschreiben, aber ich bin leider zu sehr mit anderen Kleinigkeiten (Hausbau) beschäftigt) – das sollte aber im Normalfall kein Problem sein.