1
\$\begingroup\$

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;
}
\$\endgroup\$
  • 1
    \$\begingroup\$ This is a arduino user-level problem, not about electrical engineering. \$\endgroup\$ – Olin Lathrop Nov 22 '13 at 14:04
  • \$\begingroup\$ Arduino = electronic; Ultrasonic sensor = electronic; servo = electronic. Qualifies as an on-topic "engineering question" in my book, as well as by the guidelines for this site that invites newcomers to ask questions and feel welcomed. \$\endgroup\$ – Ron J. Nov 22 '13 at 14:17
  • \$\begingroup\$ the writing of firmware for bare-metal or RTOS applications is specifically included in the list of topics that are OK on EE.SE \$\endgroup\$ – Mels Nov 22 '13 at 15:23
6
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ I switched to the NewPing library. It works well with two servo's going. \$\endgroup\$ – BrianK Nov 25 '13 at 1:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.