feat: 添加 UI 组件支持,整合界面元素和全局变量管理

This commit is contained in:
Wang Beihong
2026-03-06 20:03:31 +08:00
parent b8f33364d7
commit d532d037ab
18 changed files with 544 additions and 23 deletions

View File

@@ -205,7 +205,7 @@ static void app_main_display(void)
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_unscii_16, 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);

View File

@@ -0,0 +1,16 @@
{
"files": [
"actions.h",
"fonts.h",
"images.c",
"images.h",
"screens.c",
"screens.h",
"structs.h",
"styles.c",
"styles.h",
"ui.c",
"ui.h",
"vars.h"
]
}

View File

@@ -0,0 +1,5 @@
idf_component_register(
SRC_DIRS "."
INCLUDE_DIRS "."
REQUIRES lvgl esp_lvgl_port
)

14
components/ui/actions.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef EEZ_LVGL_UI_EVENTS_H
#define EEZ_LVGL_UI_EVENTS_H
#include <lvgl.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /*EEZ_LVGL_UI_EVENTS_H*/

24
components/ui/fonts.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef EEZ_LVGL_UI_FONTS_H
#define EEZ_LVGL_UI_FONTS_H
#include <lvgl.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef EXT_FONT_DESC_T
#define EXT_FONT_DESC_T
typedef struct _ext_font_desc_t {
const char *name;
const void *font_ptr;
} ext_font_desc_t;
#endif
extern ext_font_desc_t fonts[];
#ifdef __cplusplus
}
#endif
#endif /*EEZ_LVGL_UI_FONTS_H*/

5
components/ui/images.c Normal file
View File

@@ -0,0 +1,5 @@
#include "images.h"
const ext_img_desc_t images[1] = {
0
};

24
components/ui/images.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef EEZ_LVGL_UI_IMAGES_H
#define EEZ_LVGL_UI_IMAGES_H
#include <lvgl.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef EXT_IMG_DESC_T
#define EXT_IMG_DESC_T
typedef struct _ext_img_desc_t {
const char *name;
const lv_img_dsc_t *img_dsc;
} ext_img_desc_t;
#endif
extern const ext_img_desc_t images[1];
#ifdef __cplusplus
}
#endif
#endif /*EEZ_LVGL_UI_IMAGES_H*/

197
components/ui/screens.c Normal file
View File

