在主应用中更新显示驱动和 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

@@ -21,7 +21,7 @@
- `main/`业务编排、控制循环、MQTT 回调对接 - `main/`业务编排、控制循环、MQTT 回调对接
- `components/wifi-connect/`:配网与路由连接 - `components/wifi-connect/`:配网与路由连接
- `components/lvgl_st7735s_use/`LCD 与 LVGL 端口 - `components/lvgl_st7789_use/`LCD 与 LVGL 端口
- `components/ui/`:界面对象与变量绑定 - `components/ui/`:界面对象与变量绑定
- `components/i2c_master_messager/`AHT30、BH1750 采集 - `components/i2c_master_messager/`AHT30、BH1750 采集
- `components/capactive_soil_moisture_sensor_V2.0/`:土壤湿度采集 - `components/capactive_soil_moisture_sensor_V2.0/`:土壤湿度采集

View File

@@ -1,4 +1,4 @@
idf_component_register(SRCS "lvgl_st7735s_use.c" idf_component_register(SRCS "lvgl_st7789_use.c"
INCLUDE_DIRS "include" INCLUDE_DIRS "include"
REQUIRES driver esp_lcd esp_lvgl_port REQUIRES driver esp_lcd esp_lvgl_port
) )

View File

@@ -1,6 +1,6 @@
# lvgl_st7735s_use 组件说明 # lvgl_st7789_use 组件说明
`lvgl_st7735s_use` 是项目中的 LCD 显示组件,基于 `esp_lcd + esp_lvgl_port`,用于快速驱动 ST77xx 系列 SPI 屏并显示 LVGL 界面。 `lvgl_st7789_use` 是项目中的 LCD 显示组件,基于 `esp_lcd + esp_lvgl_port`,用于快速驱动 ST7789 SPI 屏并显示 LVGL 界面。
--- ---
@@ -17,19 +17,19 @@
## 对外 API ## 对外 API
头文件:`include/lvgl_st7735s_use.h` 头文件:`include/lvgl_st7789_use.h`
- `esp_err_t start_lvgl_demo(void);` - `esp_err_t start_lvgl_demo(void);`
- 完成 LCD + LVGL 初始化并创建默认界面 - 完成 LCD + LVGL 初始化并创建默认界面
- `esp_err_t lvgl_st7735s_set_center_text(const char *text);` - `esp_err_t lvgl_st7789_set_center_text(const char *text);`
- 运行时更新中心标签文字(线程安全,内部已加锁) - 运行时更新中心标签文字(线程安全,内部已加锁)
--- ---
## 关键配置项(可直接改宏) ## 关键配置项(可直接改宏)
`include/lvgl_st7735s_use.h` 中: `include/lvgl_st7789_use.h` 中:
### 1) 屏幕与 SPI ### 1) 屏幕与 SPI
@@ -38,9 +38,19 @@
- `EXAMPLE_LCD_SPI_NUM` - `EXAMPLE_LCD_SPI_NUM`
- `EXAMPLE_LCD_CMD_BITS` / `EXAMPLE_LCD_PARAM_BITS` - `EXAMPLE_LCD_CMD_BITS` / `EXAMPLE_LCD_PARAM_BITS`
建议:首次点亮优先用较低时钟(如 `10MHz`),稳定后再升频 建议:ST7789 默认可先用 `20MHz`,如出现花屏或不稳定再降到 `10MHz` 复测
### 2) 方向与偏移(重点) ### 2) 颜色校准(重点)
- `EXAMPLE_LCD_COLOR_ORDER_BGR`
- `EXAMPLE_LCD_INVERT_COLOR`
- `EXAMPLE_LCD_SWAP_BYTES`
说明:
- 出现“黑色发灰、红绿蓝偏紫/互串”时,优先调整这三项。
- 建议从 `RGB + 不反色 + 不交换字节` 起步,再逐项切换。
### 3) 方向与偏移(重点)
- `EXAMPLE_LCD_GAP_X` - `EXAMPLE_LCD_GAP_X`
- `EXAMPLE_LCD_GAP_Y` - `EXAMPLE_LCD_GAP_Y`
@@ -49,10 +59,9 @@
- `EXAMPLE_LCD_ROT_MIRROR_Y` - `EXAMPLE_LCD_ROT_MIRROR_Y`
说明: 说明:
- 当前项目已验证一组可用参数(顺时针 90° + 26 偏移)。
- 若出现“文字偏移/边缘花屏/方向反了”,优先微调上述宏,不要同时在多层重复旋转。 - 若出现“文字偏移/边缘花屏/方向反了”,优先微调上述宏,不要同时在多层重复旋转。
### 3) 调试项 ### 4) 调试项
- `EXAMPLE_LCD_ENABLE_COLOR_TEST` - `EXAMPLE_LCD_ENABLE_COLOR_TEST`
- `1`:上电先画 RGB 三色测试图(便于确认硬件链路) - `1`:上电先画 RGB 三色测试图(便于确认硬件链路)
@@ -64,12 +73,12 @@
```c ```c
#include "esp_check.h" #include "esp_check.h"
#include "lvgl_st7735s_use.h" #include "lvgl_st7789_use.h"
void app_main(void) void app_main(void)
{ {
ESP_ERROR_CHECK(start_lvgl_demo()); ESP_ERROR_CHECK(start_lvgl_demo());
ESP_ERROR_CHECK(lvgl_st7735s_set_center_text("BotanicalBuddy")); ESP_ERROR_CHECK(lvgl_st7789_set_center_text("BotanicalBuddy"));
} }
``` ```
@@ -80,7 +89,7 @@ void app_main(void)
### 1) 背光亮但没有内容 ### 1) 背光亮但没有内容
优先排查: 优先排查:
- 面板型号与驱动是否匹配(ST7735S / ST7789 - 面板型号与驱动是否匹配(当前应为 ST7789
- SPI 模式、时钟是否过高 - SPI 模式、时钟是否过高
- 方向/偏移参数是否正确 - 方向/偏移参数是否正确

View File

@@ -8,7 +8,7 @@
extern "C" { extern "C" {
#endif #endif
/* LCD size */ /* LCD size (ST7789 240x240) */
#define EXAMPLE_LCD_H_RES (240) #define EXAMPLE_LCD_H_RES (240)
#define EXAMPLE_LCD_V_RES (240) #define EXAMPLE_LCD_V_RES (240)
@@ -16,7 +16,7 @@ extern "C" {
#define EXAMPLE_LCD_SPI_NUM (SPI2_HOST) // 使用SPI2主机接口进行通信 #define EXAMPLE_LCD_SPI_NUM (SPI2_HOST) // 使用SPI2主机接口进行通信
/* LCD显示参数配置 */ /* LCD显示参数配置 */
#define EXAMPLE_LCD_PIXEL_CLK_HZ (10 * 1000 * 1000) // 先用10MHz提高兼容性点亮后再逐步升频 #define EXAMPLE_LCD_PIXEL_CLK_HZ (20 * 1000 * 1000) // ST7789常用20MHz兼顾稳定性与刷新速度
/* LCD命令和参数配置 */ /* LCD命令和参数配置 */
#define EXAMPLE_LCD_CMD_BITS (8) // 命令位数为8位用于发送LCD控制命令 #define EXAMPLE_LCD_CMD_BITS (8) // 命令位数为8位用于发送LCD控制命令
@@ -27,12 +27,17 @@ extern "C" {
#define EXAMPLE_LCD_DRAW_BUFF_DOUBLE (1) // 启用双缓冲模式,提高显示流畅度 #define EXAMPLE_LCD_DRAW_BUFF_DOUBLE (1) // 启用双缓冲模式,提高显示流畅度
#define EXAMPLE_LCD_DRAW_BUFF_HEIGHT (50) // 绘图缓冲区高度为50行影响刷新性能 #define EXAMPLE_LCD_DRAW_BUFF_HEIGHT (50) // 绘图缓冲区高度为50行影响刷新性能
/* ST7789颜色配置出现偏色时优先调整这三项 */
#define EXAMPLE_LCD_COLOR_ORDER_BGR (0) // 0: RGB, 1: BGR
#define EXAMPLE_LCD_INVERT_COLOR (1) // 0: 正常色, 1: 反色
#define EXAMPLE_LCD_SWAP_BYTES (1) // 0: 不交换RGB565高低字节, 1: 交换
/* LCD背光配置 */ /* LCD背光配置 */
#define EXAMPLE_LCD_BL_ON_LEVEL (1) // 背光开启电平为高电平(1) #define EXAMPLE_LCD_BL_ON_LEVEL (1) // 背光开启电平为高电平(1)
/* LCD方向/偏移配置当前为顺时针90°并保留26偏移 */ /* LCD方向/偏移配置 */
#define EXAMPLE_LCD_GAP_X (1) #define EXAMPLE_LCD_GAP_X (0)
#define EXAMPLE_LCD_GAP_Y (26) #define EXAMPLE_LCD_GAP_Y (0)
#define EXAMPLE_LCD_ROT_SWAP_XY (1) #define EXAMPLE_LCD_ROT_SWAP_XY (1)
#define EXAMPLE_LCD_ROT_MIRROR_X (1) #define EXAMPLE_LCD_ROT_MIRROR_X (1)
#define EXAMPLE_LCD_ROT_MIRROR_Y (0) #define EXAMPLE_LCD_ROT_MIRROR_Y (0)
@@ -49,7 +54,7 @@ extern "C" {
#define EXAMPLE_LCD_GPIO_BL (GPIO_NUM_NC) #define EXAMPLE_LCD_GPIO_BL (GPIO_NUM_NC)
esp_err_t start_lvgl_demo(void); esp_err_t start_lvgl_demo(void);
esp_err_t lvgl_st7735s_set_center_text(const char *text); esp_err_t lvgl_st7789_set_center_text(const char *text);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -1,6 +1,6 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "lvgl_st7735s_use.h" #include "lvgl_st7789_use.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "driver/gpio.h" #include "driver/gpio.h"
@@ -13,7 +13,7 @@
#include "esp_lcd_panel_ops.h" #include "esp_lcd_panel_ops.h"
#include "esp_lvgl_port.h" #include "esp_lvgl_port.h"
static const char *TAG = "lvgl_st7735s_use"; static const char *TAG = "lvgl_st7789_use";
static esp_lcd_panel_io_handle_t lcd_io = NULL; static esp_lcd_panel_io_handle_t lcd_io = NULL;
static esp_lcd_panel_handle_t lcd_panel = NULL; static esp_lcd_panel_handle_t lcd_panel = NULL;
@@ -62,11 +62,15 @@ static esp_err_t app_lcd_init(void)
{ {
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
if (EXAMPLE_LCD_GPIO_BL != GPIO_NUM_NC) {
gpio_config_t bk_gpio_config = { gpio_config_t bk_gpio_config = {
.mode = GPIO_MODE_OUTPUT, .mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << EXAMPLE_LCD_GPIO_BL .pin_bit_mask = 1ULL << EXAMPLE_LCD_GPIO_BL
}; };
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config)); ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
} else {
ESP_LOGW(TAG, "背光引脚未配置(GPIO_NUM_NC)跳过背光GPIO初始化");
}
ESP_LOGI(TAG, "初始化SPI总线"); ESP_LOGI(TAG, "初始化SPI总线");
const spi_bus_config_t buscfg = { const spi_bus_config_t buscfg = {
@@ -95,9 +99,9 @@ static esp_err_t app_lcd_init(void)
const esp_lcd_panel_dev_config_t panel_config = { const esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = EXAMPLE_LCD_GPIO_RST, .reset_gpio_num = EXAMPLE_LCD_GPIO_RST,
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(6, 0, 0) #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(6, 0, 0)
.rgb_endian = LCD_RGB_ENDIAN_RGB, .rgb_endian = EXAMPLE_LCD_COLOR_ORDER_BGR ? LCD_RGB_ENDIAN_BGR : LCD_RGB_ENDIAN_RGB,
#else #else
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR, .rgb_ele_order = EXAMPLE_LCD_COLOR_ORDER_BGR ? LCD_RGB_ELEMENT_ORDER_BGR : LCD_RGB_ELEMENT_ORDER_RGB,
#endif #endif
.bits_per_pixel = EXAMPLE_LCD_BITS_PER_PIXEL, .bits_per_pixel = EXAMPLE_LCD_BITS_PER_PIXEL,
}; };
@@ -111,11 +115,18 @@ static esp_err_t app_lcd_init(void)
ESP_GOTO_ON_ERROR(esp_lcd_panel_set_gap(lcd_panel, EXAMPLE_LCD_GAP_X, EXAMPLE_LCD_GAP_Y), 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_LOGI(TAG, "面板基准参数已应用: gap=(%d,%d)", EXAMPLE_LCD_GAP_X, EXAMPLE_LCD_GAP_Y);
ESP_GOTO_ON_ERROR(esp_lcd_panel_invert_color(lcd_panel, true), err, TAG, "设置反色失败"); 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, "打开显示失败"); 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_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, "背光已打开,电平=%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; return ret;
@@ -172,7 +183,7 @@ static esp_err_t app_lvgl_init(void)
.flags = { .flags = {
.buff_dma = true, .buff_dma = true,
#if LVGL_VERSION_MAJOR >= 9 #if LVGL_VERSION_MAJOR >= 9
.swap_bytes = false, .swap_bytes = EXAMPLE_LCD_SWAP_BYTES,
#endif #endif
}}; }};
@@ -218,7 +229,7 @@ static void app_main_display(void)
* *
* LCD硬件LVGL库 * LCD硬件LVGL库
*/ */
esp_err_t lvgl_st7735s_set_center_text(const char *text) 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(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"); ESP_RETURN_ON_FALSE(s_center_label != NULL, ESP_ERR_INVALID_STATE, TAG, "label not ready");

View File

@@ -17,9 +17,9 @@
// MQTT 密码 // MQTT 密码
#define MQTT_PASSWORD "YTGui8979HI" #define MQTT_PASSWORD "YTGui8979HI"
// 传感器数据发布主题 // 传感器数据发布主题
#define MQTT_SENSOR_TOPIC "topic/sensor/esp32_BotanicalBuddy_001" #define MQTT_SENSOR_TOPIC "topic/sensor/esp32_BotanicalBuddy_002"
// 控制指令订阅主题 // 控制指令订阅主题
#define MQTT_CONTROL_TOPIC "topic/control/esp32_BotanicalBuddy_001" #define MQTT_CONTROL_TOPIC "topic/control/esp32_BotanicalBuddy_002"
static const char *TAG = "mqtt_control"; // 日志标签 static const char *TAG = "mqtt_control"; // 日志标签

View File

@@ -22,101 +22,154 @@ lv_obj_t *tick_value_change_obj;
// Screens // Screens
// //
void create_screen_temperature() { void create_screen_main() {
lv_obj_t *obj = lv_obj_create(0); lv_obj_t *obj = lv_obj_create(0);
objects.temperature = obj; objects.main = obj;
lv_obj_set_pos(obj, 0, 0); lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, 160, 80); lv_obj_set_size(obj, 240, 240);
lv_obj_set_style_bg_color(obj, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_bg_color(obj, lv_color_hex(0xff000000), LV_PART_MAIN | LV_STATE_DEFAULT);
{ {
lv_obj_t *parent_obj = obj; lv_obj_t *parent_obj = obj;
{ {
lv_obj_t *obj = lv_label_create(parent_obj); lv_obj_t *obj = lv_label_create(parent_obj);
objects.obj0 = obj; objects.obj0 = obj;
lv_obj_set_pos(obj, 36, 0); lv_obj_set_pos(obj, 16, 0);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_text_color(obj, lv_color_hex(0xff000000), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_font(obj, &ui_font_source_han_sans_sc_medium_22, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_text_font(obj, &ui_font_source_han_sans_sc_medium_22, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_color(obj, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "空气温度"); lv_label_set_text(obj, "空气温度");
} }
{ {
lv_obj_t *obj = lv_label_create(parent_obj); lv_obj_t *obj = lv_label_create(parent_obj);
objects.obj1 = obj; objects.obj1 = obj;
lv_obj_set_pos(obj, 7, 30); lv_obj_set_pos(obj, 129, 2);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_text_color(obj, lv_color_hex(0xff00ff48), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_font(obj, &ui_font_source_han_sans_sc_medium_36, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "");
}
{
lv_obj_t *obj = lv_image_create(parent_obj);
objects.obj2 = obj;
lv_obj_set_pos(obj, 105, 23);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_image_set_src(obj, &img_temp);
lv_obj_set_style_bg_color(obj, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 255, LV_PART_MAIN | LV_STATE_DEFAULT);
}
}
tick_screen_temperature();
}
void tick_screen_temperature() {
{
const char *new_val = get_var_air_temperature();
const char *cur_val = lv_label_get_text(objects.obj1);
if (strcmp(new_val, cur_val) != 0) {
tick_value_change_obj = objects.obj1;
lv_label_set_text(objects.obj1, new_val);
tick_value_change_obj = NULL;
}
}
}
void create_screen_humidity() {
lv_obj_t *obj = lv_obj_create(0);
objects.humidity = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, 160, 80);
lv_obj_set_style_bg_color(obj, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 255, LV_PART_MAIN | LV_STATE_DEFAULT);
{
lv_obj_t *parent_obj = obj;
{
lv_obj_t *obj = lv_label_create(parent_obj);
objects.obj3 = obj;
lv_obj_set_pos(obj, 36, 0);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_text_color(obj, lv_color_hex(0xff000000), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_font(obj, &ui_font_source_han_sans_sc_medium_22, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_text_font(obj, &ui_font_source_han_sans_sc_medium_22, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_color(obj, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "空气湿度"); lv_label_set_text(obj, "空气湿度");
} }
{ {
lv_obj_t *obj = lv_label_create(parent_obj); lv_obj_t *obj = lv_label_create(parent_obj);
objects.obj4 = obj; objects.obj2 = obj;
lv_obj_set_pos(obj, 7, 30); lv_obj_set_pos(obj, 16, 77);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_text_color(obj, lv_color_hex(0xffd81e06), LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_text_font(obj, &ui_font_source_han_sans_sc_medium_22, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_font(obj, &ui_font_source_han_sans_sc_medium_36, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_text_color(obj, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "土壤湿度");
}
{
lv_obj_t *obj = lv_label_create(parent_obj);
objects.obj3 = obj;
lv_obj_set_pos(obj, 129, 79);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_text_font(obj, &ui_font_source_han_sans_sc_medium_22, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_color(obj, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "光照强度");
}
{
lv_obj_t *obj = lv_label_create(parent_obj);
objects.obj4 = obj;
lv_obj_set_pos(obj, 16, 38);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_text_font(obj, &ui_font_source_han_sans_sc_medium_22, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_color(obj, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "");
}
{
lv_obj_t *obj = lv_label_create(parent_obj);
objects.obj5 = obj;
lv_obj_set_pos(obj, 129, 38);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_text_font(obj, &ui_font_source_han_sans_sc_medium_22, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_color(obj, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "");
}
{
lv_obj_t *obj = lv_label_create(parent_obj);
objects.obj6 = obj;
lv_obj_set_pos(obj, 16, 116);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_text_font(obj, &ui_font_source_han_sans_sc_medium_22, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_color(obj, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "");
}
{
lv_obj_t *obj = lv_label_create(parent_obj);
objects.obj7 = obj;
lv_obj_set_pos(obj, 129, 116);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_text_font(obj, &ui_font_source_han_sans_sc_medium_22, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_color(obj, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, ""); lv_label_set_text(obj, "");
} }
{ {
lv_obj_t *obj = lv_image_create(parent_obj); lv_obj_t *obj = lv_image_create(parent_obj);
objects.obj5 = obj; lv_obj_set_pos(obj, 72, 27);
lv_obj_set_pos(obj, 105, 23); lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_image_set_src(obj, &img_temp);
}
{
lv_obj_t *obj = lv_image_create(parent_obj);
lv_obj_set_pos(obj, 192, 27);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_image_set_src(obj, &img_humi); lv_image_set_src(obj, &img_humi);
lv_obj_set_style_bg_color(obj, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT); }
{
lv_obj_t *obj = lv_image_create(parent_obj);
lv_obj_set_pos(obj, 72, 105);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_image_set_src(obj, &img_mois);
}
{
lv_obj_t *obj = lv_image_create(parent_obj);
lv_obj_set_pos(obj, 192, 105);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_image_set_src(obj, &img_light);
}
{
lv_obj_t *obj = lv_line_create(parent_obj);
objects.obj8 = obj;
lv_obj_set_pos(obj, 0, 75);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
static lv_point_precise_t line_points[] = {
{ 0, 0 },
{ 240, 0 }
};
lv_line_set_points(obj, line_points, 2);
lv_obj_set_style_line_color(obj, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT);
}
{
lv_obj_t *obj = lv_line_create(parent_obj);
objects.obj9 = obj;
lv_obj_set_pos(obj, 0, 160);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
static lv_point_precise_t line_points[] = {
{ 0, 0 },
{ 240, 0 }
};
lv_line_set_points(obj, line_points, 2);
lv_obj_set_style_line_color(obj, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT);
}
{
lv_obj_t *obj = lv_line_create(parent_obj);
objects.obj10 = obj;
lv_obj_set_pos(obj, 120, 0);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
static lv_point_precise_t line_points[] = {
{ 0, 0 },
{ 0, 160 }
};
lv_line_set_points(obj, line_points, 2);
lv_obj_set_style_line_color(obj, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT);
} }
} }
tick_screen_humidity(); tick_screen_main();
} }
void tick_screen_humidity() { void tick_screen_main() {
{ {
const char *new_val = get_var_air_humidity(); const char *new_val = get_var_air_temperature();
const char *cur_val = lv_label_get_text(objects.obj4); const char *cur_val = lv_label_get_text(objects.obj4);
if (strcmp(new_val, cur_val) != 0) { if (strcmp(new_val, cur_val) != 0) {
tick_value_change_obj = objects.obj4; tick_value_change_obj = objects.obj4;
@@ -124,51 +177,26 @@ void tick_screen_humidity() {
tick_value_change_obj = NULL; tick_value_change_obj = NULL;
} }
} }
}
void create_screen_moisture() {
lv_obj_t *obj = lv_obj_create(0);
objects.moisture = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, 160, 80);
lv_obj_set_style_bg_color(obj, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 255, LV_PART_MAIN | LV_STATE_DEFAULT);
{ {
lv_obj_t *parent_obj = obj; const char *new_val = get_var_air_humidity();
{ const char *cur_val = lv_label_get_text(objects.obj5);
lv_obj_t *obj = lv_label_create(parent_obj); if (strcmp(new_val, cur_val) != 0) {
objects.obj6 = obj; tick_value_change_obj = objects.obj5;
lv_obj_set_pos(obj, 36, 0); lv_label_set_text(objects.obj5, new_val);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); tick_value_change_obj = NULL;
lv_obj_set_style_text_color(obj, lv_color_hex(0xff000000), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_font(obj, &ui_font_source_han_sans_sc_medium_22, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "土壤湿度");
}
{
lv_obj_t *obj = lv_label_create(parent_obj);
objects.obj7 = obj;
lv_obj_set_pos(obj, 7, 30);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_text_color(obj, lv_color_hex(0xff1296db), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_font(obj, &ui_font_source_han_sans_sc_medium_36, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "");
}
{
lv_obj_t *obj = lv_image_create(parent_obj);
objects.obj8 = obj;
lv_obj_set_pos(obj, 105, 23);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_image_set_src(obj, &img_mois);
lv_obj_set_style_bg_color(obj, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT);
} }
} }
tick_screen_moisture();
}
void tick_screen_moisture() {
{ {
const char *new_val = get_var_soil_moisture(); const char *new_val = get_var_soil_moisture();
const char *cur_val = lv_label_get_text(objects.obj6);
if (strcmp(new_val, cur_val) != 0) {
tick_value_change_obj = objects.obj6;
lv_label_set_text(objects.obj6, new_val);
tick_value_change_obj = NULL;
}
}
{
const char *new_val = get_var_light_intensity();
const char *cur_val = lv_label_get_text(objects.obj7); const char *cur_val = lv_label_get_text(objects.obj7);
if (strcmp(new_val, cur_val) != 0) { if (strcmp(new_val, cur_val) != 0) {
tick_value_change_obj = objects.obj7; tick_value_change_obj = objects.obj7;
@@ -178,64 +206,9 @@ void tick_screen_moisture() {
} }
} }
void create_screen_intensity() {
lv_obj_t *obj = lv_obj_create(0);
objects.intensity = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, 160, 80);
lv_obj_set_style_bg_color(obj, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 255, LV_PART_MAIN | LV_STATE_DEFAULT);
{
lv_obj_t *parent_obj = obj;
{
lv_obj_t *obj = lv_label_create(parent_obj);
objects.obj9 = obj;
lv_obj_set_pos(obj, 36, 0);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_text_color(obj, lv_color_hex(0xff000000), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_font(obj, &ui_font_source_han_sans_sc_medium_22, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "光照强度");
}
{
lv_obj_t *obj = lv_label_create(parent_obj);
objects.obj10 = obj;
lv_obj_set_pos(obj, 7, 30);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_text_color(obj, lv_color_hex(0xff13227a), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_font(obj, &ui_font_source_han_sans_sc_medium_36, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "");
}
{
lv_obj_t *obj = lv_image_create(parent_obj);
objects.obj11 = obj;
lv_obj_set_pos(obj, 105, 23);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_image_set_src(obj, &img_light);
lv_obj_set_style_bg_color(obj, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT);
}
}
tick_screen_intensity();
}
void tick_screen_intensity() {
{
const char *new_val = get_var_light_intensity();
const char *cur_val = lv_label_get_text(objects.obj10);
if (strcmp(new_val, cur_val) != 0) {
tick_value_change_obj = objects.obj10;
lv_label_set_text(objects.obj10, new_val);
tick_value_change_obj = NULL;
}
}
}
typedef void (*tick_screen_func_t)(); typedef void (*tick_screen_func_t)();
tick_screen_func_t tick_screen_funcs[] = { tick_screen_func_t tick_screen_funcs[] = {
tick_screen_temperature, tick_screen_main,
tick_screen_humidity,
tick_screen_moisture,
tick_screen_intensity,
}; };
void tick_screen(int screen_index) { void tick_screen(int screen_index) {
tick_screen_funcs[screen_index](); tick_screen_funcs[screen_index]();
@@ -335,8 +308,5 @@ void create_screens() {
// Initialize screens // Initialize screens
// Create screens // Create screens
create_screen_temperature(); create_screen_main();
create_screen_humidity();
create_screen_moisture();
create_screen_intensity();
} }

View File

@@ -11,18 +11,12 @@ extern "C" {
enum ScreensEnum { enum ScreensEnum {
_SCREEN_ID_FIRST = 1, _SCREEN_ID_FIRST = 1,
SCREEN_ID_TEMPERATURE = 1, SCREEN_ID_MAIN = 1,
SCREEN_ID_HUMIDITY = 2, _SCREEN_ID_LAST = 1
SCREEN_ID_MOISTURE = 3,
SCREEN_ID_INTENSITY = 4,
_SCREEN_ID_LAST = 4
}; };
typedef struct _objects_t { typedef struct _objects_t {
lv_obj_t *temperature; lv_obj_t *main;
lv_obj_t *humidity;
lv_obj_t *moisture;
lv_obj_t *intensity;
lv_obj_t *obj0; lv_obj_t *obj0;
lv_obj_t *obj1; lv_obj_t *obj1;
lv_obj_t *obj2; lv_obj_t *obj2;
@@ -34,22 +28,12 @@ typedef struct _objects_t {
lv_obj_t *obj8; lv_obj_t *obj8;
lv_obj_t *obj9; lv_obj_t *obj9;
lv_obj_t *obj10; lv_obj_t *obj10;
lv_obj_t *obj11;
} objects_t; } objects_t;
extern objects_t objects; extern objects_t objects;
void create_screen_temperature(); void create_screen_main();
void tick_screen_temperature(); void tick_screen_main();
void create_screen_humidity();
void tick_screen_humidity();
void create_screen_moisture();
void tick_screen_moisture();
void create_screen_intensity();
void tick_screen_intensity();
void tick_screen_by_id(enum ScreensEnum screenId); void tick_screen_by_id(enum ScreensEnum screenId);
void tick_screen(int screen_index); void tick_screen(int screen_index);

View File

@@ -23,7 +23,7 @@ void loadScreen(enum ScreensEnum screenId) {
void ui_init() { void ui_init() {
create_screens(); create_screens();
loadScreen(SCREEN_ID_TEMPERATURE); loadScreen(SCREEN_ID_MAIN);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
idf_component_register(SRCS "main.c" "auto_ctrl_thresholds.c" "auto_alerts.c" "status_web.c" idf_component_register(SRCS "main.c" "auto_ctrl_thresholds.c" "auto_alerts.c" "status_web.c"
INCLUDE_DIRS "." INCLUDE_DIRS "."
REQUIRES wifi-connect mqtt_control esp_lvgl_port lvgl_st7735s_use i2c_master_messager io_device_control console_simple_init console console_user_cmds capactive_soil_moisture_sensor_V2.0 ui esp_app_format REQUIRES wifi-connect mqtt_control esp_lvgl_port lvgl_st7789_use i2c_master_messager io_device_control console_simple_init console console_user_cmds capactive_soil_moisture_sensor_V2.0 ui esp_app_format
) )

View File

@@ -6,7 +6,7 @@
#include "esp_check.h" #include "esp_check.h"
#include "esp_log.h" #include "esp_log.h"
#include "wifi-connect.h" #include "wifi-connect.h"
#include "lvgl_st7735s_use.h" #include "lvgl_st7789_use.h"
#include "i2c_master_messager.h" #include "i2c_master_messager.h"
#include "io_device_control.h" #include "io_device_control.h"
#include "console_simple_init.h" // 提供 console_cmd_user_register 和 console_cmd_all_register #include "console_simple_init.h" // 提供 console_cmd_user_register 和 console_cmd_all_register
@@ -62,7 +62,7 @@
// I2C 内部上拉使能 // I2C 内部上拉使能
#define BOTANY_I2C_INTERNAL_PULLUP CONFIG_I2C_MASTER_MESSAGER_ENABLE_INTERNAL_PULLUP #define BOTANY_I2C_INTERNAL_PULLUP CONFIG_I2C_MASTER_MESSAGER_ENABLE_INTERNAL_PULLUP
// MQTT 告警主题 // MQTT 告警主题
#define BOTANY_MQTT_ALERT_TOPIC "topic/alert/esp32_iothome_001" #define BOTANY_MQTT_ALERT_TOPIC "topic/alert/esp32_iothome_002"
// MQTT 遥测数据上报周期(毫秒) // MQTT 遥测数据上报周期(毫秒)
#define BOTANY_MQTT_TELEMETRY_PERIOD_MS 5000 #define BOTANY_MQTT_TELEMETRY_PERIOD_MS 5000
#define BOTANY_STATUS_WEB_PORT 8080 #define BOTANY_STATUS_WEB_PORT 8080
@@ -415,7 +415,7 @@ static void auto_control_update(bool soil_valid,
/** /**
* @brief UI 任务函数 * @brief UI 任务函数
* *
* 负责定期切换显示页面每3秒切换一次 * 负责定期驱动 UI 刷新
* *
* @param arg 任务参数(未使用) * @param arg 任务参数(未使用)
*/ */
@@ -423,29 +423,11 @@ static void ui_task(void *arg)
{ {
(void)arg; (void)arg;
uint32_t elapsed_ms = 0;
enum ScreensEnum current = SCREEN_ID_TEMPERATURE;
const uint32_t switch_period_ms = 3000; // 每3秒切一次
for (;;) for (;;)
{ {
lvgl_port_lock(0); lvgl_port_lock(0);
ui_tick(); ui_tick();
elapsed_ms += 20;
if (elapsed_ms >= switch_period_ms) {
elapsed_ms = 0;
// 下一个页面1->2->3->4->1
if (current >= _SCREEN_ID_LAST) {
current = _SCREEN_ID_FIRST;
} else {
current = (enum ScreensEnum)(current + 1);
}
loadScreen(current);
}
lvgl_port_unlock(); lvgl_port_unlock();
vTaskDelay(pdMS_TO_TICKS(20)); vTaskDelay(pdMS_TO_TICKS(20));
} }
@@ -532,7 +514,7 @@ void app_main(void)
{ {
ESP_LOGE(TAG, "I2C 传感器管理启动失败: %s", esp_err_to_name(ret)); ESP_LOGE(TAG, "I2C 传感器管理启动失败: %s", esp_err_to_name(ret));
ESP_LOGW(TAG, "请检查 I2C 引脚/上拉电阻/端口占用情况,系统将继续运行但不采集传感器"); ESP_LOGW(TAG, "请检查 I2C 引脚/上拉电阻/端口占用情况,系统将继续运行但不采集传感器");
ESP_ERROR_CHECK(lvgl_st7735s_set_center_text("I2C init failed")); ESP_ERROR_CHECK(lvgl_st7789_set_center_text("I2C init failed"));
} }
else else
{ {