MH-Z14 und MH-Z19 CO2 Sensoren

Über den Beitrag

In diesem Beitrag möchte ich mich mit CO2 Sensoren zur Überwachung der Raumluftqualität beschäftigen. Die meisten Sensoren dieser Art arbeiten entweder auf der Basis von Metalloxiden (MOx) oder mit Infrarottechnik (NDIR). Ich beschränke mich hier auf Letztere, und zwar die CO2 Sensoren der MH-Z-Reihe von Zhengzhou Winsen Electronics Technology.

Im Einzelnen behandele ich die folgenden Themen:

  • Das Messprinzip (NDIR)
  • Technische Eigenschaften der Sensoren
  • Kalibrierung
  • Messwertausgabe (analog, seriell, PWM)
  • Ansteuerung mit und ohne Bibliothek

Zum Schluss überprüfe ich dann noch die Messgenauigkeit der Sensoren. Dazu verwende ich eine selbst gebaute CO2 Messkammer und das CO2 Messgerät Technoline WL 1030. 

Vertreter der MH-Z14 und MH-Z19 CO2 Sensoren
Vertreter der MH-Z14 und MH-Z19 Reihe

NDIR Messprinzip der CO2 Sensoren

Nicht-dispersive Infrarotsensoren (NDIR) messen den CO2 Gehalt der Luft über die Absorption von infrarotem Licht. Dass CO2 infrarotes Licht absorbiert, kennt ihr vom Treibhauseffekt. Das Sonnenlicht dringt relativ ungehindert durch die Atmosphäre und das in ihr enthaltene CO2. Die aufgeheizte Erdoberfläche strahlt die Energie zum Teil in Form von infrarotem Licht, also Wärmestrahlung wieder ab. Nur leider wird diese Strahlung vom CO2 absorbiert, was dann zur Erwärmung der Atmosphäre führt.

Im NDIR Sensor gibt es eine IR Lampe und einen Detektor. Dazwischen befindet sich die zu messende Luft. Je mehr CO2 sie enthält, desto mehr IR Strahlung wird absorbiert und entsprechend weniger kommt am Detektor an. „Nicht-dispersiv“ heißt, dass man sich im Gegensatz zur Infrarotspektroskopie nicht die Mühe macht, das infrarote Licht in kleine Wellenlängenbereiche aufzuteilen. Eine gute Erklärung des Messprinzips findet ihr hier.

Ich habe einen MH-Z19 (ein Fake-Modell, dass leider nicht funktionierte) „seziert“. Die Fotos seht ihr unten. Die weißen, filzartigen Abdeckungen sorgen dafür, dass die CO2-haltige Luft eindringen kann, der Staub aber draußen bleibt. Entfernt sie also nicht. Unter der oberen Kappe befindet sich die Messkammer. Ihr seht die IR Lampe und den Sensor. Die IR Lampe ist so eingefasst, dass sie nur in eine Richtung abstrahlt. Die Strahlung wird an der gegenüber liegenden Gehäusewand reflektiert und dann über einen Spiegel auf den Detektor gelenkt. Durch den Umweg legt das IR Licht eine größere Strecke zurück. Dadurch wird mehr IR Licht absorbiert und das bewirkt eine höhere Empfindlichkeit. Unter der Messkammer befindet sich dann die Elektronik.

Innenansicht eines MH-Z19 (allerdings ein Fake Modell)

Technische Eigenschaften der MH-Z CO2 Sensoren

Die MH-Z14 Sensoren sind wesentlich größer als die Vertreter der MH-Z19 Reihe. Ansonsten sind sie hinsichtlich ihrer technischen Daten ziemlich ähnlich. Für einen MH-Z19 müsst ihr mit 20 bis 30 Euro rechnen, für den MH-Z14 ca. 10 Euro mehr. Wenn ihr ein wenig Zeit habt, dann empfehle ich einen Kauf bei AliExpress, denn da ist es deutlich günstiger.

Die folgenden Angaben habe ich den Datenblättern entnommen:

Übersicht der MH-Zxx CO2 Sensoren Familie (bitte Anmerkungen unten beachten!)

Immerhin gibt es Datenblätter, das ist ja nicht immer der Fall bei chinesischen Modulen. Eigentümlicherweise werden aber einige relevante Informationen darin unterschlagen. Es ist unter anderem nicht wirklich klar, welche Messbereiche wirklich eingestellt werden können. So konnte ich beim MH-Z14 nur den voreingestellten Bereich von 0 – 5000 ppm nutzen. Hingegen war die Einstellung des Messbereiches beim MH-Z19C kein Problem. In beiden Datenblättern wiederum ist der serielle Befehl für die Einstellung des Messbereiches nicht dokumentiert.

Zur Raumluftüberwachung braucht ihr eigentlich nur den Bereich bis 2000 ppm, da man bis zu diesem Wert längst gelüftet haben solltet. Allgemein übliche Bewertungen der Raumluft bezüglich der CO2 Konzentration sind:

CO2 Konzentration vs. Raumluftqualität

Einen ausgezeichneten Beitrag über den MH-Z19B habe ich hier gefunden. Ihr findet dort weitere Informationen über die Auswahl von Messwertbereichen, Kalibriermethoden, Fake-Modelle (Vorsicht, wenn die Platine schwarz und nicht grün ist!) und anderes.

Pinout

MH-Z14 – Reihe

Anschlüsse des MH-Z14
Anschlüsse des MH-Z14

Die MH-Z14 Sensoren geizen nicht mit Anschlüssen, aber die meisten von ihnen sind doppelt oder dreifach vorhanden. Die Anschlussleiste des MH-Z14B sieht ein wenig unterschiedlich aus, die Bezeichnungen und Funktionen sind aber dieselben. (siehe Tabelle).

MH-Z19 – Reihe

Anschlüsse des MH-Z19
Anschlüsse des MH-Z19

Die Anschlüsse innerhalb der MH-Z19 Reihe sind gleich. Manchmal ist der analoge Ausgang nicht beschriftet.

Pins der MH-Z CO2 Sensoren
Pins der MH-Z CO2 Sensoren (* V0 ist nicht unbedingt dokumentiert)

Kalibrierung der MH-Zxx CO2 Sensoren

