Using millis() for timing. A beginners guide

Part 4

Here is a program that will count button presses in a 5 second period but in this version the button has to remain pressed for 20 milliseconds before the button press is counted. Can you guess what we use to time the 20 milliseconds ?

unsigned long periodStartMillis;
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 printFinalMessage = true;
unsigned long debounceStartMillis;
unsigned long debouncePeriod = 20;
boolean debouncing = false;

void setup()
{
  Serial.begin(115200);
  pinMode(buttonPin1, INPUT_PULLUP);
  Serial.println("Press the button as many times a possible in 5 seconds");
  periodStartMillis = millis();
}

void loop()
{
  currentMillis = millis();
  if (currentMillis - periodStartMillis <= period)  //true until the period elapses
  {
    previousButtonState = currentButtonState;    //save the previous button state
    currentButtonState = digitalRead(buttonPin1);  //read the current state of the input
    if (currentButtonState != previousButtonState) //if the button state has changed
    {
      debounceStartMillis = currentMillis;  //save the time that the state change occured
      debouncing = true;  //flag that debouncing in progress
    }    //end state change check
    
    if (currentMillis - debounceStartMillis >= debouncePeriod)  //if the debounce period has elapsed
    {
      if (debouncing == true)    //debouncing taking place
      {
        if (currentButtonState == LOW)  //if the button is currently pressed
        {
          debouncing = false;    //debouncing is finished
          count++;               //increment the count
          Serial.println(count);
        }    //end count increment
      }  //end debouncing in progress check
    }    //end debounce time elapsed check
  }  //end timing period check
  else  //period has ended
  {
    if (printFinalMessage == true)
    {
      Serial.println("Time is up");
      Serial.print("Button pressed count : ");
      Serial.println(count);
      printFinalMessage = false;    //prevent the final message being displayed again
    }    //end printing final message
  }    //end final message check
}

The majority of this program is taken from the previous one but a new element has been introduced. When the change of button state is detected the value of millis() at that time is saved and a boolean variable is set to true indicating that debouncing is in progress.

Then each time through loop() the elapsed time since debouncing started is checked and if it expires and debouncing is in progress and the button is currently pressed we know that it has been pressed for at least the debouncing period, so we count the button press by incrementing the count and set the boolean to false to stop it happening again the next time through loop() even though the debouncing period has expired. It will be set to true again the next time a button press is detected to start the debouncing process again.

All of the button press detection and debouncing code above is wrapped in the test to see whether the 5 second timing period is still happening. As before we could not have used delay() for this as the 5 second delay() would have held the program up. We could conceivably used delay() for the debouncing as in this program doing nothing for 20 milliseconds would not make much difference, but it is all too easy to use delay() in circumstances where it is not appropriate, so it is better to consider using millis() from the very start. This is particularly so when programs are being tested in isolation to iron out bugs before combining them. This is good practice, but just because they work in isolation does not mean that they will work when combined, particularly when precise timing is involved.

19 Likes