/* * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #pragma once #include #include #include #include "esp_err.h" #ifdef __cplusplus extern "C" { #endif /* * Control-plane 调用与所有权概览 / Control-plane call flow and ownership overview * * 本层拥有策略、重试/退避、健康状态和长生命周期工作任务;它只通过 domain facade * 获取业务能力,不直接包含 platform 接口。app_composition 负责决定启动顺序,本层 * 负责每项策略如何运行、停止和恢复。 * This layer owns policy, retry/backoff, health and managed workers. It consumes * only the domain facade; app_composition chooses startup order while this layer * owns how each policy runs, stops and recovers. * * app_composition -> control_plane policy/worker -> domain facade -> platform * * 查询 API 返回不可变快照;工作任务必须协作停止并接受有界兜底。回调 payload 的 * 生命周期由具体契约限定,调用方需要跨任务保留时必须复制。 * Query APIs return snapshots. Workers use cooperative stop plus bounded fallback. */ /* 命令枚举属于编排策略,解析本身无副作用,执行由显式 API 完成。 */ typedef enum { CONTROL_PLANE_DIRECTION_CMD_UNKNOWN = 0, CONTROL_PLANE_DIRECTION_CMD_HELP, CONTROL_PLANE_DIRECTION_CMD_POSITION, CONTROL_PLANE_DIRECTION_CMD_DIRECTION, CONTROL_PLANE_DIRECTION_CMD_CALIBRATE } control_plane_direction_command_t; typedef enum { CONTROL_PLANE_MODEL_COMMAND_UNKNOWN = 0, CONTROL_PLANE_MODEL_COMMAND_DIRECTION, CONTROL_PLANE_MODEL_COMMAND_CALIBRATE, CONTROL_PLANE_MODEL_COMMAND_CHOUQIAN, CONTROL_PLANE_MODEL_COMMAND_WEATHER, CONTROL_PLANE_MODEL_COMMAND_LUCKY_COLOR, } control_plane_model_command_type_t; typedef void (*control_plane_model_command_callback_t)( control_plane_model_command_type_t command_type, const char *value, size_t value_index, bool value_index_valid, void *user_data); typedef enum { CONTROL_PLANE_MULTIMODAL_RESPOND_TYPE_TRANSCRIPT = 0, CONTROL_PLANE_MULTIMODAL_RESPOND_TYPE_PROMPT, } control_plane_multimodal_respond_type_t; typedef struct { /* 响应载荷类型决定 text 被解释为转写文本还是提示词。 */ control_plane_multimodal_respond_type_t type; /* 调用方拥有的 UTF-8 文本;函数只在调用期间读取。 */ const char *text; /* * parameters_json 必须是 JSON object 文本,发送函数在调用期间读取,不长期持有。 * Optional JSON object serialized as payload.parameters for * RequestToRespond. Use this for documented fields such as images and * biz_params. NULL or empty means no parameters object is sent. */ const char *parameters_json; } control_plane_multimodal_respond_request_t; typedef struct { /* * parameters_json 是调用方拥有的 JSON object 文本,函数不会保留该指针。 * JSON object serialized as payload.parameters for UpdateInfo. Expected * fields follow docs/websocket.md: images, client_info, and biz_params. */ const char *parameters_json; } control_plane_multimodal_update_info_request_t; typedef enum { CONTROL_PLANE_OTA_STATE_DISABLED = 0, CONTROL_PLANE_OTA_STATE_IDLE, CONTROL_PLANE_OTA_STATE_WAITING_FOR_NETWORK, CONTROL_PLANE_OTA_STATE_WAITING_FOR_RESOURCES, CONTROL_PLANE_OTA_STATE_CHECKING, CONTROL_PLANE_OTA_STATE_WAITING_FOR_CONFIRMATION, CONTROL_PLANE_OTA_STATE_WAITING_FOR_POWER, CONTROL_PLANE_OTA_STATE_DOWNLOADING, CONTROL_PLANE_OTA_STATE_READY_TO_REBOOT, CONTROL_PLANE_OTA_STATE_ERROR, CONTROL_PLANE_OTA_STATE_STOPPED, } control_plane_ota_state_t; typedef struct { /* OTA 快照是 control-plane SSOT 的调用方副本,不暴露内部任务状态指针。 */ control_plane_ota_state_t state; bool running; bool forced; bool confirmed; char target_version[32]; uint64_t bytes_received; uint64_t total_bytes; uint32_t consecutive_failures; int last_http_status; esp_err_t last_error; } control_plane_ota_snapshot_t; typedef enum { CONTROL_PLANE_LIGHT_STANDBY_STATE_DISABLED = 0, CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE, CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING, CONTROL_PLANE_LIGHT_STANDBY_STATE_STANDBY, CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING, CONTROL_PLANE_LIGHT_STANDBY_STATE_STOPPED, } control_plane_light_standby_state_t; typedef enum { CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_UNSPECIFIED = 0, CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_KEY, CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_LOCAL_WAKE, CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_VOICE_INPUT, CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_VOICE_OUTPUT, CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_MODEL_COMMAND, CONTROL_PLANE_LIGHT_STANDBY_ACTIVITY_EXTERNAL, } control_plane_light_standby_activity_t; typedef struct { /* 快照中的时间均为本次启动后的单调毫秒值,不是 Unix 墙上时钟。 */ control_plane_light_standby_state_t state; control_plane_light_standby_activity_t last_activity; bool running; uint32_t idle_timeout_ms; uint64_t last_activity_ms; uint64_t standby_since_ms; uint32_t transition_count; } control_plane_light_standby_snapshot_t; esp_err_t control_plane_init(void); /** * @brief 启动电池采样和充电状态呈现。 / Start battery sensing and presentation. * * 可重复调用;刻意早于联网启动,使电量和充电指示不依赖 Wi-Fi 或服务端。 * Safe to call repeatedly. This service is intentionally started before * connectivity bootstrap so battery and charger indications do not depend on * Wi-Fi, provisioning, or server readiness. */ esp_err_t control_plane_battery_service_start(void); /** * 低电量唤醒后若仍未恢复且无充电器,在正常启动前重新进入深睡。 * * Re-enter deep sleep before normal boot when a low-battery wake has not * recovered and no charger is active. */ esp_err_t control_plane_battery_apply_boot_guard(void); /** * @brief 在工作任务前准备下层持久化存储。 / Prepare persistent storage before workers. * * 幂等且没有 Wi-Fi/执行器副作用,用于避免非阻塞工作任务与 NVS store 初始化竞态。 * This is idempotent and has no Wi-Fi or actuator side effects. It exists so the * composition layer can start non-blocking workers before connectivity bootstrap * without racing NVS-backed stores. */ esp_err_t control_plane_prepare_runtime_storage(void); /** 在渲染本地时间前应用时区。 / Apply the local time zone before rendering time. */ esp_err_t control_plane_prepare_wall_clock(void); /** * @brief 启动 control-plane 拥有的轻待机协调器。 / Start the light-standby coordinator. * * 协调器为完整运行时维护一条空闲截止时间,并应用配置的本地硬件节能策略;重复启动幂等。 * The coordinator tracks one inactivity deadline for the full runtime and * applies configured local hardware power policies. Repeated starts are * idempotent. */ esp_err_t control_plane_light_standby_start(void); /** * @brief 有界停止轻待机协调器。 / Stop the light-standby coordinator with bounded teardown. * * 零超时只发起异步停止;正值最多等待指定预算,必要时执行确定性的任务清理。 * Zero requests asynchronous stop. Positive values wait up to the supplied * timeout and apply deterministic task teardown if necessary. */ esp_err_t control_plane_light_standby_stop(int timeout_ms); /** * @brief 记录活动并恢复 ACTIVE。 / Record activity and return to ACTIVE. * * 若硬件已在轻待机,本调用会先恢复配置的硬件再返回;转换串行化有界,禁止从 ISR 直接调用。 * If hardware is in light standby, this call restores configured hardware * before returning. Transition serialization is bounded; the API must not be * called directly from an ISR. */ esp_err_t control_plane_light_standby_record_activity( control_plane_light_standby_activity_t activity); /** * @brief 复制当前轻待机状态快照。 / Copy the current light-standby snapshot. */ esp_err_t control_plane_light_standby_get_snapshot( control_plane_light_standby_snapshot_t *out_snapshot); /** * @brief 无副作用解析方向命令。 / Parse one direction command without side effects. * * 初始化契约:必须先显式调用 `control_plane_init()`;查询/解析 API 不做惰性初始化; * 未初始化时返回 `CONTROL_PLANE_DIRECTION_CMD_UNKNOWN`。 * * Initialization contract: * - `control_plane_init()` must be called explicitly first. * - No implicit lazy initialization is performed by query/parse APIs. * - Returns `CONTROL_PLANE_DIRECTION_CMD_UNKNOWN` when uninitialized. */ control_plane_direction_command_t control_plane_parse_direction_command(const char *input, size_t *out_direction_index); esp_err_t control_plane_rotate_to_angle(float target_angle); esp_err_t control_plane_rotate_to_direction(size_t direction_index); /** * @brief 解析并执行一行方向命令。 / Parse and execute one direction command line. * * 执行策略:`DIRECTION` 旋转到目标;`CALIBRATE` 使用默认预算校准;其余命令只解析、无执行器 * 副作用。非动作命令或动作成功返回 `ESP_OK`,动作失败透传执行器错误。 * * Execution policy: * - `DIRECTION`: perform rotation to parsed target direction. * - `CALIBRATE`: run calibration with default timeout policy. * - `HELP`/`POSITION`/`UNKNOWN`: parse only, no actuator side effects. * * Returns: * - `ESP_OK` for non-action commands or successful action commands. * - Error from action executor for failing `DIRECTION`/`CALIBRATE`. */ esp_err_t control_plane_execute_direction_command(const char *input, control_plane_direction_command_t *out_command, size_t *out_direction_index); /** * @brief 使用明确总预算执行校准。 / Run calibration with an explicit timeout budget. * * 非正超时使用内部默认值;超预算返回 `ESP_ERR_TIMEOUT`。若进入时校准开关已触发,实现可先 * 在预算内释放并重新寻零,再设置校准角度。 * * Semantics: * - `timeout_ms <= 0`: use internal default timeout. * - Returns `ESP_ERR_TIMEOUT` when calibration cannot finish in time. * - If calibration switch is already active at entry, implementation may * perform a bounded release-and-rehome sequence before setting the * calibrated angle. */ esp_err_t control_plane_calibrate_with_timeout(int timeout_ms); esp_err_t control_plane_calibrate(void); /** * @brief 为运行时引导 Wi-Fi 连接。 / Bootstrap runtime Wi-Fi connectivity. * * 非正超时使用内部默认值;重复调用会复用已有良好状态。STA 已连接或 BLE 配网已运行均视为 * 成功;超时/失败返回后不会在后台继续隐藏的破坏性重试。 * * Semantics: * - `connect_timeout_ms <= 0`: apply internal default timeout. * - Operation is safe to call repeatedly; existing good state is reused when possible. * - Returns `ESP_OK` when STA is connected or mini-program BLE provisioning is active. * - On timeout/failure, function returns explicit error and does not continue hidden * destructive retries after return. */ esp_err_t control_plane_bootstrap_connectivity(const char *seed_ssid, const char *seed_password, int connect_timeout_ms); /** * @brief 消费一次低内存 BLE 恢复启动请求。 / Consume a one-shot BLE recovery request. * * 请求借助 RTC no-init 内存跨软重启;本次启动读到 true 后会锁存,使联网流程跳过 STA 重连, * 在完整运行时任务启动前优先进入 BLE 配网。 * * The request survives a software restart in RTC no-init memory. A true result * is latched for the current boot so connectivity skips STA reconnect and * starts BLE provisioning before full runtime workers. */ bool control_plane_take_mini_program_recovery_boot_request(void); bool control_plane_is_sta_connected(void); esp_err_t control_plane_stop_mini_program_provisioning(int timeout_ms); bool control_plane_is_mini_program_provisioning_running(void); /** * @brief 以有界语义执行 Plan A 启动音策略。 / Execute the bounded Plan A boot-sound policy. * * 当前提示音后端可能返回 `ESP_ERR_NOT_SUPPORTED`;调用方应记录并继续启动,而非视为致命失败。 * The current platform voice-prompt backend may return ESP_ERR_NOT_SUPPORTED; * callers should log and continue boot in that case. */ esp_err_t control_plane_play_boot_sound(int timeout_ms); /** * @brief 启动多模态客户端运行时。 / Start the multimodal client runtime. * * 已运行时重复调用幂等;所需后台任务采用有界启动;无法完成时返回明确错误。 * * Semantics: * - Idempotent for repeated calls when runtime is already active. * - Creates/starts required background tasks with bounded startup behavior. * - Returns explicit error when startup cannot complete. */ esp_err_t control_plane_start_multimodal_client(void); /** * @brief 停止多模态运行时及其后台任务。 / Stop multimodal runtime and owned workers. * * 重复停止幂等;先请求协作退出,再按有界预算等待资源回收;未能完成时返回明确错误。 * * Semantics: * - Idempotent and safe to call repeatedly. * - Requests cooperative stop first, then applies bounded waits for worker teardown. * - Returns an explicit error if teardown does not complete. */ esp_err_t control_plane_stop_multimodal_client(void); esp_err_t control_plane_register_model_command_callback(control_plane_model_command_callback_t cb, void *user_data); /** * @brief 取消当前 push2talk 语音输入。 / Cancel active push-to-talk speech input. * * 向活动任务发送协议 `CancelSpeech` 指令,并停止本地上行采集。 * Sends the protocol `CancelSpeech` directive for the active multimodal task * and stops local uplink capture. */ esp_err_t control_plane_multimodal_cancel_speech(void); /** * @brief 请求当前会话响应本地文本。 / Ask the active session to respond to local text. * * 仅在活动任务处于 `DialogStateChanged(Listening)` 时有效;请求编码为协议 * `RequestToRespond` 指令。 * Requires an active task in `DialogStateChanged(Listening)` state. The request * is encoded as the documented `RequestToRespond` directive. */ esp_err_t control_plane_multimodal_request_to_respond( const control_plane_multimodal_respond_request_t *request); /** * @brief 更新当前多模态会话上下文。 / Update per-session multimodal context. * * `request->parameters_json` 会作为 UpdateInfo 的 `payload.parameters` 对象编码。 * Encodes `request->parameters_json` as the documented UpdateInfo * `payload.parameters` object. */ esp_err_t control_plane_multimodal_update_info( const control_plane_multimodal_update_info_request_t *request); /** * @brief 启动 control-plane 拥有的运行时工作任务。 / Start control-plane runtime workers. * * 包含模型命令分发任务和受联网状态门禁的多模态启动任务;重复调用安全且幂等。 * This includes model-command dispatch worker and connectivity-gated multimodal startup worker. * Safe to call repeatedly. */ esp_err_t control_plane_start_runtime_workers(void); void control_plane_stop_runtime_workers(void); esp_err_t control_plane_start_ota_service(void); esp_err_t control_plane_stop_ota_service(int timeout_ms); esp_err_t control_plane_get_ota_snapshot(control_plane_ota_snapshot_t *out); /** * @brief 仅启动分阶段事件观察运行时。 / Start only the staged event-observer runtime. * * 仅启动系统事件分发和按键观察,以便通过日志验证模块事件;刻意不启动 LCD、Wi-Fi、 * WebSocket、运势、LAN HTTP、电机或音频业务任务。 * * This starts the system event dispatcher and key input observer so module * events can be verified through logs. It intentionally does not start LCD, * Wi-Fi, WebSocket, fortune, LAN HTTP, motor, or audio business workers. */ esp_err_t control_plane_start_event_observer_runtime(void); void control_plane_stop_event_observer_runtime(void); /** * @brief control-plane 拥有的每日运势策略。 / Control-plane-owned daily fortune policy. * * 启用 `CONFIG_FORTUNE_ENABLE` 时由运行时 workers 启动:等待 STA 和 SNTP,按本地日期拉取、 * 持久化并应用幸运色/财位,然后调度至本地次日 00:00。`refresh_now` 请求立即刷新;任务未 * 运行返回 `ESP_ERR_INVALID_STATE`,构建未启用返回 `ESP_ERR_NOT_SUPPORTED`。 * * The daily fortune worker is started by `control_plane_start_runtime_workers()` * when `CONFIG_FORTUNE_ENABLE` is set. It waits for STA connectivity and SNTP * time, fetches the favorable-color and wealth-direction result for the local * date, persists it, applies the direction, and reschedules for local 00:00. * * `control_plane_fortune_refresh_now()` requests an immediate refetch+apply * (e.g. after provisioning, or for manual testing). Returns * `ESP_ERR_INVALID_STATE` when the worker is not running and * `ESP_ERR_NOT_SUPPORTED` when the feature is disabled at build time. */ esp_err_t control_plane_fortune_refresh_now(void); esp_err_t control_plane_init_lcd_device(void); esp_err_t control_plane_start_lcd_render_worker(void); void control_plane_start_lcd_render_loop(void); esp_err_t control_plane_request_boot_expression(void); /** * @brief 有界停止 LCD 渲染循环。 / Stop LCD render loop with bounded teardown. * * 负超时无效;零只请求异步停止;正值等待到预算结束,再由下层执行确定性兜底。 * `timeout_ms < 0` is invalid, zero requests asynchronous stop, and positive * values wait up to the bound before lower layers apply deterministic fallback. */ esp_err_t control_plane_stop_lcd_render_loop(int timeout_ms); /** * @brief 在控制台 UART 启动工厂配置 CLI。 / Start the factory CLI on console UART. * * 创建 control-plane 拥有的 worker,读取 `\r\n` 分隔命令并返回 `OK`、`ERROR ` * 或单行 JSON;已运行时幂等。用于制造阶段灌装设备身份及读取/签名认证密钥材料。 * * Spawns a control-plane-owned worker that reads `\r\n`-delimited commands and * replies with `OK` / `ERROR ` / JSON lines. Idempotent (a no-op if * already running). Used for device identity provisioning and reading/signing * device-auth key material during manufacturing. */ esp_err_t control_plane_factory_cli_start(void); /** * @brief 协作请求并有界停止工厂 CLI。 / Stop factory CLI cooperatively with bounded teardown. * * 重复调用安全且幂等。 * Idempotent and safe to call repeatedly. */ void control_plane_factory_cli_stop(void); #ifdef __cplusplus } #endif