Paddlewheel sensor data smoothing

I'm using a 3 wire NPN paddlewheel sensor that is setup as an input pullup in the code.
It reads high every time a certain arm of the paddlewheel comes around.
I'm using an interrupt pin to mark the time in millis() every time it sees this to get the time of a full rotation of the wheel.
I chose this instead of pulsein because it is non-blocking.

Essentially this 'duration' is related to the speed of the flowing water.
My issue is that it is pretty volatile for a set flow.
I've considered using RC filters, or other software smoothing methods such as moving average.

I'm sure others have had similar issues and am looking for guidance.

I've attached a serial plot of the duration.
This was captured with a 3V signal to the pump on the 0-5V control voltage line.
No other distrubances were present.

Thanks

Code below:

const byte paddlewheel = 2; //Pin assignment method that uses the least space
volatile long oneTime = 0;  //All global variables used in interrupt need to be volatile
volatile long twoTime = 0;  //All global variables used in interrupt need to be volatile
volatile bool Time = true;  //All global variables used in interrupt need to be volatile
long duration = 0;

//Interrupt Service Routine (ISR)
void timing()
{
 if (Time == true) //Assign current time to lastTime
 {
  oneTime = millis();
  Time = false; //Switch bool so next revolution assigns time to twoTime  
 }
 else if (Time == false) //Assign current time to nowTime
 {
  twoTime = millis();
  Time = true; //Switch bool so next revolution assigns time to oneTime
 }
}

void setup() 
{
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(paddlewheel, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(paddlewheel), timing, RISING); //Sense paddlewheel pin using interrupts: attachInterrupt(PIN, ISR, MODE)

}


void loop() 
{
  // put your main code here, to run repeatedly:
  duration = (abs(oneTime - twoTime));
  Serial.println(duration); //report duration
  delay(25); //Makes graph less spiky for viewing purposes
}

Always use unsigned long variables with millis() or micros().

There is no good reason to use an interrupt in this case, but if you insist, rather than the complicated flipflop in timing variables, you can just update a previous time variable and calculate the difference. You also need to "protect" the time variable during access by the loop() function, as it may be corrupted by the interrupt. Try something like this (untested):

const byte paddlewheel = 2; //Pin assignment method that uses the least space
volatile unsigned long duration = 0;

//Interrupt Service Routine (ISR)
void timing()
{
  static unsigned long lastTime = 0; //retain value between calls
  unsigned long Time;
  Time = millis();
  duration = Time - lastTime;
  lastTime = Time;
}

void setup()
{
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(paddlewheel, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(paddlewheel), timing, RISING); //Sense paddlewheel pin using interrupts: attachInterrupt(PIN, ISR, MODE)

}


void loop()
{
  unsigned long period;
  noInterrupts(); //interrupts off
  period = duration; //make a copy of the interval
  interrupts(); //interrupts back on
  Serial.println(period); //report duration
  delay(25); //Makes graph less spiky for viewing purposes
}

A first order filter is easy to implement (inefficient, unfactored code example):

float smoothed = 0.0, alpha=0.1;
...
smoothed = (1.0-alpha)*smoothed + alpha*period;

Finally, you may want to add a global flag, so that the interrupt routine can tell the main loop that the time increment has actually been updated. But then again, using interrupts just adds complications and is a waste of effort.

I am confused as to the durationthe sensor is signaling high.

Is it one pulse, or does it sense the paddle approaching and leaving during the arc so that whole time needs to be averaged into a single moment.

You just count pulses, not the degree of pulsiness. I don't think much of the code. Using a delay can't possibly be a good idea at a time like this and it's purpose is absurd. But I recognise the intent and I think standard programmes are still not up to this.

You may find the quantity is actually OK but the rate is still unusable. I found mine very accurate for quantity but the rate at one second update is nbg and I am going to have to average ten readings and update every ten seconds, or simply have ten seconds for the count period.

My commercial heat meter only updates at ten sec. intervals and my mainreason for getting into Arduino in the first place was that I wanted faster updates. I guess I now know why my heat meter doesn't deliver them.

Do you have a 2 pulse per revolution flow sensor? If you don't know, open it up and turn the impeller by hand and see how many pulses you get per revolution.
If you do have a 2 pulses per revolution sensor you need to measure the time between every second pulse so that the magnetic trigger point is always the same for each magnet pole.

If you want to smooth the value from several readings, you can instead count the pulses during a certain time (1s?).

can you post a link to the sensor ?
My thoughts run to the pinwheel sensors for measuring water in a hose. lots of code already available for those.