Which one button library is better?

Hi all.
seems there are few of button library?
which one better please?
Thanks
Adam

What’s better :thinking: ?

Nick Gammon’s Switch Manager is very good.


//                c l a s s   S w i t c h M a n a g e r W i t h F i l t e r
//********************************************^************************************************
/*
  Class for making "switch" objects
  Noise filter Added REV.  1.01  LarryD
 
  inspired by Nick Gammon's "class" below:
  http://gammon.com.au/Arduino/SwitchManager.zip
 
  Example:
  //if used from an external library
  #include <SwitchManagerWithFilter.h>
 
  //"switch" object instantiations
  SwitchManagerWithFilter mySwitch;
 
  void setup()
  {
      //create a "switch" object
      mySwitch.begin (2, handleSwitches);
  }
 
  void loop()
  {
      mySwitch.check();  //check for "switch" operation, do this at loop() speed
  }
 
  //When a valid "switch" operation is detected:
  //- newState will be LOW or HIGH (the state the "switch" is now in)
  //- interval will be how many mS between the opposite state and this state
  //- whichPin will be the pin that caused this change
  //you can share the function amongst multiple switches
  void handleSwitches(const byte newState, const unsigned long interval, const byte whichPin)
   {
      //do something
   }
*/
 
#include <Arduino.h>
 
class SwitchManagerWithFilter
{
    enum {debounceTime = 10, noSwitch = -1};
 
    typedef void (*handlerFunction)
    (
      const byte newState,
      const unsigned long interval,
      const byte whichSwitch
    );
 
    int pinNumber_;
    handlerFunction f_;
    byte oldSwitchState_;
    unsigned long switchPressTime_;  //when the "switch" last changed state
    unsigned long lastLowTime_;
    unsigned long lastHighTime_;
    byte sampleCounter_;
 
  public:
    //constructor
    SwitchManagerWithFilter()
    {
      pinNumber_       = noSwitch;
      f_               = NULL;
      oldSwitchState_  = 0;
      switchPressTime_ = 0;
      lastLowTime_     = 0;
      lastHighTime_    = 0;
      sampleCounter_   = 0;
    }
 
    //****************************************
    void begin (const int pinNumber, handlerFunction f)
    {
      pinNumber_ = pinNumber;
      f_ = f;
 
      //*********************
      if (pinNumber_ != noSwitch)
      {
        pinMode (pinNumber_, INPUT_PULLUP);
 
        oldSwitchState_  = digitalRead(pinNumber_);
      }
    }  //END of    begin()
 
    //****************************************
    void check()
    {
      //*********************
      //we need a valid pin number and a valid function to call
      if (pinNumber_ == noSwitch || f_ == NULL)
      {
        return;
      }
 
      //*********************
      //time to check the "switch"?
      if (millis () - switchPressTime_ < debounceTime)
      {
        //it's not time yet
        return;
      }
 
      //time is up, get ready for next sequence
      switchPressTime_ = millis();
 
      byte switchState = digitalRead(pinNumber_);
 
      //*********************
      //has the "switch" changed state?
      if (switchState == oldSwitchState_)
      {
       // reset the counter as no state changed was detected
        sampleCounter_ = 0;
 
        return;
      }
 
      //the "switch" has changed state
      //this is used to filter electrical circuit noise
      sampleCounter_++;
 
      //Must read the "switch" sequential 2 times before we validate a "switch" change.
      //i.e. if debounceTime is 10ms, it will take 20ms to validate a "switch" change.
      //With an input sample rate of 10ms, input signals > 20ms are guaranteed to be captured.
      //Signals 10-20ms might be captured, with signals < 10ms guaranteed not to be captured.
 
      //*********************
      //is this the 2nd time in two sequential reads?
      if (sampleCounter_ >= 2)
      {
        //remember for next time
        oldSwitchState_ =  switchState;
 
        //get ready for the next 2 samples
        sampleCounter_ = 0;
 
        //*********************
        //is the "switch" LOW or HIGH
        if (switchState == LOW)
        {
          lastLowTime_ = switchPressTime_;
          f_ (LOW, lastLowTime_ -  lastHighTime_, pinNumber_);
        }
 
        //*********************
        //"switch" must be HIGH
        else
        {
          lastHighTime_ = switchPressTime_;
          f_ (HIGH, lastHighTime_ - lastLowTime_, pinNumber_);
        }
      }
    }  //END of   check()
 
};  //END of   class SwitchManagerWithFilter

