[Solved] MH-Z19B provides only static max values for CO2 (temperature works)

Hi all,
this is my first try and first post her so please be gentle :slight_smile:

I have a MH-Z19B attached to an ESP8266 NodeMCU v3 from AZ Delivery.
I am running the Arduino 1.8.13 and as a board I have selected the NodeMCU 0.9 (ESP-12 Module) and I also tried the NodeMCU 1.0 (ESP-12E Module). The "Generic ESP8266 Module" did not work with the SoftwareSerial pin definition ...

The cabling of the sensor to the ESP8266 is as follows:

MH-Z19B Pin TX is attached to ESP8266 Pin D7
MH-Z19B Pin RX is attached to ESP8266 Pin D6

MH-Z19B Pin GND is attached to ESP8266 Pin G (4 left of D7)
MH-Z19B Pin Vin is attached to ESP8266 Pin 3V (5 left of D7)

I created the following code from several repositories found in the web:

#include <SoftwareSerial.h>

#define MH_Z19_RX D7
#define MH_Z19_TX D6

// MH-Z19B Sensor
int co2ppm;                 // CO2 Messwert in ppm

SoftwareSerial co2Serial(MH_Z19_RX, MH_Z19_TX);
// SoftwareSerial co2Serial(D7, D6);


int readCO2()                          // Kommunikation mit MH-Z19 CO2 Sensor
{
  byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; //request CO2 and temp values
  //byte cmd[9] = {0xFF, 0x01, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; //returns firmware level
  //byte cmd[9] = {0xFF, 0x01, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; //reset MCU
  //byte cmd[9] = {0xFF, 0x01, 0x9B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; //returns Sensor range
  char antwort[9];
  
  co2Serial.write(cmd, 9);
  
  // 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)
  {
    co2Serial.read();
  }

  // Clear the Buffer
  memset(antwort, 0, 9);

  int i = 0;
  while (co2Serial.available() == 0)
  {
    Serial.print("Waiting for response ");
    Serial.print(i);
    Serial.println(" s");
    delay(1000);
    i++;
  }
  if (co2Serial.available() > 0)
  {
    co2Serial.readBytes(antwort, 9);
    Serial.println("+++ Response Start String 1 +++ " + String(antwort[0], HEX) + " +++");
    Serial.println("+++ Response Start String 2 +++ " + String(antwort[1], HEX) + " +++");
    Serial.println("+++ Response CO2 High Value +++ " + String(antwort[2], HEX) + " +++");
    Serial.println("+++ Response CO2 Low Value  +++ " + String(antwort[3], HEX) + " +++");
    Serial.println("+++ Response Temperature    +++ " + String(antwort[4], HEX) + " +++");
    Serial.println("+++ Response Status = 0x40  +++ " + String(antwort[5], HEX) + " +++");
    Serial.println("+++ Response Unknown        +++ " + String(antwort[6], HEX) + " +++");
    Serial.println("+++ Response Unknown        +++ " + String(antwort[7], HEX) + " +++");
    Serial.println("+++ Response Checksum       +++ " + String(antwort[8], HEX) + " +++");
    Serial.println("+++ Response End String     +++ " + String(antwort[9], HEX) + " +++");

  }

  //co2Serial.readBytes(antwort, 9);

  byte crc = 0;
  for (int i = 1; i < 8; i++)
  {
    crc += antwort[i];
  }
  crc = 255 - crc + 1;
  Serial.println("+++ CRC Calculated: " + String(crc) + " +++");

  
  if (antwort[0] != char(0xFF))
  {
    Serial.println("Invalid response <0xFF> from CO2 sensor!");
    return -1;
  }

  if (antwort[1] != char(0x86))
  {
    Serial.println("Invalid response <0x86> from CO2 sensor!");
    return -1;
  }


  if (antwort[8] == crc)
  {
    int antwortHigh = (int) antwort[2]; // CO2 High Byte
    int antwortLow = (int) antwort[3];  // CO2 Low Byte
    int ppm = (256 * antwortHigh) + antwortLow;
    //Serial.println(antwortHigh);
    //Serial.println(antwortLow);
    //Serial.println(ppm);
    return ppm;                         // Antwort des MH-Z19 CO2 Sensors in ppm
  }
  else
  {
    Serial.println("CRC error!");
    return -1;
  }
  
  byte status = antwort[5];
  Serial.print("Status: ");
  Serial.println(status);
  if (status != 0x40)
  {
    Serial.println("Status NOT ok");
  }
}


