语音打印
This commit is contained in:
@@ -1,4 +1,14 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
|
espressif/cmake_utilities:
|
||||||
|
component_hash: 351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f
|
||||||
|
dependencies:
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=4.1'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
type: service
|
||||||
|
version: 0.5.3
|
||||||
espressif/esp_audio_codec:
|
espressif/esp_audio_codec:
|
||||||
component_hash: af4663830bc73eca58febcc34a976d003465e78ed57458aa00a14b231e392ca1
|
component_hash: af4663830bc73eca58febcc34a976d003465e78ed57458aa00a14b231e392ca1
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -19,6 +29,20 @@ dependencies:
|
|||||||
registry_url: https://components.espressif.com/
|
registry_url: https://components.espressif.com/
|
||||||
type: service
|
type: service
|
||||||
version: 1.5.4
|
version: 1.5.4
|
||||||
|
espressif/esp_lcd_gc9a01:
|
||||||
|
component_hash: e60bc2b5649c7679bd010797a875ae64b2a57cf9cdbd6a2bf7a0ade66913edc0
|
||||||
|
dependencies:
|
||||||
|
- name: espressif/cmake_utilities
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
require: private
|
||||||
|
version: 0.*
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=4.4'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 2.0.4
|
||||||
espressif/esp_websocket_client:
|
espressif/esp_websocket_client:
|
||||||
component_hash: c5a067a9fddea370c478017e66fac302f4b79c3d4027e9bdd42a019786cceb92
|
component_hash: c5a067a9fddea370c478017e66fac302f4b79c3d4027e9bdd42a019786cceb92
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -60,9 +84,10 @@ dependencies:
|
|||||||
direct_dependencies:
|
direct_dependencies:
|
||||||
- espressif/esp_audio_codec
|
- espressif/esp_audio_codec
|
||||||
- espressif/esp_codec_dev
|
- espressif/esp_codec_dev
|
||||||
|
- espressif/esp_lcd_gc9a01
|
||||||
- espressif/esp_websocket_client
|
- espressif/esp_websocket_client
|
||||||
- espressif/libpng
|
- espressif/libpng
|
||||||
- idf
|
- idf
|
||||||
manifest_hash: 3274b53ce89a58abbad0a03dfaff3410e35deb81305a21289606c4d2177a111b
|
manifest_hash: ee0dd489986ab012202f5537ca2249a0c4e3ca234a23194dddb81cc25075b54c
|
||||||
target: esp32s3
|
target: esp32s3
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
#include "esp_audio_enc.h"
|
#include "esp_audio_enc.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "image_generation.h"
|
#include "image_generation.h"
|
||||||
|
#include "printer_protocol.h"
|
||||||
|
#include "raster_tools.h"
|
||||||
#include "voice_audio.h"
|
#include "voice_audio.h"
|
||||||
|
|
||||||
static const char *TAG = "voice_interaction";
|
static const char *TAG = "voice_interaction";
|
||||||
@@ -23,12 +25,78 @@ static const char *TAG = "voice_interaction";
|
|||||||
#define VOICE_IMAGE_MARKER_FIRST_SENTENCE_MAX_LEN 1024
|
#define VOICE_IMAGE_MARKER_FIRST_SENTENCE_MAX_LEN 1024
|
||||||
#define VOICE_IMAGE_GEN_TASK_STACK 16384
|
#define VOICE_IMAGE_GEN_TASK_STACK 16384
|
||||||
#define VOICE_IMAGE_GEN_TASK_PRIO 3
|
#define VOICE_IMAGE_GEN_TASK_PRIO 3
|
||||||
|
#define VOICE_IMAGE_MARKER_REPEAT_SUPPRESS_MS 45000
|
||||||
|
#define VOICE_IMAGE_PRINT_THRESHOLD 160
|
||||||
|
#define VOICE_IMAGE_PRINT_MAX_HEIGHT 2200
|
||||||
|
#define VOICE_IMAGE_MARKER_TEMPLATE \
|
||||||
|
"%s,黑白线稿风格,白色背景,主体居中,边缘清晰,细节简洁,高对比,避免大面积阴影和灰度渐变,适配热敏打印机打印。"
|
||||||
|
#define VOICE_IMAGE_MARKER_TEMPLATE_FALLBACK_SUBJECT "简洁卡通图案"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char prompt[VOICE_IMAGE_MARKER_PROMPT_MAX_LEN + 1];
|
char prompt[VOICE_IMAGE_MARKER_PROMPT_MAX_LEN + 1];
|
||||||
char dialog_id[40];
|
char dialog_id[40];
|
||||||
} voice_image_generation_task_arg_t;
|
} voice_image_generation_task_arg_t;
|
||||||
|
|
||||||
|
static portMUX_TYPE s_marker_image_gen_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||||
|
static bool s_marker_image_gen_running;
|
||||||
|
static int64_t s_marker_last_success_ms;
|
||||||
|
static char s_marker_last_success_prompt[VOICE_IMAGE_MARKER_PROMPT_MAX_LEN + 1];
|
||||||
|
static char s_marker_last_success_dialog_id[40];
|
||||||
|
|
||||||
|
static bool voice_marker_image_generation_try_acquire(const char *prompt,
|
||||||
|
const char *dialog_id,
|
||||||
|
int64_t now_ms,
|
||||||
|
uint32_t *out_remain_ms) {
|
||||||
|
bool acquired = false;
|
||||||
|
uint32_t remain_ms = 0;
|
||||||
|
const char *safe_prompt = prompt != NULL ? prompt : "";
|
||||||
|
const char *safe_dialog = dialog_id != NULL ? dialog_id : "";
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&s_marker_image_gen_lock);
|
||||||
|
if (s_marker_image_gen_running) {
|
||||||
|
acquired = false;
|
||||||
|
} else if (s_marker_last_success_ms > 0 &&
|
||||||
|
strcmp(safe_prompt, s_marker_last_success_prompt) == 0 &&
|
||||||
|
strcmp(safe_dialog, s_marker_last_success_dialog_id) == 0) {
|
||||||
|
int64_t elapsed_ms = now_ms - s_marker_last_success_ms;
|
||||||
|
if (elapsed_ms >= 0 && elapsed_ms < VOICE_IMAGE_MARKER_REPEAT_SUPPRESS_MS) {
|
||||||
|
remain_ms = (uint32_t)(VOICE_IMAGE_MARKER_REPEAT_SUPPRESS_MS - elapsed_ms);
|
||||||
|
acquired = false;
|
||||||
|
} else {
|
||||||
|
s_marker_image_gen_running = true;
|
||||||
|
acquired = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s_marker_image_gen_running = true;
|
||||||
|
acquired = true;
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&s_marker_image_gen_lock);
|
||||||
|
|
||||||
|
if (out_remain_ms != NULL) {
|
||||||
|
*out_remain_ms = remain_ms;
|
||||||
|
}
|
||||||
|
return acquired;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void voice_marker_image_generation_release(void) {
|
||||||
|
portENTER_CRITICAL(&s_marker_image_gen_lock);
|
||||||
|
s_marker_image_gen_running = false;
|
||||||
|
portEXIT_CRITICAL(&s_marker_image_gen_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void voice_marker_image_generation_record_success(const char *prompt,
|
||||||
|
const char *dialog_id,
|
||||||
|
int64_t now_ms) {
|
||||||
|
const char *safe_prompt = prompt != NULL ? prompt : "";
|
||||||
|
const char *safe_dialog = dialog_id != NULL ? dialog_id : "";
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&s_marker_image_gen_lock);
|
||||||
|
strlcpy(s_marker_last_success_prompt, safe_prompt, sizeof(s_marker_last_success_prompt));
|
||||||
|
strlcpy(s_marker_last_success_dialog_id, safe_dialog, sizeof(s_marker_last_success_dialog_id));
|
||||||
|
s_marker_last_success_ms = now_ms;
|
||||||
|
portEXIT_CRITICAL(&s_marker_image_gen_lock);
|
||||||
|
}
|
||||||
|
|
||||||
static void *voice_cjson_malloc_hook(size_t size) {
|
static void *voice_cjson_malloc_hook(size_t size) {
|
||||||
return voice_malloc_prefer_psram(size);
|
return voice_malloc_prefer_psram(size);
|
||||||
}
|
}
|
||||||
@@ -418,6 +486,111 @@ static bool voice_extract_marker_prompt(const char *text, char *out_prompt, size
|
|||||||
return prompt_len > 0;
|
return prompt_len > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void voice_build_marker_image_prompt(const char *subject,
|
||||||
|
char *out_prompt,
|
||||||
|
size_t out_len) {
|
||||||
|
if (out_prompt == NULL || out_len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *safe_subject = (subject != NULL && subject[0] != '\0') ?
|
||||||
|
subject :
|
||||||
|
VOICE_IMAGE_MARKER_TEMPLATE_FALLBACK_SUBJECT;
|
||||||
|
int written = snprintf(out_prompt,
|
||||||
|
out_len,
|
||||||
|
VOICE_IMAGE_MARKER_TEMPLATE,
|
||||||
|
safe_subject);
|
||||||
|
if (written < 0) {
|
||||||
|
out_prompt[0] = '\0';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((size_t)written >= out_len) {
|
||||||
|
out_prompt[out_len - 1] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t voice_submit_marker_generated_image(const voice_image_generation_task_arg_t *task_arg,
|
||||||
|
const image_generation_result_t *result,
|
||||||
|
char *err,
|
||||||
|
size_t err_len) {
|
||||||
|
if (task_arg == NULL || result == NULL || result->png == NULL || result->png_len == 0) {
|
||||||
|
voice_fill_err(err, err_len, "invalid image result");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t raster_width = 0;
|
||||||
|
uint16_t raster_height = 0;
|
||||||
|
uint8_t *raster = NULL;
|
||||||
|
size_t raster_len = 0;
|
||||||
|
char decode_err[128] = {0};
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"[stage] marker_image_print_decode_start: dialog_id=%s request_id=%s png_bytes=%u",
|
||||||
|
task_arg->dialog_id[0] != '\0' ? task_arg->dialog_id : "-",
|
||||||
|
result->request_id[0] != '\0' ? result->request_id : "-",
|
||||||
|
(unsigned)result->png_len);
|
||||||
|
esp_err_t decode_rc = raster_tools_convert_png_to_raster_384(result->png,
|
||||||
|
result->png_len,
|
||||||
|
true,
|
||||||
|
VOICE_IMAGE_PRINT_THRESHOLD,
|
||||||
|
false,
|
||||||
|
VOICE_IMAGE_PRINT_MAX_HEIGHT,
|
||||||
|
&raster_width,
|
||||||
|
&raster_height,
|
||||||
|
&raster,
|
||||||
|
&raster_len,
|
||||||
|
decode_err,
|
||||||
|
sizeof(decode_err));
|
||||||
|
if (decode_rc != ESP_OK) {
|
||||||
|
voice_fill_err(err,
|
||||||
|
err_len,
|
||||||
|
decode_err[0] != '\0' ? decode_err : "decode generated image failed");
|
||||||
|
ESP_LOGW(TAG,
|
||||||
|
"[stage] marker_image_print_decode_failed: dialog_id=%s rc=0x%x msg=%s",
|
||||||
|
task_arg->dialog_id[0] != '\0' ? task_arg->dialog_id : "-",
|
||||||
|
(unsigned)decode_rc,
|
||||||
|
err != NULL && err[0] != '\0' ? err : "decode generated image failed");
|
||||||
|
return decode_rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"[stage] marker_image_print_decode_done: dialog_id=%s raster_size=%ux%u raster_bytes=%u",
|
||||||
|
task_arg->dialog_id[0] != '\0' ? task_arg->dialog_id : "-",
|
||||||
|
(unsigned)raster_width,
|
||||||
|
(unsigned)raster_height,
|
||||||
|
(unsigned)raster_len);
|
||||||
|
|
||||||
|
uint32_t job_id = 0;
|
||||||
|
char submit_err[128] = {0};
|
||||||
|
esp_err_t submit_rc = printer_protocol_submit_raster_job(raster,
|
||||||
|
raster_len,
|
||||||
|
raster_width,
|
||||||
|
raster_height,
|
||||||
|
NULL,
|
||||||
|
&job_id,
|
||||||
|
submit_err,
|
||||||
|
sizeof(submit_err));
|
||||||
|
if (submit_rc != ESP_OK) {
|
||||||
|
voice_fill_err(err,
|
||||||
|
err_len,
|
||||||
|
submit_err[0] != '\0' ? submit_err : "submit print job failed");
|
||||||
|
ESP_LOGW(TAG,
|
||||||
|
"[stage] marker_image_print_submit_failed: dialog_id=%s rc=0x%x msg=%s",
|
||||||
|
task_arg->dialog_id[0] != '\0' ? task_arg->dialog_id : "-",
|
||||||
|
(unsigned)submit_rc,
|
||||||
|
err != NULL && err[0] != '\0' ? err : "submit print job failed");
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"[stage] marker_image_print_submit_ok: dialog_id=%s request_id=%s job_id=%u",
|
||||||
|
task_arg->dialog_id[0] != '\0' ? task_arg->dialog_id : "-",
|
||||||
|
result->request_id[0] != '\0' ? result->request_id : "-",
|
||||||
|
(unsigned)job_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(raster);
|
||||||
|
return submit_rc;
|
||||||
|
}
|
||||||
|
|
||||||
static void voice_image_generation_task(void *arg) {
|
static void voice_image_generation_task(void *arg) {
|
||||||
voice_image_generation_task_arg_t *task_arg = (voice_image_generation_task_arg_t *)arg;
|
voice_image_generation_task_arg_t *task_arg = (voice_image_generation_task_arg_t *)arg;
|
||||||
if (task_arg == NULL) {
|
if (task_arg == NULL) {
|
||||||
@@ -438,12 +611,17 @@ static void voice_image_generation_task(void *arg) {
|
|||||||
image_generation_result_t result = {0};
|
image_generation_result_t result = {0};
|
||||||
image_generation_result_reset(&result);
|
image_generation_result_reset(&result);
|
||||||
|
|
||||||
|
bool status_poll_paused = false;
|
||||||
char gen_err[160] = {0};
|
char gen_err[160] = {0};
|
||||||
ESP_LOGI(TAG,
|
ESP_LOGI(TAG,
|
||||||
"[stage] marker_image_gen_start: dialog_id=%s prompt_len=%u",
|
"[stage] marker_image_gen_start: dialog_id=%s prompt_len=%u",
|
||||||
task_arg->dialog_id[0] != '\0' ? task_arg->dialog_id : "-",
|
task_arg->dialog_id[0] != '\0' ? task_arg->dialog_id : "-",
|
||||||
(unsigned)strlen(task_arg->prompt));
|
(unsigned)strlen(task_arg->prompt));
|
||||||
|
printer_protocol_set_status_poll_paused(true);
|
||||||
|
status_poll_paused = true;
|
||||||
esp_err_t rc = image_generation_generate_png(&req, &result, gen_err, sizeof(gen_err));
|
esp_err_t rc = image_generation_generate_png(&req, &result, gen_err, sizeof(gen_err));
|
||||||
|
printer_protocol_set_status_poll_paused(false);
|
||||||
|
status_poll_paused = false;
|
||||||
if (rc == ESP_OK) {
|
if (rc == ESP_OK) {
|
||||||
ESP_LOGI(TAG,
|
ESP_LOGI(TAG,
|
||||||
"[stage] marker_image_gen_done: dialog_id=%s request_id=%s size=%ux%u png_bytes=%u",
|
"[stage] marker_image_gen_done: dialog_id=%s request_id=%s size=%ux%u png_bytes=%u",
|
||||||
@@ -452,6 +630,23 @@ static void voice_image_generation_task(void *arg) {
|
|||||||
(unsigned)result.width,
|
(unsigned)result.width,
|
||||||
(unsigned)result.height,
|
(unsigned)result.height,
|
||||||
(unsigned)result.png_len);
|
(unsigned)result.png_len);
|
||||||
|
|
||||||
|
char submit_err[160] = {0};
|
||||||
|
esp_err_t submit_rc = voice_submit_marker_generated_image(task_arg,
|
||||||
|
&result,
|
||||||
|
submit_err,
|
||||||
|
sizeof(submit_err));
|
||||||
|
if (submit_rc == ESP_OK) {
|
||||||
|
voice_marker_image_generation_record_success(task_arg->prompt,
|
||||||
|
task_arg->dialog_id,
|
||||||
|
voice_now_ms());
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG,
|
||||||
|
"[stage] marker_image_print_failed: dialog_id=%s rc=0x%x msg=%s",
|
||||||
|
task_arg->dialog_id[0] != '\0' ? task_arg->dialog_id : "-",
|
||||||
|
(unsigned)submit_rc,
|
||||||
|
submit_err[0] != '\0' ? submit_err : "marker image print failed");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(TAG,
|
ESP_LOGW(TAG,
|
||||||
"[stage] marker_image_gen_failed: dialog_id=%s rc=0x%x msg=%s",
|
"[stage] marker_image_gen_failed: dialog_id=%s rc=0x%x msg=%s",
|
||||||
@@ -460,7 +655,11 @@ static void voice_image_generation_task(void *arg) {
|
|||||||
gen_err[0] != '\0' ? gen_err : "image generation failed");
|
gen_err[0] != '\0' ? gen_err : "image generation failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (status_poll_paused) {
|
||||||
|
printer_protocol_set_status_poll_paused(false);
|
||||||
|
}
|
||||||
image_generation_result_free(&result);
|
image_generation_result_free(&result);
|
||||||
|
voice_marker_image_generation_release();
|
||||||
image_generation_schedule_prewarm();
|
image_generation_schedule_prewarm();
|
||||||
free(task_arg);
|
free(task_arg);
|
||||||
vTaskDelete(NULL);
|
vTaskDelete(NULL);
|
||||||
@@ -479,10 +678,26 @@ static void voice_schedule_marker_image_generation(const char *prompt, const cha
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t repeat_remain_ms = 0;
|
||||||
|
if (!voice_marker_image_generation_try_acquire(prompt,
|
||||||
|
dialog_id,
|
||||||
|
voice_now_ms(),
|
||||||
|
&repeat_remain_ms)) {
|
||||||
|
if (repeat_remain_ms > 0) {
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"[stage] marker_image_gen_skip: duplicate prompt suppressed, remain_ms=%u",
|
||||||
|
(unsigned)repeat_remain_ms);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "[stage] marker_image_gen_skip: previous marker task running");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
voice_image_generation_task_arg_t *task_arg =
|
voice_image_generation_task_arg_t *task_arg =
|
||||||
(voice_image_generation_task_arg_t *)calloc(1, sizeof(*task_arg));
|
(voice_image_generation_task_arg_t *)calloc(1, sizeof(*task_arg));
|
||||||
if (task_arg == NULL) {
|
if (task_arg == NULL) {
|
||||||
ESP_LOGW(TAG, "[stage] marker_image_gen_skip: no memory for task arg");
|
ESP_LOGW(TAG, "[stage] marker_image_gen_skip: no memory for task arg");
|
||||||
|
voice_marker_image_generation_release();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,6 +714,7 @@ static void voice_schedule_marker_image_generation(const char *prompt, const cha
|
|||||||
NULL);
|
NULL);
|
||||||
if (ok != pdPASS) {
|
if (ok != pdPASS) {
|
||||||
ESP_LOGW(TAG, "[stage] marker_image_gen_skip: create task failed");
|
ESP_LOGW(TAG, "[stage] marker_image_gen_skip: create task failed");
|
||||||
|
voice_marker_image_generation_release();
|
||||||
free(task_arg);
|
free(task_arg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -519,15 +735,28 @@ static void voice_try_trigger_marker_image_generation(const char *dialog_id, con
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char prompt[VOICE_IMAGE_MARKER_PROMPT_MAX_LEN + 1] = {0};
|
char marker_subject[VOICE_IMAGE_MARKER_PROMPT_MAX_LEN + 1] = {0};
|
||||||
if (!voice_extract_marker_prompt(first_sentence, prompt, sizeof(prompt))) {
|
if (!voice_extract_marker_prompt(first_sentence, marker_subject, sizeof(marker_subject))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char templated_prompt[VOICE_IMAGE_MARKER_PROMPT_MAX_LEN + 1] = {0};
|
||||||
|
voice_build_marker_image_prompt(marker_subject,
|
||||||
|
templated_prompt,
|
||||||
|
sizeof(templated_prompt));
|
||||||
|
if (templated_prompt[0] == '\0') {
|
||||||
|
ESP_LOGW(TAG, "[stage] marker_image_gen_skip: build prompt failed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGI(TAG,
|
ESP_LOGI(TAG,
|
||||||
"[stage] marker_image_gen_detected: first_sentence=%s",
|
"[stage] marker_image_gen_detected: first_sentence=%s",
|
||||||
first_sentence);
|
first_sentence);
|
||||||
voice_schedule_marker_image_generation(prompt, dialog_id);
|
ESP_LOGI(TAG,
|
||||||
|
"[stage] marker_image_gen_subject: keyword=%s",
|
||||||
|
marker_subject);
|
||||||
|
voice_log_final_text("marker_image_gen_prompt_templated", templated_prompt);
|
||||||
|
voice_schedule_marker_image_generation(templated_prompt, dialog_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void voice_handle_output_event(cJSON *output) {
|
static void voice_handle_output_event(cJSON *output) {
|
||||||
|
|||||||
@@ -18,3 +18,4 @@ dependencies:
|
|||||||
espressif/esp_codec_dev: ^1.5.4
|
espressif/esp_codec_dev: ^1.5.4
|
||||||
espressif/esp_audio_codec: ^2.4.1
|
espressif/esp_audio_codec: ^2.4.1
|
||||||
espressif/libpng: ^1.6.55
|
espressif/libpng: ^1.6.55
|
||||||
|
espressif/esp_lcd_gc9a01: ^2.0.4
|
||||||
|
|||||||
Reference in New Issue
Block a user