Has anybody interfaced the MH-Z19B CO2 sensor

johnlong

Senior Member
Hi All
Ordered a MH-Z19B NDIR CO2 sensor from flea bay
After looking at the data sheet https://www.winsen-sensor.com/d/files/MH-Z19B.pdf
I hooked it up to a 28x2 with the following code, but not recieving any data back
Code:
#no_data
#no_table
symbol byte0=w1
symbol byte1=w2
symbol byte2=w3
symbol byte3=w4
symbol byte4=w5
symbol byte5=w6
symbol byte6=w7
symbol byte7=w8
symbol byte8=w9
symbol checksum=w10
symbol PPM=w11 symbol lsb=b22 symbol msb=b23
symbol Co_braud=T9600
symbol RX=C.2'connected to TX MH-X19B pin6
symbol TX=C.3'connected to RX MH-X19B pin7
'        MH-Z19B NDIR CO2 Module
'            Commands
'        0x86|read CO2 concentration
'        0x87|calibrate zero point (zero)
'        0x88|calibrate span point (span)
'        0x79|on/off self calibrate for zero point
'        0x99| detection range (PPM)
'        Byte8 is the checksum Add together byte1 to byte7(var checksum)
'        checksum= 0xFF-checksum
'        checksum=checksum+1
pre_heat:
    sertxd(" pre heat",cr,lf)
    for b0=0 to 29
        pause 6000
        sertxd("b0= ",#b0,cr,lf)
    next b0
    sertxd("leaving pre heat",cr,lf)
inti:
high tx
pause 5         'by0 by1 by2 by3 by4 by5 by6 by7 by8
serout TX,Co_braud,($FF,$01,$79,$00,$00,$00,$00,$00,$86)'self calibration off
sertxd("self calibration sent",cr,lf)
pause 200
serout TX,Co_braud,($FF,$01,$99,$00,$00,$00,$07,$D0,$8F)'limit PPM to 0 to 2000 via byte2 $99
sertxd("PPM range sent",cr,lf)
pause 200
main:
do
    serout TX,Co_braud,($FF,$01,$86,$00,$00,$00,$00,$00,$79)'read command
    sertxd("read command sent",cr,lf)
    serin RX,Co_braud,(byte0,byte1,PPM,byte3,byte4,byte5,byte6,byte7,byte8)
    gosub verify
    pause 2000
loop

verify:
    sertxd("in verify",cr,lf)
    checksum=checksum+byte1:checksum=checksum+PPM:checksum=checksum+byte3:checksum=checksum+byte4:checksum=checksum+byte5:checksum=checksum+byte6:checksum=checksum+byte7
    checksum=$FF-checksum
    checksum= checksum+1
    if checksum=byte8 then
        sertxd("Read sucessful",cr,lf)
        elseif checksum<>byte8 then
        sertxd("Read fail Csum= ",#checksum," byte8= ",#byte8,cr,lf)
    endif
    PPM=PPM*256+byte3
  
    b1 = PPM / $10 + "0" : If b1 > "9" Then : b1 = b1 + 7 : End If
b0 = PPM & $0F + "0" : If b0 > "9" Then : b0 = b0 + 7 : End If
sertxd(#b1,#b0," PPM",cr,lf)
return
The data sheet state that interface volltage is 5v compatable or should I use a logic level shift converter on RX and TX
I have tried the baud rate as N9600 and T9600 with either not returning values
Or is the code interpritation of the data sheet skew wiff
regards john
 

Buzby

Senior Member
Hi John,

Your code looks OK, are you sure there is no reply ?

Have you got a scope you can use to check that the device is actually replying ?. Because your serin has no timeout, comms can get lost.

Also, I'd comment out all that initialisation stuff, just run the 'main loop', without calling 'verify', just sertxd the bytes to PE for viewing.

( I think your verify routine, although you don't need it to test comms, should zero the checksum before it starts adding bytes up. )

Cheers,

Buzby
 

hippy

Technical Support
Staff member
serin RX,Co_braud,(byte0,byte1,PPM,byte3,byte4,byte5,byte6,byte7,byte8)

A mistake I often make - Using brackets for SERIN when that's for qualifiers. Try -

serin RX,Co_braud, byte0,byte1,PPM,byte3,byte4,byte5,byte6,byte7,byte8


Also, remove the the two # from -

sertxd(#b1,#b0," PPM",cr,lf)

But, from the line above that, "PPM=PPM*256+byte3" it looks like you have a word value but are only outputting a byte hex value.

And I suspect there might be some issues in defining your "byteN" variables as "wN" word variables.
 

PhilHornby

Senior Member
Code:
    serout TX,Co_braud,($FF,$01,$86,$00,$00,$00,$00,$00,$79)'read command
    sertxd("read command sent",cr,lf)
    serin RX,Co_braud,(byte0,byte1,PPM,byte3,byte4,byte5,byte6,byte7,byte8)
It could be that the reply from device has been and gone, before the Serin even gets executed...

Removing the sertxd would help, as would winding up the clock frequency. You may have to switch to hserin or background serial read though ... it just depends how fast this thing responds.

A second USB-Serial adaptor, across the data line, would help you monitor the serial data - using Putty or such like.

EDIT - I don't know if Putty displays 'binary' data, but ISTR that 'Realterm' can.
 

hippy

Technical Support
Staff member
I'm with Buzby; it's probably best to start with simple code which only reports what is received and go from there. Taking note of what PhilHornby says -
Code:
#Picaxe 28X2
#Terminal 19200
#No_Data
#No_Table

Symbol TX   = C.3
Symbol RX   = C.2
Symbol BAUD = T9600_16

PowerOnReset:
  SetFreq M16
  High TX
  Pause 4000
  SerTxd( "Started", CR, LF )
  SerTxd( "B0 B1 B2 B3 B4 B5 B6 B7 B8", CR, LF )

MainLoop:
  Do
    ;                 B0   B1   B2  B3  B4  B5  B6  B7  B8
    ;                 Strt Rsvd Cmd -   -   -   -   -   Chk
    ;                 $FF  $01  $86 $00 $00 $00 $00 $00 $79
    SerOut TX, BAUD,( $FF, $01, $86,$00,$00,$00,$00,$00,$79 )

    ;                 B0   B1   B2  B3  B4  B5  B6  B7  B8
    ;                 Strt Cmd  PPM PPM -   -   -   -   Chk
    SerIn  RX, BAUD,  b0,  b1,  b2, b3, b4, b5, b6, b7, b8

    b9 = b0
    For bPtr = 0 To 8
      b9 = b9 - @bPtr
      Gosub ShowHexAtBPtr
    Next

    If b9 = 0 Then
      SerTxd( "Okay " )
    Else
      SerTxd( "Checksum FAIL " )
      bPtr = 9
      Gosub ShowHexAtBPtr
    End If

    If b0 <> $FF Then
      SerTxd( "Invalid start ")
    End If
    If b1 <> $86 Then
      SerTxd( "Invalid command ")
    End If
   
    w0 = b2 * 256 + b3
    SerTxd( "PPM = ", #w0 )

    SerTxd( CR, LF )
    Pause 2000  
  Loop

ShowHexAtBPtr:
  b11 = @bPtr / $10 + "0" : If b11 > "9" Then : b11 = b11 + 7 : End If
  b12 = @bPtr & $0F + "0" : If b12 > "9" Then : b12 = b12 + 7 : End If
  SerTxd( b11, b12, " " )
  Return
 

PhilHornby

Senior Member
It could be that the reply from device has been and gone, before the Serin even gets executed...
If that does turn out to be the issue, you can use hserout instead of serout to get the serin started a little sooner.

(hserout is slighter asynchronous, in that in completes while the penultimate character is still being transmitted (because of the hardware buffer). It's only 1mS, or so @ 9600 baud, but every little helps!)
 

johnlong

Senior Member
Hi Folks
Thanks for the input used your code Hippy it worked fine just had to alter the Baud to T9600_8 and the frequancy to M8 and data started to flood in23659
seems to start ok then a constant 2000ppm maybe it needs a longer pause between reads or are the rx, tx pins really 5v to;erant
nice to know that it runs at the 28x2 default frequancy
regards
john
 

johnlong

Senior Member
Hi Hippy
If you have time can you explain your handling of the checksum
there data sheet shows
Byte8 is the checksum Add together byte1 to byte7(b1:b7)
' checksum= 0xFF-checksum
' checksum=checksum+1
where yours $FF minus all the other values to obtain 0 with out adding the 1 to the value
how does this relate to the value recieved byte8 (checksum) from the unit as a verification that
the sent data is correct
sorry for being dense I know you will have a simple explination
regards
john
 

hippy

Technical Support
Staff member
Thanks for the input used your code Hippy it worked fine just had to alter the Baud to T9600_8 and the frequancy to M8 and data started to flood in
Sorry about that - I managed to screw up my baud rates up rather badly there. I was trying to ensure that SERIN ran as soon after SEROUT as possible.

Glad you managed to fix things and happy to hear it works at default frequencies.

seems to start ok then a constant 2000ppm maybe it needs a longer pause between reads or are the rx, tx pins really 5v to;erant
That is a bit odd. I'm not convinced that it's down to how quickly you are reading things, would have more suspected it may be an error in documentation, from your results ...
Code:
B0 B1 B2 B3 B4 B5 B6 B7 B8   PPM

FF 86 01 9A 3C 00 00 00 A3 = 401
FF 86 07 D0 3C 00 00 00 67 = 2000
The datasheet says the data is in B2 and B3, and example code elsewhere does suggest that is the case, but I suspected B2 was something else, perhaps similar to the 'reserved' byte in what's sent, and the actual data is in B4 (MSB) and B3 (LSB).

9A 3C = $3C9A = 15514
D0 3C = $3CD0 =15568

Those are much more consistent numbers with a smallish difference which could be put down to 'settling in' or 'warming up'. They are quite high though.

So maybe it is B2 (MSB) and B3 (LSB) as odd as that seems.

Also, B4 appears to be temperature+40, so if it's 20C where you were, which seems quite likely, it also seems likely the data is as you are seeing it.

Some code I looked at suggested B5 should go to $40 when the status is OK, so maybe you need to leave it running longer, until it properly warms up, to see if B5 does go to $40 - Though that may only apply to the "MH-Z19" with the "MH-Z19B" only ever outputting status $00.

https://revspace.nl/MHZ19

If you have time can you explain your handling of the checksum
The checksum algorithm in the datasheet seems insanely complicated to me. It's basically -

$FF - ( B1 + B2 + B3 + B4 + B5 + B7 ) + 1

Rearrange that and it's -

$FF + 1 - ( B1 + B2 + B3 + B4 + B5 + B7 )

$100 - ( B1 + B2 + B3 + B4 + B5 + B7 )

And as we're only using a byte, the result truncated with an "& $FF", that $100 may just as well be $00, zero -

0 - ( B1 + B2 + B3 + B4 + B5 + B7 )

0 + ( -B1 + -B2 + -B3 + -B4 + -B5 + -B7 )

0 - B1 - B2 - B3 - B4 - B5 - B7

My loop happens to include B0, but we can include that, knowing B0 - B0 = 0

B0 - B0 - B1 - B2 - B3 - B4 - B5 - B7

And, once we've done that the result should equal B8, so if we take B8 off that as well, the result will be zero if the checksum matches.

B0 - B0 - B1 - B2 - B3 - B4 - B5 - B7 == B8

B0 - B0 - B1 - B2 - B3 - B4 - B5 - B7 - B8 == 0

So, end result, set our b9 to B0 then subtract the B0 through B8 values from that in turn, see if b9 does equal zero.
 
Last edited:

johnlong

Senior Member
Hi All
Thanks Hippy for your breakdown of the checksum
Tried connecting it up via the terminal connections but just recieved 00's invalid command from that
so reverted back to the pins that I soldered on and recieved the following
Started
B0 B1 B2 B3 B4 B5 B6 B7 B8
FF 86 01 9A 00 00 00 00 DF Okay PPM = 410
FF 86 01 9A 41 00 00 00 9E Okay PPM = 410
FF 86 01 9A 41 00 00 00 9E Okay PPM = 410
FF 86 07 D0 40 00 00 00 63 Okay PPM = 2000
FF 86 07 D0 40 00 00 00 63 Okay PPM = 2000
FF 86 07 D0 40 00 00 00 63 Okay PPM = 2000
FF 86 07 D0 3F 00 00 00 64 Okay PPM = 2000
I have emailed the manufactorer out lining the issues with it and waiting for a response
In the mean time I think I will solder a pin on the HD and do a hard zero recalibration to see it helps
or destroys it
Have just reloaded it and b6 is returning 02 after 10 reads 03 another question for the manufactor me thinks
from there data sheet
About zero point calibration:This module has three methods for zero point calibration:hand-operated method, sending command method and self-calibration. All the zero point is at 400ppm CO2.Hand-operated method: Connect module’s HD pin to low level(0V), lasting for 7 seconds at least. Before calibrating the zero point, pleaseensure that the sensor is stable for more than 20 minutes at 400ppm ambient environment.

regards john
 
Last edited:

hippy

Technical Support
Staff member
It seems others have seen similar issues, one example -


It's probably only something the manufacturer can answer.

My only thought is that it's initially showing its default/calibrated "400" then, having warmed up, it switches to an actual PPM and that's what it is, though one would expect it to vary rather then remain constant. Perhaps try breathing on it seeing if it does change !
 
Last edited:

johnlong

Senior Member
Hi Hippy
It does seem problematic it after the link you have posted I have it stuck inside a carbboard box
it has not altered the values recieved even tried placeing 150R resistors on rx and tx this gave all $FF across all
bytes except the checksum A5 byte6 returned to sending F00.
I think another nudge to manufacturer is required, unless they are staying home as a result of kung flu
its strange times for us all
regards
john
 
Top