Es gibt drei Kalibriermethoden für die MH-Zxx Sensoren:

  1. Nullpunktkalibrierung: dazu muss der Sensor 20 Minuten in einer gut gelüfteten Umgebung stehen. Der Sensor nimmt an, dass die CO2 Konzentration unter diesen Bedingungen bei 400 ppm liegt, was ungefähr dem aktuellen, globalen Mittel entspricht. Es gibt zwei Methoden, diese Art der Kalibrierung zu starten:
    • der HD Pin wird für mehr als 7 Sekunden mit GND verbunden
    • über eine serielle Anweisung (dazu kommen wir noch)
  2. Span Point Kalibrierung: Kalibrierung bei einer bestimmten CO2 Konzentration – das werden wohl die wenigsten realisieren können. Ich gehe deswegen auch nicht darauf ein und rate euch: spielt besser nicht damit herum.
  3. Hintergrundkalibrierung (Self-Calibration): Die Module können sich in einem 24-Stunden-Rhythmus selbst kalibrieren (beim MH-Z14 ohne Suffix bin ich mir diesbezüglich aber nicht sicher!). Sie nehmen, vereinfacht ausgedrückt, den über den Tag ermittelten, niedrigsten Wert als 400 ppm an. Das funktioniert natürlich nur dann, wenn das Gerät in einem Raum steht, der täglich effektiv gelüftet wird. Wenn das nicht der Fall ist, dann verschiebt sich euer Nullpunkt. Diese Kalibrierung ist voreingestellt aktiv. Über eine serielle Anweisung könnt ihr sie abschalten. Dann solltet ihr aber von Zeit zu Zeit eine manuelle Kalibrierung durchführen.

Ein Fehler bei den Kalibriermethoden 1 und 3 führt zu einem Offset der Messwerte. Ich komme darauf später zurück.

Messwertausgabe

Intern nehmen die MH-Z Sensoren ca. im Sekundentakt einen Messwert auf. Ihr erkennt das am regelmäßigen Leuchten. Auslesen könnt ihr die Messwerte der MH-Zxx CO2 Sensoren auf drei verschiedenen Weisen:

  • Berechnung aus dem analogen Spannungssignal
  • Über das PWM (Pulsweitenmodulation) Signal
  • Seriell (dazu kommen wir bei der seriellen Kommunikation)

Analoges Signal

Das analoge Signal ist ein Spannungswert V zwischen 0.4 und 2.0 Volt. Das bedeutet, dass der ausgewählte Messbereich (range) in diesem Spannungsbereich von 1.6 Volt abgebildet wird:

\text{CO2}\;[\text{ppm}] = \frac{(V -0.4)\cdot range}{1.6}

Diese Methode kann ich nicht empfehlen. Bei mir zeigte sie größere Abweichungen. Mit einem guten A/D Wandler wie dem ADS1115 mag es vielleicht besser gehen.

PWM Signal

Das Signal am PWM Pin hat eine Länge von 1004 Millisekunden (+/- 5% gem. Datenblatt). Je höher die CO2-Konzentration, desto länger das HIGH Signal TH gegenüber dem LOW Signal TL. Als weiterer Parameter muss der Messbereich mit einbezogen werden:

\text{CO2}\;[\text{ppm}] =\frac{T_H-2\text{ms}}{T_H+T_L-4\text{ms}}\cdot range

Für den Messbereich 0-2000 ppm sieht das folgendermaßen aus:

PWM Signal in Abhängigkeit von der CO2 Konzentration (Range 0...2000 ppm)
PWM Signal in Abhängigkeit von der CO2 Konzentration (Range 0…2000 ppm)

Ihr könnt das PWM Signal mit der pulseIn() Funktion auslesen.

Serielle Kommunikation

Serielle Befehle und Antworten der MH-Z CO2 Sensoren bestehen aus neun Bytes. Leider versteht nicht jedes MH-Zxx Modell jeden Befehl. Und leider sind Datenblätter diesbezüglich nicht verlässlich. An anderer Stelle habe ich gelesen, dass das nicht nur vom Modell, sondern auch von der Firmware Version abhängt.

Das Byte 0 ist grundsätzlich 0xFF. Das Byte 8 ist immer die Checksumme, die nach der folgenden Formel berechnet wird:

checksum = \text{0xFF}-\left( \sum_{i=1}^{7}{byte_i}\right) +1

Die Bytes 1 bis 7 werden zusammengezählt, dieser Wert von 255 abgezogen und dann 1 aufaddiert. Wenn die Summe der Bytes größer als 255 ist, dann wird der Überlauf einfach abgeschnitten.

Bei Befehlen ist das Byte 1 grundsätzlich 0x01. Das Byte 2 ist das eigentlich Steuerbyte. Der Wert der weiteren Bytes hängt von der Art des Befehls und den Parametern ab, die gegebenenfalls übergeben werden.

Die wichtigsten Einstellungen für die MH-Zxx CO2 Sensoren
Die wichtigsten Einstellungen für die MH-Zxx CO2 Sensoren

Für Byte 6 und Byte 7 gilt bei der Messbereichseinstellung:

LSB = range\; \&\; \text{0xFF} \;\;\;\; \text{und} \;\;\;\; MSB = range >>8

Das Byte 1 bei einer Antwort auf eine Messwertabfrage ist der Steuerbefehl 0x86. Das höherwertige Byte (MSB) des Messwertes ist Byte 2, das niederwertige Byte (LSB) ist Byte 3:

 \text{CO2 [ppm]} = MSB\cdot 256 + LSB =(MSB<<8) + LSB

Weitere serielle Einstellungen

Es gibt noch weitere, undokumentierte Befehle und Abfragen. Auch für diese gilt, dass die Anwendbarkeit vom Modell und der Firmware abhängig ist. Ihr findet eine gute Übersicht hier.

Ansteuerung der CO2 Sensoren mit dem Microcontroller

Verdrahtung

Die Verdrahtung ist für alle MH-Zxx CO2 Sensoren gleich:

  • VIN/GND: Anschluss an eine stabile Spannungsversorgung.
  • RX / TX: RX an TX und TX an RX, vorzugsweise über SoftwareSerial.
  • HD: falls ihr manuell kalibrieren wollt oder müsst, würde ich diesen Pin über einen Taster an GND verbinden oder mit einem I/O Pin verbinden. OUTPUT/LOW für >7 Sekunden startet die Kalibrierung.
  • PWM: kommt an einen beliebigen I/O Pin.
  • Analog Out: verbindet ihr mit einem analogen Eingang oder A/D-Wandler.