Define 'better'.

I'm not familiar with button libraries (I roll my own one when needed, buttons are relatively easy). Check which one meets your requirements. If you e.g. need support for double-click, use one that supports it; if you need support for long-press, use one that supports it.

Thank you!

Thanks.

might consider

// check multiple buttons and toggle LEDs

enum { Off = HIGH, On = LOW };

byte pinsLed [] = { 10, 11, 12 };
byte pinsBut [] = { A1, A2, A3 };
#define N_BUT   sizeof(pinsBut)

byte butState [N_BUT];

// -----------------------------------------------------------------------------
int
chkButtons ()
{
    for (unsigned n = 0; n < sizeof(pinsBut); n++)  {
        byte but = digitalRead (pinsBut [n]);

        if (butState [n] != but)  {
            butState [n] = but;

            delay (10);     // debounce

            if (On == but)
                return n;
        }
    }
    return -1;
}

// -----------------------------------------------------------------------------
void
loop ()
{
    switch (chkButtons ())  {
    case 2:
        digitalWrite (pinsLed [2], ! digitalRead (pinsLed [2]));
        break;

    case 1:
        digitalWrite (pinsLed [1], ! digitalRead (pinsLed [1]));
        break;

    case 0:
        digitalWrite (pinsLed [0], ! digitalRead (pinsLed [0]));
        break;
    }
}

// -----------------------------------------------------------------------------
void
setup ()
{
    Serial.begin (9600);

    for (unsigned n = 0; n < sizeof(pinsBut); n++)  {
        pinMode (pinsBut [n], INPUT_PULLUP);
        butState [n] = digitalRead (pinsBut [n]);
    }

    for (unsigned n = 0; n < sizeof(pinsLed); n++)  {
        digitalWrite (pinsLed [n], Off);
        pinMode      (pinsLed [n], OUTPUT);
    }
}

Thank you.

Many button libraries. Too many. Some are just bad - if you deviate from the examples very much at all, they can disappoint.

If Nick Gammon wrote a button handler I would say just use that if it has the features you need.

He uses this pattern

      //*********************
      //time to check the "switch"?
      if (millis () - switchPressTime_ < debounceTime)
      {
        //it's not time yet
        return;
      }

which doesn't rely on delay() to do the debouncing. He doesn't use delay() at all. Which means I don't reject it out of hand, rather I park it for a close look when I get to the lab. If, not when today, as it is not a day to be indoors here, no, and my beach buddy should have been here already, sure, she can be late but do not keep her waiting... and it's Nick Gammon, so.

But I am with @sterretje - as much time as it may have saved me over the years, I almost never use someone else's button code. Not even my own. :expressionless:

At the very least, @shanren promise yourself you'll write your own one day, even if you never reuse it or publish it or whatever - just to know what can go into debouncing and so forth.

Here's a link to one of a few articles by Jack Ganssle, who is worth reading on a variety of topics.

Jack Ganssle on debouncing

a7

Thank you.

  1. I tested the example from SwitchManagerWithFilter.h which works well.
  2. the code below is an example from button2 which Serial.print buttons' status very slow, and missed quite of, why so?
  3. how to replace library for a big sketch? take the code below as an example ?

thanks
adam

/////////////////////////////////////////////////////////////////

#include "Button2.h"

/////////////////////////////////////////////////////////////////

#define BUTTON_A_PIN  2
#define BUTTON_B_PIN  0

/////////////////////////////////////////////////////////////////

Button2 buttonA, buttonB;

/////////////////////////////////////////////////////////////////

