2025-11-28 19:41:00 +08:00
/* SPDX-FileCopyrightText: 2022-2024 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 "sdkconfig.h"
static const char * TAG = " BUTTON TEST " ;
# define TEST_MEMORY_LEAK_THRESHOLD (-400)
# define BUTTON_IO_NUM 0
# define BUTTON_ACTIVE_LEVEL 0
# define BUTTON_NUM 16
static size_t before_free_8bit ;
static size_t before_free_32bit ;
static button_handle_t g_btns [ BUTTON_NUM ] = { 0 } ;
static int get_btn_index ( button_handle_t btn )
{
for ( size_t i = 0 ; i < BUTTON_NUM ; i + + ) {
if ( btn = = g_btns [ i ] ) {
return i ;
}
}
return - 1 ;
}
static void button_press_down_cb ( void * arg , void * data )
{
TEST_ASSERT_EQUAL_HEX ( BUTTON_PRESS_DOWN , iot_button_get_event ( arg ) ) ;
ESP_LOGI ( TAG , " BTN%d: BUTTON_PRESS_DOWN " , get_btn_index ( ( button_handle_t ) arg ) ) ;
}
static void button_press_up_cb ( void * arg , void * data )
{
TEST_ASSERT_EQUAL_HEX ( BUTTON_PRESS_UP , iot_button_get_event ( arg ) ) ;
ESP_LOGI ( TAG , " BTN%d: BUTTON_PRESS_UP[% " PRIu32 " ] " , get_btn_index ( ( button_handle_t ) arg ) , iot_button_get_ticks_time ( ( button_handle_t ) arg ) ) ;
}
static void button_press_repeat_cb ( void * arg , void * data )
{
ESP_LOGI ( TAG , " BTN%d: BUTTON_PRESS_REPEAT[%d] " , get_btn_index ( ( button_handle_t ) arg ) , iot_button_get_repeat ( ( button_handle_t ) arg ) ) ;
}
static void button_single_click_cb ( void * arg , void * data )
{
TEST_ASSERT_EQUAL_HEX ( BUTTON_SINGLE_CLICK , iot_button_get_event ( arg ) ) ;
ESP_LOGI ( TAG , " BTN%d: BUTTON_SINGLE_CLICK " , get_btn_index ( ( button_handle_t ) arg ) ) ;
}
static void button_double_click_cb ( void * arg , void * data )
{
TEST_ASSERT_EQUAL_HEX ( BUTTON_DOUBLE_CLICK , iot_button_get_event ( arg ) ) ;
ESP_LOGI ( TAG , " BTN%d: BUTTON_DOUBLE_CLICK " , get_btn_index ( ( button_handle_t ) arg ) ) ;
}
static void button_long_press_start_cb ( void * arg , void * data )
{
TEST_ASSERT_EQUAL_HEX ( BUTTON_LONG_PRESS_START , iot_button_get_event ( arg ) ) ;
ESP_LOGI ( TAG , " BTN%d: BUTTON_LONG_PRESS_START " , get_btn_index ( ( button_handle_t ) arg ) ) ;
}
static void button_long_press_hold_cb ( void * arg , void * data )
{
TEST_ASSERT_EQUAL_HEX ( BUTTON_LONG_PRESS_HOLD , iot_button_get_event ( arg ) ) ;
ESP_LOGI ( TAG , " BTN%d: BUTTON_LONG_PRESS_HOLD[% " PRIu32 " ],count is [%d] " , get_btn_index ( ( button_handle_t ) arg ) , iot_button_get_ticks_time ( ( button_handle_t ) arg ) , iot_button_get_long_press_hold_cnt ( ( button_handle_t ) arg ) ) ;
}
static void button_press_repeat_done_cb ( void * arg , void * data )
{
TEST_ASSERT_EQUAL_HEX ( BUTTON_PRESS_REPEAT_DONE , iot_button_get_event ( arg ) ) ;
ESP_LOGI ( TAG , " BTN%d: BUTTON_PRESS_REPEAT_DONE[%d] " , get_btn_index ( ( button_handle_t ) arg ) , iot_button_get_repeat ( ( button_handle_t ) arg ) ) ;
}
static esp_err_t custom_button_gpio_init ( void * param )
{
button_gpio_config_t * cfg = ( button_gpio_config_t * ) param ;
return button_gpio_init ( cfg ) ;
}
static uint8_t custom_button_gpio_get_key_value ( void * param )
{
button_gpio_config_t * cfg = ( button_gpio_config_t * ) param ;
return button_gpio_get_key_level ( ( void * ) cfg - > gpio_num ) ;
}
static esp_err_t custom_button_gpio_deinit ( void * param )
{
button_gpio_config_t * cfg = ( button_gpio_config_t * ) param ;
return button_gpio_deinit ( cfg - > gpio_num ) ;
}
TEST_CASE ( " custom button test " , " [button][iot] " )
{
button_gpio_config_t * gpio_cfg = calloc ( 1 , sizeof ( button_gpio_config_t ) ) ;
gpio_cfg - > active_level = 0 ;
gpio_cfg - > gpio_num = 0 ;
button_config_t cfg = {
. type = BUTTON_TYPE_CUSTOM ,
. long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS ,
. short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS ,
. custom_button_config = {
. button_custom_init = custom_button_gpio_init ,
. button_custom_deinit = custom_button_gpio_deinit ,
. button_custom_get_key_value = custom_button_gpio_get_key_value ,
. active_level = 0 ,
. priv = gpio_cfg ,
} ,
} ;
g_btns [ 0 ] = iot_button_create ( & cfg ) ;
TEST_ASSERT_NOT_NULL ( g_btns [ 0 ] ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_PRESS_DOWN , button_press_down_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_PRESS_UP , button_press_up_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_PRESS_REPEAT , button_press_repeat_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_SINGLE_CLICK , button_single_click_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_DOUBLE_CLICK , button_double_click_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_LONG_PRESS_START , button_long_press_start_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_LONG_PRESS_HOLD , button_long_press_hold_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_PRESS_REPEAT_DONE , button_press_repeat_done_cb , NULL ) ;
while ( 1 ) {
vTaskDelay ( pdMS_TO_TICKS ( 1000 ) ) ;
}
iot_button_delete ( g_btns [ 0 ] ) ;
}
TEST_CASE ( " gpio button test " , " [button][iot] " )
{
button_config_t cfg = {
. type = BUTTON_TYPE_GPIO ,
. long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS ,
. short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS ,
. gpio_button_config = {
. gpio_num = 0 ,
. active_level = 0 ,
} ,
} ;
g_btns [ 0 ] = iot_button_create ( & cfg ) ;
TEST_ASSERT_NOT_NULL ( g_btns [ 0 ] ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_PRESS_DOWN , button_press_down_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_PRESS_UP , button_press_up_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_PRESS_REPEAT , button_press_repeat_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_SINGLE_CLICK , button_single_click_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_DOUBLE_CLICK , button_double_click_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_LONG_PRESS_START , button_long_press_start_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_LONG_PRESS_HOLD , button_long_press_hold_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_PRESS_REPEAT_DONE , button_press_repeat_done_cb , NULL ) ;
uint8_t level = 0 ;
level = iot_button_get_key_level ( g_btns [ 0 ] ) ;
ESP_LOGI ( TAG , " button level is %d " , level ) ;
while ( 1 ) {
vTaskDelay ( pdMS_TO_TICKS ( 1000 ) ) ;
}
iot_button_delete ( g_btns [ 0 ] ) ;
}
TEST_CASE ( " gpio button get event test " , " [button][iot] " )
{
button_config_t cfg = {
. type = BUTTON_TYPE_GPIO ,
. long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS ,
. short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS ,
. gpio_button_config = {
. gpio_num = 0 ,
. active_level = 0 ,
} ,
} ;
g_btns [ 0 ] = iot_button_create ( & cfg ) ;
TEST_ASSERT_NOT_NULL ( g_btns [ 0 ] ) ;
uint8_t level = 0 ;
level = iot_button_get_key_level ( g_btns [ 0 ] ) ;
ESP_LOGI ( TAG , " button level is %d " , level ) ;
while ( 1 ) {
button_event_t event = iot_button_get_event ( g_btns [ 0 ] ) ;
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 ( g_btns [ 0 ] ) ;
}
TEST_CASE ( " gpio button test power save " , " [button][iot][power save] " )
{
button_config_t cfg = {
. type = BUTTON_TYPE_GPIO ,
. long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS ,
. short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS ,
. gpio_button_config = {
. gpio_num = 0 ,
. active_level = 0 ,
} ,
} ;
g_btns [ 0 ] = iot_button_create ( & cfg ) ;
TEST_ASSERT_NOT_NULL ( g_btns [ 0 ] ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_PRESS_DOWN , button_press_down_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_PRESS_UP , button_press_up_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_PRESS_REPEAT , button_press_repeat_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_SINGLE_CLICK , button_single_click_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_DOUBLE_CLICK , button_double_click_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_LONG_PRESS_START , button_long_press_start_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_LONG_PRESS_HOLD , button_long_press_hold_cb , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , BUTTON_PRESS_REPEAT_DONE , button_press_repeat_done_cb , NULL ) ;
TEST_ASSERT_EQUAL ( ESP_OK , iot_button_stop ( ) ) ;
vTaskDelay ( pdMS_TO_TICKS ( 1000 ) ) ;
TEST_ASSERT_EQUAL ( ESP_OK , iot_button_resume ( ) ) ;
while ( 1 ) {
vTaskDelay ( pdMS_TO_TICKS ( 1000 ) ) ;
}
iot_button_delete ( g_btns [ 0 ] ) ;
}
TEST_CASE ( " matrix keyboard button test " , " [button][matrix key] " )
{
int32_t row_gpio [ 4 ] = { 4 , 5 , 6 , 7 } ;
int32_t col_gpio [ 4 ] = { 3 , 8 , 16 , 15 } ;
button_config_t cfg = {
. type = BUTTON_TYPE_MATRIX ,
. long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS ,
. short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS ,
. matrix_button_config = {
. row_gpio_num = 0 ,
. col_gpio_num = 0 ,
}
} ;
for ( int i = 0 ; i < 4 ; i + + ) {
cfg . matrix_button_config . row_gpio_num = row_gpio [ i ] ;
for ( int j = 0 ; j < 4 ; j + + ) {
cfg . matrix_button_config . col_gpio_num = col_gpio [ j ] ;
g_btns [ i * 4 + j ] = iot_button_create ( & cfg ) ;
TEST_ASSERT_NOT_NULL ( g_btns [ i * 4 + j ] ) ;
iot_button_register_cb ( g_btns [ i * 4 + j ] , BUTTON_PRESS_DOWN , button_press_down_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i * 4 + j ] , BUTTON_PRESS_UP , button_press_up_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i * 4 + j ] , BUTTON_PRESS_REPEAT , button_press_repeat_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i * 4 + j ] , BUTTON_SINGLE_CLICK , button_single_click_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i * 4 + j ] , BUTTON_DOUBLE_CLICK , button_double_click_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i * 4 + j ] , BUTTON_LONG_PRESS_START , button_long_press_start_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i * 4 + j ] , BUTTON_LONG_PRESS_HOLD , button_long_press_hold_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i * 4 + j ] , BUTTON_PRESS_REPEAT_DONE , button_press_repeat_done_cb , NULL ) ;
}
}
while ( 1 ) {
vTaskDelay ( pdMS_TO_TICKS ( 1000 ) ) ;
}
for ( int i = 0 ; i < 4 ; i + + ) {
for ( int j = 0 ; j < 4 ; j + + ) {
iot_button_delete ( g_btns [ i * 4 + j ] ) ;
}
}
}
# if CONFIG_SOC_ADC_SUPPORTED
# if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
TEST_CASE ( " adc button test " , " [button][iot] " )
{
/** ESP32-S3-Korvo board */
const uint16_t vol [ 6 ] = { 380 , 820 , 1180 , 1570 , 1980 , 2410 } ;
button_config_t cfg = { 0 } ;
cfg . type = BUTTON_TYPE_ADC ;
cfg . long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS ;
cfg . short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS ;
for ( size_t i = 0 ; i < 6 ; i + + ) {
cfg . adc_button_config . adc_channel = 7 ,
cfg . adc_button_config . button_index = i ;
if ( i = = 0 ) {
cfg . adc_button_config . min = ( 0 + vol [ i ] ) / 2 ;
} else {
cfg . adc_button_config . min = ( vol [ i - 1 ] + vol [ i ] ) / 2 ;
}
if ( i = = 5 ) {
cfg . adc_button_config . max = ( vol [ i ] + 3000 ) / 2 ;
} else {
cfg . adc_button_config . max = ( vol [ i ] + vol [ i + 1 ] ) / 2 ;
}
g_btns [ i ] = iot_button_create ( & cfg ) ;
TEST_ASSERT_NOT_NULL ( g_btns [ i ] ) ;
iot_button_register_cb ( g_btns [ i ] , BUTTON_PRESS_DOWN , button_press_down_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i ] , BUTTON_PRESS_UP , button_press_up_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i ] , BUTTON_PRESS_REPEAT , button_press_repeat_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i ] , BUTTON_SINGLE_CLICK , button_single_click_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i ] , BUTTON_DOUBLE_CLICK , button_double_click_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i ] , BUTTON_LONG_PRESS_START , button_long_press_start_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i ] , BUTTON_LONG_PRESS_HOLD , button_long_press_hold_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i ] , BUTTON_PRESS_REPEAT_DONE , button_press_repeat_done_cb , NULL ) ;
}
while ( 1 ) {
vTaskDelay ( pdMS_TO_TICKS ( 1000 ) ) ;
}
for ( size_t i = 0 ; i < 6 ; i + + ) {
iot_button_delete ( g_btns [ i ] ) ;
}
}
# else
# include "esp_adc/adc_cali.h"
static esp_err_t adc_calibration_init ( adc_unit_t unit , adc_atten_t atten , adc_cali_handle_t * out_handle )
{
# if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
# define ADC_BUTTON_WIDTH SOC_ADC_RTC_MAX_BITWIDTH
# else
# define ADC_BUTTON_WIDTH ADC_WIDTH_MAX - 1
# endif
adc_cali_handle_t handle = NULL ;
esp_err_t ret = ESP_FAIL ;
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 {
ESP_LOGE ( TAG , " Invalid arg or no memory " ) ;
}
return calibrated ? ESP_OK : ESP_FAIL ;
}
TEST_CASE ( " adc button idf5 drive test " , " [button][iot] " )
{
adc_oneshot_unit_handle_t adc1_handle ;
adc_cali_handle_t adc1_cali_handle ;
adc_oneshot_unit_init_cfg_t init_config = {
. unit_id = ADC_UNIT_1 ,
} ;
esp_err_t ret = adc_oneshot_new_unit ( & init_config , & adc1_handle ) ;
TEST_ASSERT_TRUE ( ret = = ESP_OK ) ;
/*!< use atten 11db or 12db */
adc_calibration_init ( ADC_UNIT_1 , 3 , & adc1_cali_handle ) ;
/** ESP32-S3-Korvo board */
const uint16_t vol [ 6 ] = { 380 , 820 , 1180 , 1570 , 1980 , 2410 } ;
button_config_t cfg = { 0 } ;
cfg . type = BUTTON_TYPE_ADC ;
cfg . long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS ;
cfg . short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS ;
for ( size_t i = 0 ; i < 6 ; i + + ) {
cfg . adc_button_config . adc_handle = & adc1_handle ;
cfg . adc_button_config . adc_channel = 7 ,
cfg . adc_button_config . button_index = i ;
if ( i = = 0 ) {
cfg . adc_button_config . min = ( 0 + vol [ i ] ) / 2 ;
} else {
cfg . adc_button_config . min = ( vol [ i - 1 ] + vol [ i ] ) / 2 ;
}
if ( i = = 5 ) {
cfg . adc_button_config . max = ( vol [ i ] + 3000 ) / 2 ;
} else {
cfg . adc_button_config . max = ( vol [ i ] + vol [ i + 1 ] ) / 2 ;
}
g_btns [ i ] = iot_button_create ( & cfg ) ;
TEST_ASSERT_NOT_NULL ( g_btns [ i ] ) ;
iot_button_register_cb ( g_btns [ i ] , BUTTON_PRESS_DOWN , button_press_down_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i ] , BUTTON_PRESS_UP , button_press_up_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i ] , BUTTON_PRESS_REPEAT , button_press_repeat_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i ] , BUTTON_SINGLE_CLICK , button_single_click_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i ] , BUTTON_DOUBLE_CLICK , button_double_click_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i ] , BUTTON_LONG_PRESS_START , button_long_press_start_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i ] , BUTTON_LONG_PRESS_HOLD , button_long_press_hold_cb , NULL ) ;
iot_button_register_cb ( g_btns [ i ] , BUTTON_PRESS_REPEAT_DONE , button_press_repeat_done_cb , NULL ) ;
}
while ( 1 ) {
vTaskDelay ( pdMS_TO_TICKS ( 1000 ) ) ;
}
for ( size_t i = 0 ; i < 6 ; i + + ) {
iot_button_delete ( g_btns [ i ] ) ;
}
}
# endif
# endif // CONFIG_SOC_ADC_SUPPORTED
# define GPIO_OUTPUT_IO_45 45
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 ( g_btns [ 0 ] ) = = state ) {
xEventGroupSetBits ( g_check , BIT ( 1 ) ) ;
}
}
static void button_auto_check_cb ( void * arg , void * data )
{
if ( iot_button_get_event ( g_btns [ 0 ] ) = = 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 ) ) ;
button_config_t cfg = {
. type = BUTTON_TYPE_GPIO ,
. long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS ,
. short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS ,
. gpio_button_config = {
. gpio_num = 0 ,
. active_level = 0 ,
} ,
} ;
g_btns [ 0 ] = iot_button_create ( & cfg ) ;
TEST_ASSERT_NOT_NULL ( g_btns [ 0 ] ) ;
/* 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_config_t btn_cfg ;
btn_cfg . event = i ;
btn_cfg . event_data . multiple_clicks . clicks = 4 ;
iot_button_register_event_cb ( g_btns [ 0 ] , btn_cfg , button_auto_check_cb_1 , NULL ) ;
iot_button_register_event_cb ( g_btns [ 0 ] , btn_cfg , button_auto_check_cb , NULL ) ;
} else {
iot_button_register_cb ( g_btns [ 0 ] , i , button_auto_check_cb_1 , NULL ) ;
iot_button_register_cb ( g_btns [ 0 ] , i , button_auto_check_cb , NULL ) ;
}
}
TEST_ASSERT_EQUAL ( ESP_OK , iot_button_set_param ( g_btns [ 0 ] , 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_config_t btn_cfg ;
btn_cfg . event = i ;
if ( i = = BUTTON_MULTIPLE_CLICK ) {
btn_cfg . event_data . multiple_clicks . clicks = 4 ;
} else if ( i = = BUTTON_LONG_PRESS_UP | | i = = BUTTON_LONG_PRESS_START ) {
btn_cfg . event_data . long_press . press_time = 1500 ;
}
iot_button_unregister_event ( g_btns [ 0 ] , btn_cfg , button_auto_check_cb ) ;
iot_button_unregister_event ( g_btns [ 0 ] , btn_cfg , button_auto_check_cb_1 ) ;
}
TEST_ASSERT_EQUAL ( ESP_OK , iot_button_delete ( g_btns [ 0 ] ) ) ;
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 ( g_btns [ 0 ] ) ;
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 ( state ) , 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 ) ;
button_config_t cfg = {
. type = BUTTON_TYPE_GPIO ,
. long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS ,
. short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS ,
. gpio_button_config = {
. gpio_num = 0 ,
. active_level = 0 ,
} ,
} ;
g_btns [ 0 ] = iot_button_create ( & cfg ) ;
TEST_ASSERT_NOT_NULL ( g_btns [ 0 ] ) ;
button_event_config_t btn_cfg ;
btn_cfg . event = BUTTON_LONG_PRESS_START ;
for ( int i = 0 ; i < 5 ; i + + ) {
btn_cfg . event_data . long_press . press_time = long_press_time [ i ] ;
uint32_t data = ( btn_cfg . event < < 16 ) | long_press_time [ i ] ;
iot_button_register_event_cb ( g_btns [ 0 ] , btn_cfg , 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 ( g_btns [ 0 ] , BUTTON_LONG_PRESS_START ) ;
btn_cfg . event = BUTTON_LONG_PRESS_UP ;
for ( int i = 0 ; i < 5 ; i + + ) {
btn_cfg . event_data . long_press . press_time = long_press_time [ i ] ;
uint32_t data = ( btn_cfg . event < < 16 ) | long_press_time [ i ] ;
iot_button_register_event_cb ( g_btns [ 0 ] , btn_cfg , 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 ( g_btns [ 0 ] ) ) ;
vSemaphoreDelete ( long_press_check ) ;
vSemaphoreDelete ( long_press_auto_check_pass ) ;
vTaskDelay ( pdMS_TO_TICKS ( 100 ) ) ;
}
static void check_leak ( size_t before_free , size_t after_free , const char * type )
{
ssize_t delta = after_free - before_free ;
printf ( " MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d) \n " , type , before_free , after_free , delta ) ;
TEST_ASSERT_MESSAGE ( delta > = TEST_MEMORY_LEAK_THRESHOLD , " memory leak " ) ;
}
void setUp ( void )
{
before_free_8bit = heap_caps_get_free_size ( MALLOC_CAP_8BIT ) ;
before_free_32bit = heap_caps_get_free_size ( MALLOC_CAP_32BIT ) ;
}
void tearDown ( void )
{
size_t after_free_8bit = heap_caps_get_free_size ( MALLOC_CAP_8BIT ) ;
size_t after_free_32bit = heap_caps_get_free_size ( MALLOC_CAP_32BIT ) ;
check_leak ( before_free_8bit , after_free_8bit , " 8BIT " ) ;
check_leak ( before_free_32bit , after_free_32bit , " 32BIT " ) ;
}
void app_main ( void )
{
/*
* ____ _ _ _______ _
* | _ \ | | | | | __ __ | | |
* | | _ ) | _ _ | | _ | | _ ___ _ __ | | ___ ___ | | _
* | _ < | | | | | __ | | __ | / _ \ | ' _ \ | | / _ \ / __ | | __ |
* | | _ ) | | | _ | | | | _ | | _ | ( _ ) | | | | | | | | __ / \ __ \ | | _
* | ____ / \ __ , _ | \ __ | \ __ | \ ___ / | _ | | _ | | _ | \ ___ | | ___ / \ __ |
*/
printf ( " ____ _ _ _______ _ \n " ) ;
printf ( " | _ \\ | | | | |__ __| | | \n " ) ;
printf ( " | |_) | _ _ | |_ | |_ ___ _ __ | | ___ ___ | |_ \n " ) ;
printf ( " | _ < | | | || __|| __|/ _ \\ | '_ \\ | | / _ \\ / __|| __| \n " ) ;
printf ( " | |_) || |_| || |_ | |_| (_) || | | | | || __/ \\ __ \\ | |_ \n " ) ;
printf ( " |____/ \\ __,_| \\ __| \\ __| \\ ___/ |_| |_| |_| \\ ___||___/ \\ __| \n " ) ;
unity_run_menu ( ) ;
}