Skip to content

Repository files navigation

Misclick

A lightweight C library for microcontroller button input debouncing and advanced click pattern detection.

Features

  • Hardware debouncing - Filters out electrical noise from button presses
  • Multiple click patterns - Detects single, double, triple clicks
  • Long press support - Recognizes long presses and long multi-clicks
  • Configurable timing - Customizable debounce, press, and cooldown intervals
  • Callback-based - Non-blocking event-driven architecture
  • Memory efficient - Low memory footprint
  • Timer agnostic - Works with any timer implementation

Supported Click Patterns

  • Single click
  • Double click
  • Triple click
  • Long press
  • Long double click
  • Long triple click

Usage

Integration with CMake

Add this to your CMakeLists.txt to automatically download and build the library:

include(FetchContent)
FetchContent_Declare(
misclick
GIT_REPOSITORY https://github.com/cyborgize/misclick.git
GIT_TAG main # or a specific version tag like v1.0.0
)
FetchContent_MakeAvailable(misclick)
# Link to your targettarget_link_libraries(your_targetPRIVATEmisclick)

Implementation Example

This example shows integration with Zephyr RTOS, but the same patterns apply to other platforms.

1. Timer Implementation

#include<zephyr/kernel.h>#include<zephyr/drivers/gpio.h>#include"misclick/misclick.h"// Zephyr timers for the misclick librarystaticstructk_timermisclick_state_timer;
staticstructk_timermisclick_click_timer;
// Timer callback handlersstaticvoidmisclick_state_timer_handler(structk_timer*timer) {
misclick_handle_state_timeout(k_uptime_get() *1000); // Convert ms to us
}
staticvoidmisclick_click_timer_handler(structk_timer*timer) {
misclick_handle_click_timeout(k_uptime_get() *1000); // Convert ms to us
}
// Timer interface functions for misclick librarystaticvoidmisclick_stop_timer(void*handle) {
structk_timer*timer= (structk_timer*)handle;
k_timer_stop(timer);
}
staticvoidmisclick_start_timer(void*handle, int64_ttimeout_us) {
structk_timer*timer= (structk_timer*)handle;
k_timer_start(timer, K_USEC(timeout_us), K_NO_WAIT);
}

2. Button Event Callbacks

// Handle different click patternsstaticvoidbutton_click_callback(void*callback_arg, intbutton_id,
enummisclick_click_tclick, int64_ttimestamp) {
switch (click) {
caseMISCLICK_CLICK_SINGLE:
printk("Single click detected\\n");
// Trigger action for single clickbreak;
caseMISCLICK_CLICK_DOUBLE:
printk("Double click detected\\n");
// Trigger action for double clickbreak;
caseMISCLICK_CLICK_TRIPLE:
printk("Triple click detected\\n");
// Trigger action for triple clickbreak;
caseMISCLICK_CLICK_LONG:
printk("Long press detected\\n");
// Trigger action for long pressbreak;
caseMISCLICK_CLICK_LONG_DOUBLE:
printk("Long double click detected\\n");
break;
caseMISCLICK_CLICK_LONG_TRIPLE:
printk("Long triple click detected\\n");
break;
caseMISCLICK_CLICK_CANCELLED:
printk("Click sequence cancelled\\n");
break;
}
printk("Button %d click: %d at %lld\\n", button_id, click, timestamp);
}
// Optional: Handle individual press/release eventsstaticvoidbutton_press_callback(void*callback_arg, intbutton_id,
enummisclick_press_tpress, int64_ttimestamp) {
printk("Button %d press: %d at %lld\\n", button_id, press, timestamp);
}

3. Library Initialization

