combining NewPing, ServoSweep, and 'if-else' decision code

Hello all.
I have been working on a robot car that will do the following things:

Psuedo Code:
Sweep one time with Ping sensor attached to servo looking for a target
If no target found, drive forward while sweeping with Ping sensor until a target is detected
When target is detected, move towards target automatically changing direction to keep target centered in driving direction
Stop when target is predetermined distance away
Reverse if target is too close (closer than predetermined distance)

It seems to just keep looking but does not find a target unless I move something close to it.

Here is the loop that should be doing all this:

void loop()
{
  // long cm, duration;

  // Move servo:
  posOldDistance = (pos);
  pingOldDistance = (chkDistanceFwd);
  panServo.write(pos); 

  // Define next servo position:
  if (dir == 1)
  {
    if(pos < maxSweep)
    {
      pos += 10;
    }
    else
    {
      dir = -1;
    }
  }
  else if(dir == -1)
  {
    if(pos > minSweep)
    {
      pos -= 10;
    }
    else
    {
      dir = 1;
    } 
  }

  // Notice how there's no delays in this sketch to allow you to do other processing in-line while doing distance pings.
  if (millis() >= pingTimer) 
  {   // pingSpeed milliseconds since last ping, do another ping.
    pingTimer += pingSpeed;      // Set the next ping time.
    sonar.ping_timer(echoCheck); // Send out the ping, calls "echoCheck" function every 24uS where you can check the ping status.

  }
  // Do other stuff here, really. Think of it as multi-tasking.

  // print results to the serial port in the form "# #", or "degrees distance":
  // The Processing sketch is designed to read them in this form.
   Serial.print("  chkDistanceFwd=");
   Serial.print(chkDistanceFwd);
   Serial.print("  dangerThresh="); //displays value of dangerThresh to validate operation 
   Serial.print(dangerThresh); 
   Serial.print("  rightDistance=");  
   Serial.print(rightDistance);
   Serial.print("  leftDistance="); 
   Serial.print(leftDistance); 
   Serial.print(" ");
   Serial.print("  posOldDistance=");
   Serial.print(posOldDistance);
   Serial.print(" ");
   Serial.print("  posNewDistance=");
   Serial.print(posNewDistance);
   Serial.print(" ");
   Serial.print("  pos=");
   Serial.print(" ");
   Serial.print(pos);
   Serial.print(" ");
   Serial.print(" pingCloseDistance=");
   Serial.print(pingCloseDistance);
   Serial.print(" ");
   

  // give servo a break:
  //  delay(15);
}

//--------------------------------------

void echoCheck() 
{ // Timer2 interrupt calls this function every 24uS where you can check the ping status.
  // Don't do anything here!
  if (sonar.check_timer()) 
  { // This is how you check to see if the ping was received.
    // Here's where you can add code.
     Serial.print(" Ping: ");
     Serial.print(sonar.ping_result / US_ROUNDTRIP_CM); // Ping returned, uS result in ping_result, convert to cm with US_ROUNDTRIP_CM.
     Serial.println("cm");
    chkDistanceFwd = (sonar.ping_result / US_ROUNDTRIP_CM);

    posNewDistance =(pos);
    pingNewDistance = (sonar.ping_result / US_ROUNDTRIP_CM);

    if (pingNewDistance > pingOldDistance)
    {
      (pingCloseDistance) = (pingOldDistance);
    }
    else
    {
      (pingCloseDistance) = (pingNewDistance);
    }

    if (posOldDistance <= 89 || posNewDistance <= 89)//0 is to the right
      {
        (rightDistance) = (pingCloseDistance);
      }

      else if (posOldDistance >= 91 || posNewDistance >= 91)//180 is to the left
        {
          (leftDistance) = (pingCloseDistance);
        }
      else
      {
        (pingCloseDistance) = (chkDistanceFwd);
      }


        /*  if (chkDistanceFwd <= dangerThresh)//if target is <= minimum programmed distance. STOP
         {
         bothWheelStop();
         } */

        if (chkDistanceFwd > dangerThresh && chkDistanceFwd <= 60)//if target distance is greater than programmed default and <= 60 cm go straight medium speed
        {
          forwardMed();
        }

        else if (chkDistanceFwd > dangerThresh)
        {
          if (chkDistanceFwd <= rightDistance || chkDistanceFwd <= leftDistance) //if target distance is greater than programmed default, go straight
          {
            forward();
          }

          if (chkDistanceFwd > rightDistance || leftDistance > rightDistance) //if target distance is greater than programmed default AND if target is closer on the RIGHT side...turn RIGHT to face target
          {  
            turnRight();
          }

          if (chkDistanceFwd > leftDistance || rightDistance > leftDistance) //if target distance is greater than programmed default AND if target is closer on the LEFT side...turn LEFT to face target 
          {  
            turnLeft();
          }

          if (chkDistanceFwd <= dangerThresh)//if target is <= minimum programmed distance. STOP
          {
            bothWheelStop();
            reverse();
          } 
        }
        // Don't do anything here!
      }
    }
  // long cm, duration;

Do you really think that commented out code is part of your problem? My keyboard comes with a delete key. Doesn't yours?

  posOldDistance = (pos);
  pingOldDistance = (chkDistanceFwd);

What are the parentheses for? They make you look clueless. Remove them, so they don't do that.

