Files
lucky-cat/components/control_plane/src/runtime/control_plane_system_events.c
T

325 lines
9.5 KiB
C

/*
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "control_plane_internal.h"
#include <inttypes.h>
#include <stdbool.h>
#include "control_plane_events.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "sdkconfig.h"
#ifndef CONFIG_TQ_SYSTEM_EVENT_ENABLE
#define CONFIG_TQ_SYSTEM_EVENT_ENABLE 1
#endif
#ifndef CONFIG_TQ_SYSTEM_EVENT_QUEUE_LEN
#define CONFIG_TQ_SYSTEM_EVENT_QUEUE_LEN 32
#endif
#ifndef CONFIG_TQ_SYSTEM_EVENT_TASK_STACK
#define CONFIG_TQ_SYSTEM_EVENT_TASK_STACK 4096
#endif
#if !defined(CONFIG_TQ_PTT_MODE_HOLD_TO_TALK) && !defined(CONFIG_TQ_PTT_MODE_CLICK_TO_TALK)
#define CONFIG_TQ_PTT_MODE_CLICK_TO_TALK 1
#endif
#define CONTROL_PLANE_PTT_HOLD_STEP_MS 80
#ifndef CONFIG_TQ_LOCAL_TASK_CORE
#define CONFIG_TQ_LOCAL_TASK_CORE 1
#endif
#if CONFIG_TQ_LOCAL_TASK_CORE < 0
#define CONTROL_PLANE_SYSTEM_EVENT_TASK_CORE tskNO_AFFINITY
#else
#define CONTROL_PLANE_SYSTEM_EVENT_TASK_CORE ((BaseType_t)CONFIG_TQ_LOCAL_TASK_CORE)
#endif
static const char *TAG = "cp_system_event";
static QueueHandle_t s_system_event_queue;
static TaskHandle_t s_system_event_task;
static volatile bool s_stop_requested;
static const char *control_plane_event_family_name(control_plane_event_family_t family)
{
switch (family) {
case CONTROL_PLANE_EVENT_FAMILY_KEY:
return "KEY";
case CONTROL_PLANE_EVENT_FAMILY_WIFI:
return "WIFI";
case CONTROL_PLANE_EVENT_FAMILY_MULTIMODAL:
return "MULTIMODAL";
case CONTROL_PLANE_EVENT_FAMILY_LCD:
return "LCD";
case CONTROL_PLANE_EVENT_FAMILY_DIRECTION:
return "DIRECTION";
case CONTROL_PLANE_EVENT_FAMILY_CALIBRATION:
return "CALIBRATION";
case CONTROL_PLANE_EVENT_FAMILY_RUNTIME:
return "RUNTIME";
default:
return "UNKNOWN";
}
}
const char *control_plane_key_id_name(control_plane_key_id_t key_id)
{
switch (key_id) {
case CONTROL_PLANE_KEY_ID_PTT:
return "PTT";
case CONTROL_PLANE_KEY_ID_SW1:
return "SW1";
case CONTROL_PLANE_KEY_ID_SW2:
return "SW2";
default:
return "UNKNOWN";
}
}
const char *control_plane_key_event_type_name(control_plane_key_event_type_t type)
{
switch (type) {
case CONTROL_PLANE_KEY_EVENT_PRESSED:
return "PRESSED";
case CONTROL_PLANE_KEY_EVENT_RELEASED:
return "RELEASED";
case CONTROL_PLANE_KEY_EVENT_SHORT_PRESS:
return "SHORT_PRESS";
case CONTROL_PLANE_KEY_EVENT_LONG_PRESS_START:
return "LONG_PRESS_START";
case CONTROL_PLANE_KEY_EVENT_LONG_PRESS_RELEASE:
return "LONG_PRESS_RELEASE";
case CONTROL_PLANE_KEY_EVENT_DOUBLE_CLICK:
return "DOUBLE_CLICK";
case CONTROL_PLANE_KEY_EVENT_REPEAT:
return "REPEAT";
case CONTROL_PLANE_KEY_EVENT_CANCELLED:
return "CANCELLED";
default:
return "UNKNOWN";
}
}
static void control_plane_dispatch_key_event(const control_plane_key_event_t *event)
{
if (event == NULL) {
return;
}
ESP_LOGI(TAG,
"key event key=%s type=%s gpio=%d level=%d duration=%" PRIu32 "ms clicks=%" PRIu32,
control_plane_key_id_name(event->key_id),
control_plane_key_event_type_name(event->type),
(int)event->gpio_num,
event->level,
event->press_duration_ms,
event->click_count);
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();
if (request_err == ESP_ERR_INVALID_STATE) {
ESP_LOGI(TAG, "Cloud chouqian request skipped: multimodal session unavailable or request pending");
} else if (request_err != ESP_OK) {
ESP_LOGW(TAG,
"Cloud chouqian request failed: %s",
esp_err_to_name(request_err));
}
}
if (event->type == CONTROL_PLANE_KEY_EVENT_DOUBLE_CLICK) {
const domain_expression_request_t request = {
.id = DOMAIN_EXPRESSION_WUXING,
.trigger = DOMAIN_EXPRESSION_TRIGGER_SW2_DOUBLE_CLICK,
};
esp_err_t expr_err = domain_runtime_request_expression(&request);
if (expr_err != ESP_OK && expr_err != ESP_ERR_INVALID_STATE) {
ESP_LOGW(TAG, "SW2 double-click wuxing expression failed: %s", esp_err_to_name(expr_err));
}
}
return;
}
if (event->key_id != CONTROL_PLANE_KEY_ID_PTT) {
return;
}
switch (event->type) {
case CONTROL_PLANE_KEY_EVENT_PRESSED: {
#if CONFIG_TQ_PTT_MODE_CLICK_TO_TALK
control_plane_multimodal_runtime_state_t state = {0};
if (control_plane_multimodal_get_runtime_state(&state) == ESP_OK &&
state.remote_responding) {
esp_err_t err = control_plane_multimodal_send_request_to_speak();
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
ESP_LOGW(TAG, "PTT barge-in request failed: %s", esp_err_to_name(err));
}
}
#else
control_plane_set_ptt_active(true);
#endif
break;
}
case CONTROL_PLANE_KEY_EVENT_RELEASED: {
#if !CONFIG_TQ_PTT_MODE_CLICK_TO_TALK
control_plane_set_ptt_active(false);
#endif
break;
}
case CONTROL_PLANE_KEY_EVENT_SHORT_PRESS: {
#if CONFIG_TQ_PTT_MODE_CLICK_TO_TALK
control_plane_set_ptt_active(true);
#endif
break;
}
case CONTROL_PLANE_KEY_EVENT_DOUBLE_CLICK: {
const domain_expression_request_t request = {
.id = DOMAIN_EXPRESSION_MUYU,
.trigger = DOMAIN_EXPRESSION_TRIGGER_PTT_DOUBLE_CLICK,
};
esp_err_t expr_err = domain_runtime_request_expression(&request);
if (expr_err != ESP_OK && expr_err != ESP_ERR_INVALID_STATE) {
ESP_LOGW(TAG, "PTT double-click expression failed: %s", esp_err_to_name(expr_err));
}
break;
}
default:
break;
}
}
static void control_plane_dispatch_system_event(const control_plane_system_event_t *event)
{
if (event == NULL) {
return;
}
ESP_LOGI(TAG, "dispatch family=%s", control_plane_event_family_name(event->family));
switch (event->family) {
case CONTROL_PLANE_EVENT_FAMILY_KEY:
control_plane_dispatch_key_event(&event->data.key);
break;
default:
ESP_LOGW(TAG, "unhandled event family=%s", control_plane_event_family_name(event->family));
break;
}
}
static void control_plane_system_event_task(void *arg)
{
(void)arg;
ESP_LOGI(TAG,
"System event task started queue_len=%d",
CONFIG_TQ_SYSTEM_EVENT_QUEUE_LEN);
while (!s_stop_requested) {
control_plane_system_event_t event = {0};
if (xQueueReceive(s_system_event_queue, &event, pdMS_TO_TICKS(200)) == pdTRUE) {
control_plane_dispatch_system_event(&event);
}
}
ESP_LOGI(TAG, "System event task stopped");
s_system_event_task = NULL;
vTaskDelete(NULL);
}
esp_err_t control_plane_post_system_event(const control_plane_system_event_t *event, uint32_t timeout_ms)
{
#if CONFIG_TQ_SYSTEM_EVENT_ENABLE
if (event == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (s_system_event_queue == NULL) {
return ESP_ERR_INVALID_STATE;
}
TickType_t timeout_ticks = pdMS_TO_TICKS(timeout_ms);
if (xQueueSend(s_system_event_queue, event, timeout_ticks) != pdTRUE) {
return ESP_ERR_TIMEOUT;
}
return ESP_OK;
#else
(void)event;
(void)timeout_ms;
return ESP_ERR_NOT_SUPPORTED;
#endif
}
int64_t control_plane_event_timestamp_ms(void)
{
return esp_timer_get_time() / 1000;
}
esp_err_t control_plane_start_system_event_task(void)
{
#if CONFIG_TQ_SYSTEM_EVENT_ENABLE
if (s_system_event_task != NULL) {
return ESP_OK;
}
if (s_system_event_queue == NULL) {
s_system_event_queue = xQueueCreate(CONFIG_TQ_SYSTEM_EVENT_QUEUE_LEN,
sizeof(control_plane_system_event_t));
if (s_system_event_queue == NULL) {
return ESP_ERR_NO_MEM;
}
}
s_stop_requested = false;
BaseType_t created = xTaskCreatePinnedToCore(control_plane_system_event_task,
"cp_system_event",
CONFIG_TQ_SYSTEM_EVENT_TASK_STACK,
NULL,
4,
&s_system_event_task,
CONTROL_PLANE_SYSTEM_EVENT_TASK_CORE);
if (created != pdPASS) {
s_system_event_task = NULL;
return ESP_ERR_NO_MEM;
}
return ESP_OK;
#else
ESP_LOGI(TAG, "System event task disabled by configuration");
return ESP_OK;
#endif
}
void control_plane_stop_system_event_task(void)
{
#if CONFIG_TQ_SYSTEM_EVENT_ENABLE
s_stop_requested = true;
control_plane_set_ptt_active(false);
for (int i = 0; i < 20 && s_system_event_task != NULL; ++i) {
vTaskDelay(pdMS_TO_TICKS(50));
}
if (s_system_event_task != NULL) {
vTaskDelete(s_system_event_task);
s_system_event_task = NULL;
}
if (s_system_event_queue != NULL) {
vQueueDelete(s_system_event_queue);
s_system_event_queue = NULL;
}
#endif
}