Event counting and timing - positive displacement pump strokes

Fluxanode

Well-known member
I'm looking for suggestions on how to accomplish this...

I want to monitor the strokes of a pump running 0-500 strokes per minute. I think this would be done by triggering an interrupt on each stroke and comparing the elapsed time to the past interrupt event. Then do the math to calculate SMP. Is this doable? How do I start a timer to time the event? Or at least read a free running timer each event to calculate the time difference between strokes. But i can't figure out how to start or read a running timer? I also want to totalize the pump strokes.

Teensy 4.1

Thanks for any help!
 
Last edited:
For just 500 strokes/minute an interrupt should work well.

For timing see "Using elapsedMillis & elapsedMicros": pjrc.com/teensy/td_timing_elaspedMillis.html

Using elapsedMicros will give better resolution.

Using an uint32_t integer to capture the value of the elapsedMicros value at the time of the interrupt would allow code in loop() to track minutes and do the math in some fashion, and handles wrap from 2^32 to 0.

Quick note of what might work to get time diff. As written it won't be right the first time, or first after a delay between strokes longer that 2^32 microseconds. Loop() would need to add variable to track to those cases.
Code:
elapsedMicros usRunningTime;
volatile uint32_t lastStroke;
volatile bool newStroke=false;
uint32_t priorStroke;

_isr() {
 lastStroke = usRunningTime;
 newStroke=true;
}

loop() {
// ...
 if ( newStroke ) {
    newStroke=false;
    uint32_t strokeDiff = priorStroke - lastStroke;
    priorStroke = lastStroke;
 }
// ...
}
 
@defragster Is there a way to zero the elapsedMicros value?

The elapsedMicros value can be set to zero or any value - but using with uint32_t as in p#2 the DIFF always works (and zeroing isn't generally needed or useful) ... across wrapping to zero, except as noted when it runs too long and more than 2^32 us pass between reference.

That and the first use issue noted in p#2 could be avoided with another variable and perhaps another elapsedMillis to track when a second has passed between 'strokes' indicating the "priorStroke" value isn't relevant until a second "newStroke" happens in the same second.

No idea on 'input capture interrupt' - seems that would complicate the above issue detection and perhaps be overkill when the resolution is needed to only 1 ms where even elapsedMicros may not add much in this case.
 
Would the input capture interrupt be an option and if so how would you use it?

Yes, you could use timer input capture. But if the pin interrupt is working, maybe best to just go with it.

The main benefit input capture brings is some resilience to interrupt latency caused by other libraries or USB communication which is also using interrupts. But if that becomes a problem, you can also mitigate it by configuring the priority settings for each interrupt so the one you care about most has higher priority. So you probably only need input capture if you will be simultaneously doing other high priority stuff with interrupts, and those other more important interrupts are blocking your pin interrupt for long enough time to cause measurement problems.

You would probably start with the FreqMeasure library to use input capture. On each rising edge, it captures the timer and the library gives you 32 bit elapsed time (in terms of the timer counts) since the prior rising edge. The FreqMeasure examples convert this to frequency and accumulate several changes. If you're interested in the individual events you would change the code to do something for each, but hopefully those examples (in Arduino, click File > Examples > FreqMeasure) can at least help you get started.

If you want to learn the gory details of directly accessing the timer hardware for input capture, you'll need to dive into the reference manual to learn about the timers and read the FreqMeasure code as an example. The hardware is packed with a mind boggling number of very advanced features, so just the FlexPWM chapter is 118 pages and a really steep learning curve. But all the details are documented and the FreqMeasure source code is available if you want to go that route.
 
@defragster or whomever... Ok i mostly have this working except the timing between strokes is way off. Will you look this over and see where I am going wrong before I really mess it up?

Code:
//Teensy 3.2

const byte interruptPin = 2;
volatile long count = 0;
unsigned long previousMillis = 0;        
const long interval = 1000;           

elapsedMicros usRunningTime;
volatile uint32_t lastStroke;
volatile bool newStroke=false;
uint32_t priorStroke;

void setup() {
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin),_isr, HIGH);
  Serial.begin(9600);
}

void loop() {
  
 //Output to serial every one second
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    
    previousMillis = currentMillis;   //Save the last time we output data
    Serial.print("Count  ");          // Serial print count
    Serial.println(count);  
  }
  
 //calculate the time diff between strokes
  if ( newStroke ) {
    newStroke=false;
    uint32_t strokeDiff = priorStroke - lastStroke;
    priorStroke = lastStroke;
    Serial.print("StkDif  ");         //Serial print the time dif between strokes
    Serial.println(strokeDiff);
 }  
}

