Publish the CO2 concentration of an MH-Z19 sensor to Domoticz, Arduino code compatible ESP32 / ESP8266

Share on facebook
Share on twitter
Share on linkedin
Share on pinterest
Share on email
Share on whatsapp

In the previous tutorial, we discovered and tested the MH-Z19 sensor which allows to measure the CO2 concentration without calibration. We saw how to read the CO2 concentration returned on the sensor PWM pin in MicroPython and then publish it on a Domoticz server via the HTTP / JSON interface. In this new tutorial, I propose to do the same thing with Arduino code. The code is compatible with the ESP8266 and the new ESP32. For that, it will be necessary to install the Espressif SDK by following this tutorial for the ESP8266 development bards and this one for the ESP32 boards. If you have not read the previous article, the Winsen MH-Z19 sensor is more accurate than the MQ135 environmental sensor. It is much more expensive (about $24 / 20€) but measuring the CO2 level does not require a “hack” mathematical. The value expressed in ppm is retrieved directly from the PWM pin or from the serial port.

Necessary material

The code works on ESP8266 as well as on ESP32. For this tutorial, we will use the PWM output of the MH-Z19. Only one entry on the ESP8266 / ESP32 card will be required.

Presentation of the CO2 sensor MH-Z19

The MH-Z19 is a self-calibrated sensor, ie the measurement sent by the sensor does not require any mathematical treatment. In other words, the CO2 content expressed in ppm (parts per million) is recovered. The MH-Z19 has a PWM output and a UART digital interface (serial port). For this tutorial, we will already start by exploiting the PWM output.

Measuring range0 – 5000ppm
Precision± 50ppm+5%
Power supply3.6 ~ 5.5 VDC
Current< 18 mA
Numerical outputsUART and PWM
Heat time3 minutes
Working temperature0 ~ 50 ℃
Working humidity0 – 95% (without condensation)
Dimensions33 mm×20 mm×9 mm (L×W×H)
Lifetime annonced> 5 years

The PWM signal is proportional to the CO2 concentration.

mhz19 pwm signal co2 level

It will therefore be necessary to measure the time during which the signal remains at the high level. Then, the following formula allows to deduce the CO2 rate.

mhz19 pwm calculae co2 concentration

  • Cppm represents the concentration of CO2 in the atmosphere in ppm
  • Th, the time during which the signal remained high
  • Tl, the time during which the signal has remained low

For a 0-5000ppm sensor, simply replace 2000 by 5000 in the formula. The complete documentation is available here.

Prepare virtual appliances on Domoticz

Go to the Domoticz server to create two virtual devices of the temperature type and get the Idx of each probe. Follow this tutorial to learn how to do it.

domoticz ds18b20 esp8266 esp32 arduino idx device sensor

Cabling the MH-Z19’s Serial Port to an ESP8266

Although an Arduino Uno can be used to perform CO2 concentration measurements, the ESP8266, which incorporates a WiFI module, is much better suited for this project. We can move the probe in the house and why not add a small monochrome OLED screen SSD1306. In order for the CO2 concentration measurement to be correct, the MH-Z19 must be permanently powered. As you can see in the technical data, wait at least 2 minutes before the measurement is correct. Battery operation is not suitable for this type of project.

The ESP8266 has 2 UART serial ports. UART0 port (RXD0 / TXD0) and reserved for communication with the IDE. The port numbered by mistake # 1 (TXD1) is incomplete. There is only the TXD1 pin. It is reserved to flash the memory of the ESP8266. Here we will use the RXT2 / TXD2 serial port. Pin RX is located on pin D7 of the ESP8266. The TX pin is on pin D6. We will therefore wire the serial loan by crossing the pins as usual. The RX pin of the MH-Z19 on the D6. The TX pin of the MH-Z19 on the D7. The MH-Z19 can be powered with a voltage between 3.6 and 5.6V. Connect the MH-Z19’s Win Pin to the 5V Pin of the ESP8266. Close the circuit by connecting the GND pins.

esp8266 pinout

Serial communication with the MH-Z19

I adapted the source code from the previous tutorial that read several Dallas DS18B20 temperature probes. Here is what is particular. To communicate via the serial port with another device, another serial port must be opened. It is the SoftwareSerial.h library that takes care of it. To open a serial port on the RXD2 and TXD2 pins, you must instantiate a SoftwareSerial object that takes the serial port pins as parameter. In the setup (), we start the serial port. Here, it is ported at a speed of 9600 bauds according to Wesen specifications:

  • Baud rate: 9600
  • Data byte: 8 byte
  • Stop byte: 1 byte
  • Parity: no

Here, we have 2 open serial ports. The first at 115200 baud communicates with the serial monitor of the Arduino IDE (for example), the second communicates with the MH-Z19.

#include <SoftwareSerial.h>
#define MH_Z19_RX D7
#define MH_Z19_TX D6

SoftwareSerial co2Serial(MH_Z19_RX, MH_Z19_TX);

