Files
xInfo/main/DisplayManager.cpp
T

269 lines
10 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file DisplayManager.cpp
* @brief 环境监测与网络信息墨水屏 UI 绘制实现。
*/
#include "DisplayManager.h"
#include "ChineseFont.h"
#include "EPD.h"
#include "EPD_SPI.h"
#include "GUI_Paint.h"
#include "esp_log.h"
#include <cstdio>
#include <cstring>
#include <cstdlib>
static const char *TAG = "DisplayManager";
// 帧缓冲区
static unsigned char ImageBW[EPD_ARRAY]; ///< 黑白像素缓冲
#ifdef CONFIG_DISPLAY_BWR
static unsigned char ImageRW[EPD_ARRAY]; ///< 红白像素缓冲(仅 BWR 模式使用)
#endif
DisplayManager::DisplayManager() {}
DisplayManager::~DisplayManager() {}
void DisplayManager::initHardware() {
EPD_SPI_Init();
}
void DisplayManager::beginDrawing() {
Paint_NewImage(ImageBW, EPD_WIDTH, EPD_HEIGHT, 0, WHITE);
Paint_SelectImage(ImageBW);
Paint_Clear(WHITE);
#ifdef CONFIG_DISPLAY_BWR
Paint_NewImage(ImageRW, EPD_WIDTH, EPD_HEIGHT, 0, WHITE);
Paint_SelectImage(ImageRW);
Paint_Clear(WHITE);
Paint_SelectImage(ImageBW);
#endif
}
void DisplayManager::updateFullAndSleep() {
#ifdef CONFIG_DISPLAY_BWR
ESP_LOGI(TAG, "全量三色刷新墨水屏...");
EPD_HW_Init_GUI();
EPD_Display(ImageBW, ImageRW);
#else
ESP_LOGI(TAG, "全量黑白刷新墨水屏...");
EPD_HW_Init_GUI();
static unsigned char emptyRW[EPD_ARRAY];
memset(emptyRW, 0xFF, EPD_ARRAY);
EPD_Display(ImageBW, emptyRW);
#endif
EPD_DeepSleep();
}
void DisplayManager::updateFastAndSleep() {
ESP_LOGI(TAG, "快速无闪烁刷新墨水屏(局部波形)...");
#ifdef CONFIG_DISPLAY_BWR
// 三色模式下理论上不支持无闪烁,退化处理
EPD_HW_Init_Fast();
EPD_WhiteScreen_ALL_Fast(ImageBW, ImageRW);
#else
// 【终极抗闪烁及死锁修复】
EPD_HW_Init_GUI();
// 修复导致硬件死锁在 Epaper_READBUSY() 的传参错误 Bug:
// 原厂 EPD_Dis_Part 函数的签名中:
// PART_COLUMN 代表的是 Y 轴(Height,需传 300)
// PART_LINE 代表的是 X 轴像素宽(Width,需传 400,内部会除以 8)
// 之前长宽传反,越界写寄存器触碰了驱动硬件的红线,导致永远不拉高 Busy 引脚
EPD_Dis_Part(0, 0, ImageBW, EPD_HEIGHT, EPD_WIDTH);
#endif
EPD_DeepSleep();
}
void DisplayManager::switchColor(bool toRed) {
#ifdef CONFIG_DISPLAY_BWR
Paint_SelectImage(toRed ? ImageRW : ImageBW);
#else
(void)toRed;
Paint_SelectImage(ImageBW);
#endif
}
void DisplayManager::drawTopBanner(const std::string &networkStatus) {
#ifdef CONFIG_DISPLAY_BWR
switchColor(true);
Paint_DrawRectangle(0, 0, EPD_WIDTH, 26, RED, DRAW_FILL_FULL, DOT_PIXEL_1X1);
Paint_DrawString_UTF8(10, 5, "xInfo 环境监测终端", Font16CN, WHITE, RED);
char buf[32];
snprintf(buf, sizeof(buf), "[%s]", networkStatus.c_str());
int strW = static_cast<int>(strlen(buf)) * 8;
int offsetX = EPD_WIDTH - strW - 10;
if (offsetX < 220) offsetX = 220;
Paint_DrawString_EN(offsetX, 8, buf, &Font12, WHITE, RED);
switchColor(false);
#else
Paint_DrawRectangle(0, 0, EPD_WIDTH, 26, BLACK, DRAW_FILL_FULL, DOT_PIXEL_1X1);
Paint_DrawString_UTF8(10, 5, "xInfo 环境监测终端", Font16CN, WHITE, BLACK);
char buf[32];
snprintf(buf, sizeof(buf), "[%s]", networkStatus.c_str());
int strW = static_cast<int>(strlen(buf)) * 8;
int offsetX = EPD_WIDTH - strW - 10;
if (offsetX < 220) offsetX = 220;
Paint_DrawString_EN(offsetX, 8, buf, &Font12, WHITE, BLACK);
#endif
}
void DisplayManager::drawThermoHygrometer(const DashboardData &data) {
char buf[32];
if (!data.lastReport.empty()) {
char timeStr[48];
snprintf(timeStr, sizeof(timeStr), "更新: %s", data.lastReport.c_str());
Paint_DrawString_UTF8(290, 34, timeStr, Font16CN, BLACK, WHITE);
}
Paint_DrawString_UTF8(15, 36, "室内温度", Font16CN, BLACK, WHITE);
if (!data.temperature.empty()) {
snprintf(buf, sizeof(buf), "%s", data.temperature.c_str());
Paint_DrawString_EN(15, 68, buf, &Font24, BLACK, WHITE);
int numW = static_cast<int>(strlen(buf)) * 17 + 12;
Paint_DrawCircle(numW + 8, 72, 3, BLACK, DRAW_FILL_EMPTY, DOT_PIXEL_1X1);
Paint_DrawString_EN(numW + 15, 70, "C", &Font20, BLACK, WHITE);
// 舒适度判断小标签
float tempVal = strtof(data.temperature.c_str(), nullptr);
if (tempVal < 18.0f) {
Paint_DrawString_UTF8(15, 115, "[偏凉]", Font16CN, BLACK, WHITE); // [偏凉]
} else if (tempVal <= 26.0f) {
Paint_DrawString_UTF8(15, 115, "[舒适]", Font16CN, BLACK, WHITE); // [舒适]
} else {
Paint_DrawString_UTF8(15, 115, "[偏热]", Font16CN, BLACK, WHITE); // [偏热]
}
} else {
Paint_DrawString_EN(15, 68, "--.-", &Font24, BLACK, WHITE);
}
// 中间竖向分隔线
Paint_DrawLine(200, 36, 200, 142, BLACK, LINE_STYLE_SOLID, DOT_PIXEL_1X1);
// 右侧:相对湿度看板 (X: 210 ~ 390)
Paint_DrawString_UTF8(215, 36, "相对湿度", Font16CN, BLACK, WHITE); // 相对湿度
if (!data.humidity.empty()) {
snprintf(buf, sizeof(buf), "%s%%", data.humidity.c_str());
Paint_DrawString_EN(215, 68, buf, &Font24, BLACK, WHITE);
// 湿度状态小标签
float humiVal = strtof(data.humidity.c_str(), nullptr);
if (humiVal < 40.0f) {
Paint_DrawString_UTF8(215, 115, "[干燥]", Font16CN, BLACK, WHITE); // [干燥]
} else if (humiVal <= 70.0f) {
Paint_DrawString_UTF8(215, 115, "[适宜]", Font16CN, BLACK, WHITE); // [适宜]
} else {
Paint_DrawString_UTF8(215, 115, "[潮湿]", Font16CN, BLACK, WHITE); // [潮湿]
}
} else {
Paint_DrawString_EN(215, 68, "--.-%", &Font24, BLACK, WHITE);
}
}
// =========================================================================
// 空气质量指标多维度展示区
// Y: 152 ~ 265
// =========================================================================
void DisplayManager::drawAirQuality(const DashboardData &data) {
// 横向分隔线
Paint_DrawLine(10, 150, EPD_WIDTH - 10, 150, BLACK, LINE_STYLE_SOLID, DOT_PIXEL_1X1);
// 区域标题
Paint_DrawString_UTF8(15, 156, "\xe7\xa9\xba\xe6\xb0\x94\xe8\xb4\xa8\xe9\x87\x8f\xe6\x8c\x87\xe6\xa0\x87", Font16CN, BLACK, WHITE); // 【 空气质量指标 】
// 第 1 列:CO2 (X: 15 ~ 125)
Paint_DrawString_EN(15, 180, "CO", &Font16, BLACK, WHITE);
Paint_DrawString_EN(37, 184, "2", &Font12, BLACK, WHITE);
Paint_DrawString_EN(47, 182, "(ppm)", &Font12, BLACK, WHITE);
if (!data.co2.empty()) {
Paint_DrawString_EN(15, 204, data.co2.c_str(), &Font20, BLACK, WHITE);
float co2Val = strtof(data.co2.c_str(), nullptr);
if (co2Val <= 800.0f) {
Paint_DrawString_UTF8(15, 236, "优", Font16CN, BLACK, WHITE); // 优
} else if (co2Val <= 1200.0f) {
Paint_DrawString_UTF8(15, 236, "良", Font16CN, BLACK, WHITE); // 良
} else {
Paint_DrawString_UTF8(15, 236, "需通风", Font16CN, BLACK, WHITE); // 需通风
}
} else {
Paint_DrawString_EN(15, 204, "--", &Font20, BLACK, WHITE);
}
// 列分割线 1
Paint_DrawLine(135, 180, 135, 255, BLACK, LINE_STYLE_DOTTED, DOT_PIXEL_1X1);
// 第 2 列:TVOC (X: 145 ~ 255)
Paint_DrawString_EN(145, 180, "TVOC", &Font16, BLACK, WHITE);
Paint_DrawString_EN(193, 182, "(mg/m", &Font12, BLACK, WHITE);
Paint_DrawString_EN(241, 179, "3", &Font8, BLACK, WHITE);
Paint_DrawString_EN(248, 182, ")", &Font12, BLACK, WHITE);
if (!data.tvoc.empty()) {
Paint_DrawString_EN(145, 204, data.tvoc.c_str(), &Font20, BLACK, WHITE);
float tvocVal = strtof(data.tvoc.c_str(), nullptr);
if (tvocVal <= 0.6f) {
Paint_DrawString_UTF8(145, 236, "正常", Font16CN, BLACK, WHITE); // 正常
} else {
Paint_DrawString_UTF8(145, 236, "超标", Font16CN, BLACK, WHITE); // 超标
}
} else {
Paint_DrawString_EN(145, 204, "--", &Font20, BLACK, WHITE);
}
// 列分割线 2
Paint_DrawLine(265, 180, 265, 255, BLACK, LINE_STYLE_DOTTED, DOT_PIXEL_1X1);
// 第 3 列:CH2O (X: 275 ~ 385)
Paint_DrawString_EN(275, 180, "CH", &Font16, BLACK, WHITE);
Paint_DrawString_EN(297, 184, "2", &Font12, BLACK, WHITE);
Paint_DrawString_EN(307, 180, "O", &Font16, BLACK, WHITE);
Paint_DrawString_EN(320, 182, "(mg/m", &Font12, BLACK, WHITE);
Paint_DrawString_EN(368, 179, "3", &Font8, BLACK, WHITE);
Paint_DrawString_EN(375, 182, ")", &Font12, BLACK, WHITE);
if (!data.ch2o.empty()) {
Paint_DrawString_EN(275, 204, data.ch2o.c_str(), &Font20, BLACK, WHITE);
float ch2oVal = strtof(data.ch2o.c_str(), nullptr);
if (ch2oVal <= 0.08f) {
Paint_DrawString_UTF8(275, 236, "安全", Font16CN, BLACK, WHITE); // 安全
} else {
Paint_DrawString_UTF8(275, 236, "超标", Font16CN, BLACK, WHITE); // 超标
}
} else {
Paint_DrawString_EN(275, 204, "--", &Font20, BLACK, WHITE);
}
}
// =========================================================================
// 底部网络及硬件信息栏
// Y: 268 ~ 300
// =========================================================================
void DisplayManager::drawBottomInfoBar(const std::string &ip,
const std::string &mac) {
Paint_DrawLine(0, EPD_HEIGHT - 25, EPD_WIDTH, EPD_HEIGHT - 25, BLACK,
LINE_STYLE_SOLID, DOT_PIXEL_1X1);
char buf[64];
snprintf(buf, sizeof(buf), "IP: %s", ip.empty() ? "0.0.0.0" : ip.c_str());
Paint_DrawString_EN(10, EPD_HEIGHT - 18, buf, &Font12, BLACK, WHITE);
snprintf(buf, sizeof(buf), "MAC: %s", mac.c_str());
int macX = EPD_WIDTH - static_cast<int>(strlen(buf)) * 7 - 10;
if (macX < 200) macX = 200;
Paint_DrawString_EN(macX, EPD_HEIGHT - 18, buf, &Font12, BLACK, WHITE);
}
// =========================================================================
// 错误页面
// =========================================================================
void DisplayManager::drawError(const std::string &errorMsg) {
Paint_DrawString_UTF8(10, 80, "系统异常", Font16CN, BLACK, WHITE); // 系统异常
Paint_DrawString_EN(10, 120, errorMsg.c_str(), &Font16, BLACK, WHITE);
Paint_DrawString_UTF8(10, 150, "将在下个周期重试...", Font16CN, BLACK, WHITE); // 将在下个周期重试...
}