Ansteuerung mit Bibliothek

Für die MH-Zxx CO2 Sensoren stehen eine Reihe von Bibliotheken zur Auswahl. Allerdings ist die Auswahl für MH-Z14 Modelle kleiner. Zwei Bibliotheken habe ich ausprobiert.

Die Bibliothek MH-Z19 von Jonathan Dempsey wurde, wie ihr Name verrät, speziell für die MH-Z19 Sensoren geschrieben. Sie funktioniert sowohl mit der B- wie auch mit der C-Variante. Mein Versuch, damit auch den MH-Z14 anzusteuern, schlug fehl. Die MH-Z19 Bibliothek zeichnet sich dadurch aus, dass sie viele der versteckten Kommandos beherrscht, wie zum Beispiel die Abfrage der Range oder der Firmware Version. Dank der vielen Beispielsketche ist die Bedienung einfach. Ihr findet die Bibliothek hier auf Github oder ihr installiert sie direkt über die Bibliotheksverwaltung in der Arduino IDE.

Dann habe ich noch die Bibliothek MH-Z CO2 Sensors von Tobias Schürg ausprobiert. Auch sie ist sowohl auf GitHub (hier) wie auch in der Arduino Bibliotheksverwaltung zu finden. Die Bibliothek funktionierte sowohl mit dem MH-Z14 wie auch mit dem MH-Z19. Allerdings ging es bei mir erst nach einer kleinen Anpassung in MHZ.cpp. Wenn das bei  euch auch (noch) der Fall sein sollte, sucht dort nach der Zeile if (!isReady()) return STATUS_NOT_READY; und kommentiert sie heraus. Es ist im Moment Zeile 110. Ich hoffe, der Autor behebt das Problem. Es ist als Issue gemeldet. Für den MH-Z14 habe ich den Beispielsketch angepasst:

//#include <ESP8266WiFi.h>
#include <SoftwareSerial.h>
#include <MHZ.h>

// pin for pwm reading
#define CO2_IN 9

// pin for uart reading
#define MH_Z19_RX 10
#define MH_Z19_TX 11

MHZ co2(MH_Z19_RX, MH_Z19_TX, CO2_IN, MHZ14A);

void setup() {
  Serial.begin(9600);
  pinMode(CO2_IN, INPUT);
  delay(100);
  Serial.println("MHZ 14A");

  // enable debug to get addition information
  // co2.setDebug(true);

//  if (co2.isPreHeating()) {
//    Serial.print("Preheating");
//    while (co2.isPreHeating()) {
//      Serial.print(".");
//      delay(5000);
//    }
//    Serial.println();
//  }
}

void loop() {
  Serial.print("\n----- Time from start: ");
  Serial.print(millis() / 1000);
  Serial.println(" s");

  int ppm_uart = co2.readCO2UART();
  Serial.print("PPMuart: ");

  if (ppm_uart > 0) {
    Serial.print(ppm_uart);
  } else {
    Serial.print("n/a");
  }

  int ppm_pwm = co2.readCO2PWM();
  Serial.print(", PPMpwm: ");
  Serial.print(ppm_pwm);

  int temperature = co2.getLastTemperature();
  Serial.print(", Temperature: ");

  if (temperature > 0) {
    Serial.println(temperature);
  } else {
    Serial.println("n/a");
  }

  Serial.println("\n------------------------------");
  delay(5000);
}

 

Ansteuerung ohne Bibliothek

Für die Ansteuerung ohne Bibliothek habe ich einen eigenen Sketch geschrieben. Ich habe ihn mit einem MH-Z14 und einem MH-Z19C erfolgreich getestet. Ihr müsst allerdings selbst Sorge dafür tragen, dass der von euch gewählte Messbereich auch tatsächlich einstellbar ist. Ihr erkennt, dass ein Messbereich nicht einstellbar ist, wenn die Werte, die ihr über PWM und die serielle Abfrage erhaltet, signifikant unterschiedlich sind. Und – wie schon erwähnt – funktionieren nicht alle Befehle mit jedem Modell.

Bei der PWM Abfrage habe ich mich gegen die schlanke pulseIn() Methode entschieden, da sie nur die HIGH Phase bestimmt. HIGH und LOW Phase umfassen zusammen 1004 Millisekunden. Laut Datenblatt kann dieser Wert aber um bis zu 5 % variieren. Eine Bestimmung von HIGH und LOW Phase sollte diesen Fehler ausgleichen.

SoftwareSerial habe ich auf die Pins 10 und 11 gelegt. Den PWM Pin habe ich an Pin 9 gehängt und den HD Pin für die Kalibrierung auf Pin 8. Das Auslesen des analogen Signals erfolgt über A0.

getCO2AndTemp() bekommt seine Parameter als Referenzen und gibt keinen Wert zurück. Das ist eine Lösung für das Problem, dass C++ keine zwei Werte zurückgeben kann.

Die 9 Byte umfassenden Befehle werden stückweise „zusammengebaut“ und in dem Byte Array „cmd“ gespeichert. Die Antworten der MH-Zxx Sensoren werden im Byte Array „message“ gespeichert.

Ansonsten hoffe ich, dass der Code einigermaßen verständlich ist.

#define RANGE_2000  0x07D0
#define RANGE_5000  0x1388
#define RANGE_10000 0x2710
#define TEMP_CORR 38  // seems to differ from module to module!

#include <SoftwareSerial.h>
const int pwmPin = 9;
const int hdPin = 7;
unsigned int rangeFactor = 5000; //not every range works for every MH-Zxx!

SoftwareSerial myMHZ(10, 11); // RX, TX

