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

@@ -278,6 +278,12 @@ esp_err_t control_plane_lifecycle_start(void) {
voice_started = true; voice_started = true;
image_generation_schedule_prewarm(); image_generation_schedule_prewarm();
esp_err_t voice_prewarm_rc = voice_interaction_schedule_session_prewarm();
if (voice_prewarm_rc != ESP_OK && voice_prewarm_rc != ESP_ERR_INVALID_STATE) {
ESP_LOGW(TAG,
"voice session prewarm schedule failed: rc=0x%x",
(unsigned)voice_prewarm_rc);
}
if (lifecycle_lock_take(1000)) { if (lifecycle_lock_take(1000)) {
lifecycle_set_state_locked(CONTROL_PLANE_LIFECYCLE_STATE_RUNNING, ESP_OK, "running"); lifecycle_set_state_locked(CONTROL_PLANE_LIFECYCLE_STATE_RUNNING, ESP_OK, "running");

View File

@@ -153,6 +153,20 @@ typedef struct {
int64_t finished_ms; int64_t finished_ms;
} print_job_info_t; } 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 { typedef struct {
// Printer-only option: bypass paper/temp/battery precheck for this print job. // Printer-only option: bypass paper/temp/battery precheck for this print job.
bool ignore_precheck; 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_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); 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); 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); 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_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_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); void voice_interaction_get_status(voice_interaction_status_t *out_status);
const char *voice_interaction_dialog_state_str(voice_dialog_state_t state); 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_find_job_idx_locked(uint32_t id);
int printer_protocol_alloc_job_slot_locked(void); int printer_protocol_alloc_job_slot_locked(void);
bool printer_protocol_is_terminal_state(print_job_state_t state); 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); void printer_protocol_worker_task(void *arg);
bool printer_protocol_is_stopping(void); bool printer_protocol_is_stopping(void);

View File

@@ -35,6 +35,9 @@ bool s_protocol_initialized;
bool s_protocol_stopping; bool s_protocol_stopping;
static const char *TAG = "printer_protocol"; 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 #if CONFIG_FREERTOS_UNICORE
#define PRINT_WORKER_CORE_ID 0 #define PRINT_WORKER_CORE_ID 0
@@ -46,6 +49,47 @@ static esp_err_t ensure_printer_driver_ready(void) {
return platform_printer_init(); 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) { static void refresh_printer_status_locked(void) {
platform_printer_sensors_t sensors = {0}; platform_printer_sensors_t sensors = {0};
if (platform_printer_get_sensors(&sensors) == ESP_OK) { 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); 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; s_jobs[idx].data = copy;
strlcpy(s_jobs[idx].density, density != NULL ? density : "中等", sizeof(s_jobs[idx].density)); strlcpy(s_jobs[idx].density, density != NULL ? density : "中等", sizeof(s_jobs[idx].density));
bool ignore_precheck = s_jobs[idx].ignore_precheck; bool ignore_precheck = s_jobs[idx].ignore_precheck;
job_slot_t queued_snapshot = s_jobs[idx];
xSemaphoreGive(s_mutex); xSemaphoreGive(s_mutex);
@@ -177,6 +178,7 @@ esp_err_t printer_protocol_submit_raster_job_ex(const uint8_t *raster,
density != NULL ? density : "中等", density != NULL ? density : "中等",
ignore_precheck, ignore_precheck,
(unsigned)queue_depth); (unsigned)queue_depth);
printer_protocol_emit_job_event(PRINTER_JOB_EVENT_QUEUED, &queued_snapshot);
return ESP_OK; 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) { esp_err_t printer_protocol_cancel_job(uint32_t job_id, char *err, size_t err_len) {
bool canceled_finalized = false; bool canceled_finalized = false;
job_slot_t canceled_snapshot = {0};
if (job_id == 0) { if (job_id == 0) {
if (err != NULL && err_len > 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].state = PRINT_JOB_STATE_CANCELED;
s_jobs[idx].finished_ms = esp_timer_get_time() / 1000; s_jobs[idx].finished_ms = esp_timer_get_time() / 1000;
s_jobs[idx].progress = 0; 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); free(s_jobs[idx].data);
s_jobs[idx].data = NULL; s_jobs[idx].data = NULL;
canceled_finalized = true; canceled_finalized = true;
canceled_snapshot = s_jobs[idx];
} else if (s_jobs[idx].state == PRINT_JOB_STATE_RUNNING) { } 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); xSemaphoreGive(s_mutex);
if (canceled_finalized) { if (canceled_finalized) {
printer_protocol_emit_job_event(PRINTER_JOB_EVENT_FINISHED, &canceled_snapshot);
} }
return ESP_OK; return ESP_OK;
} }

