Using millis() for timing. A beginners guide

Part 3

Here we go then with counting button presses in a 5 second period

The program

unsigned long startMillis;
unsigned long currentMillis;
const unsigned long period = 5000;  //period during which button input  is valid
const byte buttonPin1 = A1;    //button on pin A1
byte currentButtonState;
byte previousButtonState;
int count = 0;
boolean counting;  //EDIT - corrected mistake

void setup()
{
  Serial.begin(115200);
  pinMode(buttonPin1, INPUT_PULLUP);
  Serial.println("Press the button as many times as possible in 5 seconds");
  counting = true;    //turn on the counting indicator
  startMillis = millis();  //save the start time
}

void loop()
{
  currentMillis = millis();
  previousButtonState = currentButtonState;
  if (currentMillis - startMillis <= period)  //true until the period elapses.  Note that this is the reverse of BWOD
  {
    currentButtonState = digitalRead(buttonPin1);
    if (currentButtonState == LOW and previousButtonState == HIGH)  //the button has become pressed
    {
      count++;    //increment count
      Serial.println(count);
    }
  }
  else  //period has ended
  {
    if (counting == true)  //if we were counting
    {
      Serial.print("Time is up");
      counting = false;    //prevent the message being displayed again
    }
  }
}

So what is going on in this program ?

It starts as do the previous ones by declaring global variables. There are some new ones here because we want to detect when the button becomes pressed rather than when it is pressed. We are going to print a message on the screen when the time is up so need a way of preventing the message from being repeated, hence the boolean variable.

As usual we start by getting the current value of millis() then establishing whether the period has elapsed by the usual subtraction and compare calculation. But this time we need to read the button state if the period has not elapsed so the comparison with the period is reversed from greater than used in the previous examples, to less than. If the period has not elapsed we read the input. In order to determine whether the button has become pressed we save the previous state, read it again next time through loop() and compare the two.

If it has changed since last read and is now LOW we know that the button has become pressed. If so we increment the count and print it. If not we do nothing but go round loop() again. Once the period has elapsed the subtract and compare calculation will return false and we need to print a message. This is done in the else clause and the final message is printed only if the boolean variable is true. Once the message has been printed the boolean is set to false to prevent the final message being printed again even though the period has ended.

Upload it to your Arduino and try is. Does it do what it should ?
It is very likely that the count will actually be higher than the number of button presses because the switch contacts bounce and each bounce is counted. You may be lucky and have perfect switches that do not bounce but most of us will not be that lucky.

We need to increment the count only when the switch contacts remain closed for a few milliseconds so that we know they have stopped bouncing. Our friend millis() allows us to do that without blocking the program and hanging around waiting for the bouncing to stop.

Part 4 will show how millis() timing can be used to detect when a switch has stopped bouncing.

10 Likes