feat(control-plane): add configurable Wi-Fi reset button

This commit is contained in:
chenjintao
2026-08-18 16:00:10 +08:00
parent 96f5e535d3
commit 22a409973e
10 changed files with 153 additions and 4 deletions
+1
View File
@@ -14,6 +14,7 @@ idf_component_register(
"src/runtime/control_plane_fortune_service.c" "src/runtime/control_plane_fortune_service.c"
"src/runtime/control_plane_system_events.c" "src/runtime/control_plane_system_events.c"
"src/runtime/control_plane_key_input.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_remote_log_service.c"
"src/runtime/control_plane_runtime_workers.c" "src/runtime/control_plane_runtime_workers.c"
"src/runtime/control_plane_ota.c" "src/runtime/control_plane_ota.c"
@@ -24,6 +24,7 @@ typedef enum {
CONTROL_PLANE_KEY_ID_PTT = 0, CONTROL_PLANE_KEY_ID_PTT = 0,
CONTROL_PLANE_KEY_ID_SW1, CONTROL_PLANE_KEY_ID_SW1,
CONTROL_PLANE_KEY_ID_SW2, CONTROL_PLANE_KEY_ID_SW2,
CONTROL_PLANE_KEY_ID_RESET,
} control_plane_key_id_t; } control_plane_key_id_t;
typedef enum { typedef enum {
@@ -54,4 +55,3 @@ typedef struct {
control_plane_key_event_t key; control_plane_key_event_t key;
} data; } data;
} control_plane_system_event_t; } control_plane_system_event_t;
@@ -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); esp_err_t control_plane_start_key_input_task(void);
void control_plane_stop_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); esp_err_t control_plane_schedule_websocket_wait_task(void);
void control_plane_set_ptt_active(bool active); void control_plane_set_ptt_active(bool active);
esp_err_t control_plane_enter_mini_program_reprovisioning(const char *reason); esp_err_t control_plane_enter_mini_program_reprovisioning(const char *reason);
@@ -94,6 +94,18 @@
#define CONFIG_TQ_CALIBRATION_SWITCH_PULLDOWN 0 #define CONFIG_TQ_CALIBRATION_SWITCH_PULLDOWN 0
#endif #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"; static const char *TAG = "cp_key_input";
typedef struct { typedef struct {
@@ -181,6 +193,22 @@ static const control_plane_key_config_t s_key_configs[] = {
.enable_long_press = true, .enable_long_press = true,
.enable_double_click = 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; 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); 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, static void control_plane_emit_key_event(const control_plane_key_config_t *config,
control_plane_key_event_type_t type, control_plane_key_event_type_t type,
int level, int level,
@@ -469,6 +522,12 @@ static void control_plane_key_input_task(void *arg)
esp_err_t setup_err = ESP_OK; esp_err_t setup_err = ESP_OK;
memset(s_key_states, 0, sizeof(s_key_states)); 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) { 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]; const control_plane_key_config_t *config = &s_key_configs[i];
s_key_states[i].initialized = true; s_key_states[i].initialized = true;
@@ -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();
}
@@ -84,6 +84,8 @@ const char *control_plane_key_id_name(control_plane_key_id_t key_id)
return "SW1"; return "SW1";
case CONTROL_PLANE_KEY_ID_SW2: case CONTROL_PLANE_KEY_ID_SW2:
return "SW2"; return "SW2";
case CONTROL_PLANE_KEY_ID_RESET:
return "RESET";
default: default:
return "UNKNOWN"; 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->press_duration_ms,
event->click_count); 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->key_id == CONTROL_PLANE_KEY_ID_SW2) {
if (event->type == CONTROL_PLANE_KEY_EVENT_SHORT_PRESS) { if (event->type == CONTROL_PLANE_KEY_EVENT_SHORT_PRESS) {
esp_err_t request_err = control_plane_request_cloud_chouqian(); esp_err_t request_err = control_plane_request_cloud_chouqian();
+1
View File
@@ -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 | | 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 | | 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 | | 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 | | 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 | | 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 | | 17 | `CONFIG_TQ_SCREEN_RESET_PIN` | Output | LCD | ST7789P3 panel reset | Active-low reset; `-1` disables hardware reset |
+1 -1
View File
@@ -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 | | 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 | | 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 | | 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 | | 23 | GPIO21 | IN4 | Output | ULN2003 IN4 | Stepper motor phase control |
| 24 | GPIO47 | IN3 | Output | ULN2003 IN3 | Stepper motor phase control | | 24 | GPIO47 | IN3 | Output | ULN2003 IN3 | Stepper motor phase control |
| 25 | GPIO48 | IN2 | Output | ULN2003 IN2 | Stepper motor phase control | | 25 | GPIO48 | IN2 | Output | ULN2003 IN2 | Stepper motor phase control |
+52 -2
View File
@@ -389,8 +389,9 @@ menu "Example Configuration"
default y default y
help help
Start the staged GPIO interrupt key input task for PTT, SW1, Start the staged GPIO interrupt key input task for PTT, SW1,
and SW2. SW1 remains the turntable calibration input and is SW2, and the optional Wi-Fi reset button. SW1 remains the
also reported for post-boot button diagnostics. turntable calibration input and is also reported for post-boot
button diagnostics.
config TQ_KEY_INPUT_TASK_STACK config TQ_KEY_INPUT_TASK_STACK
int "Key input task stack size" int "Key input task stack size"
@@ -448,6 +449,55 @@ menu "Example Configuration"
help help
Ignore raw GPIO chatter briefly after a double-click has been emitted. 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 choice TQ_PTT_INTERACTION_MODE
prompt "PTT interaction mode" prompt "PTT interaction mode"
default TQ_PTT_MODE_CLICK_TO_TALK default TQ_PTT_MODE_CLICK_TO_TALK
+1
View File
@@ -181,6 +181,7 @@ CONFIG_MULTIMODAL_PTT_BUTTON_GPIO=13
CONFIG_MULTIMODAL_PTT_BUTTON_ACTIVE_LEVEL=0 CONFIG_MULTIMODAL_PTT_BUTTON_ACTIVE_LEVEL=0
CONFIG_MULTIMODAL_PTT_BUTTON_PULLUP=y CONFIG_MULTIMODAL_PTT_BUTTON_PULLUP=y
# CONFIG_MULTIMODAL_PTT_BUTTON_PULLDOWN is not set # 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_AUDIO_CHANNEL_PROBE_ON_INIT is not set
CONFIG_MULTIMODAL_HEARTBEAT_INTERVAL_MS=50000 CONFIG_MULTIMODAL_HEARTBEAT_INTERVAL_MS=50000
CONFIG_MULTIMODAL_NO_RESPONSE_TIMEOUT_MS=60000 CONFIG_MULTIMODAL_NO_RESPONSE_TIMEOUT_MS=60000