在主应用中更新显示驱动和 MQTT 主题

- 将显示驱动从 lvgl_st7735s_use 改为 lvgl_st7789_use,在 CMakeLists.txt 和 main.c 中。
- 将 MQTT 警报主题从“topic/alert/esp32_iothome_001”更新为“topic/alert/esp32_iothome_002”。
- 重构 UI 任务,去除周期性屏幕切换逻辑,专注于界面刷新。
- 调整错误处理,使新的显示驱动程序用于错误信息。
This commit is contained in:
Wang Beihong
2026-03-14 15:44:01 +08:00
parent 7ec1ed9111
commit bec31c01ce
12 changed files with 1686 additions and 249 deletions

View File

@@ -0,0 +1,257 @@
#include <stdio.h>
#include <stdlib.h>
#include "lvgl_st7789_use.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lvgl_port.h"
static const char *TAG = "lvgl_st7789_use";
static esp_lcd_panel_io_handle_t lcd_io = NULL;
static esp_lcd_panel_handle_t lcd_panel = NULL;
static lv_display_t *lvgl_disp = NULL;
static lv_obj_t *s_center_label = NULL;
#if EXAMPLE_LCD_ENABLE_COLOR_TEST
static esp_err_t app_lcd_color_test(void)
{
const size_t pixels = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES;
uint16_t *frame = calloc(pixels, sizeof(uint16_t));
ESP_RETURN_ON_FALSE(frame != NULL, ESP_ERR_NO_MEM, TAG, "分配测试帧缓冲失败");
for (int y = 0; y < EXAMPLE_LCD_V_RES; y++) {
for (int x = 0; x < EXAMPLE_LCD_H_RES; x++) {
uint16_t color;
if (x < EXAMPLE_LCD_H_RES / 3) {
color = 0xF800; // 红
} else if (x < (EXAMPLE_LCD_H_RES * 2) / 3) {
color = 0x07E0; // 绿
} else {
color = 0x001F; // 蓝
}
frame[y * EXAMPLE_LCD_H_RES + x] = color;
}
}
esp_err_t err = esp_lcd_panel_draw_bitmap(lcd_panel, 0, 0, EXAMPLE_LCD_H_RES, EXAMPLE_LCD_V_RES, frame);
free(frame);
ESP_RETURN_ON_ERROR(err, TAG, "三色测试绘制失败");
ESP_LOGI(TAG, "LCD三色测试图已发送");
return ESP_OK;
}
#endif
/**
* @brief 初始化LCD硬件和SPI接口
*
* 该函数负责初始化LCD所需的GPIO、SPI总线并配置LCD面板
* 包括背光控制、SPI总线配置、面板IO配置和面板驱动安装
*
* @return esp_err_t 初始化结果ESP_OK表示成功
*/
static esp_err_t app_lcd_init(void)
{
esp_err_t ret = ESP_OK;
if (EXAMPLE_LCD_GPIO_BL != GPIO_NUM_NC) {
gpio_config_t bk_gpio_config = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << EXAMPLE_LCD_GPIO_BL
};
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
} else {
ESP_LOGW(TAG, "背光引脚未配置(GPIO_NUM_NC)跳过背光GPIO初始化");
}
ESP_LOGI(TAG, "初始化SPI总线");
const spi_bus_config_t buscfg = {
.sclk_io_num = EXAMPLE_LCD_GPIO_SCLK,
.mosi_io_num = EXAMPLE_LCD_GPIO_MOSI,
.miso_io_num = GPIO_NUM_NC,
.quadwp_io_num = GPIO_NUM_NC,
.quadhd_io_num = GPIO_NUM_NC,
.max_transfer_sz = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_DRAW_BUFF_HEIGHT * sizeof(uint16_t),
};
ESP_RETURN_ON_ERROR(spi_bus_initialize(EXAMPLE_LCD_SPI_NUM, &buscfg, SPI_DMA_CH_AUTO), TAG, "SPI初始化失败");
ESP_LOGI(TAG, "安装面板IO");
const esp_lcd_panel_io_spi_config_t io_config = {
.dc_gpio_num = EXAMPLE_LCD_GPIO_DC,
.cs_gpio_num = EXAMPLE_LCD_GPIO_CS,
.pclk_hz = EXAMPLE_LCD_PIXEL_CLK_HZ,
.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
.lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
.spi_mode = 0,
.trans_queue_depth = 10,
};
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)EXAMPLE_LCD_SPI_NUM, &io_config, &lcd_io), err, TAG, "创建面板IO失败");
ESP_LOGI(TAG, "安装LCD驱动");
const esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = EXAMPLE_LCD_GPIO_RST,
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(6, 0, 0)
.rgb_endian = EXAMPLE_LCD_COLOR_ORDER_BGR ? LCD_RGB_ENDIAN_BGR : LCD_RGB_ENDIAN_RGB,
#else
.rgb_ele_order = EXAMPLE_LCD_COLOR_ORDER_BGR ? LCD_RGB_ELEMENT_ORDER_BGR : LCD_RGB_ELEMENT_ORDER_RGB,
#endif
.bits_per_pixel = EXAMPLE_LCD_BITS_PER_PIXEL,
};
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_st7789(lcd_io, &panel_config, &lcd_panel), err, TAG, "创建面板失败");
ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(lcd_panel), err, TAG, "面板复位失败");
ESP_GOTO_ON_ERROR(esp_lcd_panel_init(lcd_panel), err, TAG, "面板初始化失败");
ESP_GOTO_ON_ERROR(esp_lcd_panel_swap_xy(lcd_panel, false), err, TAG, "设置面板swap_xy失败");
ESP_GOTO_ON_ERROR(esp_lcd_panel_mirror(lcd_panel, false, false), err, TAG, "设置面板镜像失败");
ESP_GOTO_ON_ERROR(esp_lcd_panel_set_gap(lcd_panel, EXAMPLE_LCD_GAP_X, EXAMPLE_LCD_GAP_Y), err, TAG, "设置显示偏移失败");
ESP_LOGI(TAG, "面板基准参数已应用: gap=(%d,%d)", EXAMPLE_LCD_GAP_X, EXAMPLE_LCD_GAP_Y);
ESP_GOTO_ON_ERROR(esp_lcd_panel_invert_color(lcd_panel, EXAMPLE_LCD_INVERT_COLOR), err, TAG, "设置反色失败");
ESP_GOTO_ON_ERROR(esp_lcd_panel_disp_on_off(lcd_panel, true), err, TAG, "打开显示失败");
if (EXAMPLE_LCD_GPIO_BL != GPIO_NUM_NC) {
ESP_RETURN_ON_ERROR(gpio_set_level(EXAMPLE_LCD_GPIO_BL, EXAMPLE_LCD_BL_ON_LEVEL), TAG, "背光引脚置位失败");
ESP_LOGI(TAG, "背光已打开,电平=%d", EXAMPLE_LCD_BL_ON_LEVEL);
}
ESP_LOGI(TAG, "颜色参数: order=%s invert=%d swap_bytes=%d",
EXAMPLE_LCD_COLOR_ORDER_BGR ? "BGR" : "RGB",
EXAMPLE_LCD_INVERT_COLOR,
EXAMPLE_LCD_SWAP_BYTES);
return ret;
// 错误处理标签,用于清理资源
err:
if (lcd_panel) {
esp_lcd_panel_del(lcd_panel);
lcd_panel = NULL;
}
if (lcd_io) {
esp_lcd_panel_io_del(lcd_io);
lcd_io = NULL;
}
spi_bus_free(EXAMPLE_LCD_SPI_NUM);
return ret;
}
/**
* @brief 初始化LVGL图形库
*
* 该函数负责初始化LVGL库并配置显示设备
* 包括LVGL任务配置、显示缓冲区配置和旋转设置
*
* @return esp_err_t 初始化结果ESP_OK表示成功
*/
static esp_err_t app_lvgl_init(void)
{
const lvgl_port_cfg_t lvgl_cfg = {
.task_priority = 4,
.task_stack = 4096,
.task_affinity = -1,
.task_max_sleep_ms = 500,
.timer_period_ms = 5
};
ESP_RETURN_ON_ERROR(lvgl_port_init(&lvgl_cfg), TAG, "LVGL端口初始化失败");
ESP_LOGI(TAG, "添加LCD屏幕");
const lvgl_port_display_cfg_t disp_cfg = {
.io_handle = lcd_io,
.panel_handle = lcd_panel,
.buffer_size = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_DRAW_BUFF_HEIGHT,
.double_buffer = EXAMPLE_LCD_DRAW_BUFF_DOUBLE,
.hres = EXAMPLE_LCD_H_RES,
.vres = EXAMPLE_LCD_V_RES,
.monochrome = false,
#if LVGL_VERSION_MAJOR >= 9
.color_format = LV_COLOR_FORMAT_RGB565,
#endif
.rotation = {
.swap_xy = EXAMPLE_LCD_ROT_SWAP_XY,
.mirror_x = EXAMPLE_LCD_ROT_MIRROR_X,
.mirror_y = EXAMPLE_LCD_ROT_MIRROR_Y,
},
.flags = {
.buff_dma = true,
#if LVGL_VERSION_MAJOR >= 9
.swap_bytes = EXAMPLE_LCD_SWAP_BYTES,
#endif
}};
lvgl_disp = lvgl_port_add_disp(&disp_cfg);
ESP_RETURN_ON_FALSE(lvgl_disp != NULL, ESP_FAIL, TAG, "添加LVGL显示设备失败");
ESP_LOGI(TAG, "LVGL旋转已应用: swap_xy=%d mirror_x=%d mirror_y=%d",
EXAMPLE_LCD_ROT_SWAP_XY, EXAMPLE_LCD_ROT_MIRROR_X, EXAMPLE_LCD_ROT_MIRROR_Y);
return ESP_OK;
}
/**
* @brief 创建并显示LVGL主界面
*
* 该函数负责创建LVGL的用户界面元素包括图像、标签和按钮
* 并设置它们的位置和属性
*/
static void app_main_display(void)
{
lv_obj_t *scr = lv_scr_act();
lvgl_port_lock(0);
lv_obj_set_style_bg_color(scr, lv_color_white(), 0);
lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0);
s_center_label = lv_label_create(scr);
lv_label_set_text(s_center_label, "BotanicalBuddy\nloading...");
lv_label_set_recolor(s_center_label, false);
lv_label_set_long_mode(s_center_label, LV_LABEL_LONG_WRAP);
lv_obj_set_size(s_center_label, EXAMPLE_LCD_H_RES - 6, EXAMPLE_LCD_V_RES - 6);
lv_obj_set_style_text_color(s_center_label, lv_color_black(), 0);
lv_obj_set_style_text_font(s_center_label, &lv_font_montserrat_14, 0);
lv_obj_set_style_text_align(s_center_label, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_pad_all(s_center_label, 0, 0);
lv_obj_align(s_center_label, LV_ALIGN_CENTER, 0, 0);
lvgl_port_unlock();
}
/**
* @brief 启动LVGL演示程序
*
* 该函数是程序的入口点负责初始化LCD硬件、LVGL库并显示主界面
*/
esp_err_t lvgl_st7789_set_center_text(const char *text)
{
ESP_RETURN_ON_FALSE(text != NULL, ESP_ERR_INVALID_ARG, TAG, "text is null");
ESP_RETURN_ON_FALSE(s_center_label != NULL, ESP_ERR_INVALID_STATE, TAG, "label not ready");
lvgl_port_lock(0);
lv_label_set_text(s_center_label, text);
lv_obj_align(s_center_label, LV_ALIGN_CENTER, 0, 0);
lvgl_port_unlock();
return ESP_OK;
}
esp_err_t start_lvgl_demo(void)
{
ESP_RETURN_ON_ERROR(app_lcd_init(), TAG, "LCD初始化失败");
#if EXAMPLE_LCD_ENABLE_COLOR_TEST
ESP_RETURN_ON_ERROR(app_lcd_color_test(), TAG, "LCD测试图绘制失败");
vTaskDelay(pdMS_TO_TICKS(300));
#endif
ESP_RETURN_ON_ERROR(app_lvgl_init(), TAG, "LVGL初始化失败");
app_main_display();
return ESP_OK;
}