Files

769 lines
35 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* Platform 层调用与所有权概览 / Platform call flow and ownership overview
*
* 上层只能通过本门面调用硬件与系统原语;domain 适配器负责把业务语义映射为
* platform 请求,platform 再把请求交给内部驱动或工作任务。典型路径如下:
* Upper layers call hardware/system primitives only through this facade. Domain
* adapters map business semantics to platform requests, which are then handled
* by internal drivers or platform-owned workers:
*
* domain adapter -> platform_* facade -> internal driver/queue -> ESP-IDF/hardware
*
* 长生命周期任务、队列、socket、codec 和面板句柄均由 platform 创建并回收;
* 调用方拥有输入/输出缓冲,除非具体契约明确说明请求数据会在调用期间复制。
* Platform owns long-lived tasks, queues, sockets, codec and panel handles.
* Callers own I/O buffers unless a contract explicitly states that data is copied.
*
* 状态查询返回快照;回调注册信息在锁内复制、在锁外调用。带 timeout_ms 的停止
* 接口遵循各自文档中的有界等待语义,不应由上层额外复制生命周期状态。
* State APIs return snapshots. Callback registrations are copied under lock and
* invoked outside locks. Timeout-bearing stop APIs use the bounded semantics below.
*/
/*
* 步进速度以“单步延时(微秒)”表示,因此数值越小速度越快;这些值是公共预设,
* 实际安全范围仍受驱动的最大角度、启动延时和加减速曲线约束。
* Stepper speed is expressed as per-step delay in microseconds, so smaller values
* are faster. Driver-side angle limits and acceleration still apply.
*/
#define PLATFORM_STEPPER_SPEED_ULTRA_FAST 600
#define PLATFORM_STEPPER_SPEED_FAST 800
#define PLATFORM_STEPPER_SPEED_NORMAL 1500
#define PLATFORM_STEPPER_SPEED_SLOW 2000
/* 通用数字输入描述:统一 GPIO 编号、有效电平及上下拉配置。 */
typedef struct {
int gpio_num;
int active_level;
bool pullup_enabled;
bool pulldown_enabled;
} platform_digital_input_config_t;
/* Wi-Fi 门户只通过这些钩子请求上层行为,不直接依赖具体业务模块。 */
typedef struct {
void (*set_keep_awake)(bool keep_awake, void *user_data);
esp_err_t (*play_prompt_file)(const char *path, void *user_data);
void *user_data;
} platform_wifi_behavior_hooks_t;
/* 多模态传输回调的数据只在回调执行期间有效,需要长期使用时必须复制。 */
typedef struct {
void (*on_connected)(void *user_data);
void (*on_disconnected)(void *user_data);
void (*on_error)(void *user_data);
void (*on_text_payload)(const uint8_t *data, size_t data_len, void *user_data);
void (*on_binary_payload)(const uint8_t *data, size_t data_len, void *user_data);
void *user_data;
} platform_multimodal_transport_callbacks_t;
/* generation 用于区分连续的下行播放会话,避免旧事件结束新会话。 */
typedef struct {
void (*on_started)(uint32_t playback_generation, void *user_data);
void (*on_completed)(uint32_t playback_generation, void *user_data);
void (*on_stopped)(uint32_t playback_generation, void *user_data);
void (*on_stalled)(uint32_t playback_generation, void *user_data);
void *user_data;
} platform_multimodal_downstream_playback_callbacks_t;
typedef void (*platform_multimodal_audio_pcm_observer_t)(
const int16_t *pcm,
size_t samples,
uint32_t sample_rate_hz,
void *user_data);
/*
* LAN HTTP 固定容量是传输层内存边界,也是调用方结构体的 ABI 上限;增大 body
* 上限会直接增加服务器常驻 I/O 缓冲,修改时需同步评估内部 RAM/PSRAM。
* LAN HTTP capacities are transport and ABI bounds. Raising body limits increases
* the server's resident I/O buffers and requires a memory-budget review.
*/
#define PLATFORM_LAN_HTTP_PATH_MAX_LEN 96
#define PLATFORM_LAN_HTTP_HOSTNAME_MAX_LEN 32
#define PLATFORM_LAN_HTTP_REQUEST_BODY_MAX_LEN 1024
#define PLATFORM_LAN_HTTP_RESPONSE_BODY_MAX_LEN 16384
/* 局域网 HTTP 层只提供有界传输能力,URL 路由与业务语义由 domain 层负责。 */
typedef enum {
PLATFORM_LAN_HTTP_METHOD_GET = 0,
PLATFORM_LAN_HTTP_METHOD_POST,
} platform_lan_http_method_t;
typedef enum {
PLATFORM_LAN_HTTP_STATUS_OK = 200,
PLATFORM_LAN_HTTP_STATUS_ACCEPTED = 202,
PLATFORM_LAN_HTTP_STATUS_BAD_REQUEST = 400,
PLATFORM_LAN_HTTP_STATUS_NOT_FOUND = 404,
PLATFORM_LAN_HTTP_STATUS_METHOD_NOT_ALLOWED = 405,
PLATFORM_LAN_HTTP_STATUS_PAYLOAD_TOO_LARGE = 413,
PLATFORM_LAN_HTTP_STATUS_INTERNAL_ERROR = 500,
} platform_lan_http_status_t;
typedef enum {
PLATFORM_LAN_HTTP_CONTENT_TEXT = 0,
PLATFORM_LAN_HTTP_CONTENT_HTML,
PLATFORM_LAN_HTTP_CONTENT_JSON,
} platform_lan_http_content_type_t;
typedef struct {
platform_lan_http_method_t method;
char path[PLATFORM_LAN_HTTP_PATH_MAX_LEN];
const uint8_t *body;
size_t body_len;
} platform_lan_http_request_t;
typedef struct {
platform_lan_http_status_t status;
platform_lan_http_content_type_t content_type;
uint8_t *body;
size_t body_capacity;
size_t body_len;
} platform_lan_http_response_t;
typedef esp_err_t (*platform_lan_http_handler_t)(
const platform_lan_http_request_t *request,
platform_lan_http_response_t *response,
void *user_data);
typedef struct {
uint16_t port;
char hostname[PLATFORM_LAN_HTTP_HOSTNAME_MAX_LEN];
platform_lan_http_handler_t handler;
void *user_data;
} platform_lan_http_server_config_t;
typedef struct {
bool running;
uint16_t port;
char hostname[PLATFORM_LAN_HTTP_HOSTNAME_MAX_LEN];
} platform_lan_http_server_state_t;
/* HTTPS 请求和响应缓冲区均由调用方持有,平台层不会长期保存这些指针。 */
typedef struct {
const char *name;
const char *value;
} platform_https_header_t;
typedef struct {
const char *url;
const char *payload;
const platform_https_header_t *headers;
size_t header_count;
int timeout_ms;
int tx_buffer_size;
int rx_buffer_size;
} platform_https_post_json_request_t;
typedef struct {
char *body;
size_t body_capacity;
size_t body_len;
int status_code;
} platform_https_response_t;
typedef void (*platform_ota_progress_callback_t)(uint64_t bytes_received,
uint64_t total_bytes,
void *user_data);
typedef bool (*platform_ota_cancel_callback_t)(void *user_data);
/*
* BLE 限制分别来自 UUID128、广播名称容量和单次 GATT 值的本地缓冲边界。
* BLE limits cover UUID128 size, advertised-name capacity and the local GATT
* value buffer; they do not define higher-level provisioning payload semantics.
*/
#define PLATFORM_BLE_UUID128_LEN 16
#define PLATFORM_BLE_GATT_NAME_MAX_LEN 31
#define PLATFORM_BLE_GATT_VALUE_MAX_LEN 512
/* BLE 层暴露通用单服务 GATT 原语,不在此处定义配网等业务协议。 */
typedef struct {
uint8_t bytes[PLATFORM_BLE_UUID128_LEN];
} platform_ble_uuid128_t;
typedef enum {
PLATFORM_BLE_GATT_NOTIFY_VALUE = 0,
PLATFORM_BLE_GATT_READ_NOTIFY_VALUE,
} platform_ble_gatt_notify_target_t;
typedef void (*platform_ble_gatt_write_callback_t)(const uint8_t *data,
size_t data_len,
void *user_data);
typedef struct {
const char *advertised_name;
platform_ble_uuid128_t service_uuid;
platform_ble_uuid128_t write_uuid;
platform_ble_uuid128_t notify_uuid;
platform_ble_uuid128_t read_notify_uuid;
const uint8_t *read_value;
size_t read_value_len;
platform_ble_gatt_write_callback_t on_write;
void *user_data;
} platform_ble_gatt_server_config_t;
/* 平台初始化与持久化存储准备分离,便于启动阶段明确处理存储失败。 */
esp_err_t platform_init(void);
esp_err_t platform_prepare_storage(void);
bool platform_storage_is_ready(void);
void platform_read_device_identity(char *device_id,
size_t device_id_len,
char *serial_number,
size_t serial_number_len);
/* 设备身份由工厂写入 NVS;读取接口始终写入调用方提供的缓冲区。 */
/**
* @brief 将设备身份(device_id + serial_number)持久化到 NVS。
* Persist device identity (device_id + serial_number) to NVS.
*
* 写入内容与 `platform_read_device_identity()` 的读取来源相同。两个参数都必须
* 非空;否则或 NVS 写入失败时返回明确错误。主要用于工厂烧录/配置。
* Writes the same storage that `platform_read_device_identity()` reads. Both
* arguments must be non-empty; returns an explicit error otherwise or on NVS
* failure. Intended for factory provisioning.
*/
esp_err_t platform_write_device_identity(const char *device_id, const char *serial_number);
/**
* @brief 工厂 CLI 使用的串口行传输原语。
* Console (UART) line-transport primitives for the factory CLI.
*
* 这些接口不保存命令状态。install 可重复调用;read 返回读取字节数,超时返回0,
* 错误返回负数。零超时表示轮询一次,负超时无效。
* Stateless primitives over the console UART. `platform_console_install()` is
* idempotent (a pre-existing console driver is treated as success).
* `platform_console_read_bytes()` returns bytes read (0 on timeout, <0 on error).
* A zero timeout polls once; negative timeouts are invalid and return <0.
*/
esp_err_t platform_console_install(void);
int platform_console_read_bytes(uint8_t *buf, size_t len, int timeout_ms);
void platform_console_write_line(const char *line);
/**
* @brief 基于 NVS 的通用持久化键值存储原语。
* Generic persistent key/value blob storage (NVS-backed).
*
* platform 只拥有无业务语义的存储能力,上层负责把类型化记录序列化为字节。
* `ns`/`key` 遵循 NVS 的15字符限制;写操作提交后才算成功,读取缺失返回
* `ESP_ERR_NOT_FOUND`,缓冲不足返回 `ESP_ERR_NVS_INVALID_LENGTH`,删除具有幂等性。
* Platform owns the storage primitive and stays semantics-free: upper layers
* serialize their own typed records into opaque bytes. `ns`/`key` follow NVS
* limits (<=15 chars). Semantics:
* - `set_blob`: opens the namespace read/write and commits atomically.
* - `get_blob`: returns `ESP_ERR_NOT_FOUND` when the namespace/key is absent,
* `ESP_ERR_NVS_INVALID_LENGTH` when `out_len` is too small. `out_len_actual`
* (optional) receives the number of bytes written.
* - `erase`: removing an absent key is treated as success (idempotent).
*/
esp_err_t platform_storage_set_blob(const char *ns, const char *key, const void *data, size_t len);
esp_err_t platform_storage_set_string(const char *ns, const char *key, const char *value);
esp_err_t platform_storage_set_u8(const char *ns, const char *key, uint8_t value);
esp_err_t platform_storage_set_i8(const char *ns, const char *key, int8_t value);
esp_err_t platform_storage_get_blob(const char *ns,
const char *key,
void *out,
size_t out_len,
size_t *out_len_actual);
esp_err_t platform_storage_get_u8(const char *ns, const char *key, uint8_t *out);
esp_err_t platform_storage_get_i8(const char *ns, const char *key, int8_t *out);
esp_err_t platform_storage_get_u32(const char *ns, const char *key, uint32_t *out);
esp_err_t platform_storage_get_u64(const char *ns, const char *key, uint64_t *out);
esp_err_t platform_storage_get_string(const char *ns,
const char *key,
char *out,
size_t out_len,
size_t *out_len_actual);
esp_err_t platform_storage_erase(const char *ns, const char *key);
/* 时间接口只负责时区和系统时钟同步,不承载业务日期或日历规则。 */
/**
* @brief 系统墙上时钟和 SNTP 同步原语。
* Wall-clock time synchronization (SNTP) primitives.
*
* 设置时区不会启动 SNTP;sync_start 应在网络就绪后调用且可重复调用;
* is_synced 通过合理的时间下界判断系统时钟是否至少成功同步过一次。
* - `platform_time_set_timezone()` applies the process-local POSIX time zone
* without starting SNTP. Call it during boot before local time is rendered.
* - `platform_time_sync_start()` is idempotent: it sets the process time zone
* (`posix_tz`, e.g. "CST-8"; ignored when NULL/empty) on every call and
* starts the SNTP client once. Must be called after the network is up.
* - `platform_time_is_synced()` reports whether the system clock has advanced
* past a sane epoch lower bound (i.e. SNTP has set the time at least once).
*/
esp_err_t platform_time_set_timezone(const char *posix_tz);
esp_err_t platform_time_sync_start(const char *sntp_server, const char *posix_tz);
bool platform_time_is_synced(void);
/* HTTPS 层负责 TLS 和连接资源生命周期,HTTP 状态码仍需由调用方判断。 */
/**
* @brief 使用调用方有界响应缓冲执行 HTTPS JSON POST。
* POST a JSON body over HTTPS with bounded caller-owned response storage.
*
* platform 管理 TLS、连接和客户端资源。一次 HTTP 交换完成即返回 ESP_OK,
* HTTP 状态码仍由调用方检查;传输、分配、响应过大或协议初始化失败返回明确错误。
* Platform owns the outbound HTTPS transport primitive and TLS client resource
* lifecycle. Completion of an HTTP exchange returns ESP_OK regardless of status
* code; callers inspect `response->status_code` and body semantics. Transport,
* allocation, oversized response, and protocol setup failures return explicit
* ESP errors.
*/
esp_err_t platform_https_post_json(const platform_https_post_json_request_t *request,
platform_https_response_t *response);
/* OTA 全程受 timeout_ms 限制,并在写入完成后校验长度与 SHA-256。 */
esp_err_t platform_ota_install_image(const char *url,
const uint8_t expected_sha256[32],
uint64_t expected_size,
int timeout_ms,
platform_ota_progress_callback_t progress_cb,
platform_ota_cancel_callback_t cancel_cb,
void *user_data);
esp_err_t platform_ota_confirm_running_image(void);
esp_err_t platform_ota_get_memory_headroom(uint32_t *free_internal,
uint32_t *largest_internal,
uint32_t *largest_dma);
void platform_restart(void);
bool platform_https_is_connect_error(esp_err_t err);
/* BLE 服务的启动、通知和停止共同构成平台拥有的 NimBLE 生命周期。 */
/** 启动通用单服务 BLE GATT 外设。 / Start a generic single-service BLE GATT peripheral. */
esp_err_t platform_ble_gatt_server_start(const platform_ble_gatt_server_config_t *config);
/** 向已订阅的 GATT 通知特征发送数据。 / Send a payload to a subscribed GATT notification characteristic. */
esp_err_t platform_ble_gatt_server_notify(platform_ble_gatt_notify_target_t target,
const uint8_t *data,
size_t data_len);
/**
* 停止 platform 拥有的 NimBLE 运行时。正超时限制等待时间;零表示仅请求协作式
* 清理并立即返回。
*
* Stop the platform-owned NimBLE runtime. A positive timeout bounds the wait;
* zero requests cooperative teardown without waiting.
*/
esp_err_t platform_ble_gatt_server_stop(int timeout_ms);
/** 将当前固件版本复制到调用方缓冲区。 / Read the running application version into caller-owned storage. */
esp_err_t platform_read_firmware_version(char *out, size_t out_len);
/* 步进电机旋转为同步调用;超时只限制本次运动,不留下后台运动任务。 */
/**
* @brief 在有界超时内同步旋转步进电机。
* Rotate stepper synchronously with a bounded timeout.
*
* 语义:非正超时不设置限制;正超时内无法完成则返回 ESP_ERR_TIMEOUT;零角度
* 直接成功且不操作硬件。函数返回后不会留下后台旋转任务。
* Semantics:
* - `timeout_ms <= 0`: no timeout limit is enforced.
* - `timeout_ms > 0`: returns `ESP_ERR_TIMEOUT` if the move cannot complete
* within the timeout budget.
* - `angle == 0`: returns `ESP_OK` and performs no hardware action (idempotent).
*/
esp_err_t platform_stepper_rotate_angle_with_accel_timeout(float angle,
int target_delay_us,
int timeout_ms);
/**
* @brief 在有界锁等待内释放步进电机保持电流。
* Release stepper holding current with bounded lock acquisition.
*
* 负超时无效,零表示非阻塞尝试,正数限制等待当前运动结束的时间。
* A negative timeout is invalid, zero is non-blocking, and a positive value
* bounds the wait for an in-progress movement to finish.
*/
esp_err_t platform_stepper_release_hold(int timeout_ms);
void platform_stepper_power_off(void);
/** 当前启动由已配置深睡唤醒源触发时返回 true。 / Return true for a configured deep-sleep wake. */
bool platform_power_woke_from_deep_sleep(void);
/** 接受深睡唤醒后释放板级输出保持。 / Release board-output holds after deep-sleep wake. */
void platform_power_release_deep_sleep_holds(void);
/**
* 配置低电平有效的充电器唤醒源和可选按键唤醒源后进入深睡;成功时不返回。
*
* Enter deep sleep after configuring active-low charger and optional button
* wake GPIOs. A successful call does not return.
*/
esp_err_t platform_power_enter_deep_sleep(int charger_gpio, int button_gpio);
/* platform 只报告原始充电和电压质量,电量等级/SOC 语义由 domain 层计算。 */
typedef enum {
PLATFORM_BATTERY_CHARGE_UNKNOWN = 0,
PLATFORM_BATTERY_CHARGE_NOT_CHARGING,
PLATFORM_BATTERY_CHARGE_CHARGING,
} platform_battery_charge_state_t;
typedef struct {
int adc_voltage_mv;
int voltage_mv;
platform_battery_charge_state_t charge_state;
bool voltage_valid;
bool high_load_active;
bool cooldown_active;
} platform_battery_sample_t;
/** 幂等初始化电池 ADC 和 TP4057 状态输入。 / Idempotently initialize battery sensing. */
esp_err_t platform_battery_init(void);
/**
* 读取一次裁剪均值电池电压和负载质量快照。即使返回成功,电气读数不合理时
* voltage_valid 仍可能为 false;此时不得解释为空电池。
*
* Read one trimmed-mean battery voltage sample and a load-quality snapshot.
* A successful call can still return voltage_valid=false for an implausible
* electrical reading; callers must not interpret such a sample as an empty
* battery.
*/
esp_err_t platform_battery_read_sample(platform_battery_sample_t *out_sample);
esp_err_t platform_configure_digital_input(const platform_digital_input_config_t *config);
bool platform_is_digital_input_active(const platform_digital_input_config_t *config);
/* 行为钩子用于隔离 Wi-Fi 门户与上层唤醒、语音提示实现。 */
void platform_behavior_set_keep_awake(bool keep_awake);
esp_err_t platform_behavior_play_prompt_file(const char *path);
void platform_wifi_portal_set_behavior_hooks(const platform_wifi_behavior_hooks_t *hooks);
esp_err_t platform_wifi_portal_init_defaults(const char *seed_ssid, const char *seed_password);
bool platform_wifi_portal_has_saved_credentials(void);
/**
* 按持久化优先级复制已保存 SSID,不暴露密码。out_ssids 是 capacity 行、每行
* ssid_stride 字节的调用方缓冲区。
*
* Copy saved SSIDs in persistent priority order without exposing passwords.
* `out_ssids` contains `capacity` rows of `ssid_stride` bytes.
*/
esp_err_t platform_wifi_portal_get_saved_ssids(char *out_ssids,
size_t ssid_stride,
size_t capacity,
size_t *out_count);
bool platform_wifi_portal_sta_is_connected(void);
/** 应用待机 modem 省电或恢复活动策略。 / Apply standby modem power save or restore active policy. */
esp_err_t platform_wifi_portal_set_light_standby(bool standby);
esp_err_t platform_wifi_portal_start_config_ap(const char *ssid_prefix);
/**
* @brief 在超时范围内使用已保存凭据连接 STA。
* Connect STA using saved credentials with timeout.
*
* 非正超时使用默认10秒;已连接时幂等成功;失败返回明确错误,返回后不会继续
* 隐藏的破坏性连接操作。
* Semantics:
* - `timeout_ms <= 0`: use default timeout (10s).
* - Returns `ESP_OK` when already connected (idempotent).
* - Returns explicit error (`ESP_ERR_TIMEOUT`, `ESP_FAIL`, etc.) on failure.
* - On timeout/failure, no hidden destructive continuation is started after return.
*/
esp_err_t platform_wifi_portal_connect_sta(int timeout_ms);
esp_err_t platform_wifi_portal_connect_sta_with_credentials(const char *ssid,
const char *password,
int timeout_ms);
esp_err_t platform_wifi_portal_reconnect_sta(int timeout_ms);
esp_err_t platform_wifi_portal_stop_sta(void);
esp_err_t platform_wifi_portal_save_credentials(const char *ssid, const char *password);
esp_err_t platform_wifi_portal_clear_saved_credentials(void);
bool platform_wifi_connect_error_keeps_credentials(esp_err_t err);
/* LAN HTTP 服务采用单工作任务;回调在锁外执行,避免业务处理阻塞状态锁。 */
/**
* @brief 在已连接的 STA 接口上启动局域网 HTTP 传输。
* Start the LAN HTTP transport on the connected STA interface.
*
* 传输层接收有界 GET/POST 请求,在锁外调用处理器,并通过 mDNS 发布
* `_http._tcp`;正在启动或运行时重复 start 具有幂等性。
* The transport accepts bounded GET/POST requests, invokes the snapshotted
* handler outside locks, and advertises `_http._tcp` using mDNS. Repeated start
* calls are idempotent while the server is starting or running.
*/
esp_err_t platform_lan_http_server_start(const platform_lan_http_server_config_t *config);
/**
* @brief 停止 LAN HTTP 传输并释放任务和 socket。
* Stop the LAN HTTP transport and release its worker/socket resources.
*
* 负超时无效;零仅发出协作停止请求;正超时等待到边界后强制删除工作任务;
* 重复停止具有幂等性。
* Semantics:
* - `timeout_ms < 0`: invalid argument.
* - `timeout_ms == 0`: request cooperative stop and return immediately.
* - `timeout_ms > 0`: wait up to the bound, then force-delete the worker.
* - Repeated stop calls are idempotent.
*/
esp_err_t platform_lan_http_server_stop(int timeout_ms);
esp_err_t platform_lan_http_server_get_state(platform_lan_http_server_state_t *out_state);
/* 多模态公开接口是 domain 适配器使用的底层原语,不暴露会话业务语义。 */
/**
* @brief domain 适配器使用的底层多模态传输原语。
* Low-level multimodal transport primitive used by the domain adapter.
*
* 跨层会话契约属于 domain;这些回调保持 platform 形状,并只在 domain 运行时
* 适配器中映射一次,编排层不会直接看到内部传输类型。
* The domain facade owns the cross-layer multimodal session contract. These
* callbacks intentionally stay platform-shaped and are mapped exactly once in
* the domain runtime adapter before orchestration code can observe them.
*/
esp_err_t platform_multimodal_transport_start(
const char *bearer_token,
const char *workspace_id,
const platform_multimodal_transport_callbacks_t *callbacks);
/**
* @brief 以有界方式停止多模态 WebSocket 并清理资源。
* Stop multimodal WebSocket transport with bounded teardown.
*
* 负超时无效;零只请求协作停止;正超时结束时强制删除所属任务;重复调用幂等。
* Semantics:
* - `timeout_ms < 0`: invalid argument.
* - `timeout_ms == 0`: request cooperative stop and return immediately.
* - `timeout_ms > 0`: wait up to the bound, then force-delete owned workers.
* - Repeated calls are idempotent.
*/
esp_err_t platform_multimodal_transport_stop(int timeout_ms);
bool platform_multimodal_transport_is_connected(void);
esp_err_t platform_multimodal_transport_send_text(const char *label, const char *text);
esp_err_t platform_multimodal_transport_send_text_realtime(const char *label, const char *text);
esp_err_t platform_multimodal_transport_send_binary_realtime(const uint8_t *data, size_t len);
/* 音频上行负责采集与编码,下行负责 Opus 解码和播放;两条链路可独立停止。 */
esp_err_t platform_multimodal_audio_init(void);
void platform_multimodal_audio_deinit(void);
esp_err_t platform_multimodal_audio_start_uplink(
esp_err_t (*uplink_send)(const uint8_t *data, size_t len, void *user_data),
void *user_data);
/**
* @brief 以有界方式停止多模态上行采集与流式发送任务。
* Stop multimodal uplink capture/stream worker with bounded teardown.
*
* 负超时无效;零只请求协作停止;正超时结束时强制清理所属任务;重复调用幂等。
* Semantics:
* - `timeout_ms < 0`: invalid argument.
* - `timeout_ms == 0`: request cooperative stop and return immediately.
* - `timeout_ms > 0`: wait up to the bound, then force-delete owned workers.
* - Repeated calls are idempotent.
*/
esp_err_t platform_multimodal_audio_stop_uplink(int timeout_ms);
bool platform_multimodal_audio_is_uplink_running(void);
bool platform_multimodal_audio_is_initialized(void);
/**
* @brief 不停止麦克风采集,仅控制扬声器功放开关。
* Gate the speaker power amplifier without stopping microphone capture.
*
* 关闭时先静音 codec 再关闭 PA;开启时恢复 PA,但保持静音直到播放真正开始。
* Disabling first mutes the codec output and then powers down the speaker PA.
* Enabling restores the PA while leaving output muted until playback resumes.
*/
esp_err_t platform_multimodal_audio_set_speaker_output_enabled(bool enabled);
/**
* @brief 为当前采集路径注册唯一的单声道 PCM 观察者。
* Register the single mono PCM observer for the active capture path.
*
* 回调在 platform 采集任务中同步执行,但不持有注册锁;回调必须有界且不可阻塞。
* The callback runs synchronously in the platform capture task, outside the
* registration lock, and must remain bounded and non-blocking.
*/
esp_err_t platform_multimodal_audio_register_pcm_observer(
platform_multimodal_audio_pcm_observer_t observer,
void *user_data);
/**
* @brief 移除匹配的 PCM 观察者并等待正在执行的回调结束。
* Remove a matching PCM observer and wait for in-flight callbacks.
*
* 负超时无效,零只执行非阻塞的排空检查。
* Negative timeout is invalid. Zero performs a non-blocking drain check.
*/
esp_err_t platform_multimodal_audio_unregister_pcm_observer(
platform_multimodal_audio_pcm_observer_t observer,
void *user_data,
int timeout_ms);
esp_err_t platform_multimodal_audio_play_downstream_opus(const uint8_t *data, size_t len);
esp_err_t platform_multimodal_audio_begin_downstream_response(uint32_t *out_playback_generation);
esp_err_t platform_multimodal_audio_finish_downstream_response(void);
esp_err_t platform_multimodal_audio_stop_downstream_playback(void);
esp_err_t platform_multimodal_audio_register_downstream_playback_callbacks(
const platform_multimodal_downstream_playback_callbacks_t *callbacks);
esp_err_t platform_init_lcd_device(void);
/* LCD 面板访问集中在渲染任务中,其他任务通过请求和状态覆盖层间接更新画面。 */
/**
* @brief 使用 PWM 设置 LCD 背光亮度。
* Set LCD backlight brightness using PWM.
*
* @param brightness_percent 亮度百分比,0为关闭、100为最亮。
* Brightness from 0 (off) to 100 (full).
* @return 成功返回 ESP_OK;超出范围返回 ESP_ERR_INVALID_ARG;LCD 未初始化返回
* ESP_ERR_INVALID_STATE。ESP_OK on success, ESP_ERR_INVALID_ARG outside the supported range,
* or ESP_ERR_INVALID_STATE before LCD initialization.
*/
esp_err_t platform_lcd_set_backlight_brightness(uint8_t brightness_percent);
/**
* @brief 启动 LCD 渲染循环。
* Start LCD render loop.
*
* 已运行时调用为空操作,具有幂等性;通过 stop 接口请求终止。
* Calling this API while the render loop is already running is a no-op (idempotent).
* Use `platform_stop_lcd_render_loop()` to request termination.
*/
void platform_start_lcd_render_loop(void);
/**
* @brief 停止 LCD 渲染循环。
* Stop LCD render loop.
*
* 负超时无效;零只请求协作停止;正超时结束后强制删除渲染任务;重复调用幂等。
* Semantics:
* - `timeout_ms < 0`: invalid argument.
* - `timeout_ms == 0`: request cooperative stop and return immediately.
* - `timeout_ms > 0`: wait up to the bound, then force-delete the render task.
* - Repeated calls are idempotent.
*/
esp_err_t platform_stop_lcd_render_loop(int timeout_ms);
/*
* presentation 的固定容量用于在请求入队时深拷贝调用方数据,避免渲染任务持有
* 外部指针。提高这些上限会增大每个内部请求对象及任务栈/队列拷贝成本。
* Fixed presentation capacities allow requests to deep-copy caller data before
* queuing. Raising them increases every internal request object's memory cost.
*/
#define PLATFORM_LCD_PRESENTATION_FRAME_ORDER_MAX 16
#define PLATFORM_LCD_PRESENTATION_ASSET_DIR_MAX 32
#define PLATFORM_LCD_PRESENTATION_STEP_MAX 12
#define PLATFORM_LCD_PRESENTATION_NO_ICON 0xFFFFU
/* presentation 仅描述播放方式;具体表情含义和资源映射属于 domain 层。 */
typedef enum {
PLATFORM_LCD_PRESENTATION_IDLE = 0,
PLATFORM_LCD_PRESENTATION_ASSET_SEQUENCE,
PLATFORM_LCD_PRESENTATION_SLOT_REELS,
PLATFORM_LCD_PRESENTATION_STEP_SEQUENCE,
} platform_lcd_presentation_kind_t;
typedef struct {
const char *asset_dir;
uint8_t frame_index;
uint16_t icon_id;
uint16_t icon_x;
uint16_t icon_y;
uint32_t delay_ms;
} platform_lcd_presentation_step_t;
typedef struct {
platform_lcd_presentation_kind_t kind;
uint8_t priority;
bool interrupt_current;
bool return_to_idle;
const char *asset_dir;
const uint8_t *frame_order;
size_t frame_order_count;
uint16_t repeat_count;
uint32_t frame_ms;
uint32_t duration_ms;
const platform_lcd_presentation_step_t *steps;
size_t step_count;
} platform_lcd_presentation_request_t;
typedef struct {
bool low_battery_visible;
bool charging_visible;
bool battery_valid; /* false 时显示未知值,不伪造 0%。 */
uint8_t battery_percent; /* domain 提供的 SOC,0..100。 */
} platform_lcd_status_overlay_t;
/* 状态覆盖层是持久状态,不会随一次 presentation 播放结束而自动清除。 */
/**
* @brief 设置持续叠加在 presentation 画面上的 LCD 状态图层。
* Set persistent LCD status overlays composited over presentation frames.
*
* 状态会同步复制并持续有效,直到被下一次调用替换;PNG 解码和面板访问只由
* 渲染任务执行。
* The state is copied synchronously and remains active until replaced. The
* render task owns all PNG decoding and panel access.
*/
esp_err_t platform_lcd_set_status_overlay(const platform_lcd_status_overlay_t *overlay);
/**
* @brief 请求播放一个 LCD presentation。
* Request a LCD presentation playback.
*
* 非阻塞;LCD 未初始化或停止中返回状态错误;请求数据在调用期间深拷贝;高优先级
* 可中断请求会替换当前播放;与当前或待处理请求相同时会合并并返回成功。
* Semantics:
* - Non-blocking.
* - Returns `ESP_ERR_INVALID_STATE` if the LCD is not initialized or is stopping.
* - Request data is copied during the call; caller-owned pointers only need to
* remain valid until this function returns.
* - Higher-priority interrupting requests replace the active presentation.
* - Same active/pending requests are coalesced and return `ESP_OK`.
*/
esp_err_t platform_lcd_request_presentation(const platform_lcd_presentation_request_t *request);
/**
* @brief 预解码 LCD presentation 资源,但不改变当前显示状态。
* Preload decoded assets without changing the current display state.
*
* 请求在渲染任务中执行,调用方阻塞到完成或内部安全超时;当前仅支持资源序列;
* 内存不足时渲染任务可以淘汰较旧的缓存资源。
* Semantics:
* - Queues the preload on the LCD render task and blocks until it completes or
* the internal safety timeout elapses.
* - Returns `ESP_ERR_INVALID_STATE` if the LCD is not initialized or is stopping.
* - Request data is copied during the call; caller-owned pointers only need to
* remain valid until this function returns.
* - Only asset-sequence presentations are supported; other kinds return
* `ESP_ERR_INVALID_ARG`.
* - The render task performs the preload between display frames and may drop
* older cached assets if memory is needed.
*/
esp_err_t platform_lcd_preheat_presentation(const platform_lcd_presentation_request_t *request);
/**
* @brief 读取 LCD presentation 完成计数器。
* Read the LCD presentation completion counter.
*
* 每当有限资源序列完成配置轮次,计数递增。提交前读取快照,再把快照传给等待接口。
* The counter increments each time a finite asset-sequence presentation
* finishes its configured cycles. Snapshot it before queuing a finite
* presentation, then pass the snapshot to
* `platform_lcd_wait_presentation_completion()`.
*/
uint32_t platform_lcd_presentation_completion_count(void);
/**
* @brief 阻塞等待有限资源序列 presentation 完成。
* Block until a finite asset-sequence presentation completes.
*
* 等待计数超过 since_count 或 timeout_ms 到期;零超时只轮询一次。无限循环的
* presentation 不会完成,因此等待必然超时。
* Semantics:
* - Blocks the calling task until the completion
* counter advances past `since_count`, or `timeout_ms` elapses.
* - `timeout_ms == 0` polls once and returns without waiting.
* - Returns `ESP_OK` on completion, `ESP_ERR_TIMEOUT` on timeout, and
* `ESP_ERR_INVALID_STATE` if the LCD is not initialized or is stopping.
* - Intended for finite presentations (`repeat_count > 0`); an infinite
* presentation never completes and will time out.
*/
esp_err_t platform_lcd_wait_presentation_completion(uint32_t since_count, uint32_t timeout_ms);
#ifdef __cplusplus
}
#endif