From 696323c11bc96767726d429270dc58fe95d3941a Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Fri, 6 Mar 2026 02:58:39 +0100 Subject: [PATCH 1/2] Fix Heltec E213 and E290 e-ink board builds --- src/helpers/ui/E213Display.cpp | 54 +++++++++++++++---- src/helpers/ui/E213Display.h | 9 +++- src/helpers/ui/E290Display.cpp | 51 +++++++++++++++--- src/helpers/ui/E290Display.h | 9 +++- variants/heltec_e213/platformio.ini | 18 ++++--- variants/heltec_e213/target.cpp | 2 +- variants/heltec_e290/HeltecE290Board.h | 2 +- variants/heltec_e290/platformio.ini | 18 ++++--- variants/heltec_e290/target.cpp | 2 +- variants/heltec_wireless_paper/platformio.ini | 4 ++ 10 files changed, 132 insertions(+), 37 deletions(-) diff --git a/src/helpers/ui/E213Display.cpp b/src/helpers/ui/E213Display.cpp index a0e71f31aa..5f04e685d4 100644 --- a/src/helpers/ui/E213Display.cpp +++ b/src/helpers/ui/E213Display.cpp @@ -59,44 +59,55 @@ bool E213Display::begin() { } void E213Display::powerOn() { + if (_periph_power) { + _periph_power->claim(); + } else { #ifdef PIN_VEXT_EN - pinMode(PIN_VEXT_EN, OUTPUT); + pinMode(PIN_VEXT_EN, OUTPUT); #ifdef PIN_VEXT_EN_ACTIVE - digitalWrite(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE); + digitalWrite(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE); #else - digitalWrite(PIN_VEXT_EN, LOW); // Active low + digitalWrite(PIN_VEXT_EN, LOW); // Active low #endif - delay(50); // Allow power to stabilize #endif + } + delay(50); // Allow power to stabilize } void E213Display::powerOff() { + if (_periph_power) { + _periph_power->release(); + } else { #ifdef PIN_VEXT_EN #ifdef PIN_VEXT_EN_ACTIVE - digitalWrite(PIN_VEXT_EN, !PIN_VEXT_EN_ACTIVE); + digitalWrite(PIN_VEXT_EN, !PIN_VEXT_EN_ACTIVE); #else - digitalWrite(PIN_VEXT_EN, HIGH); // Turn off power + digitalWrite(PIN_VEXT_EN, HIGH); // Turn off power #endif #endif + } } void E213Display::turnOn() { if (!_init) begin(); - powerOn(); + else if (!_isOn) powerOn(); _isOn = true; } void E213Display::turnOff() { - powerOff(); - _isOn = false; + if (_isOn) { + powerOff(); + _isOn = false; + } } void E213Display::clear() { display->clear(); - } void E213Display::startFrame(Color bkg) { + display_crc.reset(); + // Fill screen with white first to ensure clean background display->fillRect(0, 0, width(), height(), WHITE); @@ -107,31 +118,50 @@ void E213Display::startFrame(Color bkg) { } void E213Display::setTextSize(int sz) { + display_crc.update(sz); // The library handles text size internally display->setTextSize(sz); } void E213Display::setColor(Color c) { + display_crc.update(c); // implemented in individual display methods } void E213Display::setCursor(int x, int y) { + display_crc.update(x); + display_crc.update(y); display->setCursor(x, y); } void E213Display::print(const char *str) { + display_crc.update(str, strlen(str)); display->print(str); } void E213Display::fillRect(int x, int y, int w, int h) { + display_crc.update(x); + display_crc.update(y); + display_crc.update(w); + display_crc.update(h); display->fillRect(x, y, w, h, BLACK); } void E213Display::drawRect(int x, int y, int w, int h) { + display_crc.update(x); + display_crc.update(y); + display_crc.update(w); + display_crc.update(h); display->drawRect(x, y, w, h, BLACK); } void E213Display::drawXbm(int x, int y, const uint8_t *bits, int w, int h) { + display_crc.update(x); + display_crc.update(y); + display_crc.update(w); + display_crc.update(h); + display_crc.update(bits, w * h / 8); + // Width in bytes for bitmap processing uint16_t widthInBytes = (w + 7) / 8; @@ -160,5 +190,9 @@ uint16_t E213Display::getTextWidth(const char *str) { } void E213Display::endFrame() { + uint32_t crc = display_crc.finalize(); + if (crc != last_display_crc_value) { display->update(); + last_display_crc_value = crc; + } } diff --git a/src/helpers/ui/E213Display.h b/src/helpers/ui/E213Display.h index 657bfb4c7e..420792c8a3 100644 --- a/src/helpers/ui/E213Display.h +++ b/src/helpers/ui/E213Display.h @@ -5,15 +5,20 @@ #include #include #include +#include +#include // Display driver for E213 e-ink display class E213Display : public DisplayDriver { BaseDisplay* display=NULL; bool _init = false; bool _isOn = false; + RefCountedDigitalPin* _periph_power; + CRC32 display_crc; + uint32_t last_display_crc_value = 0; public: - E213Display() : DisplayDriver(250, 122) {} + E213Display(RefCountedDigitalPin* periph_power = NULL) : DisplayDriver(250, 122), _periph_power(periph_power) {} ~E213Display(){ if(display!=NULL) { delete display; @@ -39,4 +44,4 @@ class E213Display : public DisplayDriver { BaseDisplay* detectEInk(); void powerOn(); void powerOff(); -}; \ No newline at end of file +}; diff --git a/src/helpers/ui/E290Display.cpp b/src/helpers/ui/E290Display.cpp index 23ff2d9532..529f790952 100644 --- a/src/helpers/ui/E290Display.cpp +++ b/src/helpers/ui/E290Display.cpp @@ -21,28 +21,38 @@ bool E290Display::begin() { } void E290Display::powerOn() { + if (_periph_power) { + _periph_power->claim(); + } else { #ifdef PIN_VEXT_EN - pinMode(PIN_VEXT_EN, OUTPUT); - digitalWrite(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE); - delay(50); // Allow power to stabilize + pinMode(PIN_VEXT_EN, OUTPUT); + digitalWrite(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE); #endif + } + delay(50); // Allow power to stabilize } void E290Display::powerOff() { + if (_periph_power) { + _periph_power->release(); + } else { #ifdef PIN_VEXT_EN - digitalWrite(PIN_VEXT_EN, !PIN_VEXT_EN_ACTIVE); // Turn off power + digitalWrite(PIN_VEXT_EN, !PIN_VEXT_EN_ACTIVE); // Turn off power #endif + } } void E290Display::turnOn() { if (!_init) begin(); - powerOn(); + else if (!_isOn) powerOn(); _isOn = true; } void E290Display::turnOff() { - powerOff(); - _isOn = false; + if (_isOn) { + powerOff(); + _isOn = false; + } } void E290Display::clear() { @@ -50,6 +60,8 @@ void E290Display::clear() { } void E290Display::startFrame(Color bkg) { + display_crc.reset(); + // Fill screen with white first to ensure clean background display.fillRect(0, 0, width(), height(), WHITE); if (bkg == LIGHT) { @@ -59,31 +71,50 @@ void E290Display::startFrame(Color bkg) { } void E290Display::setTextSize(int sz) { + display_crc.update(sz); // The library handles text size internally display.setTextSize(sz); } void E290Display::setColor(Color c) { + display_crc.update(c); // implemented in individual display methods } void E290Display::setCursor(int x, int y) { + display_crc.update(x); + display_crc.update(y); display.setCursor(x, y); } void E290Display::print(const char *str) { + display_crc.update(str, strlen(str)); display.print(str); } void E290Display::fillRect(int x, int y, int w, int h) { + display_crc.update(x); + display_crc.update(y); + display_crc.update(w); + display_crc.update(h); display.fillRect(x, y, w, h, BLACK); } void E290Display::drawRect(int x, int y, int w, int h) { + display_crc.update(x); + display_crc.update(y); + display_crc.update(w); + display_crc.update(h); display.drawRect(x, y, w, h, BLACK); } void E290Display::drawXbm(int x, int y, const uint8_t *bits, int w, int h) { + display_crc.update(x); + display_crc.update(y); + display_crc.update(w); + display_crc.update(h); + display_crc.update(bits, w * h / 8); + // Width in bytes for bitmap processing uint16_t widthInBytes = (w + 7) / 8; @@ -112,5 +143,9 @@ uint16_t E290Display::getTextWidth(const char *str) { } void E290Display::endFrame() { - display.update(); + uint32_t crc = display_crc.finalize(); + if (crc != last_display_crc_value) { + display.update(); + last_display_crc_value = crc; + } } diff --git a/src/helpers/ui/E290Display.h b/src/helpers/ui/E290Display.h index 16f4538223..2ca50225d1 100644 --- a/src/helpers/ui/E290Display.h +++ b/src/helpers/ui/E290Display.h @@ -5,15 +5,20 @@ #include #include #include +#include +#include // Display driver for E290 e-ink display class E290Display : public DisplayDriver { EInkDisplay_VisionMasterE290 display; bool _init = false; bool _isOn = false; + RefCountedDigitalPin* _periph_power; + CRC32 display_crc; + uint32_t last_display_crc_value = 0; public: - E290Display() : DisplayDriver(296, 128) {} + E290Display(RefCountedDigitalPin* periph_power = NULL) : DisplayDriver(296, 128), _periph_power(periph_power) {} bool begin(); bool isOn() override { return _isOn; } @@ -34,4 +39,4 @@ class E290Display : public DisplayDriver { private: void powerOn(); void powerOff(); -}; \ No newline at end of file +}; diff --git a/variants/heltec_e213/platformio.ini b/variants/heltec_e213/platformio.ini index caba3a30e0..cfa3362f1c 100644 --- a/variants/heltec_e213/platformio.ini +++ b/variants/heltec_e213/platformio.ini @@ -40,7 +40,7 @@ lib_deps = ${esp32_base.lib_deps} https://github.com/Quency-D/heltec-eink-modules/archive/563dd41fd850a1bc3039b8723da4f3a20fe1c800.zip -[env:Heltec_E213_companion_radio_ble_] +[env:Heltec_E213_companion_radio_ble] extends = Heltec_E213_base build_flags = ${Heltec_E213_base.build_flags} @@ -59,8 +59,9 @@ build_src_filter = ${Heltec_E213_base.build_src_filter} lib_deps = ${Heltec_E213_base.lib_deps} densaugeo/base64 @ ~1.4.0 + bakercp/CRC32 @ ^2.0.0 -[env:Heltec_E213_companion_radio_usb_] +[env:Heltec_E213_companion_radio_usb] extends = Heltec_E213_base build_flags = ${Heltec_E213_base.build_flags} @@ -77,8 +78,9 @@ build_src_filter = ${Heltec_E213_base.build_src_filter} lib_deps = ${Heltec_E213_base.lib_deps} densaugeo/base64 @ ~1.4.0 + bakercp/CRC32 @ ^2.0.0 -[env:Heltec_E213_repeater_] +[env:Heltec_E213_repeater] extends = Heltec_E213_base build_flags = ${Heltec_E213_base.build_flags} @@ -94,8 +96,9 @@ build_src_filter = ${Heltec_E213_base.build_src_filter} lib_deps = ${Heltec_E213_base.lib_deps} ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 -; [env:Heltec_E213_repeater_bridge_rs232_] +; [env:Heltec_E213_repeater_bridge_rs232] ; extends = Heltec_E213_base ; build_flags = ; ${Heltec_E213_base.build_flags} @@ -118,8 +121,9 @@ lib_deps = ; lib_deps = ; ${Heltec_E213_base.lib_deps} ; ${esp32_ota.lib_deps} +; bakercp/CRC32 @ ^2.0.0 -[env:Heltec_E213_repeater_bridge_espnow_] +[env:Heltec_E213_repeater_bridge_espnow] extends = Heltec_E213_base build_flags = ${Heltec_E213_base.build_flags} @@ -140,8 +144,9 @@ build_src_filter = ${Heltec_E213_base.build_src_filter} lib_deps = ${Heltec_E213_base.lib_deps} ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 -[env:Heltec_E213_room_server_] +[env:Heltec_E213_room_server] extends = Heltec_E213_base build_flags = ${Heltec_E213_base.build_flags} @@ -157,3 +162,4 @@ build_src_filter = ${Heltec_E213_base.build_src_filter} lib_deps = ${Heltec_E213_base.lib_deps} ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 diff --git a/variants/heltec_e213/target.cpp b/variants/heltec_e213/target.cpp index c9233431cb..69c832fc6b 100644 --- a/variants/heltec_e213/target.cpp +++ b/variants/heltec_e213/target.cpp @@ -18,7 +18,7 @@ AutoDiscoverRTCClock rtc_clock(fallback_clock); SensorManager sensors; #ifdef DISPLAY_CLASS -DISPLAY_CLASS display; +DISPLAY_CLASS display(&board.periph_power); MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif diff --git a/variants/heltec_e290/HeltecE290Board.h b/variants/heltec_e290/HeltecE290Board.h index ff16e273a7..645ec3481a 100644 --- a/variants/heltec_e290/HeltecE290Board.h +++ b/variants/heltec_e290/HeltecE290Board.h @@ -10,7 +10,7 @@ class HeltecE290Board : public ESP32Board { public: RefCountedDigitalPin periph_power; - HeltecE290Board() : periph_power(PIN_VEXT_EN) { } + HeltecE290Board() : periph_power(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE) { } void begin(); void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); diff --git a/variants/heltec_e290/platformio.ini b/variants/heltec_e290/platformio.ini index 0c07c592c4..d6be213faa 100644 --- a/variants/heltec_e290/platformio.ini +++ b/variants/heltec_e290/platformio.ini @@ -34,7 +34,7 @@ lib_deps = ${esp32_base.lib_deps} https://github.com/Quency-D/heltec-eink-modules/archive/563dd41fd850a1bc3039b8723da4f3a20fe1c800.zip -[env:Heltec_E290_companion_ble_] +[env:Heltec_E290_companion_ble] extends = Heltec_E290_base build_flags = ${Heltec_E290_base.build_flags} @@ -53,8 +53,9 @@ build_src_filter = ${Heltec_E290_base.build_src_filter} lib_deps = ${Heltec_E290_base.lib_deps} densaugeo/base64 @ ~1.4.0 + bakercp/CRC32 @ ^2.0.0 -[env:Heltec_E290_companion_usb_] +[env:Heltec_E290_companion_usb] extends = Heltec_E290_base build_flags = ${Heltec_E290_base.build_flags} @@ -73,8 +74,9 @@ build_src_filter = ${Heltec_E290_base.build_src_filter} lib_deps = ${Heltec_E290_base.lib_deps} densaugeo/base64 @ ~1.4.0 + bakercp/CRC32 @ ^2.0.0 -[env:Heltec_E290_repeater_] +[env:Heltec_E290_repeater] extends = Heltec_E290_base build_flags = ${Heltec_E290_base.build_flags} @@ -90,8 +92,9 @@ build_src_filter = ${Heltec_E290_base.build_src_filter} lib_deps = ${Heltec_E290_base.lib_deps} ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 -; [env:Heltec_E290_repeater_bridge_rs232_] +; [env:Heltec_E290_repeater_bridge_rs232] ; extends = Heltec_E290_base ; build_flags = ; ${Heltec_E290_base.build_flags} @@ -114,8 +117,9 @@ lib_deps = ; lib_deps = ; ${Heltec_E290_base.lib_deps} ; ${esp32_ota.lib_deps} +; bakercp/CRC32 @ ^2.0.0 -[env:Heltec_E290_repeater_bridge_espnow_] +[env:Heltec_E290_repeater_bridge_espnow] extends = Heltec_E290_base build_flags = ${Heltec_E290_base.build_flags} @@ -136,8 +140,9 @@ build_src_filter = ${Heltec_E290_base.build_src_filter} lib_deps = ${Heltec_E290_base.lib_deps} ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 -[env:Heltec_E290_room_server_] +[env:Heltec_E290_room_server] extends = Heltec_E290_base build_flags = ${Heltec_E290_base.build_flags} @@ -153,3 +158,4 @@ build_src_filter = ${Heltec_E290_base.build_src_filter} lib_deps = ${Heltec_E290_base.lib_deps} ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 diff --git a/variants/heltec_e290/target.cpp b/variants/heltec_e290/target.cpp index b0c9630cf2..c2220b159b 100644 --- a/variants/heltec_e290/target.cpp +++ b/variants/heltec_e290/target.cpp @@ -18,7 +18,7 @@ AutoDiscoverRTCClock rtc_clock(fallback_clock); SensorManager sensors; #ifdef DISPLAY_CLASS -DISPLAY_CLASS display; +DISPLAY_CLASS display(&board.periph_power); MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif diff --git a/variants/heltec_wireless_paper/platformio.ini b/variants/heltec_wireless_paper/platformio.ini index f0bca86047..b4a5b58da7 100644 --- a/variants/heltec_wireless_paper/platformio.ini +++ b/variants/heltec_wireless_paper/platformio.ini @@ -60,6 +60,7 @@ build_src_filter = ${Heltec_Wireless_Paper_base.build_src_filter} lib_deps = ${Heltec_Wireless_Paper_base.lib_deps} densaugeo/base64 @ ~1.4.0 + bakercp/CRC32 @ ^2.0.0 [env:Heltec_Wireless_Paper_repeater] extends = Heltec_Wireless_Paper_base @@ -77,6 +78,7 @@ build_src_filter = ${Heltec_Wireless_Paper_base.build_src_filter} lib_deps = ${Heltec_Wireless_Paper_base.lib_deps} ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 ; [env:Heltec_Wireless_Paper_repeater_bridge_rs232] ; extends = Heltec_Wireless_Paper_base @@ -123,6 +125,7 @@ build_src_filter = ${Heltec_Wireless_Paper_base.build_src_filter} lib_deps = ${Heltec_Wireless_Paper_base.lib_deps} ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 [env:Heltec_Wireless_Paper_room_server] extends = Heltec_Wireless_Paper_base @@ -140,3 +143,4 @@ build_src_filter = ${Heltec_Wireless_Paper_base.build_src_filter} lib_deps = ${Heltec_Wireless_Paper_base.lib_deps} ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 From 0e6224011984e3074bd0fc39db3b26c48e031a93 Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Sun, 15 Mar 2026 13:09:53 +0100 Subject: [PATCH 2/2] Set AUTO_OFF_MILLIS to 0 for E213, E290 --- src/helpers/ui/E213Display.cpp | 5 ++++- src/helpers/ui/E290Display.cpp | 5 ++++- variants/heltec_e213/platformio.ini | 2 ++ variants/heltec_e290/platformio.ini | 2 ++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/helpers/ui/E213Display.cpp b/src/helpers/ui/E213Display.cpp index 5f04e685d4..814693a06b 100644 --- a/src/helpers/ui/E213Display.cpp +++ b/src/helpers/ui/E213Display.cpp @@ -90,7 +90,10 @@ void E213Display::powerOff() { void E213Display::turnOn() { if (!_init) begin(); - else if (!_isOn) powerOn(); + else if (!_isOn) { + powerOn(); + display->fastmodeOn(); // Reinitialize display controller after power was cut + } _isOn = true; } diff --git a/src/helpers/ui/E290Display.cpp b/src/helpers/ui/E290Display.cpp index 529f790952..ef4df05ed7 100644 --- a/src/helpers/ui/E290Display.cpp +++ b/src/helpers/ui/E290Display.cpp @@ -44,7 +44,10 @@ void E290Display::powerOff() { void E290Display::turnOn() { if (!_init) begin(); - else if (!_isOn) powerOn(); + else if (!_isOn) { + powerOn(); + display.fastmodeOn(); // Reinitialize display controller after power was cut + } _isOn = true; } diff --git a/variants/heltec_e213/platformio.ini b/variants/heltec_e213/platformio.ini index cfa3362f1c..63bf6d9b38 100644 --- a/variants/heltec_e213/platformio.ini +++ b/variants/heltec_e213/platformio.ini @@ -48,6 +48,7 @@ build_flags = -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=E213Display + -D AUTO_OFF_MILLIS=0 -D BLE_PIN_CODE=123456 ; dynamic, random PIN -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 @@ -69,6 +70,7 @@ build_flags = -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=E213Display + -D AUTO_OFF_MILLIS=0 -D OFFLINE_QUEUE_SIZE=256 build_src_filter = ${Heltec_E213_base.build_src_filter} + diff --git a/variants/heltec_e290/platformio.ini b/variants/heltec_e290/platformio.ini index d6be213faa..2681048c2f 100644 --- a/variants/heltec_e290/platformio.ini +++ b/variants/heltec_e290/platformio.ini @@ -42,6 +42,7 @@ build_flags = -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=E290Display + -D AUTO_OFF_MILLIS=0 -D BLE_PIN_CODE=123456 ; dynamic, random PIN -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 @@ -63,6 +64,7 @@ build_flags = -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=E290Display + -D AUTO_OFF_MILLIS=0 -D BLE_PIN_CODE=123456 ; dynamic, random PIN -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256