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

I think I've been confusing in my expressions. The most recent activity was to update/upgrade from 1.0 to 1.0.1 and I've not had 023 ever. When I discovered NewPing, I felt it was an easier library to use and to understand than my limited experience with ultrasonic.h library.

The demo code was uploaded and the serial monitor displays one line of non-zero distance then displays continuing zero distance figures, regardless of real world conditions. I suspect the non-zero distance is an anomaly. If I press the reset button on the Uno, the serial monitor displays non-zero distance figures.

I'm going to load the code later today that uses the ultrasonic.h library to see if the results are the same. I'm suspecting that the device is at fault, as you suggested.

You may have the trigger and echo wires backwards. I have did it 2 or 3 times already and it didn't damage anything.

I've successfully created an interrupt-driven ping function and added it to my development NewPing. I'm probably going to add a few different interrupt types to satisfy different needs. This first one uses the timer1 interrupt. It works fine, but I'm not overly happy with one aspect and I'm hoping someone with experience has some insight.

In the sketch, it's currently being called like this:

void setup() {
  sonar.ping_timer1(); // This calls the ping
}

// Timer1 interrupt (I'm using prescale at 64 with the interrupt at 7 counts, in other words, every 28 microseconds this function is called).
ISR(TIMER1_OVF_vect) {
  if (sonar.ping_check()) {  // Check to see if we got a ping echo.
    Serial.print("Ping: ");
    Serial.print(sonar.convert_cm(sonar.ping_result));  // Print the result
    Serial.println("cm");
  }
}

Again, it works great. But, what I'd really like to do is clean it up a bit for the end-user. Instead, I'd like it to look like the following:

void setup() {
  sonar.ping_timer1(echoCheck); // This calls the ping and sets the interrupt function to "echoCheck".
}

void echoCheck() {
  if (sonar.ping_check()) {  // Check to see if we got a ping echo.
    Serial.print("Ping: ");
    Serial.print(sonar.convert_cm(sonar.ping_result));  // Print the result
    Serial.println("cm");
  }
}

Functionally, identical, just a little easier to understand for the end user. I understand the basic concept of adding the ISR(TIMER1_OVF_vect) in the library and setting it, but I just can't seem to get it to work without a compiler error. So, I went out looking for someone else who's done something similar and found the TimerOne library. The library is fairly simple so it's an easy read. Specifically, I'm interested in how he does the ISR(TIMER1_OVF_vect). I've tried to duplicate it, but it just won't compile. I could give my errors, but I've tried so many different ways that I've had maybe a half dozen different errors. I think I've nailed down where my problem is to here:

TimerOne Timer1;    // preinstatiate

ISR(TIMER1_OVF_vect)          // interrupt service routine that wraps a user defined function supplied by attachInterrupt
{
   Timer1.isrCallback(); 
}

Anyone know what this "preinstatiate" is? I would think I could in the case of NewPing do the following:

ISR(TIMER1_OVF_vect)          // interrupt service routine that wraps a user defined function supplied by attachInterrupt
{
   NewPing.isrCallback(); 
}

But, no. Anyone have any tips or could point me in the right direction on how I should implement having a function call set a function and wrap it insde an interrupt service routine?

Tim

15 minutes after I post all that, I think I have it figured out. I did some snooping around in Arduino.h and WInterrupts.c and I believe this works:

void (*intFunc)();

ISR(TIMER1_OVF_vect) {
	if(intFunc) intFunc();
}

With the ping_timer1 doing this:

void NewPing::ping_timer1(void (*userFunc)(void)) {
	intFunc = userFunc;

Note to self, go snooping in the Arduino code for tips in the future.

Tim

Hi Tim,
What object do you use to for your ultrasonic sensors to sense? Could you describe the conditions? I'm gonna try your library soon on my maxbotix sensor soon. It's wired the same way, so hopefully it works.

Better do not use print statements in interrupt routines as they take relative long times, while interrupts are made for fast responses.

every 28 microseconds this function is called).
ISR(TIMER1_OVF_vect) {

That means the code is re-entered every 28 micros.

the print statements wil take - 12 bytes * 10 bits / 115200 = about 1 millisecond // 10 includes start and stop bit, actually it is higher.

1 millisecond means ~35 times reentered.

Needs some rethinking I guess ...

TeslaIaint:
Hi Tim,
What object do you use to for your ultrasonic sensors to sense? Could you describe the conditions? I'm gonna try your library soon on my maxbotix sensor soon. It's wired the same way, so hopefully it works.

Objects can be named whatever you want, and you can use any number of objects. I use "sonar" for the object name in the Example Sketch. But, in the Two Ping Sensors Example Sketch I use "sonar1" and "sonar2" for the object names. I would imagine that most people would use "ping", "radar", "sonar", "ultrasonic" or something like that for the object name. But, you could have the object named "maxbotix" and have any number of objects defined, think "look_left", "look_right", "look_ahead".

The only condition is NO_ECHO, which is defined to 0 (false) meaning there was either no ping, or the ping was outside the set range. Basically, the NO_ECHO (false) condition is that there wasn't a ping. Other than that, it returns the distance or the ping time (depending on what method you used).

With the next release, there will also be at least one type of interrupt-based ping. The event-driven methods won't have a NO_ECHO condition. Because, as it's event-driven, there will only be an event when there's a ping echo within the range specified (the only condition will be that there's something in range). Basically, the goal of the event-driven methods is that you set a range to say 100cm and then you're off to the rest of your sketch, doing whatever the real goal of your sketch is. When the ping condition is met (something within 100cm let's say) you then do whatever action is needed in your sketch. You may have different things you need the sketch to do depending on what the ping distance was. That's up to your sketch to figure out.

Basically, there's two conditions, NO_ECHO (false) and echo (the distance to the object). For the interrupt methods, there's only "alarm" conditions, that something is in range, and obviously what that range is.

Tim

robtillaart:
Better do not use print statements in interrupt routines as they take relative long times, while interrupts are made for fast responses.

That's true. But, by the time the ping_check is called and it's been satisfied, the timer interrupt has already been canceled. So, the user is free to do whatever they want with the result. Heck, they can go right back to adding delay(2000) statements in their code for all I care! :wink:

But seriously, what you're not seeing in my little code snippet is what happens in the library before a true condition is met. It doesn't make total sense with what I posted as it wasn't the full code. But, be assured that I've made provisions so Serial.print or even long delays won't break anything. To insure things work for the average joe coder, my development test not only has those 3 print calls, but 6, and I added a delay(1000) in for good measure, and it still works just fine as the timer interrupt has already been canceled.

But, thanks for pointing out a potential deal-breaker if I hadn't thought about that already.

Tim

Hi Tim,
I meant to ask what kind of physical objects do you put in front of your sensors to test them?

TeslaIaint:
Hi Tim,
I meant to ask what kind of physical objects do you put in front of your sensors to test them?

Wall, chair, hand, box, coffee cup, etc. It will seriously see anything, even a fast moving object. One of my goals was to make the library fast enough where you quickly swing your hand in front of the sensor and it would detect it. As you can do pings every 37 milliseconds, it can sense a very fast moving object. Not a bullet, but plenty fast for just about any bot situation I can think of.

On the topic of these sensors on a bot. One of the reasons I starting writing this library was because one of my end goals was to create a fairly fast moving bot that could have several ultrasonic sensors that would work in combination that could quickly map out an environment and make fast decisions on direction without ever having to stop, delay, back-up, delay, turn, delay, move forward. If we can get readings 10 times a second from 3 sensors at different angles and use an algorithm to convert that data to a frontal map, I believe fairly effective navigation can be achieved. But, that's another library for another day...

Tim

Awesome! That's great news. I'm most interested in human detection, but we're definitely on the same page. I have been getting some pretty accurate and precise readings with my maxbotix sensors using the crap (code) that I came up with recently, but there's always room for improvement. It's not in a library, but it works. For my purposes I don't even need to be that accurate, but I need it to be reliable. I have several ultrasonic sensors that I've been messing with, but I always get these stray readings. One or two are fine within a half second, but more than that mucks up my purpose. I will try to implement your library with my sensors (maxbotix) soon, and hopefully get some quality results.

TeslaIaint:
Awesome! That's great news. I'm most interested in human detection, but we're definitely on the same page. I have been getting some pretty accurate and precise readings with my maxbotix sensors using the crap (code) that I came up with recently, but there's always room for improvement. It's not in a library, but it works. For my purposes I don't even need to be that accurate, but I need it to be reliable. I have several ultrasonic sensors that I've been messing with, but I always get these stray readings. One or two are fine within a half second, but more than that mucks up my purpose. I will try to implement your library with my sensors (maxbotix) soon, and hopefully get some quality results.