Of course, we have no idea if any of that makes sense, since we can't see how any of the variables are defined.

  if (millis() >= pingTimer) 
  {   // pingSpeed milliseconds since last ping, do another ping.
    pingTimer += pingSpeed;      // Set the next ping time.
    sonar.ping_timer(echoCheck); // Send out the ping, calls "echoCheck" function every 24uS where you can check the ping status.

  }

Every so often, register a callback. Once registered, that callback is called on a regular basis. Doing this makes no sense.

  // Do other stuff here, really. Think of it as multi-tasking.

No, don't think of it as multi-tasking. It isn't.

It seems to just keep looking but does not find a target unless I move something close to it.

You mean that it doesn't find a target unless there is one? That makes sense, doesn't it?

Nothing in loop() does anything (useful) except move the servo (that presumably carries the ultrasonic sensor).

     Serial.print(" Ping: ");
     Serial.print(sonar.ping_result / US_ROUNDTRIP_CM); // Ping returned, uS result in ping_result, convert to cm with US_ROUNDTRIP_CM.
     Serial.println("cm");

You can't do serial output in an ISR. Serial output relies on interrupts, which are disabled during an ISR (which is what echoCheck() is).

    chkDistanceFwd = (sonar.ping_result / US_ROUNDTRIP_CM);

    posNewDistance =(pos);
    pingNewDistance = (sonar.ping_result / US_ROUNDTRIP_CM);

More useless parentheses.

    if (pingNewDistance > pingOldDistance)
    {
      (pingCloseDistance) = (pingOldDistance);
    }
    else
    {
      (pingCloseDistance) = (pingNewDistance);
    }

And more.

    if (posOldDistance <= 89 || posNewDistance <= 89)//0 is to the right
      {
        (rightDistance) = (pingCloseDistance);
      }

      else if (posOldDistance >= 91 || posNewDistance >= 91)//180 is to the left
        {
          (leftDistance) = (pingCloseDistance);
        }
      else
      {
        (pingCloseDistance) = (chkDistanceFwd);
      }

The comments here make no sense. Distances have nothing to do with angles. The way that the servo is pointing has nothing to do with what makes a reasonable upper or lower limit for distance.

Some comments (and removing the distracting parentheses littering your code) would be extremely useful to understand what you are trying (and, unfortunately failing) to accomplish here.

Thanks for your reply PaulS.
// long cm, duration; has been deleted. It was leftover from a previous attempt.

As for all the parentheses, when I was looking at all the serial print results to check the variable values, the values always displayed whatever was set as the default which is 400 for distances. Once I added the parentheses, the values started displaying correctly.

Code:

if (millis() >= pingTimer)
  {  // pingSpeed milliseconds since last ping, do another ping.
    pingTimer += pingSpeed;      // Set the next ping time.
    sonar.ping_timer(echoCheck); // Send out the ping, calls "echoCheck" function every 24uS where you can check the ping status.

}



Every so often, register a callback. Once registered, that callback is called on a regular basis. Doing this makes no sense.

This is directly from the #include <NewPing.h> Library NewPingEventTimer example. I did not make any changes to this. I understand this is part of the 'interrupt' feature of this library.

It seems to just keep looking but does not find a target unless I move something close to it.

You mean that it doesn't find a target unless there is one? That makes sense, doesn't it?

The range of the Ping sensor is over 15 feet so I would expect objects within this range to be detected. The robot only reacted to the object when I moved it to within 12 inches or so but it did follow the object very slowly with many pauses.

I see that I need to add additional comments to some sections to better identify what the code is supposed to be doing. I started to combine the Servo Sweep example with the Ping example but found the NewPingEventTimer example and thought it would be better. Since this does not seem to be the case, I will rework the code and see what I can come up with. I saw some other folks using arrays to store the distance returned from each servo position so I may try that as well once I understand how that works.

As for all the parentheses, when I was looking at all the serial print results to check the variable values, the values always displayed whatever was set as the default which is 400 for distances. Once I added the parentheses, the values started displaying correctly.

Sorry. This doesn't make sense. I'd need to see some code to illustrate this issue.

This is directly from the
Code:

#include <NewPing.h>

Library NewPingEventTimer example.

That doesn't make it right.

PaulS:

As for all the parentheses, when I was looking at all the serial print results to check the variable values, the values always displayed whatever was set as the default which is 400 for distances. Once I added the parentheses, the values started displaying correctly.

Sorry. This doesn't make sense. I'd need to see some code to illustrate this issue.

This is directly from the
Code:

#include <NewPing.h>

Library NewPingEventTimer example.

That doesn't make it right.

The "millis() >= pingTimer" section is done to avoid using a timer interrupt and to allow for more timed events at different intervals. The NewPing method being used already uses a timer interrupt to ping in the background (which is multitasking, as you incorrectly stated). There's not enough timers available with a ATmega328 microcontroller to waste them. With a typical sketch of a complicated system there's many timed events all happening at different intervals to be able to use interrupts for all of them.

I'm not defending drewship's sketch, obviously he's new to coding. But, some things you picked apart with his sketch were perfectly correct, and not written by drewship at all.

Tim

The NewPing method being used already uses a timer interrupt to ping in the background (which is multitasking, as you incorrectly stated).

It doesn't do squat "in the background". There are no foregrounds and backgrounds on the Arduino. When the timer fires, and triggers an interrupt, the code that was running STOPS while the interrupt is processed. Then, the interrupt STOPS and the code that was running resumes running.

There is only part of the code executing at a time. That is NOT multitasking.

But, some things you picked apart with his sketch were perfectly correct

Which ones?

PaulS:

The NewPing method being used already uses a timer interrupt to ping in the background (which is multitasking, as you incorrectly stated).

It doesn't do squat "in the background". There are no foregrounds and backgrounds on the Arduino. When the timer fires, and triggers an interrupt, the code that was running STOPS while the interrupt is processed. Then, the interrupt STOPS and the code that was running resumes running.

There is only part of the code executing at a time. That is NOT multitasking.

But, some things you picked apart with his sketch were perfectly correct

Which ones?

Sounds like someone needs to better understand the definition of "multitasking". Your incorrect definition is quite naive and cute ...

Tim

OK, that will do. Please post constructive comments. If you wish to take issue with PaulS' concept of multitasking do so without all the comments about his ignorance. Thank you.

  • moderator

I'm going to have to agree with PaulS here. Processing an interrupt is not multi-tasking.

In computing, multitasking is a method where multiple tasks, also known as processes, are performed during the same period of time. The tasks share common processing resources, such as a CPU and main memory. In the case of a computer with a single CPU, only one task is said to be running at any point in time, meaning that the CPU is actively executing instructions for that task. Multitasking solves the problem by scheduling which task may be the one running at any given time, and when another waiting task gets a turn. The act of reassigning a CPU from one task to another one is called a context switch.

There is no second process here which is context-switched in. It may be a matter of semantics, a bit, but I do not think that an ISR is properly called another process.


        (rightDistance) = (pingCloseDistance);

As for all the parentheses, when I was looking at all the serial print results to check the variable values, the values always displayed whatever was set as the default which is 400 for distances. Once I added the parentheses, the values started displaying correctly.

That explanation does not appear to me to support the parentheses above.

