Servo stops working unexpectedly. Seems some code is skipped over.

Hi everyone! This sketch is for an obstacle avoiding robot. I just started programming this sketch and already I'm running into a snag. The servo stops responding after the do...while loop in the Full_Scan() function. The servo sweeps from 0 to 180 but does not return back to the midpoint after the sweep. Does anyone have an idea why this happens?

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

#define PING_PIN 14  //Analog pin A0
#define MAX_DISTANCE 200 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400.

#define SERVO_DELAY_15 100    //Delay time for servo to travel 15 degrees
#define SERVO_DELAY_90 250   //Delay time for servo to travel 90 degrees

Servo Ping_Servo;
NewPing sonar(PING_PIN, PING_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

long Ping_Array[13];    //Stores all ping distances. Represents Left(0 degrees)--> Forward(90)--> Right(180) in 15 degree increments

void setup()  {
  Serial.begin(57600);
  Serial.println("Ready!");

  Ping_Servo.attach(10);      //Turn on servo by assigning it pin 10. Also set min and max pulse widths
  Ping_Servo.write(90);      //Move servo to midpoint
  delay(SERVO_DELAY_90);     //Give servo time to get to midpoint

  Full_Scan();      //Initialize Ping_Array[]
  Print_Ping_Array();
}

void loop()  {

}

void Full_Scan()  {      //Performs a full sweep with the Ping_Servo and fills Ping_Array[] with distance in centimeters
  int i = 0;      //Counter
  int Degrees = 0;      //Servo position
  long Next_Time = 0;

  Ping_Servo.write(Degrees);      //Get servo in position to start sweep... 0 degrees
  Next_Time = millis() + SERVO_DELAY_90;

  do  {    
      if (millis() >= Next_Time)  {
        Ping_Array[i] = sonar.ping_cm();
        Degrees = Degrees + 15;
        Ping_Servo.write(Degrees);
        Next_Time = millis() + SERVO_DELAY_15;
        i++;
      }
  } 
  while (i <= 13);

  //This doesn't work!
  Ping_Servo.write(90);
  delay(SERVO_DELAY_90);     //Give servo time to get to midpoint
}

void Print_Ping_Array()  {
  int i;
  Serial.println("Displaying Ping_Array");
  for (i = 0; i <= 12; i++)  {
    Serial.print("Element "); Serial.print(i, DEC); Serial.print(": "); Serial.print(Ping_Array[i], DEC); Serial.print("cm");
    Serial.println("");
  }
}

I was able to get the servo working using the following code but I'm curious to why the above code doesn't work.

void Full_Scan()  {      //Performs a full sweep with the Ping_Servo and fills Ping_Array[] with distance in centimeters
  int i = 0;      //Counter
  int Degrees = 0;      //Servo position
  long Next_Time = 0;

  Ping_Servo.write(Degrees);      //Get servo in position to start sweep... 0 degrees
  Next_Time = millis() + SERVO_DELAY_90;

  do  {
    if (i <= 12)  {    
      if (millis() >= Next_Time)  {
        Ping_Array[i] = sonar.ping_cm();
        Degrees = Degrees + 15;
        Ping_Servo.write(Degrees);
        Next_Time = millis() + SERVO_DELAY_15;
        i++;
      }
    }
    else  {
      Ping_Servo.write(90);
      i++;
    }
  } 
  while (i <= 14);
}

Your first code is running off the end of the array:

 long Ping_Array[13];
 int i = 0;      //Counter
  do  {    
        Ping_Array[i] = sonar.ping_cm();
        i++;
  } 
  while (i <= 13);

Valid indexes in the Ping_Array are 0 to 12. You are writing to index 13.

Since Full_Scan() doesn't return until done you can greatly simplify the code:

void Full_Scan()  {      //Performs a full sweep with the Ping_Servo and fills Ping_Array[] with distance in centimeters
  for (int i=0; i<13; i++) {
        Ping_Servo.write(i*15);
        delay(SERVO_DELAY_15);
        Ping_Array[i] = sonar.ping_cm();
  } 

  Ping_Servo.write(90);
  delay(SERVO_DELAY_90);     //Give servo time to get to midpoint
}

Using a do/while loop to iterate a fixed number of times is like using a screwdriver for a pry bar. Use the correct construct - a for loop.

PaulS:
Using a do/while loop to iterate a fixed number of times is like using a screwdriver for a pry bar. Use the correct construct - a for loop.

Which reminds me of the thing a software programmer once told be about screwdrivers, that they come with plus or negative ends. :wink:

Lefty

johnwasser:
Your first code is running off the end of the array:

 long Ping_Array[13];

int i = 0;      //Counter
 do  {    
       Ping_Array[i] = sonar.ping_cm();
       i++;
 }
 while (i <= 13);




Valid indexes in the Ping_Array are 0 to 12. You are writing to index 13.

I'm pretty sure the last time "i" is used to access Ping_Array it is 12. "i" gets updated afterwards... Am I mistaken?

Also, the reason I used the do..while loop instead of a for loop was because I am trying to avoid using the delay() function. I always read about avoiding the delay function because it makes the sketch unresponsive.

Edit: The for loop definitely makes the code cleaner looking though.

Also, the reason I used the do..while loop instead of a for loop was because I am trying to avoid using the delay() function. I always read about avoiding the delay function because it makes the sketch unresponsive.

The delay(x) function can make a sketch unresponsive because it's a 'blocking function' and nothing else in the sketch flow can execute until the delay function 'times out' after x milliseconds. Look at the blink without delay example sketch in the IDE for the best method to time events without blocking program flow.

Lefty

Bill2k:
I'm pretty sure the last time "i" is used to access Ping_Array it is 12. "i" gets updated afterwards... Am I mistaken?

You are mistaken. When element 12 is written and 'i' is incremented to 13 the while loop sees that (i <= 13) is true so it continues to loop and element 13 is written on the next iteration. Only when 'i' is incremented to 14 does the loop end.

PaulS:
Using a do/while loop to iterate a fixed number of times is like using a screwdriver for a pry bar. Use the correct construct - a for loop.

Craftman screwdrivers make excellent pry bars as long as there is a Sears nearby :wink:

johnwasser:
You are mistaken. When element 12 is written and 'i' is incremented to 13 the while loop sees that (i <= 13) is true so it continues to loop and element 13 is written on the next iteration. Only when 'i' is incremented to 14 does the loop end.

Thanks for pointing that out. I'm not sure why it looked right to me in the first place.

retrolefty:
The delay(x) function can make a sketch unresponsive because it's a 'blocking function' and nothing else in the sketch flow can execute until the delay function 'times out' after x milliseconds. Look at the blink without delay example sketch in the IDE for the best method to time events without blocking program flow.

I'm not quite following you here... Are you saying because the function doesn't return till its done, then its ok to use delay. But if other functions had to run also, then used the Blink without delay method?

Bill2k:

johnwasser:
You are mistaken. When element 12 is written and 'i' is incremented to 13 the while loop sees that (i <= 13) is true so it continues to loop and element 13 is written on the next iteration. Only when 'i' is incremented to 14 does the loop end.

Thanks for pointing that out. I'm not sure why it looked right to me in the first place.

retrolefty:
The delay(x) function can make a sketch unresponsive because it's a 'blocking function' and nothing else in the sketch flow can execute until the delay function 'times out' after x milliseconds. Look at the blink without delay example sketch in the IDE for the best method to time events without blocking program flow.

I'm not quite following you here... Are you saying because the function doesn't return till its done, then its ok to use delay. But if other functions had to run also, then used the Blink without delay method?

I'm saying that any use of delay() can be a killer in sketches where you want multiple independent tasks to do their thing without having to wait for the individuals tasks to complete their functions. Use of the millis() and micros() functions and keeping 'time stamps' variables and flags for each individual task updated is a way to have your main loop function continue to loop through all the individual tasks to see if something needs to be done in them or not in each pass of the main loop, therefore always having time to keep up with the needs of each individual task. An airline traffic controller can keep track of and service many planes in the air at the same time. He does not just work with just one plane and only service the next plane when finished with the first one. Your sketch can operate just as well as that air traffic controller does.

Lefty