@@ -0,0 +1,197 @@
#include <string.h>
#include "screens.h"
#include "images.h"
#include "fonts.h"
#include "actions.h"
#include "vars.h"
#include "styles.h"
#include "ui.h"
#include <string.h>
objects_t objects;
//
// Event handlers
//
lv_obj_t *tick_value_change_obj;
static void event_handler_cb_main_obj1(lv_event_t *e) {
lv_event_code_t event = lv_event_get_code(e);
if (event == LV_EVENT_VALUE_CHANGED) {
lv_obj_t *ta = lv_event_get_target_obj(e);
if (tick_value_change_obj != ta) {
int32_t value = lv_arc_get_value(ta);
set_var_air_temperature_int(value);
}
}
}
//
// Screens
//
void create_screen_main() {
lv_obj_t *obj = lv_obj_create(0);
objects.main = 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(0xff000000), LV_PART_MAIN | LV_STATE_DEFAULT);
{
lv_obj_t *parent_obj = obj;
{
lv_obj_t *obj = lv_label_create(parent_obj);
objects.obj0 = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_text_color(obj, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_font(obj, &lv_font_montserrat_28, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "Temp");
}
{
lv_obj_t *obj = lv_arc_create(parent_obj);
objects.obj1 = obj;
lv_obj_set_pos(obj, 92, 10);
lv_obj_set_size(obj, 58, 57);
lv_arc_set_range(obj, -5, 44);
lv_obj_add_event_cb(obj, event_handler_cb_main_obj1, LV_EVENT_ALL, 0);
}
{
lv_obj_t *obj = lv_label_create(parent_obj);
objects.obj2 = obj;
lv_obj_set_pos(obj, 25, 42);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_text_font(obj, &lv_font_montserrat_32, LV_PART_MAIN | LV_STATE_CHECKED);
lv_obj_set_style_text_color(obj, lv_color_hex(0xffd40808), LV_PART_MAIN | LV_STATE_CHECKED);
lv_label_set_text(obj, "");
}
}
tick_screen_main();
}
void tick_screen_main() {
{
int32_t new_val = get_var_air_temperature_int();
int32_t cur_val = lv_arc_get_value(objects.obj1);
if (new_val != cur_val) {
tick_value_change_obj = objects.obj1;
lv_arc_set_value(objects.obj1, new_val);
tick_value_change_obj = NULL;
}
}
{
const char *new_val = get_var_air_temperature();
const char *cur_val = lv_label_get_text(objects.obj2);
if (strcmp(new_val, cur_val) != 0) {
tick_value_change_obj = objects.obj2;
lv_label_set_text(objects.obj2, new_val);
tick_value_change_obj = NULL;
}
}
}
typedef void (*tick_screen_func_t)();
tick_screen_func_t tick_screen_funcs[] = {
tick_screen_main,
};
void tick_screen(int screen_index) {
tick_screen_funcs[screen_index]();
}
void tick_screen_by_id(enum ScreensEnum screenId) {
tick_screen_funcs[screenId - 1]();
}
//
// Fonts
//
ext_font_desc_t fonts[] = {
#if LV_FONT_MONTSERRAT_8
{ "MONTSERRAT_8", &lv_font_montserrat_8 },
#endif
#if LV_FONT_MONTSERRAT_10
{ "MONTSERRAT_10", &lv_font_montserrat_10 },
#endif
#if LV_FONT_MONTSERRAT_12
{ "MONTSERRAT_12", &lv_font_montserrat_12 },
#endif
#if LV_FONT_MONTSERRAT_14
{ "MONTSERRAT_14", &lv_font_montserrat_14 },
#endif
#if LV_FONT_MONTSERRAT_16
{ "MONTSERRAT_16", &lv_font_montserrat_16 },
#endif
#if LV_FONT_MONTSERRAT_18
{ "MONTSERRAT_18", &lv_font_montserrat_18 },
#endif
#if LV_FONT_MONTSERRAT_20
{ "MONTSERRAT_20", &lv_font_montserrat_20 },
#endif
#if LV_FONT_MONTSERRAT_22
{ "MONTSERRAT_22", &lv_font_montserrat_22 },
#endif
#if LV_FONT_MONTSERRAT_24
{ "MONTSERRAT_24", &lv_font_montserrat_24 },
#endif
#if LV_FONT_MONTSERRAT_26
{ "MONTSERRAT_26", &lv_font_montserrat_26 },
#endif
#if LV_FONT_MONTSERRAT_28
{ "MONTSERRAT_28", &lv_font_montserrat_28 },
#endif
#if LV_FONT_MONTSERRAT_30
{ "MONTSERRAT_30", &lv_font_montserrat_30 },
#endif
#if LV_FONT_MONTSERRAT_32
{ "MONTSERRAT_32", &lv_font_montserrat_32 },
#endif
#if LV_FONT_MONTSERRAT_34
{ "MONTSERRAT_34", &lv_font_montserrat_34 },
#endif
#if LV_FONT_MONTSERRAT_36
{ "MONTSERRAT_36", &lv_font_montserrat_36 },
#endif
#if LV_FONT_MONTSERRAT_38
{ "MONTSERRAT_38", &lv_font_montserrat_38 },
#endif
#if LV_FONT_MONTSERRAT_40
{ "MONTSERRAT_40", &lv_font_montserrat_40 },
#endif
#if LV_FONT_MONTSERRAT_42
{ "MONTSERRAT_42", &lv_font_montserrat_42 },
#endif
#if LV_FONT_MONTSERRAT_44
{ "MONTSERRAT_44", &lv_font_montserrat_44 },
#endif
#if LV_FONT_MONTSERRAT_46
{ "MONTSERRAT_46", &lv_font_montserrat_46 },
#endif
#if LV_FONT_MONTSERRAT_48
{ "MONTSERRAT_48", &lv_font_montserrat_48 },
#endif
};
//
// Color themes
//
uint32_t active_theme_index = 0;
//
//
//
void create_screens() {
// Set default LVGL theme
lv_display_t *dispp = lv_display_get_default();
lv_theme_t *theme = lv_theme_default_init(dispp, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED), false, LV_FONT_DEFAULT);
lv_display_set_theme(dispp, theme);
// Initialize screens
// Create screens
create_screen_main();
}

39
components/ui/screens.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef EEZ_LVGL_UI_SCREENS_H
#define EEZ_LVGL_UI_SCREENS_H
#include <lvgl.h>
#ifdef __cplusplus
extern "C" {
#endif
// Screens
enum ScreensEnum {
_SCREEN_ID_FIRST = 1,
SCREEN_ID_MAIN = 1,
_SCREEN_ID_LAST = 1
};
typedef struct _objects_t {
lv_obj_t *main;
lv_obj_t *obj0;
lv_obj_t *obj1;
lv_obj_t *obj2;
} objects_t;
extern objects_t objects;
void create_screen_main();
void tick_screen_main();
void tick_screen_by_id(enum ScreensEnum screenId);
void tick_screen(int screen_index);
void create_screens();
#ifdef __cplusplus
}
#endif
#endif /*EEZ_LVGL_UI_SCREENS_H*/

