mirror of
https://git.beihong.wang/wangbeihong/iot-bedroom-environment-controller.git
synced 2026-04-23 18:53:03 +08:00
feat: 智能家居控制系统 v1.0 初始版本
- 环境监测:温湿度/光照/空气质量传感器采集 - 智能控制:时间段/降温/通风三种自动模式 - 闹钟系统:3个闹钟+温和唤醒功能 - 远程控制:MQTT双向通信 - 本地显示:LVGL图形界面 - 双MCU架构,FreeRTOS 10任务并行 - 完整的1250行README文档
This commit is contained in:
4
components/lvgl_st7735s_use/CMakeLists.txt
Normal file
4
components/lvgl_st7735s_use/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
idf_component_register(SRCS "ui_display.c" "lvgl_st7735s_use.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES driver esp_lcd esp_lvgl_port
|
||||
)
|
||||
33
components/lvgl_st7735s_use/include/lvgl_st7735s_use.h
Normal file
33
components/lvgl_st7735s_use/include/lvgl_st7735s_use.h
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
/* LCD size */
|
||||
#define EXAMPLE_LCD_H_RES (160)
|
||||
#define EXAMPLE_LCD_V_RES (80)
|
||||
|
||||
/* LCD SPI总线配置 */
|
||||
#define EXAMPLE_LCD_SPI_NUM (SPI2_HOST) // 使用SPI2主机接口进行通信
|
||||
|
||||
/* LCD显示参数配置 */
|
||||
#define EXAMPLE_LCD_PIXEL_CLK_HZ (40 * 1000 * 1000) // 像素时钟频率设置为40MHz,控制数据传输速度
|
||||
|
||||
/* LCD命令和参数配置 */
|
||||
#define EXAMPLE_LCD_CMD_BITS (8) // 命令位数为8位,用于发送LCD控制命令
|
||||
#define EXAMPLE_LCD_PARAM_BITS (8) // 参数位数为8位,用于发送命令参数
|
||||
|
||||
/* LCD颜色和缓冲区配置 */
|
||||
#define EXAMPLE_LCD_BITS_PER_PIXEL (16) // 每个像素使用16位颜色(RGB565格式)
|
||||
#define EXAMPLE_LCD_DRAW_BUFF_DOUBLE (1) // 启用双缓冲模式,提高显示流畅度
|
||||
#define EXAMPLE_LCD_DRAW_BUFF_HEIGHT (50) // 绘图缓冲区高度为50行,影响刷新性能
|
||||
|
||||
/* LCD背光配置 */
|
||||
#define EXAMPLE_LCD_BL_ON_LEVEL (1) // 背光开启电平为高电平(1)
|
||||
|
||||
/* LCD pins */
|
||||
#define EXAMPLE_LCD_GPIO_SCLK (GPIO_NUM_2)
|
||||
#define EXAMPLE_LCD_GPIO_MOSI (GPIO_NUM_3)
|
||||
#define EXAMPLE_LCD_GPIO_RST (GPIO_NUM_9)
|
||||
#define EXAMPLE_LCD_GPIO_DC (GPIO_NUM_8)
|
||||
#define EXAMPLE_LCD_GPIO_CS (GPIO_NUM_7)
|
||||
#define EXAMPLE_LCD_GPIO_BL (GPIO_NUM_6)
|
||||
|
||||
|
||||
void start_lvgl_demo(void);
|
||||
38
components/lvgl_st7735s_use/include/ui_display.h
Normal file
38
components/lvgl_st7735s_use/include/ui_display.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef UI_DISPLAY_H
|
||||
#define UI_DISPLAY_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief 初始化UI界面
|
||||
*
|
||||
* 该函数负责创建LVGL的用户界面元素,用于显示传感器数据
|
||||
*/
|
||||
void ui_display_init(void);
|
||||
|
||||
/**
|
||||
* @brief 更新传感器数据显示
|
||||
*
|
||||
* 该函数用于更新LVGL界面上的传感器数据
|
||||
*
|
||||
* @param temperature 温度值(°C),-1.0表示无效
|
||||
* @param humidity 湿度值(%),-1.0表示无效
|
||||
* @param lux 光照强度(lx),-1.0表示无效
|
||||
* @param ppm 空气中有害气体浓度(ppm)
|
||||
* @param quality_level 空气质量等级描述
|
||||
*/
|
||||
void ui_update_sensor_data(float temperature, float humidity, float lux, float ppm, const char* quality_level);
|
||||
|
||||
/* Time page APIs */
|
||||
void ui_show_time_page(void);
|
||||
void ui_show_sensor_page(void);
|
||||
void ui_time_update(void);
|
||||
void ui_toggle_page(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* UI_DISPLAY_H */
|
||||
236
components/lvgl_st7735s_use/lvgl_st7735s_use.c
Normal file
236
components/lvgl_st7735s_use/lvgl_st7735s_use.c
Normal file
@@ -0,0 +1,236 @@
|
||||
#include <stdio.h>
|
||||
#include "lvgl_st7735s_use.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" // 包含LVGL端口头文件,用于LVGL与ESP硬件的接口
|
||||
|
||||
#include "ui_display.h" // 添加新UI界面的头文件
|
||||
static const char *TAG = "lvgl_st7735s_use"; // 用于日志输出的标签,便于调试时识别日志来源
|
||||
|
||||
|
||||
/* LCD IO和面板句柄 */
|
||||
// lcd_io: LCD面板IO句柄,用于与LCD进行通信
|
||||
// lcd_panel: LCD面板句柄,用于控制LCD的各种操作
|
||||
static esp_lcd_panel_io_handle_t lcd_io = NULL;
|
||||
static esp_lcd_panel_handle_t lcd_panel = NULL;
|
||||
|
||||
/* LVGL显示和触摸 */
|
||||
// lvgl_disp: LVGL显示设备句柄,用于LVGL库与显示设备的交互
|
||||
static lv_display_t *lvgl_disp = NULL;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/* LCD背光配置 */
|
||||
// 配置背光GPIO为输出模式,用于控制LCD的背光开关
|
||||
gpio_config_t bk_gpio_config = {
|
||||
.mode = GPIO_MODE_OUTPUT, // 设置GPIO为输出模式
|
||||
.pin_bit_mask = 1ULL << EXAMPLE_LCD_GPIO_BL // 设置背光GPIO引脚
|
||||
};
|
||||
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config)); // 应用GPIO配置并检查错误
|
||||
|
||||
/* LCD初始化 */
|
||||
ESP_LOGD(TAG, "初始化SPI总线"); // 输出调试日志
|
||||
// 配置SPI总线参数,包括时钟、数据线和最大传输大小
|
||||
const spi_bus_config_t buscfg = {
|
||||
.sclk_io_num = EXAMPLE_LCD_GPIO_SCLK, // SPI时钟引脚
|
||||
.mosi_io_num = EXAMPLE_LCD_GPIO_MOSI, // SPI主输出从输入引脚
|
||||
.miso_io_num = GPIO_NUM_NC, // 未使用MISO引脚
|
||||
.quadwp_io_num = GPIO_NUM_NC, // 未使用WP引脚
|
||||
.quadhd_io_num = GPIO_NUM_NC, // 未使用HD引脚
|
||||
.max_transfer_sz = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_DRAW_BUFF_HEIGHT * sizeof(uint16_t), // 最大传输大小
|
||||
};
|
||||
// 初始化SPI总线,使用DMA自动分配通道
|
||||
ESP_RETURN_ON_ERROR(spi_bus_initialize(EXAMPLE_LCD_SPI_NUM, &buscfg, SPI_DMA_CH_AUTO), TAG, "SPI初始化失败");
|
||||
|
||||
ESP_LOGD(TAG, "安装面板IO");
|
||||
// 配置LCD面板IO的SPI参数
|
||||
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 = 3, // SPI模式
|
||||
.trans_queue_depth = 10, // 传输队列深度
|
||||
};
|
||||
// 创建LCD面板IO,用于SPI通信
|
||||
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_LOGD(TAG, "安装LCD驱动");
|
||||
// 配置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 = LCD_RGB_ENDIAN_RGB, // RGB字节序(旧版本)
|
||||
#else
|
||||
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR, // RGB元素顺序(新版本)
|
||||
#endif
|
||||
.bits_per_pixel = EXAMPLE_LCD_BITS_PER_PIXEL, // 每像素位数
|
||||
};
|
||||
// 创建ST7789 LCD面板驱动
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_st7789(lcd_io, &panel_config, &lcd_panel), err, TAG, "创建面板失败");
|
||||
|
||||
// 复位LCD面板
|
||||
esp_lcd_panel_reset(lcd_panel);
|
||||
// 初始化LCD面板
|
||||
esp_lcd_panel_init(lcd_panel);
|
||||
// 设置显示窗口,确保使用正确的分辨率(偏移)
|
||||
esp_lcd_panel_set_gap(lcd_panel, 1, 26);
|
||||
|
||||
// 反转颜色
|
||||
esp_lcd_panel_invert_color(lcd_panel, true);
|
||||
// 打开LCD显示
|
||||
esp_lcd_panel_disp_on_off(lcd_panel, true);
|
||||
|
||||
/* 打开LCD背光 */
|
||||
ESP_ERROR_CHECK(gpio_set_level(EXAMPLE_LCD_GPIO_BL, EXAMPLE_LCD_BL_ON_LEVEL));
|
||||
|
||||
return ret;
|
||||
|
||||
// 错误处理标签,用于清理资源
|
||||
err:
|
||||
if (lcd_panel) {
|
||||
esp_lcd_panel_del(lcd_panel); // 删除面板
|
||||
}
|
||||
if (lcd_io) {
|
||||
esp_lcd_panel_io_del(lcd_io); // 删除面板IO
|
||||
}
|
||||
spi_bus_free(EXAMPLE_LCD_SPI_NUM); // 释放SPI总线资源
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 初始化LVGL图形库
|
||||
*
|
||||
* 该函数负责初始化LVGL库,并配置显示设备
|
||||
* 包括LVGL任务配置、显示缓冲区配置和旋转设置
|
||||
*
|
||||
* @return esp_err_t 初始化结果,ESP_OK表示成功
|
||||
*/
|
||||
static esp_err_t app_lvgl_init(void)
|
||||
{
|
||||
/* 初始化LVGL */
|
||||
// 配置LVGL任务参数
|
||||
const lvgl_port_cfg_t lvgl_cfg = {
|
||||
.task_priority = 4, /* LVGL任务优先级,数值越高优先级越高 */
|
||||
.task_stack = 4096, /* LVGL任务堆栈大小,单位为字节 */
|
||||
.task_affinity = -1, /* LVGL任务绑定核心,-1表示不绑定特定核心 */
|
||||
.task_max_sleep_ms = 500, /* LVGL任务最大睡眠时间,单位为毫秒 */
|
||||
.timer_period_ms = 5 /* LVGL定时器周期,单位为毫秒,用于处理动画和输入 */
|
||||
};
|
||||
// 初始化LVGL端口
|
||||
ESP_RETURN_ON_ERROR(lvgl_port_init(&lvgl_cfg), TAG, "LVGL端口初始化失败");
|
||||
|
||||
/* 添加LCD屏幕 */
|
||||
ESP_LOGD(TAG, "添加LCD屏幕");
|
||||
// 配置LVGL显示设备参数
|
||||
const lvgl_port_display_cfg_t disp_cfg = {
|
||||
.io_handle = lcd_io, // LCD面板IO句柄
|
||||
.panel_handle = lcd_panel, // LCD面板句柄
|
||||
.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, // 颜色格式(LVGL v9及以上)
|
||||
#endif
|
||||
.rotation = { // 旋转设置
|
||||
.swap_xy = 1, // 交换X和Y轴以实现横向显示
|
||||
.mirror_x = 1, // 是否水平镜像
|
||||
.mirror_y = 0, // 是否垂直镜像
|
||||
},
|
||||
.flags = { // 标志位
|
||||
.buff_dma = true, // 是否使用DMA缓冲区
|
||||
#if LVGL_VERSION_MAJOR >= 9
|
||||
.swap_bytes = false, // 是否交换字节序(LVGL v9及以上)
|
||||
#endif
|
||||
}
|
||||
};
|
||||
// 添加LVGL显示设备
|
||||
lvgl_disp = lvgl_port_add_disp(&disp_cfg);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief 创建并显示LVGL主界面
|
||||
*
|
||||
* 该函数负责创建LVGL的用户界面元素,包括图像、标签和按钮
|
||||
* 并设置它们的位置和属性
|
||||
*/
|
||||
static void app_main_display(void)
|
||||
{
|
||||
// 获取当前活动屏幕对象
|
||||
lv_obj_t *scr = lv_scr_act();
|
||||
|
||||
/* 任务锁定 */
|
||||
// 锁定LVGL任务,防止在创建UI对象时被中断
|
||||
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);
|
||||
|
||||
|
||||
/* 创建标签 */
|
||||
// 创建标签对象
|
||||
lv_obj_t *label = lv_label_create(scr);
|
||||
// 设置标签文本为"ESP32C3-LVGL"
|
||||
lv_label_set_text(label, "ESP32C3-LVGL1");
|
||||
// 设置标签文本颜色为白色
|
||||
lv_obj_set_style_text_color(label, lv_color_black(), 0);
|
||||
// 设置标签文本字体大小为更小的字体
|
||||
lv_obj_set_style_text_font(label, &lv_font_unscii_8, 0);
|
||||
|
||||
// 设置标签位置在屏幕中心
|
||||
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
|
||||
|
||||
/* 任务解锁 */
|
||||
// 解锁LVGL任务
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 启动LVGL演示程序
|
||||
*
|
||||
* 该函数是程序的入口点,负责初始化LCD硬件、LVGL库,并显示主界面
|
||||
*/
|
||||
void start_lvgl_demo(void)
|
||||
{
|
||||
/* LCD硬件初始化 */
|
||||
// 初始化LCD硬件和SPI接口
|
||||
ESP_ERROR_CHECK(app_lcd_init());
|
||||
|
||||
/* LVGL初始化 */
|
||||
// 初始化LVGL图形库
|
||||
ESP_ERROR_CHECK(app_lvgl_init());
|
||||
|
||||
/* 显示LVGL对象 */
|
||||
// 创建并显示LVGL主界面
|
||||
// app_main_display();
|
||||
|
||||
/* 显示LVGL对象 - 使用新的UI界面初始化函数 */
|
||||
ui_display_init();
|
||||
}
|
||||
258
components/lvgl_st7735s_use/ui_display.c
Normal file
258
components/lvgl_st7735s_use/ui_display.c
Normal file
@@ -0,0 +1,258 @@
|
||||
#include "ui_display.h"
|
||||
#include "esp_log.h"
|
||||
#include "lvgl.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
#include <time.h>
|
||||
|
||||
static const char *TAG = "ui_display";
|
||||
|
||||
// 全局变量用于存储传感器数据标签
|
||||
static lv_obj_t *temp_label = NULL;
|
||||
static lv_obj_t *humid_label = NULL;
|
||||
static lv_obj_t *lux_label = NULL;
|
||||
static lv_obj_t *air_quality_label = NULL; // 新增空气质量标签
|
||||
|
||||
/* Time page objects */
|
||||
static lv_obj_t *time_container = NULL;
|
||||
static lv_obj_t *date_label = NULL;
|
||||
static lv_obj_t *time_label = NULL;
|
||||
static bool time_page_visible = false;
|
||||
|
||||
/**
|
||||
* @brief 初始化UI界面
|
||||
*
|
||||
* 该函数负责创建LVGL的用户界面元素,用于显示传感器数据
|
||||
* 优化布局以适应非触摸屏设备,所有内容在一个屏幕内显示
|
||||
*/
|
||||
void ui_display_init(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);
|
||||
|
||||
/* 创建标题标签 */
|
||||
lv_obj_t *title_label = lv_label_create(scr);
|
||||
lv_label_set_text(title_label, "IoT Home Monitor");
|
||||
lv_obj_set_style_text_color(title_label, lv_palette_main(LV_PALETTE_BLUE), 0);
|
||||
lv_obj_set_style_text_font(title_label, &lv_font_unscii_8, 0);
|
||||
lv_obj_align(title_label, LV_ALIGN_TOP_MID, 0, 2); // 调整标题位置
|
||||
|
||||
/* 创建温度标签 */
|
||||
temp_label = lv_label_create(scr);
|
||||
lv_label_set_text(temp_label, "Temp: --.- C");
|
||||
lv_obj_set_style_text_color(temp_label, lv_color_black(), 0);
|
||||
lv_obj_set_style_text_font(temp_label, &lv_font_unscii_8, 0);
|
||||
lv_obj_align(temp_label, LV_ALIGN_TOP_LEFT, 3, 20); // 调整位置
|
||||
|
||||
/* 创建湿度标签 */
|
||||
humid_label = lv_label_create(scr);
|
||||
lv_label_set_text(humid_label, "Humidity: --.- %");
|
||||
lv_obj_set_style_text_color(humid_label, lv_color_black(), 0);
|
||||
lv_obj_set_style_text_font(humid_label, &lv_font_unscii_8, 0);
|
||||
lv_obj_align(humid_label, LV_ALIGN_TOP_LEFT, 3, 35); // 调整位置
|
||||
|
||||
/* 创建光照标签 */
|
||||
lux_label = lv_label_create(scr);
|
||||
lv_label_set_text(lux_label, "Light: --.- lux");
|
||||
lv_obj_set_style_text_color(lux_label, lv_color_black(), 0);
|
||||
lv_obj_set_style_text_font(lux_label, &lv_font_unscii_8, 0);
|
||||
lv_obj_align(lux_label, LV_ALIGN_TOP_LEFT, 3, 50); // 调整位置
|
||||
|
||||
/* 创建空气质量标签 */
|
||||
air_quality_label = lv_label_create(scr);
|
||||
lv_label_set_text(air_quality_label, "IAQ : --.- Index");
|
||||
lv_obj_set_style_text_color(air_quality_label, lv_color_black(), 0);
|
||||
lv_obj_set_style_text_font(air_quality_label, &lv_font_unscii_8, 0);
|
||||
lv_obj_align(air_quality_label, LV_ALIGN_TOP_LEFT, 3, 65); // 调整位置
|
||||
|
||||
/* 任务解锁 */
|
||||
lvgl_port_unlock();
|
||||
|
||||
// 创建时间页面(初始隐藏)
|
||||
lvgl_port_lock(0);
|
||||
time_container = lv_obj_create(lv_scr_act());
|
||||
lv_obj_set_size(time_container, lv_pct(100), lv_pct(100));
|
||||
lv_obj_set_style_bg_color(time_container, lv_color_white(), 0);
|
||||
lv_obj_set_style_bg_opa(time_container, LV_OPA_COVER, 0);
|
||||
|
||||
date_label = lv_label_create(time_container);
|
||||
lv_label_set_text(date_label, "---- ---- -- ---");
|
||||
lv_obj_set_style_text_color(date_label, lv_color_black(), 0);
|
||||
lv_obj_set_style_text_font(date_label, &lv_font_unscii_8, 0);
|
||||
lv_obj_align(date_label, LV_ALIGN_TOP_MID, 0, 6);
|
||||
|
||||
time_label = lv_label_create(time_container);
|
||||
lv_label_set_text(time_label, "--:--:--");
|
||||
lv_obj_set_style_text_color(time_label, lv_color_black(), 0);
|
||||
lv_obj_set_style_text_font(time_label, &lv_font_unscii_16, 0);
|
||||
lv_obj_align(time_label, LV_ALIGN_CENTER, 0, 12);
|
||||
|
||||
// 默认显示时间页面,隐藏传感器页面
|
||||
lv_obj_clear_flag(time_container, LV_OBJ_FLAG_HIDDEN);
|
||||
if (temp_label) lv_obj_add_flag(temp_label, LV_OBJ_FLAG_HIDDEN);
|
||||
if (humid_label) lv_obj_add_flag(humid_label, LV_OBJ_FLAG_HIDDEN);
|
||||
if (lux_label) lv_obj_add_flag(lux_label, LV_OBJ_FLAG_HIDDEN);
|
||||
if (air_quality_label) lv_obj_add_flag(air_quality_label, LV_OBJ_FLAG_HIDDEN);
|
||||
time_page_visible = true;
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新传感器数据显示
|
||||
*
|
||||
* 该函数用于更新LVGL界面上的传感器数据
|
||||
*
|
||||
* @param temperature 温度值(°C),-1.0表示无效
|
||||
* @param humidity 湿度值(%),-1.0表示无效
|
||||
* @param lux 光照强度(lx),-1.0表示无效
|
||||
* @param ppm 空气中有害气体浓度(ppm)
|
||||
* @param quality_level 空气质量等级描述
|
||||
*/
|
||||
void ui_update_sensor_data(float temperature, float humidity, float lux, float ppm, const char* quality_level)
|
||||
{
|
||||
if (temp_label != NULL && humid_label != NULL && lux_label != NULL && air_quality_label != NULL)
|
||||
{
|
||||
/* 任务锁定 */
|
||||
lvgl_port_lock(0);
|
||||
|
||||
// 更新温度标签 - 缩短文本以节省空间
|
||||
if (temperature >= -0.5) // -1.0表示无效
|
||||
{
|
||||
char temp_str[32];
|
||||
snprintf(temp_str, sizeof(temp_str), "Temp: %.2f C", temperature);
|
||||
lv_label_set_text(temp_label, temp_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
lv_label_set_text(temp_label, "Temp: Invalid");
|
||||
}
|
||||
|
||||
// 更新湿度标签 - 缩短文本以节省空间
|
||||
if (humidity >= -0.5) // -1.0表示无效
|
||||
{
|
||||
char humid_str[32];
|
||||
snprintf(humid_str, sizeof(humid_str), "Humidity: %.2f %%", humidity);
|
||||
lv_label_set_text(humid_label, humid_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
lv_label_set_text(humid_label, "Humidity: Invalid");
|
||||
}
|
||||
|
||||
// 更新光照标签 - 缩短文本以节省空间
|
||||
if (lux >= -0.5) // -1.0表示无效
|
||||
{
|
||||
char lux_str[32];
|
||||
snprintf(lux_str, sizeof(lux_str), "Light: %.2f lx", lux);
|
||||
lv_label_set_text(lux_label, lux_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
lv_label_set_text(lux_label, "Light: Invalid");
|
||||
}
|
||||
|
||||
// 更新空气质量标签 - 缩短文本以节省空间
|
||||
if (ppm >= 0) // 空气质量值有效
|
||||
{
|
||||
char ppm_str[32];
|
||||
snprintf(ppm_str, sizeof(ppm_str), "IAQ : %.2f Index", ppm);
|
||||
|
||||
// 根据空气质量等级更改颜色
|
||||
lv_color_t color = lv_color_black(); // 默认黑色
|
||||
if (ppm <= 20.0f) {
|
||||
color = lv_color_make(0, 128, 0); // 绿色 - 空气质量优秀
|
||||
} else if (ppm <= 100.0f) {
|
||||
color = lv_color_make(0, 0, 0); // 黑色 - 空气质量良好
|
||||
} else if (ppm <= 300.0f) {
|
||||
color = lv_color_make(255, 165, 0); // 橙色 - 轻度污染
|
||||
} else {
|
||||
color = lv_color_make(255, 0, 0); // 红色 - 中重度污染
|
||||
}
|
||||
|
||||
lv_label_set_text(air_quality_label, ppm_str);
|
||||
lv_obj_set_style_text_color(air_quality_label, color, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
lv_label_set_text(air_quality_label, "IAQ : Invalid");
|
||||
lv_obj_set_style_text_color(air_quality_label, lv_color_black(), 0);
|
||||
}
|
||||
|
||||
/* 任务解锁 */
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/* Show time page */
|
||||
void ui_show_time_page(void)
|
||||
{
|
||||
lvgl_port_lock(0);
|
||||
if (time_container)
|
||||
{
|
||||
lv_obj_clear_flag(time_container, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
if (temp_label) lv_obj_add_flag(temp_label, LV_OBJ_FLAG_HIDDEN);
|
||||
if (humid_label) lv_obj_add_flag(humid_label, LV_OBJ_FLAG_HIDDEN);
|
||||
if (lux_label) lv_obj_add_flag(lux_label, LV_OBJ_FLAG_HIDDEN);
|
||||
if (air_quality_label) lv_obj_add_flag(air_quality_label, LV_OBJ_FLAG_HIDDEN);
|
||||
time_page_visible = true;
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
/* Show sensor page */
|
||||
void ui_show_sensor_page(void)
|
||||
{
|
||||
lvgl_port_lock(0);
|
||||
if (time_container)
|
||||
{
|
||||
lv_obj_add_flag(time_container, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
if (temp_label) lv_obj_clear_flag(temp_label, LV_OBJ_FLAG_HIDDEN);
|
||||
if (humid_label) lv_obj_clear_flag(humid_label, LV_OBJ_FLAG_HIDDEN);
|
||||
if (lux_label) lv_obj_clear_flag(lux_label, LV_OBJ_FLAG_HIDDEN);
|
||||
if (air_quality_label) lv_obj_clear_flag(air_quality_label, LV_OBJ_FLAG_HIDDEN);
|
||||
time_page_visible = false;
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
/* Toggle between pages */
|
||||
void ui_toggle_page(void)
|
||||
{
|
||||
if (time_page_visible)
|
||||
ui_show_sensor_page();
|
||||
else
|
||||
ui_show_time_page();
|
||||
}
|
||||
|
||||
/* Update time label (call periodically) */
|
||||
void ui_time_update(void)
|
||||
{
|
||||
if (!time_page_visible || time_label == NULL)
|
||||
return;
|
||||
|
||||
time_t now = time(NULL);
|
||||
struct tm tm_now;
|
||||
localtime_r(&now, &tm_now);
|
||||
|
||||
char date_buf[64];
|
||||
char time_buf[32];
|
||||
char weekday_buf[32];
|
||||
|
||||
// 年月日和星期合并为一行
|
||||
strftime(date_buf, sizeof(date_buf), "%Y-%m-%d", &tm_now);
|
||||
const char *weekdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
|
||||
snprintf(date_buf + strlen(date_buf), sizeof(date_buf) - strlen(date_buf), " %s", weekdays[tm_now.tm_wday]);
|
||||
// 时分秒
|
||||
strftime(time_buf, sizeof(time_buf), "%H:%M:%S", &tm_now);
|
||||
|
||||
lvgl_port_lock(0);
|
||||
lv_label_set_text(date_label, date_buf);
|
||||
lv_label_set_text(time_label, time_buf);
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
Reference in New Issue
Block a user