void setup() {
  Serial.begin(9600);
  delay(50);
  Serial.println("\n\nMultiple Buttons Demo");
  
  buttonA.begin(BUTTON_A_PIN);
  buttonA.setClickHandler(click);

  buttonB.begin(BUTTON_B_PIN);
  buttonB.setClickHandler(click);
}

/////////////////////////////////////////////////////////////////

void loop() {
  buttonA.loop();
  buttonB.loop();
}

/////////////////////////////////////////////////////////////////

void click(Button2& btn) {
    if (btn == buttonA) {
      Serial.println("A clicked");
    } else if (btn == buttonB) {
      Serial.println("B clicked");
    }
}

/////////////////////////////////////////////////////////////////

There is something in the library that times out the click. I didn't look into that. There are some timing settings that allow a user of the library to tune the response.

Try using the setPressedHandler as the function that gets a chance to call back to your click() code.

Also - get off pin 0! For now, I moved it to pin 3.

This works better:

/////////////////////////////////////////////////////////////////

#include "Button2.h"

/////////////////////////////////////////////////////////////////

#define BUTTON_A_PIN  2
#define BUTTON_B_PIN  3

/////////////////////////////////////////////////////////////////

Button2 buttonA, buttonB;

/////////////////////////////////////////////////////////////////


void setup() {
  Serial.begin(9600);
  delay(50);
  Serial.println("\n\nMultiple Buttons Demo");
  
  buttonA.begin(BUTTON_A_PIN);
  buttonA.setPressedHandler(click);

  buttonB.begin(BUTTON_B_PIN);
  buttonB.setPressedHandler(click);
}

/////////////////////////////////////////////////////////////////

void loop() {
  buttonA.loop();
  buttonB.loop();
}

/////////////////////////////////////////////////////////////////

void click(Button2& btn) {
    if (btn == buttonA) {
      Serial.println("A pressed");
    } else if (btn == buttonB) {
      Serial.println("B pressed");
    }
}

/////////////////////////////////////////////////////////////////

but I still feel like it could be better.

TBH seven minutes of trying to use Button2 makes me leave it in the reject pile, not so much as it is no good, but because innocent striaghtforward use leads to forum questions.

I'll keep looking for a library I might actually like and use. ;-).

But this simple test seems to work OK, and the callback mechanism, if it fits into your design, is one way to handle things that happen.

a7

It depends what you need.

If you need a "one library for all needs" take the OneButton Library. It has an advanced documentation and covers most things I need in larger projects.
http://www.mathertel.de/Arduino/OneButtonLibrary.aspx

for simple things I just take my Noiasca Toolkit which contains a button class also:
https://werner.rothschopf.net/microcontroller/202203_tools_button_en.htm

#include <Noiasca_button.h> // download library from http://werner.rothschopf.net/microcontroller/202202_tools_led_en.htm

Button buttonA {A0}; // active LOW: button connects pin with GND

void setup() {
  Serial.begin(115200);
  buttonA.begin(); // you have to call the .begin() method
}

void loop() {
  if (buttonA.wasPressed() == true) // fire if button was pressed
  {
    Serial.println(F("button A was pressed")); // put here what ever you want to do when the button gets pressed
  }
}

It is is just a simple "Click" library but can be used with callbacks also.
Simple enough?

and finally, if I just need some buttons in a sketch like on wokwi I'm using a local class

Thanks.
I tested the example of OneButton, it has lot of settins:

BTW: what can be if I use two button libraries? for example: button.h and OneButton.h


 // link the button 1 functions.
  button1.attachClick(click1);
  button1.attachDoubleClick(doubleclick1);
  button1.attachLongPressStart(longPressStart1);
  button1.attachLongPressStop(longPressStop1);
  button1.attachDuringLongPress(longPress1);

and functions, how to simplify and optimize the code if I have 6 buttons?

/*
 This is a sample sketch to show how to use the OneButtonLibrary
 to detect click events on 2 buttons in parallel. 
 The library internals are explained at
 http://www.mathertel.de/Arduino/OneButtonLibrary.aspx
 
 Setup a test circuit:
 * Connect a pushbutton to pin A1 (ButtonPin) and ground.
 * Connect a pushbutton to pin A2 (ButtonPin) and ground.
 * The Serial interface is used for output the detected button events.
 
 The Sketch shows how to setup the library and bind 2 buttons to their functions.
 In the loop function the button1.tick and button2.tick functions have to be called as often as you like.
 */

