docs: 补充核心分层中文注释
This commit is contained in:
@@ -16,6 +16,12 @@
|
||||
|
||||
#if CONFIG_TQ_BATTERY_ENABLE
|
||||
|
||||
/*
|
||||
* 电池后台策略层:domain 负责把原始电压稳定为状态,本任务负责提示音、充满确认和受控关机。
|
||||
* GPIO ISR 只唤醒/通知,不读取 ADC 或播放音频;所有慢操作都在任务上下文完成。
|
||||
* 关机一旦进入独立任务即视为不可逆流程,以免普通采样任务重复触发并争用电机/音频资源。
|
||||
*/
|
||||
|
||||
#define BATTERY_WORKER_STACK_BYTES 4096
|
||||
#define BATTERY_WORKER_PRIORITY 2
|
||||
#define BATTERY_SUPPRESSED_RETRY_MS 1000
|
||||
@@ -31,6 +37,11 @@
|
||||
#define BATTERY_SHUTDOWN_TASK_STACK_BYTES 6144
|
||||
#define BATTERY_SHUTDOWN_TASK_PRIORITY 5
|
||||
#define BATTERY_SHUTDOWN_MOTOR_STOP_MS 2000
|
||||
|
||||
/*
|
||||
* 多次确认用于排除负载突变导致的瞬时低压;紧急阈值只需两次以缩短保护延迟。
|
||||
* 轻待机下采样间隔显著拉长,充电 GPIO 中断仍可及时唤醒状态更新。
|
||||
*/
|
||||
#define BATTERY_LOW_PROMPT_FILE "/littlefs/audio_prompts/battery_low_20.opus"
|
||||
#define BATTERY_FULL_PROMPT_FILE "/littlefs/audio_prompts/charge_complete.opus"
|
||||
|
||||
|
||||
@@ -17,6 +17,12 @@
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/*
|
||||
* 联网与配网用例编排:先尝试已保存凭据,失败后按配置切换门户或小程序 BLE 配网。
|
||||
* 连接周期使用同一个递减超时预算,防止每次重试重置计时;成功提示只播放一次。
|
||||
* BLE worker 通过单元素队列串行化请求,恢复重启标记放在 RTC 内存以跨软重启传递一次。
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_MULTIMODAL_CONNECT_TIMEOUT_MS
|
||||
#define CONFIG_MULTIMODAL_CONNECT_TIMEOUT_MS 10000
|
||||
#endif
|
||||
@@ -66,6 +72,11 @@
|
||||
#define CONTROL_PLANE_PROVISIONING_RESTART_DELAY_MS 100
|
||||
#define CONTROL_PLANE_PROVISIONING_RECOVERY_MAGIC 0x5451424cU
|
||||
|
||||
/*
|
||||
* 重试基准 300 ms 兼顾快速恢复与射频稳定;配网切换前先停传输并等待运行时静默,
|
||||
* 是为了释放内部 RAM/DMA 连续块,而不只是检查总剩余堆。
|
||||
*/
|
||||
|
||||
static const char *TAG = "control_plane_rt";
|
||||
static bool s_wifi_success_prompt_played;
|
||||
static bool s_provisioning_recovery_boot;
|
||||
@@ -108,6 +119,7 @@ static void control_plane_request_provisioning_recovery_restart(
|
||||
|
||||
static esp_err_t control_plane_check_provisioning_memory_headroom(void)
|
||||
{
|
||||
/* BLE 初始化需要连续内部/DMA 块;总空闲堆达标但最大块不足时仍必须拒绝并走恢复启动。 */
|
||||
domain_ota_memory_headroom_t headroom = {0};
|
||||
esp_err_t err = domain_runtime_ota_get_memory_headroom(&headroom);
|
||||
if (err != ESP_OK) {
|
||||
@@ -216,6 +228,7 @@ static void control_plane_play_wifi_success_prompt(void)
|
||||
static esp_err_t control_plane_connect_sta_with_feedback(int timeout_ms,
|
||||
bool play_connecting_prompt)
|
||||
{
|
||||
/* 所有候选网络共享调用方预算,并把失败分类反馈给 UI;此函数不保存试连失败的凭据。 */
|
||||
esp_err_t err = ESP_ERR_TIMEOUT;
|
||||
TickType_t start_tick = xTaskGetTickCount();
|
||||
|
||||
@@ -320,6 +333,7 @@ static void control_plane_provisioning_event_callback(
|
||||
const domain_wifi_provisioning_event_t *event,
|
||||
void *user_data)
|
||||
{
|
||||
/* BLE 回调只复制凭据到单元素队列;联网验证与 NVS 提交在 worker 中串行完成。 */
|
||||
(void)user_data;
|
||||
if (event == NULL) {
|
||||
return;
|
||||
@@ -343,6 +357,7 @@ static bool control_plane_provisioning_stop_requested(void)
|
||||
|
||||
static void control_plane_provisioning_worker(void *arg)
|
||||
{
|
||||
/* 状态通知顺序也是小程序协议的一部分:connecting→got_ip→saving→ready,失败则给明确原因。 */
|
||||
(void)arg;
|
||||
bool provisioning_succeeded = false;
|
||||
TickType_t last_wait_log_tick = xTaskGetTickCount();
|
||||
@@ -543,6 +558,7 @@ esp_err_t control_plane_stop_mini_program_provisioning(int timeout_ms)
|
||||
|
||||
esp_err_t control_plane_enter_mini_program_reprovisioning(const char *reason)
|
||||
{
|
||||
/* 先静默语音/网络运行时释放内存;仍不足时写 RTC 标记并软重启进入最小资源配网路径。 */
|
||||
#if !CONFIG_TQ_MINI_PROGRAM_PROVISIONING_ENABLE
|
||||
(void)reason;
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
@@ -580,6 +596,7 @@ esp_err_t control_plane_bootstrap_connectivity(const char *seed_ssid,
|
||||
const char *seed_password,
|
||||
int connect_timeout_ms)
|
||||
{
|
||||
/* 启动只做一轮有界策略:已存凭据优先,失败再配网;持续重连归 runtime worker 所有。 */
|
||||
ESP_LOGI(TAG,
|
||||
"Boot Wi-Fi config SSID=%s timeout_ms=%d",
|
||||
(seed_ssid != NULL && seed_ssid[0] != '\0') ? seed_ssid : "<empty>",
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
* command handlers, and UART replies in the control-plane layer. Reads
|
||||
* `\r\n`-delimited commands from the console UART through the domain console
|
||||
* bridge and replies with `OK`, `ERROR <reason>`, or one JSON line.
|
||||
*
|
||||
* 工厂串口 CLI 拥有行缓冲、命令解析和应答格式,但设备身份/Wi-Fi 的持久化仍经 domain 完成。
|
||||
* worker 单实例运行;停止通过标志协作完成,不能在别的任务中直接删除它,否则可能留下半条
|
||||
* UART 应答或未提交的配置。所有输入都有固定上限,超长行会整体拒绝而不是截断后误执行。
|
||||
*/
|
||||
|
||||
#include "control_plane.h"
|
||||
@@ -46,6 +50,8 @@
|
||||
#define FACTORY_CLI_START_WAIT_STEP_MS 20
|
||||
#define FACTORY_CLI_START_GATE_WAIT_MS 5000
|
||||
|
||||
/* 512 字节覆盖普通命令,独立 3 KiB Wi-Fi JSON 上限用于凭据列表;二者不可互相替代。 */
|
||||
|
||||
#ifndef CONFIG_TQ_LOCAL_TASK_CORE
|
||||
#define CONFIG_TQ_LOCAL_TASK_CORE 1
|
||||
#endif
|
||||
|
||||
@@ -18,6 +18,12 @@
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/*
|
||||
* 每日运势远端用例:读取 domain 中的出生档案,构造请求并把验证后的结果交回 domain 持久化。
|
||||
* 网络响应属于临时缓冲区,解析后全部复制到定长领域结构;服务端日期必须与请求目标一致,
|
||||
* 防止缓存或跨时区响应污染“当天结果”。启用 Device-JWT 时还会校验 fortune 所需 scope。
|
||||
*/
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#ifndef CONFIG_FORTUNE_API_URL
|
||||
@@ -43,6 +49,8 @@
|
||||
static const char *TAG = "cp_fortune";
|
||||
static const int kMaxHttpBody = 4096;
|
||||
|
||||
/* 返回字段规模固定,4 KiB 是协议护栏;超限视为服务端异常而不是尝试部分解析。 */
|
||||
|
||||
static void *fortune_psram_first_calloc(size_t count, size_t size)
|
||||
{
|
||||
if (count == 0U || size == 0U || count > (SIZE_MAX / size)) {
|
||||
|
||||
@@ -20,6 +20,12 @@
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/*
|
||||
* 物理按键输入前端:ISR 只把 GPIO 编号投入队列,任务随后完成去抖、单双击和长按判定,
|
||||
* 最终发布统一 system event。这样所有产品动作都离开 ISR,且多个按键共享同一时间语义。
|
||||
* 配置表是 GPIO、有效电平和事件类型的唯一映射;新增按键应先检查引脚冲突与上下拉组合。
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_TQ_KEY_INPUT_ENABLE
|
||||
#define CONFIG_TQ_KEY_INPUT_ENABLE 1
|
||||
#endif
|
||||
@@ -50,6 +56,11 @@
|
||||
|
||||
#ifndef CONFIG_TQ_KEY_INPUT_POLL_MS
|
||||
#define CONFIG_TQ_KEY_INPUT_POLL_MS 20
|
||||
|
||||
/*
|
||||
* 20 ms 与去抖周期一致;800 ms 长按避免普通点击误触,300 ms 双击窗口兼顾可操作性,
|
||||
* 双击后再抑制 450 ms 是为了不把第二次释放沿解释成新的单击序列。
|
||||
*/
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_TQ_PTT_MODE_HOLD_TO_TALK) && !defined(CONFIG_TQ_PTT_MODE_CLICK_TO_TALK)
|
||||
@@ -237,6 +248,7 @@ static bool control_plane_key_level_is_pressed(const control_plane_key_config_t
|
||||
|
||||
static void IRAM_ATTR control_plane_key_gpio_isr_handler(void *arg)
|
||||
{
|
||||
/* ISR 只能使用 ISR-safe 队列 API,不能记录日志、读取复杂状态或直接调用业务服务。 */
|
||||
const gpio_num_t gpio_num = (gpio_num_t)(intptr_t)arg;
|
||||
QueueHandle_t queue = s_gpio_irq_queue;
|
||||
if (queue == NULL) {
|
||||
@@ -292,6 +304,7 @@ static esp_err_t control_plane_configure_key_gpio(const control_plane_key_config
|
||||
|
||||
static esp_err_t control_plane_validate_key_configs(void)
|
||||
{
|
||||
/* 启动前一次性拒绝重复 GPIO 和互斥的上下拉设置,避免运行期出现不可解释的幽灵按键。 */
|
||||
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) {
|
||||
@@ -356,6 +369,7 @@ static void control_plane_handle_debounced_transition(const control_plane_key_co
|
||||
int level,
|
||||
int64_t now_ms)
|
||||
{
|
||||
/* 物理边沿只更新按键状态机;单击要等双击窗口关闭后才最终发布。 */
|
||||
if (state->wait_inactive_before_press) {
|
||||
if (pressed) {
|
||||
state->debounced_pressed = false;
|
||||
@@ -435,6 +449,7 @@ static void control_plane_handle_debounced_transition(const control_plane_key_co
|
||||
|
||||
static void control_plane_process_key_timers(void)
|
||||
{
|
||||
/* 定时扫描补足没有新 GPIO 中断时的长按触发和延迟单击确认。 */
|
||||
const int64_t now_ms = control_plane_event_timestamp_ms();
|
||||
|
||||
for (size_t i = 0; i < sizeof(s_key_configs) / sizeof(s_key_configs[0]); ++i) {
|
||||
|
||||
@@ -17,6 +17,12 @@
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/*
|
||||
* 轻待机协调器:根据最后活动时间和系统忙闲状态进入低功耗策略,但不执行深睡或重启。
|
||||
* transition_lock 串行化进入/退出,防止按键唤醒与定时任务同时改背光、音频、电机和 Wi-Fi。
|
||||
* 每项节能动作都可配置,唤醒按相反顺序恢复;活动时间只由明确的用户/业务事件刷新。
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_TQ_LIGHT_STANDBY_ENABLE
|
||||
#define CONFIG_TQ_LIGHT_STANDBY_ENABLE 0
|
||||
#endif
|
||||
@@ -86,6 +92,8 @@
|
||||
#define CONTROL_PLANE_LIGHT_STANDBY_STOP_POLL_MS 20U
|
||||
#define CONTROL_PLANE_LIGHT_STANDBY_TRANSITION_WAIT_MS 1000U
|
||||
|
||||
/* 每秒复查资格即可满足分钟级空闲阈值,同时避免忙时高频查询多模态和 OTA 状态。 */
|
||||
|
||||
#if CONFIG_TQ_LOCAL_TASK_CORE < 0
|
||||
#define CONTROL_PLANE_LIGHT_STANDBY_TASK_CORE tskNO_AFFINITY
|
||||
#else
|
||||
@@ -171,6 +179,7 @@ static void control_plane_light_standby_log_transition(
|
||||
|
||||
static bool control_plane_light_standby_runtime_is_eligible(void)
|
||||
{
|
||||
/* 有语音轮次、OTA、配网或其他前台交互时禁止进入,空闲时长只是必要条件而非充分条件。 */
|
||||
if (!control_plane_voice_turn_is_idle() ||
|
||||
!control_plane_ota_voice_services_allowed() ||
|
||||
control_plane_is_mini_program_provisioning_running()) {
|
||||
@@ -243,6 +252,7 @@ static esp_err_t control_plane_light_standby_apply_runtime_policy(bool standby)
|
||||
|
||||
static esp_err_t control_plane_light_standby_apply_wake_policy(void)
|
||||
{
|
||||
/* 先恢复业务所需资源再宣布 ACTIVE,调用者返回时即可安全发起屏幕、音频或网络动作。 */
|
||||
esp_err_t first_error = control_plane_light_standby_apply_runtime_policy(false);
|
||||
|
||||
#if CONFIG_TQ_LIGHT_STANDBY_DIM_BACKLIGHT
|
||||
@@ -274,6 +284,7 @@ static esp_err_t control_plane_light_standby_apply_wake_policy(void)
|
||||
|
||||
static esp_err_t control_plane_light_standby_apply_entry_policy(void)
|
||||
{
|
||||
/* 进入顺序尽量先停止高层活动再降低硬件能力;中途失败会由唤醒策略回滚已应用项。 */
|
||||
#if CONFIG_TQ_LIGHT_STANDBY_RELEASE_MOTOR_HOLD
|
||||
esp_err_t motor_err = domain_direction_release_motor_hold(
|
||||
CONTROL_PLANE_LIGHT_STANDBY_MOTOR_RELEASE_WAIT_MS);
|
||||
@@ -365,6 +376,7 @@ static bool control_plane_light_standby_entry_is_current(
|
||||
|
||||
static void control_plane_light_standby_enter(uint64_t now_ms)
|
||||
{
|
||||
/* 二次核对 activity generation,防止准备待机期间刚发生的用户活动被旧定时结果覆盖。 */
|
||||
if (!control_plane_light_standby_lock_transition()) {
|
||||
ESP_LOGW(TAG, "Enter transition lock timed out");
|
||||
return;
|
||||
@@ -672,6 +684,7 @@ esp_err_t control_plane_light_standby_stop(int timeout_ms)
|
||||
esp_err_t control_plane_light_standby_record_activity(
|
||||
control_plane_light_standby_activity_t activity)
|
||||
{
|
||||
/* 更新时间与唤醒共用转换锁,确保新的 deadline 不会和正在提交的 STANDBY 状态交叉。 */
|
||||
if (activity <= CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_UNSPECIFIED ||
|
||||
activity > CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_EXTERNAL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
|
||||
@@ -14,9 +14,16 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/portmacro.h"
|
||||
|
||||
/*
|
||||
* 网络反馈是一个小型聚合状态机:分别记录 Wi-Fi 与服务端可用性,只在持续故障或恢复边界提示。
|
||||
* 状态在临界区更新,播放提示音必须在锁外进行;否则音频回调重入会阻塞其他网络事件。
|
||||
* require_ready() 同时承担动作门禁,让“无法执行”的原因和播报保持一致。
|
||||
*/
|
||||
#define CONTROL_PLANE_SERVER_DISCONNECT_GRACE_MS 3000U
|
||||
#define CONTROL_PLANE_NETWORK_PROMPT_COOLDOWN_MS 5000U
|
||||
|
||||
/* 3 秒宽限过滤 WebSocket 短重连,5 秒冷却防止 Wi-Fi 与服务端事件交替造成提示音轰炸。 */
|
||||
|
||||
#define CONTROL_PLANE_WIFI_INTERRUPTED_PROMPT_FILE \
|
||||
"/littlefs/audio_prompts/network_interrupted_repairing.opus"
|
||||
#define CONTROL_PLANE_WIFI_ACTION_BLOCKED_PROMPT_FILE \
|
||||
|
||||
@@ -20,6 +20,12 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/*
|
||||
* OTA 策略状态机:周期报告版本、等待服务端指令、检查电量/内存/业务空闲门禁,再独占语音资源
|
||||
* 执行安装。下载与分区写入由 domain/platform 完成;本层负责“现在是否适合升级”及失败退避。
|
||||
* snapshot 是面向诊断的值拷贝,任务停止采用协作标志,安装关键区不能被外部强删任务打断。
|
||||
*/
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#ifndef CONFIG_TQ_OTA_ENABLE
|
||||
@@ -80,6 +86,11 @@
|
||||
#define CONTROL_PLANE_OTA_TASK_STACK_WORDS \
|
||||
((CONFIG_TQ_OTA_TASK_STACK_BYTES + sizeof(StackType_t) - 1U) / sizeof(StackType_t))
|
||||
|
||||
/*
|
||||
* 常规轮询与确认均为 6 小时,避免设备群频繁访问服务;低电量以 60 秒重查,因为充电状态可能
|
||||
* 很快变化。除总内存外还检查 DMA 最大连续块,防止“剩余很多但无法分配音频/网络缓冲”。
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
bool acquired;
|
||||
bool restore_multimodal;
|
||||
@@ -155,6 +166,7 @@ static void control_plane_ota_set_state(control_plane_ota_state_t state,
|
||||
esp_err_t error,
|
||||
int http_status)
|
||||
{
|
||||
/* snapshot 更新集中在同一临界区,诊断读取不会观察到 state 与 error/http 状态跨代混合。 */
|
||||
portENTER_CRITICAL(&s_ota_lock);
|
||||
s_ota_snapshot.state = state;
|
||||
s_ota_snapshot.last_error = error;
|
||||
@@ -203,6 +215,7 @@ static bool control_plane_ota_wait_interruptible(uint32_t wait_ms)
|
||||
|
||||
static uint32_t control_plane_ota_retry_delay(uint32_t failures)
|
||||
{
|
||||
/* 采用饱和倍增而非直接移位,防止连续失败次数过大产生溢出后的短延迟。 */
|
||||
uint64_t delay_ms = CONFIG_TQ_OTA_RETRY_BASE_MS;
|
||||
for (uint32_t i = 1U; i < failures && delay_ms < CONFIG_TQ_OTA_RETRY_MAX_MS; ++i) {
|
||||
delay_ms *= 2U;
|
||||
@@ -320,6 +333,7 @@ static bool control_plane_ota_cancel(void *user_data)
|
||||
|
||||
static bool control_plane_ota_runtime_is_idle(void)
|
||||
{
|
||||
/* “空闲”要求语音轮次和关键业务都可让渡,单看 WebSocket 是否连接不足以判断。 */
|
||||
if (!control_plane_voice_turn_is_idle()) {
|
||||
return false;
|
||||
}
|
||||
@@ -420,6 +434,7 @@ static esp_err_t control_plane_ota_acquire_voice_window(
|
||||
bool allow_active_teardown,
|
||||
control_plane_ota_voice_window_t *window)
|
||||
{
|
||||
/* 先设置全局门禁阻止新会话,再停止已存在的语音服务;失败时由 window 记录内容精确回滚。 */
|
||||
if (window == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
@@ -505,6 +520,7 @@ static esp_err_t control_plane_ota_acquire_voice_window(
|
||||
|
||||
static void control_plane_ota_release_voice_window(control_plane_ota_voice_window_t *window)
|
||||
{
|
||||
/* 只恢复本函数实际停止过的服务,避免把调用前就关闭的功能意外启动。 */
|
||||
if (window == NULL || !window->acquired) {
|
||||
return;
|
||||
}
|
||||
@@ -591,6 +607,7 @@ static esp_err_t control_plane_ota_prepare_report(domain_ota_report_request_t *r
|
||||
static esp_err_t control_plane_ota_check_once(domain_ota_report_response_t *response,
|
||||
bool force_initial_jwt_refresh)
|
||||
{
|
||||
/* 一次检查包含上报、候选校验和确认判定,但不在资源/电量门禁之前开始下载。 */
|
||||
if (response == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
@@ -646,6 +663,7 @@ static esp_err_t control_plane_ota_check_once(domain_ota_report_response_t *resp
|
||||
|
||||
static void control_plane_ota_worker(void *arg)
|
||||
{
|
||||
/* 所有等待均切片为可中断睡眠,使 stop 不必等完整 6 小时轮询周期。 */
|
||||
(void)arg;
|
||||
if (ulTaskNotifyTake(pdTRUE,
|
||||
pdMS_TO_TICKS(CONTROL_PLANE_OTA_START_GATE_MS)) == 0U) {
|
||||
@@ -929,6 +947,7 @@ esp_err_t control_plane_start_ota_service(void)
|
||||
|
||||
esp_err_t control_plane_stop_ota_service(int timeout_ms)
|
||||
{
|
||||
/* 安装临界区优先协作取消;只有超出有界等待后才走确定性清理,避免写分区中途强删任务。 */
|
||||
if (timeout_ms < 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,12 @@
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/*
|
||||
* 远程日志采用“日志 hook -> 内存环形队列 -> JSONL 文件 -> 批量上传”的四段式所有权链。
|
||||
* hook 只复制有限长度文本,绝不联网或写文件;worker 独占文件轮转和上传,成功后才删除文件。
|
||||
* 本地控制台输出可保留,递归保护防止本模块自己的上传日志再次进入 hook。
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_TQ_REMOTE_LOG_ENABLE
|
||||
#define CONFIG_TQ_REMOTE_LOG_ENABLE 0
|
||||
#endif
|
||||
@@ -94,6 +100,11 @@
|
||||
#define REMOTE_LOG_POLL_MS 200
|
||||
#define REMOTE_LOG_VOICE_STARTUP_GRACE_MS 60000
|
||||
|
||||
/*
|
||||
* 队列和单条长度均有界,压力过大时记录 dropped_count 而非阻塞产生日志的实时任务。
|
||||
* 语音启动后留 60 秒宽限,避免日志 TLS/上传与首个会话争夺内部 RAM 和网络带宽。
|
||||
*/
|
||||
|
||||
#if CONFIG_TQ_REMOTE_LOG_ENABLE
|
||||
|
||||
typedef struct {
|
||||
@@ -240,6 +251,7 @@ static bool should_capture_level(char level)
|
||||
|
||||
static void enqueue_line(const char *line, bool truncated)
|
||||
{
|
||||
/* hook 上下文不等待空间:队列满时累计丢弃数,稍后作为合成记录写入,保留压力证据。 */
|
||||
if (line == NULL || line[0] == '\0' || s_queue == NULL) {
|
||||
return;
|
||||
}
|
||||
@@ -384,6 +396,7 @@ static void sanitize_value(char *text, const char *key)
|
||||
|
||||
static void sanitize_log_line(char *text)
|
||||
{
|
||||
/* 落盘前脱敏常见认证字段;这是最后一道保护,不替代调用方避免打印秘密。 */
|
||||
sanitize_value(text, "password");
|
||||
sanitize_value(text, "Password");
|
||||
sanitize_value(text, "pwd");
|
||||
@@ -557,6 +570,7 @@ static size_t count_pending_files(void)
|
||||
|
||||
static void trim_old_files(void)
|
||||
{
|
||||
/* 超过文件上限时只删最旧已封存批次,当前追加文件和内存队列不受影响。 */
|
||||
uint32_t sequences[CONFIG_TQ_REMOTE_LOG_MAX_FILES + 8] = {0};
|
||||
size_t count = collect_files(sequences, sizeof(sequences) / sizeof(sequences[0]));
|
||||
while (count > CONFIG_TQ_REMOTE_LOG_MAX_FILES) {
|
||||
@@ -618,6 +632,7 @@ static esp_err_t append_entry_to_file(const remote_log_entry_t *entry)
|
||||
|
||||
static void drain_memory_queue_to_flash(void)
|
||||
{
|
||||
/* 先从队列取得值拷贝再写 flash,文件 I/O 期间不占用队列临界区。 */
|
||||
remote_log_entry_t entry = {0};
|
||||
while (dequeue_entry(&entry)) {
|
||||
if (append_entry_to_file(&entry) != ESP_OK) {
|
||||
@@ -631,6 +646,7 @@ static void drain_memory_queue_to_flash(void)
|
||||
|
||||
static bool realtime_path_busy(void)
|
||||
{
|
||||
/* 音频/OTA 等实时路径繁忙时仍允许落盘,但推迟 TLS 上传以减少带宽和堆竞争。 */
|
||||
#if CONFIG_TQ_LOCAL_WAKE_ENABLE
|
||||
if (!control_plane_ota_voice_services_allowed() ||
|
||||
!control_plane_voice_turn_is_idle()) {
|
||||
@@ -861,6 +877,7 @@ static esp_err_t build_upload_body(uint32_t sequence,
|
||||
|
||||
static bool upload_one_file(uint32_t sequence)
|
||||
{
|
||||
/* 只有服务端明确接受后才删除源文件;传输或状态码失败均保留供下次重试。 */
|
||||
uint32_t dropped = snapshot_and_clear_dropped_count();
|
||||
char *body = (char *)remote_log_psram_malloc(REMOTE_LOG_UPLOAD_BODY_MAX);
|
||||
char *response = (char *)remote_log_psram_malloc(REMOTE_LOG_RESPONSE_MAX);
|
||||
@@ -1106,6 +1123,7 @@ static void uninstall_hook(void)
|
||||
|
||||
static void remote_log_task(void *arg)
|
||||
{
|
||||
/* worker 独占 flash 文件和上传顺序:先排空内存,再按序上传,停止前尽力完成最后一次落盘。 */
|
||||
(void)arg;
|
||||
|
||||
esp_err_t mount_err = domain_runtime_mount_logfs();
|
||||
@@ -1215,6 +1233,7 @@ esp_err_t control_plane_remote_log_flush_now(void)
|
||||
|
||||
esp_err_t control_plane_remote_log_clear(void)
|
||||
{
|
||||
/* 清理仅作用于远程日志目录和内存队列,不触碰系统日志分区或 coredump 本体。 */
|
||||
portENTER_CRITICAL(&s_lock);
|
||||
s_queue_read = 0U;
|
||||
s_queue_count = 0U;
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
|
||||
static const char *TAG = "cp_reset";
|
||||
|
||||
/*
|
||||
* 恢复出厂联网状态的单一路径:先经 domain 清除持久化凭据,再请求平台重启。
|
||||
* 即使清除失败也会记录错误并继续重启,使设备下次启动仍能进入现有的恢复/配网判定流程。
|
||||
*/
|
||||
void control_plane_reset_wifi_provisioning_and_restart(void)
|
||||
{
|
||||
ESP_LOGW(TAG, "Wi-Fi reset requested; clearing saved credentials");
|
||||
|
||||
@@ -20,6 +20,12 @@
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/*
|
||||
* 控制面长期任务的集中装配与监督点。它拥有 LCD 渲染、Wi-Fi 状态、模型命令和任务监控 worker,
|
||||
* 并按依赖顺序启动/逆序停止外围服务。队列只传递值类型命令;任何指针载荷都必须明确复制。
|
||||
* 每个 task handle 同时承担“已启动”标志,创建/退出更新受生命周期门禁保护,避免重复 worker。
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_TQ_LCD_RENDER_TASK_STACK_BYTES
|
||||
#define CONFIG_TQ_LCD_RENDER_TASK_STACK_BYTES (8U * 1024U)
|
||||
#endif
|
||||
@@ -58,6 +64,12 @@
|
||||
#define CONTROL_PLANE_MEMORY_INTERNAL_DROP_BYTES 512U
|
||||
#define CONTROL_PLANE_MEMORY_DMA_LARGEST_DROP_BYTES 256U
|
||||
|
||||
/*
|
||||
* Wi-Fi 重连使用 5~60 秒指数退避,既快速处理偶发断链又避免离线时持续占用射频。
|
||||
* PTT 的 80 ms 轮询追求按键响应;普通 Wi-Fi 状态每秒采样即可。内存告警看变化量而不仅是
|
||||
* 绝对值,便于在任务监控日志中发现缓慢泄漏和最大连续块碎片化。
|
||||
*/
|
||||
|
||||
#if (configSUPPORT_STATIC_ALLOCATION == 1) && \
|
||||
((defined(CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM) && CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM) || \
|
||||
(defined(CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY) && CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY))
|
||||
@@ -325,6 +337,7 @@ static unsigned control_plane_heap_contiguous_percent(const multi_heap_info_t *i
|
||||
|
||||
static void control_plane_memory_monitor_sample(TickType_t now_tick, bool force)
|
||||
{
|
||||
/* 同时采样内部堆、PSRAM 和最大 DMA 块;后者下降往往比总空闲量更早暴露音频分配风险。 */
|
||||
bool light_standby;
|
||||
portENTER_CRITICAL(&s_runtime_lock);
|
||||
light_standby = s_runtime_diagnostics_light_standby;
|
||||
@@ -534,6 +547,7 @@ static const char *ptt_state_name(ptt_state_t state)
|
||||
|
||||
static void control_plane_ptt_transition_locked(ptt_state_t next_state, const char *reason)
|
||||
{
|
||||
/* 所有 PTT 状态迁移集中记录,便于把按键、网络和会话异步事件还原为一条时间线。 */
|
||||
if (s_ptt_runtime.state == next_state) {
|
||||
return;
|
||||
}
|
||||
@@ -876,6 +890,7 @@ static void control_plane_delete_model_cmd_queue(QueueHandle_t queue, bool queue
|
||||
|
||||
static esp_err_t control_plane_ensure_model_cmd_queue(void)
|
||||
{
|
||||
/* 先完整创建队列再发布句柄;其他任务永远看不到半初始化对象。 */
|
||||
QueueHandle_t created_queue = NULL;
|
||||
bool created_queue_with_caps = false;
|
||||
|
||||
@@ -1123,6 +1138,7 @@ static void control_plane_on_model_command(control_plane_model_command_type_t co
|
||||
bool value_index_valid,
|
||||
void *user_data)
|
||||
{
|
||||
/* 模型回调只复制白名单命令并入队,电机/LCD 等慢操作由 model_command_task 串行执行。 */
|
||||
(void)user_data;
|
||||
|
||||
QueueHandle_t queue = NULL;
|
||||
@@ -1269,6 +1285,7 @@ static esp_err_t control_plane_show_chouqian_expression(size_t result_index, boo
|
||||
|
||||
static void control_plane_model_command_task(void *arg)
|
||||
{
|
||||
/* 串行消费保证两个模型动作不会同时争用方向、表情和提示音资源。 */
|
||||
(void)arg;
|
||||
|
||||
QueueHandle_t queue = control_plane_get_model_cmd_queue();
|
||||
@@ -1633,6 +1650,7 @@ static void control_plane_request_wifi_state_expression(domain_expression_id_t i
|
||||
|
||||
static uint32_t control_plane_next_wifi_reconnect_delay_ms(uint32_t current_delay_ms)
|
||||
{
|
||||
/* 成功路径在调用方重置为基准;失败按倍增并封顶,避免整数溢出和永久过长等待。 */
|
||||
if (current_delay_ms == 0U) {
|
||||
return CONTROL_PLANE_WIFI_RECONNECT_BASE_DELAY_MS;
|
||||
}
|
||||
@@ -1897,6 +1915,7 @@ static bool control_plane_service_ptt_lifecycle(void)
|
||||
|
||||
static void control_plane_drive_ptt_state_machine(TickType_t *last_ws_start_attempt_tick)
|
||||
{
|
||||
/* 这是 PTT 唯一推进器:按键只改变意图,网络/会话条件满足后才真正建链和开麦。 */
|
||||
if (control_plane_service_ptt_lifecycle()) {
|
||||
return;
|
||||
}
|
||||
@@ -2057,6 +2076,7 @@ static void control_plane_start_wall_clock_sync(void)
|
||||
|
||||
static void control_plane_wifi_state_task(void *arg)
|
||||
{
|
||||
/* 汇总 STA、服务端、配网和 PTT 状态,负责重连与服务启停;不把策略分散到平台事件回调。 */
|
||||
(void)arg;
|
||||
|
||||
ESP_LOGI(TAG, "Wi-Fi state monitor task started");
|
||||
@@ -2451,6 +2471,7 @@ static time_t control_plane_next_local_midnight(time_t now)
|
||||
|
||||
static esp_err_t control_plane_refresh_daily_fortune(const char *target_date)
|
||||
{
|
||||
/* 远端成功后先持久化领域结果,再更新方向/表情,使重启后看到的状态与已呈现结果一致。 */
|
||||
domain_fortune_result_t result = {0};
|
||||
esp_err_t err = control_plane_fortune_fetch(target_date, &result);
|
||||
if (err != ESP_OK) {
|
||||
@@ -2617,6 +2638,7 @@ esp_err_t control_plane_start_lcd_render_worker(void)
|
||||
|
||||
esp_err_t control_plane_start_runtime_workers(void)
|
||||
{
|
||||
/* 启动顺序遵循生产者/消费者依赖;失败路径调用统一 stop,避免留下孤儿任务。 */
|
||||
control_plane_set_runtime_stop_requested(false);
|
||||
control_plane_network_feedback_reset();
|
||||
portENTER_CRITICAL(&s_runtime_lock);
|
||||
@@ -2792,6 +2814,7 @@ void control_plane_stop_event_observer_runtime(void)
|
||||
|
||||
void control_plane_stop_runtime_workers(void)
|
||||
{
|
||||
/* 先发布全局停止意图,再逆序唤醒并回收任务;最后才销毁队列和回调注册。 */
|
||||
control_plane_set_runtime_stop_requested(true);
|
||||
(void)control_plane_light_standby_stop(CONTROL_PLANE_TASK_STOP_WAIT_MS);
|
||||
(void)control_plane_battery_service_stop(CONTROL_PLANE_TASK_STOP_WAIT_MS);
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
/*
|
||||
* 系统事件任务是按键等离散输入的串行化决策点。生产者只投递值拷贝事件,消费者按 family/type
|
||||
* 调度 PTT、校准、木鱼等用例;因此耗时动作不会出现在 GPIO ISR 中,也不会并发解释同一次输入。
|
||||
* 队列满代表用户输入已过时,调用方得到明确错误而不是无限阻塞实时任务。
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifndef CONFIG_TQ_SYSTEM_EVENT_ENABLE
|
||||
@@ -35,6 +41,8 @@
|
||||
#endif
|
||||
|
||||
#define CONTROL_PLANE_PTT_HOLD_STEP_MS 80
|
||||
|
||||
/* 长按 PTT 以 80 ms 小步等待,既能及时感知释放,也能周期检查会话已失败或被取消。 */
|
||||
#define CONTROL_PLANE_MUYU_PROMPT_FILE "muyu.opus"
|
||||
|
||||
#ifndef CONFIG_TQ_LOCAL_TASK_CORE
|
||||
|
||||
Reference in New Issue
Block a user