The biggest problem with the other ultrasonic libraries is that they wait for up to a second for the ping echo to return. During that time, the ATmega is doing nothing else but waiting. It can return quickly if it "sees" something (or should I say "hears" something), but if not it basically just hangs. NewPing doesn't do that. And with the new timer interrupt code that will make it in the next release, there's even less down time. It's also just as easy to use as before too.

Tim

While I have my development library working using Timer1 interrupt, I'm not totally happy with it because Timer1 is also used for the servo library. The problem with using Timer2 is that it's only 8 bit so you can't do a long timeout event. I'm trying to do as much as possible event driven, but at the same time I don't want to cause too many conflicts. It looks like I'll be creating two new timer methods. Using the Timer1 method will allow a more full-featured event-driven ping scheduler. Using the Timer2 method will have reduced features but won't conflict with the servo library (Timer2 does conflict with the tone library, but that shouldn't be a deal-breaker for most). When using a timer you also lose PWM control on two pins, but again, having two timer options allows you to use the one that would conflict with the rest of your project the least amount. Keep in mind you can still use these pins, you just can't use them for PWM.

In any case, there's been no new updates because I want to make sure I'm heading in the right direction before I release something half-baked and then have to change things down the road which may require sketches to be adjusted. I'd rather avoid a situation where an old sketch doesn't work as-is with a new library release. I hope to have something to release by Thursday, but that's if I have no snags.

Tim

The problem with using Timer2 is that it's only 8 bit so you can't do a long timeout event.

You could add a counter in the timer2 ISR that will check only every 10 overflows (sort of cascading)

volatile byte counter = myPreferedValue;
void ISR()
{
  if (counter-- == 0)
  {
    do your check
    counter = myPreferedValue;
  }
}

does that make sense?

robtillaart:

The problem with using Timer2 is that it's only 8 bit so you can't do a long timeout event.

You could add a counter in the timer2 ISR that will check only every 10 overflows (sort of cascading)

volatile byte counter = myPreferedValue;

void ISR()
{
  if (counter-- == 0)
  {
    do your check
    counter = myPreferedValue;
  }
}




does that make sense?

Very true, I was hoping to avoid making things overly complex.

On that note, I've decided to only use Timer2 in NewPing. Even though Timer1 was a little cleaner, I believe using Timer1 is a deal-breaker as the servo library uses Timer1 and ultrasonic sensors and servos are often found on the same project. So, this still makes the library cleaner as it doesn't need the clutter of both Timer1 and Timer2 functions that basically do the same thing (not to mention the existing ping method which uses the old method). 3 different ping methods that all did the same thing was kind of overkill anyway.

As a bonus, the next release of NewPing will include general-use Timer2 interrupt methods that you could use in non-ping parts of your sketch. I needed to break the timer interrupts up into individual functions anyway as they would be used multiple times, and why not at the same time allow these functions to be used in the rest of your sketch. For that matter, you could use NewPing just for the simple Timer2 interrupt methods, and it is very simple.

Tim

NewPing v1.3 Released, download here

New in v1.3

Big feature addition, event-driven ping! Uses Timer2 interrupt, so be mindful of PWM or timing conflicts messing with Timer2 may cause (namely PWM on pins 3 & 11 on Arduino, PWM on pins 9 and 11 on ATmega, and Tone library). Simple to use timer interrupt functions you can use in your sketches totaly unrelated to ultrasonic sensors (don't use if you're also using NewPing's ping_timer because both use Timer2 interrupts). Loop counting ping method deleted in favor of timing ping method after inconsistant results kept surfacing with the loop timing ping method. Conversion to cm and inches now rounds to the nearest cm or inch. Code optimized to save program space and fixed a couple minor bugs here and there. Many new comments added as well as line spacing to group code sections for better source readability.

NOTE: For Teensy/Leonardo (ATmega32U4) the library uses Timer4 instead of Timer2. Also, only 16Mhz microcontrollers are supported with the timer methods, which means the ATmega8 and ATmega128 will not work with the timer methods. However, the standard ping method should work just fine on 8Mhz microcontrollers.

Tim

Thank you for all your hard work! I will definitely try the new version out soon!

Many thanks for doing this.

I was researching which sensors to buy.

NewPing v1.3 came just in time to help me make the right decision.

Cheers

how can i use the library with ultrasonic ks103 sensor?
www.dauxi.com/KS10X-V110_EN.pdf
thank you

how can i use the library with ultrasonic ks103 sensor?

With great difficulty; it's an I2C device.