325 lines
9.5 KiB
C
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
|
|
}
|