pulseIn() return incorrect result

I'm playing with a HC-SR04 (ultrasonic distance measurement) and Arduino UNO or Seeeduino Lite (Arduino Leonardo equivalent). I observe the same problem: the returned pulse microsecond is too short, I must add about 20%.

Here is the shetch:

// READ CODE for Arduino UNO
// Trigger pin connected to pin 7
// echo pin connected to pin 8
// Vcc connected to 5v pin
// GND connected to GND pin

const int trigPin = 7;
const int echoPin = 8;
long duration = 0;
float cm = 0;

void setup() {
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  digitalWrite(trigPin, LOW);
  Serial.begin(9600);
}

void loop()
{
  // PING is triggered by a HIGH pulse of 10 or more microseconds.
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(12);
  digitalWrite(trigPin, LOW);

  // 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.

  duration = pulseIn(echoPin, HIGH);
  // convert the time into a distance
  cm = (float) duration / 1000000 * 340 / 2 * 100 * 1.2 - 1.0;

  Serial.print(duration);
  Serial.print(" us, ");
  Serial.print(cm);
  Serial.print(" cm");
  Serial.println();

  delay(100);
}

I've studied the disassembly avr-objdump. The pulseIn code is:

...
0000079a <pulseIn>:
...	
	// wait for the pulse to stop
	while ((*portInputRegister(port) & bit) == stateMask) {
1     848:	fb 01       	movw	r30, r22
3     84a:	e5 90       	lpm	r14, Z+
3     84c:	f4 90       	lpm	r15, Z
1     84e:	f7 01       	movw	r30, r14
2     850:	f0 80       	ld	r15, Z
1     852:	fd 22       	and	r15, r29
1     854:	fc 12       	cpse	r15, r28
2     856:	0a c0       	rjmp	.+20     	; 0x86c <pulseIn+0xd2>
		if (numloops++ == maxloops)
1     858:	28 17       	cp	r18, r24
1     85a:	39 07       	cpc	r19, r25
1     85c:	4a 07       	cpc	r20, r26
1     85e:	5b 07       	cpc	r21, r27
1     860:	a9 f0       	breq	.+42     	; 0x88c <pulseIn+0xf2>
			return 0;
		width++;
1     862:	2f 5f       	subi	r18, 0xFF	; 255
1     864:	3f 4f       	sbci	r19, 0xFF	; 255
1     866:	4f 4f       	sbci	r20, 0xFF	; 255
1     868:	5f 4f       	sbci	r21, 0xFF	; 255
2     86a:	ee cf       	rjmp	.-36     	; 0x848 <pulseIn+0xae>
...

I've added the instruction cycles in front of lines.

The comment says that they are 21 processor clocks. I count 25 and that explains the 20%.

It might be caused by a compiler setting that has changed.
which version of the IDE do you use?

Please post an issue on github - GitHub - arduino/Arduino: Arduino IDE 1.x -

(splitting hairs: 25/21 = 1.1904 ==> 19% correction would be more accurate)

I have experimented with an HC SR04 and found mine to read about 10% low at test range of about 48 cm. With my setup, your code shows about 10% higher than the measured distance.

I have tested the HC SR04 using pulseIn, external interrupts, and pinchange interrupts and all methods gave similar results. I concluded that the calibration of my sensor was off.

Many thank's to robtillart to explain the more accurate 19%, it's clear. I experimented 20% with an oscilloscope before reading the disassembly.

I'm using 1.5.8. And it's probably the reason.
I will check immediatly with 1.0.6... done: 21 cycles. The 1.0.6 code is very different of 1.5.8, the optimizations really differ.

cattledog:
I have experimented with an HC SR04 and found mine to read about 10% low at test range of about 48 cm. With my setup, your code shows about 10% higher than the measured distance.

I have tested the HC SR04 using pulseIn, external interrupts, and pinchange interrupts and all methods gave similar results. I concluded that the calibration of my sensor was off.

Hi cattledog,
HC-SR04 doesn't need calibration, the measure is only based on "speed of sound in air" which can be considered to be a constant. But altitude and temperature affect speed.
What could also affect pulseIn() is too frequent interrupts.

To confirm the pulseIn() error, I've used another Arduino sketch:

   unsigned long t1;
    unsigned long t2;
    while (!digitalRead(echoPin));
    t1 = micros();
    while (digitalRead(echoPin));
    t2 = micros();
    duration = t2 - t1;
    // convert the time into a distance
    cm = (float) duration / 1000000 * 340 / 2 * 100 - 0.5;

jmP

Note that the speed of sound depends also on temperature (and huimidity) so a hardcoded 340 m/sec introduces a systematic error.

check - Ultrasonic sensor to determine water level? - #12 by robtillaart - Sensors - Arduino Forum - for the details.

Can you post the whole sketch you used for checking?

The sketch is written in the 1st post of this topic.
Remove the * 1.2 (the 20% correction for 1.5.8 ) in the calculation of cm.

thanks, missed that one (see too much threads:)