Recursion Problem with Arduino

My Arduino Duemilanova will run my (semi-infinite) recursive program 929 times before freezing, crashing, etc. Does anyone know why this may be and if there is a simple way around it. (i.e not having to re-logic the code)

Any thoughts are appreciated.

Code is below if you care to look through it, there are some troubleshooting print statements in there as well as some commented out code that I’m not using ATM b/c it doesn’t meet my requirements.

/* Ping))) Sensor
 
 This sketch reads a PING))) ultrasonic rangefinder and returns the
 distance to the closest object in range. To do this, it sends a pulse
 to the sensor to initiate a reading, then listens for a pulse 
 to return.  The length of the returning pulse is proportional to 
 the distance of the object from the sensor.
 
 created 3 Nov 2008
 by David A. Mellis
 modified 30 Jun 2009
 by Tom Igoe
 
 This example code is in the public domain.
 */

/* Auto Brake Car */

//Global Varibles:
const int watchdog= 2, button= 3, mpower= 5, dir= 6, pingPin= 7, brakeled= 8, headlight= 9;

//function declarations
void watchdogled();
void sonar();
void motor();
long int microsecondsToCentimeters(long int microseconds);
long int microsecondsToCentimeters(long int microseconds);

//onetime setups
void setup() 
{
  Serial.begin(9600);         // initialize serial communication:
  pinMode(watchdog, OUTPUT);
  pinMode(button, INPUT);
  pinMode(mpower, OUTPUT);
  pinMode(dir, OUTPUT); 
  pinMode(brakeled, OUTPUT);
  pinMode(headlight, OUTPUT);
}

//driver
void loop()
{
  Serial.println("begin driver loop");
  //watchdog call
  watchdogled();
  digitalWrite(headlight, HIGH); //turn on headlights.
  delay(3000);                   //wait for user to move away
  Serial.println("sonar call");
  sonar();
  Serial.println("end driver loop");
}

void watchdogled()
{
  Serial.println("begin watchdog");
  digitalWrite(brakeled, LOW);       //make sure lights are off
  digitalWrite(headlight, LOW);
  while(digitalRead(button) == HIGH) //run watchdogled while button isn't pressed
  {
    digitalWrite(watchdog, HIGH);    //turn on LED
    for(int i=0; i<10; i++)          //waits 250ms checking every 25 for a button press
    {
      delay(25);
      if(digitalRead(button) == LOW) //if button pressed we return to the driver function
      {
        digitalWrite(watchdog, LOW);
        return;
      }
    }

    digitalWrite(watchdog, LOW);
    for(int i=0; i<30; i++)         //waits 750ms while checking for button press
    {
      delay(25);
      if(digitalRead(button) == LOW)
      {
        digitalWrite(watchdog, LOW);
        return;
      }
    }
  }
  Serial.println("end watchdog");
} 

void sonar()
{
  Serial.println("begin sonar funct");
  /*local varibles:
   establish variables for duration of the ping, 
   and the distance result in inches and centimeters:*/
  long int duration, inches, cm;
  int stat= 0;

  // do
  // {
  /*The PING))) is triggered by a HIGH pulse of 2 or more microseconds.
   Give a short LOW pulse beforehand to ensure a clean HIGH pulse:*/
  pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin, LOW);

  /* The same pin is used to read the signal from the PING))): a HIGH
   pulse whose duration is the time (in microseconds) from the sending
   of the ping to the reception of its echo off of an object.*/
  pinMode(pingPin, INPUT);
  duration = pulseIn(pingPin, HIGH);

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

  Serial.print(inches);
  Serial.print("in, ");
  Serial.print(cm);
  Serial.print("cm");
  Serial.println();


  //Brake Lights and engine braking
  if (cm <= 13)
  {
    Serial.println("if statement");
    digitalWrite(brakeled, HIGH);
    digitalWrite(dir, HIGH);
    digitalWrite(mpower, HIGH);   // a quick reverse spurt
    delay(125);
    digitalWrite(mpower, LOW);
    return;                       // return to driver
  }
  else
  {
    Serial.println("motor call");
    motor();                      // call motor to go forward.
  }

  //}while(cm >= 13);
  Serial.println("end sonar funct");
}

void motor()
{
  Serial.println("begin motor funct");
  digitalWrite(brakeled, LOW);
  digitalWrite(dir, LOW);
  digitalWrite(mpower, HIGH); 
  //delay(50);
  //digitalWrite(mpower, LOW);
 // delay(100);
  sonar();                    // call sonar to check distance. This uses recursion to loop until car is near an object
  Serial.println("end motor funct");
}
long int microsecondsToInches(long int microseconds)
{
  /* According to Parallax's datasheet for the PING))), there are
   73.746 microseconds per inch (i.e. sound travels at 1130 feet per
   second).  This gives the distance travelled by the ping, outbound
   and return, so we divide by 2 to get the distance of the obstacle.
   See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf */
  return microseconds / 74 / 2;
}

long int microsecondsToCentimeters(long int microseconds)
{
  /* The speed of sound is 340 m/s or 29 microseconds per centimeter.
   The ping travels out and back, so to find the distance of the
   object we take half of the distance travelled. */
  return microseconds / 29 / 2;
}

Whenevere you use recursion you use memory.

Each recursive call must push the returnadress on the stack.

My guess is that you simply run ot of memory after a certain "depth of recursion" is reached, due to the very limited amount of RAM available on the Arduino.

If that is the case, there is really not much you can do about that without totally restructuring your code to do what you want without recusion, which can sometimes be difficult.

The way you have Sonar() call Motor() and then Motor() call Sonar() is probably the problem. Each call will push a return adress on the stack, but the call never returns, so the return adresses are never removed from the stack so the stack just grows until all (the very limited) RAM is used, and then your program crashes.

My Arduino Duemilanova will run my (semi-infinite) recursive program 929 times before freezing, crashing, etc. Does anyone know why this may be

Certainly. An Arduino (like all physical processors) has a limited amount of memory. Your Sketch crawls through the stack until the stack bumps into the data area, corrupting the data area, causing the program to crash. In other words, your Sketch runs out of memory.

and if there is a simple way around it.

Looks to me like it would be rather simple to restructure your Sketch to not use recursion.

(i.e not having to re-logic the code)

It that case, you’re stuffed.

Any thoughts are appreciated.

Don’t use recursion.

Thanks! MikMo , and CB.

Until now I didn't know that recursion used up memory...

I'm not used to working within small memory limits yet. And I do feel that recursion is at least the easier way if not the better way to do this program.

My original program didn't have any recursion, but I need to use PWM on the motor and still read from the Sonar Range finder. I would ideally like to do this at the same time, but we know that's impossible so within a few microseconds is the goal.

If anyone has ideas on that front let me know otherwise I'm sure I'll figure it out eventually.

LOL figured it out....

There's a reason why I should never program on a 10" screen.

LOL figured it out…

Care to share for future users that might stumble upon this thread in a search?

What puzzles me about this one is how you decided on recursion as an appropriate technique.

Simple loop or finite-state machine probably, but never recursion.