Think about it, when you’re working on IoT projects or DIY gadgets, handling various button events can turn your code into a mess… Is there a tool that can handle press, release, single click, double click, long press, and repeated presses all at once? Today, I want to recommend an efficient tool that I’ve been using daily: MultiButton. Honestly, once you try it, you won’t want to go back.
What is MultiButton and why is it so appealing?
MultiButton is essentially a lightweight and flexible multi-key state machine library. The core idea is:
- • Driven by a state machine, it progressively determines the button state
- • Built-in debouncing and timing checks
- • Supports multiple instances, with a very convenient callback mechanism
In simple terms, it helps you automatically handle everything from button reading to event triggering; you just need to attach your callbacks, and you can happily “click” away collecting various button events.
What pain points does it solve?
We’ve all encountered issues like:
- • Inaccurate button bounce detection, causing frustration for clumsy users
- • Writing logic for single click/double click/long press, only to miss some states
- • Multiple buttons in a project leading to rigid and hard-to-maintain copy-pasted code
- • Or using polling mode, fearing that the main loop will get stuck
MultiButton was created to address these problems:
| Pain Point | Traditional Approach | MultiButton Advantage |
| Button Bounce | Writing separate software delays and hardware filters | Built-in digital filtering, automatic debouncing |
| Multiple event checks with complex logic | Using if…else statements to create a tree structure | Clear state machine, various events are straightforward |
| Managing multiple buttons | Copying code repeatedly | Unlimited instance support, dynamic management via callbacks |
| Long press and repeated press detection | Manually calculating time and counts | Automatic time thresholds and repeated count tracking |
| Poor maintainability, prone to bugs | Difficult to refactor and extend | Modular design, flexible configuration, and safe parameter validation |
Check out its features (Markdown table, highlighting key points):
| Feature | Description |
| Multiple button events | Press, release, single click, double click, long press start, long press hold, repeated press |
| Hardware debouncing | Built-in digital filtering, automatic debouncing |
| State machine driven | Clear and reliable state transitions |
| Multi-button support | Supports an unlimited number of button instances |
| Callback mechanism | Flexible attach/detach registration and removal |
| Memory optimization | Compact data structure, low memory usage |
| Flexible configuration | Customizable time parameters and functional options |
| Parameter validation | Comprehensive boundary and error checks |
Let’s start with a “Hello World” level quick start:
#include "multi_button.h"
// 1. Define button instance
static Button btn1;
// 2. GPIO read function
uint8_t read_button_gpio(uint8_t button_id){
// Assuming you are using STM32 HAL
return HAL_GPIO_ReadPin(BUTTON1_GPIO_Port, BUTTON1_Pin);
}
// 3. Initialize button (active_level: 0=low level valid, 1=high level valid)
button_init(&btn1, read_button_gpio,0,1);
// 4. Register single click callback
void btn1_single_click_handler(void* btn){
printf(">> Single click event triggered!\n");
}
button_attach(&btn1, BTN_SINGLE_CLICK, btn1_single_click_handler);
// 5. Start button processing
button_start(&btn1);
// 6. Call in 5ms timer interrupt
void TIM5_IRQHandler(void){
if(/* Check if it's a 5ms interrupt */){
button_ticks();
}
}
It’s that simple; you just need to write your business logic in the callbacks, without worrying about calculating time yourself.
In-depth example: Multi-button & dynamic management
#define MAX_BTN 3
static Button btns[MAX_BTN];
void setup_buttons(void){
// Initialize 3 buttons
for(int i =0; i < MAX_BTN; i++){
button_init(&btns[i], read_button_gpio,1, i +1);
button_start(&btns[i]);
}
// Attach various events
button_attach(&btns[0], BTN_DOUBLE_CLICK, handler_double_click);
button_attach(&btns[1], BTN_LONG_PRESS_HOLD, handler_long_hold);
// Easy to remove later
// button_detach(&btns[1], BTN_LONG_PRESS_HOLD);
}
Pros and Cons Overview:
| Pros | Cons |
| Extremely simple API, clear state machine, easy to use and reliable | Depends on a 5ms timer interrupt, not suitable for ultra-low power standby scenarios |
| Built-in debouncing, long/short press, repeated press, full functionality coverage | Heavy work in callbacks can block easily |
| Supports unlimited buttons, dynamic attach/detach | C implementation, slightly weak object-oriented experience |
| Flexible configuration, all time thresholds adjustable | State machine concept may be a bit challenging for beginners |
| Modular, parameter validation, high safety | Need to implement GPIO reading interface yourself |
Conclusion: Why use MultiButton?
In short, it saves you trouble; writing button logic is no longer a hassle.
- • Want to integrate quickly? Done in 5 minutes.
- • Want to customize thresholds? Configuration file can be changed in a second.
- • Want to expand to multiple buttons? Just create a new Button, and you’re good to go.
Of course, you need to pair it with a 5ms timer, which is not suitable for ultra-low power wake-up scenarios, but for the vast majority of MCU projects, MultiButton allows you to focus on your business logic without the stress of manual coding.
Whether you are an embedded novice or a seasoned pro, give MultiButton a try; it will reliably handle all your button event management needs. Stop writing a bunch of if…else statements; with state machines, debouncing, and callbacks, it can all be done in one go, instantly freeing your “button” hands.
Project address: https://github.com/0x1abin/MultiButton