ButtonDriver is a C++ based component written for esp-idf version 5.0+, intended to simplify the use of push-buttons and tactile switches.
It allows for the creation of Button objects which automatically detect user input from externally connected tactile switches or push-buttons.
Call-back functions can be registered to button objects to handle detected user input.
-
Create a "components" directory in the root workspace directory of your esp-idf project if it does not exist already.
In workspace directory:
mkdir components
-
Cd into the components directory and clone both the ButtonDriver, and DataControl repos.
cd components git clone https://github.com/myles-parfeniuk/data_control.git git clone https://github.com/myles-parfeniuk/button_driver.git
The ButtonDriver is dependent on DataControl and will not build without it.
-
Ensure you clean your esp-idf project before rebuilding.
Within esp-idf enabled terminal:idf.py fullclean
This is intended to be a quick-guide, api documentation generated with doxygen can be found in the documentation directory of the master branch.
To initialize a button object, first initialize and configure a button_conf_t struct with the desired settings, then pass it into the Button constructor.
The settings available within a button_conf_t struct:
- gpio_num — The GPIO number associated with the button, must not be initialized as GPIO_NUM_NC
- active_lo — Set to true if the button is active low (falling edge trigger), cannot be true if active_hi is also true
- active_hi — Set to true if the button is active high (rising edge trigger), cannot be true if active_lo is also true
- pull_en — Set to true if internal pullup/pulldown resistor is enabled for button gpio pin, set to false if external resistors are used
- long_press_evt_time — long-press event generation time in microseconds (us) if the button is held for longer than (long_press_evt_time+25ms) a long-press event is generated, if it is released before (long_press_evt_time+25ms) elapses, a quick-press event is generated instead suggested time of 300000us, must be between 10000us and 5000000us
- held_event_evt_time — held event generation time in microseconds (us), if a long press event has already occurred and the button is still being held, held events will be generated every held_evt_time elapses, suggested time of 200000us, must be between 10000us and 5000000us
If the button_conf_t struct is not initialized correctly the Button constructor will output an error related to the issue in the terminal and dump a stack trace.
Example Initializations:
- Active-Low w/ external pullup
//initialize button_config_t struct
Button::button_config_t button_conf =
{
.gpio_num = GPIO_NUM_25, //gpio number connected to button, for ex.25
.active_lo = true, //active low
.active_hi = false, //not active high
.pull_en = false, //internal pullup disabled
.long_press_evt_time = 300000, //300ms long-press event generation time
.held_evt_time = 200000, //200ms held event generation time
};
//declare & initialize Button object
Button my_button(button_conf);
- Active-Low w/ no external pullup
//initialize button_config_t struct
Button::button_config_t button_conf =
{
.gpio_num = GPIO_NUM_25, //gpio number connected to button, for ex.25
.active_lo = true, //active low
.active_hi = false, //not active high
.pull_en = true, //internal pullup enabled
.long_press_evt_time = 300000, //300ms long-press event generation time
.held_evt_time = 200000, //200ms held event generation time
};
//declare & initialize Button object
Button my_button(button_conf);
- Active-High w/ external pulldown
//initialize button_config_t struct
Button::button_config_t button_conf =
{
.gpio_num = GPIO_NUM_25, //gpio number connected to button, for ex.25
.active_lo = false, //not active low
.active_hi = true, //active high
.pull_en = false, //internal pulldown disabled
.long_press_evt_time = 300000, //300ms long-press event generation time
.held_evt_time = 200000, //200ms held event generation time
};
//declare & initialize Button object
Button my_button(button_conf);
- Active-High w/ no external pulldown
//initialize button_config_t struct
Button::button_config_t button_conf =
{
.gpio_num = GPIO_NUM_25, //gpio number connected to button, for ex.25
.active_lo = false, //not active low
.active_hi = true, //active high
.pull_en = true, //internal pulldown enabled
.long_press_evt_time = 300000, //300ms long-press event generation time
.held_evt_time = 200000, //200ms held event generation time
};
//declare & initialize Button object
Button my_button(button_conf);
After being initialized, a Button object will automatically detect any user input and generate an event.
These events come in 4 flavors:
-
quick-press:
This event indicates the button was momentarily pressed. This event is generated when the push-button is pressed & then released before (25ms + long_press_evt_time) has elapsed. -
long-press:
This event indicates the button was pressed and held. This event is generated when the push-button is pressed & not released after (25ms + long_press_evt_time) has elapsed. -
held:
This event indicates a long-press event has already occurred, and the button is still being held. This event is generated every time held_evt_time elapses, after a long-press event, until the button is released. -
released:
This event indicates the button has been released. This event is generated if the button is released any time after a long_press event has occurred.
In order to be notified when a button-event has occurred, a call-back function (or multiple) can be registered with the button by calling the follow() method on its event member.
As many call-backs as desired can be registered to a button using follow(). When a button event occurs, any call-backs registered to the respective button will be called in the order they were registered— this means whichever call-back was registered first has highest priority.
It is recommended to initialize the call-back functions as lambda-functions for easy readability.
Any call-back function registered with follow() must take the form:
void call_back_example(Button::ButtonEvent event);
Example call-back functions & registrations:
- Using a lambda call-back function:
//call the follow() method on a button event member to register a callback with a button
my_button.event.follow(
//lambda call-back function— called automatically when button input is detected
[](Button::ButtonEvent event)
{
//button event handler
switch(event){
case Button::ButtonEvent::quick_press:
//place code that should be run on quick-press here
break;
case Button::ButtonEvent::long_press:
//place code that should be run on long-press here
break;
case Button::ButtonEvent::held:
//place code that should be run when button is held here
break;
case Button::ButtonEvent::released:
//place code that should be run when button is released here
break;
}
});
- Using call-back function pointer:
//call-back prototype
void my_callback(Button::ButtonEvent event);
//call the follow() method on a button event member to register a callback with a button
my_button.event.follow(my_callback);
//call-back function— called automatically when button input is detected
void my_callback(Button::ButtonEvent event)
{
//button event handler
switch(event)
{
case Button::ButtonEvent::quick_press:
//place code that should be run on quick-press here
break;
case Button::ButtonEvent::long_press:
//place code that should be run on long-press here
break;
case Button::ButtonEvent::held:
//place code that should be run when button is held here
break;
case Button::ButtonEvent::released:
//place code that should be run when button is released here
break;
}
}
Examples are available in the ButtonDriver directory of my esp_idf_cpp_examples repo:
https://github.com/myles-parfeniuk/esp_idf_cpp_examples
Distributed under the MIT License. See LICENSE.md
for more information.
Myles Parfeniuk - myles.parfenyuk@gmail.com
Project Link: https://github.com/myles-parfeniuk/button_driver