MH-Z19 sensor with Adruino Mega

Hey,

I'm currently trying to connect an MH-Z19C Co2 sensor to an Arduino Mega 2560 via one of the serial interfaces, unfortunately with moderate success.

#include <Arduino.h>
#include "MHZ19.h"                                        
//#include <SoftwareSerial.h>                                // Remove if using HardwareSerial

//#define RX_PIN 10                                          // Rx pin which the MHZ19 Tx pin is attached to
//#define TX_PIN 11                                          // Tx pin which the MHZ19 Rx pin is attached to
//#define BAUDRATE 9600                                      // Device to MH-Z19 Serial baudrate (should not be changed)

MHZ19 myMHZ19;                                             // Constructor for library
//SoftwareSerial mySerial(RX_PIN, TX_PIN);                   // (Uno example) create device to MH-Z19 serial

unsigned long getDataTimer = 0;

void setup()
{
    Serial.begin(9600); // Device to serial monitor feedback
    Serial1.begin(9600);
    //mySerial.begin(BAUDRATE);                               // (Uno example) device to MH-Z19 serial start  
    //myMHZ19.begin(mySerial); // *Serial(Stream) refence must be passed to library begin().
     myMHZ19.begin(Serial1);
      
    myMHZ19.autoCalibration();                              // Turn auto calibration ON (OFF autoCalibration(false))
}

void loop()
{
    if (millis() - getDataTimer >= 2000)
    {
        int CO2;

        /* note: getCO2() default is command "CO2 Unlimited". This returns the correct CO2 reading even
        if below background CO2 levels or above range (useful to validate sensor). You can use the
        usual documented command with getCO2(false) */

        CO2 = myMHZ19.getCO2();                             // Request CO2 (as ppm)
        
        Serial.print("CO2 (ppm): ");                      
        Serial.println(CO2);                                

        int8_t Temp;
        Temp = myMHZ19.getTemperature();                     // Request Temperature (as Celsius)
        Serial.print("Temperature (C): ");                  
        Serial.println(Temp);                              

        getDataTimer = millis();
    }
}

wiring:

VIN to 5V
GND to GND
TX on 19
RX on 18

The power supply runs via a separate power pack with Imax=2.5A

The result is a correct output of the temperature and permanently 0ppm.

I had previously read the sensor via a PWM signal, which worked, so the sensor should be intact.

Does anyone have an idea why this might be?

User’s Manual:
https://cdn-reichelt.de/documents/datenb...NBLATT.pdf

Libary:

explain pls.
i made network with 20 of MH-Z19C, so i believe to understand something about.
check this out

const  byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
byte response[9]; // for CO2-Sensor answer

void setup() {
  Serial.begin(115200);
  Serial1.begin(9600);
}

void loop() {
  readCO2UART();
  delay(5000);
}

void readCO2UART() {
  Serial1.write(cmd, 9);
  memset(response, 0, 9);
  while (Serial1.available() == 0)delay(500);

  if (Serial1.available() > 0)Serial1.readBytes(response, 9);
  byte checksum = 0;
  for (byte i = 1; i < 8; i++)checksum += response[i];
  checksum = 0xff - checksum + 1;

  if (response[8] == checksum) {
    co2 = 256 * (int)response[2] + response[3];
    Serial.println(co2);
  }
}

Hey kolaha,
thanks for your answer, I'm glad to have found someone who knows about the sensors.

"explain pls.":
when I run the code, I get the measured temperature in the serial monitor, but always 0 ppm as a co2 value.

The reported temperature value is also correct, a bit lower than real temperature but not by much.

My main problem is that I want to turn off the automatic calibration. In the room where the sensor is, there is always far more than 400ppm, which means that after a few days the sensor becomes so inaccurate that it only shows about half of the real value.

As I understand how the sensor works, the automatic calibration can only be switched off via the serial port.

"check this out":
I don't really understand how to use your code. Is it an addition to my code? or would it have to work on its own?

Sorry I've only been programming for a few months, still have a lot to learn

on its own.
but
near to all gas sensors take 2-5 minute to get hot, and after this comes true values. did you wait this time?

Ahh ok I just had to add "int co2;". I've let it run for a bit now and measure it with another Co2 meter at the same time. It seems to be work fine, thanks alot.

Do you know how I can turn off the automatic calibration?

did you read your sketch?

this will disable autocorrection

myMHZ19.autoCalibration(false);

ahh ok but that only works if I include the MHZ19 library again, doesn't it?

I changed your code like this, do you think it works like this? Or is there an easier/better way.

#include "MHZ19.h"  

const  byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
byte response[9]; // for CO2-Sensor answer
int co2;
MHZ19 myMHZ19; 

void setup() {
  Serial.begin(115200);
  Serial1.begin(9600);
  myMHZ19.autoCalibration(false);                              // Turn auto calibration ON (OFF autoCalibration(false))
}

void loop() {
  readCO2UART();
  delay(5000);
}

