Electronic – arduino – MH-Z19 CO2 sensor giving diferent values using UART and PWM

arduinopwmsensoruart

I have a MH-Z19 CO2 sensor, according the data-sheet I can obtain PPM values via UART and PWM. The difference between both method is the limit in the CO2 concentration, where UART I should have readings between 0-5000ppm, and via PWM I should have readings between 0-2000ppm.

I just wanted to know if I can have the same readings using both methods, but after tried I'm realized that I'm having different values.

I'm using the following code to test:

#include <SoftwareSerial.h>
SoftwareSerial mySerial(A0, A1); // RX, TX

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

#define pwmPin 10
int prevVal = LOW;
long th, tl, h, l, ppm, ppm2 = 0.0;



void setup() {
    Serial.begin(9600); 
    mySerial.begin(9600); 
    pinMode(pwmPin, INPUT);
}

void loop(){
    mySerial.write(cmd,9);
    mySerial.readBytes(response, 9);
    int responseHigh = (int) response[2];
    int responseLow = (int) response[3];
    ppm = (256*responseHigh)+responseLow;


    //CO2 via pwm
    do {
        th = pulseIn(pwmPin, HIGH, 1004000) / 1000.0;
        tl = 1004 - th;
        ppm2 = 2000 * (th-2)/(th+tl-4);
    } while (ppm2 < 0.0);

    Serial.println(ppm);
    Serial.println(ppm2);
    Serial.println("-----------");
    delay(5000);
}

The readings that I'm getting are the foolowing:

643
356
-----------
643
356
-----------

Any idea what could be going on here? Should I suspect that the sensor is damaged? Or, I'm doing something wrong?

Thanks and best regards.

BTW: The test is done with an Arduino Pro Mini ATMega 328, 3.3v, 8Mhz. The sensor is being powered by 5v from a NodeMCU (lolin).

Best Answer

char response[9]; 

On many systems including an Arduino, an unspecified char is a signed type.

int responseHigh = (int) response[2];
int responseLow = (int) response[3];
ppm = (256*responseHigh)+responseLow;

Therefore when you cast it to an int, any value value with the highest bit set will be considered negative and sign extended to a negative value in the full integer width. So for example an 8-bit char of 0x80 becomes a 16-bit int 0xff81. This is probably not what you want, as it will actually reduce rather than increase the value set by the upper bits.

Instead, since your values are unsigned you should either declare your array as unsigned char or cast the values to an unsigned char before you implicitly or explicitly cast them to a wider type.

Note that even if the overall value were signed, you would probably still want to be interpreting the lower 8 bit as an unsigned modifier of the signed upper 8 bits.

However, it is not immediately apparent that this change will resolve the discrepancy in your readings - it may actually increase it.

th = pulseIn(pwmPin, HIGH, 1004000) / 1000.0;

There does not seem to be any sound reason to use a floating point constant when dividing an integer type to produce an integer result.

You should probably print out your PWM times and see if they make sense, compared to say scope measurements.

Also ideally you would have some other reference sensor you could use to determine which value is more credible.