Files
lucky-cat/components/domain/include/domain.h
T

778 lines
33 KiB
C
Raw 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-2026 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
/*
* Domain 层调用与所有权概览 / Domain call flow and ownership overview
*
* domain 是业务语义的唯一所有者:方向、校准、电池等级、配网协议、表情映射、
* 用户资料、运势和 OTA 响应解析均在此定义。它只通过 platform facade 使用硬件原语,
* 不把 platform 类型暴露给 control_plane。
* Domain is the single owner of business semantics and consumes hardware only
* through the platform facade without leaking platform types upward.
*
* control_plane -> domain facade -> domain service/adapter -> platform facade
*
* store/service 状态以 domain 内部快照或下层持久化为事实来源;回调携带的临时数据
* 只在调用期间有效。阻塞接口的 timeout 包含锁等待和下层操作的完整调用方预算。
* Stores and service snapshots are SSOTs. Callback payloads are temporary, and
* blocking timeouts cover lock acquisition plus all lower-layer work.
*/
/* 方向轴与校准策略属于 domain 语义,GPIO/步进相位等硬件细节不得进入这些结构。 */
typedef struct {
float initial_angle;
int rotate_speed_us;
} domain_direction_axis_config_t;
typedef struct {
bool enabled;
float calibration_angle;
float step_angle_deg;
float max_sweep_deg;
bool search_clockwise;
int settle_delay_ms;
} domain_direction_calibration_t;
/* 电池原始采样由 platform 提供,domain 负责充电、等级、有效性和抑制语义。 */
typedef enum {
DOMAIN_BATTERY_CHARGE_UNKNOWN = 0,
DOMAIN_BATTERY_CHARGE_NOT_CHARGING,
DOMAIN_BATTERY_CHARGE_CHARGING,
} domain_battery_charge_state_t;
typedef enum {
DOMAIN_BATTERY_LEVEL_UNKNOWN = 0,
DOMAIN_BATTERY_LEVEL_NORMAL,
DOMAIN_BATTERY_LEVEL_LOW,
DOMAIN_BATTERY_LEVEL_CRITICAL,
} domain_battery_level_state_t;
typedef struct {
int voltage_mv;
int latest_adc_mv;
int latest_sample_mv;
uint8_t percentage;
domain_battery_charge_state_t charge_state;
domain_battery_level_state_t level_state;
bool valid;
bool latest_sample_valid;
bool measurement_suppressed;
bool high_load_active;
bool cooldown_active;
uint32_t consecutive_failures;
int64_t last_valid_sample_time_ms;
} domain_battery_status_t;
/** 幂等且线程安全地初始化电池领域。 / Initialize the battery domain idempotently and thread-safely. */
esp_err_t domain_battery_init(void);
/** 获取并处理一次电池采样,不执行产品策略。 / Process one battery sample without product policy. */
esp_err_t domain_battery_sample(void);
/** 复制最新电池 SSOT 快照。 / Copy the latest battery SSOT snapshot into `out_status`. */
esp_err_t domain_battery_get_status(domain_battery_status_t *out_status);
/** 进入板级深睡,成功时不返回。 / Enter configured deep sleep; success does not return. */
esp_err_t domain_battery_prepare_shutdown(void);
/** 当前启动来自深睡唤醒源时返回 true。 / Return true after a deep-sleep wake. */
bool domain_battery_woke_from_deep_sleep(void);
/** 接受唤醒后释放低功耗 GPIO 保持。 / Release low-power GPIO holds after wake. */
void domain_battery_complete_wakeup(void);
typedef enum {
DOMAIN_WIFI_PROMPT_WIFI_SUCCESS = 0,
DOMAIN_WIFI_PROMPT_AFTER_WIFI_SUCCESS,
DOMAIN_WIFI_PROMPT_WIFI_CONNECTING,
DOMAIN_WIFI_PROMPT_WIFI_FAILED,
DOMAIN_WIFI_PROMPT_WIFI_CONFIG,
} domain_wifi_prompt_id_t;
/* Wi-Fi 行为钩子使用 domain 提示语义,具体音频文件映射留在适配实现中。 */
typedef struct {
void (*set_keep_awake)(bool keep_awake);
esp_err_t (*play_prompt)(domain_wifi_prompt_id_t prompt);
esp_err_t (*play_prompt_file)(const char *path);
} domain_wifi_behavior_hooks_t;
typedef enum {
DOMAIN_MULTIMODAL_TRANSPORT_EVENT_CONNECTED = 0,
DOMAIN_MULTIMODAL_TRANSPORT_EVENT_DISCONNECTED,
DOMAIN_MULTIMODAL_TRANSPORT_EVENT_ERROR,
DOMAIN_MULTIMODAL_TRANSPORT_EVENT_TEXT_MESSAGE,
DOMAIN_MULTIMODAL_TRANSPORT_EVENT_BINARY_MESSAGE,
} domain_multimodal_transport_event_type_t;
/* 回调 payload 仅在回调期间有效;跨任务保存前必须复制。 */
typedef struct {
domain_multimodal_transport_event_type_t type;
const uint8_t *data;
size_t data_len;
} domain_multimodal_transport_event_t;
typedef void (*domain_multimodal_transport_event_callback_t)(
const domain_multimodal_transport_event_t *event,
void *user_data);
typedef esp_err_t (*domain_multimodal_audio_uplink_cb_t)(
const uint8_t *data,
size_t len,
void *user_data);
typedef enum {
DOMAIN_LOCAL_COMMAND_MIAO_ZHANG_GUI = 1,
} domain_local_command_id_t;
typedef enum {
DOMAIN_LOCAL_COMMAND_EVENT_DETECTED = 0,
} domain_local_command_event_type_t;
typedef struct {
domain_local_command_event_type_t type;
domain_local_command_id_t command_id;
uint32_t confidence_x10000;
uint32_t detection_generation;
} domain_local_command_event_t;
typedef void (*domain_local_command_event_callback_t)(
const domain_local_command_event_t *event,
void *user_data);
typedef struct {
bool started;
bool detection_running;
uint32_t detection_generation;
uint32_t detection_count;
uint32_t dropped_pcm_frames;
uint32_t task_stack_high_water_bytes;
} domain_local_command_snapshot_t;
typedef enum {
DOMAIN_MULTIMODAL_DOWNSTREAM_PLAYBACK_STARTED = 0,
DOMAIN_MULTIMODAL_DOWNSTREAM_PLAYBACK_COMPLETED,
DOMAIN_MULTIMODAL_DOWNSTREAM_PLAYBACK_STOPPED,
DOMAIN_MULTIMODAL_DOWNSTREAM_PLAYBACK_STALLED,
} domain_multimodal_downstream_playback_event_type_t;
typedef struct {
domain_multimodal_downstream_playback_event_type_t type;
uint32_t playback_generation;
} domain_multimodal_downstream_playback_event_t;
typedef void (*domain_multimodal_downstream_playback_callback_t)(
const domain_multimodal_downstream_playback_event_t *event,
void *user_data);
typedef enum {
DOMAIN_EXPRESSION_IDLE = 0,
DOMAIN_EXPRESSION_SERVER_ERROR,
DOMAIN_EXPRESSION_SLOT_REELS,
DOMAIN_EXPRESSION_WIFI_CONNECTING,
DOMAIN_EXPRESSION_WIFI_DISCONNECTED,
DOMAIN_EXPRESSION_WEALTH_SPIN,
DOMAIN_EXPRESSION_WEALTH_DIRECTION,
DOMAIN_EXPRESSION_MUYU,
DOMAIN_EXPRESSION_CHOUQIAN,
DOMAIN_EXPRESSION_WUXING,
DOMAIN_EXPRESSION_LISTENING,
DOMAIN_EXPRESSION_OTA_UPDATE,
DOMAIN_EXPRESSION_LCD_BOUNDARY_TEST,
} domain_expression_id_t;
typedef enum {
DOMAIN_WUXING_ELEMENT_WATER = 0,
DOMAIN_WUXING_ELEMENT_WOOD,
DOMAIN_WUXING_ELEMENT_FIRE,
DOMAIN_WUXING_ELEMENT_EARTH,
DOMAIN_WUXING_ELEMENT_METAL,
DOMAIN_WUXING_ELEMENT_COUNT,
} domain_wuxing_element_t;
typedef enum {
DOMAIN_EXPRESSION_TRIGGER_UNSPECIFIED = 0,
DOMAIN_EXPRESSION_TRIGGER_SERVER_TASK_FAILED,
DOMAIN_EXPRESSION_TRIGGER_SERVER_OUTPUT_ERROR,
DOMAIN_EXPRESSION_TRIGGER_SLOT_SWITCH,
DOMAIN_EXPRESSION_TRIGGER_WIFI_BOOTSTRAP,
DOMAIN_EXPRESSION_TRIGGER_WIFI_DISCONNECTED,
DOMAIN_EXPRESSION_TRIGGER_WIFI_RECONNECTED,
DOMAIN_EXPRESSION_TRIGGER_MODEL_DIRECTION,
DOMAIN_EXPRESSION_TRIGGER_DAILY_FORTUNE,
DOMAIN_EXPRESSION_TRIGGER_PTT_DOUBLE_CLICK,
DOMAIN_EXPRESSION_TRIGGER_SW2_SINGLE_CLICK,
DOMAIN_EXPRESSION_TRIGGER_SW2_DOUBLE_CLICK,
DOMAIN_EXPRESSION_TRIGGER_LOCAL_WAKE,
DOMAIN_EXPRESSION_TRIGGER_OTA_UPDATE,
DOMAIN_EXPRESSION_TRIGGER_MODEL_LUCKY_COLOR,
DOMAIN_EXPRESSION_TRIGGER_PTT_BUTTON,
DOMAIN_EXPRESSION_TRIGGER_FACTORY_LCD_TEST,
} domain_expression_trigger_t;
/* 表情请求表达业务意图;资源目录、帧序列和图标位置由 domain 适配器映射。 */
typedef struct {
domain_expression_id_t id;
domain_expression_trigger_t trigger;
size_t direction_index; /* Only used by DOMAIN_EXPRESSION_WEALTH_DIRECTION */
size_t chouqian_result_index; /* Optional for DOMAIN_EXPRESSION_CHOUQIAN */
bool chouqian_result_index_valid;
domain_wuxing_element_t wuxing_element; /* Optional for DOMAIN_EXPRESSION_WUXING */
bool wuxing_element_valid;
} domain_expression_request_t;
esp_err_t domain_init(const domain_direction_axis_config_t *config,
const domain_direction_calibration_t *calibration);
const domain_direction_axis_config_t *domain_direction_default_axis_config(void);
const domain_direction_calibration_t *domain_direction_default_calibration(void);
bool domain_is_initialized(void);
size_t domain_get_direction_count(void);
bool domain_get_direction_view(size_t index,
const char **out_name_cn,
const char **out_name_en,
float *out_angle);
bool domain_find_direction_by_name(const char *input, size_t *out_index);
bool domain_get_current_direction_index(size_t *out_index);
float domain_get_current_angle(void);
void domain_set_current_angle(float angle);
int domain_get_rotate_speed_us(void);
/**
* @brief 旋转到绝对方向角。 / Rotate to an absolute direction angle.
*
* 非正 timeout 使用 domain 默认值;预算覆盖状态锁获取和电机运动全过程。
* `timeout_ms <= 0` uses the default domain timeout. The timeout is the total
* caller-visible budget, including state-lock acquisition and motor movement.
*/
esp_err_t domain_rotate_to_angle_with_timeout(float target_angle, int timeout_ms);
esp_err_t domain_apply_rotation_delta(float delta);
/** 释放电机保持电流,下次运动自动重新励磁。 / Release hold; next movement re-energizes. */
esp_err_t domain_direction_release_motor_hold(int timeout_ms);
void domain_set_calibration(const domain_direction_calibration_t *config);
bool domain_get_calibration(domain_direction_calibration_t *out_config);
/**
* @brief 执行方向校准。 / Run direction calibration.
*
* 非正 timeout 使用默认校准预算,包含锁等待、分步运动、开关稳定等待和释放扫描。
* `timeout_ms <= 0` uses the default calibration timeout. The timeout is the
* total caller-visible budget, including state-lock acquisition, movement
* steps, switch settle delays, and release sweep.
*/
esp_err_t domain_calibrate_with_timeout(const domain_direction_calibration_t *config, int timeout_ms);
esp_err_t domain_runtime_multimodal_transport_start(
const char *bearer_token,
const char *workspace_id,
domain_multimodal_transport_event_callback_t cb,
void *user_data);
/**
* @brief 有界停止多模态传输。 / Stop multimodal transport 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 domain_runtime_multimodal_transport_stop(int timeout_ms);
bool domain_runtime_multimodal_transport_is_connected(void);
esp_err_t domain_runtime_multimodal_transport_send_text(const char *label, const char *text);
esp_err_t domain_runtime_multimodal_transport_send_text_realtime(const char *label, const char *text);
esp_err_t domain_runtime_multimodal_transport_send_binary_realtime(const uint8_t *data, size_t len);
esp_err_t domain_runtime_multimodal_audio_init(void);
void domain_runtime_multimodal_audio_deinit(void);
esp_err_t domain_runtime_multimodal_audio_start_uplink(
domain_multimodal_audio_uplink_cb_t cb,
void *user_data);
/**
* @brief 暂停采集、补齐尾帧并限时排空发送;常驻任务由 deinit 回收。
*
* 负超时无效;零尝试立即完成,否则 ESP_ERR_TIMEOUT;正数为完整调用预算。
* 成功后没有在途采集/发送;超时保留资源供重试,不后台清理,不应发送 StopSpeech。
*/
esp_err_t domain_runtime_multimodal_audio_stop_uplink(int timeout_ms);
bool domain_runtime_multimodal_audio_is_uplink_running(void);
bool domain_runtime_multimodal_audio_is_initialized(void);
/** 保留麦克风采集,仅控制扬声器输出。 / Gate speaker output while preserving capture. */
esp_err_t domain_runtime_multimodal_audio_set_speaker_output_enabled(bool enabled);
typedef struct {
uint32_t generation;
uint32_t min_speech_ms;
uint32_t end_silence_ms;
} domain_speech_endpoint_config_t;
typedef struct {
uint32_t generation;
bool active;
bool speech_confirmed;
bool ended;
uint32_t audio_ms;
uint32_t silence_ms;
uint32_t max_process_us;
uint32_t discontinuities;
} domain_speech_endpoint_snapshot_t;
/**
* 本地端点检测,无新 worker。start 对相同配置幂等,不同配置须先 stop。
* start/stop/get_snapshot 最多等锁 100 ms;stop 另以同一总预算等待 PCM 回调。
* stop 超时保持资源可重试,绝不后台释放;成功移除订阅并销毁 VAD。
* PCM 回调只用零等待锁及固定 20 ms 分帧,不执行协议或阻塞式收尾。
* 配置关闭时 start 返回 ESP_ERR_NOT_SUPPORTED,stop 幂等,快照为 inactive。
*/
esp_err_t domain_speech_endpoint_start(const domain_speech_endpoint_config_t *config);
esp_err_t domain_speech_endpoint_stop(void);
esp_err_t domain_speech_endpoint_get_snapshot(domain_speech_endpoint_snapshot_t *out);
/**
* @brief 启动“喵掌柜”本地 MultiNet 检测。 / Start local MultiNet detection for "喵掌柜".
*
* 回调运行在 domain 推理任务中,必须有界且不可阻塞;相同回调的重复启动幂等。
* The callback runs in the domain inference task and must be bounded and
* non-blocking. Repeated start with the same callback is idempotent.
*/
esp_err_t domain_runtime_local_command_start(
domain_local_command_event_callback_t callback,
void *user_data);
esp_err_t domain_runtime_local_command_suspend(int timeout_ms);
esp_err_t domain_runtime_local_command_resume(int timeout_ms);
esp_err_t domain_runtime_local_command_stop(int timeout_ms);
esp_err_t domain_runtime_local_command_get_snapshot(
domain_local_command_snapshot_t *out_snapshot);
esp_err_t domain_runtime_multimodal_audio_play_downstream_opus(const uint8_t *data, size_t len);
esp_err_t domain_runtime_multimodal_audio_begin_downstream_response(
uint32_t *out_playback_generation);
esp_err_t domain_runtime_multimodal_audio_finish_downstream_response(void);
esp_err_t domain_runtime_multimodal_audio_stop_downstream_playback(void);
esp_err_t domain_runtime_multimodal_audio_register_downstream_playback_callback(
domain_multimodal_downstream_playback_callback_t cb,
void *user_data);
esp_err_t domain_runtime_multimodal_ptt_button_init(void);
bool domain_runtime_multimodal_ptt_button_is_pressed(void);
esp_err_t domain_runtime_slot_feature_switch_init(void);
bool domain_runtime_slot_feature_switch_is_active(void);
esp_err_t domain_runtime_prepare_storage(void);
void domain_runtime_read_device_identity(char *device_id,
size_t device_id_len,
char *serial_number,
size_t serial_number_len);
esp_err_t domain_runtime_read_firmware_version(char *out, size_t out_len);
esp_err_t domain_runtime_write_device_identity(const char *device_id, const char *serial_number);
typedef enum {
DOMAIN_RUNTIME_AUTH_RECORD_KEY_MATERIAL = 0,
DOMAIN_RUNTIME_AUTH_RECORD_STATE,
} domain_runtime_auth_record_t;
/**
* @brief domain 拥有、由 platform blob 支撑的设备认证持久化端口。
* Domain-owned device-auth persistence port backed by platform blobs.
*
* domain 只拥有记录槽、存储键映射和旧版迁移边界;调用方拥有序列化 schema。
* The domain facade owns only the auth record slots, storage key mapping, and
* legacy migration boundary. Callers own the serialized record schema and pass
* opaque bytes. `load_record` returns `ESP_ERR_NOT_FOUND` when no record exists
* yet; `save_record` commits atomically.
*/
esp_err_t domain_runtime_auth_store_load_record(domain_runtime_auth_record_t record,
void *out,
size_t out_len,
size_t *out_len_actual);
esp_err_t domain_runtime_auth_store_save_record(domain_runtime_auth_record_t record,
const void *data,
size_t len);
/* ------------------------------------------------------------------------- */
/* 每日运势:用户出生资料 + 服务端计算结果。 / Daily fortune profile + result. */
/* 契约属于 domain;详见 docs/adr/0002-daily-fortune-contracts.md。 */
/* ------------------------------------------------------------------------- */
/* 固定容量使资料可直接序列化进 NVS;长度包含结尾 NUL,修改需考虑旧记录兼容。 */
#define DOMAIN_BIRTH_PLACE_MAX_LEN 64
#define DOMAIN_BIRTH_RESIDENCE_MAX_LEN 64
#define DOMAIN_BIRTH_MBTI_MAX_LEN 8
#define DOMAIN_BIRTH_TZ_MAX_LEN 32
typedef struct {
int year;
int month;
int day;
int hour;
int minute;
char birth_place[DOMAIN_BIRTH_PLACE_MAX_LEN];
char current_residence[DOMAIN_BIRTH_RESIDENCE_MAX_LEN];
char mbti[DOMAIN_BIRTH_MBTI_MAX_LEN];
char timezone[DOMAIN_BIRTH_TZ_MAX_LEN];
bool valid;
} domain_birth_profile_t;
/* 运势字段容量同时约束服务端 JSON 校验和持久化结构布局。 */
#define DOMAIN_FORTUNE_BAZI_MAX_LEN 64
#define DOMAIN_FORTUNE_ELEMENT_MAX_LEN 8
#define DOMAIN_FORTUNE_DIRECTION_MAX_LEN 16
#define DOMAIN_FORTUNE_DATE_MAX_LEN 12
typedef struct {
char bazi[DOMAIN_FORTUNE_BAZI_MAX_LEN];
char element[DOMAIN_FORTUNE_ELEMENT_MAX_LEN];
char lucky_color[DOMAIN_FORTUNE_ELEMENT_MAX_LEN];
char wealth_direction[DOMAIN_FORTUNE_DIRECTION_MAX_LEN];
char target_date[DOMAIN_FORTUNE_DATE_MAX_LEN];
size_t wealth_direction_index;
bool valid;
} domain_fortune_result_t;
typedef void (*domain_birth_profile_observer_t)(const domain_birth_profile_t *profile, void *user_data);
/**
* @brief 持久化或查询用户出生资料。 / Persist or query the user birth profile.
*
* set 校验并持久化后在锁外通知观察者;get 在无有效记录时返回 false。存储是 SSOT。
* `set` requires a valid profile, persists it, then notifies the registered
* observer with a snapshot (invoked outside any lock). `get` returns false when
* no valid profile has been stored. Storage is the single source of truth; no
* higher-layer mirror should be kept.
*/
esp_err_t domain_birth_profile_store_set(const domain_birth_profile_t *profile);
bool domain_birth_profile_store_get(domain_birth_profile_t *out_profile);
/**
* @brief 持久化或查询最近一次每日运势。 / Persist or query the latest daily fortune result.
*/
esp_err_t domain_fortune_result_store_set(const domain_fortune_result_t *result);
bool domain_fortune_result_store_get(domain_fortune_result_t *out_result);
esp_err_t domain_fortune_result_store_clear(void);
/**
* @brief 注册出生资料更新的唯一观察者。 / Register the single birth-profile observer.
*
* 新资料持久化后在调用方上下文通知,例如 LAN 配置页面提交;传 NULL 清除。
* The callback runs in the caller's context after persistence and must be lightweight.
*/
void domain_runtime_register_birth_profile_observer(domain_birth_profile_observer_t cb, void *user_data);
/**
* @brief 桥接到 platform SNTP 的墙上时钟同步。 / Wall-clock synchronization.
*
* set_timezone 仅设置本地时间转换;start 幂等启动 SNTP 并应用时区;网络就绪后调用。
* Bridged to the platform SNTP
* primitive. `set_timezone` applies local-time conversion without
* network access. `start` is idempotent and also applies the process
* time zone (`posix_tz`, e.g. "CST-8"); `is_synced` reports whether the
* clock has been set. Call `start` after the network is up.
*/
esp_err_t domain_runtime_time_set_timezone(const char *posix_tz);
esp_err_t domain_runtime_time_sync_start(const char *sntp_server, const char *posix_tz);
bool domain_runtime_time_is_synced(void);
/* mDNS 主机名按值复制到服务状态,容量包含结尾 NUL。 */
#define DOMAIN_USER_CONFIG_HOSTNAME_MAX_LEN 32
typedef struct {
uint16_t port;
char hostname[DOMAIN_USER_CONFIG_HOSTNAME_MAX_LEN];
} domain_user_config_server_config_t;
typedef struct {
bool running;
uint16_t port;
char hostname[DOMAIN_USER_CONFIG_HOSTNAME_MAX_LEN];
} domain_user_config_server_state_t;
/**
* @brief 启动 domain 拥有的资料与运势 Web 应用。 / Start the domain-owned user web app.
*
* platform 只提供有界 HTTP 传输;路由、JSON、校验和页面属于 domain;重复启动幂等。
* The platform supplies only the bounded raw HTTP transport. This domain
* service owns the routes, JSON semantics, validation, and embedded page.
* Start is idempotent while the service is starting or running.
*/
esp_err_t domain_user_config_server_start(
const domain_user_config_server_config_t *config);
/**
* @brief 有界停止用户配置服务。 / Stop the user configuration server with bounded teardown.
*
* `timeout_ms < 0` is invalid, zero requests asynchronous stop, and positive
* values wait up to the bound before the platform applies deterministic
* fallback.
*/
esp_err_t domain_user_config_server_stop(int timeout_ms);
esp_err_t domain_user_config_server_get_state(
domain_user_config_server_state_t *out_state);
/**
* @brief 通过下层 HTTPS 适配器 POST domain JSON。 / POST domain JSON via HTTPS adapter.
*
* 可选设备 ID 和 bearer token 被映射为请求头;响应写入调用方缓冲并以 NUL 结尾。
* Optional `device_id` is sent as X-Device-Id and optional `bearer_token` is
* sent as Authorization: Bearer <token>. The response body is written to the
* caller-owned buffer and NUL-terminated on success. A completed HTTP exchange
* returns ESP_OK regardless of status code; callers inspect `out_status`.
*
* `timeout_ms < 0` is invalid. `timeout_ms == 0` uses the platform HTTPS
* default bounded timeout. On `ESP_ERR_TIMEOUT`, the lower-layer exchange is
* aborted and cleaned up before this function returns; no retry is performed.
*/
esp_err_t domain_runtime_post_json(const char *url,
const char *payload,
const char *device_id,
const char *bearer_token,
int timeout_ms,
int tx_buffer_size,
int rx_buffer_size,
char *response_body,
size_t response_body_capacity,
size_t *out_response_body_len,
int *out_status);
/* OTA 固定容量是响应校验和任务间快照复制边界;SHA-256 为64位十六进制。 */
#define DOMAIN_OTA_VERSION_MAX_LEN 32
#define DOMAIN_OTA_URL_MAX_LEN 2048
#define DOMAIN_OTA_SHA256_HEX_LEN 64
typedef enum {
DOMAIN_OTA_REPORT_NO_UPDATE = 0,
DOMAIN_OTA_REPORT_UPDATE_AVAILABLE,
DOMAIN_OTA_REPORT_SERVER_ERROR,
} domain_ota_report_outcome_t;
typedef struct {
const char *report_url;
const char *device_id;
const char *serial_number;
const char *current_version;
const char *device_jwt;
const char *internal_auth_secret;
int timeout_ms;
} domain_ota_report_request_t;
typedef struct {
char version[DOMAIN_OTA_VERSION_MAX_LEN];
char url[DOMAIN_OTA_URL_MAX_LEN];
char sha256[DOMAIN_OTA_SHA256_HEX_LEN + 1];
uint64_t size_bytes;
bool forced;
bool confirmed;
} domain_ota_firmware_t;
typedef struct {
domain_ota_report_outcome_t outcome;
int http_status;
int business_code;
domain_ota_firmware_t firmware;
} domain_ota_report_response_t;
typedef struct {
uint32_t free_internal;
uint32_t largest_internal;
uint32_t largest_dma;
} domain_ota_memory_headroom_t;
typedef void (*domain_ota_progress_callback_t)(uint64_t bytes_received,
uint64_t total_bytes,
void *user_data);
typedef bool (*domain_ota_cancel_callback_t)(void *user_data);
esp_err_t domain_runtime_ota_report(const domain_ota_report_request_t *request,
domain_ota_report_response_t *response);
esp_err_t domain_runtime_ota_install(const domain_ota_firmware_t *firmware,
int timeout_ms,
domain_ota_progress_callback_t progress_cb,
domain_ota_cancel_callback_t cancel_cb,
void *user_data);
esp_err_t domain_runtime_ota_confirm_running_image(void);
esp_err_t domain_runtime_ota_get_memory_headroom(domain_ota_memory_headroom_t *out);
void domain_runtime_restart(void);
/**
* @brief 桥接到 platform 的串口行传输原语。 / Console line transport bridged to platform.
*
* `read_bytes` returns bytes read (0 on timeout, <0 on error). A zero timeout
* polls once; negative timeouts are invalid and return <0. Used by the factory
* provisioning CLI.
*/
esp_err_t domain_runtime_console_install(void);
int domain_runtime_console_read_bytes(uint8_t *buf, size_t len, int timeout_ms);
void domain_runtime_console_write_line(const char *line);
void domain_runtime_behavior_set_keep_awake(bool keep_awake);
esp_err_t domain_runtime_behavior_play_prompt(domain_wifi_prompt_id_t prompt);
esp_err_t domain_runtime_behavior_play_prompt_file(const char *path);
void domain_runtime_wifi_portal_set_behavior_hooks(const domain_wifi_behavior_hooks_t *hooks);
esp_err_t domain_runtime_wifi_portal_init_defaults(const char *seed_ssid, const char *seed_password);
bool domain_runtime_wifi_portal_has_saved_credentials(void);
/* SSID 容量包含 NUL;最多保存10个候选,返回顺序即后续连接优先级。 */
#define DOMAIN_WIFI_SSID_MAX_LEN 33
#define DOMAIN_WIFI_SAVED_NETWORK_MAX_COUNT 10
typedef struct {
size_t count;
char ssids[DOMAIN_WIFI_SAVED_NETWORK_MAX_COUNT][DOMAIN_WIFI_SSID_MAX_LEN];
} domain_wifi_saved_networks_t;
/** 按持久化优先级复制 SSID,不暴露密码。 / Copy saved SSIDs in priority order. */
esp_err_t domain_runtime_wifi_portal_get_saved_networks(
domain_wifi_saved_networks_t *out_networks);
bool domain_runtime_wifi_portal_sta_is_connected(void);
esp_err_t domain_runtime_wifi_portal_start_config_ap(const char *ssid_prefix);
bool domain_runtime_wifi_connect_error_keeps_credentials(esp_err_t err);
/**
* @brief 以明确超时使用已存凭据连接 STA。 / Connect STA using stored credentials.
*
* 非正超时使用默认值;STA 已连接时幂等返回 `ESP_OK`;超时或连接错误会明确返回失败。
*
* Semantics:
* - `timeout_ms <= 0`: use default timeout.
* - Returns `ESP_OK` when STA is already connected (idempotent).
* - Returns explicit failure on timeout/connection error.
*/
esp_err_t domain_runtime_wifi_portal_connect_sta(int timeout_ms);
/**
* @brief 替换持久化状态前先验证候选凭据。 / Validate credentials before persistence.
*
* 有界连接试验失败时保留原凭据;只有试验成功才提交新凭据。
*
* The previous credentials remain available when the bounded connection trial
* fails. A successful trial commits only the new credentials.
*/
esp_err_t domain_runtime_wifi_portal_connect_sta_with_credentials(
const char *ssid,
const char *password,
int timeout_ms);
esp_err_t domain_runtime_wifi_portal_reconnect_sta(int timeout_ms);
esp_err_t domain_runtime_wifi_portal_stop_sta(void);
/** 应用待机 Wi-Fi 省电或恢复活动策略。 / Apply standby Wi-Fi power save or restore policy. */
esp_err_t domain_runtime_wifi_portal_set_light_standby(bool standby);
bool domain_runtime_is_network_connect_error(esp_err_t err);
esp_err_t domain_runtime_wifi_portal_save_credentials(const char *ssid, const char *password);
esp_err_t domain_runtime_wifi_portal_clear_saved_credentials(void);
/* 小程序配网容量包含 NUL,并对应 BLE 写入 payload 的校验边界。 */
#define DOMAIN_WIFI_PROVISIONING_SSID_MAX_LEN DOMAIN_WIFI_SSID_MAX_LEN
#define DOMAIN_WIFI_PROVISIONING_PASSWORD_MAX_LEN 65
typedef struct {
char ssid[DOMAIN_WIFI_PROVISIONING_SSID_MAX_LEN];
char password[DOMAIN_WIFI_PROVISIONING_PASSWORD_MAX_LEN];
} domain_wifi_provisioning_credentials_t;
typedef enum {
DOMAIN_WIFI_PROVISIONING_EVENT_CREDENTIALS = 0,
DOMAIN_WIFI_PROVISIONING_EVENT_BAD_JSON,
DOMAIN_WIFI_PROVISIONING_EVENT_EMPTY_SSID,
} domain_wifi_provisioning_event_type_t;
typedef struct {
domain_wifi_provisioning_event_type_t type;
domain_wifi_provisioning_credentials_t credentials;
} domain_wifi_provisioning_event_t;
typedef void (*domain_wifi_provisioning_event_callback_t)(
const domain_wifi_provisioning_event_t *event,
void *user_data);
typedef enum {
DOMAIN_WIFI_PROVISIONING_STATUS_WIFI_CONNECTING = 0,
DOMAIN_WIFI_PROVISIONING_STATUS_WIFI_GOT_IP,
DOMAIN_WIFI_PROVISIONING_STATUS_SAVING_WIFI,
DOMAIN_WIFI_PROVISIONING_STATUS_READY,
DOMAIN_WIFI_PROVISIONING_STATUS_FAIL_BAD_JSON,
DOMAIN_WIFI_PROVISIONING_STATUS_FAIL_EMPTY_SSID,
DOMAIN_WIFI_PROVISIONING_STATUS_FAIL_TIMEOUT,
DOMAIN_WIFI_PROVISIONING_STATUS_FAIL_CONNECT,
} domain_wifi_provisioning_status_t;
/* 配网启动结果由 domain 定义;控制层据此选择等待、退避或内存恢复,不能猜测错误码来源。 */
typedef enum {
DOMAIN_WIFI_PROVISIONING_START_READY = 0,
DOMAIN_WIFI_PROVISIONING_START_IDENTITY_REQUIRED,
DOMAIN_WIFI_PROVISIONING_START_INVALID_CONFIG,
DOMAIN_WIFI_PROVISIONING_START_NO_MEMORY,
DOMAIN_WIFI_PROVISIONING_START_TRANSIENT,
DOMAIN_WIFI_PROVISIONING_START_FAILED,
} domain_wifi_provisioning_start_status_t;
/** 只检查身份前置条件,无 BLE 初始化或资源停止副作用,可重复调用。 */
domain_wifi_provisioning_start_status_t domain_runtime_mini_program_provisioning_preflight(void);
/**
* 启动固定小程序 BLE 配网协议;out_status 必填,失败时返回明确原因。
* 同步完成启动,不在返回失败后后台重试;停止和释放仍使用下方有界 stop 接口。
*/
esp_err_t domain_runtime_mini_program_provisioning_start(
domain_wifi_provisioning_event_callback_t callback,
void *user_data,
domain_wifi_provisioning_start_status_t *out_status);
esp_err_t domain_runtime_mini_program_provisioning_notify(
domain_wifi_provisioning_status_t status);
esp_err_t domain_runtime_mini_program_provisioning_stop(int timeout_ms);
esp_err_t domain_runtime_init_lcd_device(void);
/** 设置0到100%的 LCD PWM 背光。 / Set LCD PWM backlight from 0 to 100 percent. */
esp_err_t domain_runtime_set_lcd_backlight_brightness(uint8_t brightness_percent);
void domain_runtime_start_lcd_render_loop(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 domain_runtime_stop_lcd_render_loop(int timeout_ms);
/** 将电池快照映射为持久 LCD 状态层。 / Map battery snapshot to persistent LCD overlays. */
esp_err_t domain_runtime_update_battery_overlay(const domain_battery_status_t *status);
esp_err_t domain_runtime_request_expression(const domain_expression_request_t *request);
/**
* @brief 预热表情资源而不改变当前显示。 / Preload expression assets without display changes.
*
* 用于即将播放的资源型表情。请求会被复制,LCD 渲染任务在帧间执行预热,调用方等待结果;
* 不支持或非资源型表情返回明确错误。
*
* Intended for asset-backed expressions that are likely to play soon. The
* request is copied, the LCD render task performs the preload between display
* frames, and the caller waits for its result. Unsupported or non-asset
* expressions return an explicit error.
*/
esp_err_t domain_runtime_preheat_expression(const domain_expression_request_t *request);
/**
* @brief 请求表情并阻塞到有限资源序列结束或超时。 /
* Request an expression and block until its finite asset sequence
* completes, or @p timeout_ms elapses.
*
* 用于财富旋转开场等有限演示。入队失败返回底层错误;播放未在预算内结束返回
* `ESP_ERR_TIMEOUT`(调用方可自行继续);LCD 未运行返回 `ESP_ERR_INVALID_STATE`。
* `repeat_count == 0` 表示无限循环,因此本接口最终必然超时。
*
* Intended for finite presentations such as the wealth spin intro. Returns the
* underlying request error if queuing fails, ESP_ERR_TIMEOUT if playback does
* not finish within the timeout (the caller may proceed anyway), or
* ESP_ERR_INVALID_STATE if the LCD is not running. A looping expression
* (repeat_count == 0) never completes and will time out.
*/
esp_err_t domain_runtime_request_expression_blocking(const domain_expression_request_t *request,
uint32_t timeout_ms);
#ifdef __cplusplus
}
#endif