MultiButton: A Lightweight and Flexible Multi-Key State Library

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

Leave a Comment