281 lines
8.6 KiB
C
281 lines
8.6 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include "control_plane.h"
|
|
|
|
#include "control_plane_internal.h"
|
|
#include "esp_log.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "sdkconfig.h"
|
|
|
|
#ifndef CONFIG_MULTIMODAL_CONNECT_TIMEOUT_MS
|
|
#define CONFIG_MULTIMODAL_CONNECT_TIMEOUT_MS 10000
|
|
#endif
|
|
|
|
#ifndef CONFIG_TQ_BOOT_SOUND_TIMEOUT_MS
|
|
#define CONFIG_TQ_BOOT_SOUND_TIMEOUT_MS 3000
|
|
#endif
|
|
|
|
#ifndef CONFIG_TQ_BOOT_SOUND_FILE
|
|
#define CONFIG_TQ_BOOT_SOUND_FILE ""
|
|
#endif
|
|
|
|
#define CONTROL_PLANE_WIFI_SUCCESS_PROMPT_FILE "/littlefs/audio_prompts/network_connect_success.opus"
|
|
|
|
#define CONTROL_PLANE_STA_CONNECT_MAX_ATTEMPTS 3
|
|
#define CONTROL_PLANE_STA_RETRY_BASE_DELAY_MS 300
|
|
|
|
static const char *TAG = "control_plane_rt";
|
|
static bool s_wifi_success_prompt_played;
|
|
|
|
static int elapsed_ms_since(TickType_t start_tick)
|
|
{
|
|
TickType_t elapsed_ticks = xTaskGetTickCount() - start_tick;
|
|
return (int)(elapsed_ticks * portTICK_PERIOD_MS);
|
|
}
|
|
|
|
static int remaining_timeout_ms(TickType_t start_tick, int timeout_ms)
|
|
{
|
|
int elapsed_ms = elapsed_ms_since(start_tick);
|
|
if (elapsed_ms >= timeout_ms) {
|
|
return 0;
|
|
}
|
|
return timeout_ms - elapsed_ms;
|
|
}
|
|
|
|
esp_err_t control_plane_prepare_runtime_storage(void)
|
|
{
|
|
return domain_runtime_prepare_storage();
|
|
}
|
|
|
|
static void control_plane_wifi_set_keep_awake(bool keep_awake)
|
|
{
|
|
domain_runtime_behavior_set_keep_awake(keep_awake);
|
|
}
|
|
|
|
static esp_err_t control_plane_wifi_play_prompt(domain_wifi_prompt_id_t prompt)
|
|
{
|
|
return domain_runtime_behavior_play_prompt(prompt);
|
|
}
|
|
|
|
static esp_err_t control_plane_wifi_play_prompt_file(const char *path)
|
|
{
|
|
return domain_runtime_behavior_play_prompt_file(path);
|
|
}
|
|
|
|
static int control_plane_normalize_connect_timeout_ms(int timeout_ms, int min_timeout_ms)
|
|
{
|
|
int effective_timeout = timeout_ms;
|
|
if (effective_timeout <= 0) {
|
|
effective_timeout = 10000;
|
|
}
|
|
if (min_timeout_ms > 0 && effective_timeout < min_timeout_ms) {
|
|
effective_timeout = min_timeout_ms;
|
|
}
|
|
return effective_timeout;
|
|
}
|
|
|
|
static void control_plane_play_wifi_success_prompt(void)
|
|
{
|
|
if (s_wifi_success_prompt_played) {
|
|
return;
|
|
}
|
|
s_wifi_success_prompt_played = true;
|
|
|
|
bool played = false;
|
|
esp_err_t audio_err = domain_runtime_multimodal_audio_init();
|
|
if (audio_err != ESP_OK && audio_err != ESP_ERR_INVALID_STATE) {
|
|
ESP_LOGW(TAG, "Prepare audio for Wi-Fi success prompt failed: %s", esp_err_to_name(audio_err));
|
|
}
|
|
played = (control_plane_wifi_play_prompt_file(CONTROL_PLANE_WIFI_SUCCESS_PROMPT_FILE) == ESP_OK);
|
|
#ifdef CONFIG_VOICE_PROMPT_WIFI_SUCCESS_FILE
|
|
if (!played && CONFIG_VOICE_PROMPT_WIFI_SUCCESS_FILE[0] != '\0') {
|
|
played = (control_plane_wifi_play_prompt_file(CONFIG_VOICE_PROMPT_WIFI_SUCCESS_FILE) == ESP_OK);
|
|
}
|
|
#endif
|
|
if (!played) {
|
|
(void)control_plane_wifi_play_prompt(DOMAIN_WIFI_PROMPT_WIFI_SUCCESS);
|
|
}
|
|
}
|
|
|
|
static esp_err_t control_plane_connect_sta_with_feedback(int timeout_ms,
|
|
bool play_connecting_prompt)
|
|
{
|
|
esp_err_t err = ESP_ERR_TIMEOUT;
|
|
TickType_t start_tick = xTaskGetTickCount();
|
|
|
|
if (play_connecting_prompt) {
|
|
(void)control_plane_wifi_play_prompt(DOMAIN_WIFI_PROMPT_WIFI_CONNECTING);
|
|
}
|
|
|
|
control_plane_wifi_set_keep_awake(true);
|
|
for (int attempt = 1; attempt <= CONTROL_PLANE_STA_CONNECT_MAX_ATTEMPTS; ++attempt) {
|
|
int attempt_timeout_ms = remaining_timeout_ms(start_tick, timeout_ms);
|
|
if (attempt_timeout_ms <= 0) {
|
|
err = ESP_ERR_TIMEOUT;
|
|
break;
|
|
}
|
|
|
|
err = domain_runtime_wifi_portal_connect_sta(attempt_timeout_ms);
|
|
if (err == ESP_OK) {
|
|
break;
|
|
}
|
|
|
|
if (attempt >= CONTROL_PLANE_STA_CONNECT_MAX_ATTEMPTS) {
|
|
break;
|
|
}
|
|
|
|
if (err != ESP_ERR_TIMEOUT && err != ESP_FAIL) {
|
|
break;
|
|
}
|
|
|
|
int retry_delay_ms = CONTROL_PLANE_STA_RETRY_BASE_DELAY_MS * attempt;
|
|
int remaining_ms = remaining_timeout_ms(start_tick, timeout_ms);
|
|
if (remaining_ms <= 0) {
|
|
err = ESP_ERR_TIMEOUT;
|
|
break;
|
|
}
|
|
if (retry_delay_ms > remaining_ms) {
|
|
retry_delay_ms = remaining_ms;
|
|
}
|
|
ESP_LOGW(TAG,
|
|
"STA connect attempt %d/%d failed (%s), retry in %d ms",
|
|
attempt,
|
|
CONTROL_PLANE_STA_CONNECT_MAX_ATTEMPTS,
|
|
esp_err_to_name(err),
|
|
retry_delay_ms);
|
|
vTaskDelay(pdMS_TO_TICKS(retry_delay_ms));
|
|
}
|
|
control_plane_wifi_set_keep_awake(false);
|
|
if (err != ESP_OK) {
|
|
return err;
|
|
}
|
|
|
|
control_plane_play_wifi_success_prompt();
|
|
return ESP_OK;
|
|
}
|
|
|
|
static bool control_plane_wifi_connect_error_keeps_credentials(esp_err_t err)
|
|
{
|
|
return domain_runtime_wifi_connect_error_keeps_credentials(err);
|
|
}
|
|
|
|
static void control_plane_register_wifi_behavior_hooks(void)
|
|
{
|
|
domain_wifi_behavior_hooks_t hooks = {
|
|
.set_keep_awake = control_plane_wifi_set_keep_awake,
|
|
.play_prompt = control_plane_wifi_play_prompt,
|
|
.play_prompt_file = control_plane_wifi_play_prompt_file,
|
|
};
|
|
domain_runtime_wifi_portal_set_behavior_hooks(&hooks);
|
|
}
|
|
|
|
esp_err_t control_plane_bootstrap_connectivity(const char *seed_ssid,
|
|
const char *seed_password,
|
|
int connect_timeout_ms,
|
|
const char *config_ap_ssid_prefix)
|
|
{
|
|
ESP_LOGI(TAG,
|
|
"Boot Wi-Fi config SSID=%s timeout_ms=%d",
|
|
(seed_ssid != NULL && seed_ssid[0] != '\0') ? seed_ssid : "<empty>",
|
|
connect_timeout_ms);
|
|
|
|
esp_err_t err = control_plane_prepare_runtime_storage();
|
|
if (err != ESP_OK) {
|
|
return err;
|
|
}
|
|
|
|
control_plane_register_wifi_behavior_hooks();
|
|
err = domain_runtime_wifi_portal_init_defaults(seed_ssid, seed_password);
|
|
if (err != ESP_OK) {
|
|
return err;
|
|
}
|
|
|
|
if (domain_runtime_wifi_portal_has_saved_credentials()) {
|
|
int timeout_ms = control_plane_normalize_connect_timeout_ms(connect_timeout_ms, 0);
|
|
err = control_plane_connect_sta_with_feedback(timeout_ms, true);
|
|
if (err == ESP_OK) {
|
|
return ESP_OK;
|
|
}
|
|
if (control_plane_wifi_connect_error_keeps_credentials(err)) {
|
|
ESP_LOGW(TAG,
|
|
"STA connect failed, keep credentials for runtime retry: %s",
|
|
esp_err_to_name(err));
|
|
} else {
|
|
ESP_LOGW(TAG, "STA connect failed, clear credentials and fallback: %s",
|
|
esp_err_to_name(err));
|
|
(void)domain_runtime_wifi_portal_clear_saved_credentials();
|
|
}
|
|
}
|
|
|
|
control_plane_wifi_set_keep_awake(true);
|
|
err = domain_runtime_wifi_portal_start_config_ap(
|
|
(config_ap_ssid_prefix != NULL && config_ap_ssid_prefix[0] != '\0')
|
|
? config_ap_ssid_prefix
|
|
: "TQ-CAT");
|
|
if (err != ESP_OK) {
|
|
control_plane_wifi_set_keep_awake(false);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
bool control_plane_is_sta_connected(void)
|
|
{
|
|
return domain_runtime_wifi_portal_sta_is_connected();
|
|
}
|
|
|
|
esp_err_t control_plane_play_boot_sound(int timeout_ms)
|
|
{
|
|
int effective_timeout_ms = timeout_ms > 0 ? timeout_ms : CONFIG_TQ_BOOT_SOUND_TIMEOUT_MS;
|
|
ESP_LOGI(TAG, "Boot sound policy start timeout=%dms", effective_timeout_ms);
|
|
|
|
if (CONFIG_TQ_BOOT_SOUND_FILE[0] == '\0') {
|
|
ESP_LOGW(TAG, "Boot sound file is not configured");
|
|
return ESP_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
esp_err_t audio_err = domain_runtime_multimodal_audio_init();
|
|
if (audio_err != ESP_OK) {
|
|
ESP_LOGW(TAG, "Boot sound audio init failed: %s", esp_err_to_name(audio_err));
|
|
return audio_err;
|
|
}
|
|
|
|
esp_err_t play_err = domain_runtime_behavior_play_prompt_file(CONFIG_TQ_BOOT_SOUND_FILE);
|
|
|
|
if (play_err != ESP_OK) {
|
|
ESP_LOGW(TAG, "Boot sound playback failed: %s", esp_err_to_name(play_err));
|
|
}
|
|
|
|
ESP_LOGI(TAG, "Boot sound audio resources kept for runtime");
|
|
return play_err;
|
|
}
|
|
|
|
esp_err_t control_plane_init_lcd_device(void)
|
|
{
|
|
return domain_runtime_init_lcd_device();
|
|
}
|
|
|
|
void control_plane_start_lcd_render_loop(void)
|
|
{
|
|
domain_runtime_start_lcd_render_loop();
|
|
}
|
|
|
|
esp_err_t control_plane_request_boot_expression(void)
|
|
{
|
|
domain_expression_request_t request = {
|
|
.id = DOMAIN_EXPRESSION_IDLE,
|
|
.trigger = DOMAIN_EXPRESSION_TRIGGER_UNSPECIFIED,
|
|
.direction_index = 0,
|
|
};
|
|
return domain_runtime_request_expression(&request);
|
|
}
|
|
|
|
esp_err_t control_plane_stop_lcd_render_loop(int timeout_ms)
|
|
{
|
|
return domain_runtime_stop_lcd_render_loop(timeout_ms);
|
|
}
|