Arduino hangs on pulseIn, timeout not working?

I have recently bought a Arduino Duemilanove (ATmega328) and two ultrasonic range finders and are now trying to create a 360deg distance sensor with these and a hobby servo.


Radar by tirithen, on Flickr

And the code

#include <Servo.h>

// setup pins
#define radarFrontEchoPin 2
#define radarFrontInitPin 3
#define radarBackEchoPin 4
#define radarBackInitPin 5
#define radarServoPin 6

unsigned long radarReadings[360]; // stores the distance radarReadings in an array

unsigned int radarAngle = 90; // Holds the angle of the radar servo
int radarDirection = 1;
int radarStepAngle = 5;
unsigned long radarSensorPulseTime = 0; // stores the pulse in Micro Seconds
Servo radarServo;

//setup
void setup() {
      pinMode(radarFrontInitPin, OUTPUT);
      pinMode(radarFrontEchoPin, INPUT);
      pinMode(radarBackInitPin, OUTPUT);
      pinMode(radarBackEchoPin, INPUT);

      radarServo.attach(radarServoPin);

      // create array loop to iterate over every item in the array
      for(long i = 0; i < 360; i++) {
            radarReadings[i] = 0;
      }

      // initialize the serial port, lets you view the
      // distances being pinged if connected to computer
      Serial.begin(115200);

      radarServo.write(radarAngle);
      delay(1000);
}

// execute
void loop() {
      readDistance();

      //~ for(long i = 0; i < 360; i++) {
            //~ Serial.print(i, DEC);
            //~ Serial.print(',');
            //~ Serial.println(radarReadings[i], DEC);
      //~ }

      //Serial.println(averageDistance, DEC); // print out the average distance to the debugger
      //~ delay(50);
      Serial.println("tick");

      delay(1000);
}

void readDistance() {

      radarReadings[radarAngle] = getUltraSoundMeasurement(radarFrontInitPin, radarFrontEchoPin);
      radarReadings[radarAngle + 180] = getUltraSoundMeasurement(radarBackInitPin, radarBackEchoPin);

      Serial.println("Front:");
      Serial.println(radarAngle, DEC);
      Serial.println(radarReadings[radarAngle], DEC);
      Serial.println("Back:");
      Serial.println(radarAngle + 180, DEC);
      Serial.println(radarReadings[radarAngle + 180], DEC);
      Serial.print('-');
      Serial.print('-');
      Serial.print('-');
      Serial.print('-');
      Serial.print('-');
      Serial.print('-');
      Serial.println('-');
      
      if(radarDirection == 1) {
            if(radarAngle + radarStepAngle >= 179) {
                  radarAngle = 179;
                  radarDirection = 0;
            }
            else {
                  radarAngle = radarAngle + radarStepAngle;
            }
      }
      else {
            if(radarAngle - radarStepAngle + 100 <= 100) {
                  radarAngle = 0;
                  radarDirection = 1;
            }
            else {
                  radarAngle = radarAngle - radarStepAngle;
            }
      }

      radarServo.write(radarAngle);
}

unsigned long getUltraSoundMeasurement(int initPin, int echoPin) {
      digitalWrite(initPin, HIGH); // send 10 microsecond pulse
      delayMicroseconds(10); // wait 10 microseconds before turning off
      digitalWrite(initPin, LOW); // stop sending the pulse

      radarSensorPulseTime = pulseIn(echoPin, HIGH); // Look for a return pulse, it should be high as the pulse goes low-high-low

      return radarSensorPulseTime/58; // return distance = pulse time / 58 to convert to cm.
}

//~ unsigned long pulseLength(int pin, long unsigned timeout) {
      //~ unsigned long pulseBegin = millis();
      //~ while (digitalRead(pin) == HIGH){
            //~ if(millis() > pulseBegin + timeout) {
                  //~ break;
            //~ }
      //~ }
//~ 
      //~ return millis()-pulseBegin;
//~ }

The sensors are DYP-ME007. In the function getUltraSoundMeasurement the sensor are sent a 10µs pulse on one pin and I directly after use pulseIn to mesure the length of the pulse. By dividing the length of the pulse with 58 I get a cm distance that is accurate.

Now to the problem, after a random number of loops, sometimes directly, sometimes after 50 readings the arduino hangs on the pulseIn line and I have to reset the arduino (I think that it sometimes restarts itself after some time). I have tried with different timeout values on the pulseIn but it does not matter.

Why does it hang? Should it not give up if not receiving any pulse or the pulse won't end? How do I prevent this?

There is an optional 3rd argument for the pulseIn function that defines how long to wait. No 3rd argument == forever.

360 unsigned long values uses 1,440 bytes. That's a significant portion of the memory you have available. I'd try cutting that in half, and take readings ever 2 degrees, to see if that makes things work better.

There is no reason to wait 1 full second for the servo to get in position, unless it is one incredibly slow servo.

The loop index i, in setup(), is a long. The values that i can take on are nowhere large enough to require more than an int.

Yes I know that I am using a lot of memory, the ideal would be to use 9-bits maybe 10-bits for each mesurement since the max distance that the sensors can mesure is ~500cm.
The one second delay between each measurement are just for debugging.
The 3:rd argument was the one I meant by timeout, I have tried that but the arduino still freezes.

Edit: I did not realize that the int type of variables was 16-bit, thanks for the tip. I thought they where 8-bit. Thanks

I have now changed the variable types and added 30000µs as timeout on pulseIn.

#include <Servo.h>

// setup pins
#define radarFrontEchoPin 2
#define radarFrontInitPin 3
#define radarBackEchoPin 4
#define radarBackInitPin 5
#define radarServoPin 6

unsigned int radarReadings[360]; // stores the distance radarReadings in an array

byte radarAngle = 90; // Holds the angle of the radar servo
byte radarDirection = 1;
byte radarStepAngle = 5;
unsigned long radarSensorPulseTime = 0; // stores the pulse in Micro Seconds
Servo radarServo;

//setup
void setup() {
      pinMode(radarFrontInitPin, OUTPUT);
      pinMode(radarFrontEchoPin, INPUT);
      pinMode(radarBackInitPin, OUTPUT);
      pinMode(radarBackEchoPin, INPUT);

      radarServo.attach(radarServoPin);

      // create array loop to iterate over every item in the array
      for(unsigned int i = 0; i < 360; i++) {
            radarReadings[i] = 0;
      }

      // initialize the serial port, lets you view the
      // distances being pinged if connected to computer
      Serial.begin(115200);

      radarServo.write(radarAngle);
      delay(500);
Serial.println("Starting...");
}

// execute
void loop() {
      readDistance();

      //~ for(unsigned int i = 0; i < 360; i++) {
            //~ Serial.print(i, DEC);
            //~ Serial.print(',');
            //~ Serial.println(radarReadings[i], DEC);
      //~ }

      //Serial.println(averageDistance, DEC); // print out the average distance to the debugger
      //~ delay(50);

      delay(100);
}

void readDistance() {
      radarReadings[radarAngle] = getUltraSoundMeasurement(radarFrontInitPin, radarFrontEchoPin);
      radarReadings[radarAngle + 180] = getUltraSoundMeasurement(radarBackInitPin, radarBackEchoPin);

Serial.println("Front:");
Serial.println(radarAngle, DEC);
Serial.println(radarReadings[radarAngle], DEC);
Serial.println("Back:");
Serial.println(radarAngle + 180, DEC);
Serial.println(radarReadings[radarAngle + 180], DEC);
Serial.print('-');
Serial.print('-');
Serial.print('-');
Serial.print('-');
Serial.print('-');
Serial.print('-');
Serial.println('-');

      if(radarDirection == 1) {
            if(radarAngle + radarStepAngle >= 179) {
                  radarAngle = 179;
                  radarDirection = 0;
            }
            else {
                  radarAngle = radarAngle + radarStepAngle;
            }
      }
      else {
            if(radarAngle - radarStepAngle + 100 <= 100) {
                  radarAngle = 0;
                  radarDirection = 1;
            }
            else {
                  radarAngle = radarAngle - radarStepAngle;
            }
      }

      radarServo.write(radarAngle);
}

unsigned long getUltraSoundMeasurement(byte initPin, byte echoPin) {
      digitalWrite(initPin, HIGH); // send 10 microsecond pulse
      delayMicroseconds(10); // wait 10 microseconds before turning off
      digitalWrite(initPin, LOW); // stop sending the pulse

      radarSensorPulseTime = pulseIn(echoPin, HIGH, 30000); // Look for a return pulse, it should be high as the pulse goes low-high-low

      return radarSensorPulseTime / 58; // return distance = pulse time / 58 to convert to cm.
}

..but it still hangs, it seemes as it hangs when there is nothing for the signal to bounce against within the range. It seemes that the 3:rd argument on pulseIn does not work.