void setup() {
  co2Serial.begin(9600); //Init sensor MH-Z19(14)  

For the rest of the code, it’s already seen.

Publish CO2 concentration on Domoticz via HTTP / JSON interface, Arduino code compatible ESP8266 and ESP32

We are not going to review here how does sending data to the Domoticz server via the HTTP / JSON interface. Everything is explained in detail in this tutorial. Create a new sketch and paste the code below. Then change the following parameters in the code

  • wifi_ssid, WiFi network
  • wifi_password, WiFi password
  • host, IP address of the Domoticz server
  • port, default 8080
  • IDX_mhz19, Idx of the Domoticz virtual appliance that will receive the CO2 concentration
 * Read MZ-Z19 CO2 concentration using UART interface and publish value on Domoticz with HTTP request
 * Lecture de la concentration en CO2 d'un capteur MH-Z19 via le port série et plublication de la mesure sur un serveur Domoticz requete HTTP
 * - (december 2017) 
//#include <Wire.h>
#include <SoftwareSerial.h>
// Pour un Arduino ou ESP32 (le SDK Espressif doit être installé) | For Arduino or ESP32 (Espressif SDK must be installed) 
//#include <WiFi.h>
//#include <HTTPClient.h>
// Pour une carte ESP8266 | For ESP8266 development board
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <PubSubClient.h>

#define INTERVAL 5000
#define MH_Z19_RX D7
#define MH_Z19_TX D6

long previousMillis = 0;

SoftwareSerial co2Serial(MH_Z19_RX, MH_Z19_TX); // define MH-Z19

// Parametres WIFI - WiFi settings
#define wifi_ssid "********"
#define wifi_password "********"

// Paramètres HTTP Domoticz - HTTP Domoticz settings
const char* host = "***.***.***.***";
const int   port = 8080;
#define IDX_mhz19   26

HTTPClient http;

void setup() {

  // Connexion au réseau WiFi, connexion aux sondes
  // Start WiFi connexion and probes

  // Démarrer la communication série avec le MH-Z19 - Start UART communication with MZ-Z19 sensor
  unsigned long previousMillis = millis();
  co2Serial.begin(9600); //Init sensor MH-Z19(14)

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis < INTERVAL)
  previousMillis = currentMillis;
  Serial.print("Requesting CO2 concentration...");
  int ppm = readCO2();
  Serial.println("  PPM = " + String(ppm));
  // Format JSON à respecter pour l'API Domoticz - Domoticz JSON API 
  // /json.htm?type=command&param=udevice&idx=IDX&nvalue=PPM
  String url = "/json.htm?type=command&param=udevice&idx=";
   url += String(IDX_mhz19);
   url += "&nvalue=";    
   url += String(ppm); 

//Connexion au réseau WiFi
void setup_wifi() {
  Serial.print("Connecting to ");

  WiFi.begin(wifi_ssid, wifi_password);

  while (WiFi.status() != WL_CONNECTED) {

  Serial.println("WiFi connexion OK ");
  Serial.print("=> Addresse IP : ");

void sendToDomoticz(String url){
  Serial.print("Connecting to ");
  Serial.print("Requesting URL: ");
  int httpCode = http.GET();
    if (httpCode) {
      if (httpCode == 200) {
        String payload = http.getString();
        Serial.println("Domoticz response "); 
  Serial.println("closing connection");

int readCO2() {
  // D'après le code original de | From original code
  byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
  // command to ask for data
  byte response[9]; // for answer

  co2Serial.write(cmd, 9); //request PPM CO2

  // The serial stream can get out of sync. The response starts with 0xff, try to resync.
  while (co2Serial.available() > 0 && (unsigned char)co2Serial.peek() != 0xFF) {;

  memset(response, 0, 9);
  co2Serial.readBytes(response, 9);

  if (response[1] != 0x86)
    Serial.println("Invalid response from co2 sensor!");
    return -1;

  byte crc = 0;
  for (int i = 1; i < 8; i++) {
    crc += response[i];
  crc = 255 - crc + 1;

  if (response[8] == crc) {
    int responseHigh = (int) response[2];
    int responseLow = (int) response[3];
    int ppm = (256 * responseHigh) + responseLow;
    return ppm;
  } else {
    Serial.println("CRC error!");
    return -1;

Save the sketch and upload it to the ESP8266. If all is correct, you must receive a response with the status OK for each HTTP request.

Connecting to
Requesting URL: /json.htm?type=command&param=udevice&idx=26&nvalue=410
Domoticz response
    "status": "OK",
    "title": "Update Device"

closing connection

The virtual device displaying the CO2 content in the room updates after a few seconds on Domoticz.

mhz19 arduino code esp8266 esp32 co2 concentration uart

Click to rate this post!
[Total: 0 Average: 0]

Are you having a problem with this topic?

Maybe someone has already found the solution, visit the forum before asking your question
Ask your question

Share on facebook
Share on twitter
Share on linkedin
Share on pinterest
Share on email
Share on whatsapp

Did you like this project ? Don't miss any more projects by subscribing to our weekly newsletter!

We will be happy to hear your thoughts

      Leave a Reply

      DIY Projects