多模态

This commit is contained in:
admin
2026-02-24 21:36:14 +08:00
parent e1e0ac8929
commit 68840e6ea4
23 changed files with 3368 additions and 44 deletions

View File

@@ -44,3 +44,9 @@ esp_err_t rest_server_ota_jump_app_post(httpd_req_t *req);
esp_err_t rest_server_ota_erase_page_post(httpd_req_t *req);
esp_err_t rest_server_ota_write_frame_post(httpd_req_t *req);
esp_err_t rest_server_ota_upgrade_post(httpd_req_t *req);
esp_err_t rest_server_voice_status_get(httpd_req_t *req);
esp_err_t rest_server_voice_session_start_post(httpd_req_t *req);
esp_err_t rest_server_voice_session_stop_post(httpd_req_t *req);
esp_err_t rest_server_voice_tap_start_post(httpd_req_t *req);
esp_err_t rest_server_voice_tap_cancel_post(httpd_req_t *req);

View File

@@ -4,6 +4,7 @@
#include "printer_protocol.h"
#include "rest_server.h"
#include "system_runtime.h"
#include "voice_interaction.h"
static const char *TAG = "controller_lifecycle";
@@ -14,6 +15,9 @@ esp_err_t controller_lifecycle_start(void) {
ESP_LOGI(TAG, "Starting printer protocol");
ESP_ERROR_CHECK(printer_protocol_init());
ESP_LOGI(TAG, "Starting voice interaction");
ESP_ERROR_CHECK(voice_interaction_init());
ESP_LOGI(TAG, "Starting REST server");
ESP_ERROR_CHECK(rest_server_start());
@@ -22,6 +26,7 @@ esp_err_t controller_lifecycle_start(void) {
void controller_lifecycle_stop(void) {
rest_server_stop();
(void)voice_interaction_stop(NULL, 0);
printer_protocol_disconnect();
ESP_LOGI(TAG, "Controller lifecycle stopped");
}

View File

@@ -16,7 +16,7 @@ static const char *s_web_index =
"<head>\n"
" <meta charset='utf-8'>\n"
" <meta name='viewport' content='width=device-width,initial-scale=1'>\n"
" <title>LYF Printer Controller</title>\n"
" <title>TQ Printer Controller</title>\n"
" <style>\n"
" :root { color-scheme: light; }\n"
" body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; margin: 16px; background:#f7f8fb; color:#1b2430; }\n"
@@ -37,7 +37,7 @@ static const char *s_web_index =
"</head>\n"
"<body>\n"
" <div class='box'>\n"
" <h1>LYF Printer Controller</h1>\n"
" <h1>TQ Printer Controller</h1>\n"
" <p>ESP32-S3 local control page. This page calls the onboard REST API directly.</p>\n"
"\n"
" <label>API Key (optional)</label>\n"
@@ -50,7 +50,7 @@ static const char *s_web_index =
" </div>\n"
"\n"
" <label>Printer Name</label>\n"
" <input id='printerName' type='text' value='lyfPrinter'>\n"
" <input id='printerName' type='text' value='TQPrinter'>\n"
" <div class='row'>\n"
" <button id='btnConnect'>Connect Printer</button>\n"
" <button id='btnAutoConnect' class='alt'>Auto Connect</button>\n"
@@ -206,7 +206,7 @@ static const char *s_web_index =
" document.getElementById('btnJobs').onclick = () => run(() => callApi('/v1/jobs', 'GET'));\n"
"\n"
" document.getElementById('btnConnect').onclick = () => run(() => callApi('/v1/printer/connect', 'POST', {\n"
" name: printerNameEl.value.trim() || 'lyfPrinter',\n"
" name: printerNameEl.value.trim() || 'TQPrinter',\n"
" timeout_ms: 15000\n"
" }));\n"
"\n"
@@ -249,7 +249,7 @@ static esp_err_t favicon_get(httpd_req_t *req) {
esp_err_t rest_server_start(void) {
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.server_port = CONFIG_LYF_HTTP_PORT;
config.server_port = CONFIG_TQ_HTTP_PORT;
config.uri_match_fn = httpd_uri_match_wildcard;
config.max_uri_handlers = 32;
@@ -301,6 +301,41 @@ esp_err_t rest_server_start(void) {
};
httpd_register_uri_handler(s_server, &status);
httpd_uri_t voice_status = {
.uri = "/v1/voice/status",
.method = HTTP_GET,
.handler = rest_server_voice_status_get,
};
httpd_register_uri_handler(s_server, &voice_status);
httpd_uri_t voice_session_start = {
.uri = "/v1/voice/session/start",
.method = HTTP_POST,
.handler = rest_server_voice_session_start_post,
};
httpd_register_uri_handler(s_server, &voice_session_start);
httpd_uri_t voice_session_stop = {
.uri = "/v1/voice/session/stop",
.method = HTTP_POST,
.handler = rest_server_voice_session_stop_post,
};
httpd_register_uri_handler(s_server, &voice_session_stop);
httpd_uri_t voice_tap_start = {
.uri = "/v1/voice/tap/start",
.method = HTTP_POST,
.handler = rest_server_voice_tap_start_post,
};
httpd_register_uri_handler(s_server, &voice_tap_start);
httpd_uri_t voice_tap_cancel = {
.uri = "/v1/voice/tap/cancel",
.method = HTTP_POST,
.handler = rest_server_voice_tap_cancel_post,
};
httpd_register_uri_handler(s_server, &voice_tap_cancel);
httpd_uri_t print_raster = {
.uri = "/v1/print/raster",
.method = HTTP_POST,
@@ -434,7 +469,7 @@ esp_err_t rest_server_start(void) {
};
httpd_register_uri_handler(s_server, &ota_upgrade);
ESP_LOGI(TAG, "REST server started on port %d", CONFIG_LYF_HTTP_PORT);
ESP_LOGI(TAG, "REST server started on port %d", CONFIG_TQ_HTTP_PORT);
return ESP_OK;
}

View File

@@ -27,7 +27,7 @@ static void *calloc_prefer_psram(size_t n, size_t size) {
return ptr;
}
bool rest_server_auth_ok(httpd_req_t *req) {
if (strlen(CONFIG_LYF_API_KEY) == 0) {
if (strlen(CONFIG_TQ_API_KEY) == 0) {
return true;
}
@@ -36,7 +36,7 @@ bool rest_server_auth_ok(httpd_req_t *req) {
return false;
}
return strcmp(buf, CONFIG_LYF_API_KEY) == 0;
return strcmp(buf, CONFIG_TQ_API_KEY) == 0;
}
esp_err_t rest_server_send_json(httpd_req_t *req, const char *status, cJSON *root) {

View File

@@ -6,6 +6,7 @@
#include "printer_protocol.h"
#include "system_runtime.h"
#include "voice_interaction.h"
static void fill_runtime_json(cJSON *root) {
char ip[32] = {0};
system_runtime_get_ip(ip, sizeof(ip));
@@ -26,6 +27,23 @@ static void fill_runtime_json(cJSON *root) {
cJSON_AddNumberToObject(root, "queue_depth", st.queue_depth);
cJSON_AddNumberToObject(root, "mtu", st.mtu);
cJSON_AddNumberToObject(root, "last_status_ms", (double)st.last_status_ms);
voice_interaction_status_t voice = {0};
voice_interaction_get_status(&voice);
cJSON_AddBoolToObject(root, "voice_ready", voice.ready);
cJSON_AddBoolToObject(root, "voice_session_active", voice.session_active);
cJSON_AddBoolToObject(root, "voice_ws_connected", voice.ws_connected);
cJSON_AddBoolToObject(root, "voice_started", voice.started);
cJSON_AddBoolToObject(root, "voice_tap_active", voice.tap_active);
cJSON_AddStringToObject(root,
"voice_dialog_state",
voice_interaction_dialog_state_str(voice.dialog_state));
cJSON_AddStringToObject(root, "voice_task_id", voice.task_id);
cJSON_AddStringToObject(root, "voice_dialog_id", voice.dialog_id);
cJSON_AddStringToObject(root, "voice_last_error", voice.last_error);
cJSON_AddNumberToObject(root, "voice_last_event_ms", (double)voice.last_event_ms);
cJSON_AddNumberToObject(root, "voice_upstream_packets", (double)voice.upstream_packets);
cJSON_AddNumberToObject(root, "voice_downstream_packets", (double)voice.downstream_packets);
}
esp_err_t rest_server_health_get(httpd_req_t *req) {
@@ -48,7 +66,7 @@ esp_err_t rest_server_connect_post(httpd_req_t *req) {
}
char *body = NULL;
char name[32] = "lyfPrinter";
char name[32] = "TQPrinter";
uint32_t timeout_ms = 15000;
if (req->content_len > 0) {

View File

@@ -688,7 +688,7 @@ esp_err_t rest_server_print_receipt_post(httpd_req_t *req) {
const char *title = (cJSON_IsString(jtitle) && jtitle->valuestring != NULL)
? jtitle->valuestring
: "LYF RECEIPT";
: "TQ RECEIPT";
const char *footer = (cJSON_IsString(jfooter) && jfooter->valuestring != NULL)
? jfooter->valuestring
: "Thank you";

View File

@@ -0,0 +1,167 @@
#include "rest_server_internal.h"
#include <stdlib.h>
#include "voice_interaction.h"
static void rest_server_add_voice_status(cJSON *root) {
voice_interaction_status_t st = {0};
voice_interaction_get_status(&st);
cJSON_AddBoolToObject(root, "voice_ready", st.ready);
cJSON_AddBoolToObject(root, "voice_session_active", st.session_active);
cJSON_AddBoolToObject(root, "voice_ws_connected", st.ws_connected);
cJSON_AddBoolToObject(root, "voice_started", st.started);
cJSON_AddBoolToObject(root, "voice_tap_active", st.tap_active);
cJSON_AddStringToObject(root,
"voice_dialog_state",
voice_interaction_dialog_state_str(st.dialog_state));
cJSON_AddStringToObject(root, "voice_task_id", st.task_id);
cJSON_AddStringToObject(root, "voice_dialog_id", st.dialog_id);
cJSON_AddStringToObject(root, "voice_last_error", st.last_error);
cJSON_AddNumberToObject(root, "voice_last_event_ms", (double)st.last_event_ms);
cJSON_AddNumberToObject(root, "voice_upstream_packets", (double)st.upstream_packets);
cJSON_AddNumberToObject(root, "voice_downstream_packets", (double)st.downstream_packets);
}
esp_err_t rest_server_voice_status_get(httpd_req_t *req) {
if (!rest_server_auth_ok(req)) {
return rest_server_send_error(req, "401 Unauthorized", "unauthorized");
}
cJSON *root = cJSON_CreateObject();
cJSON_AddBoolToObject(root, "ok", true);
rest_server_add_voice_status(root);
esp_err_t err = rest_server_send_json(req, "200 OK", root);
cJSON_Delete(root);
return err;
}
esp_err_t rest_server_voice_session_start_post(httpd_req_t *req) {
if (!rest_server_auth_ok(req)) {
return rest_server_send_error(req, "401 Unauthorized", "unauthorized");
}
if (req->content_len > 0) {
char *body = NULL;
esp_err_t body_err = rest_server_read_body(req, &body);
if (body_err != ESP_OK) {
return rest_server_send_error(req, "400 Bad Request", "invalid request body");
}
free(body);
}
char start_err[128] = {0};
esp_err_t rc = voice_interaction_start(start_err, sizeof(start_err));
if (rc != ESP_OK) {
return rest_server_send_error(req,
"409 Conflict",
start_err[0] != '\0' ? start_err : "voice session start failed");
}
cJSON *root = cJSON_CreateObject();
cJSON_AddBoolToObject(root, "ok", true);
cJSON_AddStringToObject(root, "message", "voice session started");
rest_server_add_voice_status(root);
esp_err_t err = rest_server_send_json(req, "200 OK", root);
cJSON_Delete(root);
return err;
}
esp_err_t rest_server_voice_session_stop_post(httpd_req_t *req) {
if (!rest_server_auth_ok(req)) {
return rest_server_send_error(req, "401 Unauthorized", "unauthorized");
}
if (req->content_len > 0) {
char *body = NULL;
esp_err_t body_err = rest_server_read_body(req, &body);
if (body_err != ESP_OK) {
return rest_server_send_error(req, "400 Bad Request", "invalid request body");
}
free(body);
}
char stop_err[128] = {0};
esp_err_t rc = voice_interaction_stop(stop_err, sizeof(stop_err));
if (rc != ESP_OK) {
return rest_server_send_error(req,
"409 Conflict",
stop_err[0] != '\0' ? stop_err : "voice session stop failed");
}
cJSON *root = cJSON_CreateObject();
cJSON_AddBoolToObject(root, "ok", true);
cJSON_AddStringToObject(root, "message", "voice session stopped");
rest_server_add_voice_status(root);
esp_err_t err = rest_server_send_json(req, "200 OK", root);
cJSON_Delete(root);
return err;
}
esp_err_t rest_server_voice_tap_start_post(httpd_req_t *req) {
if (!rest_server_auth_ok(req)) {
return rest_server_send_error(req, "401 Unauthorized", "unauthorized");
}
if (req->content_len > 0) {
char *body = NULL;
esp_err_t body_err = rest_server_read_body(req, &body);
if (body_err != ESP_OK) {
return rest_server_send_error(req, "400 Bad Request", "invalid request body");
}
free(body);
}
char tap_err[128] = {0};
esp_err_t rc = voice_interaction_tap_start(tap_err, sizeof(tap_err));
if (rc != ESP_OK) {
return rest_server_send_error(req,
"409 Conflict",
tap_err[0] != '\0' ? tap_err : "tap start failed");
}
cJSON *root = cJSON_CreateObject();
cJSON_AddBoolToObject(root, "ok", true);
cJSON_AddStringToObject(root, "message", "tap started");
rest_server_add_voice_status(root);
esp_err_t err = rest_server_send_json(req, "200 OK", root);
cJSON_Delete(root);
return err;
}
esp_err_t rest_server_voice_tap_cancel_post(httpd_req_t *req) {
if (!rest_server_auth_ok(req)) {
return rest_server_send_error(req, "401 Unauthorized", "unauthorized");
}
if (req->content_len > 0) {
char *body = NULL;
esp_err_t body_err = rest_server_read_body(req, &body);
if (body_err != ESP_OK) {
return rest_server_send_error(req, "400 Bad Request", "invalid request body");
}
free(body);
}
char tap_err[128] = {0};
esp_err_t rc = voice_interaction_tap_cancel(tap_err, sizeof(tap_err));
if (rc != ESP_OK) {
return rest_server_send_error(req,
"409 Conflict",
tap_err[0] != '\0' ? tap_err : "tap cancel failed");
}
cJSON *root = cJSON_CreateObject();
cJSON_AddBoolToObject(root, "ok", true);
cJSON_AddStringToObject(root, "message", "tap canceled");
rest_server_add_voice_status(root);
esp_err_t err = rest_server_send_json(req, "200 OK", root);
cJSON_Delete(root);
return err;
}