Mysterious factor 1.5 for pulseIn vs interrupt pulse width measurement

I need a robot (see bottom animation) front ultrasonic range sensor (US-100 because of Arduino Due 3.3V) measurements to stop the robot quickly when an obstacle comes near.

I read about NewPing library, and especially this from initial comment of NewPingEventTimer.ino on NewPing’s ping_timer method:

… The advantage of using this method over the standard ping method is that it permits a more event-driven sketch which allows you to appear to do two things at once. An example would be to ping an ultrasonic sensor for a possible collision while at the same time navigating. This allows a properly developed sketch to multitask. …

I had to learn that unfortunately NewPing library does not work for the Due.

I had code to measure distance with US-100 with pulseIn(), but that does active waiting.
“Re: Is it OK to (mis?)use pins for playing role of GND and VCC?”

This was my test setup:

So I tried to use attachInterrupt() and do the pulse width measurement myself via a “CHANGE” ISR (Interrupt Service Routine). It works pretty good, but the durations (in μs) between pulseIn() and interrupt method differed. I calculated the quotient and to my surprise for different distances the factor was always 1.5, which I don’t have an explanation for:

304 mm (1792)  0 mm (1196)  1.50
297 mm (1751)  0 mm (1168)  1.50
347 mm (2047)  0 mm (1368)  1.50
390 mm (2297)  0 mm (1533)  1.50
390 mm (2297)  0 mm (1528)  1.50
423 mm (2493)  0 mm (1665)  1.50

What is the explanation for this factor? Or am I doing something just wrong?

Here is the sketch (in robot application, instead writing output, motor speed would be set to 0 if obstacle/wall is near):

#define VCC0 8
#define trigPin0 9
#define echoPin0 10
#define GND0a 11
#define GND0b 12

void setup() {
  Serial.begin (57600);
  pinMode(VCC0, OUTPUT);  digitalWrite(VCC0, HIGH);
  pinMode(trigPin0, OUTPUT);
  pinMode(echoPin0, INPUT);
  pinMode(GND0a, OUTPUT);  digitalWrite(GND0a, LOW);
  pinMode(GND0b, OUTPUT);  digitalWrite(GND0b, LOW);
}

volatile unsigned long duration=0;
unsigned long duration0, distance, dur0, dur1;

void loop() {
  detachInterrupt(echoPin0);

  distance = distdur(trigPin0, echoPin0, duration0);
  out(distance, duration0);

  attachInterrupt(echoPin0, change, CHANGE);

  duration=dur0=0;

  digitalWrite(trigPin0, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin0, HIGH);
  delayMicroseconds(50);
  digitalWrite(trigPin0, LOW);

  delay(200);
}

void change() {
  dur1 = micros();

  if (digitalRead(echoPin0)==HIGH) {
    dur0 = dur1;
  } else if (dur0) {
    duration = dur1 - dur0;
    
    out(0L, duration);
    Serial.println((1.0*duration0)/duration);

    duration=dur0=0;
  }
}

void out(long distance, long duration) {
  Serial.print(distance);
  Serial.print(" mm (");
  Serial.print(duration);
  Serial.print(")  ");
}

long distdur(int trigPin, int echoPin, volatile unsigned long &duration) {
  long distance;
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(50);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  distance = duration * 34 / 100 / 2;
  return (distance>4000) ? 4000 : distance;
}

Hermann.

 distance = duration * 34 / 100 / 2;

That looks suspiciously like about 1.5 (ok, 1/(1.5) ?); I don't see a a similar calculation in the interrupt-based version...

Thanks, but the factor is between the durations, I don't care for distance right now.

Today I repeated the setup, and got different results:

...
112 mm (662)  0 mm (737)  0.90
112 mm (662)  0 mm (737)  0.90
112 mm (662)  0 mm (738)  0.90
112 mm (662)  0 mm (737)  0.90
112 mm (662)  0 mm (738)  0.90
112 mm (662)  0 mm (737)  0.90
112 mm (662)  0 mm (737)  0.90
...

Again a constant factor, but not 1.5, this time it is always 0.9 (for varying distances).

OK, I had my 5$ 24Msps logic analyzer with me and wanted to measure which of both duration values (in parenthesis) is the correct one. It turned out to be the timer interrupt measurement! So pulseIn() function is broken?

The only fact that is special about the one of my 4 Arduino Dues that I have with me is, that I felt very big heat on Atmel CPU when touching it by accident the first time. It is so hot that you could fry an egg. Will try the same unchanged sketch on my other 3 Dues when I will be back at home end of the week.

Anyway, even if there is something wrong with the Due I have with me – the timer interrupt measurement method I am interested in does work well.

Hermann.

P.S:
Did use the interrupt driven ultrasonice distance code and did full stop when obstacle (in >1m distance) gets in sight:

Full stop was responsible for a remarkable (and repeatable) back wheelie!