void setup(){
  pinMode(hdPin, OUTPUT);
  digitalWrite(hdPin, HIGH);
  pinMode(pwmPin, INPUT);
  Serial.begin(9600);
  myMHZ.begin(9600);
  while(!Serial){}
  while(!myMHZ){}
    
  setRange(RANGE_2000); 
  Serial.print("Range: ");
  Serial.println(getRange()); // does not work with all MH-Zxx sensors
  Serial.print("Firmware: ");
  Serial.println(getFirmwareVersion()); // does not work with all MH-Zxx sensors
  
  // for calibration your MH-Zxx needs to run in a 400 ppm CO2 environment for 20 min!!!
  // if the MH-Z module supports it, you can calibrate via serial commands
  // if not, choose the HD - method)
  // calibrateMHZ(); // calibration via serial command
  // calibrateMHZByHD(); // calibration via HD pin
  // activate/deacvtivate the permanent calibration which is happening in the background (if supported):
  // activateCalibrationMode(true);
  
  Serial.print("Self Calibration Mode: ");
  if(getSelfCalModeStatus()){ // does not work with all MH-Zxx sensors
    Serial.println("ON"); 
  }
  else Serial.println("OFF");
}

void loop(){
  unsigned int ppmCO2PWM = 0;
  unsigned int ppmCO2Serial = 0;
  float ppmCO2Analog = 0.0;
  int temperature = 0;
   
  ppmCO2PWM = getCO2PWM();
  getCO2AndTemp(ppmCO2Serial, temperature);
  ppmCO2Analog = (((analogRead(A0))/1023.0 * 5.0) - 0.4)/1.6 * rangeFactor;
   
  Serial.print("CO2 in ppm (PWM): ");
  Serial.println(ppmCO2PWM);
  Serial.print("CO2 in ppm (Serial): ");
  Serial.println(ppmCO2Serial);
  Serial.print("CO2 in ppm (Analog): ");
  Serial.println(ppmCO2Analog);
  Serial.print("Temperature [°C]: ");
  Serial.println(temperature);
  
  Serial.println("**********************");
  delay(1000);
}

void setRange(unsigned int range){
  rangeFactor = range;
  sendCommand(0x99, range);
  delay(100);
}

void calibrateMHZ(){
  sendCommand(0x87, 0x00);
}

void calibrateMHZByHD(){
  Serial.println("Calibration - please wait for initialization");
  digitalWrite(hdPin, LOW);
  delay(10000);
  digitalWrite(hdPin, HIGH);
  Serial.println("Done");
}

void activateCalibrationMode(bool activate){
  if(activate){
    sendCommand(0x7A, 0x00); // my own command for activation
  }
  else{
    sendCommand(0x79, 0x00); 
  }
  delay(100);
}

unsigned int getRange(){
  byte message[9]; 
  getResponse(message, 0x9B);
  unsigned int range = (unsigned int)(message[4]<<8) + message[5];
  return range; 
}

void getCO2AndTemp(unsigned int &ppmCO2, int &temp){
  byte message[9];
  getResponse(message, 0x86);
  ppmCO2 = (unsigned int)(message[2]<<8) + message[3];
  temp = (int)(message[4] - TEMP_CORR);
}

String getFirmwareVersion(){
  byte message[9];
  String firmware = "";
  getResponse(message,0xA0);
  for(int i=2; i<6; i++){
    firmware += (char)message[i];
  }
  return firmware;
}

byte getSelfCalModeStatus(){
  byte message[9]; 
  getResponse(message, 0x7D);
  return message[7]; 
}