View File

@@ -12,6 +12,38 @@
static const char *TAG = "printer_protocol"; 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) { static uint16_t density_to_hot_time(const char *density) {
if (density == NULL) { if (density == NULL) {
return 2000; 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) { static bool run_print_job_printer_driver(job_slot_t *job) {
char check_err[96]; char check_err[96];
if (!precheck_printer_ready(job->ignore_precheck, check_err, sizeof(check_err))) { 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; return false;
} }
@@ -84,30 +118,38 @@ static bool run_print_job_printer_driver(job_slot_t *job) {
.cancel_flag = &job->cancel_requested, .cancel_flag = &job->cancel_requested,
}; };
bool emit_progress = false;
job_slot_t progress_snapshot = {0};
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(200)) == pdTRUE) { if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(200)) == pdTRUE) {
job->progress = 10; job->progress = 10;
progress_snapshot = *job;
emit_progress = true;
xSemaphoreGive(s_mutex); xSemaphoreGive(s_mutex);
} }
if (emit_progress) {
printer_protocol_emit_job_event(PRINTER_JOB_EVENT_PROGRESS, &progress_snapshot);
}
char print_err[96] = {0}; char print_err[96] = {0};
esp_err_t rc = platform_printer_print(&req, print_err, sizeof(print_err)); esp_err_t rc = platform_printer_print(&req, print_err, sizeof(print_err));
if (rc != ESP_OK) { if (rc != ESP_OK) {
if (print_err[0] != 0) { strlcpy(job->error,
snprintf(job->error, sizeof(job->error), "%s", print_err); printer_user_error_message(print_err, rc, (rc == ESP_ERR_INVALID_STATE && job->cancel_requested)),
} else if (rc == ESP_ERR_TIMEOUT) { sizeof(job->error));
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");
}
return false; return false;
} }
emit_progress = false;
memset(&progress_snapshot, 0, sizeof(progress_snapshot));
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(200)) == pdTRUE) { if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(200)) == pdTRUE) {
job->progress = 95; job->progress = 95;
progress_snapshot = *job;
emit_progress = true;
xSemaphoreGive(s_mutex); xSemaphoreGive(s_mutex);
} }
if (emit_progress) {
printer_protocol_emit_job_event(PRINTER_JOB_EVENT_PROGRESS, &progress_snapshot);
}
return true; return true;
} }
@@ -122,7 +164,7 @@ static bool run_print_job(job_slot_t *job) {
job->ignore_precheck); job->ignore_precheck);
if (printer_protocol_is_stopping()) { 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; return false;
} }
@@ -180,6 +222,7 @@ void printer_protocol_worker_task(void *arg) {
job->started_ms = esp_timer_get_time() / 1000; job->started_ms = esp_timer_get_time() / 1000;
job->progress = 1; job->progress = 1;
s_busy_refcnt = 1; s_busy_refcnt = 1;
job_slot_t running_snapshot = *job;
int64_t queue_wait_ms = 0; int64_t queue_wait_ms = 0;
if (job->started_ms >= job->created_ms) { if (job->started_ms >= job->created_ms) {
queue_wait_ms = 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, (unsigned)job->height,
job->density); job->density);
xSemaphoreGive(s_mutex); xSemaphoreGive(s_mutex);
printer_protocol_emit_job_event(PRINTER_JOB_EVENT_RUNNING, &running_snapshot);
bool ok = run_print_job(job); 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()) { } else if (s_jobs[idx].cancel_requested || printer_protocol_is_stopping()) {
s_jobs[idx].state = PRINT_JOB_STATE_CANCELED; s_jobs[idx].state = PRINT_JOB_STATE_CANCELED;
if (s_jobs[idx].error[0] == 0) { 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 { } else {
s_jobs[idx].state = PRINT_JOB_STATE_FAILED; 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; 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 : "-"); s_jobs[idx].error[0] != 0 ? s_jobs[idx].error : "-");
} }
job_slot_t finished_snapshot = s_jobs[idx];
free(s_jobs[idx].data); free(s_jobs[idx].data);
s_jobs[idx].data = NULL; s_jobs[idx].data = NULL;
xSemaphoreGive(s_mutex); xSemaphoreGive(s_mutex);
printer_protocol_emit_job_event(PRINTER_JOB_EVENT_FINISHED, &finished_snapshot);
} }
if (s_evt != NULL) { if (s_evt != NULL) {

View File

@@ -8,7 +8,8 @@
static const char *TAG = "system_runtime"; 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_wifi_hint_shown;
static bool s_power_key_ready; static bool s_power_key_ready;
@@ -59,6 +60,24 @@ static esp_err_t system_runtime_power_key_init(void) {
return ESP_OK; 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) { static void system_runtime_show_wifi_hint(void) {
if (s_wifi_hint_shown) { if (s_wifi_hint_shown) {
return; return;
@@ -76,17 +95,7 @@ static void system_runtime_show_wifi_hint(void) {
"未检测到Wi-Fi配置\n连接热点: %s\n打开: http://192.168.4.1", "未检测到Wi-Fi配置\n连接热点: %s\n打开: http://192.168.4.1",
ap_ssid); ap_ssid);
char preview_err[96] = {0}; system_runtime_show_status_text(hint, SYSTEM_RUNTIME_WIFI_HINT_TIMEOUT_MS);
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");
}
} }
static void system_runtime_try_show_wifi_hint_for_unprovisioned(void) { 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) { static void system_runtime_wifi_event_handler(wifi_manager_event_t event, void *user_data) {
(void)user_data; (void)user_data;
if (event == WIFI_MANAGER_EVENT_PROVISIONING_STARTED) { switch (event) {
case WIFI_MANAGER_EVENT_PROVISIONING_STARTED:
system_runtime_show_wifi_hint(); 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" #include "esp_log.h"
static const char *TAG = "voice_interaction"; 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) { 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; 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; 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) { const char *voice_interaction_dialog_state_str(voice_dialog_state_t state) {
switch (state) { switch (state) {
case VOICE_DIALOG_STATE_IDLE: 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.dialog_state = VOICE_DIALOG_STATE_IDLE;
s_voice.last_event_ms = voice_now_ms(); 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(); esp_err_t mic_key_err = voice_interaction_mic_key_init();
if (mic_key_err != ESP_OK) { if (mic_key_err != ESP_OK) {
@@ -115,6 +208,43 @@ esp_err_t voice_interaction_init(void) {
return ESP_OK; 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) { esp_err_t voice_interaction_start_with_timeout(uint32_t timeout_ms, char *err, size_t err_len) {
uint32_t connect_timeout_ms = uint32_t connect_timeout_ms =
clamp_timeout_ms(timeout_ms, VOICE_START_CONNECT_TIMEOUT_MS, 1000, 30000); 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; return ESP_ERR_INVALID_STATE;
} }
voice_interaction_stop_prewarm_task();
if (!voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) { if (!voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) {
voice_fill_err(err, err_len, "voice lock timeout"); voice_fill_err(err, err_len, "voice lock timeout");
ESP_LOGW(TAG, "session_stop_abort: 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_MARKER_REPEAT_SUPPRESS_MS 45000
#define VOICE_IMAGE_PRINT_THRESHOLD 160 #define VOICE_IMAGE_PRINT_THRESHOLD 160
#define VOICE_IMAGE_PRINT_MAX_HEIGHT 2200 #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 \ #define VOICE_IMAGE_MARKER_TEMPLATE \
"%s线稿风格白色背景边缘清晰细节简洁高对比避免大面积阴影和灰度渐变适配热敏打印机打印。" "%s线稿风格白色背景边缘清晰细节简洁高对比避免大面积阴影和灰度渐变适配热敏打印机打印。"
#define VOICE_IMAGE_MARKER_TEMPLATE_FALLBACK_SUBJECT "简洁卡通图案" #define VOICE_IMAGE_MARKER_TEMPLATE_FALLBACK_SUBJECT "简洁卡通图案"

View File

@@ -9,6 +9,7 @@
#define VOICE_MIC_KEY_QUEUE_LEN 8 #define VOICE_MIC_KEY_QUEUE_LEN 8
#define VOICE_MIC_KEY_SESSION_READY_WAIT_MS 2000 #define VOICE_MIC_KEY_SESSION_READY_WAIT_MS 2000
#define VOICE_MIC_KEY_SESSION_READY_POLL_MS 30 #define VOICE_MIC_KEY_SESSION_READY_POLL_MS 30
#define VOICE_MIC_KEY_STATUS_PREVIEW_TIMEOUT_MS 3000
typedef enum { typedef enum {
VOICE_MIC_KEY_CMD_PRESS_DOWN = 0, 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_task_with_caps;
static bool s_mic_key_ready; 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, static bool voice_mic_key_round_busy(const voice_interaction_status_t *voice,
const printer_runtime_status_t *printer) { const printer_runtime_status_t *printer) {
if (voice == NULL || printer == NULL) { if (voice == NULL || printer == NULL) {
@@ -56,6 +77,7 @@ static void voice_mic_key_handle_press_down(void) {
if (voice.tap_active) { if (voice.tap_active) {
ESP_LOGI(TAG, "MIC_KEY press ignored: tap already active"); ESP_LOGI(TAG, "MIC_KEY press ignored: tap already active");
voice_mic_key_show_status("语音已在录制中");
return; return;
} }
@@ -68,10 +90,12 @@ static void voice_mic_key_handle_press_down(void) {
voice.marker_image_gen_running, voice.marker_image_gen_running,
printer.busy, printer.busy,
printer.queue_depth); printer.queue_depth);
voice_mic_key_show_status("设备忙碌中\n请等待当前任务完成");
return; return;
} }
if (!voice_mic_key_session_ready(&voice)) { if (!voice_mic_key_session_ready(&voice)) {
voice_mic_key_show_status("语音连接中...\n请稍候");
char start_err[128] = {0}; char start_err[128] = {0};
esp_err_t start_rc = voice_interaction_start_with_timeout(0, start_err, sizeof(start_err)); esp_err_t start_rc = voice_interaction_start_with_timeout(0, start_err, sizeof(start_err));
if (start_rc != ESP_OK) { 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)", "MIC_KEY auto session start failed: %s (%s)",
esp_err_to_name(start_rc), esp_err_to_name(start_rc),
start_err[0] != '\0' ? start_err : "-"); start_err[0] != '\0' ? start_err : "-");
voice_mic_key_show_status("语音连接失败\n请稍后重试");
return; return;
} }
} }
@@ -90,6 +115,7 @@ static void voice_mic_key_handle_press_down(void) {
voice.session_active, voice.session_active,
voice.ws_connected, voice.ws_connected,
voice.started); voice.started);
voice_mic_key_show_status("语音服务暂不可用\n请稍后重试");
return; return;
} }
@@ -100,9 +126,11 @@ static void voice_mic_key_handle_press_down(void) {
"MIC_KEY tap start failed: %s (%s)", "MIC_KEY tap start failed: %s (%s)",
esp_err_to_name(tap_rc), esp_err_to_name(tap_rc),
tap_err[0] != '\0' ? tap_err : "-"); tap_err[0] != '\0' ? tap_err : "-");
voice_mic_key_show_status("开始录音失败\n请重试");
return; return;
} }
voice_mic_key_show_status("请开始说话");
ESP_LOGI(TAG, "MIC_KEY tap started"); 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)", "MIC_KEY tap stop failed: %s (%s)",
esp_err_to_name(tap_rc), esp_err_to_name(tap_rc),
tap_err[0] != '\0' ? tap_err : "-"); tap_err[0] != '\0' ? tap_err : "-");
voice_mic_key_show_status("结束录音失败\n请重试");
return; return;
} }
voice_mic_key_show_status("已结束录音\n正在处理...");
ESP_LOGI(TAG, "MIC_KEY tap stopped"); 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_STACK 6144
#define VOICE_RESPONSE_FINALIZE_TASK_RETRY_COUNT 3 #define VOICE_RESPONSE_FINALIZE_TASK_RETRY_COUNT 3
#define VOICE_RESPONSE_FINALIZE_TASK_RETRY_DELAY_MS 120 #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 portMUX_TYPE s_response_finalize_lock = portMUX_INITIALIZER_UNLOCKED;
static bool s_response_finalize_running; static bool s_response_finalize_running;

View File

@@ -337,6 +337,21 @@
status_connect_ok: '连接成功,正在关闭配网页面...', status_connect_ok: '连接成功,正在关闭配网页面...',
status_connect_failed: '连接失败:{error}。请重试。', status_connect_failed: '连接失败:{error}。请重试。',
status_request_failed: '请求失败,请重试。', status_request_failed: '请求失败,请重试。',
error_invalid_payload: '提交内容无效',
error_read_failed: '设备读取请求失败',
error_ssid_required: 'SSID 不能为空',
error_password_too_short: '密码至少需要 8 位',
error_wrong_password: '密码错误',
error_no_ap_found: '未找到该 Wi-Fi',
error_signal_too_weak: '信号太弱,请靠近路由器',
error_ap_not_responding: '路由器无响应',
error_association_failed: '连接被路由器拒绝',
error_ap_security_unsupported: '路由器加密方式暂不支持',
error_connect_timeout: '连接超时',
error_wifi_unavailable: '设备 Wi-Fi 暂不可用',
error_save_failed: '连接成功但保存失败,请重试',
error_connect_failed: '无法连接到该 Wi-Fi',
error_unknown: '未知错误',
signal_unit: 'dBm' signal_unit: 'dBm'
}, },
'en-US': { 'en-US': {
@@ -359,6 +374,21 @@
status_connect_ok: 'Connected successfully. Closing this provisioning page...', status_connect_ok: 'Connected successfully. Closing this provisioning page...',
status_connect_failed: 'Connection failed: {error}. Please try again.', status_connect_failed: 'Connection failed: {error}. Please try again.',
status_request_failed: 'Request failed. Please try again.', status_request_failed: 'Request failed. Please try again.',
error_invalid_payload: 'Invalid request payload',
error_read_failed: 'Device failed to read request',
error_ssid_required: 'SSID is required',
error_password_too_short: 'Password must be at least 8 characters',
error_wrong_password: 'Wrong password',
error_no_ap_found: 'Wi-Fi network not found',
error_signal_too_weak: 'Signal is too weak',
error_ap_not_responding: 'Router is not responding',
error_association_failed: 'Router rejected the connection',
error_ap_security_unsupported: 'Wi-Fi security mode is unsupported',
error_connect_timeout: 'Connection timed out',
error_wifi_unavailable: 'Device Wi-Fi is unavailable',
error_save_failed: 'Connected, but failed to save credentials',
error_connect_failed: 'Unable to connect to this Wi-Fi',
error_unknown: 'Unknown error',
signal_unit: 'dBm' signal_unit: 'dBm'
} }
}; };
@@ -402,6 +432,18 @@
setStatusText(trWithParams(key, params), type); setStatusText(trWithParams(key, params), type);
} }
function mapProvisionError(code) {
if (!code) {
return tr('error_unknown');
}
const key = 'error_' + String(code);
const mapped = tr(key);
if (mapped === key) {
return tr('error_connect_failed');
}
return mapped;
}
function decodeSsid(value) { function decodeSsid(value) {
try { try {
return decodeURIComponent(value); return decodeURIComponent(value);
@@ -562,7 +604,9 @@
return; return;
} }
setStatusKey('status_connect_failed', 'err', { error: data.error || 'unknown' }); setStatusKey('status_connect_failed', 'err', {
error: mapProvisionError(data.error)
});
} catch (_err) { } catch (_err) {
setStatusKey('status_request_failed', 'err'); setStatusKey('status_request_failed', 'err');
} }