// 01.03.2014 created by Matthias Hertel
// ... and working.

/* Sample output:

Starting TwoButtons...
Button 1 click.
Button 2 click.
Button 1 doubleclick.
Button 2 doubleclick.
Button 1 longPress start
Button 1 longPress...
Button 1 longPress...
Button 1 longPress...
Button 1 longPress stop
Button 2 longPress start
Button 2 longPress...
Button 2 longPress...
Button 2 longPress stop

*/

#include "OneButton.h"

// Setup a new OneButton on pin A1.  
OneButton button1(A1, true);
// Setup a new OneButton on pin A2.  
OneButton button2(A2, true);


// setup code here, to run once:
void setup() {
  // Setup the Serial port. see http://arduino.cc/en/Serial/IfSerial
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
  Serial.println("Starting TwoButtons...");

  // link the button 1 functions.
  button1.attachClick(click1);
  button1.attachDoubleClick(doubleclick1);
  button1.attachLongPressStart(longPressStart1);
  button1.attachLongPressStop(longPressStop1);
  button1.attachDuringLongPress(longPress1);

  // link the button 2 functions.
  button2.attachClick(click2);
  button2.attachDoubleClick(doubleclick2);
  button2.attachLongPressStart(longPressStart2);
  button2.attachLongPressStop(longPressStop2);
  button2.attachDuringLongPress(longPress2);

} // setup


// main code here, to run repeatedly: 
void loop() {
  // keep watching the push buttons:
  button1.tick();
  button2.tick();

  // You can implement other code in here or just wait a while 
  delay(10);
} // loop


// ----- button 1 callback functions

// This function will be called when the button1 was pressed 1 time (and no 2. button press followed).
void click1() {
  Serial.println("Button 1 click.");
} // click1


// This function will be called when the button1 was pressed 2 times in a short timeframe.
void doubleclick1() {
  Serial.println("Button 1 doubleclick.");
} // doubleclick1


// This function will be called once, when the button1 is pressed for a long time.
void longPressStart1() {
  Serial.println("Button 1 longPress start");
} // longPressStart1


// This function will be called often, while the button1 is pressed for a long time.
void longPress1() {
  Serial.println("Button 1 longPress...");
} // longPress1


// This function will be called once, when the button1 is released after beeing pressed for a long time.
void longPressStop1() {
  Serial.println("Button 1 longPress stop");
} // longPressStop1


// ... and the same for button 2:

void click2() {
  Serial.println("Button 2 click.");
} // click2


void doubleclick2() {
  Serial.println("Button 2 doubleclick.");
} // doubleclick2


void longPressStart2() {
  Serial.println("Button 2 longPress start");
} // longPressStart2


void longPress2() {
  Serial.println("Button 2 longPress...");
} // longPress2

void longPressStop2() {
  Serial.println("Button 2 longPress stop");
} // longPressStop2


// End

I would use just one library and handle all buttons with that library.

if you have 6 buttons with 5 actions each you end up with 30 events. that looks complex from the start.

What do you want to achieve? What should your 6 buttons do?

@noiasca nice button class. I've done more and less. I like the timing mechanism.

This

 lastDebounceTime = millis() & 0xFF;   // we store just the last byte from millis()

turns a problem that isn't a problem from happening every 49.7 days to not happening every 256 ms. :wink:

@shanren asks

BTW: what can be if I use two button libraries?

Theoretically there's no problem. You could probably even use both libraries on one button. But it sounds like a nightmare for the programmer to keep straight, and it certainly means larger code.

I too want to know where this is headed…

Anytime you find yourself making things that differ only in an appended digit, like button1, button2, button3 or clockHandler1, clockHandler2 and so forth, it is a sign that array variables may be appropriate.

Write a sketch a bit more like what you want to do, and it is possible that we can show you where well picked array variables and good functions might cut down the many many handlers you are heading for otherwise.

