Ultrasonic sensor

I have written a sketch that uses a standard ultrasonic ping sensor to measure the distance. I tried using interrupts instead of pulseIn, as not to block the sketch, however, my output is 9cm too far all the time. The distance from the sensor doesn’t matter, it shows 19 at 10 and 109 at 100. I suspect there has to be some holdup in my program somewhere that cases the extra 260ish microseconds that gives +9cm.

There is nothing wrong with the sensor as it output correct values with the ping sketch supplied with the Arduino IDE.

If anyone could take a quick look at my sketch I would be very thankful :slight_smile:

#define ECHO 3
#define TRIG 4

#define DEBUG

unsigned long pingTrigTime;
volatile unsigned long pingEchoTime;
byte data[4];
volatile bool pingEcho = false;
bool pingSent = false;

void setup() {
  Serial.begin(115200);
  #ifdef DEBUG
    Serial.println("Starting...");
  #endif  
  pinMode(TRIG, OUTPUT);
  pinMode(ECHO, INPUT); 
  digitalWrite(ECHO, LOW);
  attachInterrupt(ECHO-2, OnInterrupt, FALLING);
}

void loop() {
  
  if (pingEcho){
    int cm = microsecondsToCentimeters(pingEchoTime - pingTrigTime);
    
    #ifdef DEBUG
      Serial.print("Distance: ");
      Serial.print(cm);
      Serial.println("cm");
    #else
      byte buf[sizeof(int)];
      memcpy(buf, &cm, sizeof(buf));
      Serial.write(buf, sizeof(buf));
    #endif
    
    pingSent = false;
    pingEcho = false;
  }
  else if (!pingSent){
    delay(1);
    SendPing();
    #ifdef DEBUG
     Serial.print("Sent ping...");
    #endif 
  }  
  else if (micros() > (pingTrigTime + 10000)){
    #ifdef DEBUG
      Serial.println("Timeout! Too far!");
    #endif
    pingSent = false;
  }
  
}

void OnInterrupt(){
  pingEcho = true;
  pingEchoTime = micros();
}

void SendPing(){
  pingSent = true;
  pingEcho = false;
  digitalWrite(TRIG, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG, HIGH);
  delayMicroseconds(5);
  digitalWrite(TRIG, LOW);
  pingTrigTime = micros();
}

int microsecondsToCentimeters(long 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;
}

SoundEchoDistance.ino (1.59 KB)

The datasheet for the HCSR04 indicates that after the trigger the module sends 8 cycles of 40 KHz which takes 200 us which is equivalent to about 6.8 cm. Now the datasheet does not say how long it takes to process the receipt of the 8 cycles but one has to assume that it can't respond until it has received the last of the 8 cycles so there has to be a bias of at least 6.8 cm. I'm betting at there are other processing delays (including the Arduino processing delay) that add up the the rest of your 10cm bias.

You could compensate for this by subtracting 10cm from your measured range.

Edit: I have just dug out my module and using your basic code I need to compensate for approx 7.4 cm.

The NewPing library does something similar, but gets the distance right(er). You might look at how it manages.

You do know that the speed of sound is affected by altitude, temperate and air density....?

You need to take into account where you are and the prevalent conditions compared to the conditions under the quoted speed of sound is measured.

How do I know this? - from real world experience! I calibrated a unit in SEA and sent it to NA. Needless to say my calibrations were off.

aisc: You do know that the speed of sound is affected by altitude, temperate and air density....?

:) You forgot composition (N, O and H2O proportions).

You need to take into account where you are and the prevalent conditions compared to the conditions under the quoted speed of sound is measured.

For the range of a ping sensr, does this really matter? More likely there is something wrong with OP's code.

OP: What Arduino is that code running on?

  else if (!pingSent){
    delay(1);
    SendPing();
    #ifdef DEBUG
     Serial.print("Sent ping...");
    #endif 
  }

WTF is the delay() for?

    int cm = microsecondsToCentimeters(pingEchoTime - pingTrigTime);

What happens when the interrupt fires while you are reading the byte variable pingEchoTime? That value could change in the middle of the read, and some of the bytes could be read before, and some read after, the interrupt. When accessing volatile multi-byte variables, you should disable interrupts.

PaulS:
For the range of a ping sensr, does this really matter? More likely there is something wrong with OP’s code.

As I mentioned before, I set up an ultrasonic sensor in SEA and despatched it to NA.
I set it up to trigger at 30cm and received complaints as for them it was triggering at about 9 or 10 inches.
I sent a second calibrated unit with a 2cm correction factor.
I still received complaints and the difference was the correction factor.
The consistency in the difference got me going and that’s when I turned to Mr Google and did some investigation…
My conclusion is it absolutely matters.

It appears the speed of sound is faster by 2 to 3 inches over 30cm in Canada (rural area) than in Bangkok…
Go figure - who would have thought that?
But then again, I guess the pollution here accounts for most of that.

stowite: ...one has to assume that it can't respond until it has received the last of the 8 cycles...

Why? In theory you can derive a pulse time difference from a leading edge. You know when the transmit leading edge is, since you produced it.

stowite is correctly pointing out the problem.

You are measuring a pulse width from the end of your trigger pulse to the end of the echo pulse. There is trigger signal time and other processing considerations. These sensors are intended to be read by determining the width of the echo pulse which is proportional to the distance.

You have to trigger the sensor, then look for the rising edge to the ECHO pulse, when you find it, start a timer and then look for the falling edge of the ECHO pulse, when you find it, use the time between the two edges for the distance. Your interrupt ISR should be CHANGE.

Using trigger time to end of echo time and compensating for the extra time involved does not seem preferable to simply reading the pulse length of the echo.

aarg:
Why? In theory you can derive a pulse time difference from a leading edge. You know when the transmit leading edge is, since you produced it.

I see no point in sending 8 pulses if one ignores all but the first. At best I would expect it to be fragile and very susceptible to noise.

I see no point in sending 8 pulses if one ignores all but the first. At best I would expect it to be fragile and very susceptible to noise.

Sending a pulse, and waiting for a response, 8 times, makes sense.

Sending 8 pulses, and being interrupted when the first one arrives back, and expecting the other 7 to mean something, does not.

Or, did I miss something (again)?

Here's how I understand the operation of these sensors.

A trigger pulse 5 microseconds is sent to the sensor. When this pulse falls low, there is then a period called the "echo hold off time" before the echo line is brought high. I have measured this time period on my HC SR04 at 450 microseconds, and the data sheets of the parallax ping sensors show it to be either 750 or 350 microseconds. During this hold off period, an 8 pulse burst of 40khz ultrasound is emitted, and from the data sheets, it looks like maybe 5 pulses occur before the echo line goes high and 3 are after.

When the sensor detects the return pulse, the echo line is dropped, so the high pulse width on the echo line indicates the distance.

The electronic magic is in the "detects the return pulse". At $1.50 for these sensors, I don't think there is any high precision ADC, and I think that several high/low return transitions are required for the device to say that the pulses train has returned and its time to drop the echo line.

I believe that is the reason for the 8 pulses overlapping the start of the echo high. If, for example two pulse cycles are required for the return acquisition, there is still time for the echo line to be pulled low and the pulse length indicating the distance can be accurately synchronized to the first pulse return.

I admit that I'm not a hardware expert and, perhaps someone with more knowledge the these sensors than I do, can comment on the design of the return detection circuit and the theory of operation.