276 lines
11 KiB
C++
276 lines
11 KiB
C++
/**
|
||
* @file DisplayManager.cpp
|
||
* @brief 环境监测与网络信息墨水屏 UI 绘制实现。
|
||
*
|
||
* 屏幕:400×300,通过 CONFIG_DISPLAY_BWR / CONFIG_DISPLAY_BW 编译宏
|
||
* 区分三色(黑/白/红)和纯黑白两种模式。
|
||
* 英文字体:Font8, Font12, Font16, Font20, Font24
|
||
* 中文字体:Font16CN(Unifont 16×16)
|
||
*/
|
||
#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] = {0};
|
||
EPD_Display(ImageBW, emptyRW);
|
||
#endif
|
||
EPD_DeepSleep();
|
||
}
|
||
|
||
void DisplayManager::updateFastAndSleep() {
|
||
ESP_LOGI(TAG, "快速刷新墨水屏...");
|
||
EPD_HW_Init_Fast();
|
||
#ifdef CONFIG_DISPLAY_BWR
|
||
EPD_WhiteScreen_ALL_Fast(ImageBW, ImageRW);
|
||
#else
|
||
static unsigned char emptyRW[EPD_ARRAY] = {0};
|
||
EPD_WhiteScreen_ALL_Fast(ImageBW, emptyRW);
|
||
#endif
|
||
EPD_DeepSleep();
|
||
}
|
||
|
||
void DisplayManager::switchColor(bool toRed) {
|
||
#ifdef CONFIG_DISPLAY_BWR
|
||
Paint_SelectImage(toRed ? ImageRW : ImageBW);
|
||
#else
|
||
(void)toRed;
|
||
Paint_SelectImage(ImageBW);
|
||
#endif
|
||
}
|
||
|
||
// =========================================================================
|
||
// 顶部横幅:标题 + WiFi 状态
|
||
// Y: 0 ~ 28
|
||
// =========================================================================
|
||
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 \xe7\x8e\xaf\xe5\xa2\x83\xe7\x9b\x91\xe6\xb5\x8b\xe7\xbb\x88\xe7\xab\xaf", Font16CN, WHITE, RED); // xInfo 环境监测终端
|
||
|
||
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 \xe7\x8e\xaf\xe5\xa2\x83\xe7\x9b\x91\xe6\xb5\x8b\xe7\xbb\x88\xe7\xab\xaf", Font16CN, WHITE, BLACK); // xInfo 环境监测终端
|
||
|
||
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
|
||
}
|
||
|
||
// =========================================================================
|
||
// 核心温湿度看板区
|
||
// Y: 32 ~ 148
|
||
// =========================================================================
|
||
void DisplayManager::drawThermoHygrometer(const DashboardData &data) {
|
||
char buf[32];
|
||
|
||
// 更新时间(靠右显示)
|
||
if (!data.lastReport.empty()) {
|
||
char timeStr[48];
|
||
snprintf(timeStr, sizeof(timeStr), "\xe6\x9b\xb4\xe6\x96\xb0: %s", data.lastReport.c_str()); // 更新:
|
||
Paint_DrawString_UTF8(290, 34, timeStr, Font16CN, BLACK, WHITE);
|
||
}
|
||
|
||
// 左侧:室内温度看板 (X: 10 ~ 190)
|
||
Paint_DrawString_UTF8(15, 36, "\xe5\xae\xa4\xe5\x86\x85\xe6\xb8\xa9\xe5\xba\xa6", 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, "[\xe5\x81\x8f\xe5\x87\x89]", Font16CN, BLACK, WHITE); // [偏凉]
|
||
} else if (tempVal <= 26.0f) {
|
||
Paint_DrawString_UTF8(15, 115, "[\xe8\x88\x92\xe9\x80\x82]", Font16CN, BLACK, WHITE); // [舒适]
|
||
} else {
|
||
Paint_DrawString_UTF8(15, 115, "[\xe5\x81\x8f\xe7\x83\xad]", 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, "\xe7\x9b\xb8\xe5\xaf\xb9\xe6\xb9\xbf\xe5\xba\xa6", 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, "[\xe5\xb9\xb2\xe7\x87\xa5]", Font16CN, BLACK, WHITE); // [干燥]
|
||
} else if (humiVal <= 70.0f) {
|
||
Paint_DrawString_UTF8(215, 115, "[\xe9\x80\x82\xe5\xae\x9c]", Font16CN, BLACK, WHITE); // [适宜]
|
||
} else {
|
||
Paint_DrawString_UTF8(215, 115, "[\xe6\xbd\xae\xe6\xb9\xbf]", 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(10, 156, "\xe3\x80\x90 \xe7\xa9\xba\xe6\xb0\x94\xe8\xb2\xb4\xe9\x87\x8f\xe6\x8c\x87\xe6\xa0\x87 \xe3\x80\x91", 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, "\xe4\xbc\x98", Font16CN, BLACK, WHITE); // 优
|
||
} else if (co2Val <= 1200.0f) {
|
||
Paint_DrawString_UTF8(15, 236, "\xe8\x89\xaf", Font16CN, BLACK, WHITE); // 良
|
||
} else {
|
||
Paint_DrawString_UTF8(15, 236, "\xe9\x9c\x80\xe9\x80\x9a\xe9\xa3\x8e", 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, "\xe6\xad\xa3\xe5\xb8\xb8", Font16CN, BLACK, WHITE); // 正常
|
||
} else {
|
||
Paint_DrawString_UTF8(145, 236, "\xe8\xb6\x85\xe6\xa0\x87", 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, "\xe5\xae\x89\xe5\x85\xa8", Font16CN, BLACK, WHITE); // 安全
|
||
} else {
|
||
Paint_DrawString_UTF8(275, 236, "\xe8\xb6\x85\xe6\xa0\x87", 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, "\xe7\xb3\xbb\xe7\xbb\x9f\xe5\xbc\x82\xe5\xb8\xb8", Font16CN, BLACK, WHITE); // 系统异常
|
||
Paint_DrawString_EN(10, 120, errorMsg.c_str(), &Font16, BLACK, WHITE);
|
||
Paint_DrawString_UTF8(10, 150, "\xe5\xb0\x86\xe5\x9c\xa8\xe4\xb8\x8b\xe4\xb8\xaa\xe5\x91\xa8\xe6\x9c\x9f\xe9\x87\x8d\xe8\xaf\x95...", Font16CN, BLACK, WHITE); // 将在下个周期重试...
|
||
}
|