From 22a409973edb5085090bd262e8dbf27e0a601551 Mon Sep 17 00:00:00 2001 From: chenjintao Date: Tue, 18 Aug 2026 16:00:10 +0800 Subject: [PATCH] feat(control-plane): add configurable Wi-Fi reset button --- components/control_plane/CMakeLists.txt | 1 + .../internal/control_plane_events.h | 2 +- .../internal/control_plane_internal.h | 1 + .../src/runtime/control_plane_key_input.c | 59 +++++++++++++++++++ .../src/runtime/control_plane_reset_service.c | 27 +++++++++ .../src/runtime/control_plane_system_events.c | 9 +++ docs/firmware-pin-usage.md | 1 + docs/gpio-map.md | 2 +- main/Kconfig.projbuild | 54 ++++++++++++++++- sdkconfig.defaults | 1 + 10 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 components/control_plane/src/runtime/control_plane_reset_service.c diff --git a/components/control_plane/CMakeLists.txt b/components/control_plane/CMakeLists.txt index 15b00b8..34c49a1 100644 --- a/components/control_plane/CMakeLists.txt +++ b/components/control_plane/CMakeLists.txt @@ -14,6 +14,7 @@ idf_component_register( "src/runtime/control_plane_fortune_service.c" "src/runtime/control_plane_system_events.c" "src/runtime/control_plane_key_input.c" + "src/runtime/control_plane_reset_service.c" "src/runtime/control_plane_remote_log_service.c" "src/runtime/control_plane_runtime_workers.c" "src/runtime/control_plane_ota.c" diff --git a/components/control_plane/internal/control_plane_events.h b/components/control_plane/internal/control_plane_events.h index 288420a..c62106f 100644 --- a/components/control_plane/internal/control_plane_events.h +++ b/components/control_plane/internal/control_plane_events.h @@ -24,6 +24,7 @@ typedef enum { CONTROL_PLANE_KEY_ID_PTT = 0, CONTROL_PLANE_KEY_ID_SW1, CONTROL_PLANE_KEY_ID_SW2, + CONTROL_PLANE_KEY_ID_RESET, } control_plane_key_id_t; typedef enum { @@ -54,4 +55,3 @@ typedef struct { control_plane_key_event_t key; } data; } control_plane_system_event_t; - diff --git a/components/control_plane/internal/control_plane_internal.h b/components/control_plane/internal/control_plane_internal.h index e15828f..2c9bd69 100644 --- a/components/control_plane/internal/control_plane_internal.h +++ b/components/control_plane/internal/control_plane_internal.h @@ -158,6 +158,7 @@ const char *control_plane_key_event_type_name(control_plane_key_event_type_t typ esp_err_t control_plane_start_key_input_task(void); void control_plane_stop_key_input_task(void); +void control_plane_reset_wifi_provisioning_and_restart(void); esp_err_t control_plane_schedule_websocket_wait_task(void); void control_plane_set_ptt_active(bool active); esp_err_t control_plane_enter_mini_program_reprovisioning(const char *reason); diff --git a/components/control_plane/src/runtime/control_plane_key_input.c b/components/control_plane/src/runtime/control_plane_key_input.c index d338214..cd5b327 100644 --- a/components/control_plane/src/runtime/control_plane_key_input.c +++ b/components/control_plane/src/runtime/control_plane_key_input.c @@ -94,6 +94,18 @@ #define CONFIG_TQ_CALIBRATION_SWITCH_PULLDOWN 0 #endif +#ifndef CONFIG_TQ_RESET_BUTTON_ENABLE +#define CONFIG_TQ_RESET_BUTTON_ENABLE 0 +#endif + +#ifndef CONFIG_TQ_RESET_BUTTON_PULLUP +#define CONFIG_TQ_RESET_BUTTON_PULLUP 0 +#endif + +#ifndef CONFIG_TQ_RESET_BUTTON_PULLDOWN +#define CONFIG_TQ_RESET_BUTTON_PULLDOWN 0 +#endif + static const char *TAG = "cp_key_input"; typedef struct { @@ -181,6 +193,22 @@ static const control_plane_key_config_t s_key_configs[] = { .enable_long_press = true, .enable_double_click = true, }, +#if CONFIG_TQ_RESET_BUTTON_ENABLE + { + .key_id = CONTROL_PLANE_KEY_ID_RESET, + .gpio_num = CONFIG_TQ_RESET_BUTTON_GPIO, + .active_level = CONFIG_TQ_RESET_BUTTON_ACTIVE_LEVEL, + .pullup_enabled = CONFIG_TQ_RESET_BUTTON_PULLUP, + .pulldown_enabled = CONFIG_TQ_RESET_BUTTON_PULLDOWN, + .debounce_ms = CONFIG_TQ_KEY_DEBOUNCE_INTERVAL_MS, + .long_press_ms = CONFIG_TQ_RESET_BUTTON_LONG_PRESS_MS, + .double_click_ms = 0, + .emit_press_release = false, + .enable_short_press = false, + .enable_long_press = true, + .enable_double_click = false, + }, +#endif }; static QueueHandle_t s_gpio_irq_queue; @@ -262,6 +290,31 @@ static esp_err_t control_plane_configure_key_gpio(const control_plane_key_config return gpio_intr_enable(config->gpio_num); } +static esp_err_t control_plane_validate_key_configs(void) +{ + for (size_t i = 0; i < sizeof(s_key_configs) / sizeof(s_key_configs[0]); ++i) { + const control_plane_key_config_t *config = &s_key_configs[i]; + if (config->pullup_enabled && config->pulldown_enabled) { + ESP_LOGE(TAG, + "key=%s gpio=%d cannot enable pull-up and pull-down together", + control_plane_key_id_name(config->key_id), + (int)config->gpio_num); + return ESP_ERR_INVALID_ARG; + } + for (size_t j = 0; j < i; ++j) { + if (s_key_configs[j].gpio_num == config->gpio_num) { + ESP_LOGE(TAG, + "key GPIO conflict: key=%s and key=%s both use gpio=%d", + control_plane_key_id_name(s_key_configs[j].key_id), + control_plane_key_id_name(config->key_id), + (int)config->gpio_num); + return ESP_ERR_INVALID_ARG; + } + } + } + return ESP_OK; +} + static void control_plane_emit_key_event(const control_plane_key_config_t *config, control_plane_key_event_type_t type, int level, @@ -469,6 +522,12 @@ static void control_plane_key_input_task(void *arg) esp_err_t setup_err = ESP_OK; memset(s_key_states, 0, sizeof(s_key_states)); + setup_err = control_plane_validate_key_configs(); + if (setup_err != ESP_OK) { + s_key_input_task = NULL; + vTaskDelete(NULL); + return; + } for (size_t i = 0; i < sizeof(s_key_configs) / sizeof(s_key_configs[0]); ++i) { const control_plane_key_config_t *config = &s_key_configs[i]; s_key_states[i].initialized = true; diff --git a/components/control_plane/src/runtime/control_plane_reset_service.c b/components/control_plane/src/runtime/control_plane_reset_service.c new file mode 100644 index 0000000..50cb41b --- /dev/null +++ b/components/control_plane/src/runtime/control_plane_reset_service.c @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "control_plane_internal.h" + +#include "esp_log.h" + +static const char *TAG = "cp_reset"; + +void control_plane_reset_wifi_provisioning_and_restart(void) +{ + ESP_LOGW(TAG, "Wi-Fi reset requested; clearing saved credentials"); + + esp_err_t err = domain_runtime_wifi_portal_clear_saved_credentials(); + if (err != ESP_OK) { + ESP_LOGE(TAG, + "Wi-Fi credential reset failed; device will not restart: %s", + esp_err_to_name(err)); + return; + } + + ESP_LOGW(TAG, "Saved Wi-Fi credentials cleared; restarting device"); + domain_runtime_restart(); +} diff --git a/components/control_plane/src/runtime/control_plane_system_events.c b/components/control_plane/src/runtime/control_plane_system_events.c index a57425a..66cdfbe 100644 --- a/components/control_plane/src/runtime/control_plane_system_events.c +++ b/components/control_plane/src/runtime/control_plane_system_events.c @@ -84,6 +84,8 @@ const char *control_plane_key_id_name(control_plane_key_id_t key_id) return "SW1"; case CONTROL_PLANE_KEY_ID_SW2: return "SW2"; + case CONTROL_PLANE_KEY_ID_RESET: + return "RESET"; default: return "UNKNOWN"; } @@ -128,6 +130,13 @@ static void control_plane_dispatch_key_event(const control_plane_key_event_t *ev event->press_duration_ms, event->click_count); + if (event->key_id == CONTROL_PLANE_KEY_ID_RESET) { + if (event->type == CONTROL_PLANE_KEY_EVENT_LONG_PRESS_START) { + control_plane_reset_wifi_provisioning_and_restart(); + } + return; + } + if (event->key_id == CONTROL_PLANE_KEY_ID_SW2) { if (event->type == CONTROL_PLANE_KEY_EVENT_SHORT_PRESS) { esp_err_t request_err = control_plane_request_cloud_chouqian(); diff --git a/docs/firmware-pin-usage.md b/docs/firmware-pin-usage.md index 17a6b0d..bf9dd07 100644 --- a/docs/firmware-pin-usage.md +++ b/docs/firmware-pin-usage.md @@ -20,6 +20,7 @@ the ESP32-S3 MCU perspective. | 11 | `CONFIG_MULTIMODAL_AUDIO_PA_GPIO` | Output | Audio amplifier | PA enable | Managed by codec GPIO interface | | 12 | `CONFIG_TQ_CALIBRATION_SWITCH_GPIO` | Input | Turntable calibration | Button 1 / SW1 direction calibration | Active-low, external and internal pull-up | | 13 | `CONFIG_MULTIMODAL_PTT_BUTTON_GPIO` | Input | Keys / multimodal | Button 2 / K2 push-to-talk | Active-low, external and internal pull-up | +| 14 | `CONFIG_TQ_RESET_BUTTON_GPIO` | Input when enabled | Optional reset key | Hold to clear saved Wi-Fi credentials and restart | Disabled by default; active-low with internal pull-up when enabled | | 15 | `CONFIG_TQ_SCREEN_SPI_CS_GPIO` | Output | LCD | ST7789P3 SPI chip select | SPI2 host | | 16 | `CONFIG_TQ_SCREEN_DC_GPIO` | Output | LCD | ST7789P3 data/command select | Initialized low | | 17 | `CONFIG_TQ_SCREEN_RESET_PIN` | Output | LCD | ST7789P3 panel reset | Active-low reset; `-1` disables hardware reset | diff --git a/docs/gpio-map.md b/docs/gpio-map.md index 4e5d08d..a33c2d9 100644 --- a/docs/gpio-map.md +++ b/docs/gpio-map.md @@ -30,7 +30,7 @@ hold net; that assignment is not valid for the current board. | 19 | GPIO11 | PA_CTRL | Output | Audio power-amplifier enable | Amplifier control | | 20 | GPIO12 | SW1 | Input | Button 1 / turntable calibration switch | External 10 kOhm pull-up; active low | | 21 | GPIO13 | SW2 | Input | Button 2 / K2 push-to-talk input | External 10 kOhm pull-up; active low | -| 22 | GPIO14 | UNASSIGNED | N/A | No connection assigned | Reserved; no signal assigned | +| 22 | GPIO14 | UNASSIGNED / RESET_KEY | Optional input | Configurable Wi-Fi reset button | Unassigned and disabled by default; active-low with internal pull-up when enabled | | 23 | GPIO21 | IN4 | Output | ULN2003 IN4 | Stepper motor phase control | | 24 | GPIO47 | IN3 | Output | ULN2003 IN3 | Stepper motor phase control | | 25 | GPIO48 | IN2 | Output | ULN2003 IN2 | Stepper motor phase control | diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 97d9be4..036fcc1 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -389,8 +389,9 @@ menu "Example Configuration" default y help Start the staged GPIO interrupt key input task for PTT, SW1, - and SW2. SW1 remains the turntable calibration input and is - also reported for post-boot button diagnostics. + SW2, and the optional Wi-Fi reset button. SW1 remains the + turntable calibration input and is also reported for post-boot + button diagnostics. config TQ_KEY_INPUT_TASK_STACK int "Key input task stack size" @@ -448,6 +449,55 @@ menu "Example Configuration" help Ignore raw GPIO chatter briefly after a double-click has been emitted. + config TQ_RESET_BUTTON_ENABLE + bool "Enable Wi-Fi reset button" + default n + depends on TQ_KEY_INPUT_ENABLE + help + Enable a dedicated reset button. Holding the button for the + configured duration clears saved Wi-Fi credentials and then + restarts the device. Other persisted data is not changed. + + config TQ_RESET_BUTTON_GPIO + int "Wi-Fi reset button GPIO" + default 14 + range 0 48 + depends on TQ_RESET_BUTTON_ENABLE + help + GPIO used for the dedicated Wi-Fi reset button when enabled. + GPIO14 is unassigned on the current board revision. + + config TQ_RESET_BUTTON_ACTIVE_LEVEL + int "Wi-Fi reset button active level" + default 0 + range 0 1 + depends on TQ_RESET_BUTTON_ENABLE + help + Logic level that means the Wi-Fi reset button is pressed. + + config TQ_RESET_BUTTON_PULLUP + bool "Enable Wi-Fi reset button pull-up" + default y + depends on TQ_RESET_BUTTON_ENABLE + help + Enable the internal pull-up for active-low wiring. + + config TQ_RESET_BUTTON_PULLDOWN + bool "Enable Wi-Fi reset button pull-down" + default n + depends on TQ_RESET_BUTTON_ENABLE + help + Enable the internal pull-down for active-high wiring. + + config TQ_RESET_BUTTON_LONG_PRESS_MS + int "Wi-Fi reset button hold duration (ms)" + default 5000 + range 1000 15000 + depends on TQ_RESET_BUTTON_ENABLE + help + Continuous press duration required before saved Wi-Fi + credentials are cleared and the device restarts. + choice TQ_PTT_INTERACTION_MODE prompt "PTT interaction mode" default TQ_PTT_MODE_CLICK_TO_TALK diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 4d56079..7663708 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -181,6 +181,7 @@ CONFIG_MULTIMODAL_PTT_BUTTON_GPIO=13 CONFIG_MULTIMODAL_PTT_BUTTON_ACTIVE_LEVEL=0 CONFIG_MULTIMODAL_PTT_BUTTON_PULLUP=y # CONFIG_MULTIMODAL_PTT_BUTTON_PULLDOWN is not set +# CONFIG_TQ_RESET_BUTTON_ENABLE is not set # CONFIG_MULTIMODAL_AUDIO_CHANNEL_PROBE_ON_INIT is not set CONFIG_MULTIMODAL_HEARTBEAT_INTERVAL_MS=50000 CONFIG_MULTIMODAL_NO_RESPONSE_TIMEOUT_MS=60000