767 lines
35 KiB
C
767 lines
35 KiB
C
/*
|
||
* 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;
|
||
} 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
|