Electronic – Arduino ultrasonic sensor

arduinoultrasound

I am attempting to read an HC-SR04 ultrasonic sensor at the same time a couple of servos are running. Having a servo running is messing up the timing of the pulseIn() method.

The distance measurement will be correct for small distances (up to 24 cm or so), but anything larger sometimes read incorrectly. If a server is not attached to D2 or D3, the distance sensor works fine. How can I get both to play together? Maybe a library that doesn't rely on the same timers as a server on D2 and D3? This is on a Mega.

// read the distance sensor
// returns: distance in inches. -1 means out of range
float ReadDistance(int trigPin, int echoPin)
{
  long duration;
  float distance;
  digitalWrite(trigPin, LOW);  // Added this line
  delayMicroseconds(2); // Added this line

  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10); // Added this line
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  distance = (duration/2.0) / 29.1 / 2.54;
  if (distance >= 200 || distance <= 0)
    return -1.0;
  else
    return distance;
}

Best Answer

If we look at the source code for pulseIn we can see it doesn't use any of the timers. The pulseIn function loops until the pulse finishes and then converts clock cycles to microseconds.

The cause of your problem might be interrupts when the servos are active. Somewhere in the middle of your measurement, in the pulseIn function, an interrupt occurs which allows the underlying servo code to manage the position/movement of the servo. However this means that the pulse width (see line 59) is not being updated whilst the interrupt code runs. Therefore you get results that are too small.

This seems to perfectly match your description of small pulses reading correctly (they are less likely to be interrupted by other code) and longer pulses reading wrongly.

This is what the "capture" mode is for in most microcontroller timer modules. You start the timer in capture mode, leave it to run in the background and then use the value when it finishes. This can't get interrupted because it's a hardware function that runs in the background.

You might like to read up in this Atmel appnote (your use case is much simpler, but the appnote does a good job of explaining). If there is no Arduino code to do the measurement you'll have to use the timers directly, assuming this is compatible with the servo code. Have a read of this webpage for how to do that, see for example the ICR1 register.