a7

You have three switches but this

//"switch" object instantiations
SwitchManagerWithFilter mySwitch;

only makes one SwitchManagerWithFilter object named mySwitch!

So

//"switch" object instantiations
SwitchManagerWithFilter mySwitchNamedA, mySwitchNamedB, mySwitchNamedC;

would be how to make three switches, this implies other "adjustments" which may be obvious.

Here's one adjustment:

void loop()
{
  mySwitchNamedA.check();                       //check for "switch" operation, do this at loop() speed
  mySwitchNamedB.check();                       //check for "switch" operation, do this at loop() speed
  mySwitchNamedC.check();                       //check for "switch" operation, do this at loop() speed
}

Now anytime you find yourself making things that differ only in an appended character, like mySwitchNamedA, mySwitchNamedB, mySwitchNamedC and so forth is a sign you might benefit from using array variables.

The above might look like

void loop()
{
  for (unsigned char ii = 0; ii < NUMBER_OF_BUTTONS) 
    mySwitchArray[ii].check();                       //check for "switch" operation, do this at loop() speed
}

So array variables. And either way, as many objects as you have buttons.

HTH

a7

Thank you alto777.
It works now.
there is a question here, it output:

switchnewState =0
interval =1260
whichPin =13
switchnewState =1
interval =330
whichPin =13

if I take the switchnewState as condition to do action, seems not easy? not same as other library take 'click' as event? or I just misunderstand?

for example:

if(switchnewState ==0)
{
digitalWrite(ledC, HIGH);
}

how to keep the led ON by button pressed and OFF when next pressed or OFF after 1 second?

Please show your current complete sketch.

From the post you deleted (why?)

void handleSwitches(const byte newState, const unsigned long interval, const byte whichPin)
{
  if (whichPin == 12 && newState == 1) {
    digitalWrite(ledC, HIGH);
    delay(100);
  }
  else if (whichPin == 11 && newState == 1) {
    digitalWrite(ledB, HIGH);
    delay(100);
  }
  else if (whichPin == 10 && newState == 1) {
    digitalWrite(ledA, HIGH);
    delay(100);
  }
}

we see Nick has used one callback function for all switches.

We see internal to that function his means of seeing which button has made the callback get called.

If the functions of the buttons are different enough, one might decide to employ multiple handlers (callback functions), set up in, where else, setup()

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

  mySwitch.begin (btnA, handleSwitchA);
  mySwitch.begin (btnB, handleSwitchB);
  mySwitch.begin (btnC, handleSwitchC);
}

which implies writing the three functions. I can't help but mention that here again array variables might make this look different. You can have an array of function pointers and call any of them by index. But don't rush into that, some may never need to get down in the weeds. Learn about plain everyday use of array variables way first.

If the function of the three buttons can be largely handled by the same code, and you using Nick's library, then the way he shows how to differentiate and act is how you have to go.

That's something I don't like about any library. To use one, you have to adopt the mindset of the library writer. Here it means using the callback mechanism. There may be other ways to use it, it would also be possible to have the callback function(s) do little else that raise flags you could use however. But basically he's pushing towards the callback mechanism. Which works well.

If a button library philosophy is a good match for your code structure and how you like to do things, it can be a real shortcut. If using a library means you feel like you are bending over more than backwards to accommodate its design and how it functions, then it may not be the right library.

Which may be why I just write my own button stuff, it may be unconventional, eccentric even, but it does what I want, provides button information that I can use directly and so forth.

As always, it just depends on what kind of fun you are looking to have.

HTH

a7

Thanks.
I'll test more about.
another thing is the code below works fine, if I put switchC which attached to 12 replaced 12 , got error of:

if (whichPin == 12 && newState == 0) {
    ledCBJ = 1;
    digitalWrite(ledC, HIGH);
    delay(100);
  }

ERROR:

no match for 'operator==' (operand types are 'const byte' {aka 'const unsigned char'} and 'SwitchManagerWithFilter')

Hi, I used JC_Button in latest project and it`s pretty good.
https://github.com/JChristensen/JC_Button