Oh?
Anyway, below is a function to reliably read the input from your sensor; it handles timeouts in the reception as well as checksum errors. It's non-blocking an you have to call it repeatedly. It does NOT convert the data to distance as that is not part of receiving data.
The part of the code befor setup()
// Include the Software Serial library
#include <SoftwareSerial.h>
// Define connections to sensor
int pinRX = 10;
int pinTX = 11;
// Array to store incoming serial data
const uint8_t messageSize = 4;
unsigned char data_buffer[messageSize];
// Integer to store distance
int distance = 0;
int lastDistance = 0;
// Object to represent software serial port
SoftwareSerial mySerial(pinRX, pinTX);
// receive timeout
const uint32_t a02yuwwTimeout = 400;
// header byte
const uint8_t headerByte = 0xff;
// possible results of a02yuww receive function
enum class RECEIVERESULT
{
COMPLETE, // data available
WAITING, // wait for header
INPROGRESS, // receive in progress (header received but not yet required number of bytes)
CSERROR, // checksum error
TIMEOUT, // timeout
};
RECEIVERESULT specifies the values that the function can return.
a02yuwwTimeout is the timeout; once a header byte is received, each databyte should arrive within 400ms of the previous one. You can make this shorter if you feel that that is better.
The function is loosely based on Robin's updated serial input basics topic.
/*
read a02yuww sensor
Returns:
receive status (RECEIVERESULT)
*/
RECEIVERESULT readSensor()
{
// flag to indicate if header byte was received
static bool receiveInProgress = false;
// where to store received byte
static uint8_t index = 0;
// time that last byte was received
static uint32_t lastReceivedTime;
// clear the buffer if we haven't received the header byte
if (receiveInProgress == false)
{
memset(data_buffer, 0, sizeof(data_buffer));
}
// if there is no sensor byte
if (mySerial.available() == 0)
{
// indicate that we don't have a reading yet
if (receiveInProgress == false)
{
return RECEIVERESULT::WAITING;
}
else
{
// if we did not get the next byte in time
if (millis() - lastReceivedTime > a02yuwwTimeout)
{
// reset local variables
receiveInProgress = false;
index = 0;
return RECEIVERESULT::TIMEOUT;
}
else
{
return RECEIVERESULT::INPROGRESS;
}
}
}
// read an available byte and store in buffer
data_buffer[index++] = mySerial.read();
lastReceivedTime = millis();
// check for packet header character 0xff
if (receiveInProgress == false)
{
if (data_buffer[0] == headerByte)
{
// indicate that we've got the header byte
receiveInProgress = true;
return RECEIVERESULT::INPROGRESS;
}
// reset index if it was not the header byte
index = 0;
return RECEIVERESULT::WAITING;
}
// if we're waiting for the other bytes
if (receiveInProgress == true)
{
// if we haven't received all bytes yet
if (index == messageSize)
{
// clear local variables
index = 0;
receiveInProgress = false;
// calculate checksum
uint8_t cs = data_buffer[0] + data_buffer[1] + data_buffer[2];
// if checksum is valid
if (data_buffer[3] == cs)
{
return RECEIVERESULT::COMPLETE;
}
else
{
return RECEIVERESULT::CSERROR;
}
}
else
{
return RECEIVERESULT::INPROGRESS;
}
}
return RECEIVERESULT::WAITING;
}
I have put comments in to make it clear what/why it is doing certain steps. If something is not clear, please let me know.
There is a helper function to print the result; the results are just integers but some human readible text is easier to follow.
/*
print human readible RECEIVERESULT
In:
receive result
*/
void printReceiveResult(RECEIVERESULT r)
{
switch (r)
{
case RECEIVERESULT::COMPLETE:
Serial.println(F("Complete"));
break;
case RECEIVERESULT::WAITING:
Serial.println(F("Waiting for header"));
break;
case RECEIVERESULT::INPROGRESS:
Serial.println(F("Receive in progress"));
break;
case RECEIVERESULT::CSERROR:
Serial.println(F("...........................................Checksum error"));
break;
case RECEIVERESULT::TIMEOUT:
Serial.println(F("...........................................Timeout"));
break;
}
}
Now in loop(), you have to call this repeatedly without the use of delay() anywhere.
void loop()
{
void loop()
{
// remember previous result of readSensor
static RECEIVERESULT lastRR;
// remember last time that we succesfully received a packet
static uint32_t lastTime;
// read the a02yuww sensor
RECEIVERESULT rr = readSensor();
// if there was a change in the result
if (rr != lastRR)
{
printReceiveResult(rr);
}
lastRR = rr;
if (rr == RECEIVERESULT::COMPLETE)
{
// time that data was received
uint32_t receivedTime = millis();
// print the received data
printData();}
}
}
loop() keeps on calling the function and evaluating the result. The variable lastRR is used to prevent the serial monitor from being spammed with messages; only if there is a change in the receive result, a message will be printed. If the function returned RECEIVERESULT::COMPLETE), we will print the received data for debugging purposes using the below function. The output is in HEX
/*
print received data
*/
void printData()
{
for (uint8_t cnt = 0; cnt < sizeof(data_buffer); cnt++)
{
if (data_buffer[cnt] < 0x10)
{
Serial.print(F("0"));
}
Serial.print(data_buffer[cnt], HEX);
Serial.print(F(" "));
}
Serial.println();
}
Now you can calculate the distance (after the printData() in loop() and calculate the speed. Below the complete loop()
void loop()
{
// remember previous result of readSensor
static RECEIVERESULT lastRR;
// remember last time that we succesfully received a packet
static uint32_t lastTime;
// read the a02yuww sensor
RECEIVERESULT rr = readSensor();
// if there was a change in the result
if (rr != lastRR)
{
printReceiveResult(rr);
}
lastRR = rr;
if (rr == RECEIVERESULT::COMPLETE)
{
// time that data was received
uint32_t receivedTime = millis();
// print the received data
printData();
// calculate distance
distance = (data_buffer[1] << 8) + data_buffer[2];
Serial.print(F("\tDistance = "));
Serial.print(distance);
Serial.print(F(" / "));
Serial.println(lastDistance);
Serial.print(F("\tDelta distance = "));
Serial.println(distance - lastDistance);
Serial.print(F("\tTime = "));
Serial.print(receivedTime);
Serial.print(F(" / "));
Serial.println(lastTime);
Serial.print(F("\tDelta time = "));
Serial.println(receivedTime - lastTime);
// at overflow of the timing (49 days)
if (receivedTime < lastTime)
{
Serial.println(F("Timing overflow; packet ignored"));
}
else
{
long speed = (distance - lastDistance) * 1000L / (long)(receivedTime - lastTime);
Serial.print(F("\tSpeed = "));
Serial.println(speed);
}
lastDistance = distance;
lastTime = receivedTime;
}
}
The full code below; the only difference in my testing was that I used Serial1 on a Leonardo and not SoftwareSerial. I could not find which board you're using, if it is a Mega, Leonardo, Micro or ProMicro I suggest that you use a hardware serial port instead of software serial.
// Include the Software Serial library
#include <SoftwareSerial.h>
// Define connections to sensor
int pinRX = 10;
int pinTX = 11;
// Array to store incoming serial data
const uint8_t messageSize = 4;
unsigned char data_buffer[messageSize];
// Integer to store distance
int distance = 0;
int lastDistance = 0;
// Object to represent software serial port
SoftwareSerial mySerial(pinRX, pinTX);
// for sterretje's setup, comment out above and uncomment below
//#define mySerial Serial1
// receive timeout
const uint32_t a02yuwwTimeout = 400;
// header byte
const uint8_t headerByte = 0xff;
// possible results of a02yuww receive function
enum class RECEIVERESULT
{
COMPLETE, // data available
WAITING, // wait for header
INPROGRESS, // receive in progress (header received but not yet required number of bytes)
CSERROR, // checksum error
TIMEOUT, // timeout
};
void setup()
{
// Set up serial monitor
Serial.begin(115200);
// Set up software serial port
mySerial.begin(9600);
}
void loop()
{
// remember previous result of readSensor
static RECEIVERESULT lastRR;
// remember last time that we succesfully received a packet
static uint32_t lastTime;
// read the a02yuww sensor
RECEIVERESULT rr = readSensor();
// if there was a change in the result
if (rr != lastRR)
{
printReceiveResult(rr);
}
lastRR = rr;
if (rr == RECEIVERESULT::COMPLETE)
{
// time that data was received
uint32_t receivedTime = millis();
// print the received data
printData();
// calculate distance
distance = (data_buffer[1] << 8) + data_buffer[2];
Serial.print(F("\tDistance = "));
Serial.print(distance);
Serial.print(F(" / "));
Serial.println(lastDistance);
Serial.print(F("\tDelta distance = "));
Serial.println(distance - lastDistance);
Serial.print(F("\tTime = "));
Serial.print(receivedTime);
Serial.print(F(" / "));
Serial.println(lastTime);
Serial.print(F("\tDelta time = "));
Serial.println(receivedTime - lastTime);
// at overflow of the timing (49 days)
if (receivedTime < lastTime)
{
Serial.println(F("Timing overflow; packet ignored"));
}
else
{
long speed = (distance - lastDistance) * 1000L / (long)(receivedTime - lastTime);
Serial.print(F("\tSpeed = "));
Serial.println(speed);
}
lastDistance = distance;
lastTime = receivedTime;
}
}
/*
read a02yuww sensor
Returns:
receive status (RECEIVERESULT)
*/
RECEIVERESULT readSensor()
{
// flag to indicate if header byte was received
static bool receiveInProgress = false;
// where to store received byte
static uint8_t index = 0;
// time that last byte was received
static uint32_t lastReceivedTime;
// clear the buffer if we haven't received the header byte
if (receiveInProgress == false)
{
memset(data_buffer, 0, sizeof(data_buffer));
}
// if there is no sensor byte
if (mySerial.available() == 0)
{
// indicate that we don't have a reading yet
if (receiveInProgress == false)
{
return RECEIVERESULT::WAITING;
}
else
{
// if we did not get the next byte in time
if (millis() - lastReceivedTime > a02yuwwTimeout)
{
// reset local variables
receiveInProgress = false;
index = 0;
return RECEIVERESULT::TIMEOUT;
}
else
{
return RECEIVERESULT::INPROGRESS;
}
}
}
// read an available byte and store in buffer
data_buffer[index++] = mySerial.read();
lastReceivedTime = millis();
// check for packet header character 0xff
if (receiveInProgress == false)
{
if (data_buffer[0] == headerByte)
{
// indicate that we've got the header byte
receiveInProgress = true;
return RECEIVERESULT::INPROGRESS;
}
// reset index if it was not the header byte
index = 0;
return RECEIVERESULT::WAITING;
}
// if we're waiting for the other bytes
if (receiveInProgress == true)
{
// if we haven't received all bytes yet
if (index == messageSize)
{
// clear local variables
index = 0;
receiveInProgress = false;
// calculate checksum
uint8_t cs = data_buffer[0] + data_buffer[1] + data_buffer[2];
// if checksum is valid
if (data_buffer[3] == cs)
{
return RECEIVERESULT::COMPLETE;
}
else
{
return RECEIVERESULT::CSERROR;
}
}
else
{
return RECEIVERESULT::INPROGRESS;
}
}
return RECEIVERESULT::WAITING;
}
/*
print human readible RECEIVERESULT
In:
receive result
*/
void printReceiveResult(RECEIVERESULT r)
{
switch (r)
{
case RECEIVERESULT::COMPLETE:
Serial.println(F("Complete"));
break;
case RECEIVERESULT::WAITING:
Serial.println(F("Waiting for header"));
break;
case RECEIVERESULT::INPROGRESS:
Serial.println(F("Receive in progress"));
break;
case RECEIVERESULT::CSERROR:
Serial.println(F("...........................................Checksum error"));
break;
case RECEIVERESULT::TIMEOUT:
Serial.println(F("...........................................Timeout"));
break;
}
}
/*
print received data
*/
void printData()
{
for (uint8_t cnt = 0; cnt < sizeof(data_buffer); cnt++)
{
if (data_buffer[cnt] < 0x10)
{
Serial.print(F("0"));
}
Serial.print(data_buffer[cnt], HEX);
Serial.print(F(" "));
}
Serial.println();
}