diff --git a/components/control_plane/src/rest_server_print_image.c b/components/control_plane/src/rest_server_print_image.c index 938981c..f6f97fb 100644 --- a/components/control_plane/src/rest_server_print_image.c +++ b/components/control_plane/src/rest_server_print_image.c @@ -9,6 +9,7 @@ #include "domain.h" static const char *TAG = "rest_print"; +#define SCREEN_PREVIEW_TIMEOUT_MS 200 typedef struct { bool scale_to_width; @@ -355,6 +356,42 @@ static esp_err_t rest_server_send_image_generation_error(httpd_req_t *req, return rest_server_send_error(req, "502 Bad Gateway", message); } +static void rest_server_try_preview_raster(const char *source, + const uint8_t *raster, + size_t raster_len, + uint16_t width, + uint16_t height) { + if (raster == NULL || raster_len == 0 || width == 0 || height == 0) { + return; + } + + char preview_err[96] = {0}; + esp_err_t preview_rc = screen_preview_show_raster(raster, + raster_len, + width, + height, + SCREEN_PREVIEW_TIMEOUT_MS, + preview_err, + sizeof(preview_err)); + if (preview_rc == ESP_OK) { + ESP_LOGI(TAG, + "screen preview updated (%s), size=%ux%u", + source != NULL ? source : "unknown", + (unsigned)width, + (unsigned)height); + return; + } + if (preview_rc == ESP_ERR_NOT_SUPPORTED) { + return; + } + + ESP_LOGW(TAG, + "screen preview skipped (%s), rc=0x%x, msg=%s", + source != NULL ? source : "unknown", + (unsigned)preview_rc, + preview_err[0] != '\0' ? preview_err : "preview failed"); +} + static esp_err_t rest_server_print_image_binary(httpd_req_t *req) { bool scale_to_width = true; uint8_t threshold = 160; @@ -419,6 +456,7 @@ static esp_err_t rest_server_print_image_binary(httpd_req_t *req) { (unsigned)width, (unsigned)height, (unsigned)raster_len); + rest_server_try_preview_raster("upload", raster, raster_len, width, height); esp_err_t submit_rc = rest_server_print_submit_raster_job_and_reply(req, raster, @@ -562,6 +600,7 @@ static esp_err_t rest_server_print_image_generate(httpd_req_t *req) { (unsigned)width, (unsigned)height, (unsigned)raster_len); + rest_server_try_preview_raster("generated", raster, raster_len, width, height); char warning[160] = {0}; if (request_id[0] != '\0') { diff --git a/components/domain/CMakeLists.txt b/components/domain/CMakeLists.txt index 09f9cf5..a7761f2 100644 --- a/components/domain/CMakeLists.txt +++ b/components/domain/CMakeLists.txt @@ -7,6 +7,7 @@ idf_component_register( "src/raster_tools.c" "src/raster_tools_image_qr.c" "src/image_generation.c" + "src/screen_preview.c" "src/system_runtime.c" "src/voice_interaction.c" "src/voice_interaction_common.c" diff --git a/components/domain/include/domain.h b/components/domain/include/domain.h index fa32c95..d3bceea 100644 --- a/components/domain/include/domain.h +++ b/components/domain/include/domain.h @@ -100,6 +100,15 @@ esp_err_t image_generation_generate_png(const image_generation_request_t *req, char *err, size_t err_len); +// ---------- screen_preview ---------- +esp_err_t screen_preview_show_raster(const uint8_t *raster, + size_t raster_len, + uint16_t width, + uint16_t height, + uint32_t timeout_ms, + char *err, + size_t err_len); + // ---------- printer_protocol ---------- typedef enum { PRINT_JOB_STATE_NONE = 0, diff --git a/components/domain/src/screen_preview.c b/components/domain/src/screen_preview.c new file mode 100644 index 0000000..b7971ae --- /dev/null +++ b/components/domain/src/screen_preview.c @@ -0,0 +1,12 @@ +#include "domain.h" + +esp_err_t screen_preview_show_raster(const uint8_t *raster, + size_t raster_len, + uint16_t width, + uint16_t height, + uint32_t timeout_ms, + char *err, + size_t err_len) +{ + return platform_display_show_raster_1bpp(raster, raster_len, width, height, timeout_ms, err, err_len); +} diff --git a/components/lcd/CMakeLists.txt b/components/lcd/CMakeLists.txt index 952d0ed..d627d8b 100644 --- a/components/lcd/CMakeLists.txt +++ b/components/lcd/CMakeLists.txt @@ -12,6 +12,7 @@ idf_component_register( driver esp_driver_gpio esp_driver_spi + lvgl espressif__esp_jpeg EMBED_FILES "assets/lcd_demo.jpg" diff --git a/components/lcd/include/lcd.h b/components/lcd/include/lcd.h index ec6bf03..21cc19b 100644 --- a/components/lcd/include/lcd.h +++ b/components/lcd/include/lcd.h @@ -1,5 +1,8 @@ #pragma once +#include +#include + #include "esp_err.h" #ifdef __cplusplus @@ -8,6 +11,13 @@ extern "C" { esp_err_t lcd_module_init(void); esp_err_t lcd_module_start_demo(void); +esp_err_t lcd_module_show_raster_1bpp(const uint8_t *raster, + size_t raster_len, + uint16_t width, + uint16_t height, + uint32_t timeout_ms, + char *err, + size_t err_len); void lcd_module_deinit(void); #ifdef __cplusplus diff --git a/components/lcd/src/lcd_demo.c b/components/lcd/src/lcd_demo.c index b27565e..18d5395 100644 --- a/components/lcd/src/lcd_demo.c +++ b/components/lcd/src/lcd_demo.c @@ -1,30 +1,37 @@ #include "lcd.h" +#include #include +#include +#include +#include #include "esp_check.h" #include "esp_heap_caps.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "lcd_pretty_effect.h" +#include "freertos/semphr.h" +#include "lvgl.h" #include "sdkconfig.h" #include "st7789p3.h" -static const char *TAG = "lcd_demo"; +static const char *TAG = "lcd_module"; #define ST7789_SPI_HOST SPI2_HOST -#define LCD_DEMO_H_RES 320 -#define LCD_DEMO_V_RES 240 -#define LCD_DEMO_PARALLEL_LINES 16 -#define LCD_DEMO_TASK_STACK_SIZE (6 * 1024) -#define LCD_DEMO_TASK_PRIORITY (tskIDLE_PRIORITY + 1) -#define LCD_DEMO_TASK_CORE_ID 1 +#define LCD_PANEL_H_RES 320 +#define LCD_PANEL_V_RES 240 +#define LCD_PREVIEW_TIMEOUT_MS_DEFAULT 200 +#define LCD_LOCK_TIMEOUT_MS 1000 +#define LCD_DRAW_LINES 20 #ifndef CONFIG_TQ_SCREEN_SPI_MODE #define CONFIG_TQ_SCREEN_SPI_MODE 0 #endif +#ifndef CONFIG_TQ_SCREEN_DRAW_LINES +#define CONFIG_TQ_SCREEN_DRAW_LINES LCD_DRAW_LINES +#endif + #ifndef CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH #define CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH 1 #endif @@ -37,7 +44,18 @@ static const char *TAG = "lcd_demo"; typedef struct { st7789p3_handle_t panel; - TaskHandle_t demo_task; + SemaphoreHandle_t lock; + lv_disp_draw_buf_t lv_draw_buf; + lv_disp_drv_t lv_drv; + lv_disp_t *lv_disp; + lv_obj_t *preview_img; + lv_obj_t *demo_label; + lv_color_t *draw_buf_1; + lv_color_t *draw_buf_2; + uint16_t *preview_pixels; + lv_img_dsc_t preview_dsc; + bool lvgl_inited; + bool lvgl_ready; bool initialized; } lcd_state_t; @@ -51,70 +69,238 @@ static int resolve_reset_pin(void) return CONFIG_TQ_SCREEN_BACKLIGHT_PIN; } -static void lcd_demo_task(void *arg) +static void lcd_fill_err(char *err, size_t err_len, const char *msg) { - st7789p3_handle_t panel = (st7789p3_handle_t)arg; - uint16_t *lines[2] = {0}; - const size_t line_buffer_bytes = LCD_DEMO_H_RES * LCD_DEMO_PARALLEL_LINES * sizeof(uint16_t); - int frame = 0; - int draw_buffer_index = 0; + if (err != NULL && err_len > 0) { + snprintf(err, err_len, "%s", msg != NULL ? msg : "unknown"); + } +} - esp_err_t err = platform_lcd_pretty_effect_init(); +static bool lcd_lock_take(uint32_t timeout_ms) +{ + if (s_lcd.lock == NULL) { + return false; + } + TickType_t ticks = pdMS_TO_TICKS(timeout_ms); + if (timeout_ms > 0 && ticks == 0) { + ticks = 1; + } + return xSemaphoreTake(s_lcd.lock, ticks) == pdTRUE; +} + +static void lcd_lock_give(void) +{ + if (s_lcd.lock != NULL) { + xSemaphoreGive(s_lcd.lock); + } +} + +static void *lcd_alloc_fb(size_t bytes) +{ + void *buf = heap_caps_malloc(bytes, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); + if (buf == NULL) { + buf = heap_caps_malloc(bytes, MALLOC_CAP_8BIT); + } + return buf; +} + +static void lcd_free_lvgl_buffers(void) +{ + if (s_lcd.draw_buf_1 != NULL) { + heap_caps_free(s_lcd.draw_buf_1); + s_lcd.draw_buf_1 = NULL; + } + if (s_lcd.draw_buf_2 != NULL) { + heap_caps_free(s_lcd.draw_buf_2); + s_lcd.draw_buf_2 = NULL; + } +} + +static void lcd_lvgl_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_map) +{ + st7789p3_handle_t panel = (st7789p3_handle_t)disp_drv->user_data; + if (panel == NULL || area == NULL || color_map == NULL) { + lv_disp_flush_ready(disp_drv); + return; + } + if (area->x1 < 0 || area->y1 < 0 || area->x2 < area->x1 || area->y2 < area->y1) { + lv_disp_flush_ready(disp_drv); + return; + } + + esp_err_t err = st7789p3_draw_bitmap(panel, + (uint16_t)area->x1, + (uint16_t)area->y1, + (uint16_t)(area->x2 + 1), + (uint16_t)(area->y2 + 1), + (const uint16_t *)color_map); if (err != ESP_OK) { - ESP_LOGE(TAG, "pretty effect init failed: %s", esp_err_to_name(err)); - goto exit; + ESP_LOGW(TAG, "lvgl flush failed: %s", esp_err_to_name(err)); + } + lv_disp_flush_ready(disp_drv); +} + +static esp_err_t lcd_lvgl_init_locked(void) +{ + if (s_lcd.lvgl_ready) { + return ESP_OK; } - for (int i = 0; i < 2; i++) { - lines[i] = heap_caps_malloc(line_buffer_bytes, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); - if (lines[i] == NULL) { - lines[i] = heap_caps_malloc(line_buffer_bytes, MALLOC_CAP_8BIT); - } - if (lines[i] == NULL) { - ESP_LOGE(TAG, "lcd demo line buffer alloc failed"); - goto exit; + if (!s_lcd.lvgl_inited) { + lv_init(); + s_lcd.lvgl_inited = true; + } + + const size_t draw_buf_pixels = LCD_PANEL_H_RES * CONFIG_TQ_SCREEN_DRAW_LINES; + const size_t draw_buf_bytes = draw_buf_pixels * sizeof(lv_color_t); + s_lcd.draw_buf_1 = (lv_color_t *)lcd_alloc_fb(draw_buf_bytes); + if (s_lcd.draw_buf_1 == NULL) { + return ESP_ERR_NO_MEM; + } + s_lcd.draw_buf_2 = (lv_color_t *)lcd_alloc_fb(draw_buf_bytes); + if (s_lcd.draw_buf_2 == NULL) { + lcd_free_lvgl_buffers(); + return ESP_ERR_NO_MEM; + } + + lv_disp_draw_buf_init(&s_lcd.lv_draw_buf, s_lcd.draw_buf_1, s_lcd.draw_buf_2, draw_buf_pixels); + + lv_disp_drv_init(&s_lcd.lv_drv); + s_lcd.lv_drv.hor_res = LCD_PANEL_H_RES; + s_lcd.lv_drv.ver_res = LCD_PANEL_V_RES; + s_lcd.lv_drv.flush_cb = lcd_lvgl_flush_cb; + s_lcd.lv_drv.draw_buf = &s_lcd.lv_draw_buf; + s_lcd.lv_drv.user_data = s_lcd.panel; + s_lcd.lv_disp = lv_disp_drv_register(&s_lcd.lv_drv); + if (s_lcd.lv_disp == NULL) { + lcd_free_lvgl_buffers(); + ESP_LOGE(TAG, "lvgl display register failed"); + return ESP_FAIL; + } + + lv_obj_t *screen = lv_scr_act(); + lv_obj_set_style_bg_color(screen, lv_color_black(), LV_PART_MAIN); + lv_obj_set_style_bg_opa(screen, LV_OPA_COVER, LV_PART_MAIN); + lv_obj_clear_flag(screen, LV_OBJ_FLAG_SCROLLABLE); + + s_lcd.preview_img = lv_img_create(screen); + lv_obj_add_flag(s_lcd.preview_img, LV_OBJ_FLAG_HIDDEN); + lv_obj_center(s_lcd.preview_img); + + s_lcd.lvgl_ready = true; + lv_refr_now(s_lcd.lv_disp); + return ESP_OK; +} + +static void lcd_calc_preview_size(uint16_t src_w, uint16_t src_h, uint16_t *out_w, uint16_t *out_h) +{ + if (src_w == 0 || src_h == 0 || out_w == NULL || out_h == NULL) { + return; + } + + if (src_w <= LCD_PANEL_H_RES && src_h <= LCD_PANEL_V_RES) { + *out_w = src_w; + *out_h = src_h; + return; + } + + if ((uint64_t)src_w * LCD_PANEL_V_RES > (uint64_t)src_h * LCD_PANEL_H_RES) { + *out_w = LCD_PANEL_H_RES; + *out_h = (uint16_t)(((uint32_t)src_h * LCD_PANEL_H_RES) / src_w); + } else { + *out_h = LCD_PANEL_V_RES; + *out_w = (uint16_t)(((uint32_t)src_w * LCD_PANEL_V_RES) / src_h); + } + if (*out_w == 0) { + *out_w = 1; + } + if (*out_h == 0) { + *out_h = 1; + } +} + +static esp_err_t lcd_render_raster_to_rgb565(const uint8_t *raster, + size_t raster_len, + uint16_t src_w, + uint16_t src_h, + uint16_t **out_pixels, + uint16_t *out_w, + uint16_t *out_h, + char *err, + size_t err_len) +{ + if (raster == NULL || src_w == 0 || src_h == 0 || out_pixels == NULL || out_w == NULL || out_h == NULL) { + lcd_fill_err(err, err_len, "invalid raster"); + return ESP_ERR_INVALID_ARG; + } + + size_t bytes_per_row = ((size_t)src_w + 7u) / 8u; + size_t needed_len = bytes_per_row * src_h; + if (raster_len < needed_len) { + lcd_fill_err(err, err_len, "raster length mismatch"); + return ESP_ERR_INVALID_SIZE; + } + + lcd_calc_preview_size(src_w, src_h, out_w, out_h); + size_t pixel_count = (size_t)(*out_w) * (size_t)(*out_h); + if (pixel_count == 0 || pixel_count > SIZE_MAX / sizeof(uint16_t)) { + lcd_fill_err(err, err_len, "preview size invalid"); + return ESP_ERR_INVALID_SIZE; + } + + uint16_t *pixels = (uint16_t *)heap_caps_malloc(pixel_count * sizeof(uint16_t), + MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (pixels == NULL) { + pixels = (uint16_t *)heap_caps_malloc(pixel_count * sizeof(uint16_t), MALLOC_CAP_8BIT); + } + if (pixels == NULL) { + lcd_fill_err(err, err_len, "no memory"); + return ESP_ERR_NO_MEM; + } + + for (uint16_t y = 0; y < *out_h; ++y) { + uint16_t src_y = (uint16_t)(((uint32_t)y * src_h) / (*out_h)); + size_t row_base = (size_t)src_y * bytes_per_row; + size_t out_row = (size_t)y * (*out_w); + for (uint16_t x = 0; x < *out_w; ++x) { + uint16_t src_x = (uint16_t)(((uint32_t)x * src_w) / (*out_w)); + size_t byte_idx = row_base + (src_x / 8u); + uint8_t bit = (uint8_t)(7u - (src_x % 8u)); + bool is_black = (raster[byte_idx] & (uint8_t)(1u << bit)) != 0; + pixels[out_row + x] = is_black ? 0x0000 : 0xFFFF; } } - ESP_LOGI(TAG, "ST7789P3 pretty effect demo started"); - while (true) { - frame++; - for (int y = 0; y < LCD_DEMO_V_RES; y += LCD_DEMO_PARALLEL_LINES) { - int lines_to_draw = LCD_DEMO_V_RES - y; - if (lines_to_draw > LCD_DEMO_PARALLEL_LINES) { - lines_to_draw = LCD_DEMO_PARALLEL_LINES; - } - - platform_lcd_pretty_effect_calc_lines(lines[draw_buffer_index], y, frame, lines_to_draw); - err = st7789p3_draw_bitmap(panel, 0, y, LCD_DEMO_H_RES, y + lines_to_draw, lines[draw_buffer_index]); - if (err != ESP_OK) { - ESP_LOGE(TAG, "lcd demo draw failed: %s", esp_err_to_name(err)); - goto exit; - } - - draw_buffer_index ^= 1; - vTaskDelay(pdMS_TO_TICKS(1)); - } - } - -exit: - if (lines[0] != NULL) { - heap_caps_free(lines[0]); - } - if (lines[1] != NULL) { - heap_caps_free(lines[1]); - } - s_lcd.demo_task = NULL; - vTaskDelete(NULL); + *out_pixels = pixels; + return ESP_OK; } void lcd_module_deinit(void) { #if CONFIG_TQ_SCREEN_ENABLE - if (s_lcd.demo_task != NULL) { - vTaskDelete(s_lcd.demo_task); - s_lcd.demo_task = NULL; + if (s_lcd.lock != NULL) { + (void)xSemaphoreTake(s_lcd.lock, portMAX_DELAY); } + + if (s_lcd.lv_disp != NULL) { + lv_disp_remove(s_lcd.lv_disp); + s_lcd.lv_disp = NULL; + } + if (s_lcd.preview_pixels != NULL) { + heap_caps_free(s_lcd.preview_pixels); + s_lcd.preview_pixels = NULL; + } + lcd_free_lvgl_buffers(); + s_lcd.preview_img = NULL; + s_lcd.demo_label = NULL; + s_lcd.lvgl_ready = false; + + if (s_lcd.lock != NULL) { + xSemaphoreGive(s_lcd.lock); + vSemaphoreDelete(s_lcd.lock); + s_lcd.lock = NULL; + } + if (s_lcd.panel != NULL) { (void)st7789p3_del(s_lcd.panel); s_lcd.panel = NULL; @@ -144,11 +330,11 @@ esp_err_t lcd_module_init(void) lcd_cfg.backlight_on_level = ST7789_BACKLIGHT_ON_LEVEL; lcd_cfg.pclk_hz = CONFIG_TQ_SCREEN_PIXEL_CLOCK_HZ; lcd_cfg.spi_mode = CONFIG_TQ_SCREEN_SPI_MODE; - lcd_cfg.h_res = LCD_DEMO_H_RES; - lcd_cfg.v_res = LCD_DEMO_V_RES; + lcd_cfg.h_res = LCD_PANEL_H_RES; + lcd_cfg.v_res = LCD_PANEL_V_RES; lcd_cfg.madctl_val = ST7789P3_MADCTL_MX | ST7789P3_MADCTL_MV; lcd_cfg.invert_color = true; - lcd_cfg.max_transfer_sz = LCD_DEMO_PARALLEL_LINES * LCD_DEMO_H_RES * sizeof(uint16_t) + 8; + lcd_cfg.max_transfer_sz = CONFIG_TQ_SCREEN_DRAW_LINES * LCD_PANEL_H_RES * sizeof(uint16_t) + 8; esp_err_t err = st7789p3_new_panel(&lcd_cfg, &s_lcd.panel); if (err != ESP_OK) { @@ -161,6 +347,23 @@ esp_err_t lcd_module_init(void) return err; } + s_lcd.lock = xSemaphoreCreateMutex(); + if (s_lcd.lock == NULL) { + lcd_module_deinit(); + return ESP_ERR_NO_MEM; + } + + if (!lcd_lock_take(LCD_LOCK_TIMEOUT_MS)) { + lcd_module_deinit(); + return ESP_ERR_TIMEOUT; + } + err = lcd_lvgl_init_locked(); + lcd_lock_give(); + if (err != ESP_OK) { + lcd_module_deinit(); + return err; + } + s_lcd.initialized = true; ESP_LOGI(TAG, "st7789p3 ready: spi_host=%d mosi=%d clk=%d miso=%d dc=%d cs=%d rst=%d bk=%d pclk=%u", @@ -177,24 +380,103 @@ esp_err_t lcd_module_init(void) #endif } +esp_err_t lcd_module_show_raster_1bpp(const uint8_t *raster, + size_t raster_len, + uint16_t width, + uint16_t height, + uint32_t timeout_ms, + char *err, + size_t err_len) +{ +#if !CONFIG_TQ_SCREEN_ENABLE + return ESP_ERR_NOT_SUPPORTED; +#else + ESP_RETURN_ON_ERROR(lcd_module_init(), TAG, "display init failed"); + + if (timeout_ms == 0) { + timeout_ms = LCD_PREVIEW_TIMEOUT_MS_DEFAULT; + } + + uint16_t *preview_pixels = NULL; + uint16_t preview_w = 0; + uint16_t preview_h = 0; + esp_err_t convert_err = lcd_render_raster_to_rgb565(raster, + raster_len, + width, + height, + &preview_pixels, + &preview_w, + &preview_h, + err, + err_len); + if (convert_err != ESP_OK) { + return convert_err; + } + + if (!lcd_lock_take(timeout_ms)) { + heap_caps_free(preview_pixels); + lcd_fill_err(err, err_len, "display lock timeout"); + return ESP_ERR_TIMEOUT; + } + + if (s_lcd.demo_label != NULL) { + lv_obj_del(s_lcd.demo_label); + s_lcd.demo_label = NULL; + } + + if (s_lcd.preview_pixels != NULL) { + heap_caps_free(s_lcd.preview_pixels); + } + s_lcd.preview_pixels = preview_pixels; + + memset(&s_lcd.preview_dsc, 0, sizeof(s_lcd.preview_dsc)); + s_lcd.preview_dsc.header.always_zero = 0; + s_lcd.preview_dsc.header.w = preview_w; + s_lcd.preview_dsc.header.h = preview_h; + s_lcd.preview_dsc.header.cf = LV_IMG_CF_TRUE_COLOR; + s_lcd.preview_dsc.data_size = (uint32_t)((size_t)preview_w * preview_h * sizeof(uint16_t)); + s_lcd.preview_dsc.data = (const uint8_t *)s_lcd.preview_pixels; + + lv_obj_t *screen = lv_scr_act(); + lv_obj_set_style_bg_color(screen, lv_color_white(), LV_PART_MAIN); + lv_obj_set_style_bg_opa(screen, LV_OPA_COVER, LV_PART_MAIN); + lv_img_set_src(s_lcd.preview_img, &s_lcd.preview_dsc); + lv_obj_clear_flag(s_lcd.preview_img, LV_OBJ_FLAG_HIDDEN); + lv_obj_center(s_lcd.preview_img); + lv_refr_now(s_lcd.lv_disp); + + lcd_lock_give(); + return ESP_OK; +#endif +} + esp_err_t lcd_module_start_demo(void) { #if !CONFIG_TQ_SCREEN_ENABLE return ESP_ERR_NOT_SUPPORTED; #else ESP_RETURN_ON_ERROR(lcd_module_init(), TAG, "display init failed"); - if (s_lcd.demo_task != NULL) { - return ESP_OK; + if (!lcd_lock_take(LCD_LOCK_TIMEOUT_MS)) { + return ESP_ERR_TIMEOUT; } - BaseType_t rc = xTaskCreatePinnedToCore(lcd_demo_task, - "lcd_demo", - LCD_DEMO_TASK_STACK_SIZE, - (void *)s_lcd.panel, - LCD_DEMO_TASK_PRIORITY, - &s_lcd.demo_task, - LCD_DEMO_TASK_CORE_ID); - ESP_RETURN_ON_FALSE(rc == pdPASS, ESP_ERR_NO_MEM, TAG, "lcd demo task create failed"); + if (s_lcd.preview_img != NULL) { + lv_obj_add_flag(s_lcd.preview_img, LV_OBJ_FLAG_HIDDEN); + } + + lv_obj_t *screen = lv_scr_act(); + lv_obj_set_style_bg_color(screen, lv_color_hex(0x1D3557), LV_PART_MAIN); + lv_obj_set_style_bg_opa(screen, LV_OPA_COVER, LV_PART_MAIN); + + if (s_lcd.demo_label == NULL) { + s_lcd.demo_label = lv_label_create(screen); + } + lv_label_set_text(s_lcd.demo_label, "LCD + LVGL ready"); + lv_obj_set_style_text_color(s_lcd.demo_label, lv_color_white(), LV_PART_MAIN); + lv_obj_center(s_lcd.demo_label); + lv_refr_now(s_lcd.lv_disp); + lcd_lock_give(); + return ESP_OK; #endif } diff --git a/components/platform/include/platform.h b/components/platform/include/platform.h index 6dee0b4..618e933 100644 --- a/components/platform/include/platform.h +++ b/components/platform/include/platform.h @@ -75,6 +75,13 @@ esp_err_t platform_direct_printer_gap_move(uint32_t timeout_ms, char *err, size_ // ---------- display_st7789 ---------- esp_err_t platform_display_init(void); esp_err_t platform_display_show_test_pattern(void); +esp_err_t platform_display_show_raster_1bpp(const uint8_t *raster, + size_t raster_len, + uint16_t width, + uint16_t height, + uint32_t timeout_ms, + char *err, + size_t err_len); void platform_display_deinit(void); // ---------- voice_audio ---------- diff --git a/components/platform/src/display_st7789.c b/components/platform/src/display_st7789.c index 2b5ddea..8bf5cac 100644 --- a/components/platform/src/display_st7789.c +++ b/components/platform/src/display_st7789.c @@ -16,3 +16,14 @@ esp_err_t platform_display_show_test_pattern(void) { return lcd_module_start_demo(); } + +esp_err_t platform_display_show_raster_1bpp(const uint8_t *raster, + size_t raster_len, + uint16_t width, + uint16_t height, + uint32_t timeout_ms, + char *err, + size_t err_len) +{ + return lcd_module_show_raster_1bpp(raster, raster_len, width, height, timeout_ms, err, err_len); +} diff --git a/dependencies.lock b/dependencies.lock index 95fa7a2..a626b59 100644 --- a/dependencies.lock +++ b/dependencies.lock @@ -67,6 +67,13 @@ dependencies: source: type: idf version: 5.5.1 + lvgl/lvgl: + component_hash: d7c1ac037ae6e85d94897f807d6e7ba0946a83e720074fc95a4f6241da9f9f53 + dependencies: [] + source: + registry_url: https://components.espressif.com/ + type: service + version: 8.4.0 direct_dependencies: - espressif/esp_audio_codec - espressif/esp_codec_dev @@ -74,6 +81,7 @@ direct_dependencies: - espressif/esp_websocket_client - espressif/libpng - idf -manifest_hash: 5900d46fda5cdfbcdd8ddd277f3cf83d1ca7f79f4a98eb428297e57174c5181c +- lvgl/lvgl +manifest_hash: 4f3b822559c14ef8306b9123c3886e4a6b753913eb83fc226790b7e4d9dcd674 target: esp32s3 version: 2.0.0 diff --git a/main/idf_component.yml b/main/idf_component.yml index 663bfd6..00d7190 100644 --- a/main/idf_component.yml +++ b/main/idf_component.yml @@ -19,3 +19,4 @@ dependencies: espressif/esp_audio_codec: ^2.4.1 espressif/esp_jpeg: ">=1.0.2" espressif/libpng: ^1.6.55 + lvgl/lvgl: ^8.3.11