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

Well that's a good point, but it is still likely to jump around between 10 and 11 cm.

teckel:
Interesting! My ultrasonic sensors only use a couple mA each, while the Arduino Uno uses 60 mA (I'd guess the Mega uses more). I wouldn't think that just the sensors would put your USB or the voltage regulator over the top. Have you measured what kind of current it's using? It just seems very unlikely that it's within a couple mA of going over the top and the sensors draw enough current to do it. I guess it's possible, just doesn't seem likely.

Tim

Well it did it for me at least. Hopefully it helps someone else as well.

The operating voltage for the HC-SR04 should apperently be 4.50 to 5.0V. But I did some quick measurements which shows that that's not the case for my sensors:

With Ethernet shield:

  1. Power via USB port
    1.1. With LCD screen connected as well, at 3 HC-SR04 ultrasonic sensors: 4.55 V
    1.2. Without LCD Screen: 4.60 V

  2. Power from power outlet
    2.1. With LCD screen connected as well, at 3 HC-SR04 ultrasonic sensors: 4.98 V
    2.2. Without LCD Screen: 4.98 V

Without Ethernet shield:
3. Power via USB port
3.1. With LCD screen connected as well, at 3 HC-SR04 ultrasonic sensors: 4.76 V
3.2. Without LCD Screen: 4.82 V

  1. Power from power outlet
    4.1. With LCD screen connected as well, at 3 HC-SR04 ultrasonic sensors: 4.98 V
    4.2. Without LCD Screen: 4.98 V

i.e. since it doesn't work properly for me with just the sensors and the Ethernet shield connected (using just USB), over 4.60V seems to be needed.

michinyon:
Well that's a good point, but it is still likely to jump around between 10 and 11 cm.

Not if you turn off rounding. But, you are right, it can bounce between to different integers as well. However, that's because the sensor is not accurate to a fraction of a cm. It's not because the library doesn't do floating point. Basically, it's not helpful to do floating point math. But, feel free to do it. Replace "ping_cm()" with "ping()/57.0" and you'll get a decimal result. If you find this useful I'd really be amazed, but the library is designed so you can do it as shown.

Tim

gandalf50:

teckel:
Interesting! My ultrasonic sensors only use a couple mA each, while the Arduino Uno uses 60 mA (I'd guess the Mega uses more). I wouldn't think that just the sensors would put your USB or the voltage regulator over the top. Have you measured what kind of current it's using? It just seems very unlikely that it's within a couple mA of going over the top and the sensors draw enough current to do it. I guess it's possible, just doesn't seem likely.

Tim

Well it did it for me at least. Hopefully it helps someone else as well.

The operating voltage for the HC-SR04 should apperently be 4.50 to 5.0V. But I did some quick measurements which shows that that's not the case for my sensors:

With Ethernet shield:

  1. Power via USB port
    1.1. With LCD screen connected as well, at 3 HC-SR04 ultrasonic sensors: 4.55 V
    1.2. Without LCD Screen: 4.60 V

  2. Power from power outlet
    2.1. With LCD screen connected as well, at 3 HC-SR04 ultrasonic sensors: 4.98 V
    2.2. Without LCD Screen: 4.98 V

Without Ethernet shield:
3. Power via USB port
3.1. With LCD screen connected as well, at 3 HC-SR04 ultrasonic sensors: 4.76 V
3.2. Without LCD Screen: 4.82 V

  1. Power from power outlet
    4.1. With LCD screen connected as well, at 3 HC-SR04 ultrasonic sensors: 4.98 V
    4.2. Without LCD Screen: 4.98 V

i.e. since it doesn't work properly for me with just the sensors and the Ethernet shield connected (using just USB), over 4.60V seems to be needed.

I've ran ultrasonic sensors down to 3.3 volts and they still work. I forgot I have an Internet shield, I'll see what kind of milliamps it uses.

Tim

teckel:

tasosstr:
Hello,

I would like to ask if it is possibly to read the cm with decimal like 10.4 cm e.t.c.

Thank in advance.

Best regards,
Tasos

Yes, simply use ping() which will return the milliseconds and then divide by whatever number you want to convert ms to cm (defaults to 57). The library uses integers to save program space (saves about 1k by avoiding floating point). Also, the accuracy isn't really better than a cm and there's other variables like temperature that will change the speed of sound and mess up your calculations.

However, keep in mind that doing any of this will really be of no good as the sensor is not accurate enough to actually give you a consistent reading of 10.4. If you did use a floating point number for ms to cm conversion, the ping results would probably jump around from 10.9 to 10.1, with everything still. So, I'm sure sure what use you'd really get from it. All you'd really be doing is wasting 1k of program space and getting a bunch of different readings and not knowing which one was right.

Basically, I'd suggest leaving it as-is and accepting that the sensor is really only accurate to 1cm or 1in. And, enjoy the extra speed and 1k of program space you saved by not using floating point calculations on things that are not accurate below an integer anyway. NewPing is designed to be small, fast, and accurate. Making it floating point would not help the accuracy and it would make it slower and larger. So, it would hurt the library in every way. But, you're free to use floating point if you wish by using the ping() method along with your own conversion (after also monitoring the temperature).

Let me know if you actually find this useful, I've yet to hear of it being at all useful or more accurate.

Tim

Hello Dear Tim,

Thank you very much for the reply and for the help ! ! !

You have make amazing work well done ! ! !

I was try the bellow example:

#include <NewPing.h>

#define TRIGGER_PIN  12  // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN     11  // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 500 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

void setup() {
  Serial.begin(115200); // Open serial monitor at 115200 baud to see ping results.
}

void loop() {
  delay(50);                      // Wait 50ms between pings (about 20 pings/sec). 29ms should be the shortest delay between pings.
  float uS = sonar.ping(); // Send ping, get ping time in microseconds (uS).
  Serial.print("Ping: ");
  Serial.print(uS / US_ROUNDTRIP_CM); // Convert ping time to distance in cm and print result (0 = outside set distance range)
  Serial.println("cm");
}

and working perfect ! ! !

I need this because i want to get the height of a petrol tank, so now i can have more better results because of you :slight_smile:

Again Thank you Very Much ! ! !

Best Regards,
Tasos

THANKS FOR THIS NEW SONIC LIBRARY! GREAT WORK !!!!!!!!!!!!!!!!!!!

tasosstr:
I need this because i want to get the height of a petrol tank, so now i can have more better results because of you :slight_smile:

Tasos, just a word of caution before you get too far into your project:

Ultrasonic sensors don't work well with petrol/gasoline. I seem to recall it was due to vapor in the tank giving false readings.

My wife used to work for ESI in Wichita, KS, which specialized in designing and installing liquid storage tank level sensors. They provided a business-to-business link where their hardware would monitor tanks of, say, liquid chicken feed additive, and automatically call the tank-filling-company and schedule a delivery when the tank was low.

I recall my wife talking specifically about a big sale hanging on their ability to monitor fuel tank levels, and the lead engineer was under a lot of pressure to develop a sensor that worked, but a solution eluded him and it was causing a lot of friction at work. This was many years ago, and it's now a solved problem, but the problem still stands for generic ultrasonic sensors. (It may have been a solved problem then, and maybe the boss didn't want to use a third-party product.)

There are commercial sensors available for measuring petrol/gasoline, but I'm sure they're at least a magnitude more expensive than the hobby ultrasonic sensors.

Ultrasonic sensors don't work well with petrol/gasoline.

Was the problem simply one of calibration, i.e. the speed of sound in vapour-laden air vs. speed of sound in air?

I'm building a little robot with an HC-SR04 for obstacle avoidance, and I'm finding NewPing to be very nice. Thanks for your hard work, Tim.

I'm using NewPing in timer-mode, but I don't really need to do anything inside my echoCheck() function... I just want a distance value to be available to other parts of the code.

Here's where I run into a problem... I'm not seeing a good way to directly determine the difference between "timed out without an echo" and "still waiting for an echo" conditions. check_ping() returns false in both cases, and x.ping_result is only updated on receiving an echo. So x.ping_result always indicates the distance of the last successful ping, but it will always be a positive value even when pings are timing out without an echo.

So inside my echoCheck(), I have to assign the value of ping_result to a global variable, and then when I read the global variable elsewhere in the code, I have to remember to set it to zero. This is effectively doing the same job as ping_result, except it's happening in my code instead of inside NewPing.

Would it make sense for the timeout reset portion of check_ping() to set ping_result back to 0? ping_result isn't used anywhere else in NewPing, so it wouldn't affect internal behavior. I don't know how it might affect behavior of existing programs, but I suppose it could.

With this minor change, I could eliminate my global variable and just look at ping_result any time I want to know the status of the last ping.

On one level, I'd like the option for the timer-driven model to be even less involved. I'd like to start the "ping engine" in setup() and have it ping every 50 usec "forever" without calling ping_timer() again, and be able to just check ping_result any time I like. I can't call ping_timer(echoCheck) from within echoCheck(), because again, check_timer()'s return value doesn't differentiate between "timed out" and "still waiting". (And despite being a career developer / system architect, I'll admit that I may be missing something here and that the problem is easily solved.)