[quote author=Nick Gammon link=topic=127414.msg959742#msg959742 date=1350434864]
I'm going to have to agree with PaulS here. Processing an interrupt is not multi-tasking.

In computing, multitasking is a method where multiple tasks, also known as processes, are performed during the same period of time. The tasks share common processing resources, such as a CPU and main memory. In the case of a computer with a single CPU, only one task is said to be running at any point in time, meaning that the CPU is actively executing instructions for that task. Multitasking solves the problem by scheduling which task may be the one running at any given time, and when another waiting task gets a turn. The act of reassigning a CPU from one task to another one is called a context switch.

There is no second process here which is context-switched in. It may be a matter of semantics, a bit, but I do not think that an ISR is properly called another process.

http://en.wikipedia.org/wiki/Computer_process[/quote]

Maybe you're missing that there is a second process, the library which is context-switched in every 24uS. I believe you should read this:

Specifically, under the section: "Cooperative multitasking/time-sharing"

Clearly, using a timer interrupt with Arduino is exactly the definition of multitasking, and this is exactly what is happening in this library. I'm actually amazed that this is even a debate. The Arduino can easily do multitasking. Sure, most Arduino programmers don't follow an event-driven multitasking programming paradigm. But, it easily multitasks as well as say the Amiga OS, early MacOS or early WinOS.

And for those that believe multitasking means that two or more things must occur at the exact same time, that is not the case. Again, see the above link. Using a timer interrupt with Arduino is exactly multitasking (of the time-sharing variety).

Tim

PaulS:

     Serial.print(" Ping: ");

Serial.print(sonar.ping_result / US_ROUNDTRIP_CM); // Ping returned, uS result in ping_result, convert to cm with US_ROUNDTRIP_CM.
    Serial.println("cm");



You can't do serial output in an ISR. Serial output relies on interrupts, which are disabled during an ISR (which is what echoCheck() is).

While it's true that you can't do serial output in an ISR, he can do serial output here as "sonar.check_timer()" being true has already determined that the ping process has completed and therefore the interrupt service is no longer occurring. So, his code here is 100% correct and works just fine. It's probably outside the scope of this thread to explain the inner-workings of the NewPing library, but doing anything once check_timer() has been set to true is perfectly okay as the interrupt has been canceled at this point.

Tim

teckel:
While it's true that you can't do serial output in an ISR, he can do serial output here as "sonar.check_timer()" being true has already determined that the ping process has completed and therefore the interrupt service is no longer occurring. So, his code here is 100% correct and works just fine.

I think you are wrong here. echoCheck is not called, as a function, directly anywhere in the sketch. Therefore it is called by an ISR.

... the interrupt service is no longer occurring ...

It calls timer_stop() which stops that timer causing further interrupts. It has not turned interrupts on. Thus interrupts are still disabled.

... but doing anything once check_timer() has been set to true is perfectly okay as the interrupt has been canceled at this point.

You are confused, sorry. This does not sit well with your lecturing tone. Inside an ISR you don't "cancel interrupts". The best you could do is re-enable them, not a wise procedure. However an inspection of the library shows it does not do that. It does not call interrupts () nor sei(). At least not in the copy I have.

PaulS is perfectly correct. The OP is doing serial prints, inside an ISR. This is not recommended, and is likely to cause the sketch to hang when the serial buffer fills up, and interrupts - being disabled - it cannot be emptied.

Keep in mind that we're talking about a library that I wrote, so I probably have a better idea of what it's doing :wink: The routine is not exactly an ISR, it's a wrapped function inside an ISR. The interrupt is canceled when sonar.check_timer() is true, so you're free to do whatever you want inside the sonar.check_timer() if statement, including serial output. I wrote the library and it's working right here in front of me, serial output and all. Even one of my example sketches for the timer interrupt method works in exactly this way:

http://code.google.com/p/arduino-new-ping/wiki/Ping_Event_Timer_Sketch

It calls timer_stop() which stops that timer causing further interrupts. It has not turned interrupts on. Thus interrupts are still disabled.

Not true. Interrupts are enabled, serial output works fine, see above sketch.

You are confused, sorry. This does not sit well with your lecturing tone. Inside an ISR you don't "cancel interrupts". The best you could do is re-enable them, not a wise procedure. However an inspection of the library shows it does not do that. It does not call interrupts () nor sei(). At least not in the copy I have.

You're mistaken, sorry, and it works perfectly as shown. Keep in mind that we're talking about a library that I wrote, so I probably have a little more knowledge about this than you do. You're making assumptions that are incorrect. You're obviously a very proud programmer, and I'm not challenging that. However, you must concede that I probably have a little more working knowledge of this particular library.

PaulS is perfectly correct. The OP is doing serial prints, inside an ISR. This is not recommended, and is likely to cause the sketch to hang when the serial buffer fills up, and interrupts - being disabled - it cannot be emptied.

That's a fine and well educated guess, but not the cause of the problem in this case.

Tim

teckel:
Keep in mind that we're talking about a library that I wrote, so I probably have a better idea of what it's doing

What one person writes another one can understand.

teckel:
That's a fine and well educated guess, but not the cause of the problem in this case.

I'm grateful to you for writing this library, and since you know what the problem isn't, perhaps you can assist the OP with what the problem is.

I'll leave you to it. Thanks.

[quote author=Nick Gammon link=topic=127414.msg960767#msg960767 date=1350503364]

teckel:
Keep in mind that we're talking about a library that I wrote, so I probably have a better idea of what it's doing

What one person writes another one can understand.[/quote]

As it would seem, that may not be true in this case. :wink:

[quote author=Nick Gammon link=topic=127414.msg960767#msg960767 date=1350503364]
I'm grateful to you for writing this library, and since you know what the problem isn't, perhaps you can assist the OP with what the problem is.

I'll leave you to it. Thanks.[/quote]

I don't have a horse in this race. But, it's quite obvious that there's a lot of very questionable programming in this partial sketch. I think we can all agree that the complexity of the project appears to be above the programming skill of the user. I've yet to see the entire code (only the main loop and the echoCheck routine). If the entire sketch was provided, I'd take a look at it. As it is now, it could be as simple as how the NewPing object was called (for example, max_distance set to 10cm).

Tim

Thanks for the dialog PaulS, Nick, and teckel. I am including the entire sketch knowing full well that I have items commented out, probably some items that I may have been left over from previous attempts and are not used here, parenthesis where they may not be needed, Boolean logic instead of if-else statements. Although identifying them is important, if those errors do not actively run in the sketch, I do not see how they could be adversely affecting it, but I am just trying to learn.

// ---------------------------------------------------------------------------
// This example shows how to use NewPing's ping_timer method which uses the Timer2 interrupt to get the
// ping time. The advantage of using this method over the standard ping method is that it permits a more
// event-driven sketch which allows you to appear to do two things at once. An example would be to ping
// an ultrasonic sensor for a possible collision while at the same time navigating. This allows a
// properly developed sketch to multitask. Be aware that because the ping_timer method uses Timer2,
// other features or libraries that also use Timer2 would be effected. For example, the PWM function on
// pins 3 & 11 on Arduino Uno (pins 9 and 11 on Arduino Mega) and the Tone library. Note, only the PWM
// functionality of the pins is lost (as they use Timer2 to do PWM), the pins are still available to use.
// NOTE: For Teensy/Leonardo (ATmega32U4) the library uses Timer4 instead of Timer2.
// ---------------------------------------------------------------------------

#include <Servo.h> 
#include <NewPing.h>

long duration, inches, cm, cmRight, cmLeft; //time it takes to recieve PING))) signal

long microsecondsToInches(long microseconds)
{
  return microseconds / 74 / 2;
}
long microsecondsToCentimeters(long microseconds)
{
  return microseconds / 29 / 2;
}

#define TRIGGER_PIN  8  // Arduino pin tied to trigger pin on ping sensor.
#define ECHO_PIN     8  // Arduino pin tied to echo pin on ping 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.

unsigned int pingSpeed = 50; // How frequently are we going to send out a ping (in milliseconds). 50ms would be 20 times a second.
unsigned long pingTimer;     // Holds the next ping time.

//--------------------------------------
// User globals:

//rightServo stops at 1390
//leftServo stops at 1400
const int RForward = 500; //speed value of drive servo, value 500 CW, 2500 CCW for full speed rotation
const int RBackward = 2400; //speed value of drive servo, value 500 CW, 2500 CCW for full speed rotation
const int RForwardMed = 1330; //1200 speed value of drive servo, value 500 for full speed rotation
const int RBackwardMed = 1550; //speed value of drive servo, value 2500 for full speed rotation
const int RForwardSlow = 1310; //speed value of drive servo, value 500 for full speed rotation
const int RBackwardSlow = 1485; //speed value of drive servo, value 2500 for full speed rotation
const int LForward = 1521; //1532 the servos must rotate in opposite directions in order to drive in the same direction
const int LBackward = 500; //the servos must rotate in opposite directions in order to drive in the same direction
const int LForwardMed = 1460; //the servos must rotate in opposite directions in order to drive in the same direction
const int LBackwardMed = 1330; //the servos must rotate in opposite directions in order to drive in the same direction
const int LForwardSlow = 1430; //the servos must rotate in opposite directions in order to drive in the same direction
const int LBackwardSlow = 1315; //the servos must rotate in opposite directions in order to drive in the same direction
const int dangerThresh = 45; //threshold for obstacles (in cm), this was 10 by default
const int dangerReverseThresh = 20; //threshold for obstacles (in cm), this was 10 by default
int leftDistance, rightDistance, distanceFwd, chkDistanceFwd; //distances on either side
int delayTime = 2;
//int pos = 90;    // variable to store the servo position
int posOldRightDistance = 400;
int posOldLeftDistance = 400;
int posOldDistance = 400;
int posNewDistance = 400;
int pingOldDistance = 400;
int pingNewDistance = 400;
int pingCloseDistance = 400;

// The default settings have a 90 deg sweep, centered on 'straight ahead'.
int minSweep = 45;//45
int maxSweep = 135;//135

int pingPin = 8;  // ping))) signal (digital) pin.
int servoPin = 5; // servo signal (digital) pin.

Servo pingServo; //pings the servo when called
Servo panServo; //servo that pans the ping sensor 
Servo leftServo;  //continuous rotation servo that drives the left wheel
Servo rightServo; //continuous rotation servo that drives the right wheel

//--------------------------------------
// System globals:
int pos = minSweep;    // variable to store the servo position 
int dir = 1; // positive or negative to define direction

//--------------------------------------

void forward()
{
  rightServo.writeMicroseconds(RForward); //move forward    
  leftServo.writeMicroseconds(LForward); //move forward
}

void forwardMed()
{
  rightServo.writeMicroseconds(RForwardMed); //move forward    
  leftServo.writeMicroseconds(LForwardMed); //move forward
}

void bothWheelStop()
{
  rightServo.writeMicroseconds(1390);
  leftServo.writeMicroseconds(1400);
}

void reverse() //if they are equally obstructed
{
  rightServo.writeMicroseconds(RBackwardSlow); //rotate right drive servo backward  
  leftServo.writeMicroseconds(LBackwardSlow); //rotate left drive servo forward
  delay(100); //run motors this many milliseconds
  bothWheelStop();
}

void turnLeft() //if left is less obstructed 
{
  rightServo.writeMicroseconds(RForwardSlow); //rotate right drive servo forward "skid steer"
  leftServo.writeMicroseconds(LBackwardSlow); //rotate left drive servo backward "skid steer"
  delay(100); //run motors this many milliseconds
  //  bothWheelStop();  
}

void turnRight() //if right is less obstructed 
{
  rightServo.writeMicroseconds(RBackwardSlow); //rotate right drive servo backward "skid steer"
  leftServo.writeMicroseconds(LForwardSlow); //rotate left drive servo forward "skid steer"
  delay(100); //run motors this many milliseconds
  //  bothWheelStop(); 
}

//THIS IS THE END OF PART 1...PASTE PART 2 HERE

Here is part 2...

void setup() 
{ 
  //  Serial.begin(115200); // Open serial monitor at 115200 baud to see ping results.
  pingTimer = millis(); // Start now.
  chkDistanceFwd = 400;
  rightDistance = 400;
  leftDistance = 400;
  posOldRightDistance = 400;
  posOldLeftDistance = 400;
  posOldDistance = 400;
  posNewDistance = 400;
  pingNewDistance = 400;
  pingNewDistance = 400;
  pingCloseDistance = 400;
  rightServo.writeMicroseconds(1390);
  leftServo.writeMicroseconds(1400);
  rightServo.attach(6); //signal wire on right countinuous rotation "drive" servo connected to this arduino pin
  leftServo.attach(7);  //signal wire on left continuous rotation "drive servo connected to this arduino pin
  panServo.attach(5); //signal wire on pan servo for ping sensor connected to this arduino pin
  panServo.write(90); //set PING))) pan to center "90 by default"
  Serial.begin(9600);
} 

//--------------------------------------
void loop()
{
  // Move servo:
  posOldDistance = (pos);
  pingOldDistance = (chkDistanceFwd);
  panServo.write(pos); 

  // Define next servo position:
  if (dir == 1)
  {
    if(pos < maxSweep)
    {
      pos += 10;
    }
    else
    {
      dir = -1;
    }
  }
  else if(dir == -1)
  {
    if(pos > minSweep)
    {
      pos -= 10;
    }
    else
    {
      dir = 1;
    } 
  }

  // Notice how there's no delays in this sketch to allow you to do other processing in-line while doing distance pings.
  if (millis() >= pingTimer) 
  {   // pingSpeed milliseconds since last ping, do another ping.
    pingTimer += pingSpeed;      // Set the next ping time.
    sonar.ping_timer(echoCheck); // Send out the ping, calls "echoCheck" function every 24uS where you can check the ping status.

  }
  // Do other stuff here, really. Think of it as multi-tasking.

  // print results to the serial port in the form "# #", or "degrees distance":
  // The Processing sketch is designed to read them in this form.
   Serial.print(" ");
   Serial.print("  chkDistanceFwd=");
   Serial.print(chkDistanceFwd);
   Serial.print(" ");
   Serial.print("  dangerThresh="); //displays value of dangerThresh to validate operation 
   Serial.print(dangerThresh); 
   Serial.print(" ");
   Serial.print("  rightDistance=");  
   Serial.print(rightDistance);
   Serial.print(" ");
   Serial.print("  leftDistance="); 
   Serial.print(leftDistance); 
   Serial.print(" ");
   Serial.print("  posOldDistance=");
   Serial.print(posOldDistance);
   Serial.print(" ");
   Serial.print("  posNewDistance=");
   Serial.print(posNewDistance);
   Serial.print(" ");
   Serial.print("  pos=");
   Serial.print(pos);
   Serial.print(" ");
   Serial.print(" pingCloseDistance=");
   Serial.print(pingCloseDistance);
   Serial.print(" ");
   

  // give servo a break:
    delay(15);
}

//--------------------------------------

void echoCheck() 
{ // Timer2 interrupt calls this function every 24uS where you can check the ping status.
  // Don't do anything here!
  if (sonar.check_timer()) 
  { // This is how you check to see if the ping was received.
    // Here's where you can add code.
     Serial.print(" Ping: ");
     Serial.print(sonar.ping_result / US_ROUNDTRIP_CM); // Ping returned, uS result in ping_result, convert to cm with US_ROUNDTRIP_CM.
     Serial.println("cm");
    chkDistanceFwd = (sonar.ping_result / US_ROUNDTRIP_CM);

    posNewDistance =(pos);
    pingNewDistance = (sonar.ping_result / US_ROUNDTRIP_CM);

    if (pingNewDistance > pingOldDistance)
    {
      (pingCloseDistance) = (pingOldDistance);
    }
    else
    {
      (pingCloseDistance) = (pingNewDistance);
    }

    if (posOldDistance <= 89 || posNewDistance <= 89)//0 is to the right
      {
        (rightDistance) = (pingCloseDistance);
      }

      else if (posOldDistance >= 91 || posNewDistance >= 91)//180 is to the left
        {
          (leftDistance) = (pingCloseDistance);
        }
      else
      {
        (pingCloseDistance) = (chkDistanceFwd);
      }


        /*  if (chkDistanceFwd <= dangerThresh)//if target is <= minimum programmed distance. STOP
         {
         bothWheelStop();
         } */

        if (chkDistanceFwd > dangerThresh && chkDistanceFwd <= 60)//if target distance is greater than programmed default and <= 60 cm go straight medium speed
        {
          forwardMed();
        }

        else if (chkDistanceFwd > dangerThresh)
        {
          if (chkDistanceFwd <= rightDistance || chkDistanceFwd <= leftDistance) //if target distance is greater than programmed default, go straight
          {
            forward();
          }

          if (chkDistanceFwd > rightDistance || leftDistance > rightDistance) //if target distance is greater than programmed default AND if target is closer on the RIGHT side...turn RIGHT to face target
          {  
            turnRight();
          }

          if (chkDistanceFwd > leftDistance || rightDistance > leftDistance) //if target distance is greater than programmed default AND if target is closer on the LEFT side...turn LEFT to face target 
          {  
            turnLeft();
          }

          if (chkDistanceFwd <= dangerThresh)//if target is <= minimum programmed distance. STOP
          {
            bothWheelStop();
            reverse();
          } 
          /* Serial.print("Ping: ");
           Serial.print(sonar.ping_result / US_ROUNDTRIP_CM); // Ping returned, uS result in ping_result, convert to cm with US_ROUNDTRIP_CM.
           Serial.println("cm");*/
        }
        // Don't do anything here!
      }
    }

I do have another sketch that does work (mostly) but is jerky and unreliable, sometimes turning for no apparent reason and detecting a target to the side of the desired target. That is why I am trying to construct a better sketch. Thanks for all the help.

Adding a link to the youtube video of the robot car using this code: - YouTube

@drewship
I have had similar issues with a distance sensor on a servo. After lots of investigation it turned out that servo caused voltage spikes which makes the analog reads in Arduino unreliable.
If you have a scope use the scope to see whether you have this issue. If you do not have a scope add a big capacitor on the current line to the servo as close as possible to the servo.

@teckel
You may not like what you will read but don't get me wrong. It is clear to me you have quite some coding knowledge. The fact you made a library available also makes it clear you want to support the Arduino community. However you have been showing wrong in this post.

As to the multi tasking

Nick is referring to the link and copied the section in particular to the thread so there is no misunderstanding what he is talking about.
What he clearly states is that there is no multitasking as there is no second process. You could argue that the bootloader is a second process but this is not what is meant with multi tasking as discussed here.
So I agree with Paul and Nick that there is no multi tasking on Arduino for now (maybe when the due arrives; I don't know)

teckel:
Maybe you're missing that there is a second process, the library which is context-switched in every 24uS. I believe you should read this:

In arduino world a library is source code (not even pre compiled) that is linked in to the hex process. I'm not willing to discuss whether this method is multithreading or not but it is sure is not multi tasking. This because multi tasking assumes 2 random processes (in Arduino world that would be 2 hex files) can run together on the same CPU. As a library is linked into a runnable hex process it can not be called another process.

As to calling names
The comment below may have been intended as a pun but it doesn't come this way. I feel you own Nick a apology.

teckel:

[quote author=Nick Gammon ]
[What one person writes another one can understand.

As it would seem, that may not be true in this case. :wink:
[/quote]
And the below one is pretty insulting; surely when Paul is right and you are wrong.

Sounds like someone needs to better understand the definition of "multitasking". Your incorrect definition is quite naive and cute ...

As to "yes", "no" discussions.
Discussion held on content are less likely to become emotionally. With yes no discussion emotions become high very quickly. That is why Nick (and all moderators on this forum) gives background information and tries to describe as exact as possible what is at hand. You on the other hand prefer to be "not involved, not accurate" There are many examples but the one below is very clear.

teckel:

It calls timer_stop() which stops that timer causing further interrupts. It has not turned interrupts on. Thus interrupts are still disabled.

Not true. Interrupts are enabled, serial output works fine, see above sketch.

You are confused, sorry. This does not sit well with your lecturing tone. Inside an ISR you don't "cancel interrupts". The best you could do is re-enable them, not a wise procedure. However an inspection of the library shows it does not do that. It does not call interrupts () nor sei(). At least not in the copy I have.

You're mistaken, sorry, and it works perfectly as shown. Keep in mind that we're talking about a library that I wrote, so I probably have a little more knowledge about this than you do. You're making assumptions that are incorrect. You're obviously a very proud programmer, and I'm not challenging that. However, you must concede that I probably have a little more working knowledge of this particular library.

As to your attitude

teckel:

[quote author=Nick Gammon link=topic=127414.msg960767#msg960767 date=1350503364]

teckel:
Keep in mind that we're talking about a library that I wrote, so I probably have a better idea of what it's doing

What one person writes another one can understand.

As it would seem, that may not be true in this case. :wink:

All forums work on people liking to show off their knowledge by helping people. Members trying to show off by attacking members who try to help are generally seen as people with a attitude problem. I think you have enough Arduino knowledge to show off. It would be a shame that you and the community would have to suffer the consequences of your attitude problem

Best regards
Jantje

PS Note that you stated that you do not like the lecturing style of Nick. Because of your behavior and because this is a public forum you actually forced me to use a lecture style as well.

@Drewship
I have looked at your code and at the library code. Even though I do not claim I understand all of it: it is clear to me that Nick is correct and your function

void echoCheck()

is part of a interrupt routine and should as such comply with the interrupt routine rules.
You can read more on interrupt routines and the rules on:

The easy solution I see is to set a flag in echoCheck() when sonar.check_timer() is true. And then handle and unset the flag in your loop().
Note that this flag needs to be tagged as volatile as you can read in the article I mention above.
This will probably not fix all issues in the program but it is a problem you will have to fix because this is biting you for sure.
Another option would be that teckel fixes this issue in his library.

Best regards
Jantje

Thanks for all the comments. Like it was stated here, this is more advanced than my knowledge so I have taken a step back to my original code that I was trying to enhance. Here is a video of my car using this code. - YouTube. I commented out code that I thought would help fine tune the detection process but that seemed not to function as I thought it would. I tried to implement the comments from this and other threads as well so hopefully I have done this correctly.

The way the robot moves backward in the beginning and during its travel does not make sense to me since the target (binder) is several feet away and is outside the dangerReverseThresh value. Also can't figure out why it turns to the right and left when the center is clearly closer than the right or left. Since I point the ping sensor directly at the target, it should drive straight.

#include <Servo.h> //include Servo library

long duration, inches, cm, cmRight, cmLeft; //time it takes to receive PING))) signal
long microsecondsToInches(long microseconds)
{
  return microseconds / 74 / 2;
}
long microsecondsToCentimeters(long microseconds)
{
  return microseconds / 29 / 2;
}

//rightServo stops at 1390...
//leftServo stops at 1400
const int RForward = 500; //speed value of drive servo, value 500 CW, 2500 CCW for full speed rotation
const int RBackward = 2400; //speed value of drive servo, value 500 CW, 2500 CCW for full speed rotation
const int RForwardMed = 1330; //1330 speed value of drive servo, value 500 for full speed rotation
const int RBackwardMed = 1550; //1550 speed value of drive servo, value 2500 for full speed rotation
const int RForwardSlow = 1310; //1310 speed value of drive servo, value 500 for full speed rotation
const int RBackwardSlow = 1485; //1485 speed value of drive servo, value 2500 for full speed rotation
const int LForward = 1517; //1521 the servos must rotate in opposite directions in order to drive in the same direction
const int LBackward = 500; //500the servos must rotate in opposite directions in order to drive in the same direction
const int LForwardMed = 1460; //1460the servos must rotate in opposite directions in order to drive in the same direction
const int LBackwardMed = 1330; //1330the servos must rotate in opposite directions in order to drive in the same direction
const int LForwardSlow = 1430; //1430the servos must rotate in opposite directions in order to drive in the same direction
const int LBackwardSlow = 1315; //0315the servos must rotate in opposite directions in order to drive in the same direction
const int pingPin = 8;  //signal pin on parallax ping sensor connected to this arduino pin "remember the ping sensor is 5V, not source"
const int dangerThresh = 47; //threshold for obstacles (in cm), this was 10 by default
const int dangerReverseThresh = 20; //threshold for obstacles (in cm), this was 10 by default
const int medSpeedDistance = 60; //threshold to drive at medium speed
int leftDistance, rightDistance, distanceFwd, chkDistanceFwd; //distances on either side
int delayTime = 2;
int pos = 90;    // variable to store the servo position

Servo pingServo; //pings the servo when called
Servo panServo; //servo that pans the ping sensor 
Servo leftServo;  //continuous rotation servo that drives the left wheel
Servo rightServo; //continuous rotation servo that drives the right wheel

void ping()
{  
  pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin, LOW);
  pinMode(pingPin, INPUT);
  duration = pulseIn(pingPin, HIGH);
}

void forward()
{
  rightServo.writeMicroseconds(RForward); //move forward    
  leftServo.writeMicroseconds(LForward); //move forward
}

void forwardMed()
{
  rightServo.writeMicroseconds(RForwardMed); //move forward    
  leftServo.writeMicroseconds(LForwardMed); //move forward
}

void bothWheelStop()
{
  rightServo.writeMicroseconds(1390);
  leftServo.writeMicroseconds(1400);
}

void reverse() //if they are equally obstructed
{
  rightServo.writeMicroseconds(RBackwardSlow); //rotate right drive servo backward  
  leftServo.writeMicroseconds(LBackwardSlow); //rotate left drive servo forward
  delay(100); //run Servos this many milliseconds
  bothWheelStop();
}

void turnLeft() //if left is less obstructed 
{
  rightServo.writeMicroseconds(RForwardSlow); //rotate right drive servo forward "skid steer"
  leftServo.writeMicroseconds(LBackwardSlow); //rotate left drive servo backward "skid steer"
  delay(100); //run Servos this many milliseconds
  bothWheelStop();  
}

void turnRight() //if right is less obstructed 
{
  rightServo.writeMicroseconds(RBackwardSlow); //rotate right drive servo backward "skid steer"
  leftServo.writeMicroseconds(LForwardSlow); //rotate left drive servo forward "skid steer"
  delay(100); //run Servos this many milliseconds
  bothWheelStop(); 
}

void setup()
{
  duration = 400;
  chkDistanceFwd = 400;
  rightDistance = 400;
  leftDistance = 400;
  rightServo.writeMicroseconds(1390);
  leftServo.writeMicroseconds(1400);
  rightServo.attach(6); //signal wire on right countinuous rotation "drive" servo connected to this arduino pin
  leftServo.attach(7);  //signal wire on left continuous rotation "drive servo connected to this arduino pin
  panServo.attach(5); //signal wire on pan servo for ping sensor connected to this arduino pin
  panServo.write(pos); //set PING))) pan to center "90 by default"
  delay(500);        //wait this many milliseconds WAS 500
  Serial.begin(9600);
}

void loop()
{
  ping(); 
  chkDistanceFwd = microsecondsToCentimeters(duration); //ping stright ahead 
  panServo.write(60); //turn ping sensor right, 0 for full right
  delay(500);        //wait this many milliseconds WAS 500
  ping();
  rightDistance = microsecondsToCentimeters(duration);
  panServo.write(120); //turn ping sensor left, 180 for full right 
  delay(500);  //wait this many milliseconds WAS 500
  ping();
  leftDistance = microsecondsToCentimeters(duration);
  panServo.write(pos); //return to center, "90 by default"
  delay(500);  //wait this many milliseconds

  // convert the time into a distance
  inches = microsecondsToInches(duration);
  cm = microsecondsToCentimeters(duration);

  /* Serial.print("in = ");
   Serial.print(inches);        
   Serial.print(" ");  
   Serial.print("cm = ");          
   Serial.print(cm);            
   Serial.print(" ");
   Serial.print("  dangerThresh = "); //displays value of dangerThresh to validate operation 
   Serial.print(dangerThresh);   
   Serial.print(" ");
   Serial.print("  chkDistanceFwd = "); //displays value of chkDistanceFwd to validate operation
   Serial.print(chkDistanceFwd);
   Serial.print(" ");
   Serial.print("  rightDistance = "); //displays value of rightDistance to validate operation  
   Serial.print (rightDistance);
   Serial.print(" ");
   Serial.print("  leftDistance = "); //displays value of leftDistance to validate operation 
   Serial.print (leftDistance);  
   Serial.println();            //Prints to Serial Monitor*/


  if (chkDistanceFwd <= dangerReverseThresh)// || rightDistance <= dangerReverseThresh || leftDistance <= dangerReverseThresh)
  {
    reverse();
  }

  if (chkDistanceFwd <= dangerThresh)// || rightDistance <= dangerThresh || leftDistance <= dangerThresh) //if target is <= minimum programmed distance. STOP
  {
    bothWheelStop();
  }

  else if (chkDistanceFwd > dangerReverseThresh && chkDistanceFwd < medSpeedDistance)
    /* {
     if (rightDistance > dangerReverseThresh && rightDistance < medSpeedDistance)
     {
     if (leftDistance > dangerReverseThresh && leftDistance < medSpeedDistance)*/
  {
    forwardMed();
  }

  else if (chkDistanceFwd > dangerThresh)// && rightDistance > dangerThresh && leftDistance > dangerThresh)
    /* {
     if (chkDistanceFwd <= rightDistance)
     {
     if (chkDistanceFwd <= leftDistance) //if target distance is greater than programmed default, go straight*/
  {
    forward();
  }

  if (rightDistance < chkDistanceFwd)// || rightDistance < leftDistance) //if target distance is greater than programmed default AND if target is closer on the RIGHT side
    // ..turn RIGHT to face target
  {  
    turnRight();
  }

  if (leftDistance < chkDistanceFwd)// || leftDistance < rightDistance) //if target distance is greater than programmed default AND if target is closer on the LEFT side.
    // .turn LEFT to face target 
  {  
    turnLeft();
  }

  /*  if (chkDistanceFwd <= dangerThresh || rightDistance <= dangerThresh || leftDistance <= dangerThresh) //if target is <= minimum programmed distance. STOP
   {
   bothWheelStop();
   }*/

  /*  else if (chkDistanceFwd <= dangerReverseThresh || rightDistance <= dangerReverseThresh || leftDistance <= dangerReverseThresh) //if target is <= programmed danger distance, BACKWARDS
   {
   bothWheelStop();
   reverse();
   }*/
}
// }
//}