4
components/ui/structs.h Normal file
View File

@@ -0,0 +1,4 @@
#ifndef EEZ_LVGL_UI_STRUCTS_H
#define EEZ_LVGL_UI_STRUCTS_H
#endif /*EEZ_LVGL_UI_STRUCTS_H*/

6
components/ui/styles.c Normal file
View File

@@ -0,0 +1,6 @@
#include "styles.h"
#include "images.h"
#include "fonts.h"
#include "ui.h"
#include "screens.h"

14
components/ui/styles.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef EEZ_LVGL_UI_STYLES_H
#define EEZ_LVGL_UI_STYLES_H
#include <lvgl.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /*EEZ_LVGL_UI_STYLES_H*/

32
components/ui/ui.c Normal file
View File

@@ -0,0 +1,32 @@
#include "ui.h"
#include "screens.h"
#include "images.h"
#include "actions.h"
#include "vars.h"
#include <string.h>
static int16_t currentScreen = -1;
static lv_obj_t *getLvglObjectFromIndex(int32_t index) {
if (index == -1) {
return 0;
}
return ((lv_obj_t **)&objects)[index];
}
void loadScreen(enum ScreensEnum screenId) {
currentScreen = screenId - 1;
lv_obj_t *screen = getLvglObjectFromIndex(currentScreen);
lv_scr_load_anim(screen, LV_SCR_LOAD_ANIM_FADE_IN, 200, 0, false);
}
void ui_init() {
create_screens();
loadScreen(SCREEN_ID_MAIN);
}
void ui_tick() {
tick_screen(currentScreen);
}

21
components/ui/ui.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef EEZ_LVGL_UI_GUI_H
#define EEZ_LVGL_UI_GUI_H
#include <lvgl.h>
#include "screens.h"
#ifdef __cplusplus
extern "C" {
#endif
void ui_init();
void ui_tick();
void loadScreen(enum ScreensEnum screenId);
#ifdef __cplusplus
}
#endif
#endif // EEZ_LVGL_UI_GUI_H

57
components/ui/vars.c Normal file
View File

@@ -0,0 +1,57 @@
#include <string.h>
#include "vars.h"
char air_temperature[100] = { 0 };
const char *get_var_air_temperature() {
return air_temperature;
}
void set_var_air_temperature(const char *value) {
strncpy(air_temperature, value, sizeof(air_temperature) / sizeof(char));
air_temperature[sizeof(air_temperature) / sizeof(char) - 1] = 0;
}
char air_humidity[100] = { 0 };
const char *get_var_air_humidity() {
return air_humidity;
}
void set_var_air_humidity(const char *value) {
strncpy(air_humidity, value, sizeof(air_humidity) / sizeof(char));
air_humidity[sizeof(air_humidity) / sizeof(char) - 1] = 0;
}
char soil_moisture[100] = { 0 };
const char *get_var_soil_moisture() {
return soil_moisture;
}
void set_var_soil_moisture(const char *value) {
strncpy(soil_moisture, value, sizeof(soil_moisture) / sizeof(char));
soil_moisture[sizeof(soil_moisture) / sizeof(char) - 1] = 0;
}
char light_intensity[100] = { 0 };
const char *get_var_light_intensity() {
return light_intensity;
}
void set_var_light_intensity(const char *value) {
strncpy(light_intensity, value, sizeof(light_intensity) / sizeof(char));
light_intensity[sizeof(light_intensity) / sizeof(char) - 1] = 0;
}
int32_t air_temperature_int;
int32_t get_var_air_temperature_int() {
return air_temperature_int;
}
void set_var_air_temperature_int(int32_t value) {
air_temperature_int = value;
}

40
components/ui/vars.h Normal file
View File

@@ -0,0 +1,40 @@
#ifndef EEZ_LVGL_UI_VARS_H
#define EEZ_LVGL_UI_VARS_H
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
// enum declarations
// Flow global variables
enum FlowGlobalVariables {
FLOW_GLOBAL_VARIABLE_AIR_TEMPERATURE = 0,
FLOW_GLOBAL_VARIABLE_AIR_HUMIDITY = 1,
FLOW_GLOBAL_VARIABLE_SOIL_MOISTURE = 2,
FLOW_GLOBAL_VARIABLE_LIGHT_INTENSITY = 3,
FLOW_GLOBAL_VARIABLE_AIR_TEMPERATURE_INT = 4
};
// Native global variables
extern const char *get_var_air_temperature();
extern void set_var_air_temperature(const char *value);
extern const char *get_var_air_humidity();
extern void set_var_air_humidity(const char *value);
extern const char *get_var_soil_moisture();
extern void set_var_soil_moisture(const char *value);
extern const char *get_var_light_intensity();
extern void set_var_light_intensity(const char *value);
extern int32_t get_var_air_temperature_int();
extern void set_var_air_temperature_int(int32_t value);
#ifdef __cplusplus
}
#endif
#endif /*EEZ_LVGL_UI_VARS_H*/

View File

@@ -1,4 +1,4 @@
idf_component_register(SRCS "main.c"
INCLUDE_DIRS "."
REQUIRES wifi-connect 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
REQUIRES wifi-connect 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
)

View File

@@ -12,6 +12,9 @@
#include "console_simple_init.h" // 提供 console_cmd_user_register 和 console_cmd_all_register
#include "console_user_cmds.h"
#include "capactive_soil_moisture_sensor_V2.0.h"
#include "ui.h" // 使用EEZStudio提供的ui组件便于后续扩展
#include "esp_lvgl_port.h"
#include "vars.h" // 定义全局变量接口
#ifndef CONFIG_I2C_MASTER_MESSAGER_BH1750_ENABLE
#define CONFIG_I2C_MASTER_MESSAGER_BH1750_ENABLE 0
@@ -44,24 +47,23 @@
static const char *TAG = "main";
static const char *wifi_status_short_str(wifi_connect_status_t status)
static char s_air_temp[16];
static char s_air_hum[16];
static char s_soil[16];
static char s_lux[16];
static void ui_task(void *arg)
{
switch (status)
(void)arg;
for (;;)
{
case WIFI_CONNECT_STATUS_IDLE:
return "IDLE";
case WIFI_CONNECT_STATUS_PROVISIONING:
return "PROV";
case WIFI_CONNECT_STATUS_CONNECTING:
return "CONN";
case WIFI_CONNECT_STATUS_CONNECTED:
return "UP";
case WIFI_CONNECT_STATUS_FAILED:
return "FAIL";
case WIFI_CONNECT_STATUS_TIMEOUT:
return "TIME";
default:
return "UNKN";
lvgl_port_lock(0);
ui_tick();
lvgl_port_unlock();
// UI 刷新周期无需过高20ms 兼顾流畅度与CPU占用。
vTaskDelay(pdMS_TO_TICKS(20));
}
}
@@ -101,6 +103,14 @@ void app_main(void)
// 启动 LVGL 演示程序,显示简单的界面
ESP_ERROR_CHECK(start_lvgl_demo());
// 初始化 UI 组件(需在 LVGL 锁内进行对象创建)
lvgl_port_lock(0);
ui_init();
lvgl_port_unlock();
BaseType_t ui_task_ok = xTaskCreate(ui_task, "ui_task", 4096, NULL, 5, NULL);
ESP_ERROR_CHECK(ui_task_ok == pdPASS ? ESP_OK : ESP_FAIL);
// 初始化 IO 设备控制组件GPIO1 水泵GPIO10 光照,高电平有效)
ESP_ERROR_CHECK(io_device_control_init());
@@ -169,17 +179,30 @@ void app_main(void)
cap_soil_sensor_data_t soil_data = {0};
if (soil_ready && cap_soil_sensor_read(&soil_data) == ESP_OK)
{
// 读取成功,当前版本仅保留采样流程,显示逻辑由用户自定义。
// 读取成功
snprintf(s_soil, sizeof(s_soil), "%.0f", soil_data.moisture_percent);
set_var_soil_moisture(s_soil);
}
i2c_master_messager_data_t sensor_data = {0};
if (i2c_ready && i2c_master_messager_get_data(&sensor_data) == ESP_OK)
{
// 读取成功,当前版本仅保留采样流程,显示逻辑由用户自定义。
}
// 读取成功
if (sensor_data.aht30.valid)
{
snprintf(s_air_temp, sizeof(s_air_temp), "%.1f", sensor_data.aht30.temperature_c);
set_var_air_temperature(s_air_temp);
set_var_air_temperature_int((int32_t)(sensor_data.aht30.temperature_c * 100)); // 以 1°C 为单位的整数
// TODO: 在这里实现你自己的 LCD 显示内容与排版。
// ESP_ERROR_CHECK(lvgl_st7735s_set_center_text("custom text"));
snprintf(s_air_hum, sizeof(s_air_hum), "%.1f", sensor_data.aht30.humidity_rh);
set_var_air_humidity(s_air_hum);
}
if (sensor_data.bh1750.valid)
{
snprintf(s_lux, sizeof(s_lux), "%.0f", sensor_data.bh1750.lux);
set_var_light_intensity(s_lux);
}
}
vTaskDelay(pdMS_TO_TICKS(1000));
}