void setup() {
  //Setup for MH-Z19B Software Serial Communication
  co2Serial.begin(9600); //Init sensor MH-Z19 
  Serial.begin(115200);
  Serial.println();
}

void loop() {

    // New MH-Z19 CO2 sensor readings
    co2ppm = readCO2();
    
    Serial.println("+++ PPM: " + String(co2ppm) + " +++");
    
    delay(15000); 

}

The repeating output in 15 second cycles shows that the "CO2 High Byte" value and the "CO2 Low Byte" value stay constant for ever - obviously at their max level - as the calculated PPM value (ppm = (256 * antwortHigh) + antwortLow) is always 5000 ppm. Also the response status = antwort[5] should be 0x40 but is 0x00.

16:52:17.160 -> +++ Response Start String 1 +++ ff +++
16:52:17.160 -> +++ Response Start String 2 +++ 86 +++
16:52:17.197 -> +++ Response CO2 High Value +++ 13 +++
16:52:17.197 -> +++ Response CO2 Low Value  +++ 88 +++
16:52:17.197 -> +++ Response Temperature    +++ 42 +++
16:52:17.197 -> +++ Response Status = 0x40  +++ 0 +++
16:52:17.197 -> +++ Response Unknown        +++ 0 +++
16:52:17.197 -> +++ Response Unknown        +++ 0 +++
16:52:17.197 -> +++ Response Checksum       +++ 9d +++
16:52:17.197 -> +++ Response End String     +++ ff +++
16:52:17.197 -> +++ CRC Calculated: 157 +++
16:52:17.197 -> +++ PPM: 5000 +++

As you can see in the commented lines for the command string:

  byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; //request CO2 and temp values
  //byte cmd[9] = {0xFF, 0x01, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; //returns firmware level
  //byte cmd[9] = {0xFF, 0x01, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; //reset MCU
  //byte cmd[9] = {0xFF, 0x01, 0x9B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; //returns Sensor range

I already tried to reset the sensor or find out the defined sensor range but without success.

Is there anyone who can tell me if the sensor is broken or if there is a chance to reset it to get it working properly?
Or maybe there is something substantially wrong with my code and someone has a hint how to fix this.

Thank you for any response and support!!!

Cheers
Justus

Nice job on your first post with the code tags and plenty of detail. :slight_smile:

MH-Z19B Pin Vin is attached to ESP8266 Pin 3V (5 left of D7)

I think the first issue is how you are powering the MH-Z19B module. My data sheet indicates a 4.5-5.5 volt Vin spec. and it may be quite power hungry. I would power the device with a separate 5v supply.

Here is a link to a thread with some pretty solid code with good debuging in reply #5 which you should be able to adapt to your node mcu software serial pins. There is also a discussion of the status and temperature bytes.

https://forum.arduino.cc/index.php?topic=525459.msg3587557#msg3587557

I would strongly suggest you try to repower the module and use the code in the cited thread.

There is at least one significant error I see in your code, but I do not believe it is the cause of your issues. antwort[9] is outside the bounds of the array. The indexes of a 9 byte array run from 0 to 8. However, your code contains this line

 Serial.println("+++ Response End String     +++ " + String(antwort[9], HEX) + " +++");

Hey cattledogg,

thanks for the warm welcome and your great ideas. I will try your idea with separating the power supply and post the update. I will also read the proposed threads and see if I find some advice there as well.

I will correct the numbering of the response (antwort) array. You are right this can not work, but it does not relate to the problem. Thanks for the hint!

I try to test it before the weekend but I am currently busy working and travelling next week ... I definitely give feedback one the outcome!!!

Thank you!!!

cattledog:
I think the first issue is how you are powering the MH-Z19B module. My data sheet indicates a 4.5-5.5 volt Vin spec. and it may be quite power hungry. I would power the device with a separate 5v supply.

Hi cattledog,
unfortunately a separate 5V power supply did not change the behaviour :slight_smile: ... I will see if the link to the thread you posted gets me further ...
Cheers
Justus

Hi all,

I learned that you can reset the MH-Z19B sensor by connecting "GND" pin and the "HD" pin for 7-10 seconds!!!
This worked and I calibrated the sensor by running him at the open window and it is now starting up with 400~410 PPM.

Hence the issue was that the sensor needed to be resetted and is working now.

Question: How do I mark this topic as answered so that it can be closed?

If you edit your first post you can add solved to the title.

wildbill:
If you edit your first post you can add solved to the title.

Cheers and done!

Good job getting this sorted out. This recent data sheet explains about the three methods of setting the 400ppm zero point.