Problem with MH-Z19 serial communication with arduino mega

I am using MH-Z19 NDIR sensor with my arduino mega. I write a command through serial 0 and get the response.

//CO2 from MH-Z19 NDIR
		byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
		byte response[9];
		 Serial.write(cmd,9);
		 Serial.readBytes(response,9);

		 if (response[0] != 0xFF)
		  {
			 //Error
			 predictCo2 = 0;

		  }

		 else if (response[1] != 0x86)
		  {
			 //Error
			 predictCo2 = 1;

		  }

		  else{

			  predictCo2=((response[2]*256)+response[3]);

		  }

I expect the output to be in a particular format. response[0] has to be 0xFF and response[1] has to be 0x86.

I am having issues getting the output in the above mentioned format occasionally. But, sometimes it starts reading in the proper format straightaway.

Once its starts reading in the proper format, it never goes back to the error state unless I power it off. If I power the device off, I will have to plug out plug in multiple times until I see the values in proper format.

I would like to know if I am missing something in the code here. Should I be using serial.flush() somewhere in the code to clean the serial stream before I send and receive data? Or will this be a problem with the sensor itself?

I expect the output to be in a particular format. response[0] has to be 0xFF and response[1] has to be 0x86.

More than that, you expect a response NOW! That REALLY doesn't seem likely.

If you KNOW that the response is to be 9 bytes, wait for 9 bytes to be available before reading. I see nothing that makes 9 a reasonable value to expect, though.

Should I be using serial.flush() somewhere in the code to clean the serial stream

Only if you KNOW which stream it clears AND clearing that stream is reasonable.

What you should be doing is reading data, as it arrives, discarding data until you get the 0xFF that indicates the start of a packet. When that arrives, start another loop reading and storing serial data as it arrives, until the end of the packet arrives (or a reasonable amount of time has passed without a valid response arriving). Then, use the stored data as appropriate.

Hi,

I tried waiting till there are 9 bytes available. Here is the updated code.

             byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
 byte response[9];
 Serial.flush();
 Serial.write(cmd,9);
 
 while (Serial.available()<9) {}
 
                 for(int n=0; n<9; n++)
 {  response[n] = Serial.read(); 
 }



 if (response[0] != 0xFF)
 { //Error
 predictCo2 = 0;
      }
 else if (response[1] != 0x86)
      {
   //Error
   predictCo2 = 1;

      }
    else{

  predictCo2=((response[2]*256)+response[3]);

      }

Again, the behavior is similar to the previous code I used. ie, the value is 0 when I power it on (error) Once it starts reading predictCo2 without error, it never goes back to the error state.

Just figured out something.

The sensor starts reading proper values if I do an arduino reset (using the reset button).

Wondering why it is behaving that way? Is it clearing the serial buffer? Is it possible to do a similar operation through software? I had already included serial.flush() before writing commands to serial stream, but that didn't help.

I had already included serial.flush() before writing commands to serial stream, but that didn't help.

Do you KNOW what flush() does? If not, why did you think that it might be a magic bullet?

My guess as to what is happening is that you read 9 bytes, and expect the first one to be the start of packet marker. If it isn't, you throw away those 9 bytes, and read the next 9, again expecting that you got a complete packet, and again being disappointed.

What you need to do is read every byte as it becomes available. If the byte IS the start of a packet, wait for 8 more bytes to be available, and then read them. If the byte is NOT the start of a packet, throw it away.

Maybe have a look at the examples in Serial Input Basics - simple reliable ways to receive data. There is also a parse example.

You will need to adapt it a little if you want to receive byte values rather than char values.

If the data you are trying to receive uses 0xFF as a start-marker but does not have any end-marker I suggest you use the concept in the second example and treat 0xFF as the end-marker. It will mean that you lose the first message but you should then get all the rest without any trouble.

...R