From 29ad9936491aaee3f7f4aa7d4a34dafe5113ec76 Mon Sep 17 00:00:00 2001 From: seaHi Date: Tue, 1 Sep 2026 16:17:44 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=A2=A8=E6=B0=B4?= =?UTF-8?q?=E5=B1=8F=E5=B1=80=E9=83=A8=E5=88=B7=E6=96=B0=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E4=BC=A0=E5=8F=8D=E5=AF=BC=E8=87=B4=E7=9A=84=E6=AD=BB=E9=94=81?= =?UTF-8?q?=E5=B9=B6=E4=BC=98=E5=8C=96=E5=88=B7=E6=96=B0=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/App.cpp | 11 +++--- main/DashboardFetcher.h | 4 +-- main/DisplayManager.cpp | 78 +++++++++++++++++++---------------------- sdkconfig | 4 +-- 4 files changed, 47 insertions(+), 50 deletions(-) diff --git a/main/App.cpp b/main/App.cpp index 24921ee..979c9b1 100644 --- a/main/App.cpp +++ b/main/App.cpp @@ -86,10 +86,11 @@ void App::run() { #ifdef CONFIG_DISPLAY_BWR std::string redContent = ssid; - bool needFullRefresh = lastRedContent_.empty() - || (redContent != lastRedContent_) - || (fullRefreshCounter_ >= maxFastRefreshes); + // 修复 Bug: BWR 三色模式受限于硬件物理特性不支持无感快刷 + // 因此只要在三色模式下,强制每次更新都走全量三色刷新 + bool needFullRefresh = true; #else + // 纯黑白模式 (BW) 支持且可以正常享受无感快刷 bool needFullRefresh = (fullRefreshCounter_ == 0) || (fullRefreshCounter_ >= maxFastRefreshes); #endif @@ -99,7 +100,9 @@ void App::run() { #ifdef CONFIG_DISPLAY_BWR lastRedContent_ = redContent; #endif - fullRefreshCounter_ = 0; + // 修复 Bug:全刷完成后应把计数器重置为 1,不能是 0! + // 如果是 0,下个大循环碰到 `fullRefreshCounter_ == 0` 时又会触发全刷,导致无限连刷! + fullRefreshCounter_ = 1; } else { display_.updateFastAndSleep(); fullRefreshCounter_++; diff --git a/main/DashboardFetcher.h b/main/DashboardFetcher.h index 0e271a0..43b9c9c 100644 --- a/main/DashboardFetcher.h +++ b/main/DashboardFetcher.h @@ -27,12 +27,12 @@ struct DashboardData { /// 用于检测数据是否有变化,避免无意义的墨水屏全刷 bool operator==(const DashboardData &other) const { + // 注意:不比较 lastReport(时间),否则每分钟时间跳动都会打破跳刷机制,导致强制损耗寿命 return temperature == other.temperature && humidity == other.humidity && co2 == other.co2 && tvoc == other.tvoc && - ch2o == other.ch2o && - lastReport == other.lastReport; + ch2o == other.ch2o; } bool operator!=(const DashboardData &other) const { return !(*this == other); diff --git a/main/DisplayManager.cpp b/main/DisplayManager.cpp index 6ad3e4b..bc7e7a8 100644 --- a/main/DisplayManager.cpp +++ b/main/DisplayManager.cpp @@ -1,11 +1,6 @@ /** * @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" @@ -54,20 +49,29 @@ void DisplayManager::updateFullAndSleep() { #else ESP_LOGI(TAG, "全量黑白刷新墨水屏..."); EPD_HW_Init_GUI(); - static unsigned char emptyRW[EPD_ARRAY] = {0}; + static unsigned char emptyRW[EPD_ARRAY]; + memset(emptyRW, 0xFF, EPD_ARRAY); EPD_Display(ImageBW, emptyRW); #endif EPD_DeepSleep(); } void DisplayManager::updateFastAndSleep() { - ESP_LOGI(TAG, "快速刷新墨水屏..."); - EPD_HW_Init_Fast(); + ESP_LOGI(TAG, "快速无闪烁刷新墨水屏(局部波形)..."); #ifdef CONFIG_DISPLAY_BWR + // 三色模式下理论上不支持无闪烁,退化处理 + EPD_HW_Init_Fast(); EPD_WhiteScreen_ALL_Fast(ImageBW, ImageRW); #else - static unsigned char emptyRW[EPD_ARRAY] = {0}; - EPD_WhiteScreen_ALL_Fast(ImageBW, emptyRW); + // 【终极抗闪烁及死锁修复】 + 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(); } @@ -81,15 +85,11 @@ void DisplayManager::switchColor(bool toRed) { #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 环境监测终端 + Paint_DrawString_UTF8(10, 5, "xInfo 环境监测终端", Font16CN, WHITE, RED); char buf[32]; snprintf(buf, sizeof(buf), "[%s]", networkStatus.c_str()); @@ -101,7 +101,7 @@ void DisplayManager::drawTopBanner(const std::string &networkStatus) { 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 环境监测终端 + Paint_DrawString_UTF8(10, 5, "xInfo 环境监测终端", Font16CN, WHITE, BLACK); char buf[32]; snprintf(buf, sizeof(buf), "[%s]", networkStatus.c_str()); @@ -112,22 +112,16 @@ void DisplayManager::drawTopBanner(const std::string &networkStatus) { #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()); // 更新: + snprintf(timeStr, sizeof(timeStr), "更新: %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); // 室内温度 + 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); @@ -138,11 +132,11 @@ void DisplayManager::drawThermoHygrometer(const DashboardData &data) { // 舒适度判断小标签 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); // [偏凉] + Paint_DrawString_UTF8(15, 115, "[偏凉]", Font16CN, BLACK, WHITE); // [偏凉] } else if (tempVal <= 26.0f) { - Paint_DrawString_UTF8(15, 115, "[\xe8\x88\x92\xe9\x80\x82]", Font16CN, BLACK, WHITE); // [舒适] + Paint_DrawString_UTF8(15, 115, "[舒适]", Font16CN, BLACK, WHITE); // [舒适] } else { - Paint_DrawString_UTF8(15, 115, "[\xe5\x81\x8f\xe7\x83\xad]", Font16CN, BLACK, WHITE); // [偏热] + Paint_DrawString_UTF8(15, 115, "[偏热]", Font16CN, BLACK, WHITE); // [偏热] } } else { Paint_DrawString_EN(15, 68, "--.-", &Font24, BLACK, WHITE); @@ -152,7 +146,7 @@ void DisplayManager::drawThermoHygrometer(const DashboardData &data) { 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); // 相对湿度 + 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); @@ -160,11 +154,11 @@ void DisplayManager::drawThermoHygrometer(const DashboardData &data) { // 湿度状态小标签 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); // [干燥] + Paint_DrawString_UTF8(215, 115, "[干燥]", Font16CN, BLACK, WHITE); // [干燥] } else if (humiVal <= 70.0f) { - Paint_DrawString_UTF8(215, 115, "[\xe9\x80\x82\xe5\xae\x9c]", Font16CN, BLACK, WHITE); // [适宜] + Paint_DrawString_UTF8(215, 115, "[适宜]", Font16CN, BLACK, WHITE); // [适宜] } else { - Paint_DrawString_UTF8(215, 115, "[\xe6\xbd\xae\xe6\xb9\xbf]", Font16CN, BLACK, WHITE); // [潮湿] + Paint_DrawString_UTF8(215, 115, "[潮湿]", Font16CN, BLACK, WHITE); // [潮湿] } } else { Paint_DrawString_EN(215, 68, "--.-%", &Font24, BLACK, WHITE); @@ -180,7 +174,7 @@ 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); // 【 空气质量指标 】 + 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); @@ -191,11 +185,11 @@ void DisplayManager::drawAirQuality(const DashboardData &data) { 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); // 优 + Paint_DrawString_UTF8(15, 236, "优", Font16CN, BLACK, WHITE); // 优 } else if (co2Val <= 1200.0f) { - Paint_DrawString_UTF8(15, 236, "\xe8\x89\xaf", Font16CN, BLACK, WHITE); // 良 + Paint_DrawString_UTF8(15, 236, "良", Font16CN, BLACK, WHITE); // 良 } else { - Paint_DrawString_UTF8(15, 236, "\xe9\x9c\x80\xe9\x80\x9a\xe9\xa3\x8e", Font16CN, BLACK, WHITE); // 需通风 + Paint_DrawString_UTF8(15, 236, "需通风", Font16CN, BLACK, WHITE); // 需通风 } } else { Paint_DrawString_EN(15, 204, "--", &Font20, BLACK, WHITE); @@ -214,9 +208,9 @@ void DisplayManager::drawAirQuality(const DashboardData &data) { 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); // 正常 + Paint_DrawString_UTF8(145, 236, "正常", Font16CN, BLACK, WHITE); // 正常 } else { - Paint_DrawString_UTF8(145, 236, "\xe8\xb6\x85\xe6\xa0\x87", Font16CN, BLACK, WHITE); // 超标 + Paint_DrawString_UTF8(145, 236, "超标", Font16CN, BLACK, WHITE); // 超标 } } else { Paint_DrawString_EN(145, 204, "--", &Font20, BLACK, WHITE); @@ -237,9 +231,9 @@ void DisplayManager::drawAirQuality(const DashboardData &data) { 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); // 安全 + Paint_DrawString_UTF8(275, 236, "安全", Font16CN, BLACK, WHITE); // 安全 } else { - Paint_DrawString_UTF8(275, 236, "\xe8\xb6\x85\xe6\xa0\x87", Font16CN, BLACK, WHITE); // 超标 + Paint_DrawString_UTF8(275, 236, "超标", Font16CN, BLACK, WHITE); // 超标 } } else { Paint_DrawString_EN(275, 204, "--", &Font20, BLACK, WHITE); @@ -269,7 +263,7 @@ void DisplayManager::drawBottomInfoBar(const std::string &ip, // 错误页面 // ========================================================================= 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_UTF8(10, 80, "系统异常", 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); // 将在下个周期重试... -} + Paint_DrawString_UTF8(10, 150, "将在下个周期重试...", Font16CN, BLACK, WHITE); // 将在下个周期重试... +} \ No newline at end of file diff --git a/sdkconfig b/sdkconfig index 2e4bf89..43096b4 100644 --- a/sdkconfig +++ b/sdkconfig @@ -828,8 +828,8 @@ CONFIG_NTP_SYNC_TIMEOUT_SEC=15 # # 硬件与显示配置 # -CONFIG_DISPLAY_BWR=y -# CONFIG_DISPLAY_BW is not set +# CONFIG_DISPLAY_BWR is not set +CONFIG_DISPLAY_BW=y CONFIG_DISPLAY_REFRESH_INTERVAL_MIN=1 # end of 硬件与显示配置