void _isr() {
        //debounce switch
    static unsigned long last_stk_time = 0;
    unsigned long stk_interrupt_time = millis();
        // If interrupts come faster than 100ms, assume it's a bounce and ignore
    if (stk_interrupt_time - last_stk_time > 100) 
      {
        count = count + 1;        
      }
    last_stk_time = stk_interrupt_time;
    lastStroke = usRunningTime;
    newStroke=true;
  }
Output Results:
Count 4
StkDif 4278487857
StkDif 4294967168
StkDif 4294967230
StkDif 4294966621
StkDif 4294966998
StkDif 4294967228
StkDif 4294967101
StkDif 4294967230
StkDif 4294967182
StkDif 4294967220
StkDif 4294967105
StkDif 4294844618
Count 5
StkDif 4294116438
StkDif 4294967264
StkDif 4294967225
StkDif 4294966663
StkDif 4294967251
StkDif 4294967029
StkDif 4294967064
StkDif 4294967224
StkDif 4294967248
StkDif 4294967175
StkDif 4294967163
StkDif 4294834048
Count 6
StkDif 4294133130
StkDif 4294967147
StkDif 4294967249
StkDif 4294966596
StkDif 4294967022
StkDif 4294967252
StkDif 4294967206
StkDif 4294967173
StkDif 4294967232
StkDif 4294967115
StkDif 4294967243
StkDif 4294967186
StkDif 4294967191
StkDif 4294967119
StkDif 4294826614
Count 7
StkDif 4294167678
StkDif 4294967274
StkDif 4294967190
StkDif 4294967231
StkDif 4294966506
StkDif 4294967198
StkDif 4294967248
StkDif 4294967220
StkDif 4294967108
StkDif 4294967262
StkDif 4294967236
StkDif 4294967261
StkDif 4294967241
StkDif 4294967209
StkDif 4294967237
StkDif 4294967193
StkDif 4294967182
StkDif 4294832579
Count 8
StkDif 4294099660
StkDif 4294967246
StkDif 4294967220
StkDif 4294967228
StkDif 4294966605
StkDif 4294967141
StkDif 4294967250
StkDif 4294967219
StkDif 4294967059
StkDif 4294967251
StkDif 4294967250
StkDif 4294967263
StkDif 4294967256
StkDif 4294967204
StkDif 4294967233
StkDif 4294967255
StkDif 4294966857
StkDif 4294858847
Count 9
StkDif 4293990843
StkDif 4294967247
StkDif 4294967214
StkDif 4294967266
StkDif 4294967245
StkDif 4294966613
StkDif 4294967264
StkDif 4294967063
StkDif 4294967087
StkDif 4294967240
StkDif 4294967215
StkDif 4294967135
StkDif 4294967240
StkDif 4294967243
StkDif 4294967148
StkDif 4294967215
StkDif 4294832060
Count 10
Count 10
 
I fixed the repeating StkDif now getting only one of the difstk per stroke. But still getting a strange number like 3509671839.

Is that microseconds?
 
Why not use something like this?
It will be off by a few us every stroke, but since you are only going at 500 strokes/s I don't suppose it will matter.
The purists will say that this is NOT the way to do it, but I prefer simplicity. Makes it so much easier to understand and get right and .
Code:
//Teensy 3.2

const byte interruptPin = 2;
volatile long count = 0;
unsigned long previousMillis = 0;
const long interval = 1000;

elapsedMicros usRunningTime;
volatile uint32_t lastStroke;
volatile bool newStroke = false;
uint32_t priorStroke;

void setup() {
    pinMode(interruptPin, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(interruptPin), _isr, HIGH);
    Serial.begin(9600);
}

elapsedMillis strokeTime;
uint32_t      tmpNum;

void loop() {

    //Output to serial every one second
    if ((millis() %1000 )== 0 ) {
        Serial.print("Count  ");          // Serial print count
        Serial.println(count);
    }

    if (newStroke) {
        newStroke   = false;
        tmpNum      = strokeTime;
        strokeTime  = 0;
        Serial.print("StrokeTime: ");         //Serial print the time dif between strokes
        Serial.println(tmpNum);
    }
}

void _isr() {
    //debounce switch
    static unsigned long last_stk_time = 0;
    unsigned long stk_interrupt_time = millis();
    // If interrupts come faster than 100ms, assume it's a bounce and ignore
    if (stk_interrupt_time - last_stk_time > 100)
    {
        count = count + 1;
    }
    last_stk_time = stk_interrupt_time;
    lastStroke = usRunningTime;
    newStroke = true;
}
 
BriComp, thanks I'll give this a try. Looks pretty straight forward, I was going to try something like this using millis next.

Thanks for replying!
 
Back
Top