A better button library!

I cannot get Button2 Library to work for several buttons AND with short press long press discrimination?

  • There is currently an example with one button and multiple + long press discrimination.
  • There is another example with several buttons.

But there is no example combining both.

I have tried:

Button2 up = Button2(BUTTON_UP);
Button2 down = Button2(BUTTON_DOWN);

and
in Loop()

up.loop();
down.loop();

but then: how can i get two functions, when the long press handler in the reference is

void longpress(Button2& btn) {
...
}

I don't see in that handler any reference to the in the example previously defined "button" that would in my case be "up" and "down".

So how could I use longpress() and shortpress() referencing the two above-mentioned button instances?

How to determine how long a button is pressed - Using Arduino / Project Guidance - Arduino Forum

I see an example on GitHub: MultiHandler.ino

It's setting the handler function the same for every event, but obviously you don't have to:

button.setClickHandler(handler);
  button.setLongClickHandler(handler);
  button.setDoubleClickHandler(handler);
  button.setTripleClickHandler(handler);

I have seen the multiHandler example for one button.
But how to use it for two buttons?

Call the two functions that set the handlers you require on each of your button objects.

Yes, but how?

The function in the given example is:

void handler(Button2& btn) {
    switch (btn.getClickType()) {
        case SINGLE_CLICK:
            break;
        case DOUBLE_CLICK:
            Serial.print("double ");
            break;
        case TRIPLE_CLICK:
            Serial.print("triple ");
            break;
        case LONG_CLICK:
            Serial.print("long");
            break;
    }

how can i use it referencing my two separate instances up and down?
What is "btn" in void handler(Button2& btn) referencing to?

Can it be that the example is wrong and "btn" should be "button" referencing to the above instantiation: Button2 button = Button2(BUTTON_PIN);

The example is fine - btn is a function parameter so it refers to whichever button had the event. I expect that you want different things to happen for your various buttons, so you could have two handler functions, one for each button.

but how,?
Please,... please, explain how!

Where can i put a reference to my previously defined instances?

I have defined:

Button2 up = Button2(BUTTON_UP);
Button2 down = Button2(BUTTON_DOWN);

in setup():

void setup() {
  up.setClickHandler(handler);
  up.setLongClickHandler(handler);
  down.setClickHandler(handler);
  down.setLongClickHandler(handler);
}

and
in Loop()

up.loop();
down.loop();

but now, please, please how can i make the function void handler(Button2& btn) {}
specific to up and down?

Make a copy of the handler function and give it a new name.

In setup, use the new function as the handler for button two.

Then, when an event occurs, the specific handler for the button pressed will be called.

that way?

Button2 up = Button2(BUTTON_UP);
Button2 down = Button2(BUTTON_DOWN);

in setup():

void setup() {
  up.setClickHandler(up_handler);
  up.setLongClickHandler(up_handler);
  down.setClickHandler(down_handler);
  down.setLongClickHandler(down_handler);
}

and
in Loop()

up.loop();
down.loop();

Then finally

void up_handler(Button2& btn) {
switch (btn.getClickType()) {
case SINGLE_CLICK:
Serial.print("up_short");
break;
case LONG_CLICK:
Serial.print("up_long");
break;
}
Serial.print("click");
Serial.print(" (");
Serial.print(btn.getNumberOfClicks());
Serial.println(")");
}

void down_handler(Button2& btn) {
switch (btn.getClickType()) {
case SINGLE_CLICK:
Serial.print("down_short");
break;
case LONG_CLICK:
Serial.print("down_long");
break;
}
Serial.print("click");
Serial.print(" (");
Serial.print(btn.getNumberOfClicks());
Serial.println(")");
}

Puh! ...and that is supposed to make button handling easy?
:-\

That's it. As to whether it makes buttons easier, that's a matter of opinion, but I've never used that library, so I don't have strong feelings on the matter.

You don't need to define multiple handlers, you can just check which button caused the event in the handler. (If I understand the library correctly, I haven't used it and didn't look into the source code.)

Button2 up = Button2(BUTTON_UP);
Button2 down = Button2(BUTTON_DOWN);

void setup() {
  up.setClickHandler(handler);
  up.setLongClickHandler(handler);
  down.setClickHandler(handler);
  down.setLongClickHandler(handler);
}

void handler(Button2 &btn) {
  if (&btn == &up) {
    ...
  } else if (&btn == &down) {
    ...
  }
}

Personally, I don't think callbacks are the right approach here, in my own libraries and projects, I use button classes that return an enum value, e.g.

enum class Event {
  Nothing = 0,
  Pressed,
  PressedLong,
  ReleasedShort,
  ReleasedLong,
};

Then in your main code, you use:

switch (btn.update()) {
    case Event::Nothing: break;
    case Event::Pressed: /* ... */ break;
    case Event::ReleasedShort: /* ... */ break;
    // etc.
}

If you're going to use callbacks as required by the library though, it seems dirty to add code in the callback that figures out which button it was and then combine all your button actions into one large function. Separate handlers avoid polluting your code with that logic.

I've no dog in this fight though - If I need to read a button, I'll just digitalRead it and rely on state change logic to defend against bounce.

This is my opinion too. I don' t like button libs with callbacks either. Another point: especially with more buttons this is unnecessary ram consuming ( the lib must store all callback adresses ).
My MobaTools lib contains a class to manage multiple buttons in one instance. Within the sketch you ask simply what happend with an if statement. eg

if ( button.longPress(buttonNmbr) ) {
   // do something
}

Thank you PieterP!

That worked.

Can you recommend an easier library without callbacks?
I just need 2 buttons with short and long press each.
The easier, the better...

I am completely puzzled.
I got that lib to work in a solo program.

#include <Button2.h>
#define BUTTON_UP    35
#define BUTTON_DOWN  0  

Button2 up = Button2(BUTTON_UP);
Button2 down = Button2(BUTTON_DOWN);


void setup() {
  Serial.begin(115200);
  delay(50);
  Serial.println("\n\nDouble Multi button_handler Demo");
  
  up.setClickHandler(button_handler);
  up.setLongClickHandler(button_handler);
  up.setDoubleClickHandler(button_handler);
  up.setTripleClickHandler(button_handler);

  down.setClickHandler(button_handler);
  down.setLongClickHandler(button_handler);
  down.setDoubleClickHandler(button_handler);
  down.setTripleClickHandler(button_handler);
  
}

void loop() {
  // put your main code here, to run repeatedly:
  up.loop();
  down.loop();
}

void button_handler(Button2& btn)
{
  if (&btn == &up)
  {
    switch (btn.getClickType())
    {
      case SINGLE_CLICK:
        Serial.print("up_short");
        break;
      case LONG_CLICK:
        Serial.print("up_long");
        break;
    }
    Serial.print("click");
    Serial.print(" (");
    Serial.print(btn.getNumberOfClicks());
    Serial.println(")");
  } else if (&btn == &down)
  {
    switch (btn.getClickType())
    {
      case SINGLE_CLICK:
        Serial.print("down_short");
        break;
      case LONG_CLICK:
        Serial.print("down_long");
        break;
    }
    Serial.print("click");
    Serial.print(" (");
    Serial.print(btn.getNumberOfClicks());
    Serial.println(")");
  }
}

But now that I try to integrate exactly that code into my program I get not understandable errors::

b_Functions:10:21: error: variable or field 'button_handler' declared void
 void button_handler(Button2& btn)
                     ^
b_Functions:10:21: error: 'Button2' was not declared in this scope
b_Functions:10:30: error: 'btn' was not declared in this scope
 void button_handler(Button2& btn)

I did nothing else than loading the library, then instantiating the lib and immediately at the root level define the function:

//****** Instantiations ******
WiFiUDP UDP; // Creation of wifi Udp instance
Button2 up = Button2(BUTTON_UP);
Button2 down = Button2(BUTTON_DOWN);
void button_handler(Button2& btn)
{
  if (&btn == &up)
  {
    switch (btn.getClickType())
    {
      case SINGLE_CLICK:
        Serial.print("up_short");
        break;
      case LONG_CLICK:
        Serial.print("up_long");
        break;
    }
    Serial.print("click");
    Serial.print(" (");
...

Why is the compiler complaining about a
"variable or field 'button_handler' declared void
" where I declared a function?

I did nothing else than cut and pasting the code working from the empty example, at the same level i.e. at the root scope, where it should be.

I am now struggling since 8 hours with this stupid stuff, just to manage 2 buttons?

I think I will give up and do the edge recognition and time evaluation myself.
It was a mistake to rely to that library.
:unamused:

Did you give my MoToButtons ( part of MobaTools) a try?
Here's a short demo for 2 buttons with short and long press. I think it's fairly simple. The buttons must be connected between the pin and gnd.

// Show Events of buttons in serial monitor

#define MAX8BUTTONS     // This saves ressources if you don't need more than 8 buttons
#include <MoToButtons.h>
// define pin numbers
const byte buttonPins [] = { 2, 3 };
enum : byte { UP, DOWN };
const byte buttonCount = sizeof(buttonPins);

MoToButtons Buttons( buttonPins, buttonCount, 20, 500 ); // 500ms to distinguish short/long

void setup()
{
    Serial.begin(115200);

    Serial.println("Starting loop");
}

void loop() {
    //--------------------------------------------------------
    // read and process buttons
    Buttons.processButtons();
    //
    //--------------------------------------------------------
    // print to serial monitor if an event happens ( pressing long or short )
    if ( Buttons.longPress(UP) ) {
        Serial.println("button UP pressed long");
    }
    if ( Buttons.shortPress(UP) ) {
        Serial.println("button UP pressed short");
    }
    if ( Buttons.longPress(DOWN) ) {
        Serial.println("button DOWN pressed long");
    }
    if ( Buttons.shortPress(DOWN) ) {
        Serial.println("button DOWN pressed short");
    }

}

The MobaTools lib can be installed by means of the library manager.

1 Like

Thank you.

The MobaTools lib can be installed by means of the library manager.

I am already at 74% of my ESP32 program memory. Isn't the whole lib a bit too much, if I just need two buttons? I would probably use 1% from it...

Nothing from the rest of the lib will be part of your sketch, if you only use MoToButtons.

The compiler/linker will only use the pieces of the library need to execute what is called. You should try install and use the library and see what it really does to your memory.

You have been asking for a simple library. You were referred to one.

If you want help doing this with one of the many button libraries(I like bounce2) or with edge detection/timing independent of any library you can return to that path.