One handler function for several buttons

I’m writing a simple sketch with two buttons. To handle a button state I’ve written a function called changeButtonStatus (as a parameter, I pass a button pin) that returns an event number:

  • 0 - no click,
  • 1 - single click,
  • 2 - double click,
  • 3 - long press,
  • 4 - released after long press.

In the main loop, I’m calling this function for each of my two buttons and print out a message using Serial.println().
The problem is that my sketch works fine only for one button and it doesn’t work with two or more buttons.

Here is my code:

#include <elapsedMillis.h>

int but1 = 3; 
int but2 = 4;

int eventBut1, eventBut2;

int currentButtonStatus = 0;    // 0 - button is not pressed
                                // 1 - button is pressed for the first time
                                // 2 - button is released after being pressed
                                // 3 - button is pressed for the second time


unsigned long currentButtonStatusStart1;  // number of milliseconds when the status has changed to 1 
unsigned long currentButtonStatusStart2;  // number of milliseconds when the status has changed to 2    
unsigned long currentButtonStatusStart3;  // number of milliseconds when the status has changed to 3 

const int delayFalse = 30;               // if the value is less than 30 milliseconds, one press is not registered 
const int delayLongSingleClick = 400;    // if the value is greater than 400 milliseconds, long press is reegistered 
const int delayDeltaDoubleClick = 300;   // delay to registere double clck        


void setup() {
  pinMode (but1, INPUT);
  pinMode (but2, INPUT);
  Serial.begin(9600);
}

void loop() {
  eventBut1 = changeButtonStatus(but1); // variable to keep the state of but1 (not pressed, single press, double press, long press)
  eventBut2 = changeButtonStatus(but2); // variable to keep the state of but2 (not pressed, single press, double press, long press)

  if (eventBut1 > 0) {
      if (eventBut1 == 1) {Serial.println("But1 Single click");}
      if (eventBut1 == 4) {Serial.println("But1 is released after long press");}
    }

  if (eventBut2 > 0) {
      if (eventBut2 == 1) {Serial.println("But2 Single click");}
      if (eventBut2 == 2) {Serial.println("But2 Double press");}
    }
}

/**
 * Change current button status 
 * @return = 0 - not pressed
 *           1 - single click
 *           2 - double click
 *           3 - long press
 *           4 - released after long press
 */
int changeButtonStatus(int butPin) {
  // Event
  int event = 0;

  // Current button status
  int currentButtonClick = digitalRead(butPin);

  // Current time 
  unsigned long timeButton = millis();

  switch(currentButtonStatus) {

    case 0:
      // button has not been pressed
      if(currentButtonClick) {
        // fix button click
        currentButtonStatus = 1;
        currentButtonStatusStart1 = millis(); 
      } else {
        // button is not pressed
      }
      break;

    case 1:
      // button is pressed for the first time
      if(currentButtonClick) {
        // button is still pressed
        if(timeButton - currentButtonStatusStart1 >= delayLongSingleClick) {
          // button long press state
          event = 3; 
        }

      } else {
        // button has been released
        if(timeButton - currentButtonStatusStart1 < delayFalse) {
          currentButtonStatus = 0;
          event = 0;
        } else if(timeButton - currentButtonStatusStart1 < delayLongSingleClick) {
          currentButtonStatus = 2;
          currentButtonStatusStart2 = millis();
        } else {
          // button has been released after long press
          currentButtonStatus = 0;
          event = 4;    
        }
      }
      break;

    case 2:
      if(currentButtonClick) {
        // if the button has been pressed for the second time

        // check how long the button has been released
        if(timeButton - currentButtonStatusStart2 < delayFalse) {
          currentButtonStatus = 1;
        } else {
          // fix second press
          currentButtonStatus = 3;
          currentButtonStatusStart3 = millis();
        }
      } else {
        // if the button is still released

        // check for the single click
        if(timeButton - currentButtonStatusStart2 > delayDeltaDoubleClick) {
          // button has been released for too long, fix single click
          currentButtonStatus = 0;
          event = 1;
        } 
      }
      break;

    case 3: 
      // confirm double click

      if(currentButtonClick) {
        // button is still pressed
        // wait for the button to be released 

      } else {
        // button has been released

        // check for the debounce
        if(timeButton - currentButtonStatusStart3 < delayFalse) {
          // button has been released too early, probably it's a debounce

        } else {
          // fix double click
          event = 2;
          currentButtonStatus = 0;
        }
      }
      break;
  }
  return event;
}

I would be thankful, if you would help me figure out what may be the problem.

Of course it doesn't. You only have variables to store the data of one pin. You try to store the state and time of the other pin in the same variables.

And that's when it's easy to use a class for all the functions and varaibels associated with a single thing (single button in this case.

And a small time, yo use A LOT of variables which you can reduce. I mean, when the button is in state 2 or 3 you're not interested anymore in currentButtonStatusStart1 etc. So why use a new variable for time?

That would be the same as grabing a different stopwatch for every lap you run although after checking the time of the lap you're no longer interested in that time... You could also just restart that same stopwatch :wink:

There is a good example of code for multiple button clicks here.

...R

Thanks

septillion:
Of course it doesn’t. You only have variables to store the data of one pin. You try to store the state and time of the other pin in the same variables.

And that’s when it’s easy to use a class for all the functions and varaibels associated with a single thing (single button in this case.

And a small time, yo use A LOT of variables which you can reduce. I mean, when the button is in state 2 or 3 you’re not interested anymore in currentButtonStatusStart1 etc. So why use a new variable for time?

That would be the same as grabing a different stopwatch for every lap you run although after checking the time of the lap you’re no longer interested in that time… You could also just restart that same stopwatch :wink:

Thanks for a nice suggestion! Finally, I’ve written a class to handle a button state events :slight_smile: Also I’ll try to rid of unnecessary variables…