Let's Make Robots!

I feel dumb

It all started when I was programming PopPet. I was using the simple way of using the HC-SR04:

  long duration, distance;

  digitalWrite(trigPin, LOW); 

  delayMicroseconds(2);

  digitalWrite(trigPin, HIGH);

  delayMicroseconds(10);

  digitalWrite(trigPin, LOW); 

  duration = pulseIn(echoPin, HIGH);

  distance = (duration/2) / 29.1; 

This was all well and good except for one thing. If there is no echo from the ping, the pulseIn hangs and freezes the code for a whole second.

This wreaked havoc on the simplest obstacle avoidance routine as the bot would just spin in a circle. I wanted to conquer this delay, so I went to google and started looking for ways to be rid of the pulseIn.

My first thought was to run some sort of micro timer and manually calculate the distance by calculating the time it took to go high to low with a micros subtraction.

This didn't work for me. I figure I was too simplistic in my code or was barking up the wrong tree. So I decided to see if I could use a for loop. Using a for loop I could attempt to trigger the trig pin and set a timer, if no echo is heard in that time frame it exits and returns a 0. This also didn't work.

I came to the SB to see if someone could spark my next idea, Basile did not disappoint. He mentioned the NewPing library.

Many of you know that the NewPing library makes the use of such ultrasonic sensors much easier, no tricky timings are needed, you just need to tell it to trigger and it triggers.

One of the big benefits of this library though is the fact that is does not have this one second delay. I did no however want to import the whole library into the sketch as I am using the DAGU Mini Driver, a great board, but small on space.

Basile mentioned looking in the actual library files and seeing how they did it. Let's just say I spent way to long trying to figure it out. Being the first time looking at a library I was confused how it all linked and in the end I resorted to a bag of chips and Dr.Phil.

After my brief session watching stupid people, and making myself feel better, I returned to the conundrum at hand. I thought, hey, why not look up what the Arduino site has to say about pulseIn. This is when I realized I was dumb.

Little did I know that the pulseIn function actually has a timeout feature, and by default, this value is one second. All I had to do was set the timeout value much lower and guess what, the bastard of a thing worked perfectly. Absolutely no lag at all and incredibly quick sensor readings, faster than I can actually physically test.

Moral of the story? RTFM and don't be an dumb dumb head.

duration = pulseIn(echoPin, HIGH, 2000);

 

 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

thanks for posting this CP!  It's always good to know when others make mistakes and feel frustrated.  Helps keep the rest of us pushing on when we hit those bumps ourselves.  I always find that watching some mindless TV (my guilty pleasure is 80's action movies on Netflix) helps me solve problems.  I think it's because in the background my brain is ruminating on the problem without me focusing on it.

I wouldnt bother with

distance = (duration/2) / 29.1; 

Use this instead:

distance = duration / 58; 

The AVR cannot divide in hardware, it has to software emulate it which is slow. No point dividing twice if it can be done once. Also floating point cannot be done in hardware so again needs software emulating. The space and time wasted might not be significant on PopPet, but why waste it when easily avoided?

If you don't need a constant set of units to compare with other types of sensors, I'd forgo the division totally.

The duration variable is already proportional to the distance, and if you're comparing distance to some constant, you can #define constants for these distances that are already multiplied by 58.

If you need the distance in proper units, then you might be able do right shifts to divide by multiples of 2.

  1. You could check to see if the margin of error is such that dividing by 64 is good enough. That would be something like:

    distance = duration >> 3;

    If you have a good compiler it should translate a division by 64 into a right shift.

  2. If that isn't good enough, then to get an integer division by 58, you can factor 58 into the appropriate exponents of 2 and make multiple shifts to do this. Right now my pain pills have kicked in big time, so I will leave this as an exercise for the reader. Sorry.

I probably wouldn't do this sort of thing on a BBB or RasPi, but the AVR needs all the help it can get. :)

I learned this trick back in the day when I was programming on a 6502.

The compiler will almost always do constant calculations (like x/2/29) at compile time, instead of re-calculating it at runtime.  However, with floating point numbers they normally do NOT, because of the vagaries and inaccuracies of floating point math.  So, given it is changed to integers it should cause no harm.  But to my eye, it just "looks wrong."  The 0.1 additional is probably not really doing any good anyway since the speed of sound varies that much or more with temperature and pressure.

I was not aware it did float calculations on board rather than on compiling. I will definitely change that. I more so kept it apart just so I could look at it and see what it was doing. If I wrote 58 in there for example later on I would look at it and go 'whaa?'. Totally agree, no use having the 0.1 when there is a larger margin of error available which I can't control.

Writing code for clarity is one of the most important things you can do.  But, especially in the embedded world, when it impacts performance we often have to make sacrifices.  What I do in a case like that is do the arithmetic beforehand, put the new constant in the code, and put a comment on the line before or at the end of the line explaining what I did and why.  "comments are free" My new mantra

Good stuff, cp.  Thanks for posting putting your blemishes up for the world to see.  It's good for beginners to see more experienced programmers aren't perfect.

"comments are free" a man after my own heart.

Well you keep getting this delusion that I am some sort of smart person, have to remind you I'm not :)

I so agree with bdk. There are so many constraints that vary the speed of the sound waves that the same constant is not very accurate in all conditions. I guess the x/2/29 comes by taking the speed at 300m/s (correct me) but at 40 degrees celsius it can reach around the 360m/s mark. 

Nice to see CP himself stumble and rise !. :P Cool CP. :-)

 

That's why I put it up here Enig. Not afraid to let you all know how dumb I am :)

For the life of me I cannot work out why anyone bothered to write, or search for a library for ping sensors simply to replace a function which only has 3 or 4 lines of code.

Now you have answered the question for me. Because it is much easier to search for a library on the internet, download and install it, then read it's instruction manual instead of the Arduino reference that is already installed on your computer. Silly me.

Personally I have a low opinion of anyone who uses any sort of ping library. Below is my reason why.

//====================================================== Measure distance in cm with ultrasonic range finder ===========
void Ultrasound()
{
  digitalWrite(ustrigpin,1);                          // trigger ultrasonic pulse 
  delayMicroseconds(10);                              // wait 10uS
  digitalWrite(ustrigpin,0);                          // complete pulse trigger
  usdistance=int(pulseIn(usechopin,HIGH,17500)/58);   // measure width of output pulse from range finder in cm up to 3m
}

This is my function for reading a ping sensor (from my OB1 code). To take code as simple as this and make it into some sort of library to make it easier for people is like making 100Kg paperweights, yes it will hold the paper in place but you will need a crane to lift it enough to put the paper underneath.