Next generation CO2 sensor

  • Well, that name is getting pompous, but hey.

    Here's code for the MH-Z19 CO2 sensor. It's concidered to be the best value for money CO2 sensor currently.

    The code is non-blocking, so it can also act as a repeater.

    A pull request for the code has also been made on Github, so it can become part of the examples.

     * The MySensors Arduino library handles the wireless radio link and protocol
     * between your home built sensors/actuators and HA controller of choice.
     * The sensors forms a self healing radio network with optional repeaters. Each
     * repeater and gateway builds a routing tables in EEPROM which keeps track of the
     * network topology allowing messages to be routed to nodes.
     * Created by Henrik Ekblad <>
     * Copyright (C) 2013-2015 Sensnology AB
     * Full contributor list:
     * Documentation:
     * Support Forum:
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of the GNU General Public License
     * version 2 as published by the Free Software Foundation.
     *  MH-Z19 CO2 sensor
     *  This sensor uses a frickin' laser to measure lots of different fine dust levels, between 0.3 and 10 microns.
     *  It communicates with your board over serial at 9600 speed.
    // if you uncomment this, you can get test and debug updates about the sensor' wireless connection by using the serial monitor tool.
    #define MY_DEBUG
    // Enable and select radio type attached
    #define MY_RADIO_NRF24                            // A 2.4Ghz transmitter and receiver, often used with MySensors.
    // #define MY_RF24_PA_LEVEL RF24_PA_MIN           // This sets a low-power mode for the radio. Useful if you use the verison with the bigger antenna, but don't want to power that from a separate power source. It can also fix problems with fake Chinese versions of the radio.
    // #define MY_RADIO_RFM69                         // 433Mhz transmitter and reveiver.
    // Choose if you want this sensor to also be a repeater.
    // #define MY_REPEATER_FEATURE                    // Just remove the two slashes at the beginning of this line to also enable this sensor to act as a repeater for other sensors. If this node is on battery power, you probably shouldn't enable this.
    // Libraries
    #include <MySensors.h>
    #include <SoftwareSerial.h>
    // This can be changed:
    unsigned long co2MeasurementInterval = 30000;     // Time to wait between reads (in milliseconds).
    SoftwareSerial mySerial(10, 11);                  // RX, TX . You can choose other pins if you prefer.
    // Mysensors settings
    #define CHILD_ID_CO2 0                            // The Co2 sensor' ID on this node.
    MyMessage msgCo2(CHILD_ID_CO2, V_LEVEL);
    MyMessage msgCo2b(CHILD_ID_CO2, V_UNIT_PREFIX);
    void presentation()
      // Send the sketch version information to the gateway and Controller
      sendSketchInfo("AIQ Sensor CO2 MH-Z19", "1.1");
      // Register attached sensor(s) to gateway
      present(CHILD_ID_CO2, S_AIR_QUALITY);
    void setup() 
      while (!=-1) {};  //clear Co2 buffer.
      Serial.println("hello world, I am a sensor.");
    void loop() 
       // You should not change these variables:
       static unsigned long previousCo2Millis = 0;       // Used to remember the time of the last temperature measurement.
       unsigned long currentMillis = millis(); // The time since the sensor started, counted in milliseconds. This script tries to avoid using the Sleep function, so that it could at the same time be a MySensors repeater.
      if (currentMillis - previousCo2Millis >= co2MeasurementInterval) { // this only gets triggered when enough time has passed.
        Serial.println("CO2 - Sending data request to sensor.");
        previousCo2Millis = currentMillis;
        long co2ppm = readCO2();    
        Serial.println("Co2 - PPM = " + String(co2ppm));
        Serial.print("Co2 - zzzzZZZZzzzzZZZZzzzz\n");
    // Main function that gets the Co2 data
    int readCO2()
      while (!=-1) {};  //clear serial buffer  
      char response[9]; // for answer
      byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
      // command to ask for data
      mySerial.write(cmd, 9); //request PPM CO2 
      mySerial.readBytes(response, 9);
        Serial.print(response[0], HEX);   
        Serial.print(" - ");   
        Serial.print(response[1], HEX);   
        Serial.print(" - ");    
        Serial.print(response[2], HEX);   
        Serial.print(" - ");          
        Serial.print(response[3], HEX);   
        Serial.print(" - ");          
        Serial.print(response[4], HEX);   
        Serial.print(" - ");          
        Serial.print(response[5], HEX);   
        Serial.print(" - ");        
        Serial.print(response[6], HEX);   
        Serial.print(" - ");   
        Serial.print(response[7], HEX);   
        Serial.print(" - ");      
        Serial.print(response[8], HEX); 
        Serial.println(" - END");  
      if (response[0] != 0xFF)
        Serial.println("Wrong starting byte from co2 sensor! (should be FF)");
        return -1;
      if (response[1] != 0x86)
        Serial.println("Wrong command from co2 sensor! (should be 86)");
        return -1;
      int responseHigh = (int) response[2];
      int responseLow = (int) response[3];
      int ppm = (256 * responseHigh) + responseLow;
      return ppm;

Log in to reply

Looks like your connection to MySensors Forum was lost, please wait while we try to reconnect.