A02YUWW sensor for speed measurement

This is the link I was following to understand how to get the sensor working, as I have pretty basic knowledge in Arduino ide, so couldn't understand how to calculate speed with that sensor, I was previously using hcsr04 by which I could easily calculate speed of a moving object .

I want to modify a tank level control by calculating the speed of increasing or decreasing of liquid level , any help is much appreciated.

Your topic has been moved to a more suitable location on the forum. Installation and Troubleshooting is not for problems with (nor for advice on) your project.

1 Like

Seeing that you're trying to post code, the easiest is

  1. in the IDE, use tools -> auto format to properly format your code
  2. in the IDE, use edit -> copy for forum to place the sketch on the clipboard
  3. paste here in a reply and save the reply

Thanx mate, this forum seems more helpful than i thought

// Include the Software Serial library
#include <SoftwareSerial.h>
#include <Ultrasonic.h>

// Define connections to sensor
int pinRX = 10;
int pinTX = 11;

// Array to store incoming serial data
unsigned char data_buffer[4] = {0};

// Integer to store distance
int distance = 0;

// defines variables
long duration;
int distance1 = 0;
int distance2 = 0;
double Speed = 0;


// Variable to hold checksum
unsigned char CS;

// Object to represent software serial port
SoftwareSerial mySerial(pinRX, pinTX);

void setup() {
  // Set up serial monitor
  Serial.begin(115200);
  // Set up software serial port
  mySerial.begin(9600);
}


void loop()
{
  // Run if data available
  if (mySerial.available() > 0) {

    delay(4);

    // Check for packet header character 0xff
    if (mySerial.read() == 0xff) {
      // Insert header into array
      data_buffer[0] = 0xff;
      // Read remaining 3 characters of data and insert into array
      for (int i = 1; i < 4; i++) {
        data_buffer[i] = mySerial.read();
      }

      //Compute checksum
      CS = data_buffer[0] + data_buffer[1] + data_buffer[2];
      // If checksum is valid compose distance from data
      if (data_buffer[3] == CS) {
        distance = (data_buffer[1] << 8) + data_buffer[2];
        // Print to serial monitor
        Serial.print("distance: ");
        Serial.print(distance);
        Serial.println(" mm");
      }
      { //calculating Speed
        distance1 = (data_buffer[1] << 8) + data_buffer[2];

        delay(1000);//giving a time gap of 1 sec

        distance2 = (data_buffer[1] << 8) + data_buffer[2];

        //formula change in distance divided by change in time
        Speed = (distance2 - distance1) / 1000; //as the time gap is 1 sec we divide it by 1.

        //Displaying Speed
        Serial.print("Speed in cm/ms  :");
        Serial.println(Speed);
      }
    }
  }
}

I am using this code though it is not showing any error but only showing speed 0cm/ms .can u help? @sterretje

The sensor page is not showing up for me.

This should work better.

In the datasheet, this is written:

So the first byte (of a packet) from the sensor will always be 0xFF. The second byte is the high-byte (left-part of a 16 byte) and the third byte is the low byte (the right part).
You would put the data together by doing:

uint16_t CalculatedOutput; //must be at least 16-bit
//0xHHHHHHHHLLLLLLLL
CauclatedOutput = (hiByte << 8) /*left shift by 8 bit*/ + loByte;

The Checksum is used to verify the integrity of the data. For simplicity we can ignore this for now.

If you are using an Arduino UNO, you will also need to use SoftwareSerial as the hardware serial is tied to the USB-Serial converter.

...

Basically, if you do everything according to the sensor sheet (wiring diagram, copy-paste code, etc) you should be able to get the data.

I am getting data for distance but what i need is measurement of speed. @cdr_xavier

You are reading the sensor only once in loop and hence your data will not change. You will need to read it a second time. The way it's usually done is with a variable called e.g. lastDistance or previousDistance.

  1. Read sensor and calculate distance; store distance in a variable.
  2. Subtract distance from lastDistance to calculate distance difference and divide by time; display speed.
  3. Copy calculated distance (step (1)) to lastDistance.

Rinse and repeat

I do not have much time till Sunday or Monday, try it yourself first.

1 Like

If you want it to function like a radar gun, it's not going to work.

You can take two measurements across a certain amount of time, and compute the difference to get speed.
Speed = displacement/time

This will only work for movement toward/away from the sensor, but should be good enough for most purposes.

thanx for giving a way out

Yes I intend to do that computation across different time of liquid level,but unable to write the code,help is much appreciated

The below only covers the basics that I described before.

// Include the Software Serial library
#include <SoftwareSerial.h>

// Define connections to sensor
int pinRX = 10;
int pinTX = 11;

// Array to store incoming serial data
unsigned char data_buffer[4] = { 0 };

// Integer to store distance
int distance = 0;
int lastDistance = 0;

// Variable to hold checksum
unsigned char CS;

// Object to represent software serial port
SoftwareSerial mySerial(pinRX, pinTX);

void setup() {
  // Set up serial monitor
  Serial.begin(115200);
  // Set up software serial port
  mySerial.begin(9600);
}


