Go Down

Topic: NewPing Library: HC-SR04, SRF05, SRF06, DYP-ME007, Parallax PING))) - v1.5 (Read 128 times) previous topic - next topic

teckel


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
Arduino - Teensy - Raspberry Pi
My libraries: NewPing - LCDBitmap - toneAC - NewTone - TimerFreeTone

michinyon

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

gandalf50


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

4. 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.

teckel


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
Arduino - Teensy - Raspberry Pi
My libraries: NewPing - LCDBitmap - toneAC - NewTone - TimerFreeTone

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

4. 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
Arduino - Teensy - Raspberry Pi
My libraries: NewPing - LCDBitmap - toneAC - NewTone - TimerFreeTone

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:
Code: [Select]
#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 :-)



Again Thank you Very Much ! ! !

Best Regards,
Tasos

warren631

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

Carl Cravens


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 :-)


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.

AWOL

Quote
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?
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Carl Cravens

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.

Carl Cravens


Quote
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.

teckel


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.


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:

Code: [Select]
#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
Arduino - Teensy - Raspberry Pi
My libraries: NewPing - LCDBitmap - toneAC - NewTone - TimerFreeTone

teckel


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.


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
Arduino - Teensy - Raspberry Pi
My libraries: NewPing - LCDBitmap - toneAC - NewTone - TimerFreeTone

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

Code: [Select]
#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]);
}

teckel


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
Arduino - Teensy - Raspberry Pi
My libraries: NewPing - LCDBitmap - toneAC - NewTone - TimerFreeTone

Go Up