Button

Handles Hardware Buttons

Configuration

#define MAX_CONFIDENCE (100)
#define ON_CONFIDENCE (70)
#define OFF_CONFIDENCE (30)
#define MIN_CONFIDENCE (0)
#define CONFIDENCE_STEP (20)
  • MAX_CONFIDENCE the maximum confidence value
  • ON_CONFIDENCE the confidence value at which the button is considered to be pressed
  • OFF_CONFIDENCE the confidence value at which the button is considered to be released
  • MIN_CONFIDENCE the minimum confidence value
  • CONFIDENCE_STEP the amount by which the confidence value is incremented or decremented each tick

Public functions

button_init()

Initializes the button module

void button_init()
{
}

currently does nothing as the module is stateless, but may be used in the future

button_setup()

Registers a button handler, which will be called when the button state changes sets up the GPIO pin as an input with a pull-down resistor, and stores the handler information

void button_setup(gpio_num_t pin, gpio_int_type_t edge, gpio_isr_t callback, void* args)
{
    gpio_config_t io_config = {
        .intr_type = GPIO_INTR_DISABLE,
        .mode = GPIO_MODE_INPUT,
        .pin_bit_mask = (1ULL << pin),
        .pull_down_en = GPIO_PULLDOWN_ENABLE,
        .pull_up_en = GPIO_PULLUP_DISABLE,
    };
    ESP_ERROR_CHECK(gpio_config(&io_config));
    handlers[pin].enabled = true;
    handlers[pin].callback = callback;
    handlers[pin].edge = edge;
    handlers[pin].args = args;
    bool pinstate = getpin(pin);
    handlers[pin].confidence = 255 * pinstate;
    handlers[pin].current_state = pinstate;
}

button_tick()

Called every tick to update the button state machine

button state is determined by a confidence value, which is incremented or decremented by a fixed amount each tick based on the button's digital state

when the confidence value crosses a threshold, the button state is updated and the callback is called if the edge condition is met

there is a gap between the on and off thresholds to provide hysteresis and prevent chattering

void button_tick()
{
    for (size_t n = 0; n < GPIO_NUM_MAX; n++)
    {
        handler_t* handle = &handlers[n];
        if (handle->enabled && handle->callback != NULL)
        {
            // note: buttons are active low, hence using -CONFIDENCE_STEP when get_pin is true
            if (getpin(n))
            {
                handle->confidence = add_clamped(handle->confidence, -CONFIDENCE_STEP);
            }
            else
            {
                handle->confidence = add_clamped(handle->confidence, CONFIDENCE_STEP);
            }

            if (handle->confidence >= ON_CONFIDENCE && !handle->current_state)
            {
                handle->current_state = true;
                if (handle->edge == GPIO_INTR_POSEDGE || handle->edge == GPIO_INTR_ANYEDGE)
                {
                    handle->callback(handle->args);
                }
            }

            if (handle->confidence <= OFF_CONFIDENCE && handle->current_state)
            {
                handle->current_state = false;
                if (handle->edge == GPIO_INTR_NEGEDGE || handle->edge == GPIO_INTR_ANYEDGE)
                {
                    handle->callback(handle->args);
                }
            }
        }
    }
}

Internal types

handler_t

used to store information about a registerd button handler

typedef struct
{
    bool enabled;
    gpio_isr_t callback;
    gpio_int_type_t edge;
    void* args;
    int confidence;
    bool current_state;
} handler_t;

Internal variables

handlers

provides space to store a handler for each GPIO pin

static handler_t handlers[GPIO_NUM_MAX] = {0};

Internal functions

get_pin

helper to get the digital state of an arbitrary pin

inline bool getpin(gpio_num_t pin)
{
    uint64_t state = (uint64_t)gpio_input_get() + ((uint64_t)gpio_input_get_high() << 32);
    uint64_t bitmask = 1 << pin;
    return state & bitmask;
}

add_clamped

helper to add two numbers, clamping the result to between the minimum and maximum confidence values

inline int add_clamped(int base, int to_add)
{
    base = base + to_add;
    if (base < MIN_CONFIDENCE)
        base = MIN_CONFIDENCE;
    if (base > MAX_CONFIDENCE)
        base = MAX_CONFIDENCE;
    return base;
}