void loop() {

  // Run if data available
  if (mySerial.available() > 0) {

    // Check for packet header character 0xff
    if (mySerial.read() == 0xff) {
      // Insert header into array
      data_buffer[0] = 0xff;
      // Read remaining 3 characters of data and insert into array
      for (int i = 1; i < 4; i++) {
        data_buffer[i] = mySerial.read();
      }

      //Compute checksum
      CS = data_buffer[0] + data_buffer[1] + data_buffer[2];
      // If checksum is valid compose distance from data
      if (data_buffer[3] == CS) {
        distance = (data_buffer[1] << 8) + data_buffer[2];
        // Print to serial monitor
        Serial.print("distance: ");
        Serial.print(distance);
        Serial.println(" mm");

        float speed = (lastDistance - distance) / 1000;

        //Displaying Speed
        Serial.print("Speed in cm/s  :");
        Serial.println(speed);

        // update lastDistance
        lastDistance = distance;
        delay(1000);
      }
    }
  }
}

It more than likely requires polishing / hardening. I would probably continuously read the sensor and only calculate a speed if one second has lapsed; so no use of delay but use a millis() based timing to know when one second has passed.

Code was initially not working but as I added one line

delay(4);
 
    // Check for packet header character 0xff
    if (mySerial.read() == 0xff) {

Now it is working for distance, but speed is showing in 1 in 20 ocassion.

But thanx for helping out, it means a lot for me

I did remove that as I usually don't use delay. The way you read the sensor, you will have to make sure that you received the 4 bytes before reading them. A better approach to guarantee that there are 4 bytes to read would have been

if (mySerial.available()  >= 4) {

What does that mean? If you have read 4 bytes and no checksum error, distance and speed should always show.

It is showing distance accurately but when I move any object in random speed in front of the sensor it just showing 0cm/sec ,and sometimes it showing -1cm/sec,which is very rare

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();
}
1 Like

I do not have your sensor, so wrote a rough simulation code for another Arduino that I connected to the one that runs the above code. It sends the 4 bytes. Distance goes up and down between 300 and 4400. It basically pumps data with an interval of 100ms (the fastest that your sensor can do as far as I understand the datasheet). I added the option to force a checksum error as well as a timeout error.

// a02yuww simulator
void setup()
{
  Serial.begin(115200);
  Serial1.begin(9600);
}

void loop()
{
  static uint32_t csErrorStarttime;
  static uint32_t toErrorStarttime;

  uint8_t data[4];
  static int distIncrement = 10;
  static int distance = 500;

  if (distance >= 4400 || distance <= 310)
  {
    distIncrement *= -1;
  }

  distance += distIncrement;

  data[0] = 0xff;
  data[1] = distance >> 8;
  data[2] = distance & 0xFF;
  data[3] = data[0] + data[1] + data[2];

  char error = 0;
  if (Serial.available())
  {
    error = Serial.read();
  }

  uint8_t numBytes = sizeof(data);
  if (error == 't')
    numBytes = 3;
  if (error == 'c')
    data[3] -= 1;
  Serial1.write(data, numBytes);
  if (numBytes == 3)
    delay(500);
  else
    delay(100);
}

The serial output looks like

Waiting for header
Receive in progress
Complete
FF 0D CA D6 
	Distance = 3530 / 3520
	Delta distance = 10
	Time = 1191803 / 1191703
	Delta time = 100
	Speed = 100
Waiting for header
Receive in progress
Complete
FF 0D D4 E0 
	Distance = 3540 / 3530
	Delta distance = 10
	Time = 1191904 / 1191803
	Delta time = 101
	Speed = 99
Waiting for header
Receive in progress
Complete
FF 0D DE EA 
	Distance = 3550 / 3540
	Delta distance = 10
	Time = 1192003 / 1191904
	Delta time = 99
	Speed = 101
Waiting for header

The speed in this example is roughly 10mm in 100ms, hence 100mm / second.

When causing a timeout, the output looks like

Waiting for header
Receive in progress
Complete
FF 05 14 18 
	Distance = 1300 / 1290
	Delta distance = 10
	Time = 1905981 / 1905881
	Delta time = 100
	Speed = 100
Waiting for header
Receive in progress
...........................................Checksum error
Waiting for header
Receive in progress
Complete
FF 05 28 2C 
	Distance = 1320 / 1300
	Delta distance = 20
	Time = 1906181 / 1905981
	Delta time = 200
	Speed = 100
Waiting for header
Receive in progress
Complete
FF 05 32 36 
	Distance = 1330 / 1320
	Delta distance = 10
	Time = 1906281 / 1906181
	Delta time = 100
	Speed = 100

And for a timeout error

Waiting for header
Receive in progress
Complete
FF 0C EE F9 
	Distance = 3310 / 3300
	Delta distance = 10
	Time = 2253416 / 2253316
	Delta time = 100
	Speed = 100
Waiting for header
Receive in progress
...........................................Timeout
Waiting for header
Receive in progress
Complete
FF 0D 02 0E 
	Distance = 3330 / 3310
	Delta distance = 20
	Time = 2254016 / 2253416
	Delta time = 600
	Speed = 33
Waiting for header
Receive in progress
Complete
FF 0D 0C 18 
	Distance = 3340 / 3330
	Delta distance = 10
	Time = 2254116 / 2254016
	Delta time = 100
	Speed = 100

Note that the reading after a timeout is incorrect. This is caused by the way the simulator works; I did not spend time to fix that.

1 Like

OP man, i just had a quick glance on it, FYIP i am using Arduino Uno board , that's some humongous code, thanx again

Checked the code running well with my arduino board showing speed also, but it is showing data for every 47ms and spamming data all over serial monitor,


any solution for it? @sterretje