staticstructmisclick_t*main_button=NULL;
staticvoidinit_buttons(void) {
// Initialize timersk_timer_init(&misclick_state_timer, misclick_state_timer_handler, NULL);
k_timer_init(&misclick_click_timer, misclick_click_timer_handler, NULL);
// Configure the misclick librarystructmisclick_config_tconfig= {
.state_timer_handle=&misclick_state_timer,
.click_timer_handle=&misclick_click_timer,
.stop_timer=misclick_stop_timer,
.start_timer=misclick_start_timer,
.debounce_time_us=DEFAULT_MISCLICK_DEBOUNCE_TIME_US,
.long_press_time_us=DEFAULT_MISCLICK_LONG_PRESS_TIME_US,
.next_press_time_us=DEFAULT_MISCLICK_NEXT_PRESS_TIME_US,
.final_press_time_us=DEFAULT_MISCLICK_FINAL_PRESS_TIME_US,
.cooldown_time_us=DEFAULT_MISCLICK_COOLDOWN_TIME_US,
};
misclick_init(&config);
// Add main buttonstructmisclick_params_tbutton_params= {
.button_id=0,
.callback_arg=NULL,
.click_callback=button_click_callback,
.press_callback=button_press_callback,
};
main_button=misclick_add(&button_params);
}

4. GPIO Interrupt Handler

// GPIO interrupt callback (called from ISR context)staticvoidbutton_gpio_callback(conststructdevice*dev,
structgpio_callback*cb, uint32_tpins) {
if (!main_button) {
return;
}
// Read current button state (inverted since button is active low)intbutton_state= !gpio_pin_get_dt(&button_gpio);
// Send button event to misclick library// Convert milliseconds to microseconds for timestampmisclick_handle_input_event(main_button, button_state, k_uptime_get() *1000);
}

5. Main Application Setup

intmain(void) {
// Initialize GPIO for button inputif (!device_is_ready(button_gpio.port)) {
printk("Error: button device not ready\\n");
return-1;
}
// Configure button pin as input with interruptgpio_pin_configure_dt(&button_gpio, GPIO_INPUT);
gpio_pin_interrupt_configure_dt(&button_gpio, GPIO_INT_EDGE_BOTH);
// Set up GPIO callbackgpio_init_callback(&button_cb_data, button_gpio_callback, BIT(button_gpio.pin));
gpio_add_callback(button_gpio.port, &button_cb_data);
// Initialize misclick libraryinit_buttons();
// Application main loopwhile (1) {
// Your application logic herek_sleep(K_MSEC(100));
}
return0;
}

Configuration

Default Timing Values

#defineDEFAULT_MISCLICK_DEBOUNCE_TIME_US 40000 // 40ms debounce
#defineDEFAULT_MISCLICK_LONG_PRESS_TIME_US 600000 // 600ms for long press
#defineDEFAULT_MISCLICK_NEXT_PRESS_TIME_US 500000 // 500ms between clicks
#defineDEFAULT_MISCLICK_FINAL_PRESS_TIME_US 500000 // 500ms final click timeout
#defineDEFAULT_MISCLICK_COOLDOWN_TIME_US 1000000 // 1s cooldown after complex patterns

Timer Integration

The library requires two timers with microsecond precision:

  • State timer: Used for input debouncing
  • Click timer: Used for click pattern detection timeouts

Implement the timer interface functions according to your platform:

voidmy_start_timer(void*handle, int64_ttimeout_us) {
// Cast handle to your timer instance and configure for one-shot operation// Set the timeout period and start the timer// Timer should fire once after timeout_us microseconds
}
voidmy_stop_timer(void*handle) {
// Stop and reset the specified timer instance
}

Requirements:

  • Create two separate timer instances (for state and click timers)
  • Configure timers for one-shot mode (fire once, then stop)
  • Implement microsecond-precision timing
  • Call the library timeout handlers from your timer interrupt callbacks

Memory Usage

  • Per button: ~40 bytes
  • Global state: ~100 bytes
  • No dynamic allocation after initialization

Thread Safety

This library is not thread-safe. If using in a multi-threaded environment, provide your own synchronization.

License

Licensed under the Apache License, Version 2.0. See LICENSE for the full license text.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages