/* * SPDX-FileCopyrightText: 2024-2026 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 /* * 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 有界停止多模态上行采集和流发送。 / Stop uplink capture/stream 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_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); /** * @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 . 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; /** 启动固定 TalkingQ 小程序 BLE 配网协议。 / Start the TalkingQ BLE provisioning protocol. */ esp_err_t domain_runtime_mini_program_provisioning_start( domain_wifi_provisioning_event_callback_t callback, void *user_data); 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