OneButton Library

repository·master·Indexed 22 days ago

https://github.com/mathertel/onebutton

An Arduino library that enables a single pushbutton to handle multiple inputs, including single clicks, double clicks, and long presses. It features a Finite State Machine updated via a tick() method and supports parameterized callbacks for context handling. For memory-constrained devices like the ATtiny84, a lightweight OneButtonTiny class is provided to save program space while maintaining core event detection.

Tokens
4.9K
Snippets
19
Records
23
Agent score
73%

What's inside OneButton

  1. Use OneButtonTiny for memory-constrained devices

    master

    For processors with very limited memory and low CPU frequencies (like the ATtiny84), use the OneButtonTiny class. It provides a subset of features to save up to 1k of binary program space.

    Supported events in OneButtonTiny:

    • Click event
    • DoubleClick event
    • LongPressStart event
    • Callbacks without parameters
  2. Handle lambdas with captured context

    master

    Standard C++ function pointers do not support lambdas with captured variables. To use a lambda that captures context (like this), use the parameterized callback pattern. Pass the context pointer to the library, which will then pass it back to your lambda.

    // Using 'this' as context
    okBtn.attachClick([](void *ctx){ 
      Serial.println(*((BtnHandler*)ctx)->state); 
    }, this);
  3. Integrate OneButton into the Arduino main loop

    master

    The library relies on a Finite State Machine (FSM) that must be updated regularly. You must call the tick() method in your loop() function to process button states and trigger events. To ensure responsive detection, avoid using delay() in the main loop, as it prevents tick() from running frequently enough.

    void loop() {
      btn.tick();  // Must be called regularly
      // Other non-blocking code...
    }
  4. Initialize OneButton using the Deferred Initialization Pattern

    master

    To ensure proper hardware initialization and maximum compatibility, avoid initializing the button during instance creation. Instead, declare the OneButton object globally and call .setup() within the Arduino setup() function.

    Parameters for setup(pin, mode, activeLow):

    • pin: The digital input pin number.
    • mode: The pin mode (e.g., INPUT_PULLUP).
    • activeLow: A boolean indicating if the button is active low (true) or active high (false).
    // Declaration
    OneButton btn;
    
    // In setup()
    btn.setup(BUTTON_PIN, INPUT_PULLUP, true);  // pin, mode, activeLow
  5. Initialize a OneButton instance

    master

    Each physical button requires its own OneButton instance. There are two ways to initialize the hardware configuration:

    To avoid issues with hardware initialization order on certain boards, declare a global instance without parameters and call setup() inside your main setup() function. This allows you to pass the pinMode directly.

    2. Instance Creation (Old Way)

    You can pass configuration directly to the constructor at compile time. This is suitable for most boards but may fail on some if the hardware isn't ready during global initialization.

    Note: Each instance requires a unique physical button.

    // Recommended: Deferred initialization
    OneButton btn;
    
    void setup() {
      // Parameters: pin, pinMode, activeLow
      btn.setup(BUTTON_PIN, INPUT_PULLUP, true);
    }
    
    // Alternative: Initialization on creation
    OneButton btn = OneButton(BUTTON_PIN, true, true);
  6. Configure OneButton timing and debounce settings

    master

    You can fine-tune the button detection behavior using the following configuration methods. Note that timing priorities should follow the order: debounce < click < press < longPress.

    MethodDescriptionDefault
    setDebounceMs(ms)Debouncing delay20ms
    setClickMs(ms)Time to recognize a single click400ms
    setPressMs(ms)Time to trigger a press event800ms
    setLongPressIntervalMs(ms)Interval for repeated long-press callbacks0
    setIdleMs(ms)Auto-idle timeout (optional)N/A
    // Configuration in setup()
    btn.setDebounceMs(50);      // Debouncing delay
    btn.setClickMs(400);        // Time to recognize single click
    btn.setPressMs(800);        // Time to trigger press event
    btn.setLongPressIntervalMs(100);  // Interval for repeated long-press callbacks
    btn.setIdleMs(3000);        // Auto-idle timeout
  7. Use OneButtonTiny for memory-constrained devices

    master

    The OneButtonTiny class is a lightweight version of the standard OneButton library designed for environments with very limited memory. It provides the same core functionality: detecting single clicks, double clicks, multi-clicks, and long presses on a single button.

    Initialization

    Use the constructor to define the hardware pin and electrical configuration:

    • pin: The digital pin number.
    • activeLow: Set to true if the button connects the pin to GND when pressed (default: true).
    • pullupActive: Set to true to enable the internal pull-up resistor (default: true).

    Event Handling

    You can attach callback functions to specific button events using attachClick(), attachDoubleClick(), attachMultiClick(), and attachLongPressStart().

    #include <OneButtonTiny.h>
    
    // Callback function
    void myClickCallback() {
      Serial.println("Button clicked!");
    }
    
    // Initialize on pin 2, active low, with internal pullup
    OneButtonTiny button(2, true, true);
    
    void setup() {
      button.attachClick(myClickCallback);
    }
    
    void loop() {
      button.tick();
    }
  8. Initialize OneButton

    master

    You can initialize a OneButton instance in two ways:

    1. Constructor Initialization: Pass the pin and configuration directly to the constructor.
    2. Deferred Initialization: Create an empty instance and call setup() later. This is recommended for better compatibility with different hardware setups.

    Constructor Parameters:

    • pin: The digital pin number.
    • activeLow: true if the button connects to GND when pressed (default), false if it connects to VCC.
    • pullupActive: true to enable the internal Arduino pullup resistor (default).

    setup() Parameters:

    • pin: The digital pin number.
    • mode: The Arduino pinMode (e.g., INPUT, INPUT_PULLUP). Defaults to INPUT_PULLUP.
    • activeLow: true if the button is active LOW (default).
    // Option 1: Constructor
    OneButton button(2, true, true);
    
    // Option 2: Deferred setup (Recommended)
    OneButton button;
    void setup() {
      button.setup(2, INPUT_PULLUP, true);
    }
  9. Implement multiple buttons with a shared handler

    master

    To manage multiple buttons efficiently, you can use an array of OneButton instances and pass a unique identifier (like the index) to a parameterized callback using (void*)index.

    #define NUM_BUTTONS 3
    OneButton buttons[NUM_BUTTONS];
    int btn_pins[NUM_BUTTONS] = {2, 3, 4};
    
    void setup() {
      for (int i = 0; i < NUM_BUTTONS; i++) {
        buttons[i].setup(btn_pins[i], INPUT_PULLUP, true);
        buttons[i].attachClick(handleButtonClick, (void*)i);
      }
    }
    
    void loop() {
      for (int i = 0; i < NUM_BUTTONS; i++) {
        buttons[i].tick();
      }
    }
    
    void handleButtonClick(void *param) {
      int buttonId = (int)param;
      // Handle click for specific buttonId
    }
  10. Attach state events to a button

    master

    You can handle button events by attaching static functions or lambdas (without captured variables) to the button instance.

    For attachMultiClick, you can pass a pointer to the OneButton instance itself as a parameter to the handler function to access button metadata like the pin number or debounced value.

    // Static function handler
    static void handleClick() {
      Serial.println("Clicked!");
    }
    
    // Attach static function
    btn.attachClick(handleClick);
    
    // Attach lambda (no capture)
    btn.attachDoubleClick([]() {
      Serial.println("Double Pressed!");
    });
    
    // MultiClick with self-pointer
    static void handleMultiClick(OneButton *oneButton) {
      Serial.println(oneButton->pin());
    }
    btn.attachMultiClick(handleMultiClick, &btn);