NewPing Library: HC-SR04, SRF05, SRF06, DYP-ME007, Parallax PING))) - v1.7

CyklKent:
Is there a way to adjust the sensitivity or have a minimum size of object/return to be used for the result? I ask because if you look at the attached picture you will see the HC-SR04 mounted tipped up at 10 degrees. It will always return the distance to the 1.3" x 2.5" rectangle of cardboard and will never return the distance to the wood door (I like that it does this). However, if I put the 1/16" hex key in the path like in the picture, the sensor will return this shorter distance once in a while. A larger .090" hex key will cause most all returns to be the hex key distance.
I have read to get the sensor well off of the floor, but I don't really have that option. I don't have hex keys in the path, but I used this as an example. Many real world surfaces can cause returns it seems.
Your very good median can be used for a small amount of stray results, but I have found that sometimes the extremely small object returns can be consecutive at times and bad numbers will get through the median. The oscilloscope shows that it must be very difficult to pick out the closest object, but I was hoping to have the ultrasonic ignore the very small returns. I know that this is not in NewPing, but I just wondered if anyone had changed what must be the firmware on the sensor itself.
Thank you.

As cyclegadget stated, putting the transmitting and/or receiving sensor(s) in a tube can focus the detection to a smaller range. You can try different length tubes which will give different results.

Also, if you just want to block the ground, tilting it up slightly as you have done is a good method. If that still doesn't give the desired results, you can add a shield below the sensors. This is the same concept as the tube, but only blocks stray echos from below, not above.

Other than this, I haven't seen any ultrasonic sensor with any type of sensor adjustability.

Tim

hi im building obstacle avoidance robot using two hs-o4 sensor and two dc motor. im using following code

#include <NewPing.h>
#define lp 9// left side motor l293
#define ln 8// left side motor l293
#define rp 7// right side motor l293
#define rn 6// right side motor l293

#define SONAR_NUM     2 // Number or sensors.
#define MAX_DISTANCE 200 // Maximum distance (in cm) to ping.
#define PING_INTERVAL 33 // Milliseconds between sensor pings (29ms is about the min to avoid cross-sensor echo).

unsigned long pingTimer[SONAR_NUM]; // Holds the times when the next ping should happen for each sensor.
unsigned int cm[SONAR_NUM];         // Where the ping distances are stored.
uint8_t currentSensor = 0;          // Keeps track of which sensor is active.
int leftsensor, rightsensor;
NewPing sonar[SONAR_NUM] = {     // Sensor object array.
  NewPing(13, 12, MAX_DISTANCE), // Each sensor's trigger pin, echo pin, and max distance to ping.
  NewPing(11, 10, MAX_DISTANCE),
 
};

void setup() {
  Serial.begin(115200);
  pingTimer[0] = millis() + 75;           // First ping starts at 75ms, gives time for the Arduino to chill before starting.
  for (uint8_t i = 1; i < SONAR_NUM; i++) // Set the starting time for each sensor.
    pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;
}

void loop() {
  for (uint8_t i = 0; i < SONAR_NUM; i++) { // Loop through all the sensors.
    if (millis() >= pingTimer[i]) {         // Is it this sensor's time to ping?
      pingTimer[i] += PING_INTERVAL * SONAR_NUM;  // Set next time this sensor will be pinged.
      if (i == 0 && currentSensor == SONAR_NUM - 1) 
      {
 leftsensor =cm[0];
rightsensor =cm[1]; 
if ((leftsensor >=10) && (rightsensor >=10))
{
  digitalWrite(lp, HIGH);
digitalWrite(ln, LOW);
digitalWrite(rp, HIGH);
digitalWrite(rn, LOW);
Serial.println(" go straight");
Serial.println(cm[0]);
Serial.println(cm[1]);
}

if ((leftsensor <=10) && (rightsensor >=10))
{
digitalWrite(lp, HIGH);
digitalWrite(ln, LOW);
digitalWrite(rn, HIGH);
digitalWrite(rp, LOW);
Serial.println(" right turn");
Serial.println(cm[0]);
Serial.println(cm[1]);
}

if ((leftsensor <=10)&& (rightsensor >=10))
{
digitalWrite(ln, HIGH);
digitalWrite(lp, LOW);
digitalWrite(rn, HIGH);
digitalWrite(rp, LOW);
Serial.println("left turn ");
Serial.println(cm[0]);
Serial.println(cm[1]);

}
if ((leftsensor <=10) && (rightsensor <=10))
{
digitalWrite(lp,  LOW);
digitalWrite(ln, HIGH);
digitalWrite(rn, HIGH);
digitalWrite(rp, LOW);
Serial.println("back");
Serial.println(cm[0]);
Serial.println(cm[1]);




}

if ((leftsensor =0) && (rightsensor =0))
{digitalWrite(lp,  HIGH);
digitalWrite(ln, LOW);
digitalWrite(rn, LOW);
digitalWrite(rp, HIGH);
Serial.println("go stright");
Serial.println(cm[0]);
Serial.println(cm[1]);
  
}
if ((leftsensor =0) && (rightsensor >=10))
{digitalWrite(lp, HIGH);
digitalWrite(ln, LOW);
digitalWrite(rn, HIGH);
digitalWrite(rp, LOW);
Serial.println(" right turn");
Serial.println(cm[0]);
Serial.println(cm[1]);
  
}
if ((leftsensor =0) && (rightsensor >=10))
{digitalWrite(ln, HIGH);
digitalWrite(lp, LOW);
digitalWrite(rn, HIGH);
digitalWrite(rp, LOW);
Serial.println(" left turn");
Serial.println(cm[0]);
Serial.println(cm[1]);


      }
     
      sonar[currentSensor].timer_stop();          // Make sure previous timer is canceled before starting a new ping (insurance).
      currentSensor = i;                          // Sensor being accessed.
      cm[currentSensor] = 0;                      // Make distance zero in case there's no ping echo for this sensor.
      sonar[currentSensor].ping_timer(echoCheck); // Do the ping (processing continues, interrupt will call echoCheck to look for echo).
    }
  }
  // The rest of your code would go here.

}
}

void echoCheck() { // If ping received, set the sensor distance to array.
  if (sonar[currentSensor].check_timer())
    cm[currentSensor] = sonar[currentSensor].ping_result / US_ROUNDTRIP_CM;
}

by using this the robot is not moving straight but it is rotating in same place slowly and if try to use serial monitor it is not printing any thing

then i tried normal code

#define ta 13// trigger
#define ea 12// eco
#define tb 11 // trigger
#define eb 10// eco
#define lp 9// left side motor l293
#define ln 8// left side motor l293
#define rp 7// right side motor l293
#define rn 6// right side motor l293

#define TIMEOUT 40000 // 40ms timeout (the HC-sr04 has 38 ms timeout)

void setup()
{
Serial.begin (19200);
pinMode(lp, OUTPUT);
pinMode(ln, OUTPUT);
pinMode(rp, OUTPUT);
pinMode(rn, OUTPUT);
pinMode(ta, OUTPUT);
pinMode(ea, INPUT);
pinMode(tb, OUTPUT);
pinMode(eb, INPUT);
digitalWrite(ta, LOW); // Ensure triger is starting with low before any pulse
delayMicroseconds(20);// Delay before first pulse
digitalWrite(tb, LOW);
delayMicroseconds(20);
}

void loop()
{
long dua, da,dub,db;

digitalWrite(ta, HIGH); // Start the a  High Pulse

delayMicroseconds(10); // Delay 10 micros

digitalWrite(ta, LOW); // End the Pulse
dua = pulseIn(ea, HIGH, TIMEOUT);
da = (dua/2) / 29.1;

digitalWrite(tb, HIGH); // Start the b High Pulse

delayMicroseconds(10); // Delay 10 micros
digitalWrite(tb, LOW); // End the Pulse
dub = pulseIn(eb, HIGH, TIMEOUT);
db = (dub/2) / 29.1;
if ((da >=10) && (db >=10))
{
  
digitalWrite(lp, HIGH);
digitalWrite(ln, LOW);
digitalWrite(rp, HIGH);
digitalWrite(rn, LOW);
Serial.println(" go straight");
Serial.println(da);
Serial.println(db);
}

if ((da <=10) && (db >=10))
{
digitalWrite(lp, HIGH);
digitalWrite(ln, LOW);
digitalWrite(rn, HIGH);
digitalWrite(rp, LOW);
Serial.println(" right turn");
Serial.println(da);
Serial.println(db);
}

if ((db <=10)&& (da >=10))
{
digitalWrite(ln, HIGH);
digitalWrite(lp, LOW);
digitalWrite(rn, HIGH);
digitalWrite(rp, LOW);
Serial.println("left turn ");
Serial.println(da);
Serial.println(db);

}
if ((da <=10) && (db <=10))
{
digitalWrite(lp,  LOW);
digitalWrite(ln, HIGH);
digitalWrite(rn, HIGH);
digitalWrite(rp, LOW);
Serial.println("back");
Serial.println(da);
Serial.println(db);




}

if ((da =0) && (db =0))
{digitalWrite(lp,  HIGH);
digitalWrite(ln, LOW);
digitalWrite(rn, LOW);
digitalWrite(rp, HIGH);
Serial.println("go stright");
Serial.println(da);
Serial.println(db);
  
}
if ((da =0) && (db >=10))
{digitalWrite(lp, HIGH);
digitalWrite(ln, LOW);
digitalWrite(rn, HIGH);
digitalWrite(rp, LOW);
Serial.println(" right turn");
Serial.println(da);
Serial.println(db);
  
}
if ((db =0) && (da >=10))
{digitalWrite(ln, HIGH);
digitalWrite(lp, LOW);
digitalWrite(rn, HIGH);
digitalWrite(rp, LOW);
Serial.println(" left turn");
Serial.println(da);
Serial.println(db);
  
}
delay(50);
}

it is behaving good in serial monitor that is print turn right when blocked in left and in vice versa...
but in real robot it is rotating in clock wise not moving forward..help me to resolve it

Each movement decision is based on an "if" statement similar to this:

if ((da <=10) && (db >=10)

I recommend printing "da = " da ........and print "db = " db....... this will show what those numbers are and if they are changing. There must be a problem related to those two number results.

most of the time it show zero in one sensor but zero may denote out of range or object very near..and i think taking mean of ping is good idea but dono how to use that method sonar.mean() and new ping is not working good in my program even serial monitor and that is not show any word tx light in uno is not glowing when i use new ping program

Put "0" in the integer postions of this "if" statement.

if ((da >=10) && (db >=10))
{
  
digitalWrite(lp, HIGH);
digitalWrite(ln, LOW);
digitalWrite(rp, HIGH);
digitalWrite(rn, LOW);
Serial.println(" go straight");
Serial.println(da);
Serial.println(db);
}

If the either sensor output results as "0" you will not go straight.

for zero this will work, in that program

if ((da =0) && (db =0))
{
digitalWrite(lp,  HIGH);
digitalWrite(ln, LOW);
digitalWrite(rn, LOW);
digitalWrite(rp, HIGH);
Serial.println("go stright");
Serial.println(da);
Serial.println(db);
  
}
if ((da == 0) && (db == 0)||(da >=10) && (db >=10))

Don't forget TWO == signs when doing a check with an "if" statement.

One = sign is used for setting a variable to a value.

You could add a "or' ||, to add another check. This would say if either number is NOT = 0 and is not greater than 10....go straight.

Hi Tim,

In the end I brought some Yourdunino SRF06's. They are accurate at their distance measurements but unfortunately their distance max's out at about 200cm, beyond that they return 0's. I was looking for something which would hopefully be accurate to about 350cm.... I have gone ahead and bought a few of Maxbotix XL EZ0 sensors. Do you think that I could modify your New Ping code to work with these sensors or would I need to start from scratch?

bassmagnetic:
Hi Tim,

In the end I brought some Yourdunino SRF06's. They are accurate at their distance measurements but unfortunately their distance max's out at about 200cm, beyond that they return 0's. I was looking for something which would hopefully be accurate to about 350cm.... I have gone ahead and bought a few of Maxbotix XL EZ0 sensors. Do you think that I could modify your New Ping code to work with these sensors or would I need to start from scratch?

Did you set the maximum distance to higher than 200cm? Mine work well to easily over 400cm. Maybe it's the size of the object or how sonically reflective it is? Try using a wall as your maximum distance measurements, that's what the distance specs use.

Tim

Yes, I set the maximum distance at 300 but then the sensors return 0's. All the sensors are pointing at a solid wall 250cm away, but the sensors are returning 0's rather than seeing the wall, thus I'm assuming out of their range. I thought it strange as the Hr04's measured/worked fine over this distance. I've tried different delay times to see if there is any interference, up to 100ms but it makes no difference to the readings

bassmagnetic:
Yes, I set the maximum distance at 300 but then the sensors return 0's. All the sensors are pointing at a solid wall 250cm away, but the sensors are returning 0's rather than seeing the wall, thus I'm assuming out of their range. I thought it strange as the Hr04's measured/worked fine over this distance. I've tried different delay times to see if there is any interference, up to 100ms but it makes no difference to the readings

Are you sending the sensor 5v? Other than that, must just be your sensors.

Tim

yep, even gave them an external 5v input but no change to the distance.....

solaron99:
Hi! Fantastic library.

I simply want to use 2 HC-SR04 sensors and use the result to turn on their appropriate LED when MAX_DISTANCE is less than 30.

SR04 #1 - turns on LED1 when MAX_DISTANCE is less than 30

  • turns off LED1 when MAX_DISTANCE is greater than 30

SR04 #2 - turns on LED2 when MAX_DISTANCE is less than 30

  • turns off LED2 when MAX_DISTANCE is greater than 30

I added an if/else statement in the pingResults() function, LED 1 turns ON and remains on when
either sensor is triggered.
What do I have to do to declare both sensor 1 and sensor 2 in the function?

Sounds pretty simple but I'm kinda new at this so be gentle! :slight_smile:

Thanks.

Here's the sketch:

#include <NewPing.h>

#define SONAR_NUM 2
#define MAX_DISTANCE 30
#define PING_INTERVAL 33
//LED setup
#define LED_1 12
#define LED_2 13

unsigned long pingTimer[SONAR_NUM];
uint8_t currentSensor = 0;

NewPing sonar[SONAR_NUM] = {
NewPing(4, 5, MAX_DISTANCE),
NewPing(6, 7, MAX_DISTANCE)
};

void setup() {
pinMode(LED_1, OUTPUT);
pinMode(LED_2, OUTPUT);

Serial.begin(115200);
pingTimer[0] = millis() + 75;
for (uint8_t i = 1; i < SONAR_NUM; i++)
pingTimer = pingTimer[i - 1] + PING_INTERVAL;
}
void loop() {

  • for (uint8_t i = 0; i < SONAR_NUM; i++) {*
    _ if (millis() >= pingTimer*) { _
    pingTimer += PING_INTERVAL * SONAR_NUM;
    sonar[currentSensor].timer_stop();
    _ currentSensor = i; _
    sonar[currentSensor].ping_timer(echoCheck);
    _ }
    }
    }
    void echoCheck() { _
    if (sonar[currentSensor].check_timer())

    pingResult(currentSensor, sonar[currentSensor].ping_result / US_ROUNDTRIP_CM);
    _}
    void pingResult(uint8_t sensor, int cm) { // Sensor got a ping, do something with the result.
    // The following code would be replaced with your code that does something with the ping result.*_

* if (cm < MAX_DISTANCE) {
digitalWrite(LED_1, HIGH);
_ }
else {_
digitalWrite(LED_1, LOW);*

* }*

* for (uint8_t i = 0; i < SONAR_NUM; i++) {
_ Serial.print(sensor);
Serial.print(" ");
Serial.print(cm);
Serial.println("cm");
}*
Serial.println();_

}
[/quote]
The 15 sensor example sketch is more for advanced users that require a multi-tasking type script where multiple things will be happening at basically the same time. Therefore, your use is well outside these parameters.
Instead, simply use the basic one sensor example sketch and add a second sensor.
Basically, you're trying to make it much more complicated than it needs to be, which is why you're having problems.
Now, if you had an already event driven sketch and you wanted to add two sensors while still keeping your sketch event-driven, then using the 15 sensor sketch would be appropriate.
Tim

Hi,
I bought a HC-SR04 with Arduino Uno R3.
Without knowing anything, I wired up them together and it works like a charm.
In addition, parameters can be fine tuned (distance, sampling rate...)

Marvellous. It's really plug and play library, and examples given work !
Thank you fo your library.

Sam

some ideas about accuracy...

i have just gotten my sr04 and played a lithe with it. i intend to use it to measure oil level in a tank. so accuracy is quite important.
after reading on the web there are two main points that seem to influence the accuracy of the sensor and both are not accounted for in the newping library:

  • one is temperature. it would be nice to be able to set the temperature that newping uses for the conversion to cm/inces. depending on the temperaure the error can easily be in the range of 10% if temperature is not accounted for.

  • the other one is the which echo is received and evaluated. if i do repeated measurements i notice that the results will be relatively near around two or tree that are themselves further apart. this looks like it is consistent with some articles that say this error comes from reading the second or third echo and not the first and should be especialy noticeable in the short range.

so to be able to make more accurate measurements i started reading 10 or 50 pings, iterating over all of them and throwing away all that are equal to the largest and all that are equal to the smallest and then averaging the rest. if there are non available after throwing the smallest and largest away i take the smallest. the conversion to cm is then done in float.

this seems to give very accurate and reproduceable results. doing this repeatedly in a range of 1cm to 1m gives identical results up to at least 1mm.

maybe a 'slow scan' option could add this to the newping library for applications where fast response times are not needed.

justme1968:
some ideas about accuracy...

i have just gotten my sr04 and played a lithe with it. i intend to use it to measure oil level in a tank. so accuracy is quite important.
after reading on the web there are two main points that seem to influence the accuracy of the sensor and both are not accounted for in the newping library:

  • one is temperature. it would be nice to be able to set the temperature that newping uses for the conversion to cm/inces. depending on the temperaure the error can easily be in the range of 10% if temperature is not accounted for.

  • the other one is the which echo is received and evaluated. if i do repeated measurements i notice that the results will be relatively near around two or tree that are themselves further apart. this looks like it is consistent with some articles that say this error comes from reading the second or third echo and not the first and should be especialy noticeable in the short range.

so to be able to make more accurate measurements i started reading 10 or 50 pings, iterating over all of them and throwing away all that are equal to the largest and all that are equal to the smallest and then averaging the rest. if there are non available after throwing the smallest and largest away i take the smallest. the conversion to cm is then done in float.

this seems to give very accurate and reproduceable results. doing this repeatedly in a range of 1cm to 1m gives identical results up to at least 1mm.

maybe a 'slow scan' option could add this to the newping library for applications where fast response times are not needed.

  1. Because you need an outside thermometer to measure temperature, it was decided not to overly complicate the NewPing library. However, NewPing IS designed to adjust with temperature. Simply use the ping() method which gives you a time. Then, do your own calculation of distance based on your thermometer attached to the Arduino. In almost all situations, the default distance works well for average indoor temperatures. But, if you need accuracy, you're going to need additional hardware and then (of course) you're going to need to do your own calculation. But, NewPing will work with this situation just fine.

  2. This isn't a problem with the library, nor can it be fixed in a library. This is fixed by following the instructions in my sample sketches. Basically, if you ping a second time too quickly after a previous ping, you can get an echo. This can't be fixed by the library as it's only reading what the sensor outputs, which is a digital signal (the library has no amplitude information). The solution is to simply heed my warnings in my sketches. Which is to not ping too quickly. Your pings shouldn't be more frequently than around once every 29ms. If you're in a highly sonicly reflective environment, or your sensor is overly sensitive, you may need to increase this amount. If it's not something fast moving (probably like in your case) ping once every second or two even. That way, you can be assured that you're not reading an echo. NewPing doesn't control the speed of the pings YOU do. If you want to make it slow, just make it slow. Maybe I'm missing something?

Tim

Hi Teckel,

did investigate the duration2cm() function here - http://arduino.cc/forum/index.php/topic,165860.0.html -
might be useful in your library.

robtillaart:
Hi Teckel,

did investigate the duration2cm() function here - http://arduino.cc/forum/index.php/topic,165860.0.html -
might be useful in your library.

I tried something similar in the past, but honestly it's not worth the extra cycles adding this type of code adds. Basically, it's pointless to try to make the math more accurate than the sensor is. Ultrasonic sensors are not very accurate anyway. Also, there's actually an argument as to the speed of sound (everyone likes to use a different number). This is why I settled on an integer and the value that I default to. It provides very accurate distance results in typical indoor temperatures and does so very fast using very little program space.

Instead of getting into this battle, I avoid it. Just use the ping() method which returns a ping time. Then, use whatever math you want (based on temperature or whatever). If you want to use a fraction and want to use a different method to calculate this (like the above linked example) that's fine also.

Keep in mind that NewPing is an ultrasonic ping library, not a floating point, math, or temperature library. I'm sure you can find or develop a library that allows you to divide a floating point number without using floating point math. This would be then just included in your sketch along with NewPing to calculate a distance at whatever accuracy level you desired. But keep in mind that the sensor isn't that accurate anyway (only about 1cm). So all this effort may be wasted anyway.

Tim

The fact that the sensors are not accurate is imho no argument to use math that adds additional errors. The relative errors add up.
That said, I agree with you that most applications do not need this level of accuracy.

Thanks,

robtillaart:
The fact that the sensors are not accurate is imho no argument to use math that adds additional errors. The relative errors add up.
That said, I agree with you that most applications do not need this level of accuracy.

Thanks,

If you want to use floating point values, more power to you, go for it! NewPing FULLY supports doing this. YOU have the ability to change the US_ROUNDTRIP_CM and US_ROUNDTRIP_IN values to whatever you want, or use ping() and do any other calculations you wish factoring in temperature, altitude, humidity, etc.

The fact is, my integer values are typically more accurate than when people try to over complicate things by using floating point values in attempts to get more accurate values. But, if you're in search for the holy grail of ultrasonic measurement, use ping() and apply your magic formula.

This is NOT something that will be added to NewPing as it's outside the scope of the library to be any more accurate than it is now. Because, to be more accurate you'd need a thermometer, and apply your own formula. So, while this is a fine topic, it shouldn't be here as it will never be added to NewPing. Heck, there's debate on the speed of sound, I'm just staying out of the fight.

Topic closed.