lennarthennigs / button2 Goto Github PK
View Code? Open in Web Editor NEWArduino/ESP button library that provides callback functions to track single, double, triple and long clicks. It also takes care of debouncing.
License: MIT License
Arduino/ESP button library that provides callback functions to track single, double, triple and long clicks. It also takes care of debouncing.
License: MIT License
I wanted the debounce and longpress capability that I enjoy from Button2 library but my input was a samd21 touch sensor. Might also have other inputs such as analog threshold or buttons on i2c IO expander. If we could set a custom _getState() handler and get around the check for valid pin in Button2::loop() then buttons that are not on digitalRead would be possible and a more generic way than the esp touch button implementation. I was able to do it by changing _getState() to virtual and derived class as follows, but it was extra work and I don't like changing libraries and having a bunch of notes about that in my code. Here's what I got that might give you ideas on a better way:
#pragma once
#ifndef Button2cb_h
#define Button2cb_h
#if defined(ARDUINO_ARCH_ESP32) || defined(ESP8266)
#include
#endif
#include "Arduino.h"
#include <Button2.h>
#define VIRTUAL_PIN 254
typedef byte (*ButtonStateCallbackFunction)(void);
class Button2cb : public Button2 {
protected:
ButtonStateCallbackFunction getState_cb = NULL;
public:
void begin(boolean activeLow = true);
boolean isPressedRaw() const;
void setGetStateHandler(ButtonStateCallbackFunction f);
private:
// note, to properly override this in the base class, it has to be made virtual in Button2.h
byte _getState();
};
/////////////////////////////////////////////////////////////////
/*
Button2cb.cpp
/
/////////////////////////////////////////////////////////////////
#include "Button2cb.h"
void Button2cb::begin(boolean activeLow / = true */)
{
pin = VIRTUAL_PIN; // this gets us past loop()'s (pin != UNDEFINED_PIN) check
longclick_detected_retriggerable = false;
_pressedState = activeLow ? LOW : HIGH;
setDebounceTime(DEBOUNCE_MS);
setLongClickTime(LONGCLICK_MS);
setDoubleClickTime(DOUBLECLICK_MS);
// this constructor allows us to get around setting pinMode on our VIRTUAL_PIN
state = _getState();
prev_state = state;
}
void Button2cb::setGetStateHandler(ButtonStateCallbackFunction f) {
getState_cb = f;
}
boolean Button2cb::isPressedRaw() const { // another digitalRead to work around
if(getState_cb != NULL)
return (getState_cb() == _pressedState);
return false;
}
/////////////////////////////////////////////////////////////////
// note, to properly override this in the base class, it has to be made virtual in Button2.h
byte Button2cb::_getState() {
if(getState_cb != NULL)
{
return getState_cb(); // use our installed callback function to get the virtual pin's state
}
return 0;
}
/////////////////// Now with the new class, I can use the following
Button2cb DebouncerQtColorChange;
// callback function to read our samd21 qTouch via the measure() function to get the analog value of the touch peripheral
byte ButtonQtColorGetState()
{
return (ButtonQtColorChange.measure() > QtThreshold) ? LOW : HIGH;
}
void setup()
{
DebouncerQtColorChange.begin(); // Button2 subclass
DebouncerQtColorChange.setGetStateHandler(ButtonQtColorGetState); // set my custom way to _getState()
DebouncerQtColorChange.setClickHandler(AnimationColorShiftButtonHandler);
DebouncerQtColorChange.setLongClickHandler(AnimationColorShiftButtonHandler);
}
My understanding is that the library would distinguish between a click and a hold, not perform both actions when the button is held, which is what is happening.
Hi!
I'm working on esp32 and I need SerialBT lib AND button2.. button2 work great if I comment out SerialBT command in sketch.
As soon as I uncomment SerialBT command, button2 refuse to work. SerialBT is working.
There's no delay is code.
Currently, support for std::functional is gated by #if defined(ARDUINO_ARCH_ESP32) || defined(ESP8266)
This could be changed to #if __cplusplus >= 201103L
to include other platforms that support <functional>
such as Teensy.
@LennartHennigs Hi, in my project I'd like to implement a button that when pressed for long, the value displayed in the screen (say, an integer) increases, slowly at the beginning and then faster, according to how long I've been pressing the button.
How can I implement that with this library? Is there an example of this? Is setLongClickDetectedRetriggerable()
the function to use?
Btw, can it be added as a feature?
In some cases it would be helpful to clear all assigned functions and variables.
Example: menu handling where after a click new functions must be assigned, but all older have to be cleared. It can be done right now by manually assigning NULL to all of them, but a simple clear function would solve it.
Another option to delete buttons and create new ones as this is done when an instance is created. But that needs more resources. Any suggestions?
Perhaps really silly question, but none of the examples work with my ESP32. I have 3 buttons switching to ground. They work perfectly reliable when just reading the buttons using pinMode(#, INPUT_PULLUP) and digitalRead(#) (0=pressed, 1=not pressed). Very surprisng since the default of the library also seems to be INPUT_PULLUP.
I'm sure it has something to do with configuring the Button2 button using the constructor or begin().
Is it possible to document this configuration in the Readme file?
Many thanks.
Hi,
I was having a trouble with Arduino-CLI when using TTGO-TDisplay for the target board, this board's example is using Button2 lib, the example seems working fine in Arduino IDE (the regular with GUI one we are using) but Arduino-CLI is not. When I built with CLI, It showed:
fatal error: Button2.h: No such file or directory
Eventually it is there in the library directory.
I did a further investigation and figured it out that was not the Arduino-cli causing the problem but the Button2 library, there is an invalid property in "library.properties" of it: includes=ButtonCallback.h
. IMO, Arduino-cli expected 'ButtonCallback.h' but in the Sketch has #include = "Button2.h"
and 'Buttoncallback.h' is not in the lib directory also. I have refered this document.
I deleted the property from the file and everything worked normally again.
Hi I am trying to use your library for my code, everything seems to be working on compile. However, I am getting an error code saying src\main.cpp: In function 'void setup()':
src\main.cpp:89:27: error: 'click' was not declared in this scope
buttonA.setClickHandler(click);
I am using the multiple buttons part from your code
Hello, I have a need for a button that can detect a press while in a do/while
loop. The application is for a rock tumbler. In my code, I calculate a runtime using a 100k Pot, then use some math to calculate a total number of rotations that need to be executed. The program then hands off to a loop that commits one rotation and then loops for the number of calculated passes.
My code process is more or less like this
setup()
{
button.setLongClickHandler(longClick);
button.setDoubleClickHandler(doubleClick);
button.setClickHandler(click);
}
void longClick(Button2& btn)
{
Log("long Click, Start running\n");
DrawSmallText(szStartRunning);
b_RunningState = !b_RunningState;
if (b_RunningState)
{
StartRunning();
}
}
void doubleClick(Button2& btn)
{
Log("double click detected, change rotation count\n");
long lHours = 0;
char buf[20];
DrawSmallText(szDialHelp);
b_SettingRunTime = !b_SettingRunTime;
//This is extremely ugly, I need to come up with a better way to handle getting the value and present it.
do
{
pot_val = analogRead(POT_PIN);
lHours = map(pot_val, 0, 1023, 5, 245);
sprintf(buf, "Run %u Hrs", lHours);
DrawSmallText(String(buf));
} while (b_SettingRunTime);
//Set the total number of rotations from the calculation macro
Rotations = ROTATE(lHours);
}
//Handle the clicks, to set the event flags, (this assumes multi threading which doesnt really exist.
// so I need to re-evaluate how to handle this part
void click(Button2& btn)
{
Log("click, do something...\n");
if(b_SettingRunTime)
b_SettingRunTime = false;
if(b_SettingRPM)
b_SettingRPM = false;
if(b_RunningState)
b_RunningState = false;
}
void StartRunning()
{
digitalWrite(DIR_PIN, HIGH); //Turn stepper clockwise
digitalWrite(LED_RUNNING, HIGH); // turn on the running led
do
{
// Spin motor for 1 rotation
for(int x = 0; x < stepsPerRevolution; x++)
{
digitalWrite(STEP_PIN, HIGH);
delayMicroseconds(stepDelay);
digitalWrite(STEP_PIN, LOW);
delayMicroseconds(stepDelay);
}
}while (Rotations-- > 0);
digitalWrite(LED_RUNNING, LOW); // turn the LED off by making the voltage LOW
}
What I want to do is detect clicks from within the StartRunning()
, clicks should change the display message to show remaining time running, and a long click to stop the motor.
I can do this with making my own interrupt, but I wasn't sure if I should do that, or add a detection in the loop for a DigitalRead
, or if there were a way to interrupt to detect presses inside the loop using one of your apis?
Any advice or ideas would be most appreciated!
Cheers!
I am referencing a sketch that uses Button2 and I was pointed to this repo, however there is an error I am getting about getAttachPin
, in searching your code I see that it in fact doesn't appear.
I have found several sketches that reference this non-existant class member:
I am using your current master as of this date (1.5.3)
I am not sure where the error is, I can't see any member that returns a pin from the class. Is this something that other parties have created as an extension to your class? It's funny that several sketches refer to it
I don't know which version had this happen, but something changed in the long click and retriggerable features recently, and now my "held down" buttons don't work, though they worked with an earlier version of the library. Can you check to see if any of your recent updates caused this? Is this a known issue?
Attached is my code. Perhaps you can glean something from it?
Hi.
Thank you very much for your library. Fantastic, a time saver!
I'm using it on a project (a CO2 monitor) and I'm adding the possibility to reverse the display 180ΒΊ with a menu option.
In a board like the TTGO T-Tdisplay, when display is reversed I need also to reverse the functionality of the two buttons it has.
Until now I was initializing buttons as:
Button2 btnUp(BTN_UP); // Initialize the up button
Button2 btnDwn(BTN_DWN); // Initialize the down button
void buttonsInit() {
btnUp.setLongClickTime(LONGCLICK_TIME_MS);
btnUp.setLongClickHandler([](Button2 &b) { nav.doNav(enterCmd); });
btnUp.setClickHandler([](Button2 &b) {
// Up
nav.doNav(downCmd);
});
btnDwn.setLongClickTime(LONGCLICK_TIME_MS);
btnDwn.setLongClickHandler([](Button2 &b) { nav.doNav(escCmd); });
btnDwn.setClickHandler([](Button2 &b) {
// Down
nav.doNav(upCmd);
});
}
What is the best way to "reverse the buttons" at execution time, so now btnUp is assigned to pin BTN_DWN (instead of BTN_UP) and btnDwn to pin BTN_UP (instead of BTN_DWN)?
I've got a button, with setLongClickHandler, and setPressedHandler
When I press button shortly, my PressedHandler works fine. But it also triggers when I do LongPress. So, I get an event just once I pressed the button, and another event in few ms (long press detected)
So the question is - how to filter out the Short Press events when waiting for the LongPress?
The press type of that is stored for the button is not updated until the button is released isn't it? I ran into a bit of a problem trying to use a multi-type handler with longClickDetected. It would always report the previous click type until I did 2 in a row. But the same setup worked perfectly if I had a separate handler. That's when it occurred to me that the press type might not be updated until the button is released, therefore causing my problem. Might be something to mention as another difference between longClickHandler and longClickDetectedHanlder.
But I love the new custom handler function! I am planning on using a Seesaw GPIO expander from Adafruit for a project and the only thing that has been holding me back is needing to code all of the button handler timing manually. This is wonderful! Thank you!
Long press is not reported in polling mode.
If you uncomment commented part in this example: https://github.com/LennartHennigs/Button2/blob/master/examples/ButtonLoop/ButtonLoop.ino condition
case long_click:
Serial.println("looong");
break;
will never met.
Hi! Please link where can I get the bmp.h library?
The M5Stack has three keys A, B, C. How to decrypt the key press twice AB, BC, AC
I'w writing a BT BLE keyboard to remotely control a machine via keyboard shortcuts, the most used keys are the arrows, and sometimes you click these a lot to get to the exact position.
Is there a way to disable repetitive click to be detected as double/triple clicks?
I can hack this by duplicating the detection and issuing double/triple the amount of keyboard taps on the respective handlers, but I guess a DEFINE would be far more elegant. Same for long press would be the icing in the cake :)
Thanks!
Hello.
is it possible to use the button2 library with the build in Touchsensors of my esp32?
thanks
Setting my compiler to all messages I get this warning:
D:\5_Tinker\Arduino\libraries\Button2\src\Button2.cpp: In member function 'void Button2::loop()':
D:\5_Tinker\Arduino\libraries\Button2\src\Button2.cpp:181:11: warning: comparison is always true due to limited range of data type [-Wtype-limits]
if (pin > -1) {
^
Since pin numbers are defined as uint8 this comparison seem to be reconsidered isn't it?
'button' was not declared in this scope is the error I get when I run the SingleButton.ino referring to line 34.
Should that instead be ButtonA.loop() ?
proposed patch (tested on esp32)
diff --git a/src/Button2.h b/src/Button2.h
index 503f2b1..2729aca 100644
--- a/src/Button2.h
+++ b/src/Button2.h
@@ -35,6 +35,7 @@
class Button2 {
protected:
byte pin;
+ bool capacitive = false;
int prev_state;
int state;
int pressed;
@@ -60,7 +61,7 @@ class Button2 {
CallbackFunction triple_cb = NULL;
public:
- Button2(byte attachTo, byte buttonMode = INPUT_PULLUP, boolean activeLow = true, unsigned int debounceTimeout = DEBOUNCE_MS);
+ Button2(byte attachTo, bool isCapacitive = false, byte buttonMode = INPUT_PULLUP, boolean activeLow = true, unsigned int debounceTimeout = DEBOUNCE_MS);
void setDebounceTime(unsigned int ms);
void reset();
diff --git a/src/Button2.cpp b/src/Button2.cpp
index 63667b5..4f09d77 100644
--- a/src/Button2.cpp
+++ b/src/Button2.cpp
@@ -10,10 +10,15 @@
/////////////////////////////////////////////////////////////////
-Button2::Button2(byte attachTo, byte buttonMode /* = INPUT_PULLUP */, boolean activeLow /* = true */, unsigned int debounceTimeout /* = DEBOUNCE_MS */) {
+Button2::Button2(byte attachTo, boolean isCapacitive /* = false */, byte buttonMode /* = INPUT_PULLUP */,
+ boolean activeLow /* = true */, unsigned int debounceTimeout /* = DEBOUNCE_MS */) {
pin = attachTo;
setDebounceTime(debounceTimeout);
- pinMode(attachTo, buttonMode);
+ if (!isCapacitive)
+ pinMode(attachTo, buttonMode);
+ else
+ capacitive = true;
+
pressed = activeLow ? HIGH : LOW;
released = activeLow ? LOW : HIGH;
state = pressed;
@@ -107,7 +112,15 @@ unsigned int Button2::getClickType() {
void Button2::loop() {
prev_state = state;
- state = digitalRead(pin);
+ if (!capacitive)
+ state = digitalRead(pin);
+ else{
+ int capa = touchRead(pin);
+ state = capa > 70 ? HIGH : capa < 30 ? LOW : state;
+ }
// is button pressed?
if (prev_state == pressed && state == released) {
Hey,
I noticed that wasPressedFor()
always the returns the time for the last click.
This sounds ok but for double and triple clicks it might not be helpful.
What would you expect?
Ideas are welcome.
Not happy about pin identifiers and mode...
from D:\Electronique\PicoPDSupply\PicoPD_LVGL\PicoPD_LVGL.ino:8:
c:\Users\me\Documents\Arduino\libraries\Button2\src/Hardware.h: In member function 'virtual void ArduinoHardware::pinMode(int, int)':
c:\Users\me\Documents\Arduino\libraries\Button2\src/Hardware.h:42:24: error: invalid conversion from 'int' to 'PinMode' [-fpermissive]
42 | ::pinMode(pin, mode);
| ^~~~
| |
| int
In file included from c:\users\me\appdata\local\arduino15\packages\rp2040\hardware\rp2040\3.6.2\arduinocore-api\api\Interrupts.h:8,
from c:\users\me\appdata\local\arduino15\packages\rp2040\hardware\rp2040\3.6.2\arduinocore-api\api\arduinoapi.h:29,
from C:\Users\me\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.2\cores\rp2040/api/ArduinoAPI.h:2,
from C:\Users\me\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.2\cores\rp2040/Arduino.h:28,
from C:\TEMP\arduino\sketches\870F04C926EA2EED0B213104A8CED5D4\sketch\PicoPD_LVGL.ino.cpp:1:
c:\users\me\appdata\local\arduino15\packages\rp2040\hardware\rp2040\3.6.2\arduinocore-api\api\Common.h:98:44: note: initializing argument 2 of 'void pinMode(pin_size_t, PinMode)'
98 | void pinMode(pin_size_t pinNumber, PinMode pinMode);
| ~~~~~~~~^~~~~~~
c:\Users\me\Documents\Arduino\libraries\Button2\src/Hardware.h: In member function 'virtual void ArduinoHardware::digitalWrite(int, int)':
c:\Users\me\Documents\Arduino\libraries\Button2\src/Hardware.h:45:29: error: invalid conversion from 'int' to 'PinStatus' [-fpermissive]
45 | ::digitalWrite(pin, value);
| ^~~~~
| |
| int
c:\users\me\appdata\local\arduino15\packages\rp2040\hardware\rp2040\3.6.2\arduinocore-api\api\Common.h:99:51: note: initializing argument 2 of 'void digitalWrite(pin_size_t, PinStatus)'
99 | void digitalWrite(pin_size_t pinNumber, PinStatus status);
| ~~~~~~~~~~^~~~~~
There are few methods which don't change the state of Button2
object.
They should be marked with const
keywors:
unsigned int wasPressedFor() const;
boolean isPressed() const;
unsigned int getNumberOfClicks() const;
unsigned int getClickType() const;
Hi.
I'm using a 2 state physical switch and an ESP32.
The switch is constant ON, or constant OFF (act as a light switch), and connected as ACTIVE_LOW.
At boot, switch can be "ON" (LOW
).
In that case, right after boot sequence, light is switched ON, meaning that setPressedHandler
handler called.
Firstly, I'm looking for a way that ON-BOOT-STATE of the switch will be saved as start state.
Secondly, from what I see, inside begin
function that state is supposed to be stored, but still, it is not solved:
state = _getState();
prev_state = state ;
Thirdly, adding button2_instance.loop()
right after button2_instance.begin(Pin)
, actually solved is, but still it looks awkward.
Help any assistance to understand how to store ON-BOOT-STATE, for such cases.
Guy
Hi,
you have given an example of two buttons, you have given me in example of multi.
Well, but I'm sorry it is not obvious to me how to get two buttons with multi.
Could you please provide an example for that.
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 your reference is
void longpress(Button2& btn) {
...
}
I don't see in that handler any reference to the previously defined "button" in your example that would in my case be "up" and "down".
Would you be so kind and give a complete example for at least two buttons?
thank you
Laszlo
I'm finding the single click function doesn't always register (works 80% or less of the time). I'm using a nodemcu V2 and clickable encoder. I have tried replacing the encoder. I can see the blue led light up on the nodemcu everytime I click it even when it doesn't register. Perhaps a timing issue? The sketch is basically built off the demo. I'm using visual studio code with platformio. Using the latest encoder library I pulled from github and the button2 library is from platformio. I tried using your latest one from github and the buttons wouldn't register at all.
#define ROTARY_PIN1 D2
#define ROTARY_PIN2 D3
#define BUTTON_PIN D4
/////////////////////////////////////////////////////////////////
ESPRotary r = ESPRotary(ROTARY_PIN1, ROTARY_PIN2, 2 );
Button2 b = Button2(BUTTON_PIN);
void setup() {
Serial.begin(9600);
delay(1000);
Serial.println("\n\nFocusrite Knob");
setupWifi();
delay(3000);
broadcast();
getPort();
setupClient();
delay(50);
Serial.println("\n\nInitialized");
r.setChangedHandler(rotate);
r.setLeftRotationHandler(showDirection);
r.setRightRotationHandler(showDirection);
b.setClickHandler(muteVol);
b.setLongClickHandler(resetPosition);
}
void loop() {
r.loop();
b.loop();
}
I've borrowed the code from LongpressHandler demo, and it doesn't work.
Then I just tried the demo, and... it doesn't work either - only the 'welcome message' is shown, and no reaction on clicks.
The 'button' is connected to D1 and GND, so I've adjusted the demo sketch:
#define BUTTON_A_PIN D1
Platform: ESP8266 (NodeMCU V3) / Arduino IDE 1.8.13 / ESP8266 Core 2.7.4
When compiling a Button2 sketch using an ESP32 C3 board (I tried 2 different brands, DFRobot and Adafruit), I get the following error:
c:\LIBRARY DIRECTORY\Button2\src\Button2.cpp: In member function 'byte Button2::_getState()':
c:\LIBRARY DIRECTORY\Button2\src\Button2.cpp:293:18: error: 'touchRead' was not declared in this scope
int capa = touchRead(pin);
^~~~~~~~~
c:\LIBRARY DIRECTORY\Button2\src\Button2.cpp:293:18: note: suggested alternative: 'timerRead'
int capa = touchRead(pin);
^~~~~~~~~
timerRead
exit status 1
Compilation error: exit status 1
One thing that sets the C3 apart from other members of the ESP32 family is the fact that it doesn't support touch. I can't seem to figure out why it's reading this as capacitive. Here are the relevant sections of my sketch:
Button2 button_0, button_1, button_2, button_3, button_4, button_5, button_6, button_7;
Button2 myButtons[] = { button_0, button_1, button_2, button_3, button_4, button_5, button_6, button_7 };
int buttonPins[] = {A0, A1, A2, A3, SDA, SCL, TX, RX};
int numButtons = 8;
void setup() {
Serial.begin(115200);
delay(100);
Serial.println("Initialized");
for (int i = 0; i < numButtons; i++) {
myButtons[i].begin(buttonPins[i], INPUT_PULLUP, false, true);
myButtons[i].setClickHandler(clickHandler);
myButtons[i].setDoubleClickHandler(doubleHandler);
myButtons[i].setLongClickDetectedHandler(longHandler);
myButtons[i].setReleasedHandler(released);
myButtons[i].setLongClickTime(800);
myButtons[i].setDoubleClickTime(400);
myButtons[i].setDebounceTime(75);
}
}
And of course I have all the relevant callbacks and whatnot.
Now, If I switch to an ESP32 S2 board, the exact same sketch compiles perfectly with zero changes. So, I assume there is something wrong in the board definition or the variants pin definition for the C3 boards but for the life of me I can't find it. I also tried to define a custom state function for all of the buttons and that didn't help. I still got the same error when compiling. Any advice on where to look and what might need to be changed? I'm using the latest version of the ESP32 Core for Arduino, v2.0.5.
https://github.com/espressif/arduino-esp32/releases/tag/2.0.5
Thanks so much!!
/////////////////////////////////////////////////////////////////
#if !defined(ESP32)
#error This sketch needs an ESP32
#else
/////////////////////////////////////////////////////////////////
#include "Button2.h"
/////////////////////////////////////////////////////////////////
Button2 menu_button;
Button2 eq_button;
Button2 volup_button;
Button2 voldow_button;
#define button1 4 // menu-button
#define button2 15 // e button
#define button3 32 // vol +
#define button4 33 // vol -
/////////////////////////////////////////////////////////////////
byte capStateHandler1() {
int capa = touchRead(button1);
return capa < menu_button.getDebounceTime() ? LOW : HIGH;
}
byte capStateHandler2() {
int capa = touchRead(button2);
return capa < eq_button.getDebounceTime() ? LOW : HIGH;
}
byte capStateHandler3() {
int capa = touchRead(button3);
return capa < volup_button.getDebounceTime() ? LOW : HIGH;
}
byte capStateHandler4() {
int capa = touchRead(button4);
return capa < voldow_button.getDebounceTime() ? LOW : HIGH;
}
/////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(9600);
delay(50);
Serial.println("\n\nCapacitive Touch Demo");
menu_button.setDebounceTime(40);
menu_button.setButtonStateFunction(capStateHandler1);
menu_button.setClickHandler(click);
menu_button.setLongClickHandler(longClick);
menu_button.begin(VIRTUAL_PIN);
eq_button.setDebounceTime(40);
eq_button.setButtonStateFunction(capStateHandler2);
eq_button.setClickHandler(eq_click);
eq_button.begin(VIRTUAL_PIN);
volup_button.setDebounceTime(40);
volup_button.setButtonStateFunction(capStateHandler3);
volup_button.setClickHandler(volup_click);
volup_button.begin(VIRTUAL_PIN);
voldow_button.setDebounceTime(40);
voldow_button.setButtonStateFunction(capStateHandler4);
voldow_button.setClickHandler(voldow_click);
voldow_button.begin(VIRTUAL_PIN);
}
/////////////////////////////////////////////////////////////////
void loop() {
menu_button.loop();
eq_button.loop();
volup_button.loop();
voldow_button.loop();
}
/////////////////////////////////////////////////////////////////
void click(Button2& btn) {
Serial.println("menu click\n");
}
// long click
void longClick(Button2 &btn) {
unsigned int time = btn.wasPressedFor();
if (time > 1000) {
Serial.println("long click\n");
}
}
void eq_click(Button2& btn) {
Serial.println("eq click\n");
}
void volup_click(Button2& btn) {
Serial.println("volup click\n");
}
void voldow_click(Button2& btn) {
Serial.println("voldow click\n");
}
/////////////////////////////////////////////////////////////////
#endif
/////////////////////////////////////////////////////////////////
Hello Mr. Hennings,
First thank you for your very useful library, Button2.
I would like the button.loop() function to run in the background, from a timer interrupt (like one of the timers on STM32F4xx) so that it would be independent of the aleas of other function execution lengths, and also avoid to periodically call the button.loop() function completely. The long click, single, double and triple click would simply set a flag that can be reset at any time after reading the status of the flag(s).
I have to say that I tried to call the button.loop() function from a timer interrupt function but without success.
Thank you for your help.
Regards,
Rene-Jean Mercier
[email protected]
It is useful to let user change LONGCLICK_MS, DOUBLECLICK_MS, DEBOUNCE_MS during compile time.
Suggested change in Button2.h
#ifndef DEBOUNCE_MS
#define DEBOUNCE_MS 50
#endif
#ifndef LONGCLICK_MS
#define LONGCLICK_MS 200
#endif
#ifndef DOUBLECLICK_MS
#define DOUBLECLICK_MS 300
#endif
Usage:
#define LONGCLICK_MS 500
#include <Button2.h>
Hi, from what I understand in the code now, the long press only triggers the callback when the long press is finished. Is there a way to detect a long press while the button is still being pressed?
Hi,
when i run the example ESP32TimerInterrupt.ino, it crashed when i click the button, every time.
here the modified code:
#define BUTTON_PIN 35
btn.begin(BUTTON_PIN, INPUT_PULLDOWN, false);
Other code are not modified.
Here's the log:
click
Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1).
Core 1 register dump:
PC : 0x4008c1f2 PS : 0x00060335 A0 : 0x8008b166 A1 : 0x3ffbf0ec
A2 : 0x3ffb8314 A3 : 0x3ffb81a4 A4 : 0x00000004 A5 : 0x00060323
A6 : 0x00060323 A7 : 0x00000001 A8 : 0x3ffb81a4 A9 : 0x00000018
A10 : 0x3ffb81a4 A11 : 0x00000018 A12 : 0x00000004 A13 : 0x00060323
A14 : 0x007bf328 A15 : 0x003fffff SAR : 0x0000000a EXCCAUSE: 0x00000006
EXCVADDR: 0x00000000 LBEG : 0x4008786d LEND : 0x4008787d LCOUNT : 0xfffffffe
Core 1 was running in ISR context:
EPC1 : 0x400de797 EPC2 : 0x00000000 EPC3 : 0x00000000 EPC4 : 0x00000000
Backtrace: 0x4008c1ef:0x3ffbf0ec |<-CORRUPTED
Core 0 register dump:
PC : 0x4008c38b PS : 0x00060035 A0 : 0x8008ad8f A1 : 0x3ffbea4c
A2 : 0x3ffbf328 A3 : 0xb33fffff A4 : 0x0000abab A5 : 0x00060023
A6 : 0x00060021 A7 : 0x0000cdcd A8 : 0x0000abab A9 : 0xffffffff
A10 : 0x3ffc307c A11 : 0x00000000 A12 : 0x3ffc3078 A13 : 0x00000007
A14 : 0x007bf328 A15 : 0x003fffff SAR : 0x0000001d EXCCAUSE: 0x00000006
EXCVADDR: 0x00000000 LBEG : 0x00000000 LEND : 0x00000000 LCOUNT : 0x00000000
It seems the function "onTimer()" costs too much time? But only "btn.loop();"
Any help will be appreciated!
One more comment, if the private variables was protected, then it would be easy to create a derived class.
Consider the following button:
Button2 bigRedButton = Button2(bigRedButtonPin, INPUT_PULLUP, false, false);
For some weird reason, the button is pressed while the Arduino reboots. This doesn't fire any callbacks. I have tried setPressedHandler
, setReleasedHandler
and setChangedHandler
. Since the button is not active low, shouldn't it trigger on boot? It works fine if I release and press it after booting.
Hi!
Sry if the title might be confusing, but so is the issue I experience.
I have reduced my button code to the core to be able to reproduce it.
This code seems to be working fine on version 2.2.4 of the library, but doesn't recognise my button button anymore from 2.3.0 and 2.3.1
#include <Arduino.h>
#include <Button2.h>
#define BUTTON_PIN 19
Button2 button;
void setup() {
Serial.begin(115200);
Serial.println("Starting Setup");
button.begin(BUTTON_PIN);
}
void loop() {
button.loop();
clickType buttonState = button.read();
if (buttonState != clickType::empty) {
Serial.println("Button State not empty");
}
}
Because I first thought my button broke I even tried to just short PIN and GND. But I experienced the same behaviour with shorting.
Help or explanation would be highly appreciated.
Hi, Greetings,
Thanks for this amazing library Lennart. I'm planning to use this in my code, which includes an OLED display and a rotary encoder. So, I'm wondering if this library is compatible with my code. With respect!
It seems like the isPressed() method is somehow reversed..
When all is behaving as it should - click working etc.. the status of isPressed is 0 when the button is pressed and 1 when the button is released?
Is that by intension or is it a bug?
I have this code running on a Wemos ESP8266 Mini D1 Lite what what is the reference for isPressed - imo it should return 1 if the button is pressed, and 0 if released
I have tried to init the button with activeLow = false also, but still i always endup getting the "reverse" of what i expect in the isPressed() method..
#include <Arduino.h>
#include <Blink.h>
// #define DEBOUNCE_MS 100
#include <Button2.h>
// int LED_PIN = D8;
int LED_PIN = LED_BUILTIN;
int BTN_PIN = D1;
unsigned long lastUpdate = millis();
Button2 btn(BTN_PIN);
void statusUpdate()
{
if ((millis() - lastUpdate) > 1000)
{
bool btnPressed = btn.isPressed();
lastUpdate = millis();
if (btnPressed == 0)
{
Serial.println("Pressed");
Serial.print("Library: ");
Serial.println(btnPressed);
Serial.print("Digital Read: ");
Serial.println(digitalRead(BTN_PIN));
}
else
{
Serial.println("Released");
Serial.print("Library: ");
Serial.println(btnPressed);
Serial.print("Digital Read: ");
Serial.println(digitalRead(BTN_PIN));
}
}
}
void btnClick(Button2& btn) {
Serial.println("Click\n");
}
void btnTap(Button2 &btn)
{
Serial.println("Tap\n");
digitalWrite(LED_PIN, LOW);
delay(100);
digitalWrite(LED_PIN, HIGH);
delay(100);
}
void setup()
{
// put your setup code here, to run once:
Serial.begin(9600);
delay(100);
Serial.println("Startup!");
delay(50);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
delay(1000);
btn.setTapHandler(btnTap);
btn.setClickHandler(btnClick);
}
void loop()
{
btn.loop();
statusUpdate();
}
Hi. I am wiring a GUI for a digital watch/wearable. I am using the Button2 library and the isPressed() method to determine when selections are made. Perhaps my understanding of the the method is incorrect. I assume if I have a button e.g. buttonBack and I use
if (buttonBack.isPressed()){ insert code }
this will only be executed if the button is currently being pressed. What I am finding is that this seems to stay "latched" on many occasions and results in buttonBack.isPressed() evaluating to a value of 1 even if the button is not being pressed. I have played around with the debounce times and do not believe it is noise. I will include the loop portion of my code and delete some of the lines that are not relevant so you have an idea of what I am doing.
`void loop() {
switch (mode){
case 0 :
if (oldMode!=mode){
int numMenuItems = 4;
buttonUp.setClickHandler([scrollDirection, numMenuItems, bitmaps1=bitmaps1, menuLabels1=menuLabels1](Button2 &btn){
scrollMenu(RIGHT, numMenuItems, bitmaps, menuLabels,btn);
});
buttonUp.setLongClickDetectedHandler([scrollDirection, numMenuItems, bitmaps1=bitmaps1, menuLabels1=menuLabels1](Button2 &btn){
scrollMenu(RIGHT, numMenuItems, bitmaps, menuLabels,btn);
});
buttonUp.setLongClickDetectedRetriggerable(1);
buttonDown.setClickHandler([scrollDirection, numMenuItems, bitmaps1=bitmaps1, menuLabels1=menuLabels1](Button2 &btn){
scrollMenu(LEFT, numMenuItems, bitmaps, menuLabels,btn);
});
buttonDown.setLongClickDetectedHandler([scrollDirection, numMenuItems, bitmaps1=bitmaps1, menuLabels1=menuLabels1](Button2 &btn){
scrollMenu(LEFT, numMenuItems, bitmaps, menuLabels,btn);
});
buttonDown.setLongClickDetectedRetriggerable(1);
currentMenuSelection=0;
}
if (buttonBack.isPressed()){
mode=1;
oldMode=0;
break;
}
if (buttonEnter.isPressed()){
mode=currentMenuSelection+2;
break;
}
oldMode=mode;
loopbuttons();
break;
case 1:
if (buttonUp.isPressed()|buttonDown.isPressed()){
mode=0;
oldMode=1;
break;
}
printScreen();
loopbuttons();
break;
case 2: //settings
if (oldMode!=mode){
}
if (buttonBack.isPressed()){
mode=0;
oldMode=1;
break;
}
oldMode=mode;
loopbuttons();
break;
case 3: //Alarm
if (oldMode!=mode){
}
if (buttonBack.isPressed()){
mode=0;
oldMode=1;
break;
}
oldMode=mode;
loopbuttons();
break;
case 4: //Stopwatch
if (oldMode!=mode){
}
if (buttonBack.isPressed()){
mode=0;
oldMode=1;
break;
}
oldMode=mode;
loopbuttons();
break;
case 5: //Flashlight
if (oldMode!=mode){
}
if (buttonBack.isPressed()){
mode=0;
oldMode=1;
break;
}
oldMode=mode;
loopbuttons();
break;
}
}
one thing of interest is that the code may encounter the ' if (buttonBack.isPressed()){' multiple times prior to seeing loopbuttons(); which is simply the following:
'void loopbuttons(){
buttonUp.loop();buttonEnter.loop();buttonDown.loop();buttonBack.loop();
if (alarmFlag==1){
alarmRing();
}
if ((millis()-lastButtonPressedTime)>(buttonInactiveSleepTime*1000)){
deepSleep(dummyButton);
}
}'
At first I thought this was the issue, but even If I add loopbuttons(); at various locations, it still doesn't seem to work. I can always use a button clickHandler, but that is a bunch more code than simply using isPressed();
FYI, as you predicted when using this library inside an interrupt timer on an esp32, it seems to fail to detect long clicks. Every click, long or short, irregardless of .setLongClickTime() is detected as a long click. I haven't delved into why this behaviour is being exhibited but was able to create a quick work around by capturing the start and stop time of the button presses / release within my own application.
Here is a snippet:
#define MODE_BUTTON 35
Button2 buttonMode;
hw_timer_t *mButtonTimer = NULL;
void IRAM_ATTR buttonTimer()
{
// process buttons
buttonMode.loop();
}
setup()
{
mButtonTimer = timerBegin(0, 80, true);
timerAttachInterrupt(mButtonTimer, &buttonTimer, true);
timerAlarmWrite(mButtonTimer, 10000, true); // every 0.1 seconds
timerAlarmEnable(mButtonTimer);
buttonMode.begin(MODE_BUTTON, INPUT, false);
buttonMode.setLongClickHandler(longModeClickStart);
buttonMode.setLongClickDetectedHandler(longModeClickStop);
}
long modeStart;
void longModeClickStart(Button2 &btn)
{
modeStart = micros();
}
void longModeClickStop(Button2 &btn)
{
int secondsSinceLast = round((micros() - modeStart) / 1000000);
Serial.print("Seconds since last mode press: ");
Serial.println(secondsSinceLast);
if (secondsSinceLast > 20)
{
Serial.println("doing something after 20 seconds");
}
}
Line 17 in 8ab31ad
This code:
pressed = activeLow ? HIGH : LOW;
released = activeLow ? LOW : HIGH;
If activeLow is true (i.e. INPUT_PULLUP pin mode used), shouldn't the (button) 'pressed' state be a LOW reading on the pin? And vice versa of the 'released' state - this would be a HIGH reading on the pin (i.e. 'released' is when you're NOT pressing/pushing the button).
I'm trying to find a decent Button library, but want to understand the source code before I dig in. Just checking if I'm reading the source code correctly. The seemingly reversed references to 'pressed' and 'released' is confusing, chances are that I simply got it wrong. Appreciate any clarification you have.
I tried to replace the setLongClickHandler(handler)
...which works fine (but you don't know if your press was long enough until you release the button), by setLongClickDetectedHandler(handler)
and I noticed a side effect.
Look at the log below and my comments in bold.
And this side effect is consistent. You might want to look into it...
--- Terminal on /dev/ttyACM0 | 115200 8-N-1
--- Available filters and text transformations: colorize, debug, default,
direct, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at https://bit.ly/pio-monitor-filters
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H
Starting MMT Power Supply Interface !!!
PG set to PG40
EEPROM Beginning : 0
EEPROM End : 16384
System Voltage Set Value : 10.000
Corresponding DAC Voltage Output : 1.65
Corresponding DAC Binary Voltage Output : 2047
System Current Set Value : 0.500
Corresponding DAC Current Output : 0.82
Corresponding DAC Binary Current Output : 1023
System Input Voltage : 4.7820001
Output Voltage : 7.4822955
System Output Current : 0.0019200
Z Output : 3897.03
System Power W : 0.0090394
(1)** <------------------------------------------------------------- this was a long click (not ok) ... it happens only once after boot-up, as if no case in the function (see function below) was satisfied.***
---long click (1)
**<------------------------------------------------------------- this was a long click (ok)**
ENTERING SELECTION MODE NORMAL OPERATION
---long click (1)
**<------------------------------------------------------------- this was a long click (ok)**
Getting out of the Parameter Selection function
ENTERING MODIFICATION MODE
---long click (1)
**<------------------------------------------------------------- this was a long click (ok)**
System Voltage Set Value : 10.000
Corresponding DAC Voltage Output : 1.65
Corresponding DAC Binary Voltage Output : 2047
Getting out of the Parameter Modificaton function
---double click (2)
**<------------------------------------------------------------- this was a long click (ok)**
ENTERING PRESET SELECTION MODE
----Set pointer to the selected window
Set Coordinates To Preset Window
SetCoordinatesToPresetWindow preset3V3
*****Highlight Preset Window
*****Initialize Preset Window
CW Direction -- Encoder Value : 1
*******1----Set pointer to the selected window
++++Give Preset Colors To Window : preset5V0
Set Coordinates To Preset Window
SetCoordinatesToPresetWindow preset5V0
*****Highlight Preset Window
*****Initialize Preset Window
---double click (1)
**<------------------------------------------------------------- this was a long click (not ok) ... it appens consistently.**
---long click (1)
**<------------------------------------------------------------- this was a long click (ok)**
Preset Value Selected saved to EEPROM
System Voltage Set : 5.000
System Current Set : 0.500
System Input Voltage : 4.7900000
Output Voltage : 7.4725275
System Output Current : 0.0019200
Z Output : 3891.94
System Power W : 0.0095182
/*************************************************************************************************************************/
/* BUTTON SWITCH HANDLER SECTION */
/*************************************************************************************************************************/
void handler(Button2& btn) {
switch (btn.getType()) {
case long_click:
myButtonState = ds.ButtonState::longClick;
Serial.print("---long click ");
//Serial.print(bf.buttonStatus.buttonState); Serial.print("\t");
break;
case double_click:
myButtonState = ds.ButtonState::doubleclick;
Serial.print("---double click ");
//Serial.print(bf.buttonStatus.buttonState); Serial.print("\t");
break;
case triple_click:
myButtonState = ds.ButtonState::tripleClick;
Serial.print("---triple click");
//Serial.print(bf.buttonStatus.buttonState); Serial.print("\t");
break;
case single_click:
myButtonState = ds.ButtonState::click;
Serial.print("---single click");
//Serial.print(bf.buttonStatus.buttonState); Serial.print("\t");
break;
}
Serial.print(" (");
Serial.print(btn.getNumberOfClicks());
Serial.println(")");
}
void FunctionProcessing::buttonSetup() {
button.begin(ENCODER_SWITCH_PORT,INPUT_PULLUP,false,true); // byte
attachTo, byte buttonMode /* = INPUT_PULLUP */, boolean isCapacitive /* =
false */, boolean activeLow /* = true */
button.setDebounceTime(50); //
button.setLongClickTime(500);
button.setDoubleClickTime(700);
button.setClickHandler(handler);
button.setLongClickDetectedHandler(handler); //setLongClickHandler(handler);
button.setDoubleClickHandler(handler);
button.setTripleClickHandler(handler);
}
Regards,
Rene-Jean
Originally posted by @renejeanmercier in #43 (comment)
Looks like the Seeed Studio xiao_esp32c3 triggers ARDUINO_ARCH_ESP32
(sounds reasonable) but does not support touchRead()
?
Hi,
is there a way to call functions with values like:
button1.setButtonStateFunction(buttonstate(i)).
So i can use one state function for different buttons?
All the best and love
Adrian
ESP32 can wake up from sleep only for active HIGH, therefore most designs use this. Would be nice if this nice piece of code worked for active HIGH buttons.
TrackDualButtonClick Example not working for me. I get pressed but released never triggers.
Longpress works.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.