If check_timer() returned an int... -1 for "still waiting", 0 for "timed out" and a positive value (distance) for an "echo received", it would be a lot more flexible. And it would break existing code, since its' an API change.

I suppose I could peek at the timer value itself to see if it's still enabled, but having the library help with that would be ideal. Maybe a function or variable I could call/look at to find out why check_timer() returned false? Of course, an alternative function to ping_timer(), say, ping_forever(), that didn't (or optionally) require a echoCheck() and handled running the ping over and over on its own might be ideal. (And I'll admit, this is what I expected when I first dug into the timer-driven mode.)

I may make some tweaks to the library and see which ideas pan out.

AWOL:

Ultrasonic sensors don't work well with petrol/gasoline.

Was the problem simply one of calibration, i.e. the speed of sound in vapour-laden air vs. speed of sound in air?

A bit of searching suggests you may be on the right track, but it's apparently not as simple as a one-time calibration. The speed of sound changes with the density of the fuel vapor, which varies with temperature. If the only significant variable is temperature, knowing the temperature inside the tank would make compensation relatively simple. But the temperature at the sensor may not be representative of the rest of the tank, so may require a remote sensor.

It had an experienced sensor design engineer stumped, at least for awhile.

I asked my wife, and she said vapor was one issue, and another is that gasoline has a high thermal expansion ratio... to get an accurate volume measurement, you have to know the temperature of the fuel as well as its level. But that wouldn't be an issue if you're only concerned with "the tank's getting low". Apparently a float was a cheaper and more reliable method, at least at the time.

There's no provision in NewPing to track when a ping is in process. Once a ping is complete, you know if there was no ping or a ping and at what distance. It's designed to be like an interrupt, where once the ping is complete your code then processes the result. There's no requirement for the pingResult() function. That's just there as a sample because most people want to process results as soon as a ping has been completed. If you don't need it, simply delete it.

What you want to accomplish can be done without any modification to the library. As your task is specific and different than most, it's best to have this code be in your sketch instead of the library. This actually allows for more flexibility. The process of pinging every 50 milliseconds can easily be done with the 15 sensor sketch example. It doesn't matter if it's in your sketch or inside the NewPing library, it would be just as involved. Having this inside your sketch allows for exact control as well.

ping_forever() would be a function in your sketch, basically like the 15 sensor sketch. I've already done the work for you, you just need to impliment it in your sketch. Having the library do too much would be outside the scope of the library. It's designed to ping the sensor, not really manage all aspects of when a ping is sent or for how long. The 15 sensor sketch is your ping_forever() function. Just use that and add the rest of your code. Also, be sure to read my post on more details about the 15 sensor sketch, and ways to tweak it for your needs. I believe you'll find that I've already answered your questions there.

Here's a sample sketch that I whipped up for you that may assist:

#include <NewPing.h>

#define SONAR_NUM      3
#define MAX_DISTANCE 200
#define PING_INTERVAL 33

unsigned long pingTimer[SONAR_NUM];
unsigned int cm[SONAR_NUM];
uint8_t currentSensor = 0;
uint8_t pingStatus = 0;

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

void setup() {
  Serial.begin(115200);
  pingForever();
}

void loop() {
  for (uint8_t i = 0; i < SONAR_NUM; i++) { // Loop through all the sensors.
    if (millis() >= pingTimer[i]) {
      pingTimer[i] += PING_INTERVAL * SONAR_NUM;
      sonar[currentSensor].timer_stop();
      currentSensor = i;
      cm[currentSensor] = 0;
      pingStatus = 1;
      sonar[currentSensor].ping_timer(echoCheck);
    }
  }
  
  /*
  At any time, you can get the value of pingStatus, currentSensor,
  or read the values of the last pings in the cm[] array.  It's
  up to you to figure out what you want to do with this data.
  */
}

void pingForever() {
  pingTimer[0] = millis();
  for (uint8_t i = 1; i < SONAR_NUM; i++) pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;
}

void pingStop() {
  for (uint8_t i = 0; i < SONAR_NUM; i++) pingTimer[i] = -1;
  pingStatus = 0;
}

void echoCheck() {
  if (sonar[currentSensor].check_timer()) {
    cm[currentSensor] = sonar[currentSensor].ping_result / US_ROUNDTRIP_CM;
    pingStatus = 0;
  }
}

Beyond this, and the 15 sensor sketch help post, you'll need to post your sketch for me to offer suggestions.

Tim

I would also suggest a float because then the electronics can be placed outside the tank with only the float on the inside. Putting electronics inside fuel vapor doesn't sound like a good ideal. Actually, it sounds like a very bad idea. A short could result in a spark which results in a very bad day.

Tim

Hi everyone, I would like to ask if it's possible to hold a value, I mean, if I remove my hand from the sensor, then the sensor measure out of range, I would like that the last value sent for serial (usb) was the measure from my hand to the sensor before remove... This is my sketch which I found in this post, BTW really useful!!!

#include <NewPing.h>

#define SONAR_NUM     4 // 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.

NewPing sonar[SONAR_NUM] = {     // Sensor object array.
  NewPing(12, 11, MAX_DISTANCE), // Each sensor's trigger pin, echo pin, and max distance to ping.
  NewPing(10, 9, MAX_DISTANCE),
  NewPing(8, 7, MAX_DISTANCE),
  NewPing(6, 5, 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) oneSensorCycle(); // Sensor ping cycle complete, do something with the results.
      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;
}

void oneSensorCycle() { // Sensor ping cycle complete, do something with the results.
  for (uint8_t i = 0; i < SONAR_NUM; i++) {
      Serial.print(cm[i]);
}

oigresUPM:
Hi everyone, I would like to ask if it's possible to hold a value, I mean, if I remove my hand from the sensor, then the sensor measure out of range, I would like that the last value sent for serial (usb) was the measure from my hand to the sensor before remove... This is my sketch which I found in this post, BTW really useful!!!

You would write your sketch to hold a value. Once one cycle of pinging all the sensors is complete, it goes to the oneSensorCycle() function. This is where you would analyze the results. If you wanted to hold those results and stop pinging, this is where you would do it. The 15 sensor sketch help post shows how you would stop the ping process and resume. If you only want to know when something is in the way of one of the sensors and then hold those results. In the oneSensorCycle() function you would detect something was in the way, stop the pings, and do whatever you wanted with that information. When you wanted to restart the pings, you would use the restart code.

That's about the best I can do shy of writing your script for you, and what fun would that be?

Tim

teckel:

oigresUPM:
Hi everyone, I would like to ask if it's possible to hold a value, I mean, if I remove my hand from the sensor, then the sensor measure out of range, I would like that the last value sent for serial (usb) was the measure from my hand to the sensor before remove... This is my sketch which I found in this post, BTW really useful!!!

You would write your sketch to hold a value. Once one cycle of pinging all the sensors is complete, it goes to the oneSensorCycle() function. This is where you would analyze the results. If you wanted to hold those results and stop pinging, this is where you would do it. The 15 sensor sketch help post shows how you would stop the ping process and resume. If you only want to know when something is in the way of one of the sensors and then hold those results. In the oneSensorCycle() function you would detect something was in the way, stop the pings, and do whatever you wanted with that information. When you wanted to restart the pings, you would use the restart code.

That's about the best I can do shy of writing your script for you, and what fun would that be?

Tim

Thank you Tim!!! I'm going to work with these advices!!

Would NewPing work with a SRF08?

I've been using a HC-SR04 but I've looking for something more accurate at longer distances, to sense body movement accurately up to 3m, is the SRF08 much better?

Or any other suggestions?

The HC-SR04 recognises a body up till about 1.2m, then further away the sensor reports 0, as I guess its sending out a signal but not receiving it back.... but if I hold up a board of wood it sends back an accurate reading up to about 3m

bassmagnetic:
Would NewPing work with a SRF08?

I've been using a HC-SR04 but I've looking for something more accurate at longer distances, to sense body movement accurately up to 3m, is the SRF08 much better?

Or any other suggestions?

The HC-SR04 recognises a body up till about 1.2m, then further away the sensor reports 0, as I guess its sending out a signal but not receiving it back.... but if I hold up a board of wood it sends back an accurate reading up to about 3m

All the sensors are basically the same, with the same transducers and amps in most cases. There's just slight differences (like timeouts). I have several kinds and I would consider most to be almost identical. Also, keep in mind that the HC-RS04 is not made by one company, many make them, with slight differences. It could be that you just not one that wasn't too good.

Also, the SRF08 is not better just because the number is bigger. It's not like it's version 8 so it's better than version 4. The SRF08 is much larger, uses a lot more power, more expensive, and doesn't use the same interface at all, it uses I2C. Instead, I would opt for the SRF06 from Yourduino. There are other models of the SRF06 that have a different interface and connection header. Those won't work with NewPing as they are very rare. But, the SRF06 from YourDuino works very well with NewPing and is the best working sensor that I have. It uses the least amount of power, fastest startup time, and has the longest range of any sensor I have. The owner of YourDuino is a regular on this forum too as a bonus.

So, I would suggest either trying to buy a few additional cheapo eBay HC-RS04 sensors as you may have better luck. Or, go with the SRF06 from YourDuino (4 pins). Keep in mind that ultrasonic sensors are affected by sound absorbing materials, like clothing. It won't work as well detecting detecting a dog as it will a flat board. But, keep in mind that this still may give you what you want. For example, having the sensor hit a far wall and get the distance (say 300cm). When someone walks in front of the sensor, it will either bounce off and give a distance reading of say 150cm. Or, maybe all the sound is absorbed so it gives a reading of zero. That too could be used as a detection trigger, as normally it reads 300cm. So, whenever there's a reading of anything less than say 280cm (all the way to 0) would mean something was in the way of the sensor. This may not work in your situation, but it does in some (if the far wall is within the sensor range).

Tim

Thanks so much Tim for your reply....

I'm in the UK so I think ordering from Yourduino is a little far... I was also looking at the http://www.maxbotix.com/ Maxbotix sensors, have you any experience of these? Prices seem wildly different but can't tell if that means a more accurate sensor?

I have taken your advice and changed my current patch to take into account 0 readings for non-distance people detection, but would really like to try to get accurate distance reading of people up to 3m... especially as the sensors throw out quite a few false 0 readings when there is not a person there....

Also before I said I would post the installation that I am using the sensors for, its here: http://www.apbsound.com/2013/drone/

All the best

bassmagnetic:
Thanks so much Tim for your reply....

I'm in the UK so I think ordering from Yourduino is a little far... I was also looking at the http://www.maxbotix.com/ Maxbotix sensors, have you any experience of these? Prices seem wildly different but can't tell if that means a more accurate sensor?

I have taken your advice and changed my current patch to take into account 0 readings for non-distance people detection, but would really like to try to get accurate distance reading of people up to 3m... especially as the sensors throw out quite a few false 0 readings when there is not a person there....

Also before I said I would post the installation that I am using the sensors for, its here: http://www.apbsound.com/2013/drone/

All the best

Yourduino is far, but the sensors are cheaper and shipping low so it's not bad at all. maxbotix.com makes some good sensors. But, you need to use their libraries or make your own as they do things totally different than all the other ultrasonic sensors.

You can use the ping_median() method if you're not already to get a few readings which could weed out erroneous "no ping" readings. Looking at the installation, it would seem that would work very well for ultrasonic sensors. The far wall seems to be within 15 feet and it's a nice big flat wall. But, maybe it's not working as well as you hoped for another reason. For example, too much ambient noise or maybe because the sensors are hidden in the wall so the echo isn't as effective returning to the sensor. I'd be curious how well it works if you placed the sensors outside the wall and there was no other noise in the room. I know that doesn't help with your goal, but it could be useful for tracking down the problem. Or, maybe it's the length of the wires you have running to the sensor?

Tim

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.