Button

[中文]

The button component supports GPIO and ADC mode, and it allows the creation of two different kinds of the button at the same time. The following figure shows the hardware design of the button:

../_images/button_hardware.png
  • GPIO button: The advantage of the GPIO button is that each button occupies an independent IO and therefore does not affect each other, and has high stability however when the number of buttons increases, it may take too many IO resources.

  • ADC button: The advantage of using the ADC button is that one ADC channel can share multiple buttons and occupy fewer IO resources. The disadvantages include that you cannot press multiple buttons at the same time, and instability increases due to increase in the closing resistance of the button due to oxidation and other factors.

Note

  • The GPIO button needs to pay attention to the problem of pull-up and pull-down resistor inside the chip, which will be enabled by default. But there is no such resistor inside the IO that only supports input, external connection requires.

  • The voltage of the ADC button should not exceed the ADC range.

Button event

Triggering conditions for each button event are enlisted in the table below:

Event

Trigger Condition

BUTTON_PRESS_DOWN

Pressed

BUTTON_PRESS_UP

Released

BUTTON_PRESS_REPEAT

Pressed and released >= 2 times

BUTTON_PRESS_REPEAT_DONE

Repeated press completed

BUTTON_SINGLE_CLICK

Pressed and released once

BUTTON_DOUBLE_CLICK

Pressed and released twice

BUTTON_MULTIPLE_CLICK

Pressed and released N times specified, triggers when achieved

BUTTON_LONG_PRESS_START

Instant when held for a threshold duration of time

BUTTON_LONG_PRESS_HOLD

Triggered continuously during long press

BUTTON_LONG_PRESS_UP

Released after a long press

BUTTON_PRESS_REPEAT_DONE

Repeated press and release ended

BUTTON_PRESS_END

Indicate that the button has completed its current detection.

Each button supports call-back and pooling mode.

  • Call-back: Each event of a button can register a call-back function for it, and the call-back function will be called when an event is generated. This method has high efficiency and real-time performance, and no events will be lost.

  • Polling: Periodically call iot_button_get_event() in the program to query the current event of the button. This method is easy to use and is suitable for occasions with simple tasks

Note

you can also combine the above two methods.

Attention

No blocking operations such as TaskDelay are allowed in the call-back function

Button

Configuration

  • BUTTON_PERIOD_TIME_MS : scan cycle

  • BUTTON_DEBOUNCE_TICKS : debounce time

  • BUTTON_SHORT_PRESS_TIME_MS : short press down effective time

  • BUTTON_LONG_PRESS_TIME_MS : long press down effective time

  • ADC_BUTTON_MAX_CHANNEL : maximum number of channel for ADC

  • ADC_BUTTON_MAX_BUTTON_PER_CHANNEL : maximum number of ADC buttons per channel

  • ADC_BUTTON_SAMPLE_TIMES : ADC sample time

  • BUTTON_SERIAL_TIME_MS : call-back interval triggered by long press time

  • BUTTON_LONG_PRESS_TOLERANCE_MS: Used to set the tolerance time for long presses.

Demonstration

Create a button

// create gpio button
button_config_t gpio_btn_cfg = {
    .type = BUTTON_TYPE_GPIO,
    .long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS,
    .short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS,
    .gpio_button_config = {
        .gpio_num = 0,
        .active_level = 0,
    },
};
button_handle_t gpio_btn = iot_button_create(&gpio_btn_cfg);
if(NULL == gpio_btn) {
    ESP_LOGE(TAG, "Button create failed");
}

// create adc button
button_config_t adc_btn_cfg = {
    .type = BUTTON_TYPE_ADC,
    .long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS,
    .short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS,
    .adc_button_config = {
        .adc_channel = 0,
        .button_index = 0,
        .min = 100,
        .max = 400,
    },
};
button_handle_t adc_btn = iot_button_create(&adc_btn_cfg);
if(NULL == adc_btn) {
    ESP_LOGE(TAG, "Button create failed");
}

// create matrix keypad button
button_config_t matrix_button_cfg = {
    .type = BUTTON_TYPE_MATRIX,
    .long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS,
    .short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS,
    .matrix_button_config = {
        .row_gpio_num = 0,
        .col_gpio_num = 1,
    }
};
button_handle_t matrix_button = iot_button_create(&matrix_button_cfg);
if(NULL == matrix_button) {
    ESP_LOGE(TAG, "Button create failed");
}

Note

When the IDF version is greater than or equal to release/5.0, the ADC button uses ADC1. If ADC1 is used elsewhere in the project, please provide the adc_handle and adc_channel to configure the ADC button.

Register callback function

The Button component supports registering callback functions for multiple events, with each event capable of having its own callback function. When an event occurs, the callback function will be invoked.

In this context:

  • The BUTTON_LONG_PRESS_START and BUTTON_LONG_PRESS_UP enumerations support setting specific long press times.

  • The BUTTON_MULTIPLE_CLICK enumeration supports setting the number of consecutive button presses.

  • Here’s a simple example:

    static void button_single_click_cb(void *arg,void *usr_data)
    {
        ESP_LOGI(TAG, "BUTTON_SINGLE_CLICK");
    }
    
    iot_button_register_cb(gpio_btn, BUTTON_SINGLE_CLICK, button_single_click_cb,NULL);
    
  • And here’s an example involving multiple callback functions:

    static void button_long_press_1_cb(void *arg,void *usr_data)
    {
        ESP_LOGI(TAG, "BUTTON_LONG_PRESS_START_1");
    }
    
    static void button_long_press_2_cb(void *arg,void *usr_data)
    {
        ESP_LOGI(TAG, "BUTTON_LONG_PRESS_START_2");
    }
    button_event_config_t cfg = {
        .event = BUTTON_LONG_PRESS_START,
        .event_data.long_press.press_time = 2000,
    };
    
    iot_button_register_event_cb(gpio_btn, cfg, button_long_press_1_cb, NULL);
    
    cfg.event_data.long_press.press_time = 5000;
    iot_button_register_event_cb(gpio_btn, cfg, button_long_press_2_cb, NULL);
    

Dynamically Modifying Default Button Values

iot_button_set_param(btn, BUTTON_LONG_PRESS_TIME_MS, 5000);

Find an event

button_event_t event;
event = iot_button_get_event(button_handle);

Low power

In light_sleep mode, the esp_timer triggers periodically, resulting in sustained high overall CPU power consumption. To address this issue, the button component offers a low-power mode.

Configuration Required:

  • Enable the CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE option to include low-power-related code in the component.

  • Ensure all created buttons type are GPIO type and have enable_power_save activated. The presence of other buttons may render the low-power mode ineffective.

Note

This feature ensures that the Button component only wakes up the CPU when in use, but does not guarantee the CPU will always enter low-power mode.

Power Consumption Comparison:

  • Without enabling low-power mode, pressing the button once:

    Without enabling low-power mode, a single press
  • With low-power mode enabled, pressing the button once:

    With low-power mode enabled, a single press

Because GPIO wakes up the CPU, supporting only level triggering, the CPU is awakened only when the button is at its operating level. Therefore, in low-power mode, the average current during a single press is higher than when low-power mode is not enabled, depending on the duration of the button press. However, over larger operational periods, it saves more power than when low-power mode is not enabled.

  • Without enabling low-power mode, pressing the button three times within 4 seconds:

    ../_images/button_three_press_4s.png
  • With low-power mode enabled, pressing the button three times within 4 seconds:

    ../_images/button_power_save_three_press_4s.png

As shown, low-power mode results in more power savings.

button_config_t btn_cfg = {
    .type = BUTTON_TYPE_GPIO,
    .gpio_button_config = {
        .gpio_num = button_num,
        .active_level = BUTTON_ACTIVE_LEVEL,
        .enable_power_save = true,
    },
};
button_handle_t btn = iot_button_create(&btn_cfg);

When to Enter Light Sleep

  • Using Auto Light Sleep: The device will enter Light Sleep automatically after the button closes the esp_timer.

  • User-Controlled Light Sleep: The device can enter Light Sleep when enter_power_save_cb is called.

void btn_enter_power_save(void *usr_data)
{
    ESP_LOGI(TAG, "Can enter power save now");
}

button_power_save_config_t config = {
    .enter_power_save_cb = btn_enter_power_save,
};

iot_button_register_power_save_cb(&config);

How to Use Buttons Normally After Enabling the CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP Option?

  • When this macro is enabled, the GPIO module will be powered down. To use the button functionality, you must use RTC/LP GPIO and change the wake-up source to EXT 1.

GPIO Type

CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP Enabled?

Wake-Up Source

Digital Pin

N

GPIO Level Trigger

Digital Pin

Y

None

RTC/LP Pin

N

GPIO Level Trigger / EXT 1

RTC/LP Pin

Y

EXT 1

Note

The LP GPIOs of ESP32-C5 and ESP32-C6 support both GPIO level wake-up and EXT 1 wake-up, and you also need to enable gpio_hold_en.

Stop and resume

The component supports being turned on and off at any given moment.

// stop button
iot_button_stop();
// resume button
iot_button_resume();

API Reference

Header File

Functions

button_handle_t iot_button_create(const button_config_t *config)

Create a button.

Parameters

config – pointer of button configuration, must corresponding the button type

Returns

A handle to the created button, or NULL in case of error.

esp_err_t iot_button_delete(button_handle_t btn_handle)

Delete a button.

Parameters

btn_handle – A button handle to delete

Returns

  • ESP_OK Success

  • ESP_FAIL Failure

esp_err_t iot_button_register_cb(button_handle_t btn_handle, button_event_t event, button_cb_t cb, void *usr_data)

Register the button event callback function.

Parameters
  • btn_handle – A button handle to register

  • event – Button event

  • cb – Callback function.

  • usr_data – user data

Returns

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG Arguments is invalid.

  • ESP_ERR_INVALID_STATE The Callback is already registered. No free Space for another Callback.

  • ESP_ERR_NO_MEM No more memory allocation for the event

esp_err_t iot_button_register_event_cb(button_handle_t btn_handle, button_event_config_t event_cfg, button_cb_t cb, void *usr_data)

Register the button event callback function.

Parameters
  • btn_handle – A button handle to register

  • event_cfg – Button event configuration

  • cb – Callback function.

  • usr_data – user data

Returns

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG Arguments is invalid.

  • ESP_ERR_INVALID_STATE The Callback is already registered. No free Space for another Callback.

  • ESP_ERR_NO_MEM No more memory allocation for the event

esp_err_t iot_button_unregister_event(button_handle_t btn_handle, button_event_config_t event_cfg, button_cb_t cb)

Unregister the button event callback function. In case event_data is also passed it will unregister function for that particular event_data only.

Parameters
  • btn_handle – A button handle to unregister

  • event_cfg – Button event

  • cb – callback to unregister

Returns

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG Arguments is invalid.

  • ESP_ERR_INVALID_STATE The Callback was never registered with the event

esp_err_t iot_button_unregister_cb(button_handle_t btn_handle, button_event_t event)

Unregister all the callbacks associated with the event.

Parameters
  • btn_handle – A button handle to unregister

  • event – Button event

Returns

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG Arguments is invalid.

  • ESP_ERR_INVALID_STATE No callbacks registered for the event

size_t iot_button_count_cb(button_handle_t btn_handle)

counts total callbacks registered

Parameters

btn_handle – A button handle to the button

Returns

  • 0 if no callbacks registered, or 1 .. (BUTTON_EVENT_MAX-1) for the number of Registered Buttons.

  • ESP_ERR_INVALID_ARG if btn_handle is invalid

size_t iot_button_count_event(button_handle_t btn_handle, button_event_t event)

how many callbacks are registered for the event

Parameters
  • btn_handle – A button handle to the button

  • event – Button event

Returns

  • 0 if no callbacks registered, or 1 .. (BUTTON_EVENT_MAX-1) for the number of Registered Buttons.

  • ESP_ERR_INVALID_ARG if btn_handle is invalid

button_event_t iot_button_get_event(button_handle_t btn_handle)

Get button event.

Parameters

btn_handle – Button handle

Returns

Current button event. See button_event_t

const char *iot_button_get_event_str(button_event_t event)

Get the string representation of a button event.

This function returns the corresponding string for a given button event. If the event value is outside the valid range, the function returns error string “event value is invalid”.

Parameters

event[in] The button event to be converted to a string.

Returns

  • Pointer to the event string if the event is valid.

  • ”invalid event” if the event value is invalid.

esp_err_t iot_button_print_event(button_handle_t btn_handle)

Log the current button event as a string.

This function prints the string representation of the current event associated with the button.

Parameters

btn_handle[in] Handle to the button object.

Returns

  • ESP_OK: Successfully logged the event string.

  • ESP_FAIL: Invalid button handle.

uint8_t iot_button_get_repeat(button_handle_t btn_handle)

Get button repeat times.

Parameters

btn_handle – Button handle

Returns

button pressed times. For example, double-click return 2, triple-click return 3, etc.

uint32_t iot_button_get_ticks_time(button_handle_t btn_handle)

Get button ticks time.

Parameters

btn_handle – Button handle

Returns

Actual time from press down to up (ms).

uint16_t iot_button_get_long_press_hold_cnt(button_handle_t btn_handle)

Get button long press hold count.

Parameters

btn_handle – Button handle

Returns

Count of trigger cb(BUTTON_LONG_PRESS_HOLD)

esp_err_t iot_button_set_param(button_handle_t btn_handle, button_param_t param, void *value)

Dynamically change the parameters of the iot button.

Parameters
  • btn_handle – Button handle

  • param – Button parameter

  • value – new value

Returns

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG Arguments is invalid.

uint8_t iot_button_get_key_level(button_handle_t btn_handle)

Get button key level.

Parameters

btn_handle – Button handle

Returns

  • 1 if key is pressed

  • 0 if key is released or invalid button handle

esp_err_t iot_button_resume(void)

resume button timer, if button timer is stopped. Make sure iot_button_create() is called before calling this API.

Returns

  • ESP_OK on success

  • ESP_ERR_INVALID_STATE timer state is invalid.

esp_err_t iot_button_stop(void)

stop button timer, if button timer is running. Make sure iot_button_create() is called before calling this API.

Returns

  • ESP_OK on success

  • ESP_ERR_INVALID_STATE timer state is invalid

Unions

union button_event_data_t
#include <iot_button.h>

Button events data.

Public Members

struct button_event_data_t::long_press_t long_press

long press struct, for event BUTTON_LONG_PRESS_START and BUTTON_LONG_PRESS_UP

struct button_event_data_t::multiple_clicks_t multiple_clicks

multiple clicks struct, for event BUTTON_MULTIPLE_CLICK

struct long_press_t
#include <iot_button.h>

Long press time event data.

Public Members

uint16_t press_time

press time(ms) for the corresponding callback to trigger

struct multiple_clicks_t
#include <iot_button.h>

Multiple clicks event data.

Public Members

uint16_t clicks

number of clicks, to trigger the callback

Structures

struct button_event_config_t

Button events configuration.

Public Members

button_event_t event

button event type

button_event_data_t event_data

event data corresponding to the event

struct button_custom_config_t

custom button configuration

Public Members

uint8_t active_level

active level when press down

esp_err_t (*button_custom_init)(void *param)

user defined button init

uint8_t (*button_custom_get_key_value)(void *param)

user defined button get key value

esp_err_t (*button_custom_deinit)(void *param)

user defined button deinit

void *priv

private data used for custom button, MUST be allocated dynamically and will be auto freed in iot_button_delete

struct button_config_t

Button configuration.

Public Members

button_type_t type

button type, The corresponding button configuration must be filled

uint16_t long_press_time

Trigger time(ms) for long press, if 0 default to BUTTON_LONG_PRESS_TIME_MS

uint16_t short_press_time

Trigger time(ms) for short press, if 0 default to BUTTON_SHORT_PRESS_TIME_MS

button_gpio_config_t gpio_button_config

gpio button configuration

button_matrix_config_t matrix_button_config

matrix key button configuration

button_custom_config_t custom_button_config

custom button configuration

union button_config_t::[anonymous] [anonymous]

button configuration

Type Definitions

typedef void (*button_cb_t)(void *button_handle, void *usr_data)
typedef void *button_handle_t

Enumerations

enum button_event_t

Button events.

Values:

enumerator BUTTON_PRESS_DOWN
enumerator BUTTON_PRESS_UP
enumerator BUTTON_PRESS_REPEAT
enumerator BUTTON_PRESS_REPEAT_DONE
enumerator BUTTON_SINGLE_CLICK
enumerator BUTTON_DOUBLE_CLICK
enumerator BUTTON_MULTIPLE_CLICK
enumerator BUTTON_LONG_PRESS_START
enumerator BUTTON_LONG_PRESS_HOLD
enumerator BUTTON_LONG_PRESS_UP
enumerator BUTTON_PRESS_END
enumerator BUTTON_EVENT_MAX
enumerator BUTTON_NONE_PRESS
enum button_type_t

Supported button type.

Values:

enumerator BUTTON_TYPE_GPIO
enumerator BUTTON_TYPE_ADC
enumerator BUTTON_TYPE_MATRIX
enumerator BUTTON_TYPE_CUSTOM
enum button_param_t

Button parameter.

Values:

enumerator BUTTON_LONG_PRESS_TIME_MS
enumerator BUTTON_SHORT_PRESS_TIME_MS
enumerator BUTTON_PARAM_MAX