I have a counter attached to ISR:

void ISR_impulse() {

and I want to calculate rolling count per minute with arbitrary update rate.

However I can only get it to update every minute:

void loop() {    
  if (t - t0 > 60000) {
    CPM = CNT - CNT0;
    t0 = t;
    CNT0 = CNT;      

  t = millis();

I'd appreciated if anyone can help me to create a rolling count with a window of 1 minutes that works with arbitrary update rate like 1 second ?

Thanks in advance !

  • set up a FIFO buffer ... the ISR pushes decimal 60 into the buffer .... every second, a separate program routine decrements each of the values in the buffer by one, counts non-zero values and returns the count – jsotola Jun 23 at 17:16

If you want to update every second a one minute running average, you have no choice but store in memory the counts you got the previous 60 seconds. You store them in a ring buffer, so that every new reading replaces the oldest one. Then, every second:

  • you add to the CPM count the number of counts you got this second
  • you subtract the number of counts you got on the second that just got out of the sliding window.

I would implement that logic this way:

const uint32_t ONE_SECOND = 1000;

uint16_t counts_per_minute;
uint16_t previous_counts[60];
size_t counts_pos;

void loop() {
    static uint32_t previous_second;
    if (millis() - previous_second >= ONE_SECOND) {
        previous_second += ONE_SECOND;
        uint16_t CNT_copy = CNT;
        static uint16_t CNT_previous;
        uint16_t second_count = CNT_copy - CNT_previous;
        CNT_previous = CNT_copy;
        counts_per_minute += second_count - previous_counts[counts_pos];
        previous_counts[counts_pos] = second_count;
        if (++counts_pos >= 60) counts_pos = 0;

Note that:

  • The timing variable is updated with previous_second += ONE_SECOND rather than previous_second = now in order to avoid the accumulation of small timing errors.
  • The counter that is updated in the ISR has to be read only once, with interrupts disabled. Otherwise the interrupt may change it while you are in the middle of reading it.
| improve this answer | |
  • So it's a moving average with count-per-update-period as elements ? Wouldn't the noInterrupts() cause the counter to miss some counts ? – 7E10FC9A Jun 22 at 19:32
  • 1
    It would be perfect of you could add code to account for millis() < 60000 situation. – 7E10FC9A Jun 22 at 20:01
  • @7E10FC9A: You cannot meaningfully compute a one minute rolling average in less than one minute. – Edgar Bonet Jun 22 at 20:02
  • @7E10FC9A: noInterrupts() will not make you miss an interrupt. If an interrupt request fires while interrupts are disabled, it is put on hold and served right after the interrupts() call. Only if two instances of the same request fire within this period do you miss an interrupt. But since they are been disabled for less than half a microsecond, this could only happen with an interrupt rate way above what the Arduino can handle. – Edgar Bonet Jun 22 at 20:32

You need a First-In, First-Out buffer (FIFO) so that the oldest data drops off to make room for the newest. This is usually done with a circular buffer - an array whose beginning / end point change as data is added. (For comparison, the simplistic way is an array whose contents gets moved down (toward i = 0) each time you need to add a datum. But moving data is slow, so remembering where the oldest entry is, replacing it with the newest one, and updating the remembered oldest-data location gives a huge permormance improvement.

Secondly, you need to define how responsive your counter needs to be - how many updates per minute you'll need. That's the number of elements you need in the array.

Evaluating the immediately-passed minute's count is as simple as summing the array, which can be done without caring where the begin/end point is. Sum from 0 to N-1; the result will be the same.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.