12,953,700 members (43,755 online)
Tip/Trick
alternative version

#### Stats

18.9K views
15 bookmarked
Posted 1 Sep 2014

# A method to ignore anomalies in an ultrasonic distance sensor application

, 27 Oct 2014 CPOL
 Rate this:
Describes an algorithm to ignore spurious ultrasonic sensor readings.

## 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];

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 checkCalc;
word distance;
// Is there a character available to read
if (mySerial.available() < 1)
{
return -1.0;
}
// Move any bytes in the buffer to the left
for (byte idx = 0; idx <= 2; idx++)
{
}
// Add the new character to the end of the buffer
// If the first character in the buffer is now 0xff, then we have a reading to use
{
return -1.0;
};
// Calculate the check character
// Ensure that the check character read matches the computed check character
{
return -1.0;
};
// Compute the distance in millimeters
// Round to cm
distance += 5; // add 5 to the cm position
distance = distance / 10; // Divide by 10 to drop the cm position.
for (byte idx = 0; idx <= 3; idx++) {
}
// Divide by 100 to convert (int)cm to (float)m
return distance / 100.0; // Convert cm to meters
}```

### 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()
{
InitializeTestValues();
// Get the next MaxSample values from the sensor
for (byte idx = 0; idx <= MaxSamplesIndex; idx++)
{
do
{
current_reading = GetDistanceInMeters(); // Get value in meters from the sensor
delay(50);
}
return ReturnHighestVote();
}```

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)
{
// Either put the value in the first empty cell or add a vote to an existing value.
for (int idx = 0; idx <= MaxSamplesIndex; idx++)
{
// If an empty cell is found, then it is the first time for this value.
// Therefore, put it into this cell and set the vote to 1
if (testValues[idx] == -1)
{
testValues[idx] = x;
// Exit the For loop
break;
}
// If the cell was not empty, then check to see if the testValue is equal to the new reading
if (testValues[idx] == x)
{
// Exit the For loop
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;
for (int idx = 0; idx <= MaxSamplesIndex; idx++)
{
if (testValues[idx] == -1)
{
break;
}
{
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()
{
// Initialize test values arrays
for (int idx=0;idx<=MaxSamplesIndex;idx++)
{
testValues[idx]=-1;
}
}```

## 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

• Version 1: 1 September 2014.
• Version 2: 2 September 2014. Added `delay(50); `into `GetSensorValue()`
• Version 3: 25 October 2014. Added buffer clear in GetDistanceInMeters()

## Share

 Retired United States
I’m retired. When I started my career, programming projects consisted of plugging wires into plug boards to create punch card processing applications to be run on electrical accounting machine like the IBM 402, 407, 085, 088, 514, 519, etc. From there, I moved to writing SPS and Autocoder applications on an IBM 1401 with 4K of memory eventually upgraded to 16K of memory. After many years of migrating my skills to various languages on various hardware platforms, I became an Information Technology Director where I didn’t need to program anymore. So, starting in 1996, I volunteered my time with a local community cable television organization and built some applications to help them run their operations. Originally in Clipper Summer 1987 and later Clipper 5.2, I migrated and enhanced those applications to VB .NET 2003 in 2003. I retired from my full-time job in 2010. Since then, I have continued to support the local community cable tv organization's applications. In 2013, I migrated the VB .NET 2003 Solution to VB .NET 2012 so that it can run on 64-bit computers and interact with Microsoft Office 2010. The upgrade went smoothly. In mid 2013, I developed a VB .NET 2012 application for them to download election results data from the Secretary of State's web site, format the results and send them to a VizRT character generator for on-air display.

## You may also be interested in...

 First Prev Next
 Good pwasser27-Oct-14 20:13 pwasser 27-Oct-14 20:13
 Probably I know because you have erroneous readings G.E.24-Oct-14 4:11 G.E. 24-Oct-14 4:11
 Re: Probably I know because you have erroneous readings Mike Meinz24-Oct-14 4:21 Mike Meinz 24-Oct-14 4:21
 Re: Probably I know because you have erroneous readings G.E.24-Oct-14 5:31 G.E. 24-Oct-14 5:31
 Re: Probably I know because you have erroneous readings Mike Meinz24-Oct-14 6:45 Mike Meinz 24-Oct-14 6:45
 Nice work - Quick question rather_b_sailing2-Sep-14 7:47 rather_b_sailing 2-Sep-14 7:47
 Re: Nice work - Quick question JMMS Karunarathne2-Sep-14 8:31 JMMS Karunarathne 2-Sep-14 8:31
 Re: Nice work - Quick question rather_b_sailing2-Sep-14 8:43 rather_b_sailing 2-Sep-14 8:43
 Re: Nice work - Quick question Mike Meinz2-Sep-14 9:15 Mike Meinz 2-Sep-14 9:15
 I vote 5. Simple as it should be. David Serrano Martínez2-Sep-14 3:05 David Serrano Martínez 2-Sep-14 3:05
 Re: I vote 5. Simple as it should be. Mike Meinz2-Sep-14 3:15 Mike Meinz 2-Sep-14 3:15
 Last Visit: 31-Dec-99 18:00     Last Update: 27-May-17 22:26 Refresh 1