Introduction
This tip article is the result of work done by @JMMS-Karunarathne and @MikeMeinz in August, 2014.
The example source code is from an application to report water levels in storage tanks for a water company. A Shenzhen Dianyingpu Technology Company (DYPsensor) DYP-ME007Y-TX Ultrasonic sensor, an Arduino UNO board and an AZ Displays, Inc. ACM1602K LCD Display were used in this project.
The DYP-ME007Y-TX Ultrasonic sensor provides a distance measurement. Our application reports water tank levels. The same sensor can be used in other applications that require sensing distance like robots, vehicle proximity, etc.
Approximately every 100ms, the DYP-ME007Y-TX Ultrasonic sensor sends four 8-bit bytes to the Arduino UNO. The first character is 0xFF which identifies it as the first character. The second and third characters are a 16-bit unsigned integer of the distance from the sensor to an object in millimeters. The fourth character is a simple check character equal to the least significant 8-bits of the sum of the first three characters.
We found during our testing that the sensor would occasionally return anomalous readings. In reviewing the readings, we determined that a mathematical average or median of several readings would almost always provide incorrect values. We chose to employ a "voting" scheme whereby the reading that occurred most after "n" samples would be the one that we used. Readings occur approximately every 100ms. We chose to read thirty samples, round the values from millimeters to centimeters and determine which centimeter value occurred most often. This yielded the most accurate reading approximately every three seconds.
Using the code
Note: The example program includes code to write results to a LCD Display unit. It is included in the example program but not explained in this tip article.
In this tip article, we demonstrate how the bytes are read from the DYP-ME007Y-TX Ultrasonic sensor using serial communications and how anomalous readings are ignored.
In the example program, the following #define
specifies how many samples are to be taken before returning a reading, declares the maximum index value, declares the array that will hold the sample values collected, and the array that will hold the number of times that a specific sample value occurred (i.e. votes).
#define MaxSamples 30
int MaxSamplesIndex=MaxSamples - 1;
float testValues[MaxSamples];
int testVotes[MaxSamples];
The DYP-ME007Y-TX Ultrasonic sensor TX pin is attached to Pin 9 on the Arduino UNO board. Pin 9 is the pin on which the four-byte sensor value is received. The RX pin on the sensor is attached to Pin 8 on the Arduino UNO board. The Arduino SoftwareSerial library is used to read the values one 8-bit byte at a time.
#include <SoftwareSerial.h>
#define TX_PIN 8
#define RX_PIN 9
SoftwareSerial mySerial = SoftwareSerial(RX_PIN,TX_PIN);
GetDistanceInMeters()
GetDistanceInMeters()
is called continously by GetSensorValue()
until a four byte value has been successfully read from the serial port. The GetDistanceInMeters()
function returns the sensor value (rounded to centimeters) as a floating point value in meters or it returns -1. The sensor returns a value of zero when an "Out of Range" condition exists.
A newly read byte is placed into read_buffer[3]
after the prior contents of the read_buffer[]
array has been shifted one byte to the left. GetDistanceInMeters()
exits and is immediately called again if any of these conditions exists:
- A byte is not available to be read.
read_buffer[0]
is not equal to 0xFF
. read_buffer[3]
is not a valid cyclical redundancy check character.
float GetDistanceInMeters()
{
byte readByte;
byte checkCalc;
word distance;
if (mySerial.available() < 1)
{
return -1.0;
}
readByte = mySerial.read();
for (byte idx = 0; idx <= 2; idx++)
{
read_buffer[idx] = read_buffer[idx + 1];
}
read_buffer[3] = readByte;
if (read_buffer[0] != 0xff)
{
return -1.0;
};
checkCalc = read_buffer[0] + read_buffer[1] + read_buffer[2];
if (read_buffer[3] != checkCalc)
{
return -1.0;
};
distance = (read_buffer[1] * 256) + read_buffer[2]; distance += 5; distance = distance / 10; for (byte idx = 0; idx <= 3; idx++) {
read_buffer[idx] = 0;
}
return distance / 100.0; }
GetSensorValue()
The GetSensorValue()
function is called when the main loop of the program wants a value for display on the LCD display unit. It first calls InitializeTestValues()
to initialize the arrays used for storing the thirty values and then executes a for
loop to retrieve thirty samples from GetDistanceInMeters()
. Within the for
loop is a do
loop that calls GetDistanceInMeters()
repeatedly until a value is received. When the value is received the AddReading()
procedure is called to record this value and an associated "vote". When all thirty samples have been retrieved, the return
statement calls ReturnHighestVote
to find and return the reading that occurred most often.
float GetSensorValue()
{
float current_reading;
InitializeTestValues();
for (byte idx = 0; idx <= MaxSamplesIndex; idx++)
{
do
{
current_reading = GetDistanceInMeters(); } while (current_reading == -1.0);
AddReading(current_reading);
delay(50);
}
return ReturnHighestVote();
}
AddReading()
The AddReading()
procedure is called by GetSensorValue()
to record a vote for the new value. AddReading()
steps through the testValues[]
array and if it finds an empty array element (signified by -1), it puts the new value into that element and records the first vote for that value in the testVotes[]
array. If it finds an array element that is equal to the new value, it adds one to the corresponding element in the testVotes[]
array. In both cases, it executes a break
statement to exit the for
loop when it no longer needs to loop.
void AddReading(float x)
{
for (int idx = 0; idx <= MaxSamplesIndex; idx++)
{
if (testValues[idx] == -1)
{
testValues[idx] = x;
testVotes[idx] = 1;
break;
}
if (testValues[idx] == x)
{
testVotes[idx] = testVotes[idx] + 1;
break;
}
}
}
ReturnHighestVote()
The ReturnHighestVote()
function is called by the return
statement in GetSensorValue()
. It steps through the testVotes[]
array looking for the highest number of votes. When the first unused array element is found, a break
statement terminates the loop and the highest value found is returned.
float ReturnHighestVote()
{
float valueMax = 0;
int votesMax=0;
for (int idx = 0; idx <= MaxSamplesIndex; idx++)
{
if (testValues[idx] == -1)
{
break;
}
if (testVotes[idx]>votesMax)
{
votesMax = testVotes[idx];
valueMax = testValues[idx];
}
}
return valueMax;
}
InitializeTestValues
InitializeTestValues
is called by GetSensorValue()
to initialize the values in the testValues[]
and testVotes[]
arrays. A value of -1 in the testValues[]
array indicates an element that is unused.
void InitializeTestValues()
{
for (int idx=0;idx<=MaxSamplesIndex;idx++)
{
testValues[idx]=-1;
testVotes[idx]=0;
}
}
Bibliography
Arduino.ru Forum: Waterproof ultrasonic sensor DYP-ME007Y - A 22 July 2014 post on the Arduino.ru forum. This post provided information on how to retrieve the sensor values via serial communications. The post incorrectly calculates the distance using 0xFF
(255) instead of 256 as a mulitplier. Also, the post incorrectly uses the term CRC for the check character.
History