feat(ux): improve wifi, voice, and print feedback flows

This commit is contained in:
admin
2026-03-10 13:05:10 +08:00
parent b59c69fb3f
commit 559df3b570
15 changed files with 508 additions and 33 deletions

View File

@@ -153,6 +153,20 @@ typedef struct {
int64_t finished_ms;
} print_job_info_t;
typedef enum {
PRINTER_JOB_EVENT_QUEUED = 0,
PRINTER_JOB_EVENT_RUNNING,
PRINTER_JOB_EVENT_PROGRESS,
PRINTER_JOB_EVENT_FINISHED,
} printer_job_event_type_t;
typedef struct {
printer_job_event_type_t type;
print_job_info_t job;
} printer_job_event_t;
typedef void (*printer_job_event_cb_t)(const printer_job_event_t *event, void *user_data);
typedef struct {
// Printer-only option: bypass paper/temp/battery precheck for this print job.
bool ignore_precheck;
@@ -200,6 +214,7 @@ bool printer_protocol_get_job(uint32_t job_id, print_job_info_t *out_info);
esp_err_t printer_protocol_list_jobs(print_job_info_t *out_jobs, size_t max_jobs, size_t *out_count);
esp_err_t printer_protocol_cancel_job(uint32_t job_id, char *err, size_t err_len);
size_t printer_protocol_cleanup_jobs(bool include_success, bool include_failed, bool include_canceled);
esp_err_t printer_protocol_set_event_callback(printer_job_event_cb_t cb, void *user_data);
esp_err_t printer_protocol_gap_move(uint32_t timeout_ms, char *err, size_t err_len);
@@ -237,6 +252,7 @@ esp_err_t voice_interaction_stop(char *err, size_t err_len);
esp_err_t voice_interaction_tap_start(char *err, size_t err_len);
esp_err_t voice_interaction_tap_cancel(char *err, size_t err_len);
esp_err_t voice_interaction_schedule_session_prewarm(void);
void voice_interaction_get_status(voice_interaction_status_t *out_status);
const char *voice_interaction_dialog_state_str(voice_dialog_state_t state);

View File

@@ -67,6 +67,7 @@ extern bool s_protocol_stopping;
int printer_protocol_find_job_idx_locked(uint32_t id);
int printer_protocol_alloc_job_slot_locked(void);
bool printer_protocol_is_terminal_state(print_job_state_t state);
void printer_protocol_emit_job_event(printer_job_event_type_t event_type, const job_slot_t *job);
void printer_protocol_worker_task(void *arg);
bool printer_protocol_is_stopping(void);

View File

@@ -35,6 +35,9 @@ bool s_protocol_initialized;
bool s_protocol_stopping;
static const char *TAG = "printer_protocol";
static printer_job_event_cb_t s_job_event_cb;
static void *s_job_event_user_data;
static portMUX_TYPE s_job_event_lock = portMUX_INITIALIZER_UNLOCKED;
#if CONFIG_FREERTOS_UNICORE
#define PRINT_WORKER_CORE_ID 0
@@ -46,6 +49,47 @@ static esp_err_t ensure_printer_driver_ready(void) {
return platform_printer_init();
}
static void printer_protocol_fill_job_info_from_slot(const job_slot_t *slot, print_job_info_t *out_info) {
if (slot == NULL || out_info == NULL) {
return;
}
memset(out_info, 0, sizeof(*out_info));
out_info->id = slot->id;
out_info->state = slot->state;
out_info->progress = slot->progress;
out_info->width = slot->width;
out_info->height = slot->height;
out_info->data_len = slot->data_len;
out_info->created_ms = slot->created_ms;
out_info->started_ms = slot->started_ms;
out_info->finished_ms = slot->finished_ms;
strlcpy(out_info->density, slot->density, sizeof(out_info->density));
strlcpy(out_info->error, slot->error, sizeof(out_info->error));
}
void printer_protocol_emit_job_event(printer_job_event_type_t event_type, const job_slot_t *job) {
if (job == NULL) {
return;
}
printer_job_event_cb_t cb = NULL;
void *user_data = NULL;
portENTER_CRITICAL(&s_job_event_lock);
cb = s_job_event_cb;
user_data = s_job_event_user_data;
portEXIT_CRITICAL(&s_job_event_lock);
if (cb == NULL) {
return;
}
printer_job_event_t event = {
.type = event_type,
};
printer_protocol_fill_job_info_from_slot(job, &event.job);
cb(&event, user_data);
}
static void refresh_printer_status_locked(void) {
platform_printer_sensors_t sensors = {0};
if (platform_printer_get_sensors(&sensors) == ESP_OK) {
@@ -429,3 +473,11 @@ void printer_protocol_get_runtime_status(printer_runtime_status_t *out_status) {
xSemaphoreGive(s_mutex);
}
}
esp_err_t printer_protocol_set_event_callback(printer_job_event_cb_t cb, void *user_data) {
portENTER_CRITICAL(&s_job_event_lock);
s_job_event_cb = cb;
s_job_event_user_data = user_data;
portEXIT_CRITICAL(&s_job_event_lock);
return ESP_OK;
}

View File

@@ -140,6 +140,7 @@ esp_err_t printer_protocol_submit_raster_job_ex(const uint8_t *raster,
s_jobs[idx].data = copy;
strlcpy(s_jobs[idx].density, density != NULL ? density : "中等", sizeof(s_jobs[idx].density));
bool ignore_precheck = s_jobs[idx].ignore_precheck;
job_slot_t queued_snapshot = s_jobs[idx];
xSemaphoreGive(s_mutex);
@@ -177,6 +178,7 @@ esp_err_t printer_protocol_submit_raster_job_ex(const uint8_t *raster,
density != NULL ? density : "中等",
ignore_precheck,
(unsigned)queue_depth);
printer_protocol_emit_job_event(PRINTER_JOB_EVENT_QUEUED, &queued_snapshot);
return ESP_OK;
}
@@ -271,6 +273,7 @@ esp_err_t printer_protocol_list_jobs(print_job_info_t *out_jobs, size_t max_jobs
esp_err_t printer_protocol_cancel_job(uint32_t job_id, char *err, size_t err_len) {
bool canceled_finalized = false;
job_slot_t canceled_snapshot = {0};
if (job_id == 0) {
if (err != NULL && err_len > 0) {
@@ -308,16 +311,18 @@ esp_err_t printer_protocol_cancel_job(uint32_t job_id, char *err, size_t err_len
s_jobs[idx].state = PRINT_JOB_STATE_CANCELED;
s_jobs[idx].finished_ms = esp_timer_get_time() / 1000;
s_jobs[idx].progress = 0;
strlcpy(s_jobs[idx].error, "job canceled", sizeof(s_jobs[idx].error));
strlcpy(s_jobs[idx].error, "Job canceled.", sizeof(s_jobs[idx].error));
free(s_jobs[idx].data);
s_jobs[idx].data = NULL;
canceled_finalized = true;
canceled_snapshot = s_jobs[idx];
} else if (s_jobs[idx].state == PRINT_JOB_STATE_RUNNING) {
strlcpy(s_jobs[idx].error, "cancel requested", sizeof(s_jobs[idx].error));
strlcpy(s_jobs[idx].error, "Cancel requested.", sizeof(s_jobs[idx].error));
}
xSemaphoreGive(s_mutex);
if (canceled_finalized) {
printer_protocol_emit_job_event(PRINTER_JOB_EVENT_FINISHED, &canceled_snapshot);
}
return ESP_OK;
}

View File

@@ -12,6 +12,38 @@
static const char *TAG = "printer_protocol";
static const char *printer_user_error_message(const char *raw_err,
esp_err_t rc,
bool canceled) {
if (canceled) {
return "Job canceled.";
}
if (raw_err != NULL && raw_err[0] != '\0') {
if (strstr(raw_err, "out of paper") != NULL) {
return "Out of paper. Load paper and retry.";
}
if (strstr(raw_err, "battery too low") != NULL) {
return "Battery too low. Charge and retry.";
}
if (strstr(raw_err, "temperature out of range") != NULL) {
return "Printer temperature abnormal. Wait and retry.";
}
if (strstr(raw_err, "sensor read failed") != NULL) {
return "Sensor read failed. Retry.";
}
if (strstr(raw_err, "print timeout") != NULL) {
return "Printing timed out. Retry.";
}
return raw_err;
}
if (rc == ESP_ERR_TIMEOUT) {
return "Printing timed out. Retry.";
}
return "Printing failed. Retry.";
}
static uint16_t density_to_hot_time(const char *density) {
if (density == NULL) {
return 2000;
@@ -66,7 +98,9 @@ static bool precheck_printer_ready(bool ignore_precheck, char *err, size_t err_l
static bool run_print_job_printer_driver(job_slot_t *job) {
char check_err[96];
if (!precheck_printer_ready(job->ignore_precheck, check_err, sizeof(check_err))) {
snprintf(job->error, sizeof(job->error), "%s", check_err);
strlcpy(job->error,
printer_user_error_message(check_err, ESP_FAIL, false),
sizeof(job->error));
return false;
}
@@ -84,30 +118,38 @@ static bool run_print_job_printer_driver(job_slot_t *job) {
.cancel_flag = &job->cancel_requested,
};
bool emit_progress = false;
job_slot_t progress_snapshot = {0};
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(200)) == pdTRUE) {
job->progress = 10;
progress_snapshot = *job;
emit_progress = true;
xSemaphoreGive(s_mutex);
}
if (emit_progress) {
printer_protocol_emit_job_event(PRINTER_JOB_EVENT_PROGRESS, &progress_snapshot);
}
char print_err[96] = {0};
esp_err_t rc = platform_printer_print(&req, print_err, sizeof(print_err));
if (rc != ESP_OK) {
if (print_err[0] != 0) {
snprintf(job->error, sizeof(job->error), "%s", print_err);
} else if (rc == ESP_ERR_TIMEOUT) {
snprintf(job->error, sizeof(job->error), "print timeout");
} else if (rc == ESP_ERR_INVALID_STATE && job->cancel_requested) {
snprintf(job->error, sizeof(job->error), "job canceled");
} else {
snprintf(job->error, sizeof(job->error), "printer print failed");
}
strlcpy(job->error,
printer_user_error_message(print_err, rc, (rc == ESP_ERR_INVALID_STATE && job->cancel_requested)),
sizeof(job->error));
return false;
}
emit_progress = false;
memset(&progress_snapshot, 0, sizeof(progress_snapshot));
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(200)) == pdTRUE) {
job->progress = 95;
progress_snapshot = *job;
emit_progress = true;
xSemaphoreGive(s_mutex);
}
if (emit_progress) {
printer_protocol_emit_job_event(PRINTER_JOB_EVENT_PROGRESS, &progress_snapshot);
}
return true;
}
@@ -122,7 +164,7 @@ static bool run_print_job(job_slot_t *job) {
job->ignore_precheck);
if (printer_protocol_is_stopping()) {
snprintf(job->error, sizeof(job->error), "protocol stopping");
strlcpy(job->error, "Service is stopping. Job canceled.", sizeof(job->error));
return false;
}
@@ -180,6 +222,7 @@ void printer_protocol_worker_task(void *arg) {
job->started_ms = esp_timer_get_time() / 1000;
job->progress = 1;
s_busy_refcnt = 1;
job_slot_t running_snapshot = *job;
int64_t queue_wait_ms = 0;
if (job->started_ms >= job->created_ms) {
queue_wait_ms = job->started_ms - job->created_ms;
@@ -193,6 +236,7 @@ void printer_protocol_worker_task(void *arg) {
(unsigned)job->height,
job->density);
xSemaphoreGive(s_mutex);
printer_protocol_emit_job_event(PRINTER_JOB_EVENT_RUNNING, &running_snapshot);
bool ok = run_print_job(job);
@@ -214,10 +258,13 @@ void printer_protocol_worker_task(void *arg) {
} else if (s_jobs[idx].cancel_requested || printer_protocol_is_stopping()) {
s_jobs[idx].state = PRINT_JOB_STATE_CANCELED;
if (s_jobs[idx].error[0] == 0) {
strlcpy(s_jobs[idx].error, "job canceled", sizeof(s_jobs[idx].error));
strlcpy(s_jobs[idx].error, "Job canceled.", sizeof(s_jobs[idx].error));
}
} else {
s_jobs[idx].state = PRINT_JOB_STATE_FAILED;
strlcpy(s_jobs[idx].error,
printer_user_error_message(s_jobs[idx].error, ESP_FAIL, false),
sizeof(s_jobs[idx].error));
}
int64_t duration_ms = 0;
@@ -239,9 +286,11 @@ void printer_protocol_worker_task(void *arg) {
s_jobs[idx].error[0] != 0 ? s_jobs[idx].error : "-");
}
job_slot_t finished_snapshot = s_jobs[idx];
free(s_jobs[idx].data);
s_jobs[idx].data = NULL;
xSemaphoreGive(s_mutex);
printer_protocol_emit_job_event(PRINTER_JOB_EVENT_FINISHED, &finished_snapshot);
}
if (s_evt != NULL) {

View File

@@ -8,7 +8,8 @@
static const char *TAG = "system_runtime";
#define SYSTEM_RUNTIME_WIFI_HINT_TIMEOUT_MS 220
#define SYSTEM_RUNTIME_WIFI_HINT_TIMEOUT_MS 3000
#define SYSTEM_RUNTIME_WIFI_STATUS_TIMEOUT_MS 3000
static bool s_wifi_hint_shown;
static bool s_power_key_ready;
@@ -59,6 +60,24 @@ static esp_err_t system_runtime_power_key_init(void) {
return ESP_OK;
}
static void system_runtime_show_status_text(const char *text, uint32_t timeout_ms) {
if (text == NULL || text[0] == '\0') {
return;
}
char preview_err[96] = {0};
esp_err_t preview_rc = screen_preview_show_text(text,
timeout_ms,
preview_err,
sizeof(preview_err));
if (preview_rc != ESP_OK && preview_rc != ESP_ERR_NOT_SUPPORTED) {
ESP_LOGW(TAG,
"status_preview_failed: rc=0x%x msg=%s",
(unsigned)preview_rc,
preview_err[0] != '\0' ? preview_err : "show text failed");
}
}
static void system_runtime_show_wifi_hint(void) {
if (s_wifi_hint_shown) {
return;
@@ -76,17 +95,7 @@ static void system_runtime_show_wifi_hint(void) {
"未检测到Wi-Fi配置\n连接热点: %s\n打开: http://192.168.4.1",
ap_ssid);
char preview_err[96] = {0};
esp_err_t preview_rc = screen_preview_show_text(hint,
SYSTEM_RUNTIME_WIFI_HINT_TIMEOUT_MS,
preview_err,
sizeof(preview_err));
if (preview_rc != ESP_OK && preview_rc != ESP_ERR_NOT_SUPPORTED) {
ESP_LOGW(TAG,
"wifi_hint_preview_failed: rc=0x%x msg=%s",
(unsigned)preview_rc,
preview_err[0] != '\0' ? preview_err : "show text failed");
}
system_runtime_show_status_text(hint, SYSTEM_RUNTIME_WIFI_HINT_TIMEOUT_MS);
}
static void system_runtime_try_show_wifi_hint_for_unprovisioned(void) {
@@ -97,8 +106,32 @@ static void system_runtime_try_show_wifi_hint_for_unprovisioned(void) {
static void system_runtime_wifi_event_handler(wifi_manager_event_t event, void *user_data) {
(void)user_data;
if (event == WIFI_MANAGER_EVENT_PROVISIONING_STARTED) {
system_runtime_show_wifi_hint();
switch (event) {
case WIFI_MANAGER_EVENT_PROVISIONING_STARTED:
system_runtime_show_wifi_hint();
break;
case WIFI_MANAGER_EVENT_STA_CONNECTING:
system_runtime_show_status_text("正在连接Wi-Fi...\n请稍候", SYSTEM_RUNTIME_WIFI_STATUS_TIMEOUT_MS);
break;
case WIFI_MANAGER_EVENT_STA_CONNECTED: {
char ip[20] = {0};
wifi_manager_get_ip(ip, sizeof(ip));
char text[96] = {0};
snprintf(text,
sizeof(text),
"Wi-Fi已连接\nIP: %s",
ip[0] != '\0' ? ip : "0.0.0.0");
system_runtime_show_status_text(text, SYSTEM_RUNTIME_WIFI_STATUS_TIMEOUT_MS);
break;
}
case WIFI_MANAGER_EVENT_STA_CONNECT_FAILED:
system_runtime_show_status_text("Wi-Fi连接失败\n请检查密码/信号后重试", SYSTEM_RUNTIME_WIFI_STATUS_TIMEOUT_MS);
break;
case WIFI_MANAGER_EVENT_STA_DISCONNECTED:
system_runtime_show_status_text("Wi-Fi已断开\n正在重连...", SYSTEM_RUNTIME_WIFI_STATUS_TIMEOUT_MS);
break;
default:
break;
}
}

View File

@@ -8,6 +8,15 @@
#include "esp_log.h"
static const char *TAG = "voice_interaction";
static TaskHandle_t s_voice_prewarm_task;
static bool s_voice_prewarm_task_with_caps;
static volatile bool s_voice_prewarm_stop_requested;
#define VOICE_PREWARM_TASK_STACK 6144
#define VOICE_PREWARM_CONNECT_TIMEOUT_MS 3000
#define VOICE_PREWARM_WIFI_POLL_MS 300
#define VOICE_PREWARM_RETRY_DELAY_MS 1500
#define VOICE_PREWARM_MAX_ATTEMPTS 3
static uint32_t clamp_timeout_ms(uint32_t timeout_ms, uint32_t default_ms, uint32_t min_ms, uint32_t max_ms) {
uint32_t value = timeout_ms == 0 ? default_ms : timeout_ms;
@@ -74,6 +83,87 @@ static bool voice_wait_worker_tasks_exit(uint32_t timeout_ms) {
return false;
}
static void voice_session_prewarm_task(void *arg) {
(void)arg;
uint32_t attempts = 0;
while (!s_voice_prewarm_stop_requested) {
voice_interaction_status_t status = {0};
voice_interaction_get_status(&status);
if (status.session_active && status.ws_connected && status.started) {
break;
}
if (!system_runtime_wifi_ready()) {
vTaskDelay(pdMS_TO_TICKS(VOICE_PREWARM_WIFI_POLL_MS));
continue;
}
char prewarm_err[128] = {0};
esp_err_t prewarm_rc = voice_interaction_start_with_timeout(VOICE_PREWARM_CONNECT_TIMEOUT_MS,
prewarm_err,
sizeof(prewarm_err));
if (prewarm_rc == ESP_OK) {
ESP_LOGI(TAG, "voice session prewarm ready");
break;
}
if (prewarm_rc == ESP_ERR_INVALID_STATE &&
strstr(prewarm_err, "config missing") != NULL) {
ESP_LOGW(TAG, "voice session prewarm skipped: config missing");
break;
}
++attempts;
if (attempts >= VOICE_PREWARM_MAX_ATTEMPTS) {
ESP_LOGW(TAG,
"voice session prewarm failed after retries: rc=0x%x err=%s",
(unsigned)prewarm_rc,
prewarm_err[0] != '\0' ? prewarm_err : "-");
break;
}
vTaskDelay(pdMS_TO_TICKS(VOICE_PREWARM_RETRY_DELAY_MS));
}
bool delete_with_caps = false;
if (voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) {
delete_with_caps = s_voice_prewarm_task_with_caps;
s_voice_prewarm_task = NULL;
s_voice_prewarm_task_with_caps = false;
voice_unlock();
}
voice_delete_self_task(delete_with_caps);
}
static void voice_interaction_stop_prewarm_task(void) {
s_voice_prewarm_stop_requested = true;
TaskHandle_t prewarm_task = NULL;
if (voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) {
prewarm_task = s_voice_prewarm_task;
voice_unlock();
}
if (prewarm_task == NULL) {
return;
}
(void)xTaskAbortDelay(prewarm_task);
int64_t deadline_ms = voice_now_ms() + 500;
while (voice_now_ms() < deadline_ms) {
bool done = true;
if (voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) {
done = (s_voice_prewarm_task == NULL);
voice_unlock();
}
if (done) {
return;
}
vTaskDelay(pdMS_TO_TICKS(20));
}
ESP_LOGW(TAG, "voice prewarm task still running during stop, continue shutdown");
}
const char *voice_interaction_dialog_state_str(voice_dialog_state_t state) {
switch (state) {
case VOICE_DIALOG_STATE_IDLE:
@@ -103,6 +193,9 @@ esp_err_t voice_interaction_init(void) {
s_voice.dialog_state = VOICE_DIALOG_STATE_IDLE;
s_voice.last_event_ms = voice_now_ms();
s_voice_prewarm_task = NULL;
s_voice_prewarm_task_with_caps = false;
s_voice_prewarm_stop_requested = false;
esp_err_t mic_key_err = voice_interaction_mic_key_init();
if (mic_key_err != ESP_OK) {
@@ -115,6 +208,43 @@ esp_err_t voice_interaction_init(void) {
return ESP_OK;
}
esp_err_t voice_interaction_schedule_session_prewarm(void) {
if (!s_voice.ready) {
return ESP_ERR_INVALID_STATE;
}
if (!voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) {
return ESP_ERR_TIMEOUT;
}
bool already_running = (s_voice_prewarm_task != NULL);
voice_unlock();
if (already_running) {
return ESP_OK;
}
TaskHandle_t task = NULL;
bool with_caps = false;
s_voice_prewarm_stop_requested = false;
BaseType_t ok = voice_create_task_prefer_psram(voice_session_prewarm_task,
"voice_prewarm",
VOICE_PREWARM_TASK_STACK,
3,
&task,
&with_caps);
if (ok != pdPASS || task == NULL) {
return ESP_ERR_NO_MEM;
}
if (!voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) {
voice_delete_task(task, with_caps);
return ESP_ERR_TIMEOUT;
}
s_voice_prewarm_task = task;
s_voice_prewarm_task_with_caps = with_caps;
voice_unlock();
return ESP_OK;
}
esp_err_t voice_interaction_start_with_timeout(uint32_t timeout_ms, char *err, size_t err_len) {
uint32_t connect_timeout_ms =
clamp_timeout_ms(timeout_ms, VOICE_START_CONNECT_TIMEOUT_MS, 1000, 30000);
@@ -366,6 +496,8 @@ esp_err_t voice_interaction_stop_with_timeout(uint32_t timeout_ms, char *err, si
return ESP_ERR_INVALID_STATE;
}
voice_interaction_stop_prewarm_task();
if (!voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) {
voice_fill_err(err, err_len, "voice lock timeout");
ESP_LOGW(TAG, "session_stop_abort: lock timeout");

View File

@@ -17,7 +17,7 @@ static const char *TAG = "voice_interaction";
#define VOICE_IMAGE_MARKER_REPEAT_SUPPRESS_MS 45000
#define VOICE_IMAGE_PRINT_THRESHOLD 160
#define VOICE_IMAGE_PRINT_MAX_HEIGHT 2200
#define VOICE_IMAGE_PREVIEW_TIMEOUT_MS 200
#define VOICE_IMAGE_PREVIEW_TIMEOUT_MS 3000
#define VOICE_IMAGE_MARKER_TEMPLATE \
"%s线稿风格白色背景边缘清晰细节简洁高对比避免大面积阴影和灰度渐变适配热敏打印机打印。"
#define VOICE_IMAGE_MARKER_TEMPLATE_FALLBACK_SUBJECT "简洁卡通图案"

View File

@@ -9,6 +9,7 @@
#define VOICE_MIC_KEY_QUEUE_LEN 8
#define VOICE_MIC_KEY_SESSION_READY_WAIT_MS 2000
#define VOICE_MIC_KEY_SESSION_READY_POLL_MS 30
#define VOICE_MIC_KEY_STATUS_PREVIEW_TIMEOUT_MS 3000
typedef enum {
VOICE_MIC_KEY_CMD_PRESS_DOWN = 0,
@@ -22,6 +23,26 @@ static TaskHandle_t s_mic_key_task;
static bool s_mic_key_task_with_caps;
static bool s_mic_key_ready;
static void voice_mic_key_show_status(const char *text) {
if (text == NULL || text[0] == '\0') {
return;
}
char preview_err[96] = {0};
esp_err_t preview_rc = screen_preview_show_text(text,
VOICE_MIC_KEY_STATUS_PREVIEW_TIMEOUT_MS,
preview_err,
sizeof(preview_err));
if (preview_rc == ESP_OK || preview_rc == ESP_ERR_NOT_SUPPORTED) {
return;
}
ESP_LOGW(TAG,
"mic_key_status_preview_failed: rc=0x%x msg=%s",
(unsigned)preview_rc,
preview_err[0] != '\0' ? preview_err : "show text failed");
}
static bool voice_mic_key_round_busy(const voice_interaction_status_t *voice,
const printer_runtime_status_t *printer) {
if (voice == NULL || printer == NULL) {
@@ -56,6 +77,7 @@ static void voice_mic_key_handle_press_down(void) {
if (voice.tap_active) {
ESP_LOGI(TAG, "MIC_KEY press ignored: tap already active");
voice_mic_key_show_status("语音已在录制中");
return;
}
@@ -68,10 +90,12 @@ static void voice_mic_key_handle_press_down(void) {
voice.marker_image_gen_running,
printer.busy,
printer.queue_depth);
voice_mic_key_show_status("设备忙碌中\n请等待当前任务完成");
return;
}
if (!voice_mic_key_session_ready(&voice)) {
voice_mic_key_show_status("语音连接中...\n请稍候");
char start_err[128] = {0};
esp_err_t start_rc = voice_interaction_start_with_timeout(0, start_err, sizeof(start_err));
if (start_rc != ESP_OK) {
@@ -79,6 +103,7 @@ static void voice_mic_key_handle_press_down(void) {
"MIC_KEY auto session start failed: %s (%s)",
esp_err_to_name(start_rc),
start_err[0] != '\0' ? start_err : "-");
voice_mic_key_show_status("语音连接失败\n请稍后重试");
return;
}
}
@@ -90,6 +115,7 @@ static void voice_mic_key_handle_press_down(void) {
voice.session_active,
voice.ws_connected,
voice.started);
voice_mic_key_show_status("语音服务暂不可用\n请稍后重试");
return;
}
@@ -100,9 +126,11 @@ static void voice_mic_key_handle_press_down(void) {
"MIC_KEY tap start failed: %s (%s)",
esp_err_to_name(tap_rc),
tap_err[0] != '\0' ? tap_err : "-");
voice_mic_key_show_status("开始录音失败\n请重试");
return;
}
voice_mic_key_show_status("请开始说话");
ESP_LOGI(TAG, "MIC_KEY tap started");
}
@@ -120,9 +148,11 @@ static void voice_mic_key_handle_press_up(void) {
"MIC_KEY tap stop failed: %s (%s)",
esp_err_to_name(tap_rc),
tap_err[0] != '\0' ? tap_err : "-");
voice_mic_key_show_status("结束录音失败\n请重试");
return;
}
voice_mic_key_show_status("已结束录音\n正在处理...");
ESP_LOGI(TAG, "MIC_KEY tap stopped");
}

View File

@@ -16,7 +16,7 @@ static const char *TAG = "voice_interaction";
#define VOICE_RESPONSE_FINALIZE_TASK_STACK 6144
#define VOICE_RESPONSE_FINALIZE_TASK_RETRY_COUNT 3
#define VOICE_RESPONSE_FINALIZE_TASK_RETRY_DELAY_MS 120
#define VOICE_SCREEN_PREVIEW_TIMEOUT_MS 180
#define VOICE_SCREEN_PREVIEW_TIMEOUT_MS 3000
static portMUX_TYPE s_response_finalize_lock = portMUX_INITIALIZER_UNLOCKED;
static bool s_response_finalize_running;