How do I do an approximately equals ?

I'm measuring a pulse width which will vary from about 100us downwards. I am at the development stage at the moment and am finding that the CMOS 555 test circuit is being measured at 75uS and every 20th measurement or so it is ~69us. I have no way of knowing if this is the source, interference or some characteristic of pulseIn().

Is there an elegant way - ie not too many lines of code - of not saving those that are outwith a range of say +/- 3uS of a mean of something like 5 measurements. At the moment I'm just taking two measurements and if they are the same sending them to the Serial Monitor. I thought to begin with that I would see the transmit LED fail to flicker rarely, but it's in reality a bit the other way round.

byte pin = 8;
int duration1;
int duration2;

void setup()
{
pinMode(pin, INPUT);
Serial.begin (9600);

}

void loop()
{
duration1 = pulseIn(pin, HIGH);
duration2 = pulseIn(pin, HIGH);

if (duration1 == duration2)
{
Serial.print("Time = ");
Serial.print(duration1);
Serial.println(" usec");
}
delay(1000);

THe pulseIn() function returns an 'unsigned long', not an 'int'.

characteristic of pulseIn()

The timing of this function has been determined empirically and will probably show errors in shorter pulses. Works on pulses from 10 microseconds to 3 minutes in length.

if u have time in the run loop-- just take more samples and compute an average..

An almost equality is hard if you want to make it 100% robust. For small numbers one often wants to know the absolute almost equality while for large numbers one want to have the relative almost equality (e.g. floats to cope with significant digits).

if (abs(x - y) < epsilon)  {..}

epsilon is the allowed “noise” or threshold.

What makes it difficult is that over/underflow can occur when one does a subtraction. This overflow depends on signed or unsigned math and can give unwanted behaviour

e.g. unsigned bytes
x = 253
y = 254
epsilon = 3;

abs(253 - 254) < 3 is false ==> 253 - 254 = 255 (-1 but overflows) which is bigger than 3

if ( (abs(x - y) < epsilon)  || (abs(y - x) < epsilon) ) {..}

By “duplicating” the test it can handle overflow

Many thanks Rob T - that is what I was looking for.

Picking up on the earlier comment from Koepel about ‘unsigned long’, you have to educate a noob here !!

My logic for changing to ‘int’ was that the range of the pulse length to be measured (estimated to be 40 to 100us), is clearly not going to be ‘long’ and certainly not double the capacity as ‘unsigned long’. The code is going into an ATtiny85 so my thinking was to keep everything as small as possible.

There is now a realisation that I will need to look for the errors of no pulse or one that sticks high.

Thanks guys
Rob

From your description, it looks like what you should do is first screen out errors, then compute a running average. Because, it seems that you are actually interested in the values, not the difference in values.

It never rains but it pours.

I have just completed a sketch for dougsauto involving timing pulses at up to about 2000hz. I used a couple of techniques to smooth the data - a “ring buffer” of samples and a low-pass filter.

The code is at https://github.com/PaulMurrayCbr/arduino_dougsauto_tacho .

The relevant bits are in the ISR:

    void pulseISR() {
      if (++ringBufferPos >= RING_BUFFER_SIZE) {
        ringBufferPos = 0;
      }

      mostRecentPulseUs = micros();
      unsigned long pulseWidthUs = mostRecentPulseUs - ringBufferUs[ringBufferPos];
      ringBufferUs[ringBufferPos] = mostRecentPulseUs;

      // these floating point operations may cause problems on account of being slow and hapenning inside an ISR

      avgPulseWidthUs = LOWPASS_DAMPING * avgPulseWidthUs + (1 - LOWPASS_DAMPING) * (float)pulseWidthUs / RING_BUFFER_SIZE;
    }

Note that in order to use the values, to briefly turn off interrupts:

    void loop() {
      // make this as fast as possible to reduce glitching
      noInterrupts();

      // if the mst recent pulse was a long time ago (as specified by ZERO_SPEED_us),
      // then we manually zero the speed
      if (micros() - mostRecentPulseUs > ZERO_SPEED_us && avgPulseWidthUs <= ZERO_SPEED_us) {
        avgPulseWidthUs = ZERO_SPEED_us * 2.0; // times two to give us plenty of slop
      }

      // this needs to be in a nointerrupts block because
      // flat read and writes are not necessarily atomic
      avgPulseWidthCpyUs = avgPulseWidthUs;
      interrupts();