void sendCommand(byte cmdByte, unsigned int range){
  byte cmd[9] = {0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  cmd[2] = cmdByte;
  if(cmdByte==0x99){
    cmd[6] = range>>8;
    cmd[7] = range & 0xFF;
  }
  
  if(cmdByte==0x7A){ // my own command for activation of self calibration
    cmd[2] = 0x79;
    cmd[3] = 0xA0;
  }  
  cmd[8] = getCheckSum(cmd);
  myMHZ.write(cmd,9);
}

unsigned int getCO2PWM(){
  unsigned long highPeriod = 0;
  unsigned long lowPeriod = 0;
  unsigned long startTime = 0;
  unsigned int ppmCO2 = 0;
  
  while(digitalRead(pwmPin)){}
  while(!digitalRead(pwmPin)){}
  startTime = millis();
  while(digitalRead(pwmPin)){}
  highPeriod = millis() - startTime;
  startTime = millis(); 
  while(!digitalRead(pwmPin)){}
  lowPeriod = millis() - startTime;
  
  ppmCO2 = rangeFactor*(highPeriod - 2)/(highPeriod + lowPeriod - 4); 

  return ppmCO2; 
}

void getResponse(byte msg[], byte cmdByte){
  unsigned long maxWaitTime = 1000;
  while(myMHZ.available()){
    myMHZ.read();    // removes "rubbish"
  }
  sendCommand(cmdByte, 0x00);
  unsigned long startTime = millis();
  
  while(!myMHZ.available()){
    if((startTime-millis())>maxWaitTime){ // if the MH-Zxx does not respond
      break;
    }
  }
  
  myMHZ.readBytes(msg,9);
}

byte getCheckSum(byte *seq){
  byte checkSum = 0;
  for(int i=1; i<8; i++){
    checkSum += seq[i];
  }
  checkSum = 255 - checkSum + 1;
  return checkSum;
}

 

 

Genauigkeit der MH-Zxx CO2 Sensoren

Messkammer für CO2 Sensoren

Die Genauigkeit der MH-Zxx CO2 Sensoren habe ich mit dem Technoline WL 1030, einem kommerziell erhältlichem CO2 Messgerät, überprüft. Dieses Gerät war 2021 Testsieger der Stiftung Warentest bei den CO2 Messgeräten. Trotzdem stellt sich natürlich die Frage, wie genau es selbst misst, zumal es, wenn man sich die Funktionsweise und die technischen Daten anschaut, selbst auch auf einem MH-Zxx Sensor oder ähnlichem zu basieren scheint. 

Zu diesem Zweck habe ich mir eine CO2 Messkammer gebaut. Als Grundlage diente ein Kasten aus Acrylkunststoff, den man normalerweise zum Ausstellen von Figuren oder Modellautos benutzt. Über ein kleines, verschließbares Loch habe ich mit einer Spritze das CO2 zugeführt. Ein Elektromotor mit Propeller sorgte für die Verteilung des CO2. Dann habe ich noch ein Breadboard eingeklebt und einen Zugang für elektrische Anschlüsse eingebaut. Das Technoline Messgerät habe ich in die Kammer hineingestellt und einen Zugang für das Stromversorgungskabel in den Kasten gebohrt. Das CO2 hat mir mein Wassersprudler geliefert.

Spritzen für die CO2 Dosierung

Überprüfung des Technoline WL 1030

Versuch

Das Technoline WL 1030 habe ich mehrere Tage laufen lassen und gemäß der Bedienungsanleitung kalibriert. Das funktioniert im Prinzip wie bei den MH-Zxx CO2 Sensoren. Vor den Messungen habe ich gut gelüftet, um einen Startwert von 400 ppm zu erreichen.

Die Innenabmessungen des Kastens betragen 13 cm x 13 cm x 20 cm = 3380 cm3. Für das Technoline WL 1030 habe ich ein Volumen von 260 cm3 abgeschätzt. Es besteht aber zum überwiegenden Teil aus „Luft“. Letzten Endes habe ich mit 130 cm3 für das Messgerät und das Breadboard gerechnet. Damit ist das verbleibende Volumen in der Messkammer ca. 3250 cm3. Sollte ich mich dabei um 50 cm3 vertan haben, ist das ein Fehler von 1.5 %. Eine Zugabe von einem Milliliter CO2 sollte die CO2 Konzentration um 1/3250 = ca. 308 ppm erhöhen.

Ich habe jeweils 0.5 oder 1 Milliliter CO2 zugeführt und gewartet, dass sich ein einigermaßen stabiler Wert einstellte. Das hat jeweils ca. 5 min gedauert. Da meine Messkammer nicht absolut gasdicht ist, nahm der Messwert nach Erreichen eines Maximums langsam wieder ab. Insgesamt habe ich 4 ml CO2 in die Kammer eingebracht. Und so entwickelte sich die Konzentration:

Überpüfung des Technoline WL 1030
Überprüfung des Technoline WL 1030

Auswertung

Der Endwert bei Zugabe von 4 ml CO2 hätte bei 1632 ppm liegen müssen. Gemessen habe ich ein Maximum von ca. 1500 ppm. Nun ist die Kammer aber nicht wirklich dicht. Um den Fehler abzuschätzen, habe ich den Versuch wiederholt, aber diesmal 4 ml CO2 in einem Schritt zugeführt. Diesmal lag der Endwert bei ca. 1550 ppm (grüne Raute). Nach weiteren 5 Minuten sank die Konzentration wegen der Undichtigkeit auf etwas unter 1500 ppm (gelber Kreis), also ein Verlust von ca. 50 ppm. Auf den ersten Messwert hatte ich 5 min gewartet. Wenn ich eine konstante Undichtigkeit zugrunde lege, müsste ich also auf den ersten Messwert 50 ppm aufschlagen. Mit ca. 1600 bin ich dann schon sehr nah an den theoretischen 1632 ppm. Zugegebenermaßen war das die beste Messung. Wiederholungen zeigten gewisse Schwankungen, aber die Messungen lagen (nach Korrektur der Undichtigkeit) nicht mehr als 5 % daneben. Ausreißer sind natürlich auch durch andere Fehler bedingt, z. B. dass ich beim Aufziehen der Spritze vielleicht auch mal etwas Luft mit hineinbekommen habe. 

Mein Fazit ist, dass man sich auf die Messwerte des Technoline 1030 gut verlassen kann – sorgfältige Kalibrierung vorausgesetzt.

Messungen mit den MH-Zxx CO2 Sensoren

Ich habe dann das Technoline WL 1030 genommen, um die MH-Zxx Sensoren zu prüfen. Zunächst ein kleines Video dazu:

Bei meinen Versuchen mit den MH-Zxx CO2 Sensoren habe ich festgestellt, dass diese langsamer reagierten als das Technoline WL 1030. Vielleicht ist das einfach bauartbedingt und das CO2 braucht bei den MH-Zxx Sensoren länger, um durch die Abdeckungen in die eigentliche Messzelle zu gelangen. Vielleicht läuft auch ein Dämpfungsalgorithmus im Hintergrund. Die Folge ist jedenfalls, dass die Messwerte der MH-Zxx Sensoren noch stiegen, während die Messwerte des Technoline WL 1030 schon wieder sanken. Dadurch lagen die Werte des Technoline in der Zugabephase etwas höher. Nach der Zugabephase nahm die CO2 Konzentration wegen der Undichtigkeit der Messkammer langsam ab. Auch hier reagieren die MH-Zxx Sensoren langsamer und liefern entsprechend höhere Werte. Hier exemplarisch ein Ergebnis für den MH-Z14 Sensor:

MH-Z14 vs. Technoline WL 1030
MH-Z14 vs. Technoline WL 1030

Die Ergebnisse für den MH-Z19C sahen sehr ähnlich aus. Insgesamt war ich ebenso erstaunt wie erfreut über die guten Messergebnisse. Wichtig ist allerdings auch hier, dass man die Nullpunktkalibrierung (genauer gesagt die 400 ppm Kalibrierung) sorgfältig durchführt.

Und das passiert bei schlechter Kalibrierung

Wenn ihr die Kalibrierung in einer nicht vollständig gelüfteten Umgebung durch führt, verschiebt ihr den 400 ppm Startpunkt entsprechend. Das ist mir bei der unten abgebildeten Messung mit einem MH-Z19C passiert. Zuerst wollte ich die Daten einfach verwerfen, fand es dann aber ganz interessant. Man kann schön erkennen, dass die Messwerte parallel verschoben sind. Einen Einfluss auf die Steigung hat die fehlerhafte Kalibrierung nicht.

Messwerte bei mangelhafter Kalibrierung
Messwerte bei mangelhafter Kalibrierung

Fazit

Die MH-Zxx CO2 Sensoren liefern zuverlässige Werte, man muss sie aber richtig kalibrieren. Entweder führt ihr dazu von Zeit zu Zeit eine manuelle Kalibrierung durch oder ihr nutzt die automatische Kalibrierung. Für Letztere müsst ihr aber sicherstellen, dass der Sensor in einem Raum steht, der regelmäßig gelüftet wird. Auf keinen Fall sind die MH-Zxx Sensoren dazu geeignet, sie „mal eben“ anzuschalten, um irgendwo eine CO2 Messung durchzuführen.

Danksagung

Das Beitragsbild habe ich auf Pixabay gefunden. Es stammt von Malte Reimold. Ich habe es lediglich quadratisch beschnitten.

22 thoughts on “MH-Z14 und MH-Z19 CO2 Sensoren

    1. Das kann ich dir nicht sagen, aber würde davon ausgehen, dass die Dinger ein paar Jahre halten. Verschleißteile sind nicht dran, am ehesten wird vielleicht die IR-Lampe ihren Geist aufgeben. Wenn du die Teile in einer sehr staubigen Umgebung benutzt, dann könnte irgendwann auch der Sensor zu sein. Das Technoline WL1030 läuft bei mir seit drei Jahren im Dauerbetrieb. Für die Module habe ich keinen Langzeittest, aber ich glaube nicht, dass sie schlechter sind.

  1. Vielen Dank für die Seite.

    Ich habe diese Drei:

    und

    sowie allerlei Weiteres genutzt – dort steht dieser Eintrag auch in Analogie.

    Am Ende – und das für weitere Leute, die hierher finden – habe ich …
    … erst mit einem ältern .bin den ESP32 so geflashed bekommen, das auch der MQTT Controller lief:
    „ESP_Easy_mega_20230508_normal_ESP32_4M316k“
    Also ein .bin vom Mai 2023 …

    … gelernt , das der’B‘ Typ der Bessere gewesen wäre
    … etwas gebraucht, die PINs richtig zu finden (sind bei allen Typen des ESP wohl ’slightly‘ Unterchiedlich

  2. Hallo Wolfgang,
    wie immer ein super Artikel über die CO2 Messung. Vielen Dank dafür. Ich habe deinen Eigenbau-Sketch auf eine Risc-V MCU (CH32V203C8T6) aus dem Hause WCH portiert, schon weil es da keine Bibliotheken gibt. Läuft perfekt… nach einigen kleinen Hürden. Da ich das ganze, inkl. 3,5″ Display gern portabel betreiben möchte, habe ich Sorgen in Anbetracht der Stromaufnahme. Eine WL 1030 kann doch auch nicht permanent messen. Das Display und die MCU kann ich in den Sleep schicken, der Sensor unterstützt das nicht. Da ich einen History-Graph habe, messe ich alle 2 Minuten, macht 9 Stunden auf dem 320*240 Display. Da stört schon die eine Minute Vorheizzeit. Wobei ich auch keinen Sinn in einer Vorheizung sehe, da das Teil ja optisch arbeitet. Gibt es da Erfahrungen?
    VG Lothar

    1. Hallo Lothar,

      diese Sensoren leben tatsächlich vom Dauerbetrieb. Sie funktionieren nicht wie zum Beispiel ein elektronisches Thermometer, dass man anschaltet und dann kann man losmessen. Dabei geht es nicht nur um das Vorheizen. Nach dem Anschalten sollten die Teile 20 min in einer gut gelüfteten Umgebung stehen, weil sie das dann als 400 ppm CO2 interpretieren. Wenn man die Dinger abschaltet, muss man wieder von vorne beginnen. Auf Dauer kalibrieren sie sich auch selbst nach, indem sie den tiefsten Wert der letzten 24 Stunden als 400 ppm annehmen. Ist zwar blöd mit dem hohen Stromverbrauch, aber Abschalten halte ich für keine gute Idee.
      VG, Wolfgang

      1. Ja, so hast du es weiter oben beschrieben. Die akkubetriebenen Geräte müssen trotzdem etwas anders machen, sonst wären Batterien / Akku schnell am Ende. Vielleicht auch ein anderer Sensor. Zur Autokalibrierung habe ich nicht viel vertrauen. Gerade z.Z. bei Temperaturen um 0°C lüfte ich keine halbe Stunde. Im Netzbetrieb ist eine Handkalibrierung auch nicht praktikabel. Also die Notlösung, Netzbetrieb mit Akkupufferung. Mein Steckbrettaufbau ist auch nicht gerade für solche Experimente geeignet, wo sogar die 5V von USB über den Programmieradapter beim Messen leicht in die Knie geht. Bei meinem Sensor, ein MH-Z14D, im Sekundentakt. Ich werde trotzdem mal experimentieren, wie groß die Abweichung ist, wenn ich z.B. ein mal Pro Woche kalibriere und sonst den Sensor für zwei min. abschalte. Wenn die Ergebnisse reproduzierbar sind, geht es sicher mit einer Korrekturrechnung, ja wenn!!

        1. Genau – Versuch macht klug! Theoretisieren kann man viel. Vielleicht findest du ja einen guten Weg.

          1. Hallo Wolfgang, try and error. Es blieb beim Error. Wenn ich nach 10min. Messung die Versorgung abschalte und nach 2min. wieder einschalte, gibt der MH-Z für 65s konstant 500ppm aus. Also etwas mehr als die angegebene Vorheizzeit. Dann im 10s Takt gemessen ist der 1. Messwert etwa 50ppm zu hoch und sinkt dann langsam auf einen recht konstanten Wert. Das nur mal so zur Info, also ist kein Akkubetrieb möglich.
            Mir stellt sich immer noch die Frage, wie macht es die WL1030??
            Zum nachsehen leider zu teuer!!
            Grüße Lothar

            1. Hallo Lothar, so viel anders ist das Technoline WL 1030 gar nicht. Wenn du mal in die Bedienungsanleitung schaust:
              https://techome24.de/media/pdf/51/70/a7/tec0251660-2-technoline-co2-messgeraet-co2-anzeige-bedienungsanleitung.pdf
              …dann siehst du, dass es zwar nach 150 Sekunden die ersten Werte ausspuckt, aber sich dann über die nächsten 24 Stunden kalibriert. Im weiteren Verlauf nimmt es dann den niedrigsten gemessenen Wert der letzten sieben Tage als 400 ppm an. Alternativ kann man es manuell in einer 400 ppm Umgebung kalibrieren, was immerhin 20 min dauert. Und der Stromverbrauch ist so hoch, dass kein Batteriebetrieb vorgesehen ist, sondern es wird über ein 5 V / 1.2 A Netzteil betrieben. VG, WOlfgang

              1. Hallo Wolfgang, das hätte ich jetzt nicht erwartet, würde aber zum MH-Z passen.
                Danke für die Info
                Grüße Lothar

  3. Hallo Wolfgang, auf Deiner Seite hast Du angegeben, daß der MH-Z19c etwa jede Sekunde misst – ich dachte mein Sensor ist nicht in Ordnung. Der MH-Z14A (FW: 4.30) misst etwa alle 5 Sekunden (roter LichtPuls) für eine halbe Sekunde und mein MH-Z19b (FW: ?) (mit zwei Pinreihen) auch. Daher war ich sehr überrascht (und verunsichert), daß nun der MH-Z19c bei mir auch im Sekundentakt misst (FW: 5.15) . Eine CO2-Messung pro Sekunde halte ich für unnötig schnell. Zumal der Stromverbrauch je beim Lichtpuls deutlich höher ist, und damit der Gesammtstromverbrauch auch!
    Mein eigentliches Problem ist aber der Spannungseinbruch währed des Lichtpulses. Ich betreibe den Sensor der Einfachheit mit einem USB-Ladegerät direkt am Anschluss des ESP8266 D1-Mini. Um eine Rückspeisung von einer externer Quelle zu vermeiden ist eine (Schottky-) Diode zwischen USB-Mini-Stecker und +V5 geschaltet. Dadurch sind – wenn die Speisung über das USB-Port erfolgt – an Pin +5V nur noch 4,7V vorhanden und beim Lichtpuls nur noch 4,6V oder weniger (je nach Widerstand auf den Leitungen / Steckbrett): also außerhalb der Spezifikation des MH-Z19c! Generell sind meine USB-Ladenetzteile vom Regelverhalten nicht wirklich geeignet. Ein normales SteckerNetzteil (zur Zeit 5V; 2,5A!!!) mit Holstecker zeigt ein deutlich besseres Verhalten, ist aber total überdimensioniert!
    Ich werde diese Thematik demnächst mal untersuchen (hab zur Zeit andere Prioritäten), aber hier schon mal als Hinweis.

    1. Hi Florian, ja die Messung im Sekundentakt macht keinen Sinn. Selbst alle 5 Sekunden ist noch weit mehr als genug. Aber es ist nicht einstellbar. Danke schon mal für die Hinweise wegen der Stromversorgung. Das kann für andere sehr von Nutzem sein.
      VG, Wolfgang

  4. Hi,

    erstmal danke für den wirklich guten und ausführlichen Bericht zum MH_Z19. Ich habe mir auch einen bestellt (bei Berrybase, nicht in dubiosen anderen Quellen), an den Raspberry Pi 4 gehängt, MH_Z19 installiert und kann die Werte super über die serielle Schnittstelle abholen. Die Werte erscheinen plausibel, sinken bei Frischluftzufuhr, steigen langsam an, wenn der Raum nicht gelüftet wird. Fein, dachte ich – nun häng ich den Sensor an den Pi Pico (soll für Sohnemann sein, er wollte eine CO2-Ampel).

    Erste Ernüchterung, die MH_Z19 Bibliothek gibt es dafür nicht, aber dafür eine andere, mit der man die serielle Schnittstelle ansprechen kann – Kommando senden funktioniert, zurück kommt nur Schrott. Baudrate schrittweise runtergedreht bis auf 960, wird besser, aber stabil ist was anderes, meist kriege ich Prüfsummenfehler. Erstmal Nase voll (UART ist was für lange Regentage), also schnell PMW probiert – auslesen mit machine.time_pulse_us liefert nun H-Werte, die erstaunlich kurz sind, nur ca. 12 ms (bzw. 12000 µs). Beim Anpusten des Sensors schnellt der Wert in die Höhe und geht kurz darauf wieder zurück. Der Sensor tut also was. Großes Fragezeichen… Erster Verdacht: Versorgungsspannung instabil/wellig, Netzteil gewechselt, selbes Ergebnis. Also MH_Z19 wieder an den „großen Raspi“ gehängt, Werte über UART einwandfrei 500-600 ppm im Innenraum. Also einfach den PWM-Pin ZUSÄTZLICH an den Pico (Masse natürlich auch verbunden) – PWM liefert weiter unsinnig niedrige Werte.

    Was ich aus den Unterlagen nicht rausbekomme: Braucht der PWM-Ausgang einen Pullup/down, um stabil zu arbeiten? Hat irgendjemand schon Erfahrung mit Pico+MH_Z19 über UART und auch PWM? Meine letzte Idee wäre noch, den PWM auch mal am großen Pi auszulesen, das steht noch auf der Todo.

    Danke für Eure Tipps

    1. Hallo Björn,

      ich habe den MH-Z19 nicht am Raspberry Pi ausprobiert, deswegen habe ich keine Erfahrung, was da spezifisch alles schief gehen kann. Aber ein paar Gedanken:
      1) Hast du vielleicht ein Fake Modell erwischt? Wenn die Platine schwarz ist und nicht grün, dann könnte das sein.
      2) Ich würde mich zunächst auf PWM konzentrieren, das sollte (eigentlich) immer funktionieren. Ich habe mal schnell folgendes ohne Mikrocontroller ausprobiert: 5 V an GND/VIN, dann gewartet bis das Modul blinkt. Dann einfach eine LED mit Vorwiderstand PWM/GND: Sie blinkt. Wenn sie das bei die nicht tut, dann ist das Modul defekt. Das ist der Vorteil an dem ultralangsamen PWM Signal. Mach das aber lieber nicht zu lange, weil ich nicht weiß, ob der PWM Ausgang für die Strombelastung einer LED ausgelegt ist.
      3) Am Arduino brauchte ich keinen Pullup oder Pulldown Widerstand. Und wenn sogar eine LED leuchtet, dann brauchst du mindestens keinen Pullup.
      4) Haben der Raspi und der MHZ-19 ein gemeinsames GND? Hab ich auch schon mal bei einem anderen Projekt vergessen und nach allem möglichen anderen gesucht.
      Viel Glück bei der Fehlersuche.
      VG, Wolfgang

    2. Hallo Björn,

      Wolfgang Ewald hat da schon recht, beide Masseanschlüsse zu verbinden, darf nicht vergessen werden.
      Der PWM-Ausgang benötigt keinen Pull-up Widerstand, aber:
      Auch wenn der MH-19 mit +5V betrieben wird, der PWM-Ausgang hat nur einen Spannungshub von 3V !
      Schliesse ich PWM direkt an meinen ATMEGA (+5V Versorgungsspannung) an, gibt es grosse Probleme, weil nicht jeder ATMEGA die 3V ständig als eindeutiges H-Signal ansieht.
      Ich habe einen Spannungswandler (3V auf 5V, mit 2x 4k7 und einem 2N7000, quasi ein Teil einer I2C-Schnittstelle. Das Signal wird dadurch nicht !!! invertiert) zwischen PWM und Controller gesetzt und habe danach 100% Funktion, und zwar dauerhaft.
      Evtl. ist das 3V- PWM-Signal auch bei Dir das Problem.
      Als voll ausgebildeter Elektroniker habe ich mit der Zeit gelernt, dass es vorteilhaft sein kann, digitale Signale angeschlossener Fremdsysteme auf Versorgungsspannungshöhe (Controllerseite) zu bringen. Dies hat in der Vergangenheit viele Probleme gelöst.
      Gruss, Wolfgang S.

    3. Hallo, für die (großen) Raspberry Pi und den MH-Z19 gibt es eine gute Bibliothek von UedaTakeyuki (https://github.com/UedaTakeyuki/mh-z19/find/master„. Zur Zeit nutzte ich den MH-Z19C an einem Raspberry Pi Zero über UART. In der Datei „mh_z19.py “ sind alle wesentlichen Routinen enthalten (auch Calibration….). Ich habe diese in mein Programm (Combi mit BME680 und OLED) integriert. Das Programm speichert die Meßdaten auf die SD, die ich über WLAN auslese und mir den Verlauf der Werte mit Excel anschaue. Zum Test habe ich sowohl UART als auch PWM ausprobiert. PWM scheint etwas ungenauer aber funktioniert mit der Routine von UedaTakeyuki mit den (großen) Pi’s .

      In einem Test habe ich einen SCD30 und einen MH-Z19C in einer Kunststoffbox parallel bei ca. 16°C (Keller) laufen lassen. Dort waren die CO2-Werte bei ca. 400 ppm. Ab und zu habe ich in die Box geatmet und dann mit Deckel verschlossen. Die Werte gingen dann bis ca. 2500 ppm hoch. Im Excel-Diagramm konnte ich nach 1-2 Tagen praktisch die gleichen Werte für die beiden Sensoren messen.

      Solange ich den MH-Z19C auf „automatic self-calibration“ hat er vernünftige Werte angezeigt. Dann habe die „self-calibration“ deaktiviert und nach einigen Wochen bei ca. 22°C laufen lassen. Leider waren dann die Werte nicht mehr brauchbar. Jetzt läuft der MH-Z19C wieder mit „automatic self-calibration“ und bei einmal ordentlich Lüften täglich liefert er auch mit dem SCD30 vergleichbar Werte.

      Nach meinem Eindruck ist der SCD30 (läuft bei mir am WIO-Terminal) ein Sensor mit Langzeitstabilität und Temperaturkompensation; der MH-Z19 ist doch eher die günstige Bastellösung.

      Zum Einsatz des MH-Z19 mit einem Pico habe ich noch das gefunden: https://github.com/senarclens/mh_z19_pico

  5. Hallo,
    alles zurück.
    Ich habe in Ihrem Artikel die Tabelle gefunden, bezüglich Messwertbestimmung.
    Die wurde beim Ausdrucken unterdrückt, jetzt habe ich sie aber vorliegen.
    Nebenbei: Ihr Beitrag über das WL1030 war so überzeugend, dass ich so ein Gerät jetzt auch besitze.

    Gruss, Wolfgang Staven

  6. Hallo,
    heute ist der MH-Z19C gekommen.
    Ich verwende das PWM-Signal.
    Wie erkenne ich, ob die Range 2000ppm oder 5000ppm beträgt ?

    Gruss, Wolfgang Staven

    1. Hallo,

      der Sketch MHZxx.ino enthält die Antwort. Man kann die Range serielle abfragen. Das cmd-Byte dafür lautet 0x9B. Für eine Anfrage ist also 0xFF, 0x01, 0x9B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64 zu senden (wenn ich die Checksumme richtig ausgerechnet habe -> Byte 8).

      In der Antwort ist die Range in Byte 4 und 5 „verpackt“:
      RANGE_2000 0x07D0
      RANGE_5000 0x1388
      RANGE_10000 0x2710

      VG, Wolfang

  7. Tobias Schürg hat bemerkt, dass man den Sensor nicht in zu kurzem Zeitabstand abfragen sollte. Zum einen weil der Sensor nicht wirklich neue Messungen macht und zum andern, weil es die Lebensdauer extrem verkürzen kann.
    So wie ich das verstanden habe sollte maxtime 120000 (2 Minuten) nicht unterschreiten.

    1. Dagegen spricht, dass ich bei jeder Abfrage andere Ergebnisse erhalte. Und wieso sollte eine Abfrage die Lebensdauer verkürzen, wenn durch die Abfrage keine zusätzlichen Messungen ausgelöst werden? Und im Beispielsketch der Bibliothek von Tobias Schürg wird der Sensor alle 5 Sekunden abgefragt. Aber: genau wissen tue ich das ganz klar nicht! Und eine zu kurze Abfrage macht auch nicht wirklich Sinn (außer vielleicht bei meinen Experimenten), da sich die CO2 Konzentration in einem Raum eher langsam ändert. Also wer auf der sicheren Seite sein möchte sollte den Ratschlag berücksichtigen. Vielen Dank!

    2. Dass eine zu häufige Abfrage die Lebensdauer verkürzt, wäre unlogisch- denn der Sensor gibt selber über den PWM-Ausgang etwa einmal pro Sekunde den aktuellen Messwert aus, selbst wenn man gar nicht aktiv abfragt (exakt alle 1004ms laut Datenblatt). Man könnte also schon darüber etwa 1x pro Sekunde den aktuellen Wert ermitteln.

      Aus dem Datenblatt in https://www.winsen-sensor.com/d/files/PDF/Infrared%20Gas%20Sensor/NDIR%20CO2%20SENSOR/MH-Z19%20CO2%20Ver1.0.pdf ist ebenfalls nicht ersichtlich, dass man nicht zu oft abfragen darf.

Schreibe einen Kommentar

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