void readCO2UART() {
  Serial1.write(cmd, 9);
  memset(response, 0, 9);
  while (Serial1.available() == 0)delay(500);

  if (Serial1.available() > 0)Serial1.readBytes(response, 9);
  byte checksum = 0;
  for (byte i = 1; i < 8; i++)checksum += response[i];
  checksum = 0xff - checksum + 1;

  if (response[8] == checksum) {
    co2 = 256 * (int)response[2] + response[3];
    Serial.println(co2);
  }
}

I had the sensor recorded for the last few hours and it showed a value between 400 and 404 up to around 1300pmm, after which it went up.

I guess this is still due to the old calibration. I am now doing a zero point calibration and hope that it will measure normally again afterwards.

Thanks again for your help

yes, upload your modified sketch.

it exist no other "calibration" as current one. if sensor ate some fresh air - it know where 400ppm is. you can disable "autocal" and it remain forever. your fear

that after a few days the sensor becomes so inaccurate..

is banned.

unfortunately it does not work with the integration of the MH-Z19 library. It freeze up at:

myMHZ19.autoCalibration(false);

const  byte cmd[9] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86};

void setup() {
  Serial.begin(115200);
  Serial1.begin(9600);
  Serial1.write(cmd, 9);
  Serial.println("Sent.");
}

void loop() {}

I added the following to my code:




const  byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
byte response[9]; // for CO2-Sensor answer
const  byte ac[9] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86};
int co2;
 

void setup() {
  Serial.begin(115200);
  Serial1.begin(9600);
  Serial1.write(ac, 9);
}

void loop() {
  readCO2UART();
  delay(5000);
}

void readCO2UART() {
  Serial1.write(cmd, 9);
  memset(response, 0, 9);
  while (Serial1.available() == 0)delay(500);

  if (Serial1.available() > 0)Serial1.readBytes(response, 9);
  byte checksum = 0;
  for (byte i = 1; i < 8; i++)checksum += response[i];
  checksum = 0xff - checksum + 1;

  if (response[8] == checksum) {
    co2 = 256 * (int)response[2] + response[3];
    Serial.println(co2);
  }
}

if you have time i would be happy if you could explain me a bit what the code does exactly. That's how I understand it, please correct me if I'm wrong somewhere:

const byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
defines a const byte with the name "cmd[9]" and the information: "{0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}"

byte response[9];
defines a variable byte named "response[9]" and no information for now

const byte ac[9] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86};
defines a const byte with the name "ac[9]" and the information: "{0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86}"

Serial1.begin(9600);
opens serial port 1 with a baud rate of 9600. (do I use the number to set which serial port I need to connect the sensor to or is that just a name?)

Serial1.write(ac, 9);
I send the information I defined in ac[9] to the sensor and thus switch off the automatic calibration. As long as it is supplied with power, the sensor remembers that it should not calibrate automatically.
I don't understand why it's "ac, 9" and not "ac[9]" like the variable byte was named.

readCO2UART();
I'm not quite sure about that, but I think it inserts the "void readCO2UART()" into the "void loop", so I could also copy everything under "void readCO2UART()" into the "void loop" and delet "readCO2UART();". am I thinking right?

Serial1.write(cmd, 9);
sends the information stored in cmd[9] to the sensor. This gives him the command to return a measured value.

memset(response, 0, 9);
the command takes the response from the sensor and stores it in the variable byte "response[9]". Here again I don't know why it is "memset(response, 0, 9)" and not "memset(response[9])".

while (Serial1.available() == 0)delay(500);
checks how many serial interfaces are connected to Serial1, if there are 0 then he waits 500ms and checks again.
That would mean that if a cable comes loose, the entire code freezes. What exactly is the point of this line, couldn't you just omit it?

if (Serial1.available() > 0)Serial1.readBytes(response, 9);
if something is connected to Serial1 then it reads the response via Serial port 1 and stores it in "response[9]". I don't understand the difference between "Serial1.readBytes(response, 9);" and "memset(response, 0, 9);"

byte checksum = 0;
defines a variable byte with the name "checksum" and the information 0

for (byte i = 1; i < 8; i++)checksum += response[i];
checksum = 0xff - checksum + 1;

I don't understand that part

if (response[8] == checksum) {
co2 = 256 * (int)response[2] + response[3];
Serial.println(co2);

if "respons[8]" is equal to "cheksum" .
The co2 value is calculated using the formula.
I know that the formula from the data sheet comes from the sensor, but I don't understand what exactly is being calculated there.

General questions
what's up with the numbers in the square brackets? At first I thought it was just a name, but with "response" the number changes as the code progresses. What do the numbers mean?

MH-Z16.pdf (829.8 KB)
read, maybe you find new information.

and ask only when internet has no answer, i not able to teach you C++.

Thanks for the data sheet, that's a bit more detailed than what I had.

And thanks for your help, it gave me the impetus to learn how the serial connection works. In the end it doesn't look all that complicated.

Have a nice day.

For future readers:
if I ever fully understand the code, I'll answer my own questions when the time comes.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.