Corruption Seria1.readBytes() - first byte received

Hi I thought I would post my observations on writing/reading to Serial1.
I’m using Mkrfox1200 board and using the hardware serial1 port to send and receive commands to Next-PM airflow particles sensor.

It seems I have encountered a race condition in what I thought should asynchronously transfer written bytes and then wait and receive the response.

The Serial1 interface is running at 115200 BAUD

The problem I encountered is that sometimes immediately after sending a write command of 5 bytes and then attempting to read a 16 byte response - serial.readBytes( buffer, 16). In the returned buffer, first bit of the first character gets corrupted.

After much head scratching and testing I have implemented the following solution:

  int bytes_received = 0;

  // Send cmd bytes and wait for response
  Serial1.write(cmd, cmdlen);
  Serial1.flush();	// ..and wait for all bytes to be transmitted

  int timeout = 0;
  do {
	  delay(1);
	  if( ++timeout > 1000) { // break infinite loop wait > 1000ms
		  break;
	  }
  } while (Serial1.available()==0);

  if (timeout < 1000) {
	  Serial.print("PM responded in msecs="); Serial.println(timeout);

	  // get serial response
	  memset(&s2recvbuf, 0, sizeof(s2recvbuf));

	  // Wait for recv buffer and read len bytes (or default timeout 1000ms)
	  bytes_received = Serial1.readBytes(s2recvbuf, recvLen);
	  for(int i=0;i<recvLen;i++)
	  {
		if (s2recvbuf[i]<0x10) Serial.print("0");
		Serial.print(s2recvbuf[i],HEX);
	  }
	  Serial.println();

	  // Sanity check, in case we have more bytes returned than expected
	  int extra = 0;
	  while ( Serial1.available()) {
		  Serial1.read();
		  extra++;
	  }
	  if (extra > 0) {
		  Serial.print("Unexpected extra chars="); Serial.println(extra);
	  }
}

Using Seria1.flush() - I believe waits for the serial transmit write to finish. Then the do {} while( Serial1.available()==0) will delay at least 1ms. Note: at 115200 BAUD -16 bytes takes 1.1ms.

So far so good :wink:

Have a look at the examples in Serial Input Basics - simple reliable non-blocking ways to receive data. There is also a parse example to illustrate how to extract numbers from the received text.

It's not a good idea to use time to mediate serial data transmission - much better to use the data itself - for example waiting for an end-marker.

...R

Yes I agree using time delay is a sub-optimal solution.

I think there may be a bug in the SAMD, SERCOM overlapped transmit/receive implementation ?.. because without this delay(1) the first bit of the first received char gets corrupted when using this code:

  // Send cmd bytes
  Serial1.write(cmd, cmdlen);

  int timeout = 0;
  // Wait for response
  while (Serial1.available()==0) {
  	  if( ++timeout > 1000) { // break infinite loop wait > 1000ms
	     break;
          delay(1);
  }
 
  if (timeout < 1000) {
	  Serial.print("PM responded in msecs="); Serial.println(timeout);

	  // get serial response
	  memset(&s2recvbuf, 0, sizeof(s2recvbuf));

	  // Wait for recv buffer and read len bytes (or default timeout 1000ms)
	  bytes_received = Serial1.readBytes(s2recvbuf, recvLen);
	  for(int i=0;i<recvLen;i++)
	  {
		if (s2recvbuf[i]<0x10) Serial.print("0");
		Serial.print(s2recvbuf[i],HEX);
	  }
	  Serial.println();
   }

Went the read corruption occurs the - while (Serial1.available()==0) loop is not entered