View File

@@ -16,6 +16,10 @@ esp_err_t platform_bootstrap_init(void);
// ---------- wifi_manager ---------- // ---------- wifi_manager ----------
typedef enum { typedef enum {
WIFI_MANAGER_EVENT_PROVISIONING_STARTED = 0, WIFI_MANAGER_EVENT_PROVISIONING_STARTED = 0,
WIFI_MANAGER_EVENT_STA_CONNECTING,
WIFI_MANAGER_EVENT_STA_CONNECTED,
WIFI_MANAGER_EVENT_STA_CONNECT_FAILED,
WIFI_MANAGER_EVENT_STA_DISCONNECTED,
} wifi_manager_event_t; } wifi_manager_event_t;
typedef void (*wifi_manager_event_cb_t)(wifi_manager_event_t event, void *user_data); typedef void (*wifi_manager_event_cb_t)(wifi_manager_event_t event, void *user_data);

View File

@@ -18,7 +18,7 @@
static const char *TAG = "lcd_module"; static const char *TAG = "lcd_module";
#define ST7789_SPI_HOST SPI2_HOST #define ST7789_SPI_HOST SPI2_HOST
#define LCD_PREVIEW_TIMEOUT_MS_DEFAULT 200 #define LCD_PREVIEW_TIMEOUT_MS_DEFAULT 3000
#define LCD_LOCK_TIMEOUT_MS 1000 #define LCD_LOCK_TIMEOUT_MS 1000
#define LCD_DEFAULT_DRAW_LINES 20 #define LCD_DEFAULT_DRAW_LINES 20
#define LCD_PREVIEW_FADE_DURATION_MS 150 #define LCD_PREVIEW_FADE_DURATION_MS 150