Could it have something to do with the pulseIn function beeing called from getUltraSoundMeasurement that are called from readDistance that are called from loop (see the code above)? That pulseIn don't know where to continiue when it times out?

When I lower the timeout value, the program seem to freeze at lower distances. That could indicate that the timeout is working but that the program fails to continiue code execution at the right place... Could it be something like this?

IDE version is 0018 by the way.

You were quite right! There is a bug in the code when the signal stays high.

unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)
{
      // cache the port and bit of the pin in order to speed up the
      // pulse width measuring loop and achieve finer resolution.  calling
      // digitalRead() instead yields much coarser resolution.
      uint8_t bit = digitalPinToBitMask(pin);
      uint8_t port = digitalPinToPort(pin);
      uint8_t stateMask = (state ? bit : 0);
      unsigned long width = 0; // keep initialization out of time critical area
      
      // convert the timeout from microseconds to a number of times through
      // the initial loop; it takes 16 clock cycles per iteration.
      unsigned long numloops = 0;
      unsigned long maxloops = microsecondsToClockCycles(timeout) / 16;
      
      // wait for any previous pulse to end
      while ((*portInputRegister(port) & bit) == stateMask)
            if (numloops++ == maxloops)
                  return 0;
      
      // wait for the pulse to start
      while ((*portInputRegister(port) & bit) != stateMask)
            if (numloops++ == maxloops)
                  return 0;
      
[b][color=#ff0000]      // wait for the pulse to stop
      while ((*portInputRegister(port) & bit) == stateMask)
            width++;[/color][/b]

      // convert the reading to microseconds. The loop has been determined
      // to be 10 clock cycles long and have about 16 clocks between the edge
      // and the start of the loop. There will be some error introduced by
      // the interrupt handlers.
      return clockCyclesToMicroseconds(width * 10 + 16); 
}

Would this change generate accurate readings? or will it be inaccurate since if-statement + width increment take longer than just width increment?

unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)
{
// cache the port and bit of the pin in order to speed up the
// pulse width measuring loop and achieve finer resolution. calling
// digitalRead() instead yields much coarser resolution.
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
uint8_t stateMask = (state ? bit : 0);
unsigned long width = 0; // keep initialization out of time critical area

// convert the timeout from microseconds to a number of times through
// the initial loop; it takes 16 clock cycles per iteration.
unsigned long numloops = 0;
unsigned long maxloops = microsecondsToClockCycles(timeout) / 16;

// wait for any previous pulse to end
while ((*portInputRegister(port) & bit) == stateMask)
if (numloops++ == maxloops)
return 0;

// wait for the pulse to start
while ((*portInputRegister(port) & bit) != stateMask)
if (numloops++ == maxloops)
return 0;

// wait for the pulse to stop
while ((portInputRegister(port) & bit) == stateMask)
** if (width++ == maxloops)
*
** return 0;**
** //width++;**

// convert the reading to microseconds. The loop has been determined
// to be 10 clock cycles long and have about 16 clocks between the edge
// and the start of the loop. There will be some error introduced by
// the interrupt handlers.
return clockCyclesToMicroseconds(width * 10 + 16);
}

The if test adds clock cycles, but the comments indicate that the number of clock cycles per iteration is taken into consideration. If you were to determine how many clock cycles the if test adds, and adjust the number of clock cycles per iteration, the accuracy shouldn't be negatively impacted.

Do you have any idea of how to measure the clock cycle count?
I do not have any oscilloscope and not much knowledge of arduino and limited knowledge of assembly...
I guess that it's the 10 that should be increased a bit on the last line of the function.

return clockCyclesToMicroseconds(width * 10 + 16);

Would it be something like 12 maybe?

Yes, but to what value exactly?

The simplest test is this:

  • Generate a well defined pulse at the pin; you can do this just internally using analogWrite() and connect both pins!
  • Print what the originat version says X; now recalculate wait =(X-16)/10
  • Now the modified version; it will print Y (a little bit smaller): recalculate wait2 = (Y-16)/10
    But Y should be X, so what factor to choose instead of f=10?

wait2 = (X-16)/f = (wait10+16-16)/f = wait10/f
f = wait/wait2 *10

Yeah. What he said.

What did he say?

Thanks, I'll try with analogWrite

Thanks for all the help, the value turned out to be 19 after measured and calculated as deSilva suggested.

Here is a video of my implementation :slight_smile: