add some code
This commit is contained in:
1
managed_components/espressif__button/.component_hash
Normal file
1
managed_components/espressif__button/.component_hash
Normal file
@@ -0,0 +1 @@
|
||||
f53face2ab21fa0ffaf4cf0f6e513d393f56df6586bb2ad1146120f03f19ee05
|
||||
201
managed_components/espressif__button/CHANGELOG.md
Normal file
201
managed_components/espressif__button/CHANGELOG.md
Normal file
@@ -0,0 +1,201 @@
|
||||
# ChangeLog
|
||||
|
||||
## v4.1.3 - 2025-04-11
|
||||
|
||||
### Fix:
|
||||
|
||||
* Added initialization for gpio_config. [!485](https://github.com/espressif/esp-iot-solution/pull/485)
|
||||
|
||||
## v4.1.2 - 2025-03-24
|
||||
|
||||
### Fix:
|
||||
|
||||
* fix incorrect long press start and release check.
|
||||
|
||||
## v4.1.1 - 2025-03-13
|
||||
|
||||
### Improve:
|
||||
|
||||
* include stdbool.h before using bool
|
||||
|
||||
## v4.1.0 - 2025-02-28
|
||||
|
||||
### Improve:
|
||||
|
||||
* Update the version of dependent cmake_utilities to *
|
||||
|
||||
## v4.0.0 - 2025-1-9
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Use the factory pattern to reduce the build size.
|
||||
* Change the state machine to use enumerated values.
|
||||
|
||||
### Break change:
|
||||
|
||||
* Standardize the return value of the iot_button interface to esp_err_t.
|
||||
* Remove support for the old ADC driver.
|
||||
* Modify the callback registration interface to:
|
||||
```c
|
||||
esp_err_t iot_button_register_cb(button_handle_t btn_handle, button_event_t event, button_event_args_t *event_args, button_cb_t cb, void *usr_data);
|
||||
```
|
||||
* Modify the callback unregistration interface to:
|
||||
```c
|
||||
esp_err_t iot_button_unregister_cb(button_handle_t btn_handle, button_event_t event, button_event_args_t *event_args);
|
||||
```
|
||||
|
||||
## v3.5.0 - 2024-12-27
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Add config to disable gpio button internal pull resistor.
|
||||
|
||||
## v3.4.1 - 2024-12-6
|
||||
|
||||
### Fix:
|
||||
|
||||
* Fix the issue where `BUTTON_LONG_PRESS_START` is not triggered when the polling interval exceeds 20ms.
|
||||
* Remove the `BUTTON_LONG_PRESS_TOLERANCE_MS` configuration option.
|
||||
|
||||
## v3.4.0 - 2024-10-22
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Supports a maximum button polling interval of 500ms.
|
||||
* Fixed a potential counter overflow issue.
|
||||
|
||||
### Break change:
|
||||
|
||||
* The return value of `iot_button_get_ticks_time` has been changed from `uint16_t` to `uint32_t`.
|
||||
|
||||
## v3.3.2 - 2024-8-28
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Support macro CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP in power save mode.
|
||||
* Supports retrieving and printing the string corresponding to a button event.
|
||||
* Fixed the bug where the event was not assigned to `BUTTON_LONG_PRESS_START` before the `BUTTON_LONG_PRESS_START` event occurred.
|
||||
|
||||
## v3.3.1 - 2024-8-8
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Add Button Event **BUTTON_PRESS_END**.
|
||||
|
||||
## v3.3.0 - 2024-8-7
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Add Callback **button_power_save_cb_t** to support enter power save manually.
|
||||
* Increase the maximum polling interval supported by the button from 20ms to 50ms.
|
||||
|
||||
## v3.2.3 - 2024-7-2
|
||||
|
||||
* Fixed the issue where the GPIO button in low-power mode continuously woke up the CPU after being pressed, causing abnormal power consumption.
|
||||
|
||||
## v3.2.2 - 2024-6-17
|
||||
|
||||
* Fix the compilation error for chips that do not support ADC.
|
||||
|
||||
## v3.2.1 - 2024-6-17
|
||||
|
||||
### bugfix
|
||||
|
||||
- Fixed ignored ADC button tied to GND. thanks `demianzenkov` for the fix.
|
||||
|
||||
## v3.2.0 - 2023-11-13
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* The power consumption of GPIO buttons is lower during light sleep mode.
|
||||
|
||||
## v3.1.3 - 2023-11-13
|
||||
|
||||
* Resolved issue 'ADC_ATTEN_DB_11 is deprecated'.
|
||||
|
||||
## v3.1.2 - 2023-10-24
|
||||
|
||||
### bugfix
|
||||
|
||||
* Fixed a bug where iot_button_delete feature crashes for custom button
|
||||
|
||||
## v3.1.1 - 2023-10-18
|
||||
|
||||
### bugfix
|
||||
|
||||
* Fixed a bug where multiple callbacks feature crashes for BUTTON_MULTIPLE_CLICK
|
||||
|
||||
## v3.1.0 - 2023-10-9
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Support matrix keypad
|
||||
|
||||
## v3.0.1 - 2023-9-1
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Resolves bug for iot_button_unregister_event function returned error when reallocating with 0 byte.
|
||||
* Update Test cases to test iot_button_unregister_event_cb
|
||||
* Add api iot_button_stop & iot_button_resume for power save.
|
||||
|
||||
## v3.0.0 - 2023-8-15
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Add support to register multiple callbacks for a button_event
|
||||
|
||||
* Update iot_button_unregister_cb, to unregister all the callbacks for that event
|
||||
* Add iot_button_unregister_event to unregister specific callbacks of that event
|
||||
* Add iot_button_count_event to return number of callbacks registered for the event.
|
||||
* Update iot_button_count_cb, to return sum of number of registered callbacks.
|
||||
|
||||
* Add support for Long press on specific time
|
||||
|
||||
* Add iot_button_register_event, which takes specific event_config_t data as input.
|
||||
* Add BUTTON_LONG_PRESS_UP to trigger callback at the latest time of button release
|
||||
* Update BUTTON_LONG_PRESS_START to trigger callback as the time passes for long_press.
|
||||
|
||||
* Add support to trigger callback for specified number of clicks.
|
||||
|
||||
## v2.5.6 - 2023-8-22
|
||||
|
||||
### bugfix
|
||||
|
||||
* Fixed a bug where the Serial trigger interval in button_long_press_hold event fires at an incorrect time
|
||||
|
||||
## v2.5.5 - 2023-8-3
|
||||
|
||||
* Add modify api which can change long_press_time and short_press_time
|
||||
|
||||
## v2.5.4 - 2023-7-27
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Add test apps and ci auto test
|
||||
|
||||
## v2.5.3 - 2023-7-26
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* `repeat` defined in struct button_dev_t is reset to 0 after event `BUTTON_PRESS_REPEAT_DONE`
|
||||
|
||||
## v2.5.2 - 2023-7-18
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Set "event" member to BUTTON_PRESS_REPEAT before calling the BUTTON_PRESS_REPEAT callback
|
||||
|
||||
## v2.5.1 - 2023-3-14
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Update doc and code specification
|
||||
* Avoid overwriting callback by @franz-ms-muc in #252
|
||||
|
||||
## v2.5.0 - 2023-2-1
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Support custom button
|
||||
* Add BUTTON_PRESS_REPEAT_DONE event
|
||||
1
managed_components/espressif__button/CHECKSUMS.json
Normal file
1
managed_components/espressif__button/CHECKSUMS.json
Normal file
@@ -0,0 +1 @@
|
||||
{"version": "1.0", "algorithm": "sha256", "created_at": "2025-05-21T17:11:06.395688+00:00", "files": [{"path": "CMakeLists.txt", "size": 570, "hash": "9513b53f1d41edae9533481dcb35769d85d183bce999aacab590f187dbd0e401"}, {"path": "CHANGELOG.md", "size": 5068, "hash": "681f26d00972c770f54e0e6772e3f74d7ec748ddec393fd84b38bff72aed0edd"}, {"path": "idf_component.yml", "size": 521, "hash": "d3fe4c5d8cac965da28fb81ceaa91fe38dec6e0e5051092c2b0f635eb76db52b"}, {"path": "Kconfig", "size": 1385, "hash": "5ea358f4e061a732c3c0d565826d18dcd3fc393a0fe67a3c317ceec2f669f68b"}, {"path": "button_adc.c", "size": 12504, "hash": "9a511f69768bb975bda030046e338e835e3704a21f406675ceb1ca74e2190e74"}, {"path": "README.md", "size": 1729, "hash": "adc2c93639fabed0e77ff75b209c13f37bb97a5c09fe0b9d3688376faeda1735"}, {"path": "iot_button.c", "size": 27819, "hash": "49a6762fe887ca9a27434075f222ad10fabca2de51ccf31659069fa96dea7e82"}, {"path": "button_gpio.c", "size": 5767, "hash": "c3f2efda4dfc1cd4a0a3d1f67c3a02afc0cc2e8b58d2857a79d7f48b828847db"}, {"path": "button_matrix.c", "size": 3616, "hash": "8a2316485a31c1d40b7e662a1f7fd86cf9c85bdfa670d290c164f5f349616e81"}, {"path": "license.txt", "size": 11358, "hash": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"}, {"path": "interface/button_interface.h", "size": 771, "hash": "7fc7b7c596a9fe4e42cfe1529f484345757ef4c7b6b46ada5040ecf60da051ad"}, {"path": "include/button_gpio.h", "size": 2174, "hash": "9c46c0929b449dc751eaaa128deff18dde32370242a1214c59fba9b277408129"}, {"path": "include/button_matrix.h", "size": 3244, "hash": "5b95aa72eb47cfa3f1d603317e844d8c823b97175a6cf577857014548bf0e5ee"}, {"path": "include/iot_button.h", "size": 7448, "hash": "a6abae10ca72994e59757e7b62f131fed5c89b70b121f0d82fa9251dd96c9a8e"}, {"path": "include/button_types.h", "size": 1758, "hash": "1b956e32616cc8e397a263afc0b6e1d8d555335fc9c0c342d8c46ae556065179"}, {"path": "include/button_adc.h", "size": 2487, "hash": "5d33d87d329aa854d0879d276f8cf0bf5f8afa17b3b485a66edb0f63c9f9d24a"}, {"path": "test_apps/CMakeLists.txt", "size": 350, "hash": "234fd5c4b8c16494d8169c1490c649d23306e4e20f08ae14b128cd56c17893d5"}, {"path": "test_apps/sdkconfig.defaults", "size": 213, "hash": "9a34a6cb08c49ec24007587e0c5d492f44b5a862d9c0f583cf9f6f643669b564"}, {"path": "test_apps/pytest_button.py", "size": 755, "hash": "c5e633c4175f5d6475f1a10cb6fb800629dc82faf86bc6058ac4b90c6e3104d4"}, {"path": "examples/button_power_save/CMakeLists.txt", "size": 246, "hash": "e321ec2107e16cea7753b5c572215f181647ef0472893b2d256e28ed050180fe"}, {"path": "examples/button_power_save/sdkconfig.ci.ls_manually", "size": 42, "hash": "097c0ff2b0685db3ee4bd8f5b24521405b1bac4367096f213e66262ad7561344"}, {"path": "examples/button_power_save/README.md", "size": 1961, "hash": "9ace22e9ed47a4d6e2b7a840aaf0381009a563a51d6d3772498960ffc02952a9"}, {"path": "examples/button_power_save/sdkconfig.defaults", "size": 351, "hash": "86844871a1893fdf8556bce5f8717cc9b3cf02e494500827d94420e4f5139679"}, {"path": "examples/button_power_save/main/CMakeLists.txt", "size": 75, "hash": "27d6d39ef0cb6a9097984b41e080b09f7ff972339a0bb4ddf771db4001cb9782"}, {"path": "examples/button_power_save/main/idf_component.yml", "size": 137, "hash": "db65d8c6aef7d84eb2b09dfcc6740303c5917e69e3d61b1a3a43abd133998495"}, {"path": "examples/button_power_save/main/main.c", "size": 3939, "hash": "159bfbf6ec082f4f2484d05d211a6ac529c7bcd81830b05ad28a7fccdbd92942"}, {"path": "examples/button_power_save/main/Kconfig.projbuild", "size": 3514, "hash": "2a755747a11b0257d05b68cfb1adfbede1f6cfeda06f6a8d8ee53822ff9ba8e2"}, {"path": "test_apps/main/CMakeLists.txt", "size": 356, "hash": "9a810145f11532c705fc71396919a1e7e645f8cd7ffafd3d35c8d5a9ee40171d"}, {"path": "test_apps/main/custom_button_test.c", "size": 3807, "hash": "b62c7d92d176d014c75a1b9b9b6584d6c366edca4ca89575c75ab5818d44cfc8"}, {"path": "test_apps/main/auto_test.c", "size": 10257, "hash": "cc110a3c6a850251cfb7f238c8b7c806c9ce69a600f3e017f41bd1b2749fc5de"}, {"path": "test_apps/main/matrix_button_test.c", "size": 2940, "hash": "c329c3ff2a68d6992672c5085a00e5c8c3ee1a639ebb193570ebb0ef8946f4ef"}, {"path": "test_apps/main/button_test_main.c", "size": 1342, "hash": "841b79a2a6bef5382e8abd325927031c049522bc1731e56aebf95cd8ea01a17f"}, {"path": "test_apps/main/adc_button_test.c", "size": 5080, "hash": "7c5be72e772f75b9894f86ca4b8a1899e08bd6e3eefaf93bd40adacb75d1d9fc"}, {"path": "test_apps/main/gpio_button_test.c", "size": 7185, "hash": "0e74382a762116e9028b29e24205d3995bf395cd8ec5f663e500acfc35b9fe3d"}]}
|
||||
18
managed_components/espressif__button/CMakeLists.txt
Normal file
18
managed_components/espressif__button/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
set(PRIVREQ esp_timer)
|
||||
set(REQ driver)
|
||||
set(SRC_FILES "button_gpio.c" "iot_button.c" "button_matrix.c")
|
||||
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
|
||||
list(APPEND REQ esp_adc)
|
||||
if(CONFIG_SOC_ADC_SUPPORTED)
|
||||
list(APPEND SRC_FILES "button_adc.c")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${SRC_FILES}
|
||||
INCLUDE_DIRS include interface
|
||||
REQUIRES ${REQ}
|
||||
PRIV_REQUIRES ${PRIVREQ})
|
||||
|
||||
include(package_manager)
|
||||
cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})
|
||||
55
managed_components/espressif__button/Kconfig
Normal file
55
managed_components/espressif__button/Kconfig
Normal file
@@ -0,0 +1,55 @@
|
||||
menu "IoT Button"
|
||||
|
||||
config BUTTON_PERIOD_TIME_MS
|
||||
int "BUTTON PERIOD TIME (MS)"
|
||||
range 2 500
|
||||
default 5
|
||||
help
|
||||
"Button scan interval"
|
||||
|
||||
config BUTTON_DEBOUNCE_TICKS
|
||||
int "BUTTON DEBOUNCE TICKS"
|
||||
range 1 7
|
||||
default 2
|
||||
help
|
||||
"One CONFIG_BUTTON_DEBOUNCE_TICKS equal to CONFIG_BUTTON_PERIOD_TIME_MS"
|
||||
|
||||
config BUTTON_SHORT_PRESS_TIME_MS
|
||||
int "BUTTON SHORT PRESS TIME (MS)"
|
||||
range 50 800
|
||||
default 180
|
||||
|
||||
config BUTTON_LONG_PRESS_TIME_MS
|
||||
int "BUTTON LONG PRESS TIME (MS)"
|
||||
range 500 5000
|
||||
default 1500
|
||||
|
||||
config BUTTON_LONG_PRESS_HOLD_SERIAL_TIME_MS
|
||||
int "BUTTON LONG_PRESS_HOLD SERIAL TIME (MS)"
|
||||
range 2 1000
|
||||
default 20
|
||||
help
|
||||
"Long press hold Serial trigger interval"
|
||||
|
||||
config ADC_BUTTON_MAX_CHANNEL
|
||||
int "ADC BUTTON MAX CHANNEL"
|
||||
range 1 5
|
||||
default 3
|
||||
help
|
||||
"Maximum number of channels for ADC buttons"
|
||||
|
||||
config ADC_BUTTON_MAX_BUTTON_PER_CHANNEL
|
||||
int "ADC BUTTON MAX BUTTON PER CHANNEL"
|
||||
range 1 10
|
||||
default 8
|
||||
help
|
||||
"Maximum number of buttons per channel"
|
||||
|
||||
config ADC_BUTTON_SAMPLE_TIMES
|
||||
int "ADC BUTTON SAMPLE TIMES"
|
||||
range 1 4
|
||||
default 1
|
||||
help
|
||||
"Number of samples per scan"
|
||||
|
||||
endmenu
|
||||
42
managed_components/espressif__button/README.md
Normal file
42
managed_components/espressif__button/README.md
Normal file
@@ -0,0 +1,42 @@
|
||||
[](https://components.espressif.com/components/espressif/button)
|
||||
|
||||
# Component: Button
|
||||
[Online documentation](https://docs.espressif.com/projects/esp-iot-solution/en/latest/input_device/button.html)
|
||||
|
||||
After creating a new button object by calling function `button_create()`, the button object can create press events, every press event can have its own callback.
|
||||
|
||||
List of supported events:
|
||||
* Button pressed
|
||||
* Button released
|
||||
* Button pressed repeat
|
||||
* Button press repeat done
|
||||
* Button single click
|
||||
* Button double click
|
||||
* Button multiple click
|
||||
* Button long press start
|
||||
* Button long press hold
|
||||
* Button long press up
|
||||
* Button Press end
|
||||
|
||||

|
||||
|
||||
There are three ways this driver can handle buttons:
|
||||
1. Buttons connected to standard digital GPIO
|
||||
2. Multiple buttons connected to single ADC channel
|
||||
3. Matrix keyboard employs multiple GPIOs for operation.
|
||||
4. Custom button connect to any driver
|
||||
|
||||
The component supports the following functionalities:
|
||||
1. Creation of an unlimited number of buttons, accommodating various types simultaneously.
|
||||
2. Multiple callback functions for a single event.
|
||||
3. Allowing customization of the consecutive key press count to any desired number.
|
||||
4. Facilitating the setup of callbacks for any specified long-press duration.
|
||||
5. Support power save mode (Only for gpio button)
|
||||
|
||||
## Add component to your project
|
||||
|
||||
Please use the component manager command `add-dependency` to add the `button` to your project's dependency, during the `CMake` step the component will be downloaded automatically
|
||||
|
||||
```
|
||||
idf.py add-dependency "espressif/button=*"
|
||||
```
|
||||
327
managed_components/espressif__button/button_adc.c
Normal file
327
managed_components/espressif__button/button_adc.c
Normal file
@@ -0,0 +1,327 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_adc/adc_oneshot.h"
|
||||
#include "esp_adc/adc_cali.h"
|
||||
#include "esp_adc/adc_cali_scheme.h"
|
||||
#include "button_adc.h"
|
||||
#include "button_interface.h"
|
||||
|
||||
static const char *TAG = "adc_button";
|
||||
|
||||
#define DEFAULT_VREF 1100
|
||||
#define NO_OF_SAMPLES CONFIG_ADC_BUTTON_SAMPLE_TIMES //Multisampling
|
||||
|
||||
/*!< Using atten bigger than 6db by default, it will be 11db or 12db in different target */
|
||||
#define DEFAULT_ADC_ATTEN (ADC_ATTEN_DB_6 + 1)
|
||||
|
||||
#define ADC_BUTTON_WIDTH SOC_ADC_RTC_MAX_BITWIDTH
|
||||
#define ADC_BUTTON_CHANNEL_MAX SOC_ADC_MAX_CHANNEL_NUM
|
||||
#define ADC_BUTTON_ATTEN DEFAULT_ADC_ATTEN
|
||||
|
||||
#define ADC_BUTTON_MAX_CHANNEL CONFIG_ADC_BUTTON_MAX_CHANNEL
|
||||
#define ADC_BUTTON_MAX_BUTTON CONFIG_ADC_BUTTON_MAX_BUTTON_PER_CHANNEL
|
||||
|
||||
// ESP32C3 ADC2 it has been deprecated.
|
||||
#if (SOC_ADC_PERIPH_NUM >= 2) && !CONFIG_IDF_TARGET_ESP32C3
|
||||
#define ADC_UNIT_NUM 2
|
||||
#else
|
||||
#define ADC_UNIT_NUM 1
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint16_t min;
|
||||
uint16_t max;
|
||||
} button_data_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t channel;
|
||||
uint8_t is_init;
|
||||
button_data_t btns[ADC_BUTTON_MAX_BUTTON]; /* all button on the channel */
|
||||
uint64_t last_time; /* the last time of adc sample */
|
||||
} btn_adc_channel_t;
|
||||
|
||||
typedef enum {
|
||||
ADC_NONE_INIT = 0,
|
||||
ADC_INIT_BY_ADC_BUTTON,
|
||||
ADC_INIT_BY_USER,
|
||||
} adc_init_info_t;
|
||||
|
||||
typedef struct {
|
||||
adc_init_info_t is_configured;
|
||||
adc_cali_handle_t adc_cali_handle;
|
||||
adc_oneshot_unit_handle_t adc_handle;
|
||||
btn_adc_channel_t ch[ADC_BUTTON_MAX_CHANNEL];
|
||||
uint8_t ch_num;
|
||||
} btn_adc_unit_t;
|
||||
|
||||
typedef struct {
|
||||
btn_adc_unit_t unit[ADC_UNIT_NUM];
|
||||
} button_adc_t;
|
||||
typedef struct {
|
||||
button_driver_t base;
|
||||
adc_unit_t unit_id;
|
||||
uint32_t ch;
|
||||
uint32_t index;
|
||||
} button_adc_obj;
|
||||
|
||||
static button_adc_t g_button = {0};
|
||||
|
||||
static int find_unused_channel(adc_unit_t unit_id)
|
||||
{
|
||||
for (size_t i = 0; i < ADC_BUTTON_MAX_CHANNEL; i++) {
|
||||
if (0 == g_button.unit[unit_id].ch[i].is_init) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int find_channel(adc_unit_t unit_id, uint8_t channel)
|
||||
{
|
||||
for (size_t i = 0; i < ADC_BUTTON_MAX_CHANNEL; i++) {
|
||||
if (channel == g_button.unit[unit_id].ch[i].channel) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool adc_calibration_init(adc_unit_t unit, adc_atten_t atten, adc_cali_handle_t *out_handle)
|
||||
{
|
||||
adc_cali_handle_t handle = NULL;
|
||||
esp_err_t ret = ESP_ERR_NOT_SUPPORTED;
|
||||
bool calibrated = false;
|
||||
|
||||
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
if (!calibrated) {
|
||||
ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting");
|
||||
adc_cali_curve_fitting_config_t cali_config = {
|
||||
.unit_id = unit,
|
||||
.atten = atten,
|
||||
.bitwidth = ADC_BUTTON_WIDTH,
|
||||
};
|
||||
ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
|
||||
if (ret == ESP_OK) {
|
||||
calibrated = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
if (!calibrated) {
|
||||
ESP_LOGI(TAG, "calibration scheme version is %s", "Line Fitting");
|
||||
adc_cali_line_fitting_config_t cali_config = {
|
||||
.unit_id = unit,
|
||||
.atten = atten,
|
||||
.bitwidth = ADC_BUTTON_WIDTH,
|
||||
};
|
||||
ret = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
|
||||
if (ret == ESP_OK) {
|
||||
calibrated = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
*out_handle = handle;
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Calibration Success");
|
||||
} else if (ret == ESP_ERR_NOT_SUPPORTED || !calibrated) {
|
||||
ESP_LOGW(TAG, "eFuse not burnt, skip software calibration");
|
||||
} else if (ret == ESP_ERR_NOT_SUPPORTED) {
|
||||
ESP_LOGW(TAG, "Calibration not supported");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Invalid arg or no memory");
|
||||
}
|
||||
|
||||
return calibrated;
|
||||
}
|
||||
|
||||
static bool adc_calibration_deinit(adc_cali_handle_t handle)
|
||||
{
|
||||
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
if (adc_cali_delete_scheme_curve_fitting(handle) == ESP_OK) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
if (adc_cali_delete_scheme_line_fitting(handle) == ESP_OK) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_err_t button_adc_del(button_driver_t *button_driver)
|
||||
{
|
||||
button_adc_obj *adc_btn = __containerof(button_driver, button_adc_obj, base);
|
||||
ESP_RETURN_ON_FALSE(adc_btn->ch < ADC_BUTTON_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "channel out of range");
|
||||
ESP_RETURN_ON_FALSE(adc_btn->index < ADC_BUTTON_MAX_BUTTON, ESP_ERR_INVALID_ARG, TAG, "button_index out of range");
|
||||
|
||||
int ch_index = find_channel(adc_btn->unit_id, adc_btn->ch);
|
||||
ESP_RETURN_ON_FALSE(ch_index >= 0, ESP_ERR_INVALID_ARG, TAG, "can't find the channel");
|
||||
|
||||
g_button.unit[adc_btn->unit_id].ch[ch_index].btns[adc_btn->index].max = 0;
|
||||
g_button.unit[adc_btn->unit_id].ch[ch_index].btns[adc_btn->index].min = 0;
|
||||
|
||||
/** check button usage on the channel*/
|
||||
uint8_t unused_button = 0;
|
||||
for (size_t i = 0; i < ADC_BUTTON_MAX_BUTTON; i++) {
|
||||
if (0 == g_button.unit[adc_btn->unit_id].ch[ch_index].btns[i].max) {
|
||||
unused_button++;
|
||||
}
|
||||
}
|
||||
if (unused_button == ADC_BUTTON_MAX_BUTTON && g_button.unit[adc_btn->unit_id].ch[ch_index].is_init) { /**< if all button is unused, deinit the channel */
|
||||
g_button.unit[adc_btn->unit_id].ch[ch_index].is_init = 0;
|
||||
g_button.unit[adc_btn->unit_id].ch[ch_index].channel = ADC_BUTTON_CHANNEL_MAX;
|
||||
ESP_LOGD(TAG, "all button is unused on channel%d, deinit the channel", g_button.unit[adc_btn->unit_id].ch[ch_index].channel);
|
||||
}
|
||||
|
||||
/** check channel usage on the adc*/
|
||||
uint8_t unused_ch = 0;
|
||||
for (size_t i = 0; i < ADC_BUTTON_MAX_CHANNEL; i++) {
|
||||
if (0 == g_button.unit[adc_btn->unit_id].ch[i].is_init) {
|
||||
unused_ch++;
|
||||
}
|
||||
}
|
||||
if (unused_ch == ADC_BUTTON_MAX_CHANNEL && g_button.unit[adc_btn->unit_id].is_configured) { /**< if all channel is unused, deinit the adc */
|
||||
if (g_button.unit[adc_btn->unit_id].is_configured == ADC_INIT_BY_ADC_BUTTON) {
|
||||
esp_err_t ret = adc_oneshot_del_unit(g_button.unit[adc_btn->unit_id].adc_handle);
|
||||
ESP_RETURN_ON_FALSE(ret == ESP_OK, ret, TAG, "adc oneshot del unit fail");
|
||||
adc_calibration_deinit(g_button.unit[adc_btn->unit_id].adc_cali_handle);
|
||||
}
|
||||
|
||||
g_button.unit[adc_btn->unit_id].is_configured = ADC_NONE_INIT;
|
||||
memset(&g_button.unit[adc_btn->unit_id], 0, sizeof(btn_adc_unit_t));
|
||||
ESP_LOGD(TAG, "all channel is unused, , deinit adc");
|
||||
}
|
||||
free(adc_btn);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static uint32_t get_adc_volatge(adc_unit_t unit_id, uint8_t channel)
|
||||
{
|
||||
uint32_t adc_reading = 0;
|
||||
int adc_raw = 0;
|
||||
for (int i = 0; i < NO_OF_SAMPLES; i++) {
|
||||
adc_oneshot_read(g_button.unit[unit_id].adc_handle, channel, &adc_raw);
|
||||
adc_reading += adc_raw;
|
||||
}
|
||||
adc_reading /= NO_OF_SAMPLES;
|
||||
//Convert adc_reading to voltage in mV
|
||||
int voltage = 0;
|
||||
adc_cali_raw_to_voltage(g_button.unit[unit_id].adc_cali_handle, adc_reading, &voltage);
|
||||
ESP_LOGV(TAG, "Raw: %"PRIu32"\tVoltage: %dmV", adc_reading, voltage);
|
||||
return voltage;
|
||||
}
|
||||
|
||||
uint8_t button_adc_get_key_level(button_driver_t *button_driver)
|
||||
{
|
||||
button_adc_obj *adc_btn = __containerof(button_driver, button_adc_obj, base);
|
||||
static uint16_t vol = 0;
|
||||
uint32_t ch = adc_btn->ch;
|
||||
uint32_t index = adc_btn->index;
|
||||
ESP_RETURN_ON_FALSE(ch < ADC_BUTTON_CHANNEL_MAX, 0, TAG, "channel out of range");
|
||||
ESP_RETURN_ON_FALSE(index < ADC_BUTTON_MAX_BUTTON, 0, TAG, "button_index out of range");
|
||||
|
||||
int ch_index = find_channel(adc_btn->unit_id, ch);
|
||||
ESP_RETURN_ON_FALSE(ch_index >= 0, 0, TAG, "The button_index is not init");
|
||||
|
||||
/** It starts only when the elapsed time is more than 1ms */
|
||||
if ((esp_timer_get_time() - g_button.unit[adc_btn->unit_id].ch[ch_index].last_time) > 1000) {
|
||||
vol = get_adc_volatge(adc_btn->unit_id, ch);
|
||||
g_button.unit[adc_btn->unit_id].ch[ch_index].last_time = esp_timer_get_time();
|
||||
}
|
||||
|
||||
if (vol <= g_button.unit[adc_btn->unit_id].ch[ch_index].btns[index].max &&
|
||||
vol >= g_button.unit[adc_btn->unit_id].ch[ch_index].btns[index].min) {
|
||||
return BUTTON_ACTIVE;
|
||||
}
|
||||
return BUTTON_INACTIVE;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_new_adc_device(const button_config_t *button_config, const button_adc_config_t *adc_config, button_handle_t *ret_button)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_RETURN_ON_FALSE(button_config && adc_config && ret_button, ESP_ERR_INVALID_ARG, TAG, "Invalid argument");
|
||||
ESP_RETURN_ON_FALSE(adc_config->unit_id < ADC_UNIT_NUM, ESP_ERR_INVALID_ARG, TAG, "adc_handle out of range");
|
||||
ESP_RETURN_ON_FALSE(adc_config->adc_channel < ADC_BUTTON_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "channel out of range");
|
||||
ESP_RETURN_ON_FALSE(adc_config->button_index < ADC_BUTTON_MAX_BUTTON, ESP_ERR_INVALID_ARG, TAG, "button_index out of range");
|
||||
ESP_RETURN_ON_FALSE(adc_config->max > 0, ESP_ERR_INVALID_ARG, TAG, "key max voltage invalid");
|
||||
button_adc_obj *adc_btn = calloc(1, sizeof(button_adc_obj));
|
||||
ESP_RETURN_ON_FALSE(adc_btn, ESP_ERR_NO_MEM, TAG, "calloc fail");
|
||||
adc_btn->unit_id = adc_config->unit_id;
|
||||
|
||||
int ch_index = find_channel(adc_btn->unit_id, adc_config->adc_channel);
|
||||
if (ch_index >= 0) { /**< the channel has been initialized */
|
||||
ESP_GOTO_ON_FALSE(g_button.unit[adc_btn->unit_id].ch[ch_index].btns[adc_config->button_index].max == 0, ESP_ERR_INVALID_STATE, err, TAG, "The button_index has been used");
|
||||
} else { /**< this is a new channel */
|
||||
int unused_ch_index = find_unused_channel(adc_config->unit_id);
|
||||
ESP_GOTO_ON_FALSE(unused_ch_index >= 0, ESP_ERR_INVALID_STATE, err, TAG, "exceed max channel number, can't create a new channel");
|
||||
ch_index = unused_ch_index;
|
||||
}
|
||||
|
||||
/** initialize adc */
|
||||
if (0 == g_button.unit[adc_btn->unit_id].is_configured) {
|
||||
esp_err_t ret;
|
||||
if (NULL == adc_config->adc_handle) {
|
||||
//ADC1 Init
|
||||
adc_oneshot_unit_init_cfg_t init_config = {
|
||||
.unit_id = adc_btn->unit_id,
|
||||
};
|
||||
ret = adc_oneshot_new_unit(&init_config, &g_button.unit[adc_btn->unit_id].adc_handle);
|
||||
ESP_GOTO_ON_FALSE(ret == ESP_OK, ESP_FAIL, err, TAG, "adc oneshot new unit fail!");
|
||||
g_button.unit[adc_btn->unit_id].is_configured = ADC_INIT_BY_ADC_BUTTON;
|
||||
} else {
|
||||
g_button.unit[adc_btn->unit_id].adc_handle = *adc_config->adc_handle;
|
||||
ESP_LOGI(TAG, "ADC1 has been initialized");
|
||||
g_button.unit[adc_btn->unit_id].is_configured = ADC_INIT_BY_USER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** initialize adc channel */
|
||||
if (0 == g_button.unit[adc_btn->unit_id].ch[ch_index].is_init) {
|
||||
//ADC1 Config
|
||||
adc_oneshot_chan_cfg_t oneshot_config = {
|
||||
.bitwidth = ADC_BUTTON_WIDTH,
|
||||
.atten = ADC_BUTTON_ATTEN,
|
||||
};
|
||||
esp_err_t ret = adc_oneshot_config_channel(g_button.unit[adc_btn->unit_id].adc_handle, adc_config->adc_channel, &oneshot_config);
|
||||
ESP_GOTO_ON_FALSE(ret == ESP_OK, ESP_FAIL, err, TAG, "adc oneshot config channel fail!");
|
||||
//-------------ADC1 Calibration Init---------------//
|
||||
adc_calibration_init(adc_btn->unit_id, ADC_BUTTON_ATTEN, &g_button.unit[adc_btn->unit_id].adc_cali_handle);
|
||||
g_button.unit[adc_btn->unit_id].ch[ch_index].channel = adc_config->adc_channel;
|
||||
g_button.unit[adc_btn->unit_id].ch[ch_index].is_init = 1;
|
||||
g_button.unit[adc_btn->unit_id].ch[ch_index].last_time = 0;
|
||||
}
|
||||
g_button.unit[adc_btn->unit_id].ch[ch_index].btns[adc_config->button_index].max = adc_config->max;
|
||||
g_button.unit[adc_btn->unit_id].ch[ch_index].btns[adc_config->button_index].min = adc_config->min;
|
||||
g_button.unit[adc_btn->unit_id].ch_num++;
|
||||
|
||||
adc_btn->ch = adc_config->adc_channel;
|
||||
adc_btn->index = adc_config->button_index;
|
||||
adc_btn->base.get_key_level = button_adc_get_key_level;
|
||||
adc_btn->base.del = button_adc_del;
|
||||
ret = iot_button_create(button_config, &adc_btn->base, ret_button);
|
||||
ESP_GOTO_ON_FALSE(ret == ESP_OK, ESP_FAIL, err, TAG, "Create button failed");
|
||||
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (adc_btn) {
|
||||
free(adc_btn);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
149
managed_components/espressif__button/button_gpio.c
Normal file
149
managed_components/espressif__button/button_gpio.c
Normal file
@@ -0,0 +1,149 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "button_gpio.h"
|
||||
#include "esp_sleep.h"
|
||||
#include "button_interface.h"
|
||||
#include "iot_button.h"
|
||||
|
||||
static const char *TAG = "gpio_button";
|
||||
|
||||
typedef struct {
|
||||
button_driver_t base; /**< button driver */
|
||||
int32_t gpio_num; /**< num of gpio */
|
||||
uint8_t active_level; /**< gpio level when press down */
|
||||
bool enable_power_save; /**< enable power save */
|
||||
} button_gpio_obj;
|
||||
|
||||
static esp_err_t button_gpio_del(button_driver_t *button_driver)
|
||||
{
|
||||
button_gpio_obj *gpio_btn = __containerof(button_driver, button_gpio_obj, base);
|
||||
esp_err_t ret = gpio_reset_pin(gpio_btn->gpio_num);
|
||||
free(gpio_btn);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint8_t button_gpio_get_key_level(button_driver_t *button_driver)
|
||||
{
|
||||
button_gpio_obj *gpio_btn = __containerof(button_driver, button_gpio_obj, base);
|
||||
int level = gpio_get_level(gpio_btn->gpio_num);
|
||||
return level == gpio_btn->active_level ? 1 : 0;
|
||||
}
|
||||
|
||||
static esp_err_t button_gpio_enable_gpio_wakeup(uint32_t gpio_num, uint8_t active_level, bool enable)
|
||||
{
|
||||
esp_err_t ret;
|
||||
if (enable) {
|
||||
gpio_intr_enable(gpio_num);
|
||||
ret = gpio_wakeup_enable(gpio_num, active_level == 0 ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL);
|
||||
} else {
|
||||
gpio_intr_disable(gpio_num);
|
||||
ret = gpio_wakeup_disable(gpio_num);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t button_gpio_set_intr(int gpio_num, gpio_int_type_t intr_type, gpio_isr_t isr_handler)
|
||||
{
|
||||
static bool isr_service_installed = false;
|
||||
gpio_set_intr_type(gpio_num, intr_type);
|
||||
if (!isr_service_installed) {
|
||||
gpio_install_isr_service(ESP_INTR_FLAG_IRAM);
|
||||
isr_service_installed = true;
|
||||
}
|
||||
gpio_isr_handler_add(gpio_num, isr_handler, (void *)gpio_num);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void button_power_save_isr_handler(void* arg)
|
||||
{
|
||||
/*!< resume the button */
|
||||
iot_button_resume();
|
||||
/*!< disable gpio wakeup not need active level*/
|
||||
button_gpio_enable_gpio_wakeup((uint32_t)arg, 0, false);
|
||||
}
|
||||
|
||||
static esp_err_t button_enter_power_save(button_driver_t *button_driver)
|
||||
{
|
||||
button_gpio_obj *gpio_btn = __containerof(button_driver, button_gpio_obj, base);
|
||||
return button_gpio_enable_gpio_wakeup(gpio_btn->gpio_num, gpio_btn->active_level, true);
|
||||
}
|
||||
|
||||
esp_err_t iot_button_new_gpio_device(const button_config_t *button_config, const button_gpio_config_t *gpio_cfg, button_handle_t *ret_button)
|
||||
{
|
||||
button_gpio_obj *gpio_btn = NULL;
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE(button_config && gpio_cfg && ret_button, ESP_ERR_INVALID_ARG, err, TAG, "Invalid argument");
|
||||
ESP_GOTO_ON_FALSE(GPIO_IS_VALID_GPIO(gpio_cfg->gpio_num), ESP_ERR_INVALID_ARG, err, TAG, "GPIO number error");
|
||||
|
||||
gpio_btn = (button_gpio_obj *)calloc(1, sizeof(button_gpio_obj));
|
||||
ESP_GOTO_ON_FALSE(gpio_btn, ESP_ERR_NO_MEM, err, TAG, "No memory for gpio button");
|
||||
gpio_btn->gpio_num = gpio_cfg->gpio_num;
|
||||
gpio_btn->active_level = gpio_cfg->active_level;
|
||||
gpio_btn->enable_power_save = gpio_cfg->enable_power_save;
|
||||
|
||||
gpio_config_t gpio_conf = {0};
|
||||
gpio_conf.intr_type = GPIO_INTR_DISABLE;
|
||||
gpio_conf.mode = GPIO_MODE_INPUT;
|
||||
gpio_conf.pin_bit_mask = (1ULL << gpio_cfg->gpio_num);
|
||||
if (!gpio_cfg->disable_pull) {
|
||||
if (gpio_cfg->active_level) {
|
||||
gpio_conf.pull_down_en = GPIO_PULLDOWN_ENABLE;
|
||||
gpio_conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||
} else {
|
||||
gpio_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
gpio_conf.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||
}
|
||||
}
|
||||
gpio_config(&gpio_conf);
|
||||
|
||||
if (gpio_cfg->enable_power_save) {
|
||||
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
|
||||
if (!esp_sleep_is_valid_wakeup_gpio(gpio_cfg->gpio_num)) {
|
||||
ESP_LOGE(TAG, "GPIO %ld is not a valid wakeup source under CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE", gpio_cfg->gpio_num);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
gpio_hold_en(gpio_cfg->gpio_num);
|
||||
#endif
|
||||
/* Enable wake up from GPIO */
|
||||
esp_err_t ret = gpio_wakeup_enable(gpio_cfg->gpio_num, gpio_cfg->active_level == 0 ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL);
|
||||
ESP_GOTO_ON_FALSE(ret == ESP_OK, ESP_ERR_INVALID_STATE, err, TAG, "Enable gpio wakeup failed");
|
||||
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
|
||||
#if SOC_PM_SUPPORT_EXT1_WAKEUP
|
||||
ret = esp_sleep_enable_ext1_wakeup_io((1ULL << gpio_cfg->gpio_num), gpio_cfg->active_level == 0 ? ESP_EXT1_WAKEUP_ANY_LOW : ESP_EXT1_WAKEUP_ANY_HIGH);
|
||||
#else
|
||||
/*!< Not support etc: esp32c2, esp32c3. Target must support ext1 wakeup */
|
||||
ret = ESP_FAIL;
|
||||
ESP_GOTO_ON_FALSE(ret == ESP_OK, ESP_FAIL, err, TAG, "Target must support ext1 wakeup");
|
||||
#endif
|
||||
#else
|
||||
ret = esp_sleep_enable_gpio_wakeup();
|
||||
#endif
|
||||
ESP_GOTO_ON_FALSE(ret == ESP_OK, ESP_FAIL, err, TAG, "Configure gpio as wakeup source failed");
|
||||
|
||||
ret = button_gpio_set_intr(gpio_btn->gpio_num, gpio_cfg->active_level == 0 ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL, button_power_save_isr_handler);
|
||||
ESP_GOTO_ON_FALSE(ret == ESP_OK, ESP_FAIL, err, TAG, "Set gpio interrupt failed");
|
||||
|
||||
gpio_btn->base.enable_power_save = true;
|
||||
gpio_btn->base.enter_power_save = button_enter_power_save;
|
||||
}
|
||||
|
||||
gpio_btn->base.get_key_level = button_gpio_get_key_level;
|
||||
gpio_btn->base.del = button_gpio_del;
|
||||
|
||||
ret = iot_button_create(button_config, &gpio_btn->base, ret_button);
|
||||
ESP_GOTO_ON_FALSE(ret == ESP_OK, ESP_FAIL, err, TAG, "Create button failed");
|
||||
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (gpio_btn) {
|
||||
free(gpio_btn);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
88
managed_components/espressif__button/button_matrix.c
Normal file
88
managed_components/espressif__button/button_matrix.c
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "button_matrix.h"
|
||||
#include "button_interface.h"
|
||||
|
||||
static const char *TAG = "matrix_button";
|
||||
|
||||
typedef struct {
|
||||
button_driver_t base; /**< base button driver */
|
||||
int32_t row_gpio_num; /**< row gpio */
|
||||
int32_t col_gpio_num; /**< col gpio */
|
||||
} button_matrix_obj;
|
||||
|
||||
static esp_err_t button_matrix_gpio_init(int32_t gpio_num, gpio_mode_t mode)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "gpio_num error");
|
||||
gpio_config_t gpio_conf = {0};
|
||||
gpio_conf.intr_type = GPIO_INTR_DISABLE;
|
||||
gpio_conf.pull_down_en = GPIO_PULLDOWN_ENABLE;
|
||||
gpio_conf.pin_bit_mask = (1ULL << gpio_num);
|
||||
gpio_conf.mode = mode;
|
||||
gpio_config(&gpio_conf);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t button_matrix_del(button_driver_t *button_driver)
|
||||
{
|
||||
button_matrix_obj *matrix_btn = __containerof(button_driver, button_matrix_obj, base);
|
||||
//Reset an gpio to default state (select gpio function, enable pullup and disable input and output).
|
||||
gpio_reset_pin(matrix_btn->row_gpio_num);
|
||||
gpio_reset_pin(matrix_btn->col_gpio_num);
|
||||
free(matrix_btn);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint8_t button_matrix_get_key_level(button_driver_t *button_driver)
|
||||
{
|
||||
button_matrix_obj *matrix_btn = __containerof(button_driver, button_matrix_obj, base);
|
||||
gpio_set_level(matrix_btn->row_gpio_num, 1);
|
||||
uint8_t level = gpio_get_level(matrix_btn->col_gpio_num);
|
||||
gpio_set_level(matrix_btn->row_gpio_num, 0);
|
||||
return level;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_new_matrix_device(const button_config_t *button_config, const button_matrix_config_t *matrix_config, button_handle_t *ret_button, size_t *size)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_RETURN_ON_FALSE(button_config && matrix_config && ret_button, ESP_ERR_INVALID_ARG, TAG, "Invalid argument");
|
||||
ESP_RETURN_ON_FALSE(matrix_config->col_gpios && matrix_config->row_gpios, ESP_ERR_INVALID_ARG, TAG, "Invalid matrix config");
|
||||
ESP_RETURN_ON_FALSE(matrix_config->col_gpio_num > 0 && matrix_config->row_gpio_num > 0, ESP_ERR_INVALID_ARG, TAG, "Invalid matrix config");
|
||||
ESP_RETURN_ON_FALSE(*size == matrix_config->row_gpio_num * matrix_config->col_gpio_num, ESP_ERR_INVALID_ARG, TAG, "Invalid size");
|
||||
|
||||
button_matrix_obj *matrix_btn = calloc(*size, sizeof(button_matrix_obj));
|
||||
for (int i = 0; i < matrix_config->row_gpio_num; i++) {
|
||||
button_matrix_gpio_init(matrix_config->row_gpios[i], GPIO_MODE_OUTPUT);
|
||||
}
|
||||
|
||||
for (int i = 0; i < matrix_config->col_gpio_num; i++) {
|
||||
button_matrix_gpio_init(matrix_config->col_gpios[i], GPIO_MODE_INPUT);
|
||||
}
|
||||
|
||||
for (int i = 0; i < *size; i++) {
|
||||
matrix_btn[i].base.get_key_level = button_matrix_get_key_level;
|
||||
matrix_btn[i].base.del = button_matrix_del;
|
||||
matrix_btn[i].row_gpio_num = matrix_config->row_gpios[i / matrix_config->col_gpio_num];
|
||||
matrix_btn[i].col_gpio_num = matrix_config->col_gpios[i % matrix_config->col_gpio_num];
|
||||
ESP_LOGD(TAG, "row_gpio_num: %"PRId32", col_gpio_num: %"PRId32"", matrix_btn[i].row_gpio_num, matrix_btn[i].col_gpio_num);
|
||||
ret = iot_button_create(button_config, &matrix_btn[i].base, &ret_button[i]);
|
||||
ESP_GOTO_ON_FALSE(ret == ESP_OK, ESP_FAIL, err, TAG, "Create button failed");
|
||||
}
|
||||
*size = matrix_config->row_gpio_num * matrix_config->col_gpio_num;
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (matrix_btn) {
|
||||
free(matrix_btn);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# The following five lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(button_power_save)
|
||||
@@ -0,0 +1,43 @@
|
||||
## Button Power Save Example
|
||||
|
||||
This example demonstrates how to utilize the `button` component in conjunction with the light sleep low-power mode.
|
||||
|
||||
* `button` [Component Introduction](https://docs.espressif.com/projects/esp-iot-solution/en/latest/input_device/button.html)
|
||||
|
||||
## Hardware
|
||||
|
||||
* Any GPIO on any development board can be used in this example.
|
||||
|
||||
## Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run the monitor tool to view the serial output:
|
||||
|
||||
* Run `. ./export.sh` to set IDF environment
|
||||
* Run `idf.py set-target esp32xx` to set target chip
|
||||
* Run `idf.py -p PORT flash monitor` to build, flash and monitor the project
|
||||
|
||||
(To exit the serial monitor, type `Ctrl-]`.)
|
||||
|
||||
See the Getting Started Guide for all the steps to configure and use the ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
I (1139) pm: Frequency switching config: CPU_MAX: 160, APB_MAX: 80, APB_MIN: 80, Light sleep: ENABLED
|
||||
I (1149) sleep: Code start at 42000020, total 119.03 KiB, data start at 3c000000, total 49152.00 KiB
|
||||
I (1159) button: IoT Button Version: 3.2.0
|
||||
I (1163) gpio: GPIO[0]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (2922) button_power_save: Button event BUTTON_PRESS_DOWN
|
||||
I (3017) button_power_save: Button event BUTTON_PRESS_UP
|
||||
I (3017) button_power_save: Wake up from light sleep, reason 4
|
||||
I (3200) button_power_save: Button event BUTTON_SINGLE_CLICK
|
||||
I (3200) button_power_save: Wake up from light sleep, reason 4
|
||||
I (3202) button_power_save: Button event BUTTON_PRESS_REPEAT_DONE
|
||||
I (3208) button_power_save: Wake up from light sleep, reason 4
|
||||
I (3627) button_power_save: Button event BUTTON_PRESS_DOWN
|
||||
I (3702) button_power_save: Button event BUTTON_PRESS_UP
|
||||
I (3702) button_power_save: Wake up from light sleep, reason 4
|
||||
I (3887) button_power_save: Button event BUTTON_SINGLE_CLICK
|
||||
I (3887) button_power_save: Wake up from light sleep, reason 4
|
||||
I (3889) button_power_save: Button event BUTTON_PRESS_REPEAT_DONE
|
||||
```
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,87 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
choice ENTER_LIGHT_SLEEP_MODE
|
||||
prompt "Enter light sleep mode"
|
||||
default ENTER_LIGHT_SLEEP_AUTO
|
||||
depends on PM_ENABLE
|
||||
help
|
||||
Enable light sleep mode to save power.
|
||||
|
||||
config ENTER_LIGHT_SLEEP_AUTO
|
||||
bool "Auto enter Light Sleep"
|
||||
config ENTER_LIGHT_SLEEP_MODE_MANUALLY
|
||||
bool "Manually enter Light Sleep"
|
||||
endchoice
|
||||
|
||||
choice EXAMPLE_MAX_CPU_FREQ
|
||||
prompt "Maximum CPU frequency"
|
||||
default EXAMPLE_MAX_CPU_FREQ_80 if !IDF_TARGET_ESP32H2
|
||||
default EXAMPLE_MAX_CPU_FREQ_96 if IDF_TARGET_ESP32H2
|
||||
depends on PM_ENABLE
|
||||
help
|
||||
Maximum CPU frequency to use for dynamic frequency scaling.
|
||||
|
||||
config EXAMPLE_MAX_CPU_FREQ_80
|
||||
bool "80 MHz"
|
||||
config EXAMPLE_MAX_CPU_FREQ_96
|
||||
bool "96 MHz"
|
||||
depends on IDF_TARGET_ESP32H2
|
||||
config EXAMPLE_MAX_CPU_FREQ_120
|
||||
bool "120 MHz"
|
||||
depends on IDF_TARGET_ESP32C2
|
||||
config EXAMPLE_MAX_CPU_FREQ_160
|
||||
bool "160 MHz"
|
||||
depends on !IDF_TARGET_ESP32C2
|
||||
config EXAMPLE_MAX_CPU_FREQ_240
|
||||
bool "240 MHz"
|
||||
depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_MAX_CPU_FREQ_MHZ
|
||||
int
|
||||
default 80 if EXAMPLE_MAX_CPU_FREQ_80
|
||||
default 96 if EXAMPLE_MAX_CPU_FREQ_96
|
||||
default 120 if EXAMPLE_MAX_CPU_FREQ_120
|
||||
default 160 if EXAMPLE_MAX_CPU_FREQ_160
|
||||
default 240 if EXAMPLE_MAX_CPU_FREQ_240
|
||||
|
||||
choice EXAMPLE_MIN_CPU_FREQ
|
||||
prompt "Minimum CPU frequency"
|
||||
default EXAMPLE_MIN_CPU_FREQ_10M if !IDF_TARGET_ESP32H2
|
||||
default EXAMPLE_MIN_CPU_FREQ_32M if IDF_TARGET_ESP32H2
|
||||
depends on PM_ENABLE
|
||||
help
|
||||
Minimum CPU frequency to use for dynamic frequency scaling.
|
||||
Should be set to XTAL frequency or XTAL frequency divided by integer.
|
||||
|
||||
config EXAMPLE_MIN_CPU_FREQ_40M
|
||||
bool "40 MHz (use with 40MHz XTAL)"
|
||||
depends on XTAL_FREQ_40 || XTAL_FREQ_AUTO || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO || !IDF_TARGET_ESP32
|
||||
config EXAMPLE_MIN_CPU_FREQ_20M
|
||||
bool "20 MHz (use with 40MHz XTAL)"
|
||||
depends on XTAL_FREQ_40 || XTAL_FREQ_AUTO || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO || !IDF_TARGET_ESP32
|
||||
config EXAMPLE_MIN_CPU_FREQ_10M
|
||||
bool "10 MHz (use with 40MHz XTAL)"
|
||||
depends on XTAL_FREQ_40 || XTAL_FREQ_AUTO || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO || !IDF_TARGET_ESP32
|
||||
config EXAMPLE_MIN_CPU_FREQ_26M
|
||||
bool "26 MHz (use with 26MHz XTAL)"
|
||||
depends on XTAL_FREQ_26 || XTAL_FREQ_AUTO || ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO
|
||||
config EXAMPLE_MIN_CPU_FREQ_13M
|
||||
bool "13 MHz (use with 26MHz XTAL)"
|
||||
depends on XTAL_FREQ_26 || XTAL_FREQ_AUTO || ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO
|
||||
config EXAMPLE_MIN_CPU_FREQ_32M
|
||||
bool "32 MHz (use with 32MHz XTAL)"
|
||||
depends on IDF_TARGET_ESP32H2
|
||||
depends on XTAL_FREQ_32 || XTAL_FREQ_AUTO
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_MIN_CPU_FREQ_MHZ
|
||||
int
|
||||
default 40 if EXAMPLE_MIN_CPU_FREQ_40M
|
||||
default 20 if EXAMPLE_MIN_CPU_FREQ_20M
|
||||
default 10 if EXAMPLE_MIN_CPU_FREQ_10M
|
||||
default 26 if EXAMPLE_MIN_CPU_FREQ_26M
|
||||
default 13 if EXAMPLE_MIN_CPU_FREQ_13M
|
||||
default 32 if EXAMPLE_MIN_CPU_FREQ_32M
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,6 @@
|
||||
version: "0.1.0"
|
||||
dependencies:
|
||||
idf: ">=4.4"
|
||||
button:
|
||||
version: "^4.0.0"
|
||||
override_path: "../../../../components/button"
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_pm.h"
|
||||
#include "iot_button.h"
|
||||
#include "esp_sleep.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include "button_gpio.h"
|
||||
|
||||
/* Most development boards have "boot" button attached to GPIO0.
|
||||
* You can also change this to another pin.
|
||||
*/
|
||||
#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C6
|
||||
#define BOOT_BUTTON_NUM 9
|
||||
#else
|
||||
#define BOOT_BUTTON_NUM 0
|
||||
#endif
|
||||
#define BUTTON_ACTIVE_LEVEL 0
|
||||
|
||||
static const char *TAG = "button_power_save";
|
||||
|
||||
static void button_event_cb(void *arg, void *data)
|
||||
{
|
||||
iot_button_print_event((button_handle_t)arg);
|
||||
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
|
||||
if (cause != ESP_SLEEP_WAKEUP_UNDEFINED) {
|
||||
ESP_LOGI(TAG, "Wake up from light sleep, reason %d", cause);
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_ENTER_LIGHT_SLEEP_MODE_MANUALLY
|
||||
void button_enter_power_save(void *usr_data)
|
||||
{
|
||||
ESP_LOGI(TAG, "Can enter power save now");
|
||||
esp_light_sleep_start();
|
||||
}
|
||||
#endif
|
||||
|
||||
void button_init(uint32_t button_num)
|
||||
{
|
||||
button_config_t btn_cfg = {0};
|
||||
button_gpio_config_t gpio_cfg = {
|
||||
.gpio_num = button_num,
|
||||
.active_level = BUTTON_ACTIVE_LEVEL,
|
||||
.enable_power_save = true,
|
||||
};
|
||||
|
||||
button_handle_t btn;
|
||||
esp_err_t ret = iot_button_new_gpio_device(&btn_cfg, &gpio_cfg, &btn);
|
||||
assert(ret == ESP_OK);
|
||||
|
||||
ret = iot_button_register_cb(btn, BUTTON_PRESS_DOWN, NULL, button_event_cb, NULL);
|
||||
ret |= iot_button_register_cb(btn, BUTTON_PRESS_UP, NULL, button_event_cb, NULL);
|
||||
ret |= iot_button_register_cb(btn, BUTTON_PRESS_REPEAT, NULL, button_event_cb, NULL);
|
||||
ret |= iot_button_register_cb(btn, BUTTON_PRESS_REPEAT_DONE, NULL, button_event_cb, NULL);
|
||||
ret |= iot_button_register_cb(btn, BUTTON_SINGLE_CLICK, NULL, button_event_cb, NULL);
|
||||
ret |= iot_button_register_cb(btn, BUTTON_DOUBLE_CLICK, NULL, button_event_cb, NULL);
|
||||
ret |= iot_button_register_cb(btn, BUTTON_LONG_PRESS_START, NULL, button_event_cb, NULL);
|
||||
ret |= iot_button_register_cb(btn, BUTTON_LONG_PRESS_HOLD, NULL, button_event_cb, NULL);
|
||||
ret |= iot_button_register_cb(btn, BUTTON_LONG_PRESS_UP, NULL, button_event_cb, NULL);
|
||||
ret |= iot_button_register_cb(btn, BUTTON_PRESS_END, NULL, button_event_cb, NULL);
|
||||
|
||||
#if CONFIG_ENTER_LIGHT_SLEEP_MODE_MANUALLY
|
||||
/*!< For enter Power Save */
|
||||
button_power_save_config_t config = {
|
||||
.enter_power_save_cb = button_enter_power_save,
|
||||
};
|
||||
ret |= iot_button_register_power_save_cb(&config);
|
||||
#endif
|
||||
|
||||
ESP_ERROR_CHECK(ret);
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||
void power_save_init(void)
|
||||
{
|
||||
esp_pm_config_t pm_config = {
|
||||
.max_freq_mhz = CONFIG_EXAMPLE_MAX_CPU_FREQ_MHZ,
|
||||
.min_freq_mhz = CONFIG_EXAMPLE_MIN_CPU_FREQ_MHZ,
|
||||
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
|
||||
.light_sleep_enable = true
|
||||
#endif
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_pm_configure(&pm_config));
|
||||
}
|
||||
#else
|
||||
void power_save_init(void)
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
esp_pm_config_esp32_t pm_config = {
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
esp_pm_config_esp32s2_t pm_config = {
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
esp_pm_config_esp32c3_t pm_config = {
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
esp_pm_config_esp32s3_t pm_config = {
|
||||
#elif CONFIG_IDF_TARGET_ESP32C2
|
||||
esp_pm_config_esp32c2_t pm_config = {
|
||||
#endif
|
||||
.max_freq_mhz = CONFIG_EXAMPLE_MAX_CPU_FREQ_MHZ,
|
||||
.min_freq_mhz = CONFIG_EXAMPLE_MIN_CPU_FREQ_MHZ,
|
||||
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
|
||||
.light_sleep_enable = true
|
||||
#endif
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_pm_configure(&pm_config));
|
||||
}
|
||||
#endif
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
button_init(BOOT_BUTTON_NUM);
|
||||
#if CONFIG_ENTER_LIGHT_SLEEP_AUTO
|
||||
power_save_init();
|
||||
#else
|
||||
esp_light_sleep_start();
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_ENTER_LIGHT_SLEEP_MODE_MANUALLY=y\
|
||||
@@ -0,0 +1,11 @@
|
||||
# Enable support for power management
|
||||
CONFIG_PM_ENABLE=y
|
||||
# Enable tickless idle mode
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
# Put related source code in IRAM
|
||||
CONFIG_PM_SLP_IRAM_OPT=y
|
||||
CONFIG_PM_RTOS_IDLE_OPT=y
|
||||
# Use 1000Hz freertos tick to lower sleep time threshold
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
# For button power save
|
||||
CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE=y
|
||||
12
managed_components/espressif__button/idf_component.yml
Normal file
12
managed_components/espressif__button/idf_component.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
dependencies:
|
||||
cmake_utilities: '*'
|
||||
idf: '>=4.0'
|
||||
description: GPIO and ADC and Matrix button driver
|
||||
documentation: https://docs.espressif.com/projects/esp-iot-solution/en/latest/input_device/button.html
|
||||
issues: https://github.com/espressif/esp-iot-solution/issues
|
||||
repository: git://github.com/espressif/esp-iot-solution.git
|
||||
repository_info:
|
||||
commit_sha: 3339788bc154632d7cd8e37bd4b1a83155835eee
|
||||
path: components/button
|
||||
url: https://github.com/espressif/esp-iot-solution/tree/master/components/button
|
||||
version: 4.1.3
|
||||
57
managed_components/espressif__button/include/button_adc.h
Normal file
57
managed_components/espressif__button/include/button_adc.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_adc/adc_oneshot.h"
|
||||
#include "button_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief adc button configuration
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
adc_oneshot_unit_handle_t *adc_handle; /**< handle of adc unit, if NULL will create new one internal, else will use the handle */
|
||||
adc_unit_t unit_id; /**< ADC unit */
|
||||
uint8_t adc_channel; /**< Channel of ADC */
|
||||
uint8_t button_index; /**< button index on the channel */
|
||||
uint16_t min; /**< min voltage in mv corresponding to the button */
|
||||
uint16_t max; /**< max voltage in mv corresponding to the button */
|
||||
} button_adc_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create a new ADC button device
|
||||
*
|
||||
* This function initializes and configures a new ADC button device using the given configuration parameters.
|
||||
* It manages the ADC unit, channels, and button-specific parameters, and ensures proper resource allocation
|
||||
* for the ADC button object.
|
||||
*
|
||||
* @param[in] button_config Configuration for the button device, including callbacks and debounce parameters.
|
||||
* @param[in] adc_config Configuration for the ADC channel and button, including the ADC unit, channel,
|
||||
* button index, and voltage range (min and max).
|
||||
* @param[out] ret_button Handle to the newly created button device.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully created the ADC button device.
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument provided.
|
||||
* - ESP_ERR_NO_MEM: Memory allocation failed.
|
||||
* - ESP_ERR_INVALID_STATE: The requested button index or channel is already in use, or no channels are available.
|
||||
* - ESP_FAIL: Failed to initialize or configure the ADC or button device.
|
||||
*
|
||||
* @note
|
||||
* - If the ADC unit is not already configured, it will be initialized with the provided or default settings.
|
||||
* - If the ADC channel is not initialized, it will be configured for the specified unit and calibrated.
|
||||
* - This function ensures that ADC resources are reused whenever possible to optimize resource allocation.
|
||||
*/
|
||||
esp_err_t iot_button_new_adc_device(const button_config_t *button_config, const button_adc_config_t *adc_config, button_handle_t *ret_button);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
53
managed_components/espressif__button/include/button_gpio.h
Normal file
53
managed_components/espressif__button/include/button_gpio.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "button_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief gpio button configuration
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
int32_t gpio_num; /**< num of gpio */
|
||||
uint8_t active_level; /**< gpio level when press down */
|
||||
bool enable_power_save; /**< enable power save mode */
|
||||
bool disable_pull; /**< disable internal pull up or down */
|
||||
} button_gpio_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create a new GPIO button device
|
||||
*
|
||||
* This function initializes and configures a GPIO-based button device using the given configuration parameters.
|
||||
* It sets up the GPIO pin, configures its input mode, and optionally enables power-saving features or wake-up functionality.
|
||||
*
|
||||
* @param[in] button_config Configuration for the button device, including callbacks and debounce parameters.
|
||||
* @param[in] gpio_cfg Configuration for the GPIO, including the pin number, active level, and power-save options.
|
||||
* @param[out] ret_button Handle to the newly created GPIO button device.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully created the GPIO button device.
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument provided, such as an invalid GPIO number.
|
||||
* - ESP_ERR_NO_MEM: Memory allocation failed.
|
||||
* - ESP_ERR_INVALID_STATE: Failed to configure GPIO wake-up or interrupt settings.
|
||||
* - ESP_FAIL: General failure, such as unsupported wake-up configuration on the target.
|
||||
*
|
||||
* @note
|
||||
* - If power-saving is enabled, the GPIO will be configured as a wake-up source for light sleep.
|
||||
* - Pull-up or pull-down resistors are configured based on the `active_level` and the `disable_pull` flag.
|
||||
* - This function checks for the validity of the GPIO as a wake-up source when power-saving is enabled.
|
||||
* - If power-saving is not supported by the hardware or configuration, the function will return an error.
|
||||
*/
|
||||
esp_err_t iot_button_new_gpio_device(const button_config_t *button_config, const button_gpio_config_t *gpio_config, button_handle_t *ret_button);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
70
managed_components/espressif__button/include/button_matrix.h
Normal file
70
managed_components/espressif__button/include/button_matrix.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "button_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Button matrix key configuration.
|
||||
* Just need to configure the GPIO associated with this GPIO in the matrix keyboard.
|
||||
*
|
||||
* Matrix Keyboard Layout (3x3):
|
||||
* ----------------------------------------
|
||||
* | Button 1 | Button 2 | Button 3 |
|
||||
* | (R1-C1) | (R1-C2) | (R1-C3) |
|
||||
* |--------------------------------------|
|
||||
* | Button 4 | Button 5 | Button 6 |
|
||||
* | (R2-C1) | (R2-C2) | (R2-C3) |
|
||||
* |--------------------------------------|
|
||||
* | Button 7 | Button 8 | Button 9 |
|
||||
* | (R3-C1) | (R3-C2) | (R3-C3) |
|
||||
* ----------------------------------------
|
||||
*
|
||||
* - Button matrix key is driven using row scanning.
|
||||
* - Buttons within the same column cannot be detected simultaneously,
|
||||
* but buttons within the same row can be detected without conflicts.
|
||||
*/
|
||||
typedef struct {
|
||||
int32_t *row_gpios; /**< GPIO number list for the row */
|
||||
int32_t *col_gpios; /**< GPIO number list for the column */
|
||||
uint32_t row_gpio_num; /**< Number of GPIOs associated with the row */
|
||||
uint32_t col_gpio_num; /**< Number of GPIOs associated with the column */
|
||||
} button_matrix_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create a new button matrix device
|
||||
*
|
||||
* This function initializes and configures a button matrix device using the specified row and column GPIOs.
|
||||
* Each button in the matrix is represented as an independent button object, and its handle is returned in the `ret_button` array.
|
||||
*
|
||||
* @param[in] button_config Configuration for the button device, including callbacks and debounce parameters.
|
||||
* @param[in] matrix_config Configuration for the matrix, including row and column GPIOs and their counts.
|
||||
* @param[out] ret_button Array of handles for the buttons in the matrix.
|
||||
* @param[inout] size Pointer to the total number of buttons in the matrix. Must match the product of row and column GPIO counts.
|
||||
* On success, this value is updated to reflect the size of the button matrix.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully created the button matrix device.
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument provided, such as null pointers or mismatched matrix dimensions.
|
||||
* - ESP_ERR_NO_MEM: Memory allocation failed.
|
||||
* - ESP_FAIL: General failure, such as button creation failure for one or more buttons.
|
||||
*
|
||||
* @note
|
||||
* - Each row GPIO is configured as an output, while each column GPIO is configured as an input.
|
||||
* - The total number of buttons in the matrix must equal the product of the row and column GPIO counts.
|
||||
* - The `ret_button` array must be large enough to store handles for all buttons in the matrix.
|
||||
* - If any button creation fails, the function will free all allocated resources and return an error.
|
||||
*/
|
||||
esp_err_t iot_button_new_matrix_device(const button_config_t *button_config, const button_matrix_config_t *matrix_config, button_handle_t *ret_button, size_t *size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
57
managed_components/espressif__button/include/button_types.h
Normal file
57
managed_components/espressif__button/include/button_types.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "button_interface.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
BUTTON_INACTIVE = 0,
|
||||
BUTTON_ACTIVE,
|
||||
};
|
||||
|
||||
typedef struct button_dev_t *button_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Button configuration
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
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_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create a new IoT button instance
|
||||
*
|
||||
* This function initializes a new button instance with the specified configuration
|
||||
* and driver. It also sets up internal resources such as the button timer if not
|
||||
* already initialized.
|
||||
*
|
||||
* @param[in] config Pointer to the button configuration structure
|
||||
* @param[in] driver Pointer to the button driver structure
|
||||
* @param[out] ret_button Pointer to where the handle of the created button will be stored
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully created the button
|
||||
* - ESP_ERR_INVALID_ARG: Invalid arguments passed to the function
|
||||
* - ESP_ERR_NO_MEM: Memory allocation failed
|
||||
*
|
||||
* @note
|
||||
* - The first call to this function logs the IoT Button version.
|
||||
* - The function initializes a global button timer if it is not already running.
|
||||
* - Timer is started only if the driver does not enable power-saving mode.
|
||||
*/
|
||||
esp_err_t iot_button_create(const button_config_t *config, const button_driver_t *driver, button_handle_t *ret_button);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
265
managed_components/espressif__button/include/iot_button.h
Normal file
265
managed_components/espressif__button/include/iot_button.h
Normal file
@@ -0,0 +1,265 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_err.h"
|
||||
#include "button_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void (* button_cb_t)(void *button_handle, void *usr_data);
|
||||
|
||||
typedef void (* button_power_save_cb_t)(void *usr_data);
|
||||
|
||||
/**
|
||||
* @brief Structs to store power save callback info
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
button_power_save_cb_t enter_power_save_cb; /**< Callback function when entering power save mode */
|
||||
void *usr_data; /**< User data for the callback */
|
||||
} button_power_save_config_t;
|
||||
|
||||
/**
|
||||
* @brief Button events
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
BUTTON_PRESS_DOWN = 0,
|
||||
BUTTON_PRESS_UP,
|
||||
BUTTON_PRESS_REPEAT,
|
||||
BUTTON_PRESS_REPEAT_DONE,
|
||||
BUTTON_SINGLE_CLICK,
|
||||
BUTTON_DOUBLE_CLICK,
|
||||
BUTTON_MULTIPLE_CLICK,
|
||||
BUTTON_LONG_PRESS_START,
|
||||
BUTTON_LONG_PRESS_HOLD,
|
||||
BUTTON_LONG_PRESS_UP,
|
||||
BUTTON_PRESS_END,
|
||||
BUTTON_EVENT_MAX,
|
||||
BUTTON_NONE_PRESS,
|
||||
} button_event_t;
|
||||
|
||||
/**
|
||||
* @brief Button events arg
|
||||
*
|
||||
*/
|
||||
typedef union {
|
||||
/**
|
||||
* @brief Long press time event data
|
||||
*
|
||||
*/
|
||||
struct long_press_t {
|
||||
uint16_t press_time; /**< press time(ms) for the corresponding callback to trigger */
|
||||
} long_press; /**< long press struct, for event BUTTON_LONG_PRESS_START and BUTTON_LONG_PRESS_UP */
|
||||
|
||||
/**
|
||||
* @brief Multiple clicks event data
|
||||
*
|
||||
*/
|
||||
struct multiple_clicks_t {
|
||||
uint16_t clicks; /**< number of clicks, to trigger the callback */
|
||||
} multiple_clicks; /**< multiple clicks struct, for event BUTTON_MULTIPLE_CLICK */
|
||||
} button_event_args_t;
|
||||
|
||||
/**
|
||||
* @brief Button parameter
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
BUTTON_LONG_PRESS_TIME_MS = 0,
|
||||
BUTTON_SHORT_PRESS_TIME_MS,
|
||||
BUTTON_PARAM_MAX,
|
||||
} button_param_t;
|
||||
|
||||
/**
|
||||
* @brief Delete a button
|
||||
*
|
||||
* @param btn_handle A button handle to delete
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Failure
|
||||
*/
|
||||
esp_err_t iot_button_delete(button_handle_t btn_handle);
|
||||
|
||||
/**
|
||||
* @brief Register the button event callback function.
|
||||
*
|
||||
* @param btn_handle A button handle to register
|
||||
* @param event Button event
|
||||
* @param event_args Button event arguments
|
||||
* @param cb Callback function.
|
||||
* @param usr_data user data
|
||||
*
|
||||
* @return
|
||||
* - 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_cb(button_handle_t btn_handle, button_event_t event, button_event_args_t *event_args, button_cb_t cb, void *usr_data);
|
||||
|
||||
/**
|
||||
* @brief Unregister all the callbacks associated with the event.
|
||||
*
|
||||
* @param btn_handle A button handle to unregister
|
||||
* @param event Button event
|
||||
* @param event_args Used for unregistering a specific callback.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG Arguments is invalid.
|
||||
* - ESP_ERR_INVALID_STATE No callbacks registered for the event
|
||||
*/
|
||||
esp_err_t iot_button_unregister_cb(button_handle_t btn_handle, button_event_t event, button_event_args_t *event_args);
|
||||
|
||||
/**
|
||||
* @brief counts total callbacks registered
|
||||
*
|
||||
* @param btn_handle A button handle to the button
|
||||
*
|
||||
* @return
|
||||
* - 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_cb(button_handle_t btn_handle);
|
||||
|
||||
/**
|
||||
* @brief how many callbacks are registered for the event
|
||||
*
|
||||
* @param btn_handle A button handle to the button
|
||||
*
|
||||
* @param event Button event
|
||||
*
|
||||
* @return
|
||||
* - 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_cb(button_handle_t btn_handle, button_event_t event);
|
||||
|
||||
/**
|
||||
* @brief Get button event
|
||||
*
|
||||
* @param btn_handle Button handle
|
||||
*
|
||||
* @return Current button event. See button_event_t
|
||||
*/
|
||||
button_event_t iot_button_get_event(button_handle_t btn_handle);
|
||||
|
||||
/**
|
||||
* @brief 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".
|
||||
*
|
||||
* @param[in] event The button event to be converted to a string.
|
||||
*
|
||||
* @return
|
||||
* - Pointer to the event string if the event is valid.
|
||||
* - "invalid event" if the event value is invalid.
|
||||
*/
|
||||
const char *iot_button_get_event_str(button_event_t event);
|
||||
|
||||
/**
|
||||
* @brief Log the current button event as a string.
|
||||
*
|
||||
* This function prints the string representation of the current event associated with the button.
|
||||
*
|
||||
* @param[in] btn_handle Handle to the button object.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully logged the event string.
|
||||
* - ESP_FAIL: Invalid button handle.
|
||||
*/
|
||||
esp_err_t iot_button_print_event(button_handle_t btn_handle);
|
||||
|
||||
/**
|
||||
* @brief Get button repeat times
|
||||
*
|
||||
* @param btn_handle Button handle
|
||||
*
|
||||
* @return button pressed times. For example, double-click return 2, triple-click return 3, etc.
|
||||
*/
|
||||
uint8_t iot_button_get_repeat(button_handle_t btn_handle);
|
||||
|
||||
/**
|
||||
* @brief Get button ticks time
|
||||
*
|
||||
* @param btn_handle Button handle
|
||||
*
|
||||
* @return Actual time from press down to up (ms).
|
||||
*/
|
||||
uint32_t iot_button_get_ticks_time(button_handle_t btn_handle);
|
||||
|
||||
/**
|
||||
* @brief Get button long press hold count
|
||||
*
|
||||
* @param btn_handle Button handle
|
||||
*
|
||||
* @return Count of trigger cb(BUTTON_LONG_PRESS_HOLD)
|
||||
*/
|
||||
uint16_t iot_button_get_long_press_hold_cnt(button_handle_t btn_handle);
|
||||
|
||||
/**
|
||||
* @brief Dynamically change the parameters of the iot button
|
||||
*
|
||||
* @param btn_handle Button handle
|
||||
* @param param Button parameter
|
||||
* @param value new value
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG Arguments is invalid.
|
||||
*/
|
||||
esp_err_t iot_button_set_param(button_handle_t btn_handle, button_param_t param, void *value);
|
||||
|
||||
/**
|
||||
* @brief Get button key level
|
||||
*
|
||||
* @param btn_handle Button handle
|
||||
* @return
|
||||
* - 1 if key is pressed
|
||||
* - 0 if key is released or invalid button handle
|
||||
*/
|
||||
uint8_t iot_button_get_key_level(button_handle_t btn_handle);
|
||||
|
||||
/**
|
||||
* @brief resume button timer, if button timer is stopped. Make sure iot_button_create() is called before calling this API.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE timer state is invalid.
|
||||
*/
|
||||
esp_err_t iot_button_resume(void);
|
||||
|
||||
/**
|
||||
* @brief stop button timer, if button timer is running. Make sure iot_button_create() is called before calling this API.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE timer state is invalid
|
||||
*/
|
||||
esp_err_t iot_button_stop(void);
|
||||
|
||||
/**
|
||||
* @brief Register a callback function for power saving.
|
||||
* The config->enter_power_save_cb function will be called when all keys stop working.
|
||||
*
|
||||
* @param config Button power save config
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE No button registered
|
||||
* - ESP_ERR_INVALID_ARG Arguments is invalid
|
||||
* - ESP_ERR_NO_MEM Not enough memory
|
||||
*/
|
||||
esp_err_t iot_button_register_power_save_cb(const button_power_save_config_t *config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct button_driver_t button_driver_t; /*!< Type of button object */
|
||||
|
||||
struct button_driver_t {
|
||||
/*!< (optional) Need Support Power Save */
|
||||
bool enable_power_save;
|
||||
|
||||
/*!< (necessary) Get key level */
|
||||
uint8_t (*get_key_level)(button_driver_t *button_driver);
|
||||
|
||||
/*!< (optional) Enter Power Save cb */
|
||||
esp_err_t (*enter_power_save)(button_driver_t *button_driver);
|
||||
|
||||
/*!< (optional) Del the hardware driver and cleanup */
|
||||
esp_err_t (*del)(button_driver_t *button_driver);
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
711
managed_components/espressif__button/iot_button.c
Normal file
711
managed_components/espressif__button/iot_button.c
Normal file
@@ -0,0 +1,711 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/timers.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "iot_button.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "button_interface.h"
|
||||
|
||||
static const char *TAG = "button";
|
||||
static portMUX_TYPE s_button_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||
#define BUTTON_ENTER_CRITICAL() portENTER_CRITICAL(&s_button_lock)
|
||||
#define BUTTON_EXIT_CRITICAL() portEXIT_CRITICAL(&s_button_lock)
|
||||
|
||||
#define BTN_CHECK(a, str, ret_val) \
|
||||
if (!(a)) { \
|
||||
ESP_LOGE(TAG, "%s(%d): %s", __FUNCTION__, __LINE__, str); \
|
||||
return (ret_val); \
|
||||
}
|
||||
|
||||
static const char *button_event_str[] = {
|
||||
"BUTTON_PRESS_DOWN",
|
||||
"BUTTON_PRESS_UP",
|
||||
"BUTTON_PRESS_REPEAT",
|
||||
"BUTTON_PRESS_REPEAT_DONE",
|
||||
"BUTTON_SINGLE_CLICK",
|
||||
"BUTTON_DOUBLE_CLICK",
|
||||
"BUTTON_MULTIPLE_CLICK",
|
||||
"BUTTON_LONG_PRESS_START",
|
||||
"BUTTON_LONG_PRESS_HOLD",
|
||||
"BUTTON_LONG_PRESS_UP",
|
||||
"BUTTON_PRESS_END",
|
||||
"BUTTON_EVENT_MAX",
|
||||
"BUTTON_NONE_PRESS",
|
||||
};
|
||||
|
||||
enum {
|
||||
PRESS_DOWN_CHECK = 0,
|
||||
PRESS_UP_CHECK,
|
||||
PRESS_REPEAT_DOWN_CHECK,
|
||||
PRESS_REPEAT_UP_CHECK,
|
||||
PRESS_LONG_PRESS_UP_CHECK,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Structs to store callback info
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
button_cb_t cb;
|
||||
void *usr_data;
|
||||
button_event_args_t event_args;
|
||||
} button_cb_info_t;
|
||||
|
||||
/**
|
||||
* @brief Structs to record individual key parameters
|
||||
*
|
||||
*/
|
||||
typedef struct button_dev_t {
|
||||
uint32_t ticks; /*!< Count for the current button state. */
|
||||
uint32_t long_press_ticks; /*!< Trigger ticks for long press, */
|
||||
uint32_t short_press_ticks; /*!< Trigger ticks for repeat press */
|
||||
uint32_t long_press_hold_cnt; /*!< Record long press hold count */
|
||||
uint8_t repeat;
|
||||
uint8_t state: 3;
|
||||
uint8_t debounce_cnt: 4; /*!< Max 15 */
|
||||
uint8_t button_level: 1;
|
||||
button_event_t event;
|
||||
button_driver_t *driver;
|
||||
button_cb_info_t *cb_info[BUTTON_EVENT_MAX];
|
||||
size_t size[BUTTON_EVENT_MAX];
|
||||
int count[2];
|
||||
struct button_dev_t *next;
|
||||
} button_dev_t;
|
||||
|
||||
//button handle list head.
|
||||
static button_dev_t *g_head_handle = NULL;
|
||||
static esp_timer_handle_t g_button_timer_handle = NULL;
|
||||
static bool g_is_timer_running = false;
|
||||
static button_power_save_config_t power_save_usr_cfg = {0};
|
||||
|
||||
#define TICKS_INTERVAL CONFIG_BUTTON_PERIOD_TIME_MS
|
||||
#define DEBOUNCE_TICKS CONFIG_BUTTON_DEBOUNCE_TICKS //MAX 8
|
||||
#define SHORT_TICKS (CONFIG_BUTTON_SHORT_PRESS_TIME_MS /TICKS_INTERVAL)
|
||||
#define LONG_TICKS (CONFIG_BUTTON_LONG_PRESS_TIME_MS /TICKS_INTERVAL)
|
||||
#define SERIAL_TICKS (CONFIG_BUTTON_LONG_PRESS_HOLD_SERIAL_TIME_MS /TICKS_INTERVAL)
|
||||
#define TOLERANCE (CONFIG_BUTTON_PERIOD_TIME_MS*4)
|
||||
|
||||
#define CALL_EVENT_CB(ev) \
|
||||
if (btn->cb_info[ev]) { \
|
||||
for (int i = 0; i < btn->size[ev]; i++) { \
|
||||
btn->cb_info[ev][i].cb(btn, btn->cb_info[ev][i].usr_data); \
|
||||
} \
|
||||
} \
|
||||
|
||||
#define TIME_TO_TICKS(time, congfig_time) (0 == (time))?congfig_time:(((time) / TICKS_INTERVAL))?((time) / TICKS_INTERVAL):1
|
||||
|
||||
/**
|
||||
* @brief Button driver core function, driver state machine.
|
||||
*/
|
||||
static void button_handler(button_dev_t *btn)
|
||||
{
|
||||
uint8_t read_gpio_level = btn->driver->get_key_level(btn->driver);
|
||||
|
||||
/** ticks counter working.. */
|
||||
if ((btn->state) > 0) {
|
||||
btn->ticks++;
|
||||
}
|
||||
|
||||
/**< button debounce handle */
|
||||
if (read_gpio_level != btn->button_level) {
|
||||
if (++(btn->debounce_cnt) >= DEBOUNCE_TICKS) {
|
||||
btn->button_level = read_gpio_level;
|
||||
btn->debounce_cnt = 0;
|
||||
}
|
||||
} else {
|
||||
btn->debounce_cnt = 0;
|
||||
}
|
||||
|
||||
/** State machine */
|
||||
switch (btn->state) {
|
||||
case PRESS_DOWN_CHECK:
|
||||
if (btn->button_level == BUTTON_ACTIVE) {
|
||||
btn->event = (uint8_t)BUTTON_PRESS_DOWN;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_DOWN);
|
||||
btn->ticks = 0;
|
||||
btn->repeat = 1;
|
||||
btn->state = PRESS_UP_CHECK;
|
||||
} else {
|
||||
btn->event = (uint8_t)BUTTON_NONE_PRESS;
|
||||
}
|
||||
break;
|
||||
|
||||
case PRESS_UP_CHECK:
|
||||
if (btn->button_level != BUTTON_ACTIVE) {
|
||||
btn->event = (uint8_t)BUTTON_PRESS_UP;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_UP);
|
||||
btn->ticks = 0;
|
||||
btn->state = PRESS_REPEAT_DOWN_CHECK;
|
||||
|
||||
} else if (btn->ticks >= btn->long_press_ticks) {
|
||||
btn->event = (uint8_t)BUTTON_LONG_PRESS_START;
|
||||
btn->state = PRESS_LONG_PRESS_UP_CHECK;
|
||||
/** Calling callbacks for BUTTON_LONG_PRESS_START */
|
||||
uint32_t ticks_time = iot_button_get_ticks_time(btn);
|
||||
int32_t diff = ticks_time - btn->long_press_ticks * TICKS_INTERVAL;
|
||||
if (btn->cb_info[btn->event] && btn->count[0] == 0) {
|
||||
if (abs(diff) <= TOLERANCE && btn->cb_info[btn->event][btn->count[0]].event_args.long_press.press_time == (btn->long_press_ticks * TICKS_INTERVAL)) {
|
||||
do {
|
||||
btn->cb_info[btn->event][btn->count[0]].cb(btn, btn->cb_info[btn->event][btn->count[0]].usr_data);
|
||||
btn->count[0]++;
|
||||
if (btn->count[0] >= btn->size[btn->event]) {
|
||||
break;
|
||||
}
|
||||
} while (btn->cb_info[btn->event][btn->count[0]].event_args.long_press.press_time == btn->long_press_ticks * TICKS_INTERVAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PRESS_REPEAT_DOWN_CHECK:
|
||||
if (btn->button_level == BUTTON_ACTIVE) {
|
||||
btn->event = (uint8_t)BUTTON_PRESS_DOWN;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_DOWN);
|
||||
btn->event = (uint8_t)BUTTON_PRESS_REPEAT;
|
||||
btn->repeat++;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_REPEAT); // repeat hit
|
||||
btn->ticks = 0;
|
||||
btn->state = PRESS_REPEAT_UP_CHECK;
|
||||
} else if (btn->ticks > btn->short_press_ticks) {
|
||||
if (btn->repeat == 1) {
|
||||
btn->event = (uint8_t)BUTTON_SINGLE_CLICK;
|
||||
CALL_EVENT_CB(BUTTON_SINGLE_CLICK);
|
||||
} else if (btn->repeat == 2) {
|
||||
btn->event = (uint8_t)BUTTON_DOUBLE_CLICK;
|
||||
CALL_EVENT_CB(BUTTON_DOUBLE_CLICK); // repeat hit
|
||||
}
|
||||
|
||||
btn->event = (uint8_t)BUTTON_MULTIPLE_CLICK;
|
||||
|
||||
/** Calling the callbacks for MULTIPLE BUTTON CLICKS */
|
||||
for (int i = 0; i < btn->size[btn->event]; i++) {
|
||||
if (btn->repeat == btn->cb_info[btn->event][i].event_args.multiple_clicks.clicks) {
|
||||
do {
|
||||
btn->cb_info[btn->event][i].cb(btn, btn->cb_info[btn->event][i].usr_data);
|
||||
i++;
|
||||
if (i >= btn->size[btn->event]) {
|
||||
break;
|
||||
}
|
||||
} while (btn->cb_info[btn->event][i].event_args.multiple_clicks.clicks == btn->repeat);
|
||||
}
|
||||
}
|
||||
|
||||
btn->event = (uint8_t)BUTTON_PRESS_REPEAT_DONE;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_REPEAT_DONE); // repeat hit
|
||||
btn->repeat = 0;
|
||||
btn->state = 0;
|
||||
btn->event = (uint8_t)BUTTON_PRESS_END;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_END);
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (btn->button_level != BUTTON_ACTIVE) {
|
||||
btn->event = (uint8_t)BUTTON_PRESS_UP;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_UP);
|
||||
if (btn->ticks < btn->short_press_ticks) {
|
||||
btn->ticks = 0;
|
||||
btn->state = PRESS_REPEAT_DOWN_CHECK; //repeat press
|
||||
} else {
|
||||
btn->state = PRESS_DOWN_CHECK;
|
||||
btn->event = (uint8_t)BUTTON_PRESS_END;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_END);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PRESS_LONG_PRESS_UP_CHECK:
|
||||
if (btn->button_level == BUTTON_ACTIVE) {
|
||||
//continue hold trigger
|
||||
if (btn->ticks >= (btn->long_press_hold_cnt + 1) * SERIAL_TICKS + btn->long_press_ticks) {
|
||||
btn->event = (uint8_t)BUTTON_LONG_PRESS_HOLD;
|
||||
btn->long_press_hold_cnt++;
|
||||
CALL_EVENT_CB(BUTTON_LONG_PRESS_HOLD);
|
||||
}
|
||||
|
||||
/** Calling callbacks for BUTTON_LONG_PRESS_START based on press_time */
|
||||
uint32_t ticks_time = iot_button_get_ticks_time(btn);
|
||||
if (btn->cb_info[BUTTON_LONG_PRESS_START]) {
|
||||
button_cb_info_t *cb_info = btn->cb_info[BUTTON_LONG_PRESS_START];
|
||||
uint16_t time = cb_info[btn->count[0]].event_args.long_press.press_time;
|
||||
if (btn->long_press_ticks * TICKS_INTERVAL > time) {
|
||||
for (int i = btn->count[0] + 1; i < btn->size[BUTTON_LONG_PRESS_START]; i++) {
|
||||
time = cb_info[i].event_args.long_press.press_time;
|
||||
if (btn->long_press_ticks * TICKS_INTERVAL <= time) {
|
||||
btn->count[0] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (btn->count[0] < btn->size[BUTTON_LONG_PRESS_START] && abs((int)ticks_time - (int)time) <= TOLERANCE) {
|
||||
btn->event = (uint8_t)BUTTON_LONG_PRESS_START;
|
||||
do {
|
||||
cb_info[btn->count[0]].cb(btn, cb_info[btn->count[0]].usr_data);
|
||||
btn->count[0]++;
|
||||
if (btn->count[0] >= btn->size[BUTTON_LONG_PRESS_START]) {
|
||||
break;
|
||||
}
|
||||
} while (time == cb_info[btn->count[0]].event_args.long_press.press_time);
|
||||
}
|
||||
}
|
||||
|
||||
/** Updating counter for BUTTON_LONG_PRESS_UP press_time */
|
||||
if (btn->cb_info[BUTTON_LONG_PRESS_UP]) {
|
||||
button_cb_info_t *cb_info = btn->cb_info[BUTTON_LONG_PRESS_UP];
|
||||
uint16_t time = cb_info[btn->count[1] + 1].event_args.long_press.press_time;
|
||||
if (btn->long_press_ticks * TICKS_INTERVAL > time) {
|
||||
for (int i = btn->count[1] + 1; i < btn->size[BUTTON_LONG_PRESS_UP]; i++) {
|
||||
time = cb_info[i].event_args.long_press.press_time;
|
||||
if (btn->long_press_ticks * TICKS_INTERVAL <= time) {
|
||||
btn->count[1] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (btn->count[1] + 1 < btn->size[BUTTON_LONG_PRESS_UP] && abs((int)ticks_time - (int)time) <= TOLERANCE) {
|
||||
do {
|
||||
btn->count[1]++;
|
||||
if (btn->count[1] + 1 >= btn->size[BUTTON_LONG_PRESS_UP]) {
|
||||
break;
|
||||
}
|
||||
} while (time == cb_info[btn->count[1] + 1].event_args.long_press.press_time);
|
||||
}
|
||||
}
|
||||
} else { //releasd
|
||||
|
||||
btn->event = BUTTON_LONG_PRESS_UP;
|
||||
|
||||
/** calling callbacks for BUTTON_LONG_PRESS_UP press_time */
|
||||
if (btn->cb_info[btn->event] && btn->count[1] >= 0) {
|
||||
button_cb_info_t *cb_info = btn->cb_info[btn->event];
|
||||
do {
|
||||
cb_info[btn->count[1]].cb(btn, cb_info[btn->count[1]].usr_data);
|
||||
if (!btn->count[1]) {
|
||||
break;
|
||||
}
|
||||
btn->count[1]--;
|
||||
} while (cb_info[btn->count[1]].event_args.long_press.press_time == cb_info[btn->count[1] + 1].event_args.long_press.press_time);
|
||||
|
||||
/** Reset the counter */
|
||||
btn->count[1] = -1;
|
||||
}
|
||||
/** Reset counter */
|
||||
if (btn->cb_info[BUTTON_LONG_PRESS_START]) {
|
||||
btn->count[0] = 0;
|
||||
}
|
||||
|
||||
btn->event = (uint8_t)BUTTON_PRESS_UP;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_UP);
|
||||
btn->state = PRESS_DOWN_CHECK; //reset
|
||||
btn->long_press_hold_cnt = 0;
|
||||
btn->event = (uint8_t)BUTTON_PRESS_END;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_END);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void button_cb(void *args)
|
||||
{
|
||||
button_dev_t *target;
|
||||
/*!< When all buttons enter the BUTTON_NONE_PRESS state, the system enters low-power mode */
|
||||
bool enter_power_save_flag = true;
|
||||
for (target = g_head_handle; target; target = target->next) {
|
||||
button_handler(target);
|
||||
if (!(target->driver->enable_power_save && target->debounce_cnt == 0 && target->event == BUTTON_NONE_PRESS)) {
|
||||
enter_power_save_flag = false;
|
||||
}
|
||||
}
|
||||
if (enter_power_save_flag) {
|
||||
/*!< Stop esp timer for power save */
|
||||
if (g_is_timer_running) {
|
||||
esp_timer_stop(g_button_timer_handle);
|
||||
g_is_timer_running = false;
|
||||
}
|
||||
for (target = g_head_handle; target; target = target->next) {
|
||||
if (target->driver->enable_power_save && target->driver->enter_power_save) {
|
||||
target->driver->enter_power_save(target->driver);
|
||||
}
|
||||
}
|
||||
/*!< Notify the user that the Button has entered power save mode by calling this callback function. */
|
||||
if (power_save_usr_cfg.enter_power_save_cb) {
|
||||
power_save_usr_cfg.enter_power_save_cb(power_save_usr_cfg.usr_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t iot_button_register_cb(button_handle_t btn_handle, button_event_t event, button_event_args_t *event_args, button_cb_t cb, void *usr_data)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(NULL != btn_handle, ESP_ERR_INVALID_ARG, TAG, "Pointer of handle is invalid");
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
ESP_RETURN_ON_FALSE(event < BUTTON_EVENT_MAX, ESP_ERR_INVALID_ARG, TAG, "event is invalid");
|
||||
ESP_RETURN_ON_FALSE(NULL != cb, ESP_ERR_INVALID_ARG, TAG, "Pointer of cb is invalid");
|
||||
ESP_RETURN_ON_FALSE(event != BUTTON_MULTIPLE_CLICK || event_args, ESP_ERR_INVALID_ARG, TAG, "event is invalid");
|
||||
|
||||
if (event_args) {
|
||||
ESP_RETURN_ON_FALSE(!(event == BUTTON_LONG_PRESS_START || event == BUTTON_LONG_PRESS_UP) || event_args->long_press.press_time > btn->short_press_ticks * TICKS_INTERVAL, ESP_ERR_INVALID_ARG, TAG, "event_args is invalid");
|
||||
ESP_RETURN_ON_FALSE(event != BUTTON_MULTIPLE_CLICK || event_args->multiple_clicks.clicks, ESP_ERR_INVALID_ARG, TAG, "event_args is invalid");
|
||||
}
|
||||
|
||||
if (!btn->cb_info[event]) {
|
||||
btn->cb_info[event] = calloc(1, sizeof(button_cb_info_t));
|
||||
BTN_CHECK(NULL != btn->cb_info[event], "calloc cb_info failed", ESP_ERR_NO_MEM);
|
||||
if (event == BUTTON_LONG_PRESS_START) {
|
||||
btn->count[0] = 0;
|
||||
} else if (event == BUTTON_LONG_PRESS_UP) {
|
||||
btn->count[1] = -1;
|
||||
}
|
||||
} else {
|
||||
button_cb_info_t *p = realloc(btn->cb_info[event], sizeof(button_cb_info_t) * (btn->size[event] + 1));
|
||||
BTN_CHECK(NULL != p, "realloc cb_info failed", ESP_ERR_NO_MEM);
|
||||
btn->cb_info[event] = p;
|
||||
}
|
||||
|
||||
btn->cb_info[event][btn->size[event]].cb = cb;
|
||||
btn->cb_info[event][btn->size[event]].usr_data = usr_data;
|
||||
btn->size[event]++;
|
||||
|
||||
/** Inserting the event_args in sorted manner */
|
||||
if (event == BUTTON_LONG_PRESS_START || event == BUTTON_LONG_PRESS_UP) {
|
||||
uint16_t press_time = btn->long_press_ticks * TICKS_INTERVAL;
|
||||
if (event_args) {
|
||||
press_time = event_args->long_press.press_time;
|
||||
}
|
||||
BTN_CHECK(press_time / TICKS_INTERVAL > btn->short_press_ticks, "press_time event_args is less than short_press_ticks", ESP_ERR_INVALID_ARG);
|
||||
if (btn->size[event] >= 2) {
|
||||
for (int i = btn->size[event] - 2; i >= 0; i--) {
|
||||
if (btn->cb_info[event][i].event_args.long_press.press_time > press_time) {
|
||||
btn->cb_info[event][i + 1] = btn->cb_info[event][i];
|
||||
|
||||
btn->cb_info[event][i].event_args.long_press.press_time = press_time;
|
||||
btn->cb_info[event][i].cb = cb;
|
||||
btn->cb_info[event][i].usr_data = usr_data;
|
||||
} else {
|
||||
btn->cb_info[event][i + 1].event_args.long_press.press_time = press_time;
|
||||
btn->cb_info[event][i + 1].cb = cb;
|
||||
btn->cb_info[event][i + 1].usr_data = usr_data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
btn->cb_info[event][btn->size[event] - 1].event_args.long_press.press_time = press_time;
|
||||
}
|
||||
|
||||
int32_t press_ticks = press_time / TICKS_INTERVAL;
|
||||
if (btn->short_press_ticks < press_ticks && press_ticks < btn->long_press_ticks) {
|
||||
iot_button_set_param(btn, BUTTON_LONG_PRESS_TIME_MS, (void*)(intptr_t)press_time);
|
||||
}
|
||||
}
|
||||
|
||||
if (event == BUTTON_MULTIPLE_CLICK) {
|
||||
uint16_t clicks = btn->long_press_ticks * TICKS_INTERVAL;
|
||||
if (event_args) {
|
||||
clicks = event_args->multiple_clicks.clicks;
|
||||
}
|
||||
if (btn->size[event] >= 2) {
|
||||
for (int i = btn->size[event] - 2; i >= 0; i--) {
|
||||
if (btn->cb_info[event][i].event_args.multiple_clicks.clicks > clicks) {
|
||||
btn->cb_info[event][i + 1] = btn->cb_info[event][i];
|
||||
|
||||
btn->cb_info[event][i].event_args.multiple_clicks.clicks = clicks;
|
||||
btn->cb_info[event][i].cb = cb;
|
||||
btn->cb_info[event][i].usr_data = usr_data;
|
||||
} else {
|
||||
btn->cb_info[event][i + 1].event_args.multiple_clicks.clicks = clicks;
|
||||
btn->cb_info[event][i + 1].cb = cb;
|
||||
btn->cb_info[event][i + 1].usr_data = usr_data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
btn->cb_info[event][btn->size[event] - 1].event_args.multiple_clicks.clicks = clicks;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_unregister_cb(button_handle_t btn_handle, button_event_t event, button_event_args_t *event_args)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(NULL != btn_handle, ESP_ERR_INVALID_ARG, TAG, "Pointer of handle is invalid");
|
||||
ESP_RETURN_ON_FALSE(event < BUTTON_EVENT_MAX, ESP_ERR_INVALID_ARG, TAG, "event is invalid");
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
ESP_RETURN_ON_FALSE(btn->cb_info[event], ESP_ERR_INVALID_STATE, TAG, "No callbacks registered for the event");
|
||||
|
||||
int check = -1;
|
||||
|
||||
if ((event == BUTTON_LONG_PRESS_START || event == BUTTON_LONG_PRESS_UP) && event_args) {
|
||||
if (event_args->long_press.press_time != 0) {
|
||||
goto unregister_event;
|
||||
}
|
||||
}
|
||||
|
||||
if (event == BUTTON_MULTIPLE_CLICK && event_args) {
|
||||
if (event_args->multiple_clicks.clicks != 0) {
|
||||
goto unregister_event;
|
||||
}
|
||||
}
|
||||
|
||||
if (btn->cb_info[event]) {
|
||||
free(btn->cb_info[event]);
|
||||
|
||||
/** Reset the counter */
|
||||
if (event == BUTTON_LONG_PRESS_START) {
|
||||
btn->count[0] = 0;
|
||||
} else if (event == BUTTON_LONG_PRESS_UP) {
|
||||
btn->count[1] = -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
btn->cb_info[event] = NULL;
|
||||
btn->size[event] = 0;
|
||||
return ESP_OK;
|
||||
|
||||
unregister_event:
|
||||
|
||||
for (int i = 0; i < btn->size[event]; i++) {
|
||||
if ((event == BUTTON_LONG_PRESS_START || event == BUTTON_LONG_PRESS_UP) && event_args->long_press.press_time) {
|
||||
if (event_args->long_press.press_time != btn->cb_info[event][i].event_args.long_press.press_time) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (event == BUTTON_MULTIPLE_CLICK && event_args->multiple_clicks.clicks) {
|
||||
if (event_args->multiple_clicks.clicks != btn->cb_info[event][i].event_args.multiple_clicks.clicks) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
check = i;
|
||||
for (int j = i; j <= btn->size[event] - 1; j++) {
|
||||
btn->cb_info[event][j] = btn->cb_info[event][j + 1];
|
||||
}
|
||||
|
||||
if (btn->size[event] != 1) {
|
||||
button_cb_info_t *p = realloc(btn->cb_info[event], sizeof(button_cb_info_t) * (btn->size[event] - 1));
|
||||
BTN_CHECK(NULL != p, "realloc cb_info failed", ESP_ERR_NO_MEM);
|
||||
btn->cb_info[event] = p;
|
||||
btn->size[event]--;
|
||||
} else {
|
||||
free(btn->cb_info[event]);
|
||||
btn->cb_info[event] = NULL;
|
||||
btn->size[event] = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ESP_RETURN_ON_FALSE(check != -1, ESP_ERR_NOT_FOUND, TAG, "No such callback registered for the event");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
size_t iot_button_count_cb(button_handle_t btn_handle)
|
||||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
size_t ret = 0;
|
||||
for (size_t i = 0; i < BUTTON_EVENT_MAX; i++) {
|
||||
if (btn->cb_info[i]) {
|
||||
ret += btn->size[i];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t iot_button_count_event_cb(button_handle_t btn_handle, button_event_t event)
|
||||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
return btn->size[event];
|
||||
}
|
||||
|
||||
button_event_t iot_button_get_event(button_handle_t btn_handle)
|
||||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", BUTTON_NONE_PRESS);
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
return btn->event;
|
||||
}
|
||||
|
||||
const char *iot_button_get_event_str(button_event_t event)
|
||||
{
|
||||
BTN_CHECK(event <= BUTTON_NONE_PRESS && event >= BUTTON_PRESS_DOWN, "event value is invalid", "invalid event");
|
||||
return button_event_str[event];
|
||||
}
|
||||
|
||||
esp_err_t iot_button_print_event(button_handle_t btn_handle)
|
||||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_FAIL);
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
ESP_LOGI(TAG, "%s", button_event_str[btn->event]);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint8_t iot_button_get_repeat(button_handle_t btn_handle)
|
||||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", 0);
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
return btn->repeat;
|
||||
}
|
||||
|
||||
uint32_t iot_button_get_ticks_time(button_handle_t btn_handle)
|
||||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", 0);
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
return (btn->ticks * TICKS_INTERVAL);
|
||||
}
|
||||
|
||||
uint16_t iot_button_get_long_press_hold_cnt(button_handle_t btn_handle)
|
||||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", 0);
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
return btn->long_press_hold_cnt;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_set_param(button_handle_t btn_handle, button_param_t param, void *value)
|
||||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
BUTTON_ENTER_CRITICAL();
|
||||
switch (param) {
|
||||
case BUTTON_LONG_PRESS_TIME_MS:
|
||||
btn->long_press_ticks = (int32_t)value / TICKS_INTERVAL;
|
||||
break;
|
||||
case BUTTON_SHORT_PRESS_TIME_MS:
|
||||
btn->short_press_ticks = (int32_t)value / TICKS_INTERVAL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
BUTTON_EXIT_CRITICAL();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint8_t iot_button_get_key_level(button_handle_t btn_handle)
|
||||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", 0);
|
||||
button_dev_t *btn = (button_dev_t *)btn_handle;
|
||||
uint8_t level = btn->driver->get_key_level(btn->driver);
|
||||
return level;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_resume(void)
|
||||
{
|
||||
if (!g_button_timer_handle) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (!g_is_timer_running) {
|
||||
esp_timer_start_periodic(g_button_timer_handle, TICKS_INTERVAL * 1000U);
|
||||
g_is_timer_running = true;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_stop(void)
|
||||
{
|
||||
BTN_CHECK(g_button_timer_handle, "Button timer handle is invalid", ESP_ERR_INVALID_STATE);
|
||||
BTN_CHECK(g_is_timer_running, "Button timer is not running", ESP_ERR_INVALID_STATE);
|
||||
|
||||
esp_err_t err = esp_timer_stop(g_button_timer_handle);
|
||||
BTN_CHECK(ESP_OK == err, "Button timer stop failed", ESP_FAIL);
|
||||
g_is_timer_running = false;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_register_power_save_cb(const button_power_save_config_t *config)
|
||||
{
|
||||
BTN_CHECK(g_head_handle, "No button registered", ESP_ERR_INVALID_STATE);
|
||||
BTN_CHECK(config->enter_power_save_cb, "Enter power save callback is invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
power_save_usr_cfg.enter_power_save_cb = config->enter_power_save_cb;
|
||||
power_save_usr_cfg.usr_data = config->usr_data;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_create(const button_config_t *config, const button_driver_t *driver, button_handle_t *ret_button)
|
||||
{
|
||||
if (!g_head_handle) {
|
||||
ESP_LOGI(TAG, "IoT Button Version: %d.%d.%d", BUTTON_VER_MAJOR, BUTTON_VER_MINOR, BUTTON_VER_PATCH);
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(driver && config && ret_button, ESP_ERR_INVALID_ARG, TAG, "Invalid argument");
|
||||
button_dev_t *btn = (button_dev_t *) calloc(1, sizeof(button_dev_t));
|
||||
ESP_RETURN_ON_FALSE(btn, ESP_ERR_NO_MEM, TAG, "Button memory alloc failed");
|
||||
|
||||
btn->driver = (button_driver_t *)driver;
|
||||
btn->long_press_ticks = TIME_TO_TICKS(config->long_press_time, LONG_TICKS);
|
||||
btn->short_press_ticks = TIME_TO_TICKS(config->short_press_time, SHORT_TICKS);
|
||||
btn->event = BUTTON_NONE_PRESS;
|
||||
btn->button_level = BUTTON_INACTIVE;
|
||||
|
||||
btn->next = g_head_handle;
|
||||
g_head_handle = btn;
|
||||
|
||||
if (!g_button_timer_handle) {
|
||||
esp_timer_create_args_t button_timer = {0};
|
||||
button_timer.arg = NULL;
|
||||
button_timer.callback = button_cb;
|
||||
button_timer.dispatch_method = ESP_TIMER_TASK;
|
||||
button_timer.name = "button_timer";
|
||||
esp_timer_create(&button_timer, &g_button_timer_handle);
|
||||
}
|
||||
|
||||
if (!driver->enable_power_save && !g_is_timer_running) {
|
||||
esp_timer_start_periodic(g_button_timer_handle, TICKS_INTERVAL * 1000U);
|
||||
g_is_timer_running = true;
|
||||
}
|
||||
|
||||
*ret_button = (button_handle_t)btn;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_delete(button_handle_t btn_handle)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_RETURN_ON_FALSE(NULL != btn_handle, ESP_ERR_INVALID_ARG, TAG, "Pointer of handle is invalid");
|
||||
button_dev_t *btn = (button_dev_t *)btn_handle;
|
||||
|
||||
for (int i = 0; i < BUTTON_EVENT_MAX; i++) {
|
||||
if (btn->cb_info[i]) {
|
||||
free(btn->cb_info[i]);
|
||||
}
|
||||
}
|
||||
|
||||
ret = btn->driver->del(btn->driver);
|
||||
ESP_RETURN_ON_FALSE(ESP_OK == ret, ret, TAG, "Failed to delete button driver");
|
||||
|
||||
button_dev_t **curr;
|
||||
for (curr = &g_head_handle; *curr;) {
|
||||
button_dev_t *entry = *curr;
|
||||
if (entry == btn) {
|
||||
*curr = entry->next;
|
||||
free(entry);
|
||||
} else {
|
||||
curr = &entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* count button number */
|
||||
uint16_t number = 0;
|
||||
button_dev_t *target = g_head_handle;
|
||||
while (target) {
|
||||
target = target->next;
|
||||
number++;
|
||||
}
|
||||
ESP_LOGD(TAG, "remain btn number=%d", number);
|
||||
|
||||
if (0 == number && g_is_timer_running) { /**< if all button is deleted, stop the timer */
|
||||
esp_timer_stop(g_button_timer_handle);
|
||||
esp_timer_delete(g_button_timer_handle);
|
||||
g_button_timer_handle = NULL;
|
||||
g_is_timer_running = false;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
202
managed_components/espressif__button/license.txt
Normal file
202
managed_components/espressif__button/license.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components"
|
||||
"../../button")
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(button_test)
|
||||
@@ -0,0 +1,9 @@
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
|
||||
list(APPEND PRIVREQ esp_adc)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRC_DIRS "."
|
||||
PRIV_REQUIRES esp_event
|
||||
PRIV_INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES unity test_utils button ${PRIVREQ}
|
||||
WHOLE_ARCHIVE)
|
||||
@@ -0,0 +1,134 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/timers.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include "esp_log.h"
|
||||
#include "unity.h"
|
||||
#include "iot_button.h"
|
||||
#include "button_adc.h"
|
||||
|
||||
static const char *TAG = "ADC BUTTON TEST";
|
||||
|
||||
static void button_event_cb(void *arg, void *data)
|
||||
{
|
||||
button_event_t event = iot_button_get_event(arg);
|
||||
ESP_LOGI(TAG, "BTN[%d] %s", (int)data, iot_button_get_event_str(event));
|
||||
if (BUTTON_PRESS_REPEAT == event || BUTTON_PRESS_REPEAT_DONE == event) {
|
||||
ESP_LOGI(TAG, "\tREPEAT[%d]", iot_button_get_repeat(arg));
|
||||
}
|
||||
|
||||
if (BUTTON_PRESS_UP == event || BUTTON_LONG_PRESS_HOLD == event || BUTTON_LONG_PRESS_UP == event) {
|
||||
ESP_LOGI(TAG, "\tTICKS[%"PRIu32"]", iot_button_get_ticks_time(arg));
|
||||
}
|
||||
|
||||
if (BUTTON_MULTIPLE_CLICK == event) {
|
||||
ESP_LOGI(TAG, "\tMULTIPLE[%d]", (int)data);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("adc button test", "[button][adc]")
|
||||
{
|
||||
/** ESP32-S3-Korvo2 board */
|
||||
const button_config_t btn_cfg = {0};
|
||||
button_adc_config_t btn_adc_cfg = {
|
||||
.unit_id = ADC_UNIT_1,
|
||||
.adc_channel = 4,
|
||||
};
|
||||
|
||||
button_handle_t btns[6] = {NULL};
|
||||
|
||||
const uint16_t vol[6] = {380, 820, 1180, 1570, 1980, 2410};
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
btn_adc_cfg.button_index = i;
|
||||
if (i == 0) {
|
||||
btn_adc_cfg.min = (0 + vol[i]) / 2;
|
||||
} else {
|
||||
btn_adc_cfg.min = (vol[i - 1] + vol[i]) / 2;
|
||||
}
|
||||
|
||||
if (i == 5) {
|
||||
btn_adc_cfg.max = (vol[i] + 3000) / 2;
|
||||
} else {
|
||||
btn_adc_cfg.max = (vol[i] + vol[i + 1]) / 2;
|
||||
}
|
||||
|
||||
esp_err_t ret = iot_button_new_adc_device(&btn_cfg, &btn_adc_cfg, &btns[i]);
|
||||
TEST_ASSERT(ret == ESP_OK);
|
||||
TEST_ASSERT_NOT_NULL(btns[i]);
|
||||
iot_button_register_cb(btns[i], BUTTON_PRESS_DOWN, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_PRESS_UP, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_PRESS_REPEAT, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_PRESS_REPEAT_DONE, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_SINGLE_CLICK, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_DOUBLE_CLICK, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_LONG_PRESS_START, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_LONG_PRESS_HOLD, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_LONG_PRESS_UP, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_PRESS_END, NULL, button_event_cb, (void *)i);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
iot_button_delete(btns[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("adc button test memory leak", "[button][adc][memory leak]")
|
||||
{
|
||||
/** ESP32-S3-Korvo2 board */
|
||||
const button_config_t btn_cfg = {0};
|
||||
button_adc_config_t btn_adc_cfg = {
|
||||
.unit_id = ADC_UNIT_1,
|
||||
.adc_channel = 4,
|
||||
};
|
||||
|
||||
button_handle_t btns[6] = {NULL};
|
||||
|
||||
const uint16_t vol[6] = {380, 820, 1180, 1570, 1980, 2410};
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
btn_adc_cfg.button_index = i;
|
||||
if (i == 0) {
|
||||
btn_adc_cfg.min = (0 + vol[i]) / 2;
|
||||
} else {
|
||||
btn_adc_cfg.min = (vol[i - 1] + vol[i]) / 2;
|
||||
}
|
||||
|
||||
if (i == 5) {
|
||||
btn_adc_cfg.max = (vol[i] + 3000) / 2;
|
||||
} else {
|
||||
btn_adc_cfg.max = (vol[i] + vol[i + 1]) / 2;
|
||||
}
|
||||
|
||||
esp_err_t ret = iot_button_new_adc_device(&btn_cfg, &btn_adc_cfg, &btns[i]);
|
||||
TEST_ASSERT(ret == ESP_OK);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(btns[i]);
|
||||
iot_button_register_cb(btns[i], BUTTON_PRESS_DOWN, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_PRESS_UP, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_PRESS_REPEAT, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_PRESS_REPEAT_DONE, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_SINGLE_CLICK, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_DOUBLE_CLICK, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_LONG_PRESS_START, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_LONG_PRESS_HOLD, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_LONG_PRESS_UP, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_PRESS_END, NULL, button_event_cb, (void *)i);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
iot_button_delete(btns[i]);
|
||||
}
|
||||
}
|
||||
288
managed_components/espressif__button/test_apps/main/auto_test.c
Normal file
288
managed_components/espressif__button/test_apps/main/auto_test.c
Normal file
@@ -0,0 +1,288 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_log.h"
|
||||
#include "unity.h"
|
||||
#include "iot_button.h"
|
||||
#include "button_gpio.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
static const char *TAG = "BUTTON AUTO TEST";
|
||||
|
||||
#define GPIO_OUTPUT_IO_45 45
|
||||
#define BUTTON_IO_NUM 0
|
||||
#define BUTTON_ACTIVE_LEVEL 0
|
||||
|
||||
static EventGroupHandle_t g_check = NULL;
|
||||
static SemaphoreHandle_t g_auto_check_pass = NULL;
|
||||
|
||||
static button_event_t state = BUTTON_PRESS_DOWN;
|
||||
|
||||
static void button_auto_press_test_task(void *arg)
|
||||
{
|
||||
// test BUTTON_PRESS_DOWN
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
// // test BUTTON_PRESS_UP
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
|
||||
// test BUTTON_PRESS_REPEAT
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
// test BUTTON_PRESS_REPEAT_DONE
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
|
||||
// test BUTTON_SINGLE_CLICK
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
|
||||
// test BUTTON_DOUBLE_CLICK
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
|
||||
// test BUTTON_MULTIPLE_CLICK
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
// test BUTTON_LONG_PRESS_START
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(1600));
|
||||
|
||||
// test BUTTON_LONG_PRESS_HOLD and BUTTON_LONG_PRESS_UP
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
|
||||
ESP_LOGI(TAG, "Auto Press Success!");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
static void button_auto_check_cb_1(void *arg, void *data)
|
||||
{
|
||||
if (iot_button_get_event(arg) == state) {
|
||||
xEventGroupSetBits(g_check, BIT(1));
|
||||
}
|
||||
}
|
||||
static void button_auto_check_cb(void *arg, void *data)
|
||||
{
|
||||
if (iot_button_get_event(arg) == state) {
|
||||
ESP_LOGI(TAG, "Auto check: button event %s pass", iot_button_get_event_str(state));
|
||||
xEventGroupSetBits(g_check, BIT(0));
|
||||
if (++state >= BUTTON_EVENT_MAX) {
|
||||
xSemaphoreGive(g_auto_check_pass);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("gpio button auto-test", "[button][iot][auto]")
|
||||
{
|
||||
state = BUTTON_PRESS_DOWN;
|
||||
g_check = xEventGroupCreate();
|
||||
g_auto_check_pass = xSemaphoreCreateBinary();
|
||||
xEventGroupSetBits(g_check, BIT(0) | BIT(1));
|
||||
const button_config_t btn_cfg = {0};
|
||||
const button_gpio_config_t btn_gpio_cfg = {
|
||||
.gpio_num = BUTTON_IO_NUM,
|
||||
.active_level = BUTTON_ACTIVE_LEVEL,
|
||||
};
|
||||
|
||||
button_handle_t btn = NULL;
|
||||
esp_err_t ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_cfg, &btn);
|
||||
TEST_ASSERT(ret == ESP_OK);
|
||||
TEST_ASSERT_NOT_NULL(btn);
|
||||
|
||||
/* register iot_button callback for all the button_event */
|
||||
for (uint8_t i = 0; i < BUTTON_EVENT_MAX; i++) {
|
||||
if (i == BUTTON_MULTIPLE_CLICK) {
|
||||
button_event_args_t args = {
|
||||
.multiple_clicks.clicks = 4,
|
||||
};
|
||||
iot_button_register_cb(btn, BUTTON_MULTIPLE_CLICK, &args, button_auto_check_cb_1, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_MULTIPLE_CLICK, &args, button_auto_check_cb, NULL);
|
||||
} else {
|
||||
iot_button_register_cb(btn, i, NULL, button_auto_check_cb_1, NULL);
|
||||
iot_button_register_cb(btn, i, NULL, button_auto_check_cb, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, iot_button_set_param(btn, BUTTON_LONG_PRESS_TIME_MS, (void *)1500));
|
||||
|
||||
gpio_config_t io_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = (1ULL << GPIO_OUTPUT_IO_45),
|
||||
.pull_down_en = 0,
|
||||
.pull_up_en = 0,
|
||||
};
|
||||
gpio_config(&io_conf);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
|
||||
xTaskCreate(button_auto_press_test_task, "button_auto_press_test_task", 1024 * 4, NULL, 10, NULL);
|
||||
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(g_auto_check_pass, pdMS_TO_TICKS(6000)));
|
||||
|
||||
for (uint8_t i = 0; i < BUTTON_EVENT_MAX; i++) {
|
||||
button_event_args_t args;
|
||||
|
||||
if (i == BUTTON_MULTIPLE_CLICK) {
|
||||
args.multiple_clicks.clicks = 4;
|
||||
iot_button_unregister_cb(btn, i, &args);
|
||||
} else if (i == BUTTON_LONG_PRESS_UP || i == BUTTON_LONG_PRESS_START) {
|
||||
args.long_press.press_time = 1500;
|
||||
iot_button_unregister_cb(btn, i, &args);
|
||||
} else {
|
||||
iot_button_unregister_cb(btn, i, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, iot_button_delete(btn));
|
||||
vEventGroupDelete(g_check);
|
||||
vSemaphoreDelete(g_auto_check_pass);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
#define TOLERANCE (CONFIG_BUTTON_PERIOD_TIME_MS * 4)
|
||||
|
||||
uint16_t long_press_time[5] = {2000, 2500, 3000, 3500, 4000};
|
||||
static SemaphoreHandle_t long_press_check = NULL;
|
||||
static SemaphoreHandle_t long_press_auto_check_pass = NULL;
|
||||
unsigned int status = 0;
|
||||
|
||||
static void button_auto_long_press_test_task(void *arg)
|
||||
{
|
||||
// Test for BUTTON_LONG_PRESS_START
|
||||
for (int i = 0; i < 5; i++) {
|
||||
xSemaphoreTake(long_press_check, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
status = (BUTTON_LONG_PRESS_START << 16) | long_press_time[i];
|
||||
if (i > 0) {
|
||||
vTaskDelay(pdMS_TO_TICKS(long_press_time[i] - long_press_time[i - 1]));
|
||||
} else {
|
||||
vTaskDelay(pdMS_TO_TICKS(long_press_time[i]));
|
||||
}
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
xSemaphoreGive(long_press_auto_check_pass);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
// Test for BUTTON_LONG_PRESS_UP
|
||||
for (int i = 0; i < 5; i++) {
|
||||
xSemaphoreTake(long_press_check, portMAX_DELAY);
|
||||
status = (BUTTON_LONG_PRESS_UP << 16) | long_press_time[i];
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(long_press_time[i] + 10));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Auto Long Press Success!");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void button_long_press_auto_check_cb(void *arg, void *data)
|
||||
{
|
||||
uint32_t value = (uint32_t)data;
|
||||
uint16_t event = (0xffff0000 & value) >> 16;
|
||||
uint16_t time = 0xffff & value;
|
||||
uint32_t ticks_time = iot_button_get_ticks_time(arg);
|
||||
int32_t diff = ticks_time - time;
|
||||
if (status == value && abs(diff) <= TOLERANCE) {
|
||||
ESP_LOGI(TAG, "Auto check: button event: %s and time: %d pass", iot_button_get_event_str(event), time);
|
||||
|
||||
if (event == BUTTON_LONG_PRESS_UP && time == long_press_time[4]) {
|
||||
xSemaphoreGive(long_press_auto_check_pass);
|
||||
}
|
||||
|
||||
xSemaphoreGive(long_press_check);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("gpio button long_press auto-test", "[button][long_press][auto]")
|
||||
{
|
||||
ESP_LOGI(TAG, "Starting the test");
|
||||
long_press_check = xSemaphoreCreateBinary();
|
||||
long_press_auto_check_pass = xSemaphoreCreateBinary();
|
||||
xSemaphoreGive(long_press_check);
|
||||
const button_config_t btn_cfg = {0};
|
||||
const button_gpio_config_t btn_gpio_cfg = {
|
||||
.gpio_num = BUTTON_IO_NUM,
|
||||
.active_level = BUTTON_ACTIVE_LEVEL,
|
||||
};
|
||||
|
||||
button_handle_t btn = NULL;
|
||||
esp_err_t ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_cfg, &btn);
|
||||
TEST_ASSERT(ret == ESP_OK);
|
||||
TEST_ASSERT_NOT_NULL(btn);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
button_event_args_t args = {
|
||||
.long_press.press_time = long_press_time[i],
|
||||
};
|
||||
|
||||
uint32_t data = (BUTTON_LONG_PRESS_START << 16) | long_press_time[i];
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_START, &args, button_long_press_auto_check_cb, (void*)data);
|
||||
}
|
||||
|
||||
gpio_config_t io_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = (1ULL << GPIO_OUTPUT_IO_45),
|
||||
.pull_down_en = 0,
|
||||
.pull_up_en = 0,
|
||||
};
|
||||
gpio_config(&io_conf);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
xTaskCreate(button_auto_long_press_test_task, "button_auto_long_press_test_task", 1024 * 4, NULL, 10, NULL);
|
||||
|
||||
xSemaphoreTake(long_press_auto_check_pass, portMAX_DELAY);
|
||||
iot_button_unregister_cb(btn, BUTTON_LONG_PRESS_START, NULL);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
button_event_args_t args = {
|
||||
.long_press.press_time = long_press_time[i]
|
||||
};
|
||||
|
||||
uint32_t data = (BUTTON_LONG_PRESS_UP << 16) | long_press_time[i];
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_UP, &args, button_long_press_auto_check_cb, (void*)data);
|
||||
}
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(long_press_auto_check_pass, pdMS_TO_TICKS(17000)));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, iot_button_delete(btn));
|
||||
vSemaphoreDelete(long_press_check);
|
||||
vSemaphoreDelete(long_press_auto_check_pass);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "unity_test_utils_memory.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define LEAKS (400)
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
unity_utils_record_free_mem();
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
unity_utils_evaluate_leaks_direct(LEAKS);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/*
|
||||
* ____ _ _ _______ _
|
||||
*| _ \ | | | | |__ __| | |
|
||||
*| |_) | _ _ | |_ | |_ ___ _ __ | | ___ ___ | |_
|
||||
*| _ < | | | || __|| __|/ _ \ | '_ \ | | / _ \/ __|| __|
|
||||
*| |_) || |_| || |_ | |_| (_) || | | | | || __/\__ \| |_
|
||||
*|____/ \__,_| \__| \__|\___/ |_| |_| |_| \___||___/ \__|
|
||||
*/
|
||||
printf(" ____ _ _ _______ _ \n");
|
||||
printf(" | _ \\ | | | | |__ __| | | \n");
|
||||
printf(" | |_) | _ _ | |_ | |_ ___ _ __ | | ___ ___ | |_ \n");
|
||||
printf(" | _ < | | | || __|| __|/ _ \\ | '_ \\ | | / _ \\/ __|| __|\n");
|
||||
printf(" | |_) || |_| || |_ | |_| (_) || | | | | || __/\\__ \\| |_ \n");
|
||||
printf(" |____/ \\__,_| \\__| \\__|\\___/ |_| |_| |_| \\___||___/ \\__|\n");
|
||||
unity_run_menu();
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "unity.h"
|
||||
#include "iot_button.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
static const char *TAG = "CUSTOM BUTTON TEST";
|
||||
|
||||
#define BUTTON_IO_NUM 0
|
||||
#define BUTTON_ACTIVE_LEVEL 0
|
||||
|
||||
static void button_event_cb(void *arg, void *data)
|
||||
{
|
||||
button_event_t event = iot_button_get_event(arg);
|
||||
ESP_LOGI(TAG, "%s", iot_button_get_event_str(event));
|
||||
if (BUTTON_PRESS_REPEAT == event || BUTTON_PRESS_REPEAT_DONE == event) {
|
||||
ESP_LOGI(TAG, "\tREPEAT[%d]", iot_button_get_repeat(arg));
|
||||
}
|
||||
|
||||
if (BUTTON_PRESS_UP == event || BUTTON_LONG_PRESS_HOLD == event || BUTTON_LONG_PRESS_UP == event) {
|
||||
ESP_LOGI(TAG, "\tTICKS[%"PRIu32"]", iot_button_get_ticks_time(arg));
|
||||
}
|
||||
|
||||
if (BUTTON_MULTIPLE_CLICK == event) {
|
||||
ESP_LOGI(TAG, "\tMULTIPLE[%d]", (int)data);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
button_driver_t base;
|
||||
int32_t gpio_num; /**< num of gpio */
|
||||
uint8_t active_level; /**< gpio level when press down */
|
||||
} custom_gpio_obj;
|
||||
|
||||
static uint8_t button_get_key_level(button_driver_t *button_driver)
|
||||
{
|
||||
custom_gpio_obj *custom_btn = __containerof(button_driver, custom_gpio_obj, base);
|
||||
int level = gpio_get_level(custom_btn->gpio_num);
|
||||
return level == custom_btn->active_level ? 1 : 0;
|
||||
}
|
||||
|
||||
static esp_err_t button_del(button_driver_t *button_driver)
|
||||
{
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
TEST_CASE("custom button test", "[button][custom]")
|
||||
{
|
||||
gpio_config_t gpio_conf = {
|
||||
.pin_bit_mask = 1ULL << BUTTON_IO_NUM,
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = 1,
|
||||
.pull_down_en = 0,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
gpio_config(&gpio_conf);
|
||||
|
||||
custom_gpio_obj *custom_btn = (custom_gpio_obj *)calloc(1, sizeof(custom_gpio_obj));
|
||||
TEST_ASSERT_NOT_NULL(custom_btn);
|
||||
custom_btn->active_level = BUTTON_ACTIVE_LEVEL;
|
||||
custom_btn->gpio_num = BUTTON_IO_NUM;
|
||||
|
||||
button_handle_t btn = NULL;
|
||||
const button_config_t btn_cfg = {0};
|
||||
custom_btn->base.get_key_level = button_get_key_level;
|
||||
custom_btn->base.del = button_del;
|
||||
esp_err_t ret = iot_button_create(&btn_cfg, &custom_btn->base, &btn);
|
||||
TEST_ASSERT(ESP_OK == ret);
|
||||
TEST_ASSERT_NOT_NULL(btn);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_DOWN, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_UP, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_REPEAT, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_REPEAT_DONE, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_SINGLE_CLICK, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_DOUBLE_CLICK, NULL, button_event_cb, NULL);
|
||||
|
||||
/*!< Multiple Click must provide button_event_args_t */
|
||||
/*!< Double Click */
|
||||
button_event_args_t args = {
|
||||
.multiple_clicks.clicks = 2,
|
||||
};
|
||||
iot_button_register_cb(btn, BUTTON_MULTIPLE_CLICK, &args, button_event_cb, (void *)2);
|
||||
/*!< Triple Click */
|
||||
args.multiple_clicks.clicks = 3;
|
||||
iot_button_register_cb(btn, BUTTON_MULTIPLE_CLICK, &args, button_event_cb, (void *)3);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_START, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_HOLD, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_UP, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_END, NULL, button_event_cb, NULL);
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
iot_button_delete(btn);
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "unity.h"
|
||||
#include "iot_button.h"
|
||||
#include "button_gpio.h"
|
||||
|
||||
static const char *TAG = "GPIO BUTTON TEST";
|
||||
|
||||
#define BUTTON_IO_NUM 0
|
||||
#define BUTTON_ACTIVE_LEVEL 0
|
||||
|
||||
static void button_event_cb(void *arg, void *data)
|
||||
{
|
||||
button_event_t event = iot_button_get_event(arg);
|
||||
ESP_LOGI(TAG, "%s", iot_button_get_event_str(event));
|
||||
if (BUTTON_PRESS_REPEAT == event || BUTTON_PRESS_REPEAT_DONE == event) {
|
||||
ESP_LOGI(TAG, "\tREPEAT[%d]", iot_button_get_repeat(arg));
|
||||
}
|
||||
|
||||
if (BUTTON_PRESS_UP == event || BUTTON_LONG_PRESS_HOLD == event || BUTTON_LONG_PRESS_UP == event) {
|
||||
ESP_LOGI(TAG, "\tTICKS[%"PRIu32"]", iot_button_get_ticks_time(arg));
|
||||
}
|
||||
|
||||
if (BUTTON_MULTIPLE_CLICK == event) {
|
||||
ESP_LOGI(TAG, "\tMULTIPLE[%d]", (int)data);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("gpio button test", "[button][gpio]")
|
||||
{
|
||||
const button_config_t btn_cfg = {0};
|
||||
const button_gpio_config_t btn_gpio_cfg = {
|
||||
.gpio_num = BUTTON_IO_NUM,
|
||||
.active_level = BUTTON_ACTIVE_LEVEL,
|
||||
};
|
||||
|
||||
button_handle_t btn = NULL;
|
||||
esp_err_t ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_cfg, &btn);
|
||||
TEST_ASSERT(ret == ESP_OK);
|
||||
TEST_ASSERT_NOT_NULL(btn);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_DOWN, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_UP, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_REPEAT, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_REPEAT_DONE, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_SINGLE_CLICK, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_DOUBLE_CLICK, NULL, button_event_cb, NULL);
|
||||
|
||||
/*!< Multiple Click must provide button_event_args_t */
|
||||
/*!< Double Click */
|
||||
button_event_args_t args = {
|
||||
.multiple_clicks.clicks = 2,
|
||||
};
|
||||
iot_button_register_cb(btn, BUTTON_MULTIPLE_CLICK, &args, button_event_cb, (void *)2);
|
||||
/*!< Triple Click */
|
||||
args.multiple_clicks.clicks = 3;
|
||||
iot_button_register_cb(btn, BUTTON_MULTIPLE_CLICK, &args, button_event_cb, (void *)3);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_START, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_HOLD, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_UP, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_END, NULL, button_event_cb, NULL);
|
||||
|
||||
uint8_t level = 0;
|
||||
level = iot_button_get_key_level(btn);
|
||||
ESP_LOGI(TAG, "button level is %d", level);
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
iot_button_delete(btn);
|
||||
}
|
||||
|
||||
TEST_CASE("gpio button get event test", "[button][gpio][event test]")
|
||||
{
|
||||
const button_config_t btn_cfg = {0};
|
||||
const button_gpio_config_t btn_gpio_cfg = {
|
||||
.gpio_num = BUTTON_IO_NUM,
|
||||
.active_level = BUTTON_ACTIVE_LEVEL,
|
||||
};
|
||||
|
||||
button_handle_t btn = NULL;
|
||||
esp_err_t ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_cfg, &btn);
|
||||
TEST_ASSERT(ret == ESP_OK);
|
||||
TEST_ASSERT_NOT_NULL(btn);
|
||||
|
||||
uint8_t level = 0;
|
||||
level = iot_button_get_key_level(btn);
|
||||
ESP_LOGI(TAG, "button level is %d", level);
|
||||
|
||||
while (1) {
|
||||
button_event_t event = iot_button_get_event(btn);
|
||||
if (event != BUTTON_NONE_PRESS) {
|
||||
ESP_LOGI(TAG, "event is %s", iot_button_get_event_str(event));
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
}
|
||||
|
||||
iot_button_delete(btn);
|
||||
}
|
||||
|
||||
TEST_CASE("gpio button test power save", "[button][gpio][power save]")
|
||||
{
|
||||
const button_config_t btn_cfg = {0};
|
||||
const button_gpio_config_t btn_gpio_cfg = {
|
||||
.gpio_num = BUTTON_IO_NUM,
|
||||
.active_level = BUTTON_ACTIVE_LEVEL,
|
||||
.enable_power_save = true,
|
||||
};
|
||||
|
||||
button_handle_t btn = NULL;
|
||||
esp_err_t ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_cfg, &btn);
|
||||
TEST_ASSERT(ret == ESP_OK);
|
||||
TEST_ASSERT_NOT_NULL(btn);
|
||||
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_DOWN, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_UP, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_REPEAT, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_REPEAT_DONE, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_SINGLE_CLICK, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_DOUBLE_CLICK, NULL, button_event_cb, NULL);
|
||||
|
||||
/*!< Multiple Click must provide button_event_args_t */
|
||||
/*!< Double Click */
|
||||
button_event_args_t args = {
|
||||
.multiple_clicks.clicks = 2,
|
||||
};
|
||||
iot_button_register_cb(btn, BUTTON_MULTIPLE_CLICK, &args, button_event_cb, (void *)2);
|
||||
/*!< Triple Click */
|
||||
args.multiple_clicks.clicks = 3;
|
||||
iot_button_register_cb(btn, BUTTON_MULTIPLE_CLICK, &args, button_event_cb, (void *)3);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_START, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_HOLD, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_UP, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_END, NULL, button_event_cb, NULL);
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
iot_button_delete(btn);
|
||||
}
|
||||
|
||||
TEST_CASE("gpio button test memory leak", "[button][gpio][memory leak]")
|
||||
{
|
||||
const button_config_t btn_cfg = {0};
|
||||
const button_gpio_config_t btn_gpio_cfg = {
|
||||
.gpio_num = BUTTON_IO_NUM,
|
||||
.active_level = BUTTON_ACTIVE_LEVEL,
|
||||
};
|
||||
|
||||
button_handle_t btn = NULL;
|
||||
esp_err_t ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_cfg, &btn);
|
||||
TEST_ASSERT(ret == ESP_OK);
|
||||
TEST_ASSERT_NOT_NULL(btn);
|
||||
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_DOWN, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_UP, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_REPEAT, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_REPEAT_DONE, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_SINGLE_CLICK, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_DOUBLE_CLICK, NULL, button_event_cb, NULL);
|
||||
|
||||
/*!< Multiple Click must provide button_event_args_t */
|
||||
/*!< Double Click */
|
||||
button_event_args_t args = {
|
||||
.multiple_clicks.clicks = 2,
|
||||
};
|
||||
iot_button_register_cb(btn, BUTTON_MULTIPLE_CLICK, &args, button_event_cb, (void *)2);
|
||||
/*!< Triple Click */
|
||||
args.multiple_clicks.clicks = 3;
|
||||
iot_button_register_cb(btn, BUTTON_MULTIPLE_CLICK, &args, button_event_cb, (void *)3);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_START, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_HOLD, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_UP, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_END, NULL, button_event_cb, NULL);
|
||||
|
||||
iot_button_delete(btn);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "unity.h"
|
||||
#include "iot_button.h"
|
||||
#include "button_matrix.h"
|
||||
|
||||
static const char *TAG = "MATRIX BUTTON TEST";
|
||||
|
||||
static void button_event_cb(void *arg, void *data)
|
||||
{
|
||||
button_event_t event = iot_button_get_event(arg);
|
||||
ESP_LOGI(TAG, "BUTTON[%d] %s", (int)data, iot_button_get_event_str(event));
|
||||
if (BUTTON_PRESS_REPEAT == event || BUTTON_PRESS_REPEAT_DONE == event) {
|
||||
ESP_LOGI(TAG, "\tREPEAT[%d]", iot_button_get_repeat(arg));
|
||||
}
|
||||
|
||||
if (BUTTON_PRESS_UP == event || BUTTON_LONG_PRESS_HOLD == event || BUTTON_LONG_PRESS_UP == event) {
|
||||
ESP_LOGI(TAG, "\tTICKS[%"PRIu32"]", iot_button_get_ticks_time(arg));
|
||||
}
|
||||
|
||||
if (BUTTON_MULTIPLE_CLICK == event) {
|
||||
ESP_LOGI(TAG, "\tMULTIPLE[%d]", (int)data);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("matrix keyboard button test", "[button][matrix key]")
|
||||
{
|
||||
const button_config_t btn_cfg = {0};
|
||||
const button_matrix_config_t matrix_cfg = {
|
||||
.row_gpios = (int32_t[]){4, 5, 6, 7},
|
||||
.col_gpios = (int32_t[]){3, 8, 16, 15},
|
||||
.row_gpio_num = 4,
|
||||
.col_gpio_num = 4,
|
||||
};
|
||||
|
||||
button_handle_t btns[16] = {0};
|
||||
size_t btn_num = 16;
|
||||
esp_err_t ret = iot_button_new_matrix_device(&btn_cfg, &matrix_cfg, btns, &btn_num);
|
||||
TEST_ASSERT(ret == ESP_OK);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
int index = i * 4 + j;
|
||||
TEST_ASSERT_NOT_NULL(btns[index]);
|
||||
iot_button_register_cb(btns[index], BUTTON_PRESS_DOWN, NULL, button_event_cb, (void *)index);
|
||||
iot_button_register_cb(btns[index], BUTTON_PRESS_UP, NULL, button_event_cb, (void *)index);
|
||||
iot_button_register_cb(btns[index], BUTTON_PRESS_REPEAT, NULL, button_event_cb, (void *)index);
|
||||
iot_button_register_cb(btns[index], BUTTON_PRESS_REPEAT_DONE, NULL, button_event_cb, (void *)index);
|
||||
iot_button_register_cb(btns[index], BUTTON_SINGLE_CLICK, NULL, button_event_cb, (void *)index);
|
||||
iot_button_register_cb(btns[index], BUTTON_DOUBLE_CLICK, NULL, button_event_cb, (void *)index);
|
||||
iot_button_register_cb(btns[index], BUTTON_LONG_PRESS_START, NULL, button_event_cb, (void *)index);
|
||||
iot_button_register_cb(btns[index], BUTTON_LONG_PRESS_HOLD, NULL, button_event_cb, (void *)index);
|
||||
iot_button_register_cb(btns[index], BUTTON_LONG_PRESS_UP, NULL, button_event_cb, (void *)index);
|
||||
iot_button_register_cb(btns[index], BUTTON_PRESS_END, NULL, button_event_cb, (void *)index);
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
iot_button_delete(btns[i * 4 + j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'''
|
||||
Steps to run these cases:
|
||||
- Build
|
||||
- . ${IDF_PATH}/export.sh
|
||||
- pip install idf_build_apps
|
||||
- python tools/build_apps.py components/button/test_apps -t esp32s3
|
||||
- Test
|
||||
- pip install -r tools/requirements/requirement.pytest.txt
|
||||
- pytest components/button/test_apps --target esp32s3
|
||||
'''
|
||||
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
@pytest.mark.target('esp32s3')
|
||||
@pytest.mark.env('button')
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'defaults',
|
||||
],
|
||||
)
|
||||
def test_button(dut: Dut)-> None:
|
||||
dut.expect_exact('Press ENTER to see the list of tests.')
|
||||
dut.write('[auto]')
|
||||
dut.expect_unity_test_output(timeout = 300)
|
||||
@@ -0,0 +1,9 @@
|
||||
# For IDF 5.0
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESP_TASK_WDT_EN=n
|
||||
|
||||
# For IDF4.4
|
||||
CONFIG_ESP32S2_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
Reference in New Issue
Block a user