View File

@@ -66,6 +66,8 @@ static esp_netif_t *s_ap_netif;
static httpd_handle_t s_prov_httpd; static httpd_handle_t s_prov_httpd;
static esp_event_handler_instance_t s_wifi_event_inst; static esp_event_handler_instance_t s_wifi_event_inst;
static esp_event_handler_instance_t s_ip_event_inst; static esp_event_handler_instance_t s_ip_event_inst;
static uint8_t s_last_sta_disconnect_reason;
static bool s_last_sta_disconnect_reason_valid;
static void wifi_manager_notify_event(wifi_manager_event_t event) { static void wifi_manager_notify_event(wifi_manager_event_t event) {
wifi_manager_event_cb_t cb = s_event_cb; wifi_manager_event_cb_t cb = s_event_cb;
@@ -75,6 +77,87 @@ static void wifi_manager_notify_event(wifi_manager_event_t event) {
cb(event, s_event_cb_user_data); cb(event, s_event_cb_user_data);
} }
static const char *wifi_manager_error_from_disconnect_reason(uint8_t reason) {
#ifdef WIFI_REASON_AUTH_EXPIRE
if (reason == WIFI_REASON_AUTH_EXPIRE) {
return "wrong_password";
}
#endif
#ifdef WIFI_REASON_AUTH_FAIL
if (reason == WIFI_REASON_AUTH_FAIL) {
return "wrong_password";
}
#endif
#ifdef WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT
if (reason == WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT) {
return "wrong_password";
}
#endif
#ifdef WIFI_REASON_HANDSHAKE_TIMEOUT
if (reason == WIFI_REASON_HANDSHAKE_TIMEOUT) {
return "wrong_password";
}
#endif
#ifdef WIFI_REASON_ASSOC_FAIL
if (reason == WIFI_REASON_ASSOC_FAIL) {
return "association_failed";
}
#endif
#ifdef WIFI_REASON_CONNECTION_FAIL
if (reason == WIFI_REASON_CONNECTION_FAIL) {
return "association_failed";
}
#endif
#ifdef WIFI_REASON_NO_AP_FOUND
if (reason == WIFI_REASON_NO_AP_FOUND) {
return "no_ap_found";
}
#endif
#ifdef WIFI_REASON_NO_AP_FOUND_IN_AUTHMODE_THRESHOLD
if (reason == WIFI_REASON_NO_AP_FOUND_IN_AUTHMODE_THRESHOLD) {
return "ap_security_unsupported";
}
#endif
#ifdef WIFI_REASON_NO_AP_FOUND_IN_RSSI_THRESHOLD
if (reason == WIFI_REASON_NO_AP_FOUND_IN_RSSI_THRESHOLD) {
return "signal_too_weak";
}
#endif
#ifdef WIFI_REASON_BEACON_TIMEOUT
if (reason == WIFI_REASON_BEACON_TIMEOUT) {
return "ap_not_responding";
}
#endif
#ifdef WIFI_REASON_ASSOC_EXPIRE
if (reason == WIFI_REASON_ASSOC_EXPIRE) {
return "ap_not_responding";
}
#endif
#ifdef WIFI_REASON_ASSOC_LEAVE
if (reason == WIFI_REASON_ASSOC_LEAVE) {
return "ap_not_responding";
}
#endif
return "connect_failed";
}
static const char *wifi_manager_resolve_connect_error_code(esp_err_t connect_rc) {
if (connect_rc == ESP_ERR_TIMEOUT) {
return "connect_timeout";
}
if (connect_rc == ESP_ERR_INVALID_STATE) {
return "wifi_unavailable";
}
if (connect_rc != ESP_FAIL) {
return "connect_failed";
}
if (s_last_sta_disconnect_reason_valid) {
return wifi_manager_error_from_disconnect_reason(s_last_sta_disconnect_reason);
}
return "connect_failed";
}
static void wifi_manager_apply_sta_throughput_profile(void) { static void wifi_manager_apply_sta_throughput_profile(void) {
const uint8_t protocol = WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N; const uint8_t protocol = WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N;
esp_err_t proto_rc = esp_wifi_set_protocol(WIFI_IF_STA, protocol); esp_err_t proto_rc = esp_wifi_set_protocol(WIFI_IF_STA, protocol);
@@ -175,11 +258,15 @@ static void wifi_event_handler(void *arg,
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
s_ready = false; s_ready = false;
if (!s_started || !s_sta_reconnect_enabled) { if (!s_started || !s_sta_reconnect_enabled) {
wifi_manager_notify_event(WIFI_MANAGER_EVENT_STA_DISCONNECTED);
return; return;
} }
wifi_event_sta_disconnected_t *disc = (wifi_event_sta_disconnected_t *)event_data; wifi_event_sta_disconnected_t *disc = (wifi_event_sta_disconnected_t *)event_data;
uint8_t reason = disc != NULL ? disc->reason : 0; uint8_t reason = disc != NULL ? disc->reason : 0;
s_last_sta_disconnect_reason = reason;
s_last_sta_disconnect_reason_valid = (reason != 0);
wifi_manager_notify_event(WIFI_MANAGER_EVENT_STA_DISCONNECTED);
if (s_retry_num < CONFIG_TQ_WIFI_MAXIMUM_RETRY) { if (s_retry_num < CONFIG_TQ_WIFI_MAXIMUM_RETRY) {
(void)esp_wifi_connect(); (void)esp_wifi_connect();
@@ -208,6 +295,7 @@ static void wifi_event_handler(void *arg,
if (s_wifi_event_group != NULL) { if (s_wifi_event_group != NULL) {
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
} }
wifi_manager_notify_event(WIFI_MANAGER_EVENT_STA_CONNECTED);
} }
} }
@@ -476,6 +564,8 @@ static esp_err_t wifi_manager_connect_sta(const wifi_sta_credentials_t *creds, b
xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT); xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT);
s_retry_num = 0; s_retry_num = 0;
s_ready = false; s_ready = false;
s_last_sta_disconnect_reason = 0;
s_last_sta_disconnect_reason_valid = false;
s_sta_reconnect_enabled = false; s_sta_reconnect_enabled = false;
esp_err_t err = esp_wifi_disconnect(); esp_err_t err = esp_wifi_disconnect();
@@ -486,23 +576,28 @@ static esp_err_t wifi_manager_connect_sta(const wifi_sta_credentials_t *creds, b
err = esp_wifi_set_mode(keep_softap ? WIFI_MODE_APSTA : WIFI_MODE_STA); err = esp_wifi_set_mode(keep_softap ? WIFI_MODE_APSTA : WIFI_MODE_STA);
if (err != ESP_OK) { if (err != ESP_OK) {
wifi_manager_notify_event(WIFI_MANAGER_EVENT_STA_CONNECT_FAILED);
return err; return err;
} }
err = esp_wifi_set_config(WIFI_IF_STA, &wifi_config); err = esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
if (err != ESP_OK) { if (err != ESP_OK) {
wifi_manager_notify_event(WIFI_MANAGER_EVENT_STA_CONNECT_FAILED);
return err; return err;
} }
err = esp_wifi_start(); err = esp_wifi_start();
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) { if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
wifi_manager_notify_event(WIFI_MANAGER_EVENT_STA_CONNECT_FAILED);
return err; return err;
} }
err = esp_wifi_connect(); err = esp_wifi_connect();
if (err != ESP_OK && err != ESP_ERR_WIFI_CONN) { if (err != ESP_OK && err != ESP_ERR_WIFI_CONN) {
wifi_manager_notify_event(WIFI_MANAGER_EVENT_STA_CONNECT_FAILED);
return err; return err;
} }
wifi_manager_notify_event(WIFI_MANAGER_EVENT_STA_CONNECTING);
ESP_LOGI(TAG, "trying to connect to SSID:%s", creds->ssid); ESP_LOGI(TAG, "trying to connect to SSID:%s", creds->ssid);
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
@@ -526,10 +621,12 @@ static esp_err_t wifi_manager_connect_sta(const wifi_sta_credentials_t *creds, b
if (bits & WIFI_FAIL_BIT) { if (bits & WIFI_FAIL_BIT) {
ESP_LOGW(TAG, "failed to connect to SSID:%s", creds->ssid); ESP_LOGW(TAG, "failed to connect to SSID:%s", creds->ssid);
wifi_manager_notify_event(WIFI_MANAGER_EVENT_STA_CONNECT_FAILED);
return ESP_FAIL; return ESP_FAIL;
} }
ESP_LOGW(TAG, "Wi-Fi connect timeout, SSID:%s", creds->ssid); ESP_LOGW(TAG, "Wi-Fi connect timeout, SSID:%s", creds->ssid);
wifi_manager_notify_event(WIFI_MANAGER_EVENT_STA_CONNECT_FAILED);
return ESP_ERR_TIMEOUT; return ESP_ERR_TIMEOUT;
} }
@@ -583,7 +680,9 @@ static esp_err_t wifi_manager_prov_submit_post_handler(httpd_req_t *req) {
esp_err_t err = wifi_manager_connect_sta(&creds, true); esp_err_t err = wifi_manager_connect_sta(&creds, true);
if (err != ESP_OK) { if (err != ESP_OK) {
return wifi_manager_send_prov_json(req, false, "connect_failed"); return wifi_manager_send_prov_json(req,
false,
wifi_manager_resolve_connect_error_code(err));
} }
err = wifi_manager_save_credentials(&creds); err = wifi_manager_save_credentials(&creds);
@@ -789,6 +888,8 @@ esp_err_t wifi_manager_start(void) {
s_ready = false; s_ready = false;
s_sta_reconnect_enabled = true; s_sta_reconnect_enabled = true;
s_provisioning_active = false; s_provisioning_active = false;
s_last_sta_disconnect_reason = 0;
s_last_sta_disconnect_reason_valid = false;
if (s_wifi_event_group == NULL) { if (s_wifi_event_group == NULL) {
s_wifi_event_group = xEventGroupCreate(); s_wifi_event_group = xEventGroupCreate();
@@ -888,6 +989,8 @@ esp_err_t wifi_manager_stop(void) {
s_ready = false; s_ready = false;
s_retry_num = 0; s_retry_num = 0;
s_sta_reconnect_enabled = false; s_sta_reconnect_enabled = false;
s_last_sta_disconnect_reason = 0;
s_last_sta_disconnect_reason_valid = false;
wifi_manager_stop_softap_provisioning(); wifi_manager_stop_softap_provisioning();