1

I am trying to make a production counter for a machine. I want to display production count of the last minute for each seconds. So, display should update every second and should display real value of last 60 seconds. I have a system for detect real-time product output and get it to the Arduino using digital input pin.

I wrote below codes and for that. what I have done is count output and get output per second. Then store it on an array as separated value for each seconds. Every second the array updated and added new value for current second. At last add all count value of last 60 seconds and output as production rate of current minute.

My question is, Is there any easy and efficient way for do this without using few variables ?

    #define countSensor 8  //count sensor input
    
    uint8_t records[60];
    uint8_t countSensorState = 0;
    uint8_t totalCount = 0;
    uint8_t countOfLastSec = 0;
    uint8_t lastCount = 0;
    uint8_t lastMinCount;
    uint32_t preMills = 0;
    
    
    void setup() {
      Serial.begin(9600);
      //init sensor input pin
      pinMode(countSensor, INPUT_PULLUP); //change according to sensor type
    
      //clear random value of memory locations
      for (int i = 0; i < 60; i++) {
        records[i] = 0;
      }
    
    }
    
    void loop() {
      //get count at falling edge
      if (digitalRead(countSensor)^ countSensorState) {
        if (!digitalRead(countSensor)) {
          totalCount++;
    
        }
        countSensorState = digitalRead(countSensor);
      }
      //get count of last second
      if (millis() - preMills > 1000) {
        preMills = millis();
        countOfLastSec = totalCount - lastCount;
        lastCount = totalCount;
        //record last 60s counts
        //--shift back a second in the array
        for (int i = 0; i < 59; i++) {
          records[i] = records[i + 1];
        }
        //--record current second in array at 59th index
        records[59] = countOfLastSec;
        //get last 60s count
        for (int i = 0; i < 60; i++) {
          lastMinCount += records[i];
        }
        Serial.println(lastMinCount);
      }
    
    
    
    }
4
  • 1
    If you really need the total of the last 60 seconds, then yes, you have to store that array. If you can live with something like an exponentially weighted moving average, then only the previous value of the average has to be stored. – Edgar Bonet Apr 11 at 21:03
  • 1
    you don't need to shift the values .... index the array in circular manner – jsotola Apr 12 at 0:26
  • @jsotola I couldn't get it correctly. How can I apply circular array to my code? Could you give me a modified code... – user_fs10 Apr 12 at 8:22
  • 1
    right now you use for loop to iterate the array using a pointer i .... remove the for statement ... use i++ every second instead ... count 0 1 2.. 59 0 1 2 .... – jsotola Apr 12 at 14:44
2

As I said in a comment, if you really need to compute that moving sum, you absolutely need to store the last 60 one-second counts in memory. That being said, there are a few small issues with this code for which I would have some suggestions:

  • There is no point in storing totalCount. You do not really use it, and it will eventually overflow.

  • You do not need to explicitly initialize all globals to zero, as that is implicitly done by the runtime: it is a requirement of the language.

  • Your are calling digitalRead(countSensor) three times in a row. The logic for counting edges is sound as long as the three calls yield the same result, but that is not guaranteed. It is safer to only read the pin once per loop iteration.

  • Similarly, the two calls to millis() can yield different results, and that will make your timings slightly off.

  • The condition millis() - preMills > 1000 will only fire when 1001 milliseconds have elapsed. Because of the way millis() works, sometimes you will miss the 1001th millisecond, and process on the 1002nd millisecond. In order to avoid accumulating these small timing errors, it is best to update preMills by always adding exactly 1000.

  • As pointed out by jsotola, you do not need to shift the array: you can instead reuse the array cell that holds the data you do not need anymore.

  • You do not need to perform the whole sum every time. You can instead update the previously computed sum by removing the count of the second that falls off the current one-minute window, and adding the count of the current second.

Here is my attempt at applying all the above suggestions. Warning: it has not been tested.

const uint8_t countSensor = 8;

uint8_t record[60];       // last 60 one-second counts
uint8_t currentIndex;     // current index in record[]
uint8_t oldSensorState;   // previous state of countSensor input
uint32_t previousSecond;  // last time we printed minuteCount
uint8_t thisSecondCount;  // count of items in the current second
uint16_t minuteCount;     // count of items in the last minute

void setup() {
    Serial.begin(9600);
    pinMode(countSensor, INPUT_PULLUP);
}

void loop() {
    // Count falling edges.
    uint8_t sensorState = digitalRead(countSensor);
    if (oldSensorState == HIGH && sensorState == LOW) {
        thisSecondCount++;
    }
    oldSensorState = sensorState;

    // Update stats every full second.
    if (millis() - previousSecond >= 1000) {
        previousSecond += 1000;

        // Update minuteCount by removing the second that falls off
        // the window and adding the one that enters the window.
        minuteCount += thisSecondCount - record[currentIndex];

        // Replace the count that is not needed anymore by
        // the current one.
        record[currentIndex] = thisSecondCount;

        // Start counting a new second.
        thisSecondCount = 0;

        // Update current index, wrapping at 60.
        if (++currentIndex >= 60) {
            currentIndex = 0;
        }

        Serial.println(minuteCount);
    }
}

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.