EZBUTTON multiple button array

Hello!

I'm trying to use EZBUTTON for multiple buttons, for short and long press detect.

For 8 buttons in the array, serial print confirms that button (i) is the button I pressed and released.

For the long press, the serial print confirms the correct button (i) is pressed, but after the 1 sec press time, it returns a random button and that random button's LED.

for example, I will copy and paste the serial print output for button (1) short press once and long press 3 times:

Button Pressed 1
Button 1 - A short press is detected -

Button Pressed 1
Button 7 - A long press is detected - LED 7

Button Pressed 1
Button 3 - A long press is detected - LED 3

Button Pressed 1
Button 0 - A long press is detected - LED 0

To my understanding, it's returning whatever value of (i) that is being cycled once a long press is detected. How do I only get it to focus on the button that is pressed?

Any guidance will be appreciated.

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <ezButton.h>

const int SHORT_PRESS_TIME = 1000; // 1000 milliseconds
const int LONG_PRESS_TIME  = 1000; // 1000 milliseconds
const int ledPin[8] = {22, 28, 34, 40, 23, 29, 35, 41};    // the pin that the LED is attached to
ezButton button[8] = {2, 3, 4, 5, 6, 7, 8, 9};  // create ezButton object that attach to pin 7;
int number_led = 8;
int NUMBER_BUTTONS = 8;


unsigned long pressedTime  = 0;
unsigned long releasedTime = 0;
bool isPressing = false;
bool isLongDetected = false;

void setup() {
  Serial.begin(9600);
  for (int i = 0; i < (NUMBER_BUTTONS); i = i + 1) {
    button[i].setDebounceTime(50);} // set debounce time to 50 milliseconds
  
  for (byte n = 0; n <= number_led; n++) {
    pinMode(ledPin[n], OUTPUT);
  }
}

void loop() {
  for (int i = 0; i < (NUMBER_BUTTONS); i = i + 1) {
    button[i].loop(); // MUST call the loop() function first

    if(button[i].isPressed()){
      pressedTime = millis();
      isPressing = true;
      isLongDetected = false;

        Serial.print("Button Pressed ");
        Serial.println(i);
        //Serial.println(pressedTime);
        //Serial.println(isPressing);
        //Serial.println(isLongDetected);
      }
  
  if(isPressing == true && isLongDetected == false) {
    long pressDuration = millis() - pressedTime;
        
        //Serial.print("pressDuration");
        //Serial.println(pressDuration);


    if( pressDuration > LONG_PRESS_TIME ) {

      isLongDetected = true;

      Serial.print("Button ");
      Serial.print(i);
      Serial.print(" - A long press is detected - ");

      

      for (byte n = 0; n < NUMBER_BUTTONS; n++) {
          digitalWrite(ledPin[n], LOW);
        }
        digitalWrite(ledPin[i], HIGH);
          
          Serial.print("LED ");
          Serial.println(i);

    }
  }
  
  if(button[i].isReleased()) {
    isPressing = false;
    releasedTime = millis();

    long pressDuration = releasedTime - pressedTime;

    if( pressDuration < SHORT_PRESS_TIME ) {
      Serial.print("Button ");
      Serial.print(i);
      Serial.println(" - A short press is detected - "); 
          }
    }
  }
}

you have a for loop going through all the buttons to check their state

as long as you are in this for loop, the variable i is the index of the button currently being checked and updated by the call to its loop() function. Calling this function updates the status of the button, which then lets you check if the ith button is pressed or released

if(button[i].isPressed()) ...

if(button[i].isReleased()) ...

the for loop does it jobs and so when you are done dealing with button at index 0, you go to index 1 and 2 and 3...


if you are only touching button 1 and that something is detected on button 7 it means you have something wrongly wired in your circuit.

I don't use the ezButton library - seems there is no begin() function which could be an issue to set the pullup on the pin for some platforms ==> which arduino are you using?

Thanks for your reply. I'll play with it and see if I can rework it so it works.

The buttons are wired correctly though. It's my midi controller pedal that I use with another sketch (I want to change the buttons from single HIGH/LOW state to short long press function).

I'm using a Mega clone. The hardware is connected correctly, so, unless there's no INPUT_PULLUP in the ezbutton library, there should be no issue with the wiring. I'll look into the library, I think.

Maybe just a typo, but shouldn’t these be different durations?


const int SHORT_PRESS_TIME = 1000; // 1000 milliseconds
const int LONG_PRESS_TIME  = 1000; // 1000 milliseconds

Why I hate button libraries.

ezButton does not suck, that's my endorsement. I use it in a pinch, in a hurry, and have gotten it to be OK.

This sketch, however, has some strange things going on.

I added a print statement to indicate which button was in the loop. With but one button wired (2), I got

@button 1
@button 2
Button Pressed 2
@button 3
@button 4
@button 5
@button 6
@button 7
@button 0
@button 1
@button 2
Button Pressed 2
@button 3
@button 4
@button 5
@button 6
@button 7
@button 0
@button 1
@button 2
@button 3
@button 4
@button 5
@button 6
Button 6 - A long press is detected - LED 6
@button 7
@button 0
@button 1

ezButton pulls up the input pin unless you say otherwise.

So either the sketch has an mistake the likes of which I do not make, which makes it hard for me to see, or... I just don't know.

I did think @Terrypin was onto something, so I made long press 2000 milliseconds.

Oh, I did see

  for (byte n = 0; n <= number_led; n++) {

and had some brief hope that it might have caused all problems... no:

Button Pressed 2
Button 2 - A short press is detected -
Button Pressed 2
Button 5 - A long press is detected - LED 5
Button Pressed 2
Button 2 - A short press is detected -
Button Pressed 2
Button 2 - A short press is detected -

a7

No begin() method, pin mode is INPUT_PULLUP by default, the instance can specific using an argument additional to the pin number, viz:

ezButton myButto(myPin, INPUT);

It uses LOW means pressed, I do not see a way to dissuade it.

a7

Me again.

This

    if(button[i].isPressed()){
      pressedTime = millis();

is where it gores wrong. Each button needs its own pressedTime.

An array will do. I have to jet, she who must not be kept waiting is, in fact, waiting... I'll pay for that, but when I look again, please someone say this, and maybe another or two variables that need to be unique to each button instance, has solved this away.

a7

May be more easy to use the MoToButtons class of MobaTools. This class is specifically designed for multiple buttons. It can evaluate up to 32 buttuns in one instance. And it integrates functionality for short and long press ( and single/double click ).

If you enable all warnings you will see another error:

This will go beyond the size of the array

if you make a global instance as this is the case here, the constructor (where INPUT_PULLUP is set) is called way before main and setup are called. That means that if the initialisation code from Arduino resets the pins' states for some reason then you loose the configuration.

OneButton has this issue on Nano ESP32 (see Library not working on Nano ESP32 Board · Issue #129 · mathertel/OneButton · GitHub) and any library doing the same thing will have the same problem.

There are numerous button library such as Button in easyRun or OneButton or Toggle or EasyButton or Bounce2, ... so plenty to choose from

1 Like

Interesting. It does not appear that with either the UNO or the Mega any such intitialisation removes the pin mode set by the early global object.

I printed the port values. Only by changing the pin mode (back) to INPUT in setup() did I see the pullups disabled.

Still, something to keep in mind. Explicitly setting again to INPUT_PULLUP in setup() won't hurt, and it might save someone the time it takes to go and look at the library and come away with false hope that ezButton works on all processors.

Haha, or test and throw in the trash. I've stopped looking at button libraries until they may be or are in fact someone's problem.

Here it was not ezButton at all to blame. I can't add the array(s) that are necessary to check my theory, and I do not use long/short/still pressed with buttons, but I think the arrays will fix any trouble.

a7

indeed. Or that's usually what a begin() function does.

:wink:

OneButton handles long and short press or double click easily

Adding arrays to all the possible variables seems to work. Time to rewrite this in a slightly more efficient way, but, at least in wokwi.com, it returns the correct button pressed for the short and long press.

[EDIT: I removed the unnecessary arrays and default array values.


#include <ezButton.h>

const int SHORT_PRESS_TIME = 1000; // 1000 milliseconds
const int LONG_PRESS_TIME = 1000; // 1000 milliseconds
const int ledPin[8] = {22, 28, 34, 40, 23, 29, 35, 41};    // the pin that the LED is attached to
ezButton button[8] = {2, 3, 4, 5, 6, 7, 8, 9};  // create ezButton object that attach to pin 7;
int number_led = 8;
int NUMBER_BUTTONS = 8;


unsigned long pressedTime[8]; //default: 0 for array
unsigned long releasedTime[8]; //default: 0 for array
bool isPressing[8]; //default: false for array
bool isLongDetected[8]; //default: false for array

void setup() {
  Serial.begin(9600);
  for (int i = 0; i < (NUMBER_BUTTONS); i++) {
    button[i].setDebounceTime(50);} // set debounce time to 50 milliseconds
  
  for (byte n = 0; n <= number_led; n++) {
    pinMode(ledPin[n], OUTPUT);
  }
}

void loop() {
 for (int i = 0; i < (NUMBER_BUTTONS); i++) {
    button[i].loop(); // MUST call the loop() function first

    if(button[i].isPressed()){
      pressedTime[i] = millis();
      isPressing[i] = true;
      isLongDetected[i] = false;

        Serial.print("Button Pressed ");
        Serial.println(i);
      }
  
  if(isPressing[i] == true && isLongDetected[i] == false) {
    long pressDuration = millis() - pressedTime[i];
        
    if( pressDuration > LONG_PRESS_TIME ) {

      isLongDetected[i] = true;

      Serial.print("Button ");
      Serial.print(i);
      Serial.print(" - A long press is detected - ");

      

      for (byte n = 0; n < NUMBER_BUTTONS; n++) {
          digitalWrite(ledPin[n], LOW);
        }
        digitalWrite(ledPin[i], HIGH);
          
          Serial.print("LED ");
          Serial.println(i);

    }
  }
  
  if(button[i].isReleased()) {
    isPressing[i] = false;
    releasedTime[i] = millis();

    long pressDuration = releasedTime[i] - pressedTime[i];

    if( pressDuration < SHORT_PRESS_TIME ) {
      
      digitalWrite(ledPin[i], LOW);
      Serial.print("Button ");
      Serial.print(i);
      Serial.println(" - A short press is detected - "); 
          }
    }
  }
}

Thank you. I will definitely look into this in the future.

Nice. I would say "does" rather than "seems to". If you followed through the original code, the problem shows up fairly soon for what it is. Even though it took me awhile to notice. :expressionless:

Here

unsigned long pressedTime[8] = {0,0,0,0,0,0,0,0};

and the similar initial values for the global arrays can be eliminated. These variables are assured to be 0 and false. I like to eliminate as much ink as possible, and everyone should know that those will be zero or false as the case may be anyway.

Sometimes a redundant or unnecessary initial value can be used to make a point.

One small other change of no consequence is to write two loops. I like to get the update method for objects took care of right away, then write the other loop just as it is minus the update call, for this library the loop() method, so like

  for (int i = 0; i < (NUMBER_BUTTONS); i = i + 1) {
    button[i].loop();
  }

  for (int i = 0; i < (NUMBER_BUTTONS); i = i + 1) {

// other  stuff
  

It makes a better fit with separating input, processing and output as three steps your loop() makes each pass.

It's called the IPO Model, see it in theoretical terms here:

IPO Model

Also, a few places you write

   i = i + 1

which believe it or not made me slow down and think, it just looks so odd compared to using C's increment operator

   i++

like

  for (int i = 0; i < (NUMBER_BUTTONS); i++)  {

When you get to incrementing more complicated entities, using the increment operator saves time and typing mistakes and makes code easier to read.

The next big thing you might enjoy learning how to exploit is structured variables. They allow you to package up a bunch of things that might want to travel around together, so rather than a half dozen or so arrays you could have one array of structured variables, each having its own set of variables.

With this sketch it might actually be overkill and make things less clear; at some point the extra burden of some new pesky syntax will be worth the benefit of switching.

google

 arduino struct

or

C struct variables

and find things like this competent and gentle introduction

HTH

a7

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.