From 69794d495f3411cfbd0e161ad0901748c08b078d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Sat, 18 Apr 2026 20:14:16 +0200 Subject: [PATCH] This should explode in multiple ways. Let's see. --- .github/copilot-instructions.md | 6 +- .github/prompts/new-variant.prompt.md | 2 +- platformio.ini | 24 +- src/graphics/BaseUIEInkDisplay.cpp | 211 +++++++ src/graphics/BaseUIEInkDisplay.h | 98 +++ src/graphics/EInkDisplay2.cpp | 293 --------- src/graphics/EInkDisplay2.h | 106 ---- src/graphics/EInkDynamicDisplay.cpp | 564 ------------------ src/graphics/EInkDynamicDisplay.h | 155 ----- src/graphics/EInkParallelDisplay.cpp | 427 ------------- src/graphics/EInkParallelDisplay.h | 69 --- src/graphics/GxEPD2Multi.h | 135 ----- src/graphics/Screen.cpp | 52 +- src/graphics/Screen.h | 3 +- .../Backlight/LatchingBacklight.cpp | 0 .../Backlight/LatchingBacklight.h | 0 .../EInk => eink/Drivers}/DEPG0213BNS800.cpp | 0 .../EInk => eink/Drivers}/DEPG0213BNS800.h | 0 .../EInk => eink/Drivers}/DEPG0290BNS800.cpp | 0 .../EInk => eink/Drivers}/DEPG0290BNS800.h | 0 .../EInk => eink/Drivers}/E0213A367.cpp | 0 .../Drivers/EInk => eink/Drivers}/E0213A367.h | 0 .../Drivers/EInk => eink/Drivers}/EInk.cpp | 0 .../Drivers/EInk => eink/Drivers}/EInk.h | 0 src/graphics/eink/Drivers/EInkParallel.cpp | 132 ++++ src/graphics/eink/Drivers/EInkParallel.h | 69 +++ src/graphics/eink/Drivers/GDEH0122T61.cpp | 49 ++ src/graphics/eink/Drivers/GDEH0122T61.h | 43 ++ src/graphics/eink/Drivers/GDEQ031T10.cpp | 57 ++ src/graphics/eink/Drivers/GDEQ031T10.h | 43 ++ .../EInk => eink/Drivers}/GDEW0102T4.cpp | 0 .../EInk => eink/Drivers}/GDEW0102T4.h | 0 .../EInk => eink/Drivers}/GDEY0154D67.cpp | 0 .../EInk => eink/Drivers}/GDEY0154D67.h | 0 .../EInk => eink/Drivers}/GDEY0213B74.cpp | 0 .../EInk => eink/Drivers}/GDEY0213B74.h | 0 src/graphics/eink/Drivers/GDEY029T94.cpp | 49 ++ src/graphics/eink/Drivers/GDEY029T94.h | 43 ++ src/graphics/eink/Drivers/GDEY042T81.cpp | 49 ++ src/graphics/eink/Drivers/GDEY042T81.h | 42 ++ src/graphics/eink/Drivers/GDEY0579T93.cpp | 57 ++ src/graphics/eink/Drivers/GDEY0579T93.h | 43 ++ .../EInk => eink/Drivers}/HINK_E0213A289.cpp | 0 .../EInk => eink/Drivers}/HINK_E0213A289.h | 0 .../EInk => eink/Drivers}/HINK_E042A87.cpp | 0 .../EInk => eink/Drivers}/HINK_E042A87.h | 0 .../EInk => eink/Drivers}/LCMEN2R13ECC1.cpp | 0 .../EInk => eink/Drivers}/LCMEN2R13ECC1.h | 0 .../EInk => eink/Drivers}/LCMEN2R13EFC1.cpp | 0 .../EInk => eink/Drivers}/LCMEN2R13EFC1.h | 0 .../Drivers/README-legacy.md} | 0 .../Drivers/EInk => eink/Drivers}/README.md | 0 .../Drivers/EInk => eink/Drivers}/SSD1682.cpp | 0 .../Drivers/EInk => eink/Drivers}/SSD1682.h | 0 .../Drivers/EInk => eink/Drivers}/SSD16XX.cpp | 0 .../Drivers/EInk => eink/Drivers}/SSD16XX.h | 0 .../Drivers/EInk => eink/Drivers}/UC8175.cpp | 0 .../Drivers/EInk => eink/Drivers}/UC8175.h | 0 .../Drivers}/ZJY122250_0213BAAMFGN.cpp | 0 .../Drivers}/ZJY122250_0213BAAMFGN.h | 0 .../Drivers}/ZJY128296_029EAAMFGN.cpp | 0 .../Drivers}/ZJY128296_029EAAMFGN.h | 0 .../Drivers}/ZJY200200_0154DAAMFGN.h | 0 src/graphics/eink/Panels/DEPG0213BNS800.h | 25 + src/graphics/eink/Panels/DEPG0290BNS800.h | 25 + src/graphics/eink/Panels/E0213A367.h | 25 + src/graphics/eink/Panels/GDEH0122T61.h | 24 + src/graphics/eink/Panels/GDEQ031T10.h | 24 + src/graphics/eink/Panels/GDEW0102T4.h | 25 + src/graphics/eink/Panels/GDEY0154D67.h | 24 + src/graphics/eink/Panels/GDEY0213B74.h | 25 + src/graphics/eink/Panels/GDEY029T94.h | 25 + src/graphics/eink/Panels/GDEY042T81.h | 24 + src/graphics/eink/Panels/GDEY0579T93.h | 24 + src/graphics/eink/Panels/HINK_E042A87.h | 24 + src/graphics/eink/Panels/LCMEN213EFC1.h | 25 + src/graphics/eink/Panels/LCMEN2R13ECC1.h | 25 + src/graphics/eink/Panels/PanelProfile.cpp | 70 +++ src/graphics/eink/Panels/PanelProfile.h | 52 ++ src/graphics/eink/Panels/T5Epaper.h | 38 ++ .../eink/Panels/ZJY122250_0213BAAMFGN.h | 25 + src/graphics/niche/{InkHUD => }/Applet.cpp | 2 +- src/graphics/niche/{InkHUD => }/Applet.h | 2 +- .../niche/{InkHUD => }/AppletFont.cpp | 0 src/graphics/niche/{InkHUD => }/AppletFont.h | 0 .../Applets/Bases/Map/MapApplet.cpp | 0 .../Applets/Bases/Map/MapApplet.h | 2 +- .../Applets/Bases/NodeList/NodeListApplet.cpp | 0 .../Applets/Bases/NodeList/NodeListApplet.h | 2 +- .../BasicExample/BasicExampleApplet.cpp | 0 .../BasicExample/BasicExampleApplet.h | 2 +- .../NewMsgExample/NewMsgExampleApplet.cpp | 0 .../NewMsgExample/NewMsgExampleApplet.h | 2 +- .../UserAppletInputExample.cpp | 0 .../UserAppletInputExample.h | 2 +- .../System/AlignStick/AlignStickApplet.cpp | 0 .../System/AlignStick/AlignStickApplet.h | 2 +- .../System/BatteryIcon/BatteryIconApplet.cpp | 0 .../System/BatteryIcon/BatteryIconApplet.h | 2 +- .../System/Keyboard/KeyboardApplet.cpp | 0 .../Applets/System/Keyboard/KeyboardApplet.h | 4 +- .../Applets/System/Logo/LogoApplet.cpp | 0 .../Applets/System/Logo/LogoApplet.h | 2 +- .../Applets/System/Menu/MenuAction.h | 0 .../Applets/System/Menu/MenuApplet.cpp | 0 .../Applets/System/Menu/MenuApplet.h | 10 +- .../Applets/System/Menu/MenuItem.h | 0 .../Applets/System/Menu/MenuPage.h | 0 .../System/Notification/Notification.h | 0 .../Notification/NotificationApplet.cpp | 2 +- .../System/Notification/NotificationApplet.h | 2 +- .../Applets/System/Pairing/PairingApplet.cpp | 0 .../Applets/System/Pairing/PairingApplet.h | 2 +- .../System/Placeholder/PlaceholderApplet.cpp | 0 .../System/Placeholder/PlaceholderApplet.h | 2 +- .../Applets/System/Tips/TipsApplet.cpp | 2 +- .../Applets/System/Tips/TipsApplet.h | 2 +- .../User/AllMessage/AllMessageApplet.cpp | 0 .../User/AllMessage/AllMessageApplet.h | 2 +- .../{InkHUD => }/Applets/User/DM/DMApplet.cpp | 0 .../{InkHUD => }/Applets/User/DM/DMApplet.h | 2 +- .../User/FavoritesMap/FavoritesMapApplet.cpp | 0 .../User/FavoritesMap/FavoritesMapApplet.h | 2 +- .../Applets/User/Heard/HeardApplet.cpp | 0 .../Applets/User/Heard/HeardApplet.h | 2 +- .../User/Positions/PositionsApplet.cpp | 0 .../Applets/User/Positions/PositionsApplet.h | 2 +- .../User/RecentsList/RecentsListApplet.cpp | 0 .../User/RecentsList/RecentsListApplet.h | 2 +- .../ThreadedMessage/ThreadedMessageApplet.cpp | 0 .../ThreadedMessage/ThreadedMessageApplet.h | 4 +- .../niche/{Utils => }/CannedMessageStore.cpp | 0 .../niche/{Utils => }/CannedMessageStore.h | 0 .../niche/{InkHUD => }/DisplayHealth.cpp | 0 .../niche/{InkHUD => }/DisplayHealth.h | 2 +- src/graphics/niche/{InkHUD => }/Events.cpp | 2 +- src/graphics/niche/{InkHUD => }/Events.h | 0 src/graphics/niche/{Utils => }/FlashData.h | 0 src/graphics/niche/{InkHUD => }/InkHUD.cpp | 0 src/graphics/niche/{InkHUD => }/InkHUD.h | 2 +- .../niche/InkHUD/PlatformioConfig.ini | 12 - .../niche/{InkHUD => }/MessageStore.cpp | 0 .../niche/{InkHUD => }/MessageStore.h | 0 .../niche/{InkHUD => }/Persistence.cpp | 0 src/graphics/niche/{InkHUD => }/Persistence.h | 4 +- src/graphics/niche/{InkHUD => }/Renderer.cpp | 0 src/graphics/niche/{InkHUD => }/Renderer.h | 2 +- .../niche/{InkHUD => }/SystemApplet.h | 0 src/graphics/niche/{InkHUD => }/Tile.cpp | 0 src/graphics/niche/{InkHUD => }/Tile.h | 0 .../niche/{InkHUD => }/WindowManager.cpp | 0 .../niche/{InkHUD => }/WindowManager.h | 0 .../niche/{InkHUD => }/docs/README.md | 26 +- .../niche/{InkHUD => }/docs/appletfont.png | Bin .../niche/{InkHUD => }/docs/disclaimer.jpg | Bin .../niche/{InkHUD => }/docs/rendering.gif | Bin .../{InkHUD => }/docs/tile_translation.png | Bin src/modules/CannedMessageModule.cpp | 9 +- .../esp32/m5stack_coreink/nicheGraphics.h | 44 ++ variants/esp32/m5stack_coreink/platformio.ini | 17 +- .../ELECROW-ThinkNode-M5/nicheGraphics.h | 42 ++ .../ELECROW-ThinkNode-M5/platformio.ini | 15 +- .../nicheGraphics.h | 61 ++ .../crowpanel-esp32s3-5-epaper/platformio.ini | 86 +-- .../diy/my_esp32s3_diy_eink/nicheGraphics.h | 45 ++ .../diy/my_esp32s3_diy_eink/platformio.ini | 13 +- .../esp32s3/esp32-s3-pico/nicheGraphics.h | 38 ++ variants/esp32s3/esp32-s3-pico/platformio.ini | 10 +- .../heltec_vision_master_e213/nicheGraphics.h | 173 +++--- .../heltec_vision_master_e213/platformio.ini | 22 +- .../heltec_vision_master_e290/nicheGraphics.h | 174 +++--- .../heltec_vision_master_e290/platformio.ini | 26 +- .../heltec_wireless_paper/nicheGraphics.h | 167 +++--- .../heltec_wireless_paper/platformio.ini | 23 +- .../heltec_wireless_paper_v1/nicheGraphics.h | 38 ++ .../heltec_wireless_paper_v1/platformio.ini | 20 +- .../esp32s3/mini-epaper-s3/nicheGraphics.h | 127 ++-- .../esp32s3/mini-epaper-s3/platformio.ini | 17 +- variants/esp32s3/t-deck-pro/nicheGraphics.h | 39 ++ variants/esp32s3/t-deck-pro/platformio.ini | 12 +- variants/esp32s3/t5s3_epaper/nicheGraphics.h | 172 +++--- variants/esp32s3/t5s3_epaper/platformio.ini | 13 +- .../esp32s3/tlora_t3s3_epaper/nicheGraphics.h | 115 ++-- .../esp32s3/tlora_t3s3_epaper/platformio.ini | 18 +- .../nicheGraphics.h | 35 ++ .../platformio.ini | 12 +- .../ELECROW-ThinkNode-M1/nicheGraphics.h | 145 ++--- .../ELECROW-ThinkNode-M1/platformio.ini | 27 +- .../ME25LS01-4Y10TD_e-ink/nicheGraphics.h | 27 + .../ME25LS01-4Y10TD_e-ink/platformio.ini | 12 +- .../MakePython_nRF52840_eink/nicheGraphics.h | 35 ++ .../MakePython_nRF52840_eink/platformio.ini | 12 +- variants/nrf52840/TWC_mesh_v4/platformio.ini | 4 +- .../nrf52_promicro_diy_tcxo/nicheGraphics.h | 27 +- .../nicheGraphics.h | 27 +- .../heltec_mesh_pocket/nicheGraphics.h | 104 ++-- .../heltec_mesh_pocket/platformio.ini | 50 +- .../heltec_mesh_solar/nicheGraphics.h | 117 ++-- .../nrf52840/heltec_mesh_solar/platformio.ini | 17 +- variants/nrf52840/meshlink/nicheGraphics.h | 28 + variants/nrf52840/meshlink/platformio.ini | 20 +- .../nrf52840/rak4631_epaper/nicheGraphics.h | 43 ++ .../nrf52840/rak4631_epaper/platformio.ini | 12 +- .../rak4631_epaper_onrxtx/platformio.ini | 12 +- .../nrf52840/rak4631_eth_gw/platformio.ini | 3 - .../platformio.ini | 3 - .../nrf52840/rak_wismeshtap/platformio.ini | 3 - .../seeed_wio_tracker_L1_eink/nicheGraphics.h | 114 ++-- .../seeed_wio_tracker_L1_eink/platformio.ini | 22 +- variants/nrf52840/t-echo-lite/nicheGraphics.h | 27 + variants/nrf52840/t-echo-lite/platformio.ini | 16 +- variants/nrf52840/t-echo-plus/nicheGraphics.h | 55 +- variants/nrf52840/t-echo-plus/platformio.ini | 12 +- variants/nrf52840/t-echo/nicheGraphics.h | 127 ++-- variants/nrf52840/t-echo/platformio.ini | 25 +- 215 files changed, 3092 insertions(+), 3079 deletions(-) create mode 100644 src/graphics/BaseUIEInkDisplay.cpp create mode 100644 src/graphics/BaseUIEInkDisplay.h delete mode 100644 src/graphics/EInkDisplay2.cpp delete mode 100644 src/graphics/EInkDisplay2.h delete mode 100644 src/graphics/EInkDynamicDisplay.cpp delete mode 100644 src/graphics/EInkDynamicDisplay.h delete mode 100644 src/graphics/EInkParallelDisplay.cpp delete mode 100644 src/graphics/EInkParallelDisplay.h delete mode 100644 src/graphics/GxEPD2Multi.h rename src/graphics/{niche/Drivers => eink}/Backlight/LatchingBacklight.cpp (100%) rename src/graphics/{niche/Drivers => eink}/Backlight/LatchingBacklight.h (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/DEPG0213BNS800.cpp (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/DEPG0213BNS800.h (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/DEPG0290BNS800.cpp (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/DEPG0290BNS800.h (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/E0213A367.cpp (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/E0213A367.h (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/EInk.cpp (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/EInk.h (100%) create mode 100644 src/graphics/eink/Drivers/EInkParallel.cpp create mode 100644 src/graphics/eink/Drivers/EInkParallel.h create mode 100644 src/graphics/eink/Drivers/GDEH0122T61.cpp create mode 100644 src/graphics/eink/Drivers/GDEH0122T61.h create mode 100644 src/graphics/eink/Drivers/GDEQ031T10.cpp create mode 100644 src/graphics/eink/Drivers/GDEQ031T10.h rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/GDEW0102T4.cpp (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/GDEW0102T4.h (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/GDEY0154D67.cpp (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/GDEY0154D67.h (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/GDEY0213B74.cpp (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/GDEY0213B74.h (100%) create mode 100644 src/graphics/eink/Drivers/GDEY029T94.cpp create mode 100644 src/graphics/eink/Drivers/GDEY029T94.h create mode 100644 src/graphics/eink/Drivers/GDEY042T81.cpp create mode 100644 src/graphics/eink/Drivers/GDEY042T81.h create mode 100644 src/graphics/eink/Drivers/GDEY0579T93.cpp create mode 100644 src/graphics/eink/Drivers/GDEY0579T93.h rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/HINK_E0213A289.cpp (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/HINK_E0213A289.h (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/HINK_E042A87.cpp (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/HINK_E042A87.h (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/LCMEN2R13ECC1.cpp (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/LCMEN2R13ECC1.h (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/LCMEN2R13EFC1.cpp (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/LCMEN2R13EFC1.h (100%) rename src/graphics/{niche/Drivers/README.md => eink/Drivers/README-legacy.md} (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/README.md (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/SSD1682.cpp (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/SSD1682.h (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/SSD16XX.cpp (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/SSD16XX.h (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/UC8175.cpp (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/UC8175.h (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/ZJY122250_0213BAAMFGN.cpp (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/ZJY122250_0213BAAMFGN.h (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/ZJY128296_029EAAMFGN.cpp (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/ZJY128296_029EAAMFGN.h (100%) rename src/graphics/{niche/Drivers/EInk => eink/Drivers}/ZJY200200_0154DAAMFGN.h (100%) create mode 100644 src/graphics/eink/Panels/DEPG0213BNS800.h create mode 100644 src/graphics/eink/Panels/DEPG0290BNS800.h create mode 100644 src/graphics/eink/Panels/E0213A367.h create mode 100644 src/graphics/eink/Panels/GDEH0122T61.h create mode 100644 src/graphics/eink/Panels/GDEQ031T10.h create mode 100644 src/graphics/eink/Panels/GDEW0102T4.h create mode 100644 src/graphics/eink/Panels/GDEY0154D67.h create mode 100644 src/graphics/eink/Panels/GDEY0213B74.h create mode 100644 src/graphics/eink/Panels/GDEY029T94.h create mode 100644 src/graphics/eink/Panels/GDEY042T81.h create mode 100644 src/graphics/eink/Panels/GDEY0579T93.h create mode 100644 src/graphics/eink/Panels/HINK_E042A87.h create mode 100644 src/graphics/eink/Panels/LCMEN213EFC1.h create mode 100644 src/graphics/eink/Panels/LCMEN2R13ECC1.h create mode 100644 src/graphics/eink/Panels/PanelProfile.cpp create mode 100644 src/graphics/eink/Panels/PanelProfile.h create mode 100644 src/graphics/eink/Panels/T5Epaper.h create mode 100644 src/graphics/eink/Panels/ZJY122250_0213BAAMFGN.h rename src/graphics/niche/{InkHUD => }/Applet.cpp (99%) rename src/graphics/niche/{InkHUD => }/Applet.h (99%) rename src/graphics/niche/{InkHUD => }/AppletFont.cpp (100%) rename src/graphics/niche/{InkHUD => }/AppletFont.h (100%) rename src/graphics/niche/{InkHUD => }/Applets/Bases/Map/MapApplet.cpp (100%) rename src/graphics/niche/{InkHUD => }/Applets/Bases/Map/MapApplet.h (97%) rename src/graphics/niche/{InkHUD => }/Applets/Bases/NodeList/NodeListApplet.cpp (100%) rename src/graphics/niche/{InkHUD => }/Applets/Bases/NodeList/NodeListApplet.h (98%) rename src/graphics/niche/{InkHUD => }/Applets/Examples/BasicExample/BasicExampleApplet.cpp (100%) rename src/graphics/niche/{InkHUD => }/Applets/Examples/BasicExample/BasicExampleApplet.h (93%) rename src/graphics/niche/{InkHUD => }/Applets/Examples/NewMsgExample/NewMsgExampleApplet.cpp (100%) rename src/graphics/niche/{InkHUD => }/Applets/Examples/NewMsgExample/NewMsgExampleApplet.h (97%) rename src/graphics/niche/{InkHUD => }/Applets/Examples/UserAppletInputExample/UserAppletInputExample.cpp (100%) rename src/graphics/niche/{InkHUD => }/Applets/Examples/UserAppletInputExample/UserAppletInputExample.h (94%) rename src/graphics/niche/{InkHUD => }/Applets/System/AlignStick/AlignStickApplet.cpp (100%) rename src/graphics/niche/{InkHUD => }/Applets/System/AlignStick/AlignStickApplet.h (95%) rename src/graphics/niche/{InkHUD => }/Applets/System/BatteryIcon/BatteryIconApplet.cpp (100%) rename src/graphics/niche/{InkHUD => }/Applets/System/BatteryIcon/BatteryIconApplet.h (95%) rename src/graphics/niche/{InkHUD => }/Applets/System/Keyboard/KeyboardApplet.cpp (100%) rename src/graphics/niche/{InkHUD => }/Applets/System/Keyboard/KeyboardApplet.h (95%) rename src/graphics/niche/{InkHUD => }/Applets/System/Logo/LogoApplet.cpp (100%) rename src/graphics/niche/{InkHUD => }/Applets/System/Logo/LogoApplet.h (94%) rename src/graphics/niche/{InkHUD => }/Applets/System/Menu/MenuAction.h (100%) rename src/graphics/niche/{InkHUD => }/Applets/System/Menu/MenuApplet.cpp (100%) rename src/graphics/niche/{InkHUD => }/Applets/System/Menu/MenuApplet.h (95%) rename src/graphics/niche/{InkHUD => }/Applets/System/Menu/MenuItem.h (100%) rename src/graphics/niche/{InkHUD => }/Applets/System/Menu/MenuPage.h (100%) rename src/graphics/niche/{InkHUD => }/Applets/System/Notification/Notification.h (100%) rename src/graphics/niche/{InkHUD => }/Applets/System/Notification/NotificationApplet.cpp (99%) rename src/graphics/niche/{InkHUD => }/Applets/System/Notification/NotificationApplet.h (97%) rename src/graphics/niche/{InkHUD => }/Applets/System/Pairing/PairingApplet.cpp (100%) rename src/graphics/niche/{InkHUD => }/Applets/System/Pairing/PairingApplet.h (94%) rename src/graphics/niche/{InkHUD => }/Applets/System/Placeholder/PlaceholderApplet.cpp (100%) rename src/graphics/niche/{InkHUD => }/Applets/System/Placeholder/PlaceholderApplet.h (92%) rename src/graphics/niche/{InkHUD => }/Applets/System/Tips/TipsApplet.cpp (99%) rename src/graphics/niche/{InkHUD => }/Applets/System/Tips/TipsApplet.h (95%) rename src/graphics/niche/{InkHUD => }/Applets/User/AllMessage/AllMessageApplet.cpp (100%) rename src/graphics/niche/{InkHUD => }/Applets/User/AllMessage/AllMessageApplet.h (97%) rename src/graphics/niche/{InkHUD => }/Applets/User/DM/DMApplet.cpp (100%) rename src/graphics/niche/{InkHUD => }/Applets/User/DM/DMApplet.h (97%) rename src/graphics/niche/{InkHUD => }/Applets/User/FavoritesMap/FavoritesMapApplet.cpp (100%) rename src/graphics/niche/{InkHUD => }/Applets/User/FavoritesMap/FavoritesMapApplet.h (94%) rename src/graphics/niche/{InkHUD => }/Applets/User/Heard/HeardApplet.cpp (100%) rename src/graphics/niche/{InkHUD => }/Applets/User/Heard/HeardApplet.h (91%) rename src/graphics/niche/{InkHUD => }/Applets/User/Positions/PositionsApplet.cpp (100%) rename src/graphics/niche/{InkHUD => }/Applets/User/Positions/PositionsApplet.h (94%) rename src/graphics/niche/{InkHUD => }/Applets/User/RecentsList/RecentsListApplet.cpp (100%) rename src/graphics/niche/{InkHUD => }/Applets/User/RecentsList/RecentsListApplet.h (95%) rename src/graphics/niche/{InkHUD => }/Applets/User/ThreadedMessage/ThreadedMessageApplet.cpp (100%) rename src/graphics/niche/{InkHUD => }/Applets/User/ThreadedMessage/ThreadedMessageApplet.h (94%) rename src/graphics/niche/{Utils => }/CannedMessageStore.cpp (100%) rename src/graphics/niche/{Utils => }/CannedMessageStore.h (100%) rename src/graphics/niche/{InkHUD => }/DisplayHealth.cpp (100%) rename src/graphics/niche/{InkHUD => }/DisplayHealth.h (97%) rename src/graphics/niche/{InkHUD => }/Events.cpp (99%) rename src/graphics/niche/{InkHUD => }/Events.h (100%) rename src/graphics/niche/{Utils => }/FlashData.h (100%) rename src/graphics/niche/{InkHUD => }/InkHUD.cpp (100%) rename src/graphics/niche/{InkHUD => }/InkHUD.h (98%) delete mode 100644 src/graphics/niche/InkHUD/PlatformioConfig.ini rename src/graphics/niche/{InkHUD => }/MessageStore.cpp (100%) rename src/graphics/niche/{InkHUD => }/MessageStore.h (100%) rename src/graphics/niche/{InkHUD => }/Persistence.cpp (100%) rename src/graphics/niche/{InkHUD => }/Persistence.h (98%) rename src/graphics/niche/{InkHUD => }/Renderer.cpp (100%) rename src/graphics/niche/{InkHUD => }/Renderer.h (98%) rename src/graphics/niche/{InkHUD => }/SystemApplet.h (100%) rename src/graphics/niche/{InkHUD => }/Tile.cpp (100%) rename src/graphics/niche/{InkHUD => }/Tile.h (100%) rename src/graphics/niche/{InkHUD => }/WindowManager.cpp (100%) rename src/graphics/niche/{InkHUD => }/WindowManager.h (100%) rename src/graphics/niche/{InkHUD => }/docs/README.md (97%) rename src/graphics/niche/{InkHUD => }/docs/appletfont.png (100%) rename src/graphics/niche/{InkHUD => }/docs/disclaimer.jpg (100%) rename src/graphics/niche/{InkHUD => }/docs/rendering.gif (100%) rename src/graphics/niche/{InkHUD => }/docs/tile_translation.png (100%) create mode 100644 variants/esp32/m5stack_coreink/nicheGraphics.h create mode 100644 variants/esp32s3/ELECROW-ThinkNode-M5/nicheGraphics.h create mode 100644 variants/esp32s3/crowpanel-esp32s3-5-epaper/nicheGraphics.h create mode 100644 variants/esp32s3/diy/my_esp32s3_diy_eink/nicheGraphics.h create mode 100644 variants/esp32s3/esp32-s3-pico/nicheGraphics.h create mode 100644 variants/esp32s3/heltec_wireless_paper_v1/nicheGraphics.h create mode 100644 variants/esp32s3/t-deck-pro/nicheGraphics.h create mode 100644 variants/nrf52840/Dongle_nRF52840-pca10059-v1/nicheGraphics.h create mode 100644 variants/nrf52840/ME25LS01-4Y10TD_e-ink/nicheGraphics.h create mode 100644 variants/nrf52840/MakePython_nRF52840_eink/nicheGraphics.h create mode 100644 variants/nrf52840/meshlink/nicheGraphics.h create mode 100644 variants/nrf52840/rak4631_epaper/nicheGraphics.h create mode 100644 variants/nrf52840/t-echo-lite/nicheGraphics.h diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 24e11bd4d..b8285ec8e 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -216,14 +216,14 @@ Multiple display driver families in `src/graphics/`: - **OLED**: SSD1306, SH1106, ST7567 - **TFT**: TFTDisplay (LovyanGFX-based) -- **E-Ink**: EInkDisplay2, EInkDynamicDisplay, EInkParallelDisplay +- **E-Ink**: `src/graphics/BaseUIEInkDisplay.*` is the OLEDDisplay-compatible adapter (peer of `TFTDisplay`). The hardware layer it drives lives in `src/graphics/eink/` — chipset drivers in `Drivers/`, panel profiles in `Panels/`, optional `Backlight/`. Shared by both BaseUI-on-eink and InkHUD builds. -**InkHUD** (`src/graphics/niche/InkHUD/`) is an event-driven e-ink UI framework: +**InkHUD** (`src/graphics/niche/`) is an event-driven e-ink UI framework that sits on top of the `graphics/eink/` layer: - Applet-based architecture — modular display tiles - Read-only, static display optimized for minimal refreshes and low power - Configured per-variant via `nicheGraphics.h` -- Separate PlatformIO config: `src/graphics/niche/InkHUD/PlatformioConfig.ini` +- Build helpers in top-level `platformio.ini` — `[niche]` pulls `graphics/eink/` only (BaseUI path), `[inkhud]` extends it with `graphics/niche/` ### Input System diff --git a/.github/prompts/new-variant.prompt.md b/.github/prompts/new-variant.prompt.md index 1a324cea9..0d0e0ad6b 100644 --- a/.github/prompts/new-variant.prompt.md +++ b/.github/prompts/new-variant.prompt.md @@ -132,7 +132,7 @@ For e-ink display variants using the InkHUD framework, add `nicheGraphics.h`: // Configure display, applets, and refresh behavior per device ``` -InkHUD has its own PlatformIO config: `src/graphics/niche/InkHUD/PlatformioConfig.ini` +InkHUD and the shared E-Ink layer are wired up via the top-level `platformio.ini` `[niche]` (BaseUI + driver/panel layer in `src/graphics/eink/`) and `[inkhud]` (adds the InkHUD UI in `src/graphics/niche/`). Variants opt in with `extends = ..., niche` or `extends = ..., inkhud`. ## I2C Device Detection diff --git a/platformio.ini b/platformio.ini index 0205d1ad8..2aafe81a4 100644 --- a/platformio.ini +++ b/platformio.ini @@ -8,10 +8,30 @@ extra_configs = variants/*/*.ini variants/*/*/platformio.ini variants/*/diy/*/platformio.ini - src/graphics/niche/InkHUD/PlatformioConfig.ini description = Meshtastic +; E-Ink / NicheGraphics build helpers. +[niche] +build_src_filter = + + +build_flags = + -D MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +[inkhud] +build_src_filter = + ${niche.build_src_filter} + + +build_flags = + ${niche.build_flags} + -D MESHTASTIC_INCLUDE_INKHUD ; Use InkHUD as the UI + -D MESHTASTIC_EXCLUDE_SCREEN ; Suppress default Screen class + -D MESHTASTIC_EXCLUDE_INPUTBROKER ; Suppress default input handling + -D HAS_BUTTON=0 ; Suppress default ButtonThread +lib_deps = + # renovate: datasource=github-tags depName=GFX_Root packageName=ZinggJM/GFX_Root + https://github.com/ZinggJM/GFX_Root/archive/3195764e352a0d2567c8d277ac408ca7293a99b0.zip ; Used by InkHUD as a "slimmer" version of AdafruitGFX + [env] test_build_src = true extra_scripts = @@ -100,7 +120,7 @@ build_unflags = -std=gnu++11 build_flags = ${env.build_flags} -Os -std=gnu++17 -build_src_filter = ${env.build_src_filter} - - +build_src_filter = ${env.build_src_filter} - - - ; Common libs for communicating over TCP/IP networks such as MQTT [networking_base] diff --git a/src/graphics/BaseUIEInkDisplay.cpp b/src/graphics/BaseUIEInkDisplay.cpp new file mode 100644 index 000000000..f9ce40008 --- /dev/null +++ b/src/graphics/BaseUIEInkDisplay.cpp @@ -0,0 +1,211 @@ +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "./BaseUIEInkDisplay.h" + +#include "configuration.h" +#include "main.h" + +using namespace NicheGraphics; + +BaseUIEInkDisplay::BaseUIEInkDisplay(Drivers::EInk *driver, uint8_t rotation) : driver(driver), rotation(rotation & 0x3) +{ + this->geometry = GEOMETRY_RAWMODE; + + // BaseUI draws in UI orientation. Physical panel dimensions are swapped for 90°/270°. + const bool swap = (this->rotation == 1) || (this->rotation == 3); + this->displayWidth = swap ? driver->height : driver->width; + this->displayHeight = swap ? driver->width : driver->height; + + uint16_t shortSide = min(displayWidth, displayHeight); + uint16_t longSide = max(displayWidth, displayHeight); + if (shortSide % 8 != 0) + shortSide = (shortSide | 7) + 1; + this->displayBufferSize = longSide * (shortSide / 8); + + // Panel-native row-major buffer + panelRowBytes = ((driver->width - 1) / 8) + 1; + panelBufferSize = panelRowBytes * driver->height; + panelBuffer = new uint8_t[panelBufferSize]; + memset(panelBuffer, 0xFF, panelBufferSize); // All white +} + +BaseUIEInkDisplay::~BaseUIEInkDisplay() +{ + delete[] panelBuffer; +} + +bool BaseUIEInkDisplay::connect() +{ + LOG_INFO("Init BaseUI E-Ink (%u x %u, rot %u)", driver->width, driver->height, rotation); + return true; +} + +void BaseUIEInkDisplay::addFrameFlag(frameFlagTypes flag) +{ + frameFlags = (frameFlagTypes)(frameFlags | flag); +} + +void BaseUIEInkDisplay::setDisplayResilience(uint8_t fastPerFull, float stressMultiplier) +{ + this->fastPerFull = (fastPerFull == 0) ? 1 : fastPerFull; + this->stressMultiplier = stressMultiplier; +} + +void BaseUIEInkDisplay::joinAsyncRefresh() +{ + if (driver->busy()) + driver->await(); +} + +// OLEDDisplayUi tick path. Honours rate-limit unless flags demand otherwise. +void BaseUIEInkDisplay::display() +{ + const bool demandFast = frameFlags & DEMAND_FAST; + const bool cosmetic = frameFlags & COSMETIC; + const bool unlimitedFast = frameFlags & UNLIMITED_FAST; + + if (!demandFast && !cosmetic && !unlimitedFast) { + if (!forceDisplay(lastDrawMsec == 0 ? 0 : 1000)) + return; + return; + } + + forceDisplay(0); +} + +// Keyframe path. Returns true if a frame was pushed (sets lastDrawMsec). +bool BaseUIEInkDisplay::forceDisplay(uint32_t msecLimit) +{ + const uint32_t now = millis(); + if (lastDrawMsec != 0 && (now - lastDrawMsec) < msecLimit) + return false; + + const bool blocking = frameFlags & BLOCKING; + Drivers::EInk::UpdateTypes type = decide(); + + // Don't pile frames on top of a running update - wait it out. + if (driver->busy()) + driver->await(); + + const bool pushed = commit(type, blocking); + if (pushed) + lastDrawMsec = now; + + // Reset flags for next frame + frameFlags = BACKGROUND; + return pushed; +} + +bool BaseUIEInkDisplay::commit(Drivers::EInk::UpdateTypes type, bool blocking) +{ + uint32_t hash = repack(); + + // Skip if frame unchanged. Exception: caller explicitly wants a refresh (COSMETIC or FULL). + if (hash == lastHash && type != Drivers::EInk::UpdateTypes::FULL && lastDrawMsec != 0) + return false; + lastHash = hash; + + // Fall back to FULL on panels that don't advertise FAST support. + if (type == Drivers::EInk::UpdateTypes::FAST && !driver->supports(Drivers::EInk::UpdateTypes::FAST)) + type = Drivers::EInk::UpdateTypes::FULL; + + driver->update(panelBuffer, type); + + if (blocking) + driver->await(); + return true; +} + +Drivers::EInk::UpdateTypes BaseUIEInkDisplay::decide() +{ + typedef Drivers::EInk::UpdateTypes UT; + + const bool unlimitedFast = frameFlags & UNLIMITED_FAST; + + // Explicit flag wins outright + if (frameFlags & COSMETIC) { + fullRefreshDebt = max(fullRefreshDebt - 1.0f, 0.0f); + return UT::FULL; + } + if (frameFlags & DEMAND_FAST) { + if (!unlimitedFast) { + fullRefreshDebt += (fullRefreshDebt < 1.0f) ? (1.0f / fastPerFull) : (stressMultiplier * (1.0f / fastPerFull)); + } + return UT::FAST; + } + + const bool explicitFast = frameFlags & RESPONSIVE; + + if (explicitFast || unlimitedFast) { + if (!unlimitedFast) { + fullRefreshDebt += (fullRefreshDebt < 1.0f) ? (1.0f / fastPerFull) : (stressMultiplier * (1.0f / fastPerFull)); + } + return UT::FAST; + } + + // BACKGROUND / unspecified: let debt decide + if (fullRefreshDebt >= 1.0f) { + fullRefreshDebt = max(fullRefreshDebt - 1.0f, 0.0f); + return UT::FULL; + } + fullRefreshDebt += 1.0f / fastPerFull; + return UT::FAST; +} + +uint32_t BaseUIEInkDisplay::repack() +{ + memset(panelBuffer, 0xFF, panelBufferSize); // start all-white + + const uint16_t pw = driver->width; + const uint16_t ph = driver->height; + + // OLEDDisplay buffer: byte = buffer[x + (y/8) * displayWidth]; bit = 1 << (y & 7); 1 = black + // Niche buffer: byte = (y * panelRowBytes) + (x/8); bit = 1 << (7 - x%8); 1 = white + for (uint16_t oy = 0; oy < displayHeight; oy++) { + for (uint16_t ox = 0; ox < displayWidth; ox++) { + const uint8_t b = buffer[ox + (oy / 8) * displayWidth]; + const bool isBlack = b & (1 << (oy & 7)); + + uint16_t px, py; + switch (rotation) { + case 1: // 90° CW: OLED (ox,oy) → panel (pw-1-oy, ox) + px = pw - 1 - oy; + py = ox; + break; + case 2: // 180° + px = pw - 1 - ox; + py = ph - 1 - oy; + break; + case 3: // 270° CW + px = oy; + py = ph - 1 - ox; + break; + case 0: + default: + px = ox; + py = oy; + break; + } + + if (px >= pw || py >= ph) + continue; + + const uint32_t byteNum = (py * panelRowBytes) + (px / 8); + const uint8_t bitNum = 7 - (px % 8); + if (isBlack) + panelBuffer[byteNum] &= ~(1 << bitNum); + else + panelBuffer[byteNum] |= (1 << bitNum); + } + } + + // FNV-1a + uint32_t h = 2166136261u; + for (uint32_t i = 0; i < panelBufferSize; i++) { + h ^= panelBuffer[i]; + h *= 16777619u; + } + return h; +} + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/src/graphics/BaseUIEInkDisplay.h b/src/graphics/BaseUIEInkDisplay.h new file mode 100644 index 000000000..5a411ffb4 --- /dev/null +++ b/src/graphics/BaseUIEInkDisplay.h @@ -0,0 +1,98 @@ +/* + +OLEDDisplay adapter that routes BaseUI pixel output to a NicheGraphics::Drivers::EInk driver. + +One adapter serves all E-Ink variants: the panel driver and orientation are injected at construction, +and FULL/FAST selection is made by the shared DisplayHealth model (same as InkHUD). + +Replaces the per-board branching in EInkDisplay2 / EInkDynamicDisplay / EInkParallelDisplay. + +*/ + +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "configuration.h" + +#include "graphics/eink/Drivers/EInk.h" + +#include + +namespace NicheGraphics +{ + +class BaseUIEInkDisplay : public OLEDDisplay +{ + public: + // Flags Screen.cpp sets via EINK_ADD_FRAMEFLAG before triggering a draw. + // Bits are combined; decided at render time. + enum frameFlagTypes : uint8_t { + BACKGROUND = (1 << 0), // Regular OLEDDisplayUi tick - no urgency, UNSPECIFIED + RESPONSIVE = (1 << 1), // User-driven refresh - prefer FAST + COSMETIC = (1 << 2), // Clean splash / wake-from-sleep - force FULL + DEMAND_FAST = (1 << 3), // Menu interaction - force FAST + BLOCKING = (1 << 4), // Wait for update to finish before returning + UNLIMITED_FAST = (1 << 5), // Suppress health-driven FULL promotion (typing modes) + }; + + BaseUIEInkDisplay(Drivers::EInk *driver, uint8_t rotation); + ~BaseUIEInkDisplay() override; + + // OLEDDisplay overrides + bool connect() override; + void display() override; + void sendCommand(uint8_t com) override { (void)com; } + int getBufferOffset(void) override { return 0; } + + // BaseUI public API (same shape as the old EInkDynamicDisplay) + bool forceDisplay(uint32_t msecLimit = 1000); + void addFrameFlag(frameFlagTypes flag); + void joinAsyncRefresh(); + void enableUnlimitedFastMode() { addFrameFlag(UNLIMITED_FAST); } + void disableUnlimitedFastMode() { frameFlags = (frameFlagTypes)(frameFlags & ~UNLIMITED_FAST); } + + // Tuning, called once per panel profile + void setDisplayResilience(uint8_t fastPerFull, float stressMultiplier = 2.0f); + + // Exposed so Screen.cpp / variants can read the rotation passed in at construction + uint8_t getRotation() const { return rotation; } + + private: + // Perform an update now, unconditionally. Returns true if a frame was pushed to the driver. + bool commit(Drivers::EInk::UpdateTypes type, bool blocking); + + // Convert OLEDDisplay's column-major buffer into the panel's row-major MSB-left buffer. + // Applies rotation. Returns the hash of the panel buffer for frame-skip comparison. + uint32_t repack(); + + // Decide FULL vs FAST based on current frame flags + accumulated debt. + Drivers::EInk::UpdateTypes decide(); + + Drivers::EInk *driver = nullptr; + uint8_t rotation = 0; // 0=0°, 1=90°CW, 2=180°, 3=270°CW + uint8_t *panelBuffer = nullptr; + uint32_t panelBufferSize = 0; + uint16_t panelRowBytes = 0; + + frameFlagTypes frameFlags = BACKGROUND; + uint32_t lastDrawMsec = 0; + uint32_t lastHash = 0; + + // DisplayHealth-style debt tracking + float fullRefreshDebt = 0.0f; + uint8_t fastPerFull = 7; + float stressMultiplier = 2.0f; +}; + +} // namespace NicheGraphics + +// Compat macros used throughout Screen.cpp - route straight to the adapter. +#define EINK_ADD_FRAMEFLAG(display, flag) \ + static_cast(display)->addFrameFlag(NicheGraphics::BaseUIEInkDisplay::flag) +#define EINK_JOIN_ASYNCREFRESH(display) static_cast(display)->joinAsyncRefresh() + +#else // !MESHTASTIC_INCLUDE_NICHE_GRAPHICS +#define EINK_ADD_FRAMEFLAG(display, flag) +#define EINK_JOIN_ASYNCREFRESH(display) +#endif diff --git a/src/graphics/EInkDisplay2.cpp b/src/graphics/EInkDisplay2.cpp deleted file mode 100644 index 704487bc8..000000000 --- a/src/graphics/EInkDisplay2.cpp +++ /dev/null @@ -1,293 +0,0 @@ -#include "configuration.h" - -#if defined(USE_EINK) && !defined(USE_EINK_PARALLELDISPLAY) -#include "EInkDisplay2.h" -#include "SPILock.h" -#include "main.h" -#include - -#ifdef GXEPD2_DRIVER_0 -#include "einkDetect.h" -#endif - -/* - The macros EINK_DISPLAY_MODEL, EINK_WIDTH, and EINK_HEIGHT are defined as build_flags in a variant's platformio.ini - Previously, these macros were defined at the top of this file. - - For archival reasons, note that the following configurations had also been tested during this period: - * ifdef RAK4631 - - 4.2 inch - EINK_DISPLAY_MODEL: GxEPD2_420_M01 - EINK_WIDTH: 300 - EINK_WIDTH: 400 - - - 2.9 inch - EINK_DISPLAY_MODEL: GxEPD2_290_T5D - EINK_WIDTH: 296 - EINK_HEIGHT: 128 - - - 1.54 inch - EINK_DISPLAY_MODEL: GxEPD2_154_M09 - EINK_WIDTH: 200 - EINK_HEIGHT: 200 -*/ - -// Constructor -EInkDisplay::EInkDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus) -{ - // Set dimensions in OLEDDisplay base class - this->geometry = GEOMETRY_RAWMODE; - this->displayWidth = EINK_WIDTH; - this->displayHeight = EINK_HEIGHT; - - // Round shortest side up to nearest byte, to prevent truncation causing an undersized buffer - uint16_t shortSide = min(EINK_WIDTH, EINK_HEIGHT); - uint16_t longSide = max(EINK_WIDTH, EINK_HEIGHT); - if (shortSide % 8 != 0) - shortSide = (shortSide | 7) + 1; - - this->displayBufferSize = longSide * (shortSide / 8); -} - -/** - * Force a display update if we haven't drawn within the specified msecLimit - */ -bool EInkDisplay::forceDisplay(uint32_t msecLimit) -{ - // No need to grab this lock because we are on our own SPI bus - // concurrency::LockGuard g(spiLock); - - uint32_t now = millis(); - uint32_t sinceLast = now - lastDrawMsec; - - if (adafruitDisplay && (sinceLast > msecLimit || lastDrawMsec == 0)) - lastDrawMsec = now; - else - return false; - - // FIXME - only draw bits have changed (use backbuf similar to the other displays) - const bool flipped = config.display.flip_screen; - // HACK for L1 EInk -#if defined(SEEED_WIO_TRACKER_L1_EINK) - // For SEEED_WIO_TRACKER_L1_EINK, setRotation(3) is correct but mirrored; flip both axes - for (uint32_t y = 0; y < displayHeight; y++) { - for (uint32_t x = 0; x < displayWidth; x++) { - auto b = buffer[x + (y / 8) * displayWidth]; - auto isset = b & (1 << (y & 7)); - adafruitDisplay->drawPixel((displayWidth - 1) - x, (displayHeight - 1) - y, isset ? GxEPD_BLACK : GxEPD_WHITE); - } - } -#else - for (uint32_t y = 0; y < displayHeight; y++) { - for (uint32_t x = 0; x < displayWidth; x++) { - auto b = buffer[x + (y / 8) * displayWidth]; - auto isset = b & (1 << (y & 7)); - if (flipped) - adafruitDisplay->drawPixel((displayWidth - 1) - x, (displayHeight - 1) - y, isset ? GxEPD_BLACK : GxEPD_WHITE); - else - adafruitDisplay->drawPixel(x, y, isset ? GxEPD_BLACK : GxEPD_WHITE); - } - } -#endif - - // Trigger the refresh in GxEPD2 - LOG_DEBUG("Update E-Paper"); - adafruitDisplay->nextPage(); - - // End the update process - endUpdate(); - - LOG_DEBUG("done"); - return true; -} - -// End the update process - virtual method, overridden in derived class -void EInkDisplay::endUpdate() -{ - // Power off display hardware, then deep-sleep (Except Wireless Paper V1.1, no deep-sleep) - adafruitDisplay->hibernate(); -} - -// Write the buffer to the display memory -void EInkDisplay::display(void) -{ - // We don't allow regular 'dumb' display() calls to draw on eink until we've shown - // at least one forceDisplay() keyframe. This prevents flashing when we should the critical - // bootscreen (that we want to look nice) - - if (lastDrawMsec) { - forceDisplay(slowUpdateMsec); // Show the first screen a few seconds after boot, then slower - } -} - -// Send a command to the display (low level function) -void EInkDisplay::sendCommand(uint8_t com) -{ - (void)com; - // Drop all commands to device (we just update the buffer) -} - -void EInkDisplay::setDetected(uint8_t detected) -{ - (void)detected; -} - -// Connect to the display - variant specific -bool EInkDisplay::connect() -{ - LOG_INFO("Do EInk init"); - -#ifdef PIN_EINK_EN - // backlight power, HIGH is backlight on, LOW is off - pinMode(PIN_EINK_EN, OUTPUT); -#ifdef ELECROW_ThinkNode_M1 - // ThinkNode M1 has a hardware dimmable backlight. Start enabled - digitalWrite(PIN_EINK_EN, HIGH); -#elif defined(MINI_EPAPER_S3) - // T-Mini Epaper S3 requires panel power rail enabled before SPI transfer. - digitalWrite(PIN_EINK_EN, HIGH); - delay(10); -#else - digitalWrite(PIN_EINK_EN, LOW); -#endif -#endif - -#if defined(TTGO_T_ECHO) || defined(ELECROW_ThinkNode_M1) || defined(T_ECHO_LITE) || defined(TTGO_T_ECHO_PLUS) - { - auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1); - - adafruitDisplay = new GxEPD2_BW(*lowLevel); - adafruitDisplay->init(); -#if defined(ELECROW_ThinkNode_M1) || defined(T_ECHO_LITE) - adafruitDisplay->setRotation(4); -#else - adafruitDisplay->setRotation(3); -#endif - adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight); - } -#elif defined(ELECROW_ThinkNode_M5) - { - // Start HSPI - hspi = new SPIClass(HSPI); - hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); // SCLK, MISO, MOSI, SS - - auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *hspi); - - adafruitDisplay = new GxEPD2_BW(*lowLevel); - adafruitDisplay->init(); - - adafruitDisplay->setRotation(4); - - adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight); - } -#elif defined(MESHLINK) - { - auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1); - - adafruitDisplay = new GxEPD2_BW(*lowLevel); - adafruitDisplay->init(); - adafruitDisplay->setRotation(3); - adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight); - } -#elif defined(RAK4630) || defined(MAKERPYTHON) - { - if (eink_found) { - auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY); - adafruitDisplay = new GxEPD2_BW(*lowLevel); - adafruitDisplay->init(115200, true, 10, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0)); - // RAK14000 2.13 inch b/w 250x122 does actually now support fast refresh - adafruitDisplay->setRotation(3); - // Fast refresh support for 1.54, 2.13 RAK14000 b/w , 2.9 and 4.2 - // adafruitDisplay->setRotation(1); - adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight); - } else { - (void)adafruitDisplay; - } - } - -#elif defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_VISION_MASTER_E290) || defined(TLORA_T3S3_EPAPER) || \ - defined(CROWPANEL_ESP32S3_5_EPAPER) || defined(CROWPANEL_ESP32S3_4_EPAPER) || defined(CROWPANEL_ESP32S3_2_EPAPER) || \ - defined(MINI_EPAPER_S3) - { - // Start HSPI - hspi = new SPIClass(HSPI); - hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); // SCLK, MISO, MOSI, SS - // VExt already enabled in setup() - // RTC GPIO hold disabled in setup() - - // Create GxEPD2 objects - auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *hspi); - adafruitDisplay = new GxEPD2_BW(*lowLevel); - - // Init GxEPD2 - adafruitDisplay->init(); -#if defined(MINI_EPAPER_S3) - adafruitDisplay->setRotation(3); -#else - adafruitDisplay->setRotation(3); -#if defined(CROWPANEL_ESP32S3_5_EPAPER) || defined(CROWPANEL_ESP32S3_4_EPAPER) - adafruitDisplay->setRotation(0); -#endif -#endif - } -#elif defined(PCA10059) || defined(ME25LS01) - { - auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY); - adafruitDisplay = new GxEPD2_BW(*lowLevel); - adafruitDisplay->init(115200, true, 40, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0)); - adafruitDisplay->setRotation(0); - adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT); - } -#elif defined(M5_COREINK) || defined(T_DECK_PRO) - auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY); - adafruitDisplay = new GxEPD2_BW(*lowLevel); - adafruitDisplay->init(115200, true, 40, false, SPI, SPISettings(4000000, MSBFIRST, SPI_MODE0)); - adafruitDisplay->setRotation(0); - adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT); -#elif defined(my) || defined(ESP32_S3_PICO) - { - auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY); - adafruitDisplay = new GxEPD2_BW(*lowLevel); - adafruitDisplay->init(115200, true, 40, false, SPI, SPISettings(4000000, MSBFIRST, SPI_MODE0)); - adafruitDisplay->setRotation(1); - adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT); - } -#elif defined(HELTEC_MESH_POCKET) || defined(SEEED_WIO_TRACKER_L1_EINK) || defined(HELTEC_MESH_SOLAR_EINK) - { - spi1 = &SPI1; - spi1->begin(); - // VExt already enabled in setup() - // RTC GPIO hold disabled in setup() - - // Create GxEPD2 objects - auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *spi1); - adafruitDisplay = new GxEPD2_BW(*lowLevel); - - // Init GxEPD2 - adafruitDisplay->init(); - adafruitDisplay->setRotation(3); - adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT); - } -#elif defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_VISION_MASTER_E213) - - // Detect display model, before starting SPI - EInkDetectionResult displayModel = detectEInk(); - - // Start HSPI - hspi = new SPIClass(HSPI); - hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); // SCLK, MISO, MOSI, SS - - // Create GxEPD2 object - adafruitDisplay = new GxEPD2_Multi((uint8_t)displayModel, PIN_EINK_CS, PIN_EINK_DC, - PIN_EINK_RES, PIN_EINK_BUSY, *hspi); - - // Init GxEPD2 - adafruitDisplay->init(); - adafruitDisplay->setRotation(3); - -#endif - - return true; -} - -#endif diff --git a/src/graphics/EInkDisplay2.h b/src/graphics/EInkDisplay2.h deleted file mode 100644 index 645a3f2d0..000000000 --- a/src/graphics/EInkDisplay2.h +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once - -#if defined(USE_EINK) && !defined(USE_EINK_PARALLELDISPLAY) - -#include "GxEPD2_BW.h" -#include - -#ifdef GXEPD2_DRIVER_0 // If variant has multiple possible display models -#include "GxEPD2Multi.h" -#endif - -// Limit how often we push a full E-Ink refresh. T-Deck Pro needs faster updates for typing. -#ifndef EINK_FORCE_DISPLAY_THROTTLE_MS -#if defined(T_DECK_PRO) -#define EINK_FORCE_DISPLAY_THROTTLE_MS 200 -#else -#define EINK_FORCE_DISPLAY_THROTTLE_MS 1000 -#endif -#endif - -/** - * An adapter class that allows using the GxEPD2 library as if it was an OLEDDisplay implementation. - * - * Note: EInkDynamicDisplay derives from this class. - * - * Remaining TODO: - * optimize display() to only draw changed pixels (see other OLED subclasses for examples) - * implement displayOn/displayOff to turn off the TFT device (and backlight) - * Use the fast NRF52 SPI API rather than the slow standard arduino version - * - * turn radio back on - currently with both on spi bus is fucked? or are we leaving chip select asserted? - * Suggestion: perhaps similar to HELTEC_WIRELESS_PAPER issue, which resolved with rtc_gpio_hold_dis() - */ -class EInkDisplay : public OLEDDisplay -{ - /// How often should we update the display - /// thereafter we do once per 5 minutes - uint32_t slowUpdateMsec = 5 * 60 * 1000; - - public: - /* constructor - FIXME - the parameters are not used, just a temporary hack to keep working like the old displays - */ - EInkDisplay(uint8_t, int, int, OLEDDISPLAY_GEOMETRY, HW_I2C); - - // Write the buffer to the display memory (for eink we only do this occasionally) - virtual void display(void) override; - - /** - * Force a display update if we haven't drawn within the specified msecLimit - * - * @return true if we did draw the screen - */ - virtual bool forceDisplay(uint32_t msecLimit = EINK_FORCE_DISPLAY_THROTTLE_MS); - - /** - * Run any code needed to complete an update, after the physical refresh has completed. - * Split from forceDisplay(), to enable async refresh in derived EInkDynamicDisplay class. - * - */ - virtual void endUpdate(); - - /** - * shim to make the abstraction happy - * - */ - void setDetected(uint8_t detected); - - protected: - // the header size of the buffer used, e.g. for the SPI command header - virtual int getBufferOffset(void) override { return 0; } - - // Send a command to the display (low level function) - virtual void sendCommand(uint8_t com) override; - - // Connect to the display - virtual bool connect() override; - -#ifdef GXEPD2_DRIVER_0 - // AdafruitGFX display object - wrapper for multiple drivers - // Allows runtime detection of multiple displays - // Avoid this situation if possible! - GxEPD2_Multi *adafruitDisplay = NULL; -#else - // AdafruitGFX display object (for single display model) - instantiated in connect(), variant specific - GxEPD2_BW *adafruitDisplay = NULL; -#endif - - // If display uses HSPI -#if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_VISION_MASTER_E213) || \ - defined(HELTEC_VISION_MASTER_E290) || defined(TLORA_T3S3_EPAPER) || defined(CROWPANEL_ESP32S3_5_EPAPER) || \ - defined(CROWPANEL_ESP32S3_4_EPAPER) || defined(CROWPANEL_ESP32S3_2_EPAPER) || defined(ELECROW_ThinkNode_M5) || \ - defined(MINI_EPAPER_S3) - SPIClass *hspi = NULL; -#endif - -#if defined(HELTEC_MESH_POCKET) || defined(SEEED_WIO_TRACKER_L1_EINK) || defined(HELTEC_MESH_SOLAR_EINK) - SPIClass *spi1 = NULL; -#endif - - private: - // FIXME quick hack to limit drawing to a very slow rate - uint32_t lastDrawMsec = 0; -}; - -#endif diff --git a/src/graphics/EInkDynamicDisplay.cpp b/src/graphics/EInkDynamicDisplay.cpp deleted file mode 100644 index a48ba5c93..000000000 --- a/src/graphics/EInkDynamicDisplay.cpp +++ /dev/null @@ -1,564 +0,0 @@ -#include "Throttle.h" -#include "configuration.h" - -#if defined(USE_EINK) && defined(USE_EINK_DYNAMICDISPLAY) -#include "EInkDynamicDisplay.h" - -// Constructor -EInkDynamicDisplay::EInkDynamicDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus) - : EInkDisplay(address, sda, scl, geometry, i2cBus), NotifiedWorkerThread("EInkDynamicDisplay") -{ - // If tracking ghost pixels, grab memory -#ifdef EINK_LIMIT_GHOSTING_PX - dirtyPixels = std::unique_ptr(new uint8_t[EInkDisplay::displayBufferSize]()); // Init with zeros -#endif -} - -// Destructor -EInkDynamicDisplay::~EInkDynamicDisplay() -{ - // If we were tracking ghost pixels, free the memory -#ifdef EINK_LIMIT_GHOSTING_PX - dirtyPixels = nullptr; -#endif -} - -// Screen requests a BACKGROUND frame -void EInkDynamicDisplay::display() -{ - addFrameFlag(BACKGROUND); - update(); -} - -// Screen requests a RESPONSIVE frame -bool EInkDynamicDisplay::forceDisplay(uint32_t msecLimit) -{ - addFrameFlag(RESPONSIVE); - return update(); // (Unutilized) Base class promises to return true if update ran -} - -// Add flag for the next frame -void EInkDynamicDisplay::addFrameFlag(frameFlagTypes flag) -{ - // OR the new flag into the existing flags - this->frameFlags = (frameFlagTypes)(this->frameFlags | flag); -} - -// GxEPD2 code to set fast refresh -void EInkDynamicDisplay::configForFastRefresh() -{ - // Variant-specific code can go here -#if defined(PRIVATE_HW) -#else - // Otherwise: - adafruitDisplay->setPartialWindow(0, 0, adafruitDisplay->width(), adafruitDisplay->height()); -#endif -} - -// GxEPD2 code to set full refresh -void EInkDynamicDisplay::configForFullRefresh() -{ - // Variant-specific code can go here -#if defined(PRIVATE_HW) -#else - // Otherwise: - adafruitDisplay->setFullWindow(); -#endif -} - -// Run any relevant GxEPD2 code, so next update will use correct refresh type -void EInkDynamicDisplay::applyRefreshMode() -{ - // Change from FULL to FAST - if (currentConfig == FULL && refresh == FAST) { - configForFastRefresh(); - currentConfig = FAST; - } - - // Change from FAST back to FULL - else if (currentConfig == FAST && refresh == FULL) { - configForFullRefresh(); - currentConfig = FULL; - } -} - -// Update fastRefreshCount -void EInkDynamicDisplay::adjustRefreshCounters() -{ - if (refresh == FAST) - fastRefreshCount++; - - else if (refresh == FULL) - fastRefreshCount = 0; -} - -// Trigger the display update by calling base class -bool EInkDynamicDisplay::update() -{ - // Determine the refresh mode to use, and start the update - bool refreshApproved = determineMode(); - if (refreshApproved) { - EInkDisplay::forceDisplay(0); // Bypass base class' own rate-limiting system - storeAndReset(); // Store the result of this loop for next time. Note: call *before* endOrDetach() - endOrDetach(); // endUpdate() right now, or set the async refresh flag (if FULL and HAS_EINK_ASYNCFULL) - } else - storeAndReset(); // No update, no post-update code, just store the results - - return refreshApproved; // (Unutilized) Base class promises to return true if update ran -} - -// Figure out who runs the post-update code -void EInkDynamicDisplay::endOrDetach() -{ - // If the GxEPD2 version reports that it has the async modifications -#ifdef HAS_EINK_ASYNCFULL - if (previousRefresh == FULL) { - asyncRefreshRunning = true; // Set the flag - checked in determineMode(); cleared by onNotify() - - if (previousFrameFlags & BLOCKING) - awaitRefresh(); - else { - // Async begins - LOG_DEBUG("Async full-refresh begins (drop frames)"); - notifyLater(intervalPollAsyncRefresh, DUE_POLL_ASYNCREFRESH, true); // Hand-off to NotifiedWorkerThread - } - } - - // Fast Refresh - else if (previousRefresh == FAST) - EInkDisplay::endUpdate(); // Still block while updating, but EInkDisplay needs us to call endUpdate() ourselves. - - // Fallback - If using an unmodified version of GxEPD2 for some reason -#else - if (previousRefresh == FULL || previousRefresh == FAST) { // If refresh wasn't skipped (on unspecified..) - LOG_WARN( - "GxEPD2 version has not been modified to support async refresh; using fallback behavior. Please update lib_deps in " - "variant's platformio.ini file"); - EInkDisplay::endUpdate(); - } -#endif -} - -// Assess situation, pick a refresh type -bool EInkDynamicDisplay::determineMode() -{ - checkInitialized(); - checkForPromotion(); -#if defined(HAS_EINK_ASYNCFULL) - checkBusyAsyncRefresh(); -#endif - checkRateLimiting(); - - // If too soon for a new frame, or display busy, abort early - if (refresh == SKIPPED) - return false; // No refresh - - // -- New frame is due -- - - resetRateLimiting(); // Once determineMode() ends, will have to wait again - hashImage(); // Generate here, so we can still copy it to previousImageHash, even if we skip the comparison check - LOG_DEBUG("determineMode(): "); // Begin log entry - - // Once mode determined, any remaining checks will bypass - checkCosmetic(); - checkDemandingFast(); - checkFrameMatchesPrevious(); - checkConsecutiveFastRefreshes(); -#ifdef EINK_LIMIT_GHOSTING_PX - checkExcessiveGhosting(); -#endif - checkFastRequested(); - - if (refresh == UNSPECIFIED) - LOG_WARN("There was a flaw in the determineMode() logic"); - - // -- Decision has been reached -- - applyRefreshMode(); - adjustRefreshCounters(); - -#ifdef EINK_LIMIT_GHOSTING_PX - // Full refresh clears any ghosting - if (refresh == FULL) - resetGhostPixelTracking(); -#endif - - // Return - call a refresh or not? - if (refresh == SKIPPED) - return false; // Don't trigger a refresh - else - return true; // Do trigger a refresh -} - -// Is this the very first frame? -void EInkDynamicDisplay::checkInitialized() -{ - if (!initialized) { - // Undo GxEPD2_BW::partialWindow(), if set by developer in EInkDisplay::connect() - configForFullRefresh(); - - // Clear any existing image, so we can draw logo with fast-refresh, but also to set GxEPD2_EPD::_initial_write - adafruitDisplay->clearScreen(); - - LOG_DEBUG("initialized, "); - initialized = true; - - // Use a fast-refresh for the next frame; no skipping or else blank screen when waking from deep sleep - addFrameFlag(DEMAND_FAST); - } -} - -// Was a frame skipped (rate, display busy) that should have been a FAST refresh? -void EInkDynamicDisplay::checkForPromotion() -{ - // If a frame was skipped (rate, display busy), then promote a BACKGROUND frame - // Because we DID want a RESPONSIVE/COSMETIC/DEMAND_FULL frame last time, we just didn't get it - - switch (previousReason) { - case ASYNC_REFRESH_BLOCKED_DEMANDFAST: - addFrameFlag(DEMAND_FAST); - break; - case ASYNC_REFRESH_BLOCKED_COSMETIC: - addFrameFlag(COSMETIC); - break; - case ASYNC_REFRESH_BLOCKED_RESPONSIVE: - case EXCEEDED_RATELIMIT_FAST: - addFrameFlag(RESPONSIVE); - break; - default: - break; - } -} - -// Is it too soon for another frame of this type? -void EInkDynamicDisplay::checkRateLimiting() -{ - // Sanity check: millis() overflow - just let the update run.. - if (previousRunMs > millis()) - return; - - // Skip update: too soon for BACKGROUND - if (frameFlags == BACKGROUND) { - if (Throttle::isWithinTimespanMs(previousRunMs, 30000)) { - refresh = SKIPPED; - reason = EXCEEDED_RATELIMIT_FULL; - return; - } - } - - // No rate-limit for these special cases - if (frameFlags & COSMETIC || frameFlags & DEMAND_FAST) - return; - - // Skip update: too soon for RESPONSIVE - if (frameFlags & RESPONSIVE) { - if (Throttle::isWithinTimespanMs(previousRunMs, 1000)) { - refresh = SKIPPED; - reason = EXCEEDED_RATELIMIT_FAST; - LOG_DEBUG("refresh=SKIPPED, reason=EXCEEDED_RATELIMIT_FAST, frameFlags=0x%x", frameFlags); - return; - } - } -} - -// Is this frame COSMETIC (splash screens?) -void EInkDynamicDisplay::checkCosmetic() -{ - // If a decision was already reached, don't run the check - if (refresh != UNSPECIFIED) - return; - - // A full refresh is requested for cosmetic purposes: we have a decision - if (frameFlags & COSMETIC) { - refresh = FULL; - reason = FLAGGED_COSMETIC; - LOG_DEBUG("refresh=FULL, reason=FLAGGED_COSMETIC, frameFlags=0x%x", frameFlags); - } -} - -// Is this a one-off special circumstance, where we REALLY want a fast refresh? -void EInkDynamicDisplay::checkDemandingFast() -{ - // If a decision was already reached, don't run the check - if (refresh != UNSPECIFIED) - return; - - // A fast refresh is demanded: we have a decision - if (frameFlags & DEMAND_FAST) { - refresh = FAST; - reason = FLAGGED_DEMAND_FAST; - LOG_DEBUG("refresh=FAST, reason=FLAGGED_DEMAND_FAST, frameFlags=0x%x", frameFlags); - } -} - -// Does the new frame match the currently displayed image? -void EInkDynamicDisplay::checkFrameMatchesPrevious() -{ - // If a decision was already reached, don't run the check - if (refresh != UNSPECIFIED) - return; - - // If frame is *not* a duplicate, abort the check - if (imageHash != previousImageHash) - return; - -#if !defined(EINK_BACKGROUND_USES_FAST) - // If BACKGROUND, and last update was FAST: redraw the same image in FULL (for display health + image quality) - if (frameFlags == BACKGROUND && fastRefreshCount > 0) { - refresh = FULL; - reason = REDRAW_WITH_FULL; - LOG_DEBUG("refresh=FULL, reason=REDRAW_WITH_FULL, frameFlags=0x%x", frameFlags); - return; - } -#endif - - // Not redrawn, not COSMETIC, not DEMAND_FAST - refresh = SKIPPED; - reason = FRAME_MATCHED_PREVIOUS; - LOG_DEBUG("refresh=SKIPPED, reason=FRAME_MATCHED_PREVIOUS, frameFlags=0x%x", frameFlags); -} - -// Have too many fast-refreshes occurred consecutively, since last full refresh? -void EInkDynamicDisplay::checkConsecutiveFastRefreshes() -{ - // If a decision was already reached, don't run the check - if (refresh != UNSPECIFIED) - return; - - // Bypass limit if UNLIMITED_FAST mode is active - if (frameFlags & UNLIMITED_FAST) { - refresh = FAST; - reason = NO_OBJECTIONS; - LOG_DEBUG("refresh=FAST, reason=UNLIMITED_FAST_MODE_ACTIVE, frameFlags=0x%x", frameFlags); - return; - } - - // If too many FAST refreshes consecutively - force a FULL refresh - if (fastRefreshCount >= EINK_LIMIT_FASTREFRESH) { - refresh = FULL; - reason = EXCEEDED_LIMIT_FASTREFRESH; - LOG_DEBUG("refresh=FULL, reason=EXCEEDED_LIMIT_FASTREFRESH, frameFlags=0x%x", frameFlags); - } -} - -// No objections, we can perform fast-refresh, if desired -void EInkDynamicDisplay::checkFastRequested() -{ - if (refresh != UNSPECIFIED) - return; - - if (frameFlags == BACKGROUND) { -#ifdef EINK_BACKGROUND_USES_FAST - // If we want BACKGROUND to use fast. (FULL only when a limit is hit) - refresh = FAST; - reason = BACKGROUND_USES_FAST; - LOG_DEBUG("refresh=FAST, reason=BACKGROUND_USES_FAST, fastRefreshCount=%lu, frameFlags=0x%x", fastRefreshCount, - frameFlags); -#else - // If we do want to use FULL for BACKGROUND updates - refresh = FULL; - reason = FLAGGED_BACKGROUND; - LOG_DEBUG("refresh=FULL, reason=FLAGGED_BACKGROUND"); -#endif - } - - // Sanity: confirm that we did ask for a RESPONSIVE frame. - if (frameFlags & RESPONSIVE) { - refresh = FAST; - reason = NO_OBJECTIONS; - LOG_DEBUG("refresh=FAST, reason=NO_OBJECTIONS, fastRefreshCount=%lu, frameFlags=0x%x", fastRefreshCount, frameFlags); - } -} - -// Reset the timer used for rate-limiting -void EInkDynamicDisplay::resetRateLimiting() -{ - previousRunMs = millis(); -} - -// Generate a hash of this frame, to compare against previous update -void EInkDynamicDisplay::hashImage() -{ - imageHash = 0; - - // Sum all bytes of the image buffer together - for (uint16_t b = 0; b < (displayWidth / 8) * displayHeight; b++) { - imageHash ^= buffer[b] << b; - } -} - -// Store the results of determineMode() for future use, and reset for next call -void EInkDynamicDisplay::storeAndReset() -{ - previousFrameFlags = frameFlags; - previousRefresh = refresh; - previousReason = reason; - - // Only store image hash if the display will update - if (refresh != SKIPPED) { - previousImageHash = imageHash; - } - - frameFlags = BACKGROUND; - refresh = UNSPECIFIED; -} - -#ifdef EINK_LIMIT_GHOSTING_PX -// Count how many ghost pixels the new image will display -void EInkDynamicDisplay::countGhostPixels() -{ - // If a decision was already reached, don't run the check - if (refresh != UNSPECIFIED) - return; - - // Start a new count - ghostPixelCount = 0; - - // Check new image, bit by bit, for any white pixels at locations marked "dirty" - for (uint16_t i = 0; i < displayBufferSize; i++) { - for (uint8_t bit = 0; bit < 7; bit++) { - - const bool dirty = (dirtyPixels[i] >> bit) & 1; // Has pixel location been drawn to since full-refresh? - const bool shouldBeBlank = !((buffer[i] >> bit) & 1); // Is pixel location white in the new image? - - // If pixel is (or has been) black since last full-refresh, and now is white: ghosting - if (dirty && shouldBeBlank) - ghostPixelCount++; - - // Update the dirty status for this pixel - will this location become a ghost if set white in future? - if (!dirty && !shouldBeBlank) - dirtyPixels[i] |= (1 << bit); - } - } - - LOG_DEBUG("ghostPixels=%hu, ", ghostPixelCount); -} - -// Check if ghost pixel count exceeds the defined limit -void EInkDynamicDisplay::checkExcessiveGhosting() -{ - // If a decision was already reached, don't run the check - if (refresh != UNSPECIFIED) - return; - - countGhostPixels(); - - // If too many ghost pixels, select full refresh - if (ghostPixelCount > EINK_LIMIT_GHOSTING_PX) { - refresh = FULL; - reason = EXCEEDED_GHOSTINGLIMIT; - LOG_DEBUG("refresh=FULL, reason=EXCEEDED_GHOSTINGLIMIT, frameFlags=0x%x", frameFlags); - } -} - -// Clear the dirty pixels array. Call when full-refresh cleans the display. -void EInkDynamicDisplay::resetGhostPixelTracking() -{ - // Copy the current frame into dirtyPixels[] from the display buffer - memcpy(dirtyPixels.get(), EInkDisplay::buffer, EInkDisplay::displayBufferSize); -} -#endif // EINK_LIMIT_GHOSTING_PX - -// Handle any asyc tasks -void EInkDynamicDisplay::onNotify(uint32_t notification) -{ - // Which task - switch (notification) { - case DUE_POLL_ASYNCREFRESH: - pollAsyncRefresh(); - break; - } -} - -#ifdef HAS_EINK_ASYNCFULL -// Public: wait for an refresh already in progress, then run the post-update code. See Screen::setScreensaverFrames() -void EInkDynamicDisplay::joinAsyncRefresh() -{ - // If no async refresh running, nothing to do - if (!asyncRefreshRunning) - return; - - LOG_DEBUG("Join an async refresh in progress"); - - // Continually poll the BUSY pin - while (adafruitDisplay->epd2.isBusy()) - yield(); - - // If asyncRefreshRunning flag is still set, but display's BUSY pin reports the refresh is done - adafruitDisplay->endAsyncFull(); // Run the end of nextPage() code - EInkDisplay::endUpdate(); // Run base-class code to finish off update (NOT our derived class override) - asyncRefreshRunning = false; // Unset the flag - LOG_DEBUG("Refresh complete"); - - // Note: this code only works because of a modification to meshtastic/GxEPD2. - // It is only equipped to intercept calls to nextPage() -} - -// Called from NotifiedWorkerThread. Run the post-update code if the hardware is ready -void EInkDynamicDisplay::pollAsyncRefresh() -{ - // In theory, this condition should never be met - if (!asyncRefreshRunning) - return; - - // Still running, check back later - if (adafruitDisplay->epd2.isBusy()) { - // Schedule next call of pollAsyncRefresh() - NotifiedWorkerThread::notifyLater(intervalPollAsyncRefresh, DUE_POLL_ASYNCREFRESH, true); - return; - } - - // If asyncRefreshRunning flag is still set, but display's BUSY pin reports the refresh is done - adafruitDisplay->endAsyncFull(); // Run the end of nextPage() code - EInkDisplay::endUpdate(); // Run base-class code to finish off update (NOT our derived class override) - asyncRefreshRunning = false; // Unset the flag - LOG_DEBUG("Async full-refresh complete"); - - // Note: this code only works because of a modification to meshtastic/GxEPD2. - // It is only equipped to intercept calls to nextPage() -} - -// Check the status of "async full-refresh"; skip if running -void EInkDynamicDisplay::checkBusyAsyncRefresh() -{ - // No refresh taking place, continue with determineMode() - if (!asyncRefreshRunning) - return; - - // Full refresh still running - if (adafruitDisplay->epd2.isBusy()) { - // No refresh - refresh = SKIPPED; - - // Set the reason, marking what type of frame we're skipping - if (frameFlags & DEMAND_FAST) - reason = ASYNC_REFRESH_BLOCKED_DEMANDFAST; - else if (frameFlags & COSMETIC) - reason = ASYNC_REFRESH_BLOCKED_COSMETIC; - else if (frameFlags & RESPONSIVE) - reason = ASYNC_REFRESH_BLOCKED_RESPONSIVE; - else - reason = ASYNC_REFRESH_BLOCKED_BACKGROUND; - - return; - } - - // Async refresh appears to have stopped, but wasn't caught by onNotify() - else - pollAsyncRefresh(); // Check (and terminate) the async refresh manually -} - -// Hold control while an async refresh runs -void EInkDynamicDisplay::awaitRefresh() -{ - // Continually poll the BUSY pin - while (adafruitDisplay->epd2.isBusy()) - yield(); - - // End the full-refresh process - adafruitDisplay->endAsyncFull(); // Run the end of nextPage() code - EInkDisplay::endUpdate(); // Run base-class code to finish off update (NOT our derived class override) - asyncRefreshRunning = false; // Unset the flag -} -#endif // HAS_EINK_ASYNCFULL - -#endif // USE_EINK_DYNAMICDISPLAY diff --git a/src/graphics/EInkDynamicDisplay.h b/src/graphics/EInkDynamicDisplay.h deleted file mode 100644 index b03061873..000000000 --- a/src/graphics/EInkDynamicDisplay.h +++ /dev/null @@ -1,155 +0,0 @@ -#pragma once - -#include "configuration.h" -#include - -#if defined(USE_EINK) && defined(USE_EINK_DYNAMICDISPLAY) - -#include "EInkDisplay2.h" -#include "GxEPD2_BW.h" -#include "concurrency/NotifiedWorkerThread.h" - -/* - Derives from the EInkDisplay adapter class. - Accepts suggestions from Screen class about frame type. - Determines which refresh type is most suitable. - (Full, Fast, Skip) -*/ - -class EInkDynamicDisplay : public EInkDisplay, protected concurrency::NotifiedWorkerThread -{ - public: - // Constructor - // ( Parameters unused, passed to EInkDisplay. Maintains compatibility OLEDDisplay class ) - EInkDynamicDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus); - ~EInkDynamicDisplay(); - - // Methods to enable or disable unlimited fast refresh mode - void enableUnlimitedFastMode() { addFrameFlag(UNLIMITED_FAST); } - void disableUnlimitedFastMode() { frameFlags = (frameFlagTypes)(frameFlags & ~UNLIMITED_FAST); } - - // What kind of frame is this - enum frameFlagTypes : uint8_t { - BACKGROUND = (1 << 0), // For frames via display() - RESPONSIVE = (1 << 1), // For frames via forceDisplay() - COSMETIC = (1 << 2), // For splashes - DEMAND_FAST = (1 << 3), // Special case only - BLOCKING = (1 << 4), // Modifier - block while refresh runs - UNLIMITED_FAST = (1 << 5) - }; - void addFrameFlag(frameFlagTypes flag); - - // Set the correct frame flag, then call universal "update()" method - void display() override; - bool forceDisplay(uint32_t msecLimit) override; // Shadows base class. Parameter and return val unused. - - protected: - enum refreshTypes : uint8_t { // Which refresh operation will be used - UNSPECIFIED, - FULL, - FAST, - SKIPPED, - }; - enum reasonTypes : uint8_t { // How was the decision reached - NO_OBJECTIONS, - ASYNC_REFRESH_BLOCKED_DEMANDFAST, - ASYNC_REFRESH_BLOCKED_COSMETIC, - ASYNC_REFRESH_BLOCKED_RESPONSIVE, - ASYNC_REFRESH_BLOCKED_BACKGROUND, - EXCEEDED_RATELIMIT_FAST, - EXCEEDED_RATELIMIT_FULL, - FLAGGED_COSMETIC, - FLAGGED_DEMAND_FAST, - EXCEEDED_LIMIT_FASTREFRESH, - EXCEEDED_GHOSTINGLIMIT, - FRAME_MATCHED_PREVIOUS, - BACKGROUND_USES_FAST, - FLAGGED_BACKGROUND, - REDRAW_WITH_FULL, - }; - - enum notificationTypes : uint8_t { // What was onNotify() called for - NONE = 0, // This behavior (NONE=0) is fixed by NotifiedWorkerThread class - DUE_POLL_ASYNCREFRESH = 1, - }; - const uint32_t intervalPollAsyncRefresh = 100; - - void onNotify(uint32_t notification) override; // Handle any async tasks - overrides NotifiedWorkerThread - void configForFastRefresh(); // GxEPD2 code to set fast-refresh - void configForFullRefresh(); // GxEPD2 code to set full-refresh - bool determineMode(); // Assess situation, pick a refresh type - void applyRefreshMode(); // Run any relevant GxEPD2 code, so next update will use correct refresh type - void adjustRefreshCounters(); // Update fastRefreshCount - bool update(); // Trigger the display update - determine mode, then call base class - void endOrDetach(); // Run the post-update code, or delegate it off to checkBusyAsyncRefresh() - - // Checks as part of determineMode() - void checkInitialized(); // Is this the very first frame? - void checkForPromotion(); // Was a frame skipped (rate, display busy) that should have been a FAST refresh? - void checkRateLimiting(); // Is this frame too soon? - void checkCosmetic(); // Was the COSMETIC flag set? - void checkDemandingFast(); // Was the DEMAND_FAST flag set? - void checkFrameMatchesPrevious(); // Does the new frame match the existing display image? - void checkConsecutiveFastRefreshes(); // Too many fast-refreshes consecutively? - void checkFastRequested(); // Was the flag set for RESPONSIVE, or only BACKGROUND? - - void resetRateLimiting(); // Set previousRunMs - this now counts as an update, for rate-limiting - void hashImage(); // Generate a hashed version of this frame, to compare against previous update - void storeAndReset(); // Keep results of determineMode() for later, tidy-up for next call - - // What we are determining for this frame - frameFlagTypes frameFlags = BACKGROUND; // Frame characteristics - determineMode() input - refreshTypes refresh = UNSPECIFIED; // Refresh type - determineMode() output - reasonTypes reason = NO_OBJECTIONS; // Reason - why was refresh type used - - // What happened last time determineMode() ran - frameFlagTypes previousFrameFlags = BACKGROUND; // (Previous) Frame flags - refreshTypes previousRefresh = UNSPECIFIED; // (Previous) Outcome - reasonTypes previousReason = NO_OBJECTIONS; // (Previous) Reason - - bool initialized = false; // Have we drawn at least one frame yet? - uint32_t previousRunMs = -1; // When did determineMode() last run (rather than rejecting for rate-limiting) - uint32_t imageHash = 0; // Hash of the current frame. Don't bother updating if nothing has changed! - uint32_t previousImageHash = 0; // Hash of the previous update's frame - uint32_t fastRefreshCount = 0; // How many fast-refreshes consecutively since last full refresh? - refreshTypes currentConfig = FULL; // Which refresh type is GxEPD2 currently configured for - - // Optional - track ghosting, pixel by pixel - // May 2024: no longer used by any display. Kept for possible future use. -#ifdef EINK_LIMIT_GHOSTING_PX - void countGhostPixels(); // Count any pixels which have moved from black to white since last full-refresh - void checkExcessiveGhosting(); // Check if ghosting exceeds defined limit - void resetGhostPixelTracking(); // Clear the dirty pixels array. Call when full-refresh cleans the display. - std::unique_ptr dirtyPixels; // Any pixels that have been black since last full-refresh (dynamically allocated mem) - uint32_t ghostPixelCount = 0; // Number of pixels with problematic ghosting. Retained here for LOG_DEBUG use -#endif - - // Conditional - async full refresh - only with modified meshtastic/GxEPD2 -#if defined(HAS_EINK_ASYNCFULL) - public: - void joinAsyncRefresh(); // Main thread joins an async refresh already in progress. Blocks, then runs post-update code - - protected: - void pollAsyncRefresh(); // Run the post-update code if the hardware is ready - void checkBusyAsyncRefresh(); // Check if display is busy running an async full-refresh (rejecting new frames) - void awaitRefresh(); // Hold control while an async refresh runs - void endUpdate() override {} // Disable base-class behavior of running post-update immediately after forceDisplay() - bool asyncRefreshRunning = false; // Flag, checked by checkBusyAsyncRefresh() -#else - public: - void joinAsyncRefresh() {} // Dummy method - - protected: - void pollAsyncRefresh() {} // Dummy method. In theory, not reachable -#endif -}; - -// Hide the ugly casts used in Screen.cpp -#define EINK_ADD_FRAMEFLAG(display, flag) static_cast(display)->addFrameFlag(EInkDynamicDisplay::flag) -#define EINK_JOIN_ASYNCREFRESH(display) static_cast(display)->joinAsyncRefresh() - -#else // !USE_EINK_DYNAMICDISPLAY -// Dummy-macro, removes the need for include guards -#define EINK_ADD_FRAMEFLAG(display, flag) -#define EINK_JOIN_ASYNCREFRESH(display) -#endif \ No newline at end of file diff --git a/src/graphics/EInkParallelDisplay.cpp b/src/graphics/EInkParallelDisplay.cpp deleted file mode 100644 index 293f57e81..000000000 --- a/src/graphics/EInkParallelDisplay.cpp +++ /dev/null @@ -1,427 +0,0 @@ -#include "EInkParallelDisplay.h" - -#ifdef USE_EINK_PARALLELDISPLAY - -#include "Wire.h" -#include "variant.h" -#include -#include -#include -#include - -#include "FastEPD.h" - -// Thresholds for choosing partial vs full update -#ifndef EPD_PARTIAL_THRESHOLD_ROWS -#define EPD_PARTIAL_THRESHOLD_ROWS 128 // if changed region <= this many rows, prefer partial -#endif -#ifndef EPD_FULLSLOW_PERIOD -#define EPD_FULLSLOW_PERIOD 100 // every N full updates do a slow (CLEAR_SLOW) full refresh -#endif -#ifndef EPD_RESPONSIVE_MIN_MS -#define EPD_RESPONSIVE_MIN_MS 1000 // simple rate-limit (ms) for responsive updates -#endif - -EInkParallelDisplay::EInkParallelDisplay(uint16_t width, uint16_t height, EpdRotation rot) : epaper(nullptr), rotation(rot) -{ - LOG_INFO("init EInkParallelDisplay"); - // Set dimensions in OLEDDisplay base class - this->geometry = GEOMETRY_RAWMODE; - this->displayWidth = width; - this->displayHeight = height; - - // Round shortest side up to nearest byte, to prevent truncation causing an undersized buffer - uint16_t shortSide = min(width, height); - uint16_t longSide = max(width, height); - if (shortSide % 8 != 0) - shortSide = (shortSide | 7) + 1; - - this->displayBufferSize = longSide * (shortSide / 8); - -#ifdef EINK_LIMIT_GHOSTING_PX - // allocate dirty pixel buffer same size as epaper buffers (rowBytes * height) - size_t rowBytes = (this->displayWidth + 7) / 8; - dirtyPixelsSize = rowBytes * this->displayHeight; - dirtyPixels = (uint8_t *)calloc(dirtyPixelsSize, 1); - ghostPixelCount = 0; -#endif -} - -EInkParallelDisplay::~EInkParallelDisplay() -{ -#ifdef EINK_LIMIT_GHOSTING_PX - if (dirtyPixels) { - free(dirtyPixels); - dirtyPixels = nullptr; - } -#endif - // If an async full update is running, wait for it to finish - if (asyncFullRunning.load()) { - // wait a short while for task to finish - for (int i = 0; i < 50 && asyncFullRunning.load(); ++i) { - delay(50); - } - if (asyncTaskHandle) { - // Let it finish or delete it - vTaskDelete(asyncTaskHandle); - asyncTaskHandle = nullptr; - } - } - - delete epaper; -} - -/* - * Called by the OLEDDisplay::init() path. - */ -bool EInkParallelDisplay::connect() -{ - LOG_INFO("Do EPD init"); - if (!epaper) { - epaper = new FASTEPD; -#if defined(T5_S3_EPAPER_PRO_V1) - epaper->initPanel(BB_PANEL_LILYGO_T5PRO, 28000000); -#elif defined(T5_S3_EPAPER_PRO_V2) - epaper->initPanel(BB_PANEL_LILYGO_T5PRO_V2, 28000000); - // initialize all port 0 pins (0-7) as outputs / HIGH - for (int i = 0; i < 8; i++) { - epaper->ioPinMode(i, OUTPUT); - epaper->ioWrite(i, HIGH); - } -#else -#error "unsupported EPD device!" -#endif - } - - // epaper->setRotation(rotation); // does not work, messes up width/height - epaper->setMode(BB_MODE_1BPP); - epaper->clearWhite(); - epaper->fullUpdate(true); - -#ifdef EINK_LIMIT_GHOSTING_PX - // After a full/clear the dirty tracking should be reset - resetGhostPixelTracking(); -#endif - - return true; -} - -/* - * sendCommand - simple passthrough (not required for epd_driver-based path) - */ -void EInkParallelDisplay::sendCommand(uint8_t com) -{ - LOG_DEBUG("EInkParallelDisplay::sendCommand %d", (int)com); -} - -/* - * Start a background task that will perform a blocking fullUpdate(). This lets - * display() return quickly while the heavy refresh runs in the background. - */ -void EInkParallelDisplay::startAsyncFullUpdate(int clearMode) -{ - if (asyncFullRunning.load()) - return; // already running - - asyncFullRunning.store(true); - // pass 'this' as parameter - BaseType_t rc = xTaskCreatePinnedToCore(EInkParallelDisplay::asyncFullUpdateTask, "epd_full", 4096 / sizeof(StackType_t), - this, 2, &asyncTaskHandle, -#if CONFIG_FREERTOS_UNICORE - 0 -#else - 1 -#endif - ); - if (rc != pdPASS) { - LOG_WARN("Failed to create async full-update task, falling back to blocking update"); - epaper->fullUpdate(clearMode, false); - epaper->backupPlane(); - asyncFullRunning.store(false); - asyncTaskHandle = nullptr; - } -} - -/* - * FreeRTOS task entry: runs the full update and then backs up plane. - */ -void EInkParallelDisplay::asyncFullUpdateTask(void *pvParameters) -{ - EInkParallelDisplay *self = static_cast(pvParameters); - if (!self) { - vTaskDelete(nullptr); - return; - } - - // choose CLEAR_SLOW occasionally - int clearMode = CLEAR_FAST; - if (self->fastRefreshCount >= EPD_FULLSLOW_PERIOD) { - clearMode = CLEAR_SLOW; - self->fastRefreshCount = 0; - } else { - // when running async full, treat it as a full so reset fast count - self->fastRefreshCount = 0; - } - - self->epaper->fullUpdate(clearMode, false); - self->epaper->backupPlane(); - -#ifdef EINK_LIMIT_GHOSTING_PX - // A full refresh clears ghosting state - self->resetGhostPixelTracking(); -#endif - - self->asyncFullRunning.store(false); - self->asyncTaskHandle = nullptr; - - // delete this task - vTaskDelete(nullptr); -} - -/* - * Convert the OLEDDisplay buffer (vertical byte layout) into the 1bpp horizontal-bytes - * buffer used by the FASTEPD library. For performance we write directly into FASTEPD's - * currentBuffer() while comparing against previousBuffer() to detect changed rows. - * After conversion we call FASTEPD::partialUpdate() or FASTEPD::fullUpdate() according - * to a heuristic so only the minimal region is refreshed. - */ -void EInkParallelDisplay::display(void) -{ - const uint16_t w = this->displayWidth; - const uint16_t h = this->displayHeight; - - // Simple rate limiting: avoid very-frequent responsive updates - uint32_t nowMs = millis(); - if (lastUpdateMs != 0 && (nowMs - lastUpdateMs) < EPD_RESPONSIVE_MIN_MS) { - LOG_DEBUG("rate-limited, skipping update"); - return; - } - - // bytes per row in epd format (one byte = 8 horizontal pixels) - const uint32_t rowBytes = (w + 7) / 8; - - // Get pointers to internal buffers - uint8_t *cur = epaper->currentBuffer(); - const uint8_t *prev = epaper->previousBuffer(); // may be NULL on first init - - // Track changed row range while converting - int newTop = h; // min changed row (initialized to out-of-range) - int newBottom = -1; // max changed row - -#ifdef FAST_EPD_PARTIAL_UPDATE_BUG - // Track changed byte column range (for clipped fullUpdate fallback) - int newLeftByte = (int)rowBytes; - int newRightByte = -1; -#endif - - // Compute a quick hash of the incoming OLED buffer (so we can skip identical frames) - uint32_t imageHash = 0; - uint32_t bufBytes = (w / 8) * h; // vertical-byte layout size - for (uint32_t bi = 0; bi < bufBytes; ++bi) { - imageHash ^= ((uint32_t)buffer[bi]) << (bi & 31); - } - if (imageHash == previousImageHash) { - // LOG_DEBUG("image identical to previous, skipping update"); - return; - } - -#ifdef EINK_LIMIT_GHOSTING_PX - // reset ghost count for this conversion pass; we'll mark bits that change - ghostPixelCount = 0; -#endif - - // Convert: OLED buffer layout -> FASTEPD 1bpp horizontal-bytes layout into cur, - // comparing against prev when available to detect changes. - for (uint32_t y = 0; y < h; ++y) { - const uint32_t base = (y >> 3) * w; // (y/8) * width - const uint8_t bitMask = (uint8_t)(1u << (y & 7)); // mask for this row in vertical-byte layout - const uint32_t rowBase = y * rowBytes; - - // process full 8-pixel bytes - for (uint32_t xb = 0; xb < rowBytes; ++xb) { - uint32_t x0 = xb * 8; - // read up to 8 source bytes (vertical-byte per column) - uint8_t b0 = (x0 + 0 < w) ? buffer[base + x0 + 0] : 0; - uint8_t b1 = (x0 + 1 < w) ? buffer[base + x0 + 1] : 0; - uint8_t b2 = (x0 + 2 < w) ? buffer[base + x0 + 2] : 0; - uint8_t b3 = (x0 + 3 < w) ? buffer[base + x0 + 3] : 0; - uint8_t b4 = (x0 + 4 < w) ? buffer[base + x0 + 4] : 0; - uint8_t b5 = (x0 + 5 < w) ? buffer[base + x0 + 5] : 0; - uint8_t b6 = (x0 + 6 < w) ? buffer[base + x0 + 6] : 0; - uint8_t b7 = (x0 + 7 < w) ? buffer[base + x0 + 7] : 0; - - // build output byte: MSB = leftmost pixel - uint8_t out = 0; - out |= (uint8_t)((b0 & bitMask) ? 0x80 : 0x00); - out |= (uint8_t)((b1 & bitMask) ? 0x40 : 0x00); - out |= (uint8_t)((b2 & bitMask) ? 0x20 : 0x00); - out |= (uint8_t)((b3 & bitMask) ? 0x10 : 0x00); - out |= (uint8_t)((b4 & bitMask) ? 0x08 : 0x00); - out |= (uint8_t)((b5 & bitMask) ? 0x04 : 0x00); - out |= (uint8_t)((b6 & bitMask) ? 0x02 : 0x00); - out |= (uint8_t)((b7 & bitMask) ? 0x01 : 0x00); - - // handle partial byte at end of row by masking off invalid bits - uint8_t mask = 0xFF; - uint32_t bitsRemain = (w > x0) ? (w - x0) : 0; - if (bitsRemain > 0 && bitsRemain < 8) { - mask = (uint8_t)(0xFF << (8 - bitsRemain)); - out &= mask; - } - - // invert to FASTEPD polarity - out = (~out) & mask; - - uint32_t pos = rowBase + xb; - uint8_t prevVal = prev ? (prev[pos] & mask) : 0x00; - // Consider this byte changed if previous buffer differs (or prev is null) - bool changed = (prev == nullptr) || (prevVal != out); - -#ifdef EINK_LIMIT_GHOSTING_PX - if (changed && prev) - markDirtyBits(prev, pos, mask, out); -#endif - - // mark row changed only if the previous buffer differs - if (changed) { - if (y < (uint32_t)newTop) - newTop = y; - if ((int)y > newBottom) - newBottom = y; -#ifdef FAST_EPD_PARTIAL_UPDATE_BUG - // record changed column bytes - if ((int)xb < newLeftByte) - newLeftByte = (int)xb; - if ((int)xb > newRightByte) - newRightByte = (int)xb; -#endif - } - - // Always write the computed value into the current buffer (avoid leaving stale bytes) - cur[pos] = (cur[pos] & ~mask) | out; - } - } - - // If nothing changed, avoid any panel update - if (newBottom < 0) { - LOG_DEBUG("no pixel changes detected, skipping update (conv)"); - previousImageHash = imageHash; // still remember that frame - return; - } - - // Choose partial vs full update using heuristic - // Decide if we should force a full update after many fast updates - bool forceFull = (fastRefreshCount >= EPD_FULLSLOW_PERIOD); - -#ifdef EINK_LIMIT_GHOSTING_PX - // If ghost pixels exceed limit, force a full update to clear ghosting - if (ghostPixelCount > ghostPixelLimit) { - LOG_WARN("ghost pixels %u > limit %u, forcing full refresh", ghostPixelCount, ghostPixelLimit); - forceFull = true; - } -#endif - - // Compute pixel bounds from newTop/newBottom - int startRow = (newTop / 8) * 8; - int endRow = (newBottom / 8) * 8 + 7; - - LOG_DEBUG("EPD update rows=%d..%d alignedRows=%d..%d rowBytes=%u", newTop, newBottom, startRow, endRow, rowBytes); - - if (epaper->getMode() == BB_MODE_1BPP && !forceFull && (newBottom - newTop) <= EPD_PARTIAL_THRESHOLD_ROWS) { - // Prefer partial update path if driver is reliable; otherwise use clipped fullUpdate fallback. -#ifdef FAST_EPD_PARTIAL_UPDATE_BUG - // Workaround for FastEPD partial update bug: use clipped fullUpdate instead - // Build a pixel rectangle for a clipped fullUpdate using the changed columns - int startCol = (newLeftByte <= newRightByte) ? (newLeftByte * 8) : 0; - int endCol = (newLeftByte <= newRightByte) ? ((newRightByte + 1) * 8 - 1) : (w - 1); - - BB_RECT rect{startCol, startRow, endCol - startCol + 1, endRow - startRow + 1}; - // LOG_DEBUG("Using clipped fullUpdate rect x=%d y=%d w=%d h=%d", rect.x, rect.y, rect.w, rect.h); - epaper->fullUpdate(CLEAR_FAST, false, &rect); -#else - // Use rows for partial update - LOG_DEBUG("calling partialUpdate startRow=%d endRow=%d", startRow, endRow); - epaper->partialUpdate(true, startRow, endRow); -#endif - epaper->backupPlane(); - fastRefreshCount++; - } else { - // Full update: run async if possible (startAsyncFullUpdate will fall back to blocking) - startAsyncFullUpdate(forceFull ? CLEAR_SLOW : CLEAR_FAST); - } - - lastUpdateMs = millis(); - previousImageHash = imageHash; - - // Keep same behavior as before - lastDrawMsec = millis(); -} - -#ifdef EINK_LIMIT_GHOSTING_PX -// markDirtyBits: mark per-bit dirty flags and update ghostPixelCount -void EInkParallelDisplay::markDirtyBits(const uint8_t *prevBuf, uint32_t pos, uint8_t mask, uint8_t out) -{ - // defensive: need dirtyPixels allocated and prevBuf valid - if (!dirtyPixels || !prevBuf) - return; - - // 'out' is in FASTEPD polarity (1 = black, 0 = white) - uint8_t newBlack = out & mask; // bits that will be black now - uint8_t newWhite = (~out) & mask; // bits that will be white now - - // previously recorded dirty bits for this byte - uint8_t before = dirtyPixels[pos]; - - // Ghost bits: bits that were previously marked dirty and are now being driven white - uint8_t ghostBits = before & newWhite; - if (ghostBits) { - ghostPixelCount += __builtin_popcount((unsigned)ghostBits); - } - - // Only mark bits dirty when they turn black now (accumulate until a full refresh) - uint8_t newlyDirty = newBlack & (~before); - if (newlyDirty) { - dirtyPixels[pos] |= newlyDirty; - } -} - -// reset ghost tracking (call after a full refresh) -void EInkParallelDisplay::resetGhostPixelTracking() -{ - if (!dirtyPixels) - return; - memset(dirtyPixels, 0, dirtyPixelsSize); - ghostPixelCount = 0; -} -#endif - -/* - * forceDisplay: use lastDrawMsec - */ -bool EInkParallelDisplay::forceDisplay(uint32_t msecLimit) -{ - uint32_t now = millis(); - if (lastDrawMsec == 0 || (now - lastDrawMsec) > msecLimit) { - display(); - return true; - } - return false; -} - -void EInkParallelDisplay::endUpdate() -{ - { - // ensure any async full update is started/completed - if (asyncFullRunning.load()) { - // nothing to do; background task will run and call backupPlane when done - } else { - epaper->fullUpdate(CLEAR_FAST, false); - epaper->backupPlane(); -#ifdef EINK_LIMIT_GHOSTING_PX - resetGhostPixelTracking(); -#endif - } - } -} - -#endif \ No newline at end of file diff --git a/src/graphics/EInkParallelDisplay.h b/src/graphics/EInkParallelDisplay.h deleted file mode 100644 index 81189e400..000000000 --- a/src/graphics/EInkParallelDisplay.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include "configuration.h" - -#ifdef USE_EINK_PARALLELDISPLAY -#include - -#include -#include -#include - -class FASTEPD; - -/** - * Adapter for E-Ink 8-bit parallel displays (EPD), specifically devices supported by FastEPD library - */ -class EInkParallelDisplay : public OLEDDisplay -{ - public: - enum EpdRotation { - EPD_ROT_LANDSCAPE = 0, - EPD_ROT_PORTRAIT = 90, - EPD_ROT_INVERTED_LANDSCAPE = 180, - EPD_ROT_INVERTED_PORTRAIT = 270, - }; - - EInkParallelDisplay(uint16_t width, uint16_t height, EpdRotation rotation); - virtual ~EInkParallelDisplay(); - - // OLEDDisplay virtuals - bool connect() override; - void sendCommand(uint8_t com) override; - int getBufferOffset(void) override { return 0; } - - void display(void) override; - bool forceDisplay(uint32_t msecLimit = 1000); - void endUpdate(); - - protected: - uint32_t lastDrawMsec = 0; - FASTEPD *epaper; - - private: - // Async full-refresh support - std::atomic asyncFullRunning{false}; - TaskHandle_t asyncTaskHandle = nullptr; - void startAsyncFullUpdate(int clearMode); - static void asyncFullUpdateTask(void *pvParameters); - -#ifdef EINK_LIMIT_GHOSTING_PX - // helpers - void resetGhostPixelTracking(); - void markDirtyBits(const uint8_t *prevBuf, uint32_t pos, uint8_t mask, uint8_t out); - void countGhostPixelsAndMaybePromote(int &newTop, int &newBottom, bool &forceFull); - - // per-bit dirty buffer (same format as epaper buffers): one bit == one pixel - uint8_t *dirtyPixels = nullptr; - size_t dirtyPixelsSize = 0; - uint32_t ghostPixelCount = 0; - uint32_t ghostPixelLimit = EINK_LIMIT_GHOSTING_PX; -#endif - - EpdRotation rotation; - uint32_t previousImageHash = 0; - uint32_t lastUpdateMs = 0; - int fastRefreshCount = 0; -}; - -#endif diff --git a/src/graphics/GxEPD2Multi.h b/src/graphics/GxEPD2Multi.h deleted file mode 100644 index f3807c9de..000000000 --- a/src/graphics/GxEPD2Multi.h +++ /dev/null @@ -1,135 +0,0 @@ -// Wrapper class for GxEPD2_BW - -// Generic signature at build-time, so that we can detect display model at run-time -// Workaround for issue of GxEPD2_BW objects not having a shared base class -// Only exposes methods which we are actually using - -template class GxEPD2_Multi -{ - public: - void drawPixel(int16_t x, int16_t y, uint16_t color) - { - if (which == 0) - driver0->drawPixel(x, y, color); - else - driver1->drawPixel(x, y, color); - } - - bool nextPage() - { - if (which == 0) - return driver0->nextPage(); - else - return driver1->nextPage(); - } - - void hibernate() - { - if (which == 0) - driver0->hibernate(); - else - driver1->hibernate(); - } - - void init(uint32_t serial_diag_bitrate = 0) - { - if (which == 0) - driver0->init(serial_diag_bitrate); - else - driver1->init(serial_diag_bitrate); - } - - void init(uint32_t serial_diag_bitrate, bool initial, uint16_t reset_duration = 20, bool pulldown_rst_mode = false) - { - if (which == 0) - driver0->init(serial_diag_bitrate, initial, reset_duration, pulldown_rst_mode); - else - driver1->init(serial_diag_bitrate, initial, reset_duration, pulldown_rst_mode); - } - - void setRotation(uint8_t x) - { - if (which == 0) - driver0->setRotation(x); - else - driver1->setRotation(x); - } - - void setPartialWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) - { - if (which == 0) - driver0->setPartialWindow(x, y, w, h); - else - driver1->setPartialWindow(x, y, w, h); - } - - void setFullWindow() - { - if (which == 0) - driver0->setFullWindow(); - else - driver1->setFullWindow(); - } - - int16_t width() - { - if (which == 0) - return driver0->width(); - else - return driver1->width(); - } - - int16_t height() - { - if (which == 0) - return driver0->height(); - else - return driver1->height(); - } - - void clearScreen(uint8_t value = 0xFF) - { - if (which == 0) - driver0->clearScreen(); - else - driver1->clearScreen(); - } - - void endAsyncFull() - { - if (which == 0) - driver0->endAsyncFull(); - else - driver1->endAsyncFull(); - } - - // Exposes methods of the GxEPD2_EPD object which is usually available as GxEPD2_BW::epd - class Epd2Wrapper - { - public: - bool isBusy() { return m_epd2->isBusy(); } - GxEPD2_EPD *m_epd2; - } epd2; - - // Constructor - // Select driver by passing whichDriver as 0 or 1 - GxEPD2_Multi(uint8_t whichDriver, int16_t cs, int16_t dc, int16_t rst, int16_t busy, SPIClass &spi) - { - assert(whichDriver == 0 || whichDriver == 1); - which = whichDriver; - LOG_DEBUG("GxEPD2_Multi driver: %d", which); - - if (which == 0) { - driver0 = new GxEPD2_BW(Driver0(cs, dc, rst, busy, spi)); - epd2.m_epd2 = &(driver0->epd2); - } else if (which == 1) { - driver1 = new GxEPD2_BW(Driver1(cs, dc, rst, busy, spi)); - epd2.m_epd2 = &(driver1->epd2); - } - } - - private: - uint8_t which; - GxEPD2_BW *driver0; - GxEPD2_BW *driver1; -}; \ No newline at end of file diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index fa9d98a0e..e99153a91 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -27,9 +27,14 @@ along with this program. If not, see . #include "configuration.h" #include "meshUtils.h" #if HAS_SCREEN -#include "EInkParallelDisplay.h" #include +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS +#include "graphics/BaseUIEInkDisplay.h" +// Provided by each niche-enabled variant's nicheGraphics.h (defined once, in main.cpp TU). +extern NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI(); +#endif + #include "DisplayFormatters.h" #include "TimeFormatters.h" #include "draw/ClockRenderer.h" @@ -365,14 +370,9 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(HACKADAY_COMMUNICATOR) dispdev = new TFTDisplay(address.address, -1, -1, geometry, (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE); -#elif defined(USE_EINK) && !defined(USE_EINK_DYNAMICDISPLAY) && !defined(USE_EINK_PARALLELDISPLAY) - dispdev = new EInkDisplay(address.address, -1, -1, geometry, - (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE); -#elif defined(USE_EINK) && defined(USE_EINK_DYNAMICDISPLAY) - dispdev = new EInkDynamicDisplay(address.address, -1, -1, geometry, - (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE); -#elif defined(USE_EINK_PARALLELDISPLAY) - dispdev = new EInkParallelDisplay(EPD_WIDTH, EPD_HEIGHT, EInkParallelDisplay::EPD_ROT_PORTRAIT); +#elif defined(USE_EINK) && defined(MESHTASTIC_INCLUDE_NICHE_GRAPHICS) + // NicheGraphics-backed BaseUI E-Ink path. Variant provides setupNicheGraphicsBaseUI() in its nicheGraphics.h. + dispdev = setupNicheGraphicsBaseUI(); #elif defined(USE_ST7567) dispdev = new ST7567Wire(address.address, -1, -1, geometry, (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE); @@ -762,10 +762,8 @@ void Screen::forceDisplay(bool forceUiUpdate) } // Tell EInk class to update the display -#if defined(USE_EINK_PARALLELDISPLAY) - static_cast(dispdev)->forceDisplay(); -#elif defined(USE_EINK) - static_cast(dispdev)->forceDisplay(); +#if defined(MESHTASTIC_INCLUDE_NICHE_GRAPHICS) + static_cast(dispdev)->forceDisplay(); #endif #else // No delay between UI frame rendering @@ -939,12 +937,7 @@ int32_t Screen::runOnce() NotificationRenderer::current_notification_type != notificationTypeEnum::text_input && !Throttle::isWithinTimespanMs(lastScreenTransition, config.display.auto_screen_carousel_secs * 1000)) { - // If an E-Ink display struggles with fast refresh, force carousel to use full refresh instead - // Carousel is potentially a major source of E-Ink display wear -#if !defined(EINK_BACKGROUND_USES_FAST) - EINK_ADD_FRAMEFLAG(dispdev, COSMETIC); -#endif - + // Carousel rotations let BaseUIEInkDisplay's DisplayHealth debt model decide FAST vs FULL. LOG_DEBUG("LastScreenTransition exceeded %ums transition to next frame", (millis() - lastScreenTransition)); handleOnPress(); } @@ -978,11 +971,8 @@ void Screen::setScreensaverFrames(FrameCallback einkScreensaver) static FrameCallback screensaverFrame; static OverlayCallback screensaverOverlay; -#if defined(HAS_EINK_ASYNCFULL) && defined(USE_EINK_DYNAMICDISPLAY) - // Join (await) a currently running async refresh, then run the post-update code. - // Avoid skipping of screensaver frame. Would otherwise be handled by NotifiedWorkerThread. + // Join (await) any currently running async refresh before drawing the screensaver frame. EINK_JOIN_ASYNCREFRESH(dispdev); -#endif // If: one-off screensaver frame passed as argument. Handles doDeepSleep() if (einkScreensaver != NULL) { @@ -1005,23 +995,17 @@ void Screen::setScreensaverFrames(FrameCallback einkScreensaver) ui->update(); } while (ui->getUiState()->lastUpdate < startUpdate); -#if defined(USE_EINK_PARALLELDISPLAY) - static_cast(dispdev)->forceDisplay(0); -#elif defined(USE_EINK) && !defined(USE_EINK_DYNAMICDISPLAY) - // Old EInkDisplay class - static_cast(dispdev)->forceDisplay(0); // Screen::forceDisplay(), but override rate-limit +#if defined(MESHTASTIC_INCLUDE_NICHE_GRAPHICS) + static_cast(dispdev)->forceDisplay(0); #endif // Prepare now for next frame, shown when display wakes ui->setOverlays(NULL, 0); // Clear overlay setFrames(FOCUS_PRESERVE); // Return to normal display updates, showing same frame as before screensaver, ideally - // Pick a refresh method, for when display wakes -#ifdef EINK_HASQUIRK_GHOSTING - EINK_ADD_FRAMEFLAG(dispdev, COSMETIC); // Really ugly to see ghosting from "screen paused" -#else - EINK_ADD_FRAMEFLAG(dispdev, RESPONSIVE); // Really nice to wake screen with a fast-refresh -#endif + // Pick a refresh method for when the display wakes. RESPONSIVE = FAST; DisplayHealth + // will promote to FULL on its own schedule if FAST debt has built up. + EINK_ADD_FRAMEFLAG(dispdev, RESPONSIVE); } #endif diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index e259f7691..62ff529a1 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -87,8 +87,7 @@ class Screen #include #endif -#include "EInkDisplay2.h" -#include "EInkDynamicDisplay.h" +#include "BaseUIEInkDisplay.h" #include "PointStruct.h" #include "TFTDisplay.h" #include "TypedQueue.h" diff --git a/src/graphics/niche/Drivers/Backlight/LatchingBacklight.cpp b/src/graphics/eink/Backlight/LatchingBacklight.cpp similarity index 100% rename from src/graphics/niche/Drivers/Backlight/LatchingBacklight.cpp rename to src/graphics/eink/Backlight/LatchingBacklight.cpp diff --git a/src/graphics/niche/Drivers/Backlight/LatchingBacklight.h b/src/graphics/eink/Backlight/LatchingBacklight.h similarity index 100% rename from src/graphics/niche/Drivers/Backlight/LatchingBacklight.h rename to src/graphics/eink/Backlight/LatchingBacklight.h diff --git a/src/graphics/niche/Drivers/EInk/DEPG0213BNS800.cpp b/src/graphics/eink/Drivers/DEPG0213BNS800.cpp similarity index 100% rename from src/graphics/niche/Drivers/EInk/DEPG0213BNS800.cpp rename to src/graphics/eink/Drivers/DEPG0213BNS800.cpp diff --git a/src/graphics/niche/Drivers/EInk/DEPG0213BNS800.h b/src/graphics/eink/Drivers/DEPG0213BNS800.h similarity index 100% rename from src/graphics/niche/Drivers/EInk/DEPG0213BNS800.h rename to src/graphics/eink/Drivers/DEPG0213BNS800.h diff --git a/src/graphics/niche/Drivers/EInk/DEPG0290BNS800.cpp b/src/graphics/eink/Drivers/DEPG0290BNS800.cpp similarity index 100% rename from src/graphics/niche/Drivers/EInk/DEPG0290BNS800.cpp rename to src/graphics/eink/Drivers/DEPG0290BNS800.cpp diff --git a/src/graphics/niche/Drivers/EInk/DEPG0290BNS800.h b/src/graphics/eink/Drivers/DEPG0290BNS800.h similarity index 100% rename from src/graphics/niche/Drivers/EInk/DEPG0290BNS800.h rename to src/graphics/eink/Drivers/DEPG0290BNS800.h diff --git a/src/graphics/niche/Drivers/EInk/E0213A367.cpp b/src/graphics/eink/Drivers/E0213A367.cpp similarity index 100% rename from src/graphics/niche/Drivers/EInk/E0213A367.cpp rename to src/graphics/eink/Drivers/E0213A367.cpp diff --git a/src/graphics/niche/Drivers/EInk/E0213A367.h b/src/graphics/eink/Drivers/E0213A367.h similarity index 100% rename from src/graphics/niche/Drivers/EInk/E0213A367.h rename to src/graphics/eink/Drivers/E0213A367.h diff --git a/src/graphics/niche/Drivers/EInk/EInk.cpp b/src/graphics/eink/Drivers/EInk.cpp similarity index 100% rename from src/graphics/niche/Drivers/EInk/EInk.cpp rename to src/graphics/eink/Drivers/EInk.cpp diff --git a/src/graphics/niche/Drivers/EInk/EInk.h b/src/graphics/eink/Drivers/EInk.h similarity index 100% rename from src/graphics/niche/Drivers/EInk/EInk.h rename to src/graphics/eink/Drivers/EInk.h diff --git a/src/graphics/eink/Drivers/EInkParallel.cpp b/src/graphics/eink/Drivers/EInkParallel.cpp new file mode 100644 index 000000000..ed0c48a26 --- /dev/null +++ b/src/graphics/eink/Drivers/EInkParallel.cpp @@ -0,0 +1,132 @@ +#include "./EInkParallel.h" + +#if defined(MESHTASTIC_INCLUDE_NICHE_GRAPHICS) && defined(ARCH_ESP32) && defined(NICHE_HAS_FASTEPD) + +#include "FastEPD.h" + +using namespace NicheGraphics::Drivers; + +EInkParallel::EInkParallel(uint16_t width, uint16_t height, uint32_t panelType, uint32_t panelClock) + : EInk(width, height, (UpdateTypes)(FULL | FAST)), panelType(panelType), panelClock(panelClock) +{ +} + +EInkParallel::~EInkParallel() +{ + if (asyncRunning.load()) { + for (int i = 0; i < 50 && asyncRunning.load(); ++i) + delay(50); + if (asyncTaskHandle) { + vTaskDelete(asyncTaskHandle); + asyncTaskHandle = nullptr; + } + } + delete epaper; +} + +void EInkParallel::begin(SPIClass *, uint8_t, uint8_t, uint8_t, uint8_t) +{ + // Parallel panels don't use the SPI args; FastEPD owns the bus. + if (!epaper) { + epaper = new FASTEPD; + epaper->initPanel((int)panelType, panelClock); + postPanelInit(); + epaper->setMode(BB_MODE_1BPP); + epaper->clearWhite(); + epaper->fullUpdate(true); + } +} + +void EInkParallel::update(uint8_t *imageData, UpdateTypes type) +{ + if (!epaper) + return; + + pendingType = type; + copyImageInverted(imageData); + + if (type == FULL) { + // Pick CLEAR_SLOW periodically to clear ghosting. + const int clearMode = (fastRefreshCount >= FULL_SLOW_PERIOD) ? CLEAR_SLOW : CLEAR_FAST; + fastRefreshCount = 0; + + if (!asyncRunning.load()) { + asyncRunning.store(true); + BaseType_t rc = + xTaskCreatePinnedToCore(asyncFullTask, "epd_full", 4096 / sizeof(StackType_t), this, 2, &asyncTaskHandle, +#if CONFIG_FREERTOS_UNICORE + 0 +#else + 1 +#endif + ); + if (rc != pdPASS) { + LOG_WARN("Async full failed; running blocking"); + epaper->fullUpdate(clearMode, false); + epaper->backupPlane(); + asyncRunning.store(false); + asyncTaskHandle = nullptr; + return; // synchronous: nothing to poll + } + // Begin polling for completion. + beginPolling(100, 1500); + } + } else { + // FAST: synchronous partial / clipped fullUpdate. Block briefly here. + epaper->fullUpdate(CLEAR_FAST, false); + epaper->backupPlane(); + fastRefreshCount++; + // No polling needed; isUpdateDone() will report done immediately. + beginPolling(10, 0); + } +} + +void EInkParallel::asyncFullTask(void *param) +{ + auto *self = static_cast(param); + if (!self) { + vTaskDelete(nullptr); + return; + } + self->epaper->fullUpdate(CLEAR_FAST, false); + self->epaper->backupPlane(); + self->asyncRunning.store(false); + self->asyncTaskHandle = nullptr; + vTaskDelete(nullptr); +} + +bool EInkParallel::isUpdateDone() +{ + return !asyncRunning.load(); +} + +void EInkParallel::finalizeUpdate() +{ + pendingType = UpdateTypes::UNSPECIFIED; +} + +// Convert a niche-format buffer (row-major, MSB-left, 1=WHITE) into FastEPD's currentBuffer +// (row-major, MSB-left, 1=BLACK). Polarity inversion only. +void EInkParallel::copyImageInverted(const uint8_t *src) +{ + uint8_t *dst = epaper->currentBuffer(); + if (!dst || !src) + return; + + const uint16_t rowBytes = ((width - 1) / 8) + 1; + const uint32_t total = rowBytes * height; + + // Mask off bits beyond the panel width in the trailing byte of each row. + const uint8_t trailingMask = (uint8_t)(0xFFu << ((rowBytes * 8) - width)); + + for (uint16_t y = 0; y < height; y++) { + const uint32_t base = y * rowBytes; + for (uint16_t b = 0; b < rowBytes - 1; b++) { + dst[base + b] = ~src[base + b]; + } + dst[base + rowBytes - 1] = (~src[base + rowBytes - 1]) & trailingMask; + } + (void)total; +} + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS && ARCH_ESP32 && NICHE_HAS_FASTEPD diff --git a/src/graphics/eink/Drivers/EInkParallel.h b/src/graphics/eink/Drivers/EInkParallel.h new file mode 100644 index 000000000..7a0978be1 --- /dev/null +++ b/src/graphics/eink/Drivers/EInkParallel.h @@ -0,0 +1,69 @@ +/* + +Parallel-EPD niche driver, backed by FastEPD. + +Used for boards with an 8-bit parallel EPD interface (e.g. LILYGO T5 S3 ePaper). +The base class signature passes SPI parameters; this driver ignores them and uses FastEPD +to drive the parallel bus directly. + +Gated on NICHE_HAS_FASTEPD because FastEPD is a heavy dependency that only parallel-EPD +variants want pulled in. Variants opt in by defining NICHE_HAS_FASTEPD in their platformio.ini +and adding the FastEPD library to lib_deps. + +*/ + +#pragma once + +#include "configuration.h" + +#if defined(MESHTASTIC_INCLUDE_NICHE_GRAPHICS) && defined(ARCH_ESP32) && defined(NICHE_HAS_FASTEPD) + +#include "./EInk.h" + +#include +#include +#include + +class FASTEPD; + +namespace NicheGraphics::Drivers +{ + +class EInkParallel : public EInk +{ + public: + EInkParallel(uint16_t width, uint16_t height, uint32_t panelType, uint32_t panelClock = 28000000); + ~EInkParallel(); + + // SPI parameters are unused for parallel panels. + void begin(SPIClass *spi, uint8_t pin_dc, uint8_t pin_cs, uint8_t pin_busy, uint8_t pin_rst = -1) override; + void update(uint8_t *imageData, UpdateTypes type) override; + + FASTEPD *fastEpd() { return epaper; } + + protected: + bool isUpdateDone() override; + void finalizeUpdate() override; + + // Hook for boards that need to bring up GPIO expanders / power pins after FastEPD::initPanel. + virtual void postPanelInit() {} + + private: + void copyImageInverted(const uint8_t *src); + static void asyncFullTask(void *param); + + FASTEPD *epaper = nullptr; + uint32_t panelType; + uint32_t panelClock; + + UpdateTypes pendingType = UpdateTypes::UNSPECIFIED; + std::atomic asyncRunning{false}; + TaskHandle_t asyncTaskHandle = nullptr; + + uint8_t fastRefreshCount = 0; + static constexpr uint8_t FULL_SLOW_PERIOD = 100; +}; + +} // namespace NicheGraphics::Drivers + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS && ARCH_ESP32 && NICHE_HAS_FASTEPD diff --git a/src/graphics/eink/Drivers/GDEH0122T61.cpp b/src/graphics/eink/Drivers/GDEH0122T61.cpp new file mode 100644 index 000000000..38aac91fc --- /dev/null +++ b/src/graphics/eink/Drivers/GDEH0122T61.cpp @@ -0,0 +1,49 @@ +#include "./GDEH0122T61.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +using namespace NicheGraphics::Drivers; + +void GDEH0122T61::configScanning() +{ + sendCommand(0x01); + sendData(0xAF); // Scan until gate 175 (176px vertical resolution, low byte) + sendData(0x00); // high byte + sendData(0x00); +} + +void GDEH0122T61::configWaveform() +{ + sendCommand(0x3C); + sendData(0x05); + + sendCommand(0x18); + sendData(0x80); +} + +void GDEH0122T61::configUpdateSequence() +{ + switch (updateType) { + case FAST: + sendCommand(0x22); + sendData(0xFF); + break; + case FULL: + default: + sendCommand(0x22); + sendData(0xF7); + break; + } +} + +void GDEH0122T61::detachFromUpdate() +{ + switch (updateType) { + case FAST: + return beginPolling(50, 250); + case FULL: + default: + return beginPolling(100, 1500); + } +} +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/src/graphics/eink/Drivers/GDEH0122T61.h b/src/graphics/eink/Drivers/GDEH0122T61.h new file mode 100644 index 000000000..42c585d0e --- /dev/null +++ b/src/graphics/eink/Drivers/GDEH0122T61.h @@ -0,0 +1,43 @@ +/* + +E-Ink display driver + - GDEH0122T61 + - Manufacturer: Good Display + - Size: 1.22 inch + - Resolution: 192px x 176px + - Controller IC: SSD1681 (operating in a sub-200x200 window) + + Used by: t-echo-lite. + +*/ + +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "configuration.h" + +#include "./SSD16XX.h" + +namespace NicheGraphics::Drivers +{ +class GDEH0122T61 : public SSD16XX +{ + private: + static constexpr uint32_t width = 192; + static constexpr uint32_t height = 176; + static constexpr UpdateTypes supported = (UpdateTypes)(FULL | FAST); + + public: + GDEH0122T61() : SSD16XX(width, height, supported) {} + + protected: + void configScanning() override; + void configWaveform() override; + void configUpdateSequence() override; + void detachFromUpdate() override; +}; + +} // namespace NicheGraphics::Drivers + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/src/graphics/eink/Drivers/GDEQ031T10.cpp b/src/graphics/eink/Drivers/GDEQ031T10.cpp new file mode 100644 index 000000000..f6ed66ee8 --- /dev/null +++ b/src/graphics/eink/Drivers/GDEQ031T10.cpp @@ -0,0 +1,57 @@ +#include "./GDEQ031T10.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +using namespace NicheGraphics::Drivers; + +void GDEQ031T10::configScanning() +{ + sendCommand(0x01); + sendData(0x3F); // 319, low byte + sendData(0x01); // 319, high byte + sendData(0x00); +} + +void GDEQ031T10::configWaveform() +{ + sendCommand(0x3C); + sendData(0x01); + + sendCommand(0x18); + sendData(0x80); +} + +void GDEQ031T10::configUpdateSequence() +{ + switch (updateType) { + case FAST: + sendCommand(0x21); + sendData(0x00); + sendData(0x00); + + sendCommand(0x22); + sendData(0xFF); + break; + case FULL: + default: + sendCommand(0x21); + sendData(0x40); + sendData(0x00); + + sendCommand(0x22); + sendData(0xF7); + break; + } +} + +void GDEQ031T10::detachFromUpdate() +{ + switch (updateType) { + case FAST: + return beginPolling(50, 400); + case FULL: + default: + return beginPolling(100, 2500); + } +} +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/src/graphics/eink/Drivers/GDEQ031T10.h b/src/graphics/eink/Drivers/GDEQ031T10.h new file mode 100644 index 000000000..9d26156fe --- /dev/null +++ b/src/graphics/eink/Drivers/GDEQ031T10.h @@ -0,0 +1,43 @@ +/* + +E-Ink display driver + - GDEQ031T10 + - Manufacturer: Good Display + - Size: 3.1 inch + - Resolution: 240px x 320px + - Controller IC: SSD1677 (SSD16XX-family, larger memory range) + + Used by: t-deck-pro. + +*/ + +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "configuration.h" + +#include "./SSD16XX.h" + +namespace NicheGraphics::Drivers +{ +class GDEQ031T10 : public SSD16XX +{ + private: + static constexpr uint32_t width = 240; + static constexpr uint32_t height = 320; + static constexpr UpdateTypes supported = (UpdateTypes)(FULL | FAST); + + public: + GDEQ031T10() : SSD16XX(width, height, supported) {} + + protected: + void configScanning() override; + void configWaveform() override; + void configUpdateSequence() override; + void detachFromUpdate() override; +}; + +} // namespace NicheGraphics::Drivers + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/src/graphics/niche/Drivers/EInk/GDEW0102T4.cpp b/src/graphics/eink/Drivers/GDEW0102T4.cpp similarity index 100% rename from src/graphics/niche/Drivers/EInk/GDEW0102T4.cpp rename to src/graphics/eink/Drivers/GDEW0102T4.cpp diff --git a/src/graphics/niche/Drivers/EInk/GDEW0102T4.h b/src/graphics/eink/Drivers/GDEW0102T4.h similarity index 100% rename from src/graphics/niche/Drivers/EInk/GDEW0102T4.h rename to src/graphics/eink/Drivers/GDEW0102T4.h diff --git a/src/graphics/niche/Drivers/EInk/GDEY0154D67.cpp b/src/graphics/eink/Drivers/GDEY0154D67.cpp similarity index 100% rename from src/graphics/niche/Drivers/EInk/GDEY0154D67.cpp rename to src/graphics/eink/Drivers/GDEY0154D67.cpp diff --git a/src/graphics/niche/Drivers/EInk/GDEY0154D67.h b/src/graphics/eink/Drivers/GDEY0154D67.h similarity index 100% rename from src/graphics/niche/Drivers/EInk/GDEY0154D67.h rename to src/graphics/eink/Drivers/GDEY0154D67.h diff --git a/src/graphics/niche/Drivers/EInk/GDEY0213B74.cpp b/src/graphics/eink/Drivers/GDEY0213B74.cpp similarity index 100% rename from src/graphics/niche/Drivers/EInk/GDEY0213B74.cpp rename to src/graphics/eink/Drivers/GDEY0213B74.cpp diff --git a/src/graphics/niche/Drivers/EInk/GDEY0213B74.h b/src/graphics/eink/Drivers/GDEY0213B74.h similarity index 100% rename from src/graphics/niche/Drivers/EInk/GDEY0213B74.h rename to src/graphics/eink/Drivers/GDEY0213B74.h diff --git a/src/graphics/eink/Drivers/GDEY029T94.cpp b/src/graphics/eink/Drivers/GDEY029T94.cpp new file mode 100644 index 000000000..ec2e801db --- /dev/null +++ b/src/graphics/eink/Drivers/GDEY029T94.cpp @@ -0,0 +1,49 @@ +#include "./GDEY029T94.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +using namespace NicheGraphics::Drivers; + +void GDEY029T94::configScanning() +{ + sendCommand(0x01); + sendData(0x27); // 295, low byte + sendData(0x01); // 295, high byte + sendData(0x00); +} + +void GDEY029T94::configWaveform() +{ + sendCommand(0x3C); + sendData(0x05); + + sendCommand(0x18); + sendData(0x80); +} + +void GDEY029T94::configUpdateSequence() +{ + switch (updateType) { + case FAST: + sendCommand(0x22); + sendData(0xFF); + break; + case FULL: + default: + sendCommand(0x22); + sendData(0xF7); + break; + } +} + +void GDEY029T94::detachFromUpdate() +{ + switch (updateType) { + case FAST: + return beginPolling(50, 300); + case FULL: + default: + return beginPolling(100, 2000); + } +} +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/src/graphics/eink/Drivers/GDEY029T94.h b/src/graphics/eink/Drivers/GDEY029T94.h new file mode 100644 index 000000000..732fb185c --- /dev/null +++ b/src/graphics/eink/Drivers/GDEY029T94.h @@ -0,0 +1,43 @@ +/* + +E-Ink display driver + - GDEY029T94 (also sold as GDEY029T94-V2) + - Manufacturer: Good Display + - Size: 2.9 inch + - Resolution: 128px x 296px + - Controller IC: SSD1680 + + Used by: esp32-s3-pico, crowpanel-esp32s3-2-epaper. + +*/ + +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "configuration.h" + +#include "./SSD16XX.h" + +namespace NicheGraphics::Drivers +{ +class GDEY029T94 : public SSD16XX +{ + private: + static constexpr uint32_t width = 128; + static constexpr uint32_t height = 296; + static constexpr UpdateTypes supported = (UpdateTypes)(FULL | FAST); + + public: + GDEY029T94() : SSD16XX(width, height, supported) {} + + protected: + void configScanning() override; + void configWaveform() override; + void configUpdateSequence() override; + void detachFromUpdate() override; +}; + +} // namespace NicheGraphics::Drivers + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/src/graphics/eink/Drivers/GDEY042T81.cpp b/src/graphics/eink/Drivers/GDEY042T81.cpp new file mode 100644 index 000000000..4f19dcc9e --- /dev/null +++ b/src/graphics/eink/Drivers/GDEY042T81.cpp @@ -0,0 +1,49 @@ +#include "./GDEY042T81.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +using namespace NicheGraphics::Drivers; + +void GDEY042T81::configWaveform() +{ + sendCommand(0x3C); + sendData(0x01); + + sendCommand(0x18); + sendData(0x80); +} + +void GDEY042T81::configUpdateSequence() +{ + switch (updateType) { + case FAST: + sendCommand(0x21); + sendData(0x00); + sendData(0x00); + + sendCommand(0x22); + sendData(0xFF); + break; + case FULL: + default: + sendCommand(0x21); + sendData(0x40); + sendData(0x00); + + sendCommand(0x22); + sendData(0xF7); + break; + } +} + +void GDEY042T81::detachFromUpdate() +{ + switch (updateType) { + case FAST: + return beginPolling(50, 1000); + case FULL: + default: + return beginPolling(100, 3500); + } +} +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/src/graphics/eink/Drivers/GDEY042T81.h b/src/graphics/eink/Drivers/GDEY042T81.h new file mode 100644 index 000000000..166b45266 --- /dev/null +++ b/src/graphics/eink/Drivers/GDEY042T81.h @@ -0,0 +1,42 @@ +/* + +E-Ink display driver + - GDEY042T81 + - Manufacturer: Good Display + - Size: 4.2 inch + - Resolution: 400px x 300px + - Controller IC: SSD1683 + + Used by: ME25LS01-4Y10TD_e-ink. + +*/ + +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "configuration.h" + +#include "./SSD16XX.h" + +namespace NicheGraphics::Drivers +{ +class GDEY042T81 : public SSD16XX +{ + private: + static constexpr uint32_t width = 400; + static constexpr uint32_t height = 300; + static constexpr UpdateTypes supported = (UpdateTypes)(FULL | FAST); + + public: + GDEY042T81() : SSD16XX(width, height, supported) {} + + protected: + void configWaveform() override; + void configUpdateSequence() override; + void detachFromUpdate() override; +}; + +} // namespace NicheGraphics::Drivers + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/src/graphics/eink/Drivers/GDEY0579T93.cpp b/src/graphics/eink/Drivers/GDEY0579T93.cpp new file mode 100644 index 000000000..f076108fb --- /dev/null +++ b/src/graphics/eink/Drivers/GDEY0579T93.cpp @@ -0,0 +1,57 @@ +#include "./GDEY0579T93.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +using namespace NicheGraphics::Drivers; + +void GDEY0579T93::configScanning() +{ + sendCommand(0x01); + sendData(0x0F); // 271, low byte + sendData(0x01); // 271, high byte + sendData(0x00); +} + +void GDEY0579T93::configWaveform() +{ + sendCommand(0x3C); + sendData(0x01); + + sendCommand(0x18); + sendData(0x80); +} + +void GDEY0579T93::configUpdateSequence() +{ + switch (updateType) { + case FAST: + sendCommand(0x21); + sendData(0x00); + sendData(0x00); + + sendCommand(0x22); + sendData(0xFF); + break; + case FULL: + default: + sendCommand(0x21); + sendData(0x40); + sendData(0x00); + + sendCommand(0x22); + sendData(0xF7); + break; + } +} + +void GDEY0579T93::detachFromUpdate() +{ + switch (updateType) { + case FAST: + return beginPolling(100, 2000); + case FULL: + default: + return beginPolling(150, 5000); + } +} +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/src/graphics/eink/Drivers/GDEY0579T93.h b/src/graphics/eink/Drivers/GDEY0579T93.h new file mode 100644 index 000000000..05956405c --- /dev/null +++ b/src/graphics/eink/Drivers/GDEY0579T93.h @@ -0,0 +1,43 @@ +/* + +E-Ink display driver + - GDEY0579T93 + - Manufacturer: Good Display + - Size: 5.79 inch + - Resolution: 792px x 272px + - Controller IC: SSD1683 (extended memory range) + + Used by: crowpanel-esp32s3-5-epaper. + +*/ + +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "configuration.h" + +#include "./SSD16XX.h" + +namespace NicheGraphics::Drivers +{ +class GDEY0579T93 : public SSD16XX +{ + private: + static constexpr uint32_t width = 792; + static constexpr uint32_t height = 272; + static constexpr UpdateTypes supported = (UpdateTypes)(FULL | FAST); + + public: + GDEY0579T93() : SSD16XX(width, height, supported) {} + + protected: + void configScanning() override; + void configWaveform() override; + void configUpdateSequence() override; + void detachFromUpdate() override; +}; + +} // namespace NicheGraphics::Drivers + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/src/graphics/niche/Drivers/EInk/HINK_E0213A289.cpp b/src/graphics/eink/Drivers/HINK_E0213A289.cpp similarity index 100% rename from src/graphics/niche/Drivers/EInk/HINK_E0213A289.cpp rename to src/graphics/eink/Drivers/HINK_E0213A289.cpp diff --git a/src/graphics/niche/Drivers/EInk/HINK_E0213A289.h b/src/graphics/eink/Drivers/HINK_E0213A289.h similarity index 100% rename from src/graphics/niche/Drivers/EInk/HINK_E0213A289.h rename to src/graphics/eink/Drivers/HINK_E0213A289.h diff --git a/src/graphics/niche/Drivers/EInk/HINK_E042A87.cpp b/src/graphics/eink/Drivers/HINK_E042A87.cpp similarity index 100% rename from src/graphics/niche/Drivers/EInk/HINK_E042A87.cpp rename to src/graphics/eink/Drivers/HINK_E042A87.cpp diff --git a/src/graphics/niche/Drivers/EInk/HINK_E042A87.h b/src/graphics/eink/Drivers/HINK_E042A87.h similarity index 100% rename from src/graphics/niche/Drivers/EInk/HINK_E042A87.h rename to src/graphics/eink/Drivers/HINK_E042A87.h diff --git a/src/graphics/niche/Drivers/EInk/LCMEN2R13ECC1.cpp b/src/graphics/eink/Drivers/LCMEN2R13ECC1.cpp similarity index 100% rename from src/graphics/niche/Drivers/EInk/LCMEN2R13ECC1.cpp rename to src/graphics/eink/Drivers/LCMEN2R13ECC1.cpp diff --git a/src/graphics/niche/Drivers/EInk/LCMEN2R13ECC1.h b/src/graphics/eink/Drivers/LCMEN2R13ECC1.h similarity index 100% rename from src/graphics/niche/Drivers/EInk/LCMEN2R13ECC1.h rename to src/graphics/eink/Drivers/LCMEN2R13ECC1.h diff --git a/src/graphics/niche/Drivers/EInk/LCMEN2R13EFC1.cpp b/src/graphics/eink/Drivers/LCMEN2R13EFC1.cpp similarity index 100% rename from src/graphics/niche/Drivers/EInk/LCMEN2R13EFC1.cpp rename to src/graphics/eink/Drivers/LCMEN2R13EFC1.cpp diff --git a/src/graphics/niche/Drivers/EInk/LCMEN2R13EFC1.h b/src/graphics/eink/Drivers/LCMEN2R13EFC1.h similarity index 100% rename from src/graphics/niche/Drivers/EInk/LCMEN2R13EFC1.h rename to src/graphics/eink/Drivers/LCMEN2R13EFC1.h diff --git a/src/graphics/niche/Drivers/README.md b/src/graphics/eink/Drivers/README-legacy.md similarity index 100% rename from src/graphics/niche/Drivers/README.md rename to src/graphics/eink/Drivers/README-legacy.md diff --git a/src/graphics/niche/Drivers/EInk/README.md b/src/graphics/eink/Drivers/README.md similarity index 100% rename from src/graphics/niche/Drivers/EInk/README.md rename to src/graphics/eink/Drivers/README.md diff --git a/src/graphics/niche/Drivers/EInk/SSD1682.cpp b/src/graphics/eink/Drivers/SSD1682.cpp similarity index 100% rename from src/graphics/niche/Drivers/EInk/SSD1682.cpp rename to src/graphics/eink/Drivers/SSD1682.cpp diff --git a/src/graphics/niche/Drivers/EInk/SSD1682.h b/src/graphics/eink/Drivers/SSD1682.h similarity index 100% rename from src/graphics/niche/Drivers/EInk/SSD1682.h rename to src/graphics/eink/Drivers/SSD1682.h diff --git a/src/graphics/niche/Drivers/EInk/SSD16XX.cpp b/src/graphics/eink/Drivers/SSD16XX.cpp similarity index 100% rename from src/graphics/niche/Drivers/EInk/SSD16XX.cpp rename to src/graphics/eink/Drivers/SSD16XX.cpp diff --git a/src/graphics/niche/Drivers/EInk/SSD16XX.h b/src/graphics/eink/Drivers/SSD16XX.h similarity index 100% rename from src/graphics/niche/Drivers/EInk/SSD16XX.h rename to src/graphics/eink/Drivers/SSD16XX.h diff --git a/src/graphics/niche/Drivers/EInk/UC8175.cpp b/src/graphics/eink/Drivers/UC8175.cpp similarity index 100% rename from src/graphics/niche/Drivers/EInk/UC8175.cpp rename to src/graphics/eink/Drivers/UC8175.cpp diff --git a/src/graphics/niche/Drivers/EInk/UC8175.h b/src/graphics/eink/Drivers/UC8175.h similarity index 100% rename from src/graphics/niche/Drivers/EInk/UC8175.h rename to src/graphics/eink/Drivers/UC8175.h diff --git a/src/graphics/niche/Drivers/EInk/ZJY122250_0213BAAMFGN.cpp b/src/graphics/eink/Drivers/ZJY122250_0213BAAMFGN.cpp similarity index 100% rename from src/graphics/niche/Drivers/EInk/ZJY122250_0213BAAMFGN.cpp rename to src/graphics/eink/Drivers/ZJY122250_0213BAAMFGN.cpp diff --git a/src/graphics/niche/Drivers/EInk/ZJY122250_0213BAAMFGN.h b/src/graphics/eink/Drivers/ZJY122250_0213BAAMFGN.h similarity index 100% rename from src/graphics/niche/Drivers/EInk/ZJY122250_0213BAAMFGN.h rename to src/graphics/eink/Drivers/ZJY122250_0213BAAMFGN.h diff --git a/src/graphics/niche/Drivers/EInk/ZJY128296_029EAAMFGN.cpp b/src/graphics/eink/Drivers/ZJY128296_029EAAMFGN.cpp similarity index 100% rename from src/graphics/niche/Drivers/EInk/ZJY128296_029EAAMFGN.cpp rename to src/graphics/eink/Drivers/ZJY128296_029EAAMFGN.cpp diff --git a/src/graphics/niche/Drivers/EInk/ZJY128296_029EAAMFGN.h b/src/graphics/eink/Drivers/ZJY128296_029EAAMFGN.h similarity index 100% rename from src/graphics/niche/Drivers/EInk/ZJY128296_029EAAMFGN.h rename to src/graphics/eink/Drivers/ZJY128296_029EAAMFGN.h diff --git a/src/graphics/niche/Drivers/EInk/ZJY200200_0154DAAMFGN.h b/src/graphics/eink/Drivers/ZJY200200_0154DAAMFGN.h similarity index 100% rename from src/graphics/niche/Drivers/EInk/ZJY200200_0154DAAMFGN.h rename to src/graphics/eink/Drivers/ZJY200200_0154DAAMFGN.h diff --git a/src/graphics/eink/Panels/DEPG0213BNS800.h b/src/graphics/eink/Panels/DEPG0213BNS800.h new file mode 100644 index 000000000..9ea40b2fb --- /dev/null +++ b/src/graphics/eink/Panels/DEPG0213BNS800.h @@ -0,0 +1,25 @@ +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "./PanelProfile.h" +#include "graphics/eink/Drivers/DEPG0213BNS800.h" + +namespace NicheGraphics::Panels +{ +class DEPG0213BNS800 : public PanelProfile +{ + public: + NicheGraphics::Drivers::EInk *create() override + { + prePowerOn(); + SPIClass *spi = beginSpi(); + auto *drv = new NicheGraphics::Drivers::DEPG0213BNS800(); + drv->begin(spi, pinDC(), pinCS(), pinBusy(), pinReset()); + return drv; + } + uint8_t rotation() const override { return 3; } +}; +} // namespace NicheGraphics::Panels + +#endif diff --git a/src/graphics/eink/Panels/DEPG0290BNS800.h b/src/graphics/eink/Panels/DEPG0290BNS800.h new file mode 100644 index 000000000..3ad4e0ac3 --- /dev/null +++ b/src/graphics/eink/Panels/DEPG0290BNS800.h @@ -0,0 +1,25 @@ +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "./PanelProfile.h" +#include "graphics/eink/Drivers/DEPG0290BNS800.h" + +namespace NicheGraphics::Panels +{ +class DEPG0290BNS800 : public PanelProfile +{ + public: + NicheGraphics::Drivers::EInk *create() override + { + prePowerOn(); + SPIClass *spi = beginSpi(); + auto *drv = new NicheGraphics::Drivers::DEPG0290BNS800(); + drv->begin(spi, pinDC(), pinCS(), pinBusy(), pinReset()); + return drv; + } + uint8_t rotation() const override { return 1; } +}; +} // namespace NicheGraphics::Panels + +#endif diff --git a/src/graphics/eink/Panels/E0213A367.h b/src/graphics/eink/Panels/E0213A367.h new file mode 100644 index 000000000..6637063c4 --- /dev/null +++ b/src/graphics/eink/Panels/E0213A367.h @@ -0,0 +1,25 @@ +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "./PanelProfile.h" +#include "graphics/eink/Drivers/E0213A367.h" + +namespace NicheGraphics::Panels +{ +class E0213A367 : public PanelProfile +{ + public: + NicheGraphics::Drivers::EInk *create() override + { + prePowerOn(); + SPIClass *spi = beginSpi(); + auto *drv = new NicheGraphics::Drivers::E0213A367(); + drv->begin(spi, pinDC(), pinCS(), pinBusy(), pinReset()); + return drv; + } + uint8_t rotation() const override { return 3; } +}; +} // namespace NicheGraphics::Panels + +#endif diff --git a/src/graphics/eink/Panels/GDEH0122T61.h b/src/graphics/eink/Panels/GDEH0122T61.h new file mode 100644 index 000000000..8a09f30dd --- /dev/null +++ b/src/graphics/eink/Panels/GDEH0122T61.h @@ -0,0 +1,24 @@ +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "./PanelProfile.h" +#include "graphics/eink/Drivers/GDEH0122T61.h" + +namespace NicheGraphics::Panels +{ +class GDEH0122T61 : public PanelProfile +{ + public: + NicheGraphics::Drivers::EInk *create() override + { + prePowerOn(); + SPIClass *spi = beginSpi(); + auto *drv = new NicheGraphics::Drivers::GDEH0122T61(); + drv->begin(spi, pinDC(), pinCS(), pinBusy(), pinReset()); + return drv; + } +}; +} // namespace NicheGraphics::Panels + +#endif diff --git a/src/graphics/eink/Panels/GDEQ031T10.h b/src/graphics/eink/Panels/GDEQ031T10.h new file mode 100644 index 000000000..f1d0dfbe7 --- /dev/null +++ b/src/graphics/eink/Panels/GDEQ031T10.h @@ -0,0 +1,24 @@ +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "./PanelProfile.h" +#include "graphics/eink/Drivers/GDEQ031T10.h" + +namespace NicheGraphics::Panels +{ +class GDEQ031T10 : public PanelProfile +{ + public: + NicheGraphics::Drivers::EInk *create() override + { + prePowerOn(); + SPIClass *spi = beginSpi(); + auto *drv = new NicheGraphics::Drivers::GDEQ031T10(); + drv->begin(spi, pinDC(), pinCS(), pinBusy(), pinReset()); + return drv; + } +}; +} // namespace NicheGraphics::Panels + +#endif diff --git a/src/graphics/eink/Panels/GDEW0102T4.h b/src/graphics/eink/Panels/GDEW0102T4.h new file mode 100644 index 000000000..68eb62d2c --- /dev/null +++ b/src/graphics/eink/Panels/GDEW0102T4.h @@ -0,0 +1,25 @@ +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "./PanelProfile.h" +#include "graphics/eink/Drivers/GDEW0102T4.h" + +namespace NicheGraphics::Panels +{ +class GDEW0102T4 : public PanelProfile +{ + public: + NicheGraphics::Drivers::EInk *create() override + { + prePowerOn(); + SPIClass *spi = beginSpi(); + auto *drv = new NicheGraphics::Drivers::GDEW0102T4(); + drv->begin(spi, pinDC(), pinCS(), pinBusy(), pinReset()); + return drv; + } + uint8_t rotation() const override { return 3; } +}; +} // namespace NicheGraphics::Panels + +#endif diff --git a/src/graphics/eink/Panels/GDEY0154D67.h b/src/graphics/eink/Panels/GDEY0154D67.h new file mode 100644 index 000000000..940bf7c8e --- /dev/null +++ b/src/graphics/eink/Panels/GDEY0154D67.h @@ -0,0 +1,24 @@ +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "./PanelProfile.h" +#include "graphics/eink/Drivers/GDEY0154D67.h" + +namespace NicheGraphics::Panels +{ +class GDEY0154D67 : public PanelProfile +{ + public: + NicheGraphics::Drivers::EInk *create() override + { + prePowerOn(); + SPIClass *spi = beginSpi(); + auto *drv = new NicheGraphics::Drivers::GDEY0154D67(); + drv->begin(spi, pinDC(), pinCS(), pinBusy(), pinReset()); + return drv; + } +}; +} // namespace NicheGraphics::Panels + +#endif diff --git a/src/graphics/eink/Panels/GDEY0213B74.h b/src/graphics/eink/Panels/GDEY0213B74.h new file mode 100644 index 000000000..f743d477d --- /dev/null +++ b/src/graphics/eink/Panels/GDEY0213B74.h @@ -0,0 +1,25 @@ +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "./PanelProfile.h" +#include "graphics/eink/Drivers/GDEY0213B74.h" + +namespace NicheGraphics::Panels +{ +class GDEY0213B74 : public PanelProfile +{ + public: + NicheGraphics::Drivers::EInk *create() override + { + prePowerOn(); + SPIClass *spi = beginSpi(); + auto *drv = new NicheGraphics::Drivers::GDEY0213B74(); + drv->begin(spi, pinDC(), pinCS(), pinBusy(), pinReset()); + return drv; + } + uint8_t rotation() const override { return 3; } +}; +} // namespace NicheGraphics::Panels + +#endif diff --git a/src/graphics/eink/Panels/GDEY029T94.h b/src/graphics/eink/Panels/GDEY029T94.h new file mode 100644 index 000000000..18cad37b2 --- /dev/null +++ b/src/graphics/eink/Panels/GDEY029T94.h @@ -0,0 +1,25 @@ +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "./PanelProfile.h" +#include "graphics/eink/Drivers/GDEY029T94.h" + +namespace NicheGraphics::Panels +{ +class GDEY029T94 : public PanelProfile +{ + public: + NicheGraphics::Drivers::EInk *create() override + { + prePowerOn(); + SPIClass *spi = beginSpi(); + auto *drv = new NicheGraphics::Drivers::GDEY029T94(); + drv->begin(spi, pinDC(), pinCS(), pinBusy(), pinReset()); + return drv; + } + uint8_t rotation() const override { return 1; } +}; +} // namespace NicheGraphics::Panels + +#endif diff --git a/src/graphics/eink/Panels/GDEY042T81.h b/src/graphics/eink/Panels/GDEY042T81.h new file mode 100644 index 000000000..bba0253f8 --- /dev/null +++ b/src/graphics/eink/Panels/GDEY042T81.h @@ -0,0 +1,24 @@ +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "./PanelProfile.h" +#include "graphics/eink/Drivers/GDEY042T81.h" + +namespace NicheGraphics::Panels +{ +class GDEY042T81 : public PanelProfile +{ + public: + NicheGraphics::Drivers::EInk *create() override + { + prePowerOn(); + SPIClass *spi = beginSpi(); + auto *drv = new NicheGraphics::Drivers::GDEY042T81(); + drv->begin(spi, pinDC(), pinCS(), pinBusy(), pinReset()); + return drv; + } +}; +} // namespace NicheGraphics::Panels + +#endif diff --git a/src/graphics/eink/Panels/GDEY0579T93.h b/src/graphics/eink/Panels/GDEY0579T93.h new file mode 100644 index 000000000..5c3d98f55 --- /dev/null +++ b/src/graphics/eink/Panels/GDEY0579T93.h @@ -0,0 +1,24 @@ +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "./PanelProfile.h" +#include "graphics/eink/Drivers/GDEY0579T93.h" + +namespace NicheGraphics::Panels +{ +class GDEY0579T93 : public PanelProfile +{ + public: + NicheGraphics::Drivers::EInk *create() override + { + prePowerOn(); + SPIClass *spi = beginSpi(); + auto *drv = new NicheGraphics::Drivers::GDEY0579T93(); + drv->begin(spi, pinDC(), pinCS(), pinBusy(), pinReset()); + return drv; + } +}; +} // namespace NicheGraphics::Panels + +#endif diff --git a/src/graphics/eink/Panels/HINK_E042A87.h b/src/graphics/eink/Panels/HINK_E042A87.h new file mode 100644 index 000000000..2b6ae446d --- /dev/null +++ b/src/graphics/eink/Panels/HINK_E042A87.h @@ -0,0 +1,24 @@ +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "./PanelProfile.h" +#include "graphics/eink/Drivers/HINK_E042A87.h" + +namespace NicheGraphics::Panels +{ +class HINK_E042A87 : public PanelProfile +{ + public: + NicheGraphics::Drivers::EInk *create() override + { + prePowerOn(); + SPIClass *spi = beginSpi(); + auto *drv = new NicheGraphics::Drivers::HINK_E042A87(); + drv->begin(spi, pinDC(), pinCS(), pinBusy(), pinReset()); + return drv; + } +}; +} // namespace NicheGraphics::Panels + +#endif diff --git a/src/graphics/eink/Panels/LCMEN213EFC1.h b/src/graphics/eink/Panels/LCMEN213EFC1.h new file mode 100644 index 000000000..7a2dc6e2a --- /dev/null +++ b/src/graphics/eink/Panels/LCMEN213EFC1.h @@ -0,0 +1,25 @@ +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "./PanelProfile.h" +#include "graphics/eink/Drivers/LCMEN2R13EFC1.h" + +namespace NicheGraphics::Panels +{ +class LCMEN213EFC1 : public PanelProfile +{ + public: + NicheGraphics::Drivers::EInk *create() override + { + prePowerOn(); + SPIClass *spi = beginSpi(); + auto *drv = new NicheGraphics::Drivers::LCMEN213EFC1(); + drv->begin(spi, pinDC(), pinCS(), pinBusy(), pinReset()); + return drv; + } + uint8_t rotation() const override { return 3; } +}; +} // namespace NicheGraphics::Panels + +#endif diff --git a/src/graphics/eink/Panels/LCMEN2R13ECC1.h b/src/graphics/eink/Panels/LCMEN2R13ECC1.h new file mode 100644 index 000000000..18c6adcf0 --- /dev/null +++ b/src/graphics/eink/Panels/LCMEN2R13ECC1.h @@ -0,0 +1,25 @@ +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "./PanelProfile.h" +#include "graphics/eink/Drivers/LCMEN2R13ECC1.h" + +namespace NicheGraphics::Panels +{ +class LCMEN2R13ECC1 : public PanelProfile +{ + public: + NicheGraphics::Drivers::EInk *create() override + { + prePowerOn(); + SPIClass *spi = beginSpi(); + auto *drv = new NicheGraphics::Drivers::LCMEN2R13ECC1(); + drv->begin(spi, pinDC(), pinCS(), pinBusy(), pinReset()); + return drv; + } + uint8_t rotation() const override { return 3; } +}; +} // namespace NicheGraphics::Panels + +#endif diff --git a/src/graphics/eink/Panels/PanelProfile.cpp b/src/graphics/eink/Panels/PanelProfile.cpp new file mode 100644 index 000000000..3ec35e34c --- /dev/null +++ b/src/graphics/eink/Panels/PanelProfile.cpp @@ -0,0 +1,70 @@ +#include "./PanelProfile.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +using namespace NicheGraphics::Panels; + +SPIClass *PanelProfile::beginSpi() +{ +#if defined(ARCH_ESP32) + auto *spi = new SPIClass(HSPI); +#if defined(PIN_EINK_SCLK) && defined(PIN_EINK_MOSI) && defined(PIN_EINK_CS) + spi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); +#else + spi->begin(); +#endif + return spi; +#elif defined(ARCH_NRF52) + SPI1.begin(); + return &SPI1; +#else + return &SPI; +#endif +} + +int8_t PanelProfile::backlightPin() const +{ +#ifdef PIN_EINK_EN + return PIN_EINK_EN; +#else + return -1; +#endif +} + +uint8_t PanelProfile::pinDC() const +{ +#ifdef PIN_EINK_DC + return PIN_EINK_DC; +#else + return 0xFF; +#endif +} + +uint8_t PanelProfile::pinCS() const +{ +#ifdef PIN_EINK_CS + return PIN_EINK_CS; +#else + return 0xFF; +#endif +} + +uint8_t PanelProfile::pinBusy() const +{ +#ifdef PIN_EINK_BUSY + return PIN_EINK_BUSY; +#else + return 0xFF; +#endif +} + +int8_t PanelProfile::pinReset() const +{ +#ifdef PIN_EINK_RES + return PIN_EINK_RES; +#else + return -1; +#endif +} + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/src/graphics/eink/Panels/PanelProfile.h b/src/graphics/eink/Panels/PanelProfile.h new file mode 100644 index 000000000..81d599efa --- /dev/null +++ b/src/graphics/eink/Panels/PanelProfile.h @@ -0,0 +1,52 @@ +/* + +Panel profile: single source of truth for how a specific E-Ink panel is wired and brought up. +Variants subclass a per-panel profile only to override differences (SPI bus, pins, rotation, backlight pin, +power-up quirks). The profile's create() constructs and begins the underlying +NicheGraphics::Drivers::EInk subclass exactly once. + +*/ + +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "configuration.h" + +#include "graphics/eink/Drivers/EInk.h" + +#include + +namespace NicheGraphics::Panels +{ + +class PanelProfile +{ + public: + virtual ~PanelProfile() = default; + + // Produce and begin() the underlying E-Ink driver. Called once per boot. + virtual NicheGraphics::Drivers::EInk *create() = 0; + + // Public, variant-overridable metadata + virtual uint8_t rotation() const { return 0; } + virtual int8_t backlightPin() const; + + protected: + // Default SPI bring-up. ESP32 uses HSPI with PIN_EINK_SCLK/MOSI; nRF52 uses SPI1 (pins from variant.h). + // Variants override when using a non-default bus or pin set. + virtual SPIClass *beginSpi(); + + // Pin defaults read the variant's PIN_EINK_* macros. Variants override if mapping differs. + virtual uint8_t pinDC() const; + virtual uint8_t pinCS() const; + virtual uint8_t pinBusy() const; + virtual int8_t pinReset() const; + + // Hook for variants that need to raise a power rail / observe settle time before SPI traffic. + virtual void prePowerOn() {} +}; + +} // namespace NicheGraphics::Panels + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/src/graphics/eink/Panels/T5Epaper.h b/src/graphics/eink/Panels/T5Epaper.h new file mode 100644 index 000000000..05091f3f1 --- /dev/null +++ b/src/graphics/eink/Panels/T5Epaper.h @@ -0,0 +1,38 @@ +/* + +Panel profile base for the LILYGO T5 ePaper Pro family (ED047TC1, 960x540, 8-bit parallel via FastEPD). + +V1 and V2 use different FastEPD panel IDs and V2 also needs GPIO-expander pins raised. +Variants subclass to provide a Drivers::EInkParallel subclass that implements +postPanelInit() if needed. + +*/ + +#pragma once + +#include "configuration.h" + +#if defined(MESHTASTIC_INCLUDE_NICHE_GRAPHICS) && defined(ARCH_ESP32) && defined(NICHE_HAS_FASTEPD) + +#include "./PanelProfile.h" +#include "graphics/eink/Drivers/EInkParallel.h" + +namespace NicheGraphics::Panels +{ +class T5EpaperPanel : public PanelProfile +{ + public: + NicheGraphics::Drivers::EInk *create() override + { + auto *drv = makeDriver(); + drv->begin(nullptr, 0, 0, 0); // SPI args ignored + return drv; + } + + protected: + // Variant returns a Drivers::EInkParallel subclass configured for its specific panel/init. + virtual NicheGraphics::Drivers::EInkParallel *makeDriver() = 0; +}; +} // namespace NicheGraphics::Panels + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS && ARCH_ESP32 && NICHE_HAS_FASTEPD diff --git a/src/graphics/eink/Panels/ZJY122250_0213BAAMFGN.h b/src/graphics/eink/Panels/ZJY122250_0213BAAMFGN.h new file mode 100644 index 000000000..d84c4d7eb --- /dev/null +++ b/src/graphics/eink/Panels/ZJY122250_0213BAAMFGN.h @@ -0,0 +1,25 @@ +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "./PanelProfile.h" +#include "graphics/eink/Drivers/ZJY122250_0213BAAMFGN.h" + +namespace NicheGraphics::Panels +{ +class ZJY122250_0213BAAMFGN : public PanelProfile +{ + public: + NicheGraphics::Drivers::EInk *create() override + { + prePowerOn(); + SPIClass *spi = beginSpi(); + auto *drv = new NicheGraphics::Drivers::ZJY122250_0213BAAMFGN(); + drv->begin(spi, pinDC(), pinCS(), pinBusy(), pinReset()); + return drv; + } + uint8_t rotation() const override { return 1; } +}; +} // namespace NicheGraphics::Panels + +#endif diff --git a/src/graphics/niche/InkHUD/Applet.cpp b/src/graphics/niche/Applet.cpp similarity index 99% rename from src/graphics/niche/InkHUD/Applet.cpp rename to src/graphics/niche/Applet.cpp index 0a9cd3add..c017591bb 100644 --- a/src/graphics/niche/InkHUD/Applet.cpp +++ b/src/graphics/niche/Applet.cpp @@ -1,4 +1,4 @@ -#include "graphics/niche/InkHUD/Tile.h" +#include "graphics/niche/Tile.h" #include #ifdef MESHTASTIC_INCLUDE_INKHUD diff --git a/src/graphics/niche/InkHUD/Applet.h b/src/graphics/niche/Applet.h similarity index 99% rename from src/graphics/niche/InkHUD/Applet.h rename to src/graphics/niche/Applet.h index 3c14c2607..9fa36b194 100644 --- a/src/graphics/niche/InkHUD/Applet.h +++ b/src/graphics/niche/Applet.h @@ -23,7 +23,7 @@ #include "./InkHUD.h" #include "./Persistence.h" #include "./Tile.h" -#include "graphics/niche/Drivers/EInk/EInk.h" +#include "graphics/eink/Drivers/EInk.h" namespace NicheGraphics::InkHUD { diff --git a/src/graphics/niche/InkHUD/AppletFont.cpp b/src/graphics/niche/AppletFont.cpp similarity index 100% rename from src/graphics/niche/InkHUD/AppletFont.cpp rename to src/graphics/niche/AppletFont.cpp diff --git a/src/graphics/niche/InkHUD/AppletFont.h b/src/graphics/niche/AppletFont.h similarity index 100% rename from src/graphics/niche/InkHUD/AppletFont.h rename to src/graphics/niche/AppletFont.h diff --git a/src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.cpp b/src/graphics/niche/Applets/Bases/Map/MapApplet.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.cpp rename to src/graphics/niche/Applets/Bases/Map/MapApplet.cpp diff --git a/src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.h b/src/graphics/niche/Applets/Bases/Map/MapApplet.h similarity index 97% rename from src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.h rename to src/graphics/niche/Applets/Bases/Map/MapApplet.h index 11dfb39d9..c3b4c886a 100644 --- a/src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.h +++ b/src/graphics/niche/Applets/Bases/Map/MapApplet.h @@ -16,7 +16,7 @@ The base applet doesn't handle any events; this is left to the derived applets. #include "configuration.h" -#include "graphics/niche/InkHUD/Applet.h" +#include "graphics/niche/Applet.h" #include "MeshModule.h" #include "gps/GeoCoord.h" diff --git a/src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.cpp b/src/graphics/niche/Applets/Bases/NodeList/NodeListApplet.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.cpp rename to src/graphics/niche/Applets/Bases/NodeList/NodeListApplet.cpp diff --git a/src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.h b/src/graphics/niche/Applets/Bases/NodeList/NodeListApplet.h similarity index 98% rename from src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.h rename to src/graphics/niche/Applets/Bases/NodeList/NodeListApplet.h index baee3f0f4..d73b3ab3a 100644 --- a/src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.h +++ b/src/graphics/niche/Applets/Bases/NodeList/NodeListApplet.h @@ -21,7 +21,7 @@ Used by the "Recents" and "Heard" applets. Possibly more in future? #include "configuration.h" -#include "graphics/niche/InkHUD/Applet.h" +#include "graphics/niche/Applet.h" #include "main.h" diff --git a/src/graphics/niche/InkHUD/Applets/Examples/BasicExample/BasicExampleApplet.cpp b/src/graphics/niche/Applets/Examples/BasicExample/BasicExampleApplet.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Applets/Examples/BasicExample/BasicExampleApplet.cpp rename to src/graphics/niche/Applets/Examples/BasicExample/BasicExampleApplet.cpp diff --git a/src/graphics/niche/InkHUD/Applets/Examples/BasicExample/BasicExampleApplet.h b/src/graphics/niche/Applets/Examples/BasicExample/BasicExampleApplet.h similarity index 93% rename from src/graphics/niche/InkHUD/Applets/Examples/BasicExample/BasicExampleApplet.h rename to src/graphics/niche/Applets/Examples/BasicExample/BasicExampleApplet.h index a36f6e8d5..1ebb6a5a5 100644 --- a/src/graphics/niche/InkHUD/Applets/Examples/BasicExample/BasicExampleApplet.h +++ b/src/graphics/niche/Applets/Examples/BasicExample/BasicExampleApplet.h @@ -17,7 +17,7 @@ In variants//nicheGraphics.h: #include "configuration.h" -#include "graphics/niche/InkHUD/Applet.h" +#include "graphics/niche/Applet.h" namespace NicheGraphics::InkHUD { diff --git a/src/graphics/niche/InkHUD/Applets/Examples/NewMsgExample/NewMsgExampleApplet.cpp b/src/graphics/niche/Applets/Examples/NewMsgExample/NewMsgExampleApplet.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Applets/Examples/NewMsgExample/NewMsgExampleApplet.cpp rename to src/graphics/niche/Applets/Examples/NewMsgExample/NewMsgExampleApplet.cpp diff --git a/src/graphics/niche/InkHUD/Applets/Examples/NewMsgExample/NewMsgExampleApplet.h b/src/graphics/niche/Applets/Examples/NewMsgExample/NewMsgExampleApplet.h similarity index 97% rename from src/graphics/niche/InkHUD/Applets/Examples/NewMsgExample/NewMsgExampleApplet.h rename to src/graphics/niche/Applets/Examples/NewMsgExample/NewMsgExampleApplet.h index 599f08a7a..04aafc7f4 100644 --- a/src/graphics/niche/InkHUD/Applets/Examples/NewMsgExample/NewMsgExampleApplet.h +++ b/src/graphics/niche/Applets/Examples/NewMsgExample/NewMsgExampleApplet.h @@ -20,7 +20,7 @@ In variants//nicheGraphics.h: #include "configuration.h" -#include "graphics/niche/InkHUD/Applet.h" +#include "graphics/niche/Applet.h" #include "mesh/SinglePortModule.h" diff --git a/src/graphics/niche/InkHUD/Applets/Examples/UserAppletInputExample/UserAppletInputExample.cpp b/src/graphics/niche/Applets/Examples/UserAppletInputExample/UserAppletInputExample.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Applets/Examples/UserAppletInputExample/UserAppletInputExample.cpp rename to src/graphics/niche/Applets/Examples/UserAppletInputExample/UserAppletInputExample.cpp diff --git a/src/graphics/niche/InkHUD/Applets/Examples/UserAppletInputExample/UserAppletInputExample.h b/src/graphics/niche/Applets/Examples/UserAppletInputExample/UserAppletInputExample.h similarity index 94% rename from src/graphics/niche/InkHUD/Applets/Examples/UserAppletInputExample/UserAppletInputExample.h rename to src/graphics/niche/Applets/Examples/UserAppletInputExample/UserAppletInputExample.h index a99dec00c..4ea8aa530 100644 --- a/src/graphics/niche/InkHUD/Applets/Examples/UserAppletInputExample/UserAppletInputExample.h +++ b/src/graphics/niche/Applets/Examples/UserAppletInputExample/UserAppletInputExample.h @@ -4,7 +4,7 @@ #include "configuration.h" -#include "graphics/niche/InkHUD/Applet.h" +#include "graphics/niche/Applet.h" namespace NicheGraphics::InkHUD { diff --git a/src/graphics/niche/InkHUD/Applets/System/AlignStick/AlignStickApplet.cpp b/src/graphics/niche/Applets/System/AlignStick/AlignStickApplet.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Applets/System/AlignStick/AlignStickApplet.cpp rename to src/graphics/niche/Applets/System/AlignStick/AlignStickApplet.cpp diff --git a/src/graphics/niche/InkHUD/Applets/System/AlignStick/AlignStickApplet.h b/src/graphics/niche/Applets/System/AlignStick/AlignStickApplet.h similarity index 95% rename from src/graphics/niche/InkHUD/Applets/System/AlignStick/AlignStickApplet.h rename to src/graphics/niche/Applets/System/AlignStick/AlignStickApplet.h index 7c8d00155..ea56f125f 100644 --- a/src/graphics/niche/InkHUD/Applets/System/AlignStick/AlignStickApplet.h +++ b/src/graphics/niche/Applets/System/AlignStick/AlignStickApplet.h @@ -13,7 +13,7 @@ and not aligned to the screen #include "configuration.h" -#include "graphics/niche/InkHUD/SystemApplet.h" +#include "graphics/niche/SystemApplet.h" namespace NicheGraphics::InkHUD { diff --git a/src/graphics/niche/InkHUD/Applets/System/BatteryIcon/BatteryIconApplet.cpp b/src/graphics/niche/Applets/System/BatteryIcon/BatteryIconApplet.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Applets/System/BatteryIcon/BatteryIconApplet.cpp rename to src/graphics/niche/Applets/System/BatteryIcon/BatteryIconApplet.cpp diff --git a/src/graphics/niche/InkHUD/Applets/System/BatteryIcon/BatteryIconApplet.h b/src/graphics/niche/Applets/System/BatteryIcon/BatteryIconApplet.h similarity index 95% rename from src/graphics/niche/InkHUD/Applets/System/BatteryIcon/BatteryIconApplet.h rename to src/graphics/niche/Applets/System/BatteryIcon/BatteryIconApplet.h index ceaf88d7f..9c3d2b4d0 100644 --- a/src/graphics/niche/InkHUD/Applets/System/BatteryIcon/BatteryIconApplet.h +++ b/src/graphics/niche/Applets/System/BatteryIcon/BatteryIconApplet.h @@ -11,7 +11,7 @@ It should be optional, enabled by the on-screen menu #include "configuration.h" -#include "graphics/niche/InkHUD/SystemApplet.h" +#include "graphics/niche/SystemApplet.h" #include "PowerStatus.h" diff --git a/src/graphics/niche/InkHUD/Applets/System/Keyboard/KeyboardApplet.cpp b/src/graphics/niche/Applets/System/Keyboard/KeyboardApplet.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Applets/System/Keyboard/KeyboardApplet.cpp rename to src/graphics/niche/Applets/System/Keyboard/KeyboardApplet.cpp diff --git a/src/graphics/niche/InkHUD/Applets/System/Keyboard/KeyboardApplet.h b/src/graphics/niche/Applets/System/Keyboard/KeyboardApplet.h similarity index 95% rename from src/graphics/niche/InkHUD/Applets/System/Keyboard/KeyboardApplet.h rename to src/graphics/niche/Applets/System/Keyboard/KeyboardApplet.h index 0ae181a2c..e63af2e6f 100644 --- a/src/graphics/niche/InkHUD/Applets/System/Keyboard/KeyboardApplet.h +++ b/src/graphics/niche/Applets/System/Keyboard/KeyboardApplet.h @@ -9,8 +9,8 @@ System Applet to render an on-screen keyboard #pragma once #include "configuration.h" -#include "graphics/niche/InkHUD/InkHUD.h" -#include "graphics/niche/InkHUD/SystemApplet.h" +#include "graphics/niche/InkHUD.h" +#include "graphics/niche/SystemApplet.h" #include namespace NicheGraphics::InkHUD { diff --git a/src/graphics/niche/InkHUD/Applets/System/Logo/LogoApplet.cpp b/src/graphics/niche/Applets/System/Logo/LogoApplet.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Applets/System/Logo/LogoApplet.cpp rename to src/graphics/niche/Applets/System/Logo/LogoApplet.cpp diff --git a/src/graphics/niche/InkHUD/Applets/System/Logo/LogoApplet.h b/src/graphics/niche/Applets/System/Logo/LogoApplet.h similarity index 94% rename from src/graphics/niche/InkHUD/Applets/System/Logo/LogoApplet.h rename to src/graphics/niche/Applets/System/Logo/LogoApplet.h index d70dcc7b2..648851da7 100644 --- a/src/graphics/niche/InkHUD/Applets/System/Logo/LogoApplet.h +++ b/src/graphics/niche/Applets/System/Logo/LogoApplet.h @@ -12,7 +12,7 @@ #include "configuration.h" #include "concurrency/OSThread.h" -#include "graphics/niche/InkHUD/SystemApplet.h" +#include "graphics/niche/SystemApplet.h" namespace NicheGraphics::InkHUD { diff --git a/src/graphics/niche/InkHUD/Applets/System/Menu/MenuAction.h b/src/graphics/niche/Applets/System/Menu/MenuAction.h similarity index 100% rename from src/graphics/niche/InkHUD/Applets/System/Menu/MenuAction.h rename to src/graphics/niche/Applets/System/Menu/MenuAction.h diff --git a/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.cpp b/src/graphics/niche/Applets/System/Menu/MenuApplet.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.cpp rename to src/graphics/niche/Applets/System/Menu/MenuApplet.cpp diff --git a/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.h b/src/graphics/niche/Applets/System/Menu/MenuApplet.h similarity index 95% rename from src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.h rename to src/graphics/niche/Applets/System/Menu/MenuApplet.h index b5c1c86e4..8c3603afd 100644 --- a/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.h +++ b/src/graphics/niche/Applets/System/Menu/MenuApplet.h @@ -2,11 +2,11 @@ #include "configuration.h" -#include "graphics/niche/Drivers/Backlight/LatchingBacklight.h" -#include "graphics/niche/InkHUD/InkHUD.h" -#include "graphics/niche/InkHUD/Persistence.h" -#include "graphics/niche/InkHUD/SystemApplet.h" -#include "graphics/niche/Utils/CannedMessageStore.h" +#include "graphics/eink/Backlight/LatchingBacklight.h" +#include "graphics/niche/CannedMessageStore.h" +#include "graphics/niche/InkHUD.h" +#include "graphics/niche/Persistence.h" +#include "graphics/niche/SystemApplet.h" #include "./MenuItem.h" #include "./MenuPage.h" diff --git a/src/graphics/niche/InkHUD/Applets/System/Menu/MenuItem.h b/src/graphics/niche/Applets/System/Menu/MenuItem.h similarity index 100% rename from src/graphics/niche/InkHUD/Applets/System/Menu/MenuItem.h rename to src/graphics/niche/Applets/System/Menu/MenuItem.h diff --git a/src/graphics/niche/InkHUD/Applets/System/Menu/MenuPage.h b/src/graphics/niche/Applets/System/Menu/MenuPage.h similarity index 100% rename from src/graphics/niche/InkHUD/Applets/System/Menu/MenuPage.h rename to src/graphics/niche/Applets/System/Menu/MenuPage.h diff --git a/src/graphics/niche/InkHUD/Applets/System/Notification/Notification.h b/src/graphics/niche/Applets/System/Notification/Notification.h similarity index 100% rename from src/graphics/niche/InkHUD/Applets/System/Notification/Notification.h rename to src/graphics/niche/Applets/System/Notification/Notification.h diff --git a/src/graphics/niche/InkHUD/Applets/System/Notification/NotificationApplet.cpp b/src/graphics/niche/Applets/System/Notification/NotificationApplet.cpp similarity index 99% rename from src/graphics/niche/InkHUD/Applets/System/Notification/NotificationApplet.cpp rename to src/graphics/niche/Applets/System/Notification/NotificationApplet.cpp index 6c8069c8b..5626de2f1 100644 --- a/src/graphics/niche/InkHUD/Applets/System/Notification/NotificationApplet.cpp +++ b/src/graphics/niche/Applets/System/Notification/NotificationApplet.cpp @@ -3,7 +3,7 @@ #include "./NotificationApplet.h" #include "./Notification.h" -#include "graphics/niche/InkHUD/Persistence.h" +#include "graphics/niche/Persistence.h" #include "meshUtils.h" #include "modules/TextMessageModule.h" diff --git a/src/graphics/niche/InkHUD/Applets/System/Notification/NotificationApplet.h b/src/graphics/niche/Applets/System/Notification/NotificationApplet.h similarity index 97% rename from src/graphics/niche/InkHUD/Applets/System/Notification/NotificationApplet.h rename to src/graphics/niche/Applets/System/Notification/NotificationApplet.h index d398a36f3..524d9c2b0 100644 --- a/src/graphics/niche/InkHUD/Applets/System/Notification/NotificationApplet.h +++ b/src/graphics/niche/Applets/System/Notification/NotificationApplet.h @@ -16,7 +16,7 @@ Feature should be optional; enable disable via on-screen menu #include "concurrency/OSThread.h" -#include "graphics/niche/InkHUD/SystemApplet.h" +#include "graphics/niche/SystemApplet.h" namespace NicheGraphics::InkHUD { diff --git a/src/graphics/niche/InkHUD/Applets/System/Pairing/PairingApplet.cpp b/src/graphics/niche/Applets/System/Pairing/PairingApplet.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Applets/System/Pairing/PairingApplet.cpp rename to src/graphics/niche/Applets/System/Pairing/PairingApplet.cpp diff --git a/src/graphics/niche/InkHUD/Applets/System/Pairing/PairingApplet.h b/src/graphics/niche/Applets/System/Pairing/PairingApplet.h similarity index 94% rename from src/graphics/niche/InkHUD/Applets/System/Pairing/PairingApplet.h rename to src/graphics/niche/Applets/System/Pairing/PairingApplet.h index 4c2e95321..b89fe62bb 100644 --- a/src/graphics/niche/InkHUD/Applets/System/Pairing/PairingApplet.h +++ b/src/graphics/niche/Applets/System/Pairing/PairingApplet.h @@ -10,7 +10,7 @@ #include "configuration.h" -#include "graphics/niche/InkHUD/SystemApplet.h" +#include "graphics/niche/SystemApplet.h" #include "main.h" diff --git a/src/graphics/niche/InkHUD/Applets/System/Placeholder/PlaceholderApplet.cpp b/src/graphics/niche/Applets/System/Placeholder/PlaceholderApplet.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Applets/System/Placeholder/PlaceholderApplet.cpp rename to src/graphics/niche/Applets/System/Placeholder/PlaceholderApplet.cpp diff --git a/src/graphics/niche/InkHUD/Applets/System/Placeholder/PlaceholderApplet.h b/src/graphics/niche/Applets/System/Placeholder/PlaceholderApplet.h similarity index 92% rename from src/graphics/niche/InkHUD/Applets/System/Placeholder/PlaceholderApplet.h rename to src/graphics/niche/Applets/System/Placeholder/PlaceholderApplet.h index fa40913e0..b22529680 100644 --- a/src/graphics/niche/InkHUD/Applets/System/Placeholder/PlaceholderApplet.h +++ b/src/graphics/niche/Applets/System/Placeholder/PlaceholderApplet.h @@ -9,7 +9,7 @@ Fills the area with diagonal lines #include "configuration.h" -#include "graphics/niche/InkHUD/SystemApplet.h" +#include "graphics/niche/SystemApplet.h" namespace NicheGraphics::InkHUD { diff --git a/src/graphics/niche/InkHUD/Applets/System/Tips/TipsApplet.cpp b/src/graphics/niche/Applets/System/Tips/TipsApplet.cpp similarity index 99% rename from src/graphics/niche/InkHUD/Applets/System/Tips/TipsApplet.cpp rename to src/graphics/niche/Applets/System/Tips/TipsApplet.cpp index a45e8d9b5..e65ee86b6 100644 --- a/src/graphics/niche/InkHUD/Applets/System/Tips/TipsApplet.cpp +++ b/src/graphics/niche/Applets/System/Tips/TipsApplet.cpp @@ -2,7 +2,7 @@ #include "./TipsApplet.h" -#include "graphics/niche/InkHUD/Persistence.h" +#include "graphics/niche/Persistence.h" #include "main.h" diff --git a/src/graphics/niche/InkHUD/Applets/System/Tips/TipsApplet.h b/src/graphics/niche/Applets/System/Tips/TipsApplet.h similarity index 95% rename from src/graphics/niche/InkHUD/Applets/System/Tips/TipsApplet.h rename to src/graphics/niche/Applets/System/Tips/TipsApplet.h index 2e81d678b..2042d5ac4 100644 --- a/src/graphics/niche/InkHUD/Applets/System/Tips/TipsApplet.h +++ b/src/graphics/niche/Applets/System/Tips/TipsApplet.h @@ -12,7 +12,7 @@ #include "configuration.h" -#include "graphics/niche/InkHUD/SystemApplet.h" +#include "graphics/niche/SystemApplet.h" namespace NicheGraphics::InkHUD { diff --git a/src/graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.cpp b/src/graphics/niche/Applets/User/AllMessage/AllMessageApplet.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.cpp rename to src/graphics/niche/Applets/User/AllMessage/AllMessageApplet.cpp diff --git a/src/graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h b/src/graphics/niche/Applets/User/AllMessage/AllMessageApplet.h similarity index 97% rename from src/graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h rename to src/graphics/niche/Applets/User/AllMessage/AllMessageApplet.h index 4aa97e4f1..e084f2b37 100644 --- a/src/graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h +++ b/src/graphics/niche/Applets/User/AllMessage/AllMessageApplet.h @@ -18,7 +18,7 @@ to know when a new message has arrived, and trigger the update. #include "configuration.h" -#include "graphics/niche/InkHUD/Applet.h" +#include "graphics/niche/Applet.h" #include "modules/TextMessageModule.h" diff --git a/src/graphics/niche/InkHUD/Applets/User/DM/DMApplet.cpp b/src/graphics/niche/Applets/User/DM/DMApplet.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Applets/User/DM/DMApplet.cpp rename to src/graphics/niche/Applets/User/DM/DMApplet.cpp diff --git a/src/graphics/niche/InkHUD/Applets/User/DM/DMApplet.h b/src/graphics/niche/Applets/User/DM/DMApplet.h similarity index 97% rename from src/graphics/niche/InkHUD/Applets/User/DM/DMApplet.h rename to src/graphics/niche/Applets/User/DM/DMApplet.h index 4eb0ec704..c667b17c0 100644 --- a/src/graphics/niche/InkHUD/Applets/User/DM/DMApplet.h +++ b/src/graphics/niche/Applets/User/DM/DMApplet.h @@ -18,7 +18,7 @@ to know when a new message has arrived, and trigger the update. #include "configuration.h" -#include "graphics/niche/InkHUD/Applet.h" +#include "graphics/niche/Applet.h" #include "modules/TextMessageModule.h" diff --git a/src/graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.cpp b/src/graphics/niche/Applets/User/FavoritesMap/FavoritesMapApplet.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.cpp rename to src/graphics/niche/Applets/User/FavoritesMap/FavoritesMapApplet.cpp diff --git a/src/graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h b/src/graphics/niche/Applets/User/FavoritesMap/FavoritesMapApplet.h similarity index 94% rename from src/graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h rename to src/graphics/niche/Applets/User/FavoritesMap/FavoritesMapApplet.h index da5fb0dc3..825af64a8 100644 --- a/src/graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h +++ b/src/graphics/niche/Applets/User/FavoritesMap/FavoritesMapApplet.h @@ -13,7 +13,7 @@ The favorite node which most recently sent a position will be labeled. #include "configuration.h" -#include "graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.h" +#include "graphics/niche/Applets/Bases/Map/MapApplet.h" #include "SinglePortModule.h" diff --git a/src/graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.cpp b/src/graphics/niche/Applets/User/Heard/HeardApplet.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.cpp rename to src/graphics/niche/Applets/User/Heard/HeardApplet.cpp diff --git a/src/graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h b/src/graphics/niche/Applets/User/Heard/HeardApplet.h similarity index 91% rename from src/graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h rename to src/graphics/niche/Applets/User/Heard/HeardApplet.h index 932b5a75e..a09ce573b 100644 --- a/src/graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h +++ b/src/graphics/niche/Applets/User/Heard/HeardApplet.h @@ -11,7 +11,7 @@ Most of the work is done by the InkHUD::NodeListApplet base class #include "configuration.h" -#include "graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.h" +#include "graphics/niche/Applets/Bases/NodeList/NodeListApplet.h" namespace NicheGraphics::InkHUD { diff --git a/src/graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.cpp b/src/graphics/niche/Applets/User/Positions/PositionsApplet.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.cpp rename to src/graphics/niche/Applets/User/Positions/PositionsApplet.cpp diff --git a/src/graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h b/src/graphics/niche/Applets/User/Positions/PositionsApplet.h similarity index 94% rename from src/graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h rename to src/graphics/niche/Applets/User/Positions/PositionsApplet.h index d0d3e5f07..3d31518c6 100644 --- a/src/graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h +++ b/src/graphics/niche/Applets/User/Positions/PositionsApplet.h @@ -13,7 +13,7 @@ The node which has most recently sent a position will be labeled. #include "configuration.h" -#include "graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.h" +#include "graphics/niche/Applets/Bases/Map/MapApplet.h" #include "SinglePortModule.h" diff --git a/src/graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.cpp b/src/graphics/niche/Applets/User/RecentsList/RecentsListApplet.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.cpp rename to src/graphics/niche/Applets/User/RecentsList/RecentsListApplet.cpp diff --git a/src/graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h b/src/graphics/niche/Applets/User/RecentsList/RecentsListApplet.h similarity index 95% rename from src/graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h rename to src/graphics/niche/Applets/User/RecentsList/RecentsListApplet.h index 74f5f3e57..0395570ff 100644 --- a/src/graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h +++ b/src/graphics/niche/Applets/User/RecentsList/RecentsListApplet.h @@ -13,7 +13,7 @@ Most of the work is done by the shared InkHUD::NodeListApplet base class #include "configuration.h" -#include "graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.h" +#include "graphics/niche/Applets/Bases/NodeList/NodeListApplet.h" namespace NicheGraphics::InkHUD { diff --git a/src/graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.cpp b/src/graphics/niche/Applets/User/ThreadedMessage/ThreadedMessageApplet.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.cpp rename to src/graphics/niche/Applets/User/ThreadedMessage/ThreadedMessageApplet.cpp diff --git a/src/graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.h b/src/graphics/niche/Applets/User/ThreadedMessage/ThreadedMessageApplet.h similarity index 94% rename from src/graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.h rename to src/graphics/niche/Applets/User/ThreadedMessage/ThreadedMessageApplet.h index 2cd2c4163..7a03b1885 100644 --- a/src/graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.h +++ b/src/graphics/niche/Applets/User/ThreadedMessage/ThreadedMessageApplet.h @@ -20,8 +20,8 @@ Suggest a max of two channel, to minimize fs usage? #include "configuration.h" -#include "graphics/niche/InkHUD/Applet.h" -#include "graphics/niche/InkHUD/MessageStore.h" +#include "graphics/niche/Applet.h" +#include "graphics/niche/MessageStore.h" #include "modules/TextMessageModule.h" diff --git a/src/graphics/niche/Utils/CannedMessageStore.cpp b/src/graphics/niche/CannedMessageStore.cpp similarity index 100% rename from src/graphics/niche/Utils/CannedMessageStore.cpp rename to src/graphics/niche/CannedMessageStore.cpp diff --git a/src/graphics/niche/Utils/CannedMessageStore.h b/src/graphics/niche/CannedMessageStore.h similarity index 100% rename from src/graphics/niche/Utils/CannedMessageStore.h rename to src/graphics/niche/CannedMessageStore.h diff --git a/src/graphics/niche/InkHUD/DisplayHealth.cpp b/src/graphics/niche/DisplayHealth.cpp similarity index 100% rename from src/graphics/niche/InkHUD/DisplayHealth.cpp rename to src/graphics/niche/DisplayHealth.cpp diff --git a/src/graphics/niche/InkHUD/DisplayHealth.h b/src/graphics/niche/DisplayHealth.h similarity index 97% rename from src/graphics/niche/InkHUD/DisplayHealth.h rename to src/graphics/niche/DisplayHealth.h index 2bd887f9d..f11dcf0fe 100644 --- a/src/graphics/niche/InkHUD/DisplayHealth.h +++ b/src/graphics/niche/DisplayHealth.h @@ -16,7 +16,7 @@ Responsible for maintaining display health, by optimizing the ratio of FAST vs F #include "InkHUD.h" -#include "graphics/niche/Drivers/EInk/EInk.h" +#include "graphics/eink/Drivers/EInk.h" namespace NicheGraphics::InkHUD { diff --git a/src/graphics/niche/InkHUD/Events.cpp b/src/graphics/niche/Events.cpp similarity index 99% rename from src/graphics/niche/InkHUD/Events.cpp rename to src/graphics/niche/Events.cpp index 577a773bb..6576b52bb 100644 --- a/src/graphics/niche/InkHUD/Events.cpp +++ b/src/graphics/niche/Events.cpp @@ -10,7 +10,7 @@ #include "./Applet.h" #include "./SystemApplet.h" -#include "graphics/niche/Utils/FlashData.h" +#include "graphics/niche/FlashData.h" using namespace NicheGraphics; diff --git a/src/graphics/niche/InkHUD/Events.h b/src/graphics/niche/Events.h similarity index 100% rename from src/graphics/niche/InkHUD/Events.h rename to src/graphics/niche/Events.h diff --git a/src/graphics/niche/Utils/FlashData.h b/src/graphics/niche/FlashData.h similarity index 100% rename from src/graphics/niche/Utils/FlashData.h rename to src/graphics/niche/FlashData.h diff --git a/src/graphics/niche/InkHUD/InkHUD.cpp b/src/graphics/niche/InkHUD.cpp similarity index 100% rename from src/graphics/niche/InkHUD/InkHUD.cpp rename to src/graphics/niche/InkHUD.cpp diff --git a/src/graphics/niche/InkHUD/InkHUD.h b/src/graphics/niche/InkHUD.h similarity index 98% rename from src/graphics/niche/InkHUD/InkHUD.h rename to src/graphics/niche/InkHUD.h index abd53951a..ebefef87f 100644 --- a/src/graphics/niche/InkHUD/InkHUD.h +++ b/src/graphics/niche/InkHUD.h @@ -12,7 +12,7 @@ #include "configuration.h" -#include "graphics/niche/Drivers/EInk/EInk.h" +#include "graphics/eink/Drivers/EInk.h" #include "./AppletFont.h" diff --git a/src/graphics/niche/InkHUD/PlatformioConfig.ini b/src/graphics/niche/InkHUD/PlatformioConfig.ini deleted file mode 100644 index 67ad5098f..000000000 --- a/src/graphics/niche/InkHUD/PlatformioConfig.ini +++ /dev/null @@ -1,12 +0,0 @@ -[inkhud] -build_src_filter = - +; Include the nicheGraphics directory -build_flags = - -D MESHTASTIC_INCLUDE_NICHE_GRAPHICS ; Use NicheGraphics - -D MESHTASTIC_INCLUDE_INKHUD ; Use InkHUD (a NicheGraphics UI) - -D MESHTASTIC_EXCLUDE_SCREEN ; Suppress default Screen class - -D MESHTASTIC_EXCLUDE_INPUTBROKER ; Suppress default input handling - -D HAS_BUTTON=0 ; Suppress default ButtonThread -lib_deps = - # renovate: datasource=github-tags depName=GFX_Root packageName=ZinggJM/GFX_Root - https://github.com/ZinggJM/GFX_Root/archive/3195764e352a0d2567c8d277ac408ca7293a99b0.zip ; Used by InkHUD as a "slimmer" version of AdafruitGFX diff --git a/src/graphics/niche/InkHUD/MessageStore.cpp b/src/graphics/niche/MessageStore.cpp similarity index 100% rename from src/graphics/niche/InkHUD/MessageStore.cpp rename to src/graphics/niche/MessageStore.cpp diff --git a/src/graphics/niche/InkHUD/MessageStore.h b/src/graphics/niche/MessageStore.h similarity index 100% rename from src/graphics/niche/InkHUD/MessageStore.h rename to src/graphics/niche/MessageStore.h diff --git a/src/graphics/niche/InkHUD/Persistence.cpp b/src/graphics/niche/Persistence.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Persistence.cpp rename to src/graphics/niche/Persistence.cpp diff --git a/src/graphics/niche/InkHUD/Persistence.h b/src/graphics/niche/Persistence.h similarity index 98% rename from src/graphics/niche/InkHUD/Persistence.h rename to src/graphics/niche/Persistence.h index 5054b7234..d3a42ec86 100644 --- a/src/graphics/niche/InkHUD/Persistence.h +++ b/src/graphics/niche/Persistence.h @@ -15,8 +15,8 @@ The save / load mechanism is a shared NicheGraphics feature. #include "configuration.h" #include "./InkHUD.h" -#include "graphics/niche/InkHUD/MessageStore.h" -#include "graphics/niche/Utils/FlashData.h" +#include "graphics/niche/FlashData.h" +#include "graphics/niche/MessageStore.h" namespace NicheGraphics::InkHUD { diff --git a/src/graphics/niche/InkHUD/Renderer.cpp b/src/graphics/niche/Renderer.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Renderer.cpp rename to src/graphics/niche/Renderer.cpp diff --git a/src/graphics/niche/InkHUD/Renderer.h b/src/graphics/niche/Renderer.h similarity index 98% rename from src/graphics/niche/InkHUD/Renderer.h rename to src/graphics/niche/Renderer.h index 1ab94b70b..607177747 100644 --- a/src/graphics/niche/InkHUD/Renderer.h +++ b/src/graphics/niche/Renderer.h @@ -17,7 +17,7 @@ Orchestrates updating of the display image #include "./DisplayHealth.h" #include "./InkHUD.h" #include "./Persistence.h" -#include "graphics/niche/Drivers/EInk/EInk.h" +#include "graphics/eink/Drivers/EInk.h" namespace NicheGraphics::InkHUD { diff --git a/src/graphics/niche/InkHUD/SystemApplet.h b/src/graphics/niche/SystemApplet.h similarity index 100% rename from src/graphics/niche/InkHUD/SystemApplet.h rename to src/graphics/niche/SystemApplet.h diff --git a/src/graphics/niche/InkHUD/Tile.cpp b/src/graphics/niche/Tile.cpp similarity index 100% rename from src/graphics/niche/InkHUD/Tile.cpp rename to src/graphics/niche/Tile.cpp diff --git a/src/graphics/niche/InkHUD/Tile.h b/src/graphics/niche/Tile.h similarity index 100% rename from src/graphics/niche/InkHUD/Tile.h rename to src/graphics/niche/Tile.h diff --git a/src/graphics/niche/InkHUD/WindowManager.cpp b/src/graphics/niche/WindowManager.cpp similarity index 100% rename from src/graphics/niche/InkHUD/WindowManager.cpp rename to src/graphics/niche/WindowManager.cpp diff --git a/src/graphics/niche/InkHUD/WindowManager.h b/src/graphics/niche/WindowManager.h similarity index 100% rename from src/graphics/niche/InkHUD/WindowManager.h rename to src/graphics/niche/WindowManager.h diff --git a/src/graphics/niche/InkHUD/docs/README.md b/src/graphics/niche/docs/README.md similarity index 97% rename from src/graphics/niche/InkHUD/docs/README.md rename to src/graphics/niche/docs/README.md index 7cd468d73..e154f1bb2 100644 --- a/src/graphics/niche/InkHUD/docs/README.md +++ b/src/graphics/niche/docs/README.md @@ -51,8 +51,8 @@ It is intended to supplement a connected client app. ### Self-Contained -- Keep InkHUD code within `/src/graphics/niche/InkHUD`. -- Place reusable components within `/src/graphics/niche`, for other UIs to take advantage of. +- Keep InkHUD code within `/src/graphics/niche`. +- Reusable E-Ink driver, panel, backlight, and BaseUI-adapter code lives in `/src/graphics/eink` so other UIs (including the default BaseUI) can use the same hardware layer without pulling InkHUD. - Interact with the firmware code using the **Module API**, **Observables**, and other similarly non-intrusive hooks. ### Static @@ -138,7 +138,7 @@ Hypothetical examples: E-Ink, 1602 LCDs, tiny OLEDs, smart watches, etc A NicheGraphics UI: - Is self-contained -- Makes use of the loose collection of resources (drivers, input methods, etc) gathered in the `/src/graphics/niche` folder. +- Makes use of shared E-Ink hardware resources (drivers, panels, backlight, BaseUI adapter) in `/src/graphics/eink`, plus InkHUD-specific resources (input helpers, fonts, applets, storage) in `/src/graphics/niche`. - Implements a `setupNicheGraphics()` method. ### NicheGraphics E-Ink Drivers @@ -149,7 +149,7 @@ Interacting with the drivers is straightforward. InkHUD generates a frame of 1-b `driver->update(uint8_t* buffer, EInk::UpdateTypes::FULL)` -For more information, see the documentation in `src/graphics/niche/Drivers/EInk` +For more information, see the documentation in `src/graphics/eink/Drivers` ### InkHUD Applets @@ -161,7 +161,7 @@ Consider: `DMApplet.h` (displays most recent direct message) and `RecentsList.h` - Applets use responsive design. They should scale for different screens / layouts / fonts. - Applets decide when to update. They use the Module API, Observers, etc, to retrieve information, and request a display update when they have something interesting to show. -See `src/graphics/niche/InkHUD/Applets/Examples` for example code. +See `src/graphics/niche/Applets/Examples` for example code. #### Writing an Applet @@ -252,7 +252,7 @@ You will need to add these lines to any variants which will use your applet. #### Applet Bases -If you need to create several similar applets, it might make sense to create a reusable base class. Several of these already exist in `src/graphics/niche/InkHUD/Applets/Bases`, but use these with caution, as they may be modified in future. +If you need to create several similar applets, it might make sense to create a reusable base class. Several of these already exist in `src/graphics/niche/Applets/Bases`, but use these with caution, as they may be modified in future. #### System Applets @@ -394,7 +394,7 @@ See [Encoding](#encoding) for details on using an extended-ASCII font. ### `InkHUD::InkHUD` -_`src/graphics/niche/InkHUD/InkHUD.h`_ +_`src/graphics/niche/InkHUD.h`_ - singleton - mediator between other InkHUD components @@ -415,7 +415,7 @@ For convenience, many InkHUD components call this on `begin`, and store it as `I ### `InkHUD::Persistence` -_`src/graphics/niche/InkHUD/Persistence.h`_ +_`src/graphics/niche/Persistence.h`_ Stores InkHUD data in flash @@ -430,7 +430,7 @@ Data saved only on shutdown / reboot. Not saved if power is removed unexpectedly ### `InkHUD::Persistence::Settings` -_`src/graphics/niche/InkHUD/Persistence.h`_ +_`src/graphics/niche/Persistence.h`_ Settings which relate to InkHUD. Mostly user's customization, but some values record the UI's state (e.g. `tips.safeShutdownSeen`) @@ -455,7 +455,7 @@ By modifying the values at this point, they will be used if we fail to load prev ### `InkHUD::Persistence::LatestMessage` -_`src/graphics/niche/InkHUD/Persistence.h`_ +_`src/graphics/niche/Persistence.h`_ Most recently received text message @@ -483,7 +483,7 @@ The hack is: ### `InkHUD::WindowManager` -_`src/graphics/niche/InkHUD/WindowManager.h`_ +_`src/graphics/niche/WindowManager.h`_ Manages which applets are shown, and their size / position (by manipulating the "tiles") @@ -502,7 +502,7 @@ Note: Some of the methods (incl. `changeLayout`, `changeActivatedApplets`) don't ### `InkHUD::Renderer` -_`src/graphics/niche/InkHUD/Renderer.h`_ +_`src/graphics/niche/Renderer.h`_ Get pixel output from applets (via a tile), combine, and pass to the driver. @@ -534,7 +534,7 @@ The exact size / position / rotation of InkHUD applets is configurable by the us ### `InkHUD::Renderer::DisplayHealth` -_`src/graphics/niche/InkHUD/DisplayHealth.h`_ +_`src/graphics/niche/DisplayHealth.h`_ Responsible for maintaining display health, by optimizing the ratio of FAST vs FULL refreshes diff --git a/src/graphics/niche/InkHUD/docs/appletfont.png b/src/graphics/niche/docs/appletfont.png similarity index 100% rename from src/graphics/niche/InkHUD/docs/appletfont.png rename to src/graphics/niche/docs/appletfont.png diff --git a/src/graphics/niche/InkHUD/docs/disclaimer.jpg b/src/graphics/niche/docs/disclaimer.jpg similarity index 100% rename from src/graphics/niche/InkHUD/docs/disclaimer.jpg rename to src/graphics/niche/docs/disclaimer.jpg diff --git a/src/graphics/niche/InkHUD/docs/rendering.gif b/src/graphics/niche/docs/rendering.gif similarity index 100% rename from src/graphics/niche/InkHUD/docs/rendering.gif rename to src/graphics/niche/docs/rendering.gif diff --git a/src/graphics/niche/InkHUD/docs/tile_translation.png b/src/graphics/niche/docs/tile_translation.png similarity index 100% rename from src/graphics/niche/InkHUD/docs/tile_translation.png rename to src/graphics/niche/docs/tile_translation.png diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 65e903134..e0469511e 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -33,8 +33,8 @@ extern MessageStore messageStore; #if !MESHTASTIC_EXCLUDE_GPS #include "GPS.h" #endif -#if defined(USE_EINK) && defined(USE_EINK_DYNAMICDISPLAY) -#include "graphics/EInkDynamicDisplay.h" // To select between full and fast refresh on E-Ink displays +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS +#include "graphics/BaseUIEInkDisplay.h" #endif #ifndef INPUTBROKER_MATRIX_TYPE @@ -1915,9 +1915,8 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st // Free Text Input Screen if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) { requestFocus(); -#if defined(USE_EINK) && defined(USE_EINK_DYNAMICDISPLAY) - EInkDynamicDisplay *einkDisplay = static_cast(display); - einkDisplay->enableUnlimitedFastMode(); +#if defined(USE_EINK) && defined(MESHTASTIC_INCLUDE_NICHE_GRAPHICS) + static_cast(display)->enableUnlimitedFastMode(); #endif #if defined(USE_VIRTUAL_KEYBOARD) drawKeyboard(display, state, 0, 0); diff --git a/variants/esp32/m5stack_coreink/nicheGraphics.h b/variants/esp32/m5stack_coreink/nicheGraphics.h new file mode 100644 index 000000000..6c0675a65 --- /dev/null +++ b/variants/esp32/m5stack_coreink/nicheGraphics.h @@ -0,0 +1,44 @@ +/* + +NicheGraphics setup for M5Stack CoreInk (1.54" 200x200 GDEW0154M09). + +The GDEW0154M09 uses an SSD1681-class controller, so the GDEY0154D67 panel +profile (same controller family, same resolution) drives it directly. + +LoRa and the e-ink panel share the default SPI bus (SCLK=18, MOSI=23); +use the board's default SPI instance rather than spinning up a separate +HSPI peripheral on the same pins. + +*/ + +#pragma once + +#include "configuration.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Panels/GDEY0154D67.h" + +class M5CoreInkPanel : public NicheGraphics::Panels::GDEY0154D67 +{ + protected: + SPIClass *beginSpi() override + { + SPI.begin(); + return &SPI; + } +}; + +void setupNicheGraphics() {} + +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new M5CoreInkPanel(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panel->rotation()); + display->setDisplayResilience(10, 2.0f); + return display; +} + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/esp32/m5stack_coreink/platformio.ini b/variants/esp32/m5stack_coreink/platformio.ini index e107bd893..177f5e08a 100644 --- a/variants/esp32/m5stack_coreink/platformio.ini +++ b/variants/esp32/m5stack_coreink/platformio.ini @@ -1,25 +1,22 @@ [env:m5stack-coreink] -extends = esp32_base +extends = esp32_base, niche board = m5stack-coreink board_check = true -build_src_filter = +build_src_filter = ${esp32_base.build_src_filter} -build_flags = + ${niche.build_src_filter} +build_flags = ${esp32_base.build_flags} + ${niche.build_flags} -I variants/esp32/m5stack_coreink ;-D RADIOLIB_VERBOSE -Ofast -D__MCUXPRESSO - -DEINK_DISPLAY_MODEL=GxEPD2_154_M09 - -DEINK_WIDTH=200 - -DEINK_HEIGHT=200 -DUSER_SETUP_LOADED - -DM5_COREINK + -DM5_COREINK -DM5STACK -lib_deps = +lib_deps = ${esp32_base.lib_deps} - # renovate: datasource=custom.pio depName=GxEPD2 packageName=zinggjm/library/GxEPD2 - zinggjm/GxEPD2@1.6.8 # renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib lewisxhe/SensorLib@0.3.4 lib_ignore = diff --git a/variants/esp32s3/ELECROW-ThinkNode-M5/nicheGraphics.h b/variants/esp32s3/ELECROW-ThinkNode-M5/nicheGraphics.h new file mode 100644 index 000000000..42cf6665a --- /dev/null +++ b/variants/esp32s3/ELECROW-ThinkNode-M5/nicheGraphics.h @@ -0,0 +1,42 @@ +/* + +NicheGraphics setup for Elecrow ThinkNode-M5 (GDEY0154D67 1.54" on HSPI). + +*/ + +#pragma once + +#include "configuration.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Panels/GDEY0154D67.h" + +class ThinkNodeM5Panel : public NicheGraphics::Panels::GDEY0154D67 +{ + protected: + SPIClass *beginSpi() override + { + auto *hspi = new SPIClass(HSPI); + hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); + return hspi; + } + // Old GxEPD2 path used setRotation(4) (= 0° + mirror). BaseUIEInkDisplay does not implement + // mirroring; expose 0 here and rely on hardware orientation. Adjust if testing shows otherwise. + public: + uint8_t rotation() const override { return 0; } +}; + +void setupNicheGraphics() {} + +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new ThinkNodeM5Panel(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panel->rotation()); + display->setDisplayResilience(10, 1.5f); + return display; +} + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/esp32s3/ELECROW-ThinkNode-M5/platformio.ini b/variants/esp32s3/ELECROW-ThinkNode-M5/platformio.ini index 9f8c3a871..2394d2348 100644 --- a/variants/esp32s3/ELECROW-ThinkNode-M5/platformio.ini +++ b/variants/esp32s3/ELECROW-ThinkNode-M5/platformio.ini @@ -9,29 +9,20 @@ custom_meshtastic_images = thinknode_m1.svg custom_meshtastic_tags = Elecrow custom_meshtastic_requires_dfu = false -extends = esp32s3_base +extends = esp32s3_base, niche board = ESP32-S3-WROOM-1-N4 build_src_filter = ${esp32s3_base.build_src_filter} +<../variants/esp32s3/ELECROW-ThinkNode-M5> + ${niche.build_src_filter} build_flags = ${esp32s3_base.build_flags} + ${niche.build_flags} -D ELECROW_ThinkNode_M5 -I variants/esp32s3/ELECROW-ThinkNode-M5 - -DEINK_DISPLAY_MODEL=GxEPD2_154_D67 - -DEINK_WIDTH=200 - -DEINK_HEIGHT=200 - -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk - -DEINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted //20 - -DEINK_LIMIT_RATE_BACKGROUND_SEC=10 ; Minimum interval between BACKGROUND updates //30 - -DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates -; -DEINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated - -DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. lib_deps = ${esp32s3_base.lib_deps} - # renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master - https://github.com/meshtastic/GxEPD2/archive/c7eb4c3c167cf396ef4f541cc5d4c6aa42f3c46b.zip # renovate: datasource=custom.pio depName=PCA9557-arduino packageName=maxpromer/library/PCA9557-arduino maxpromer/PCA9557-arduino@1.0.0 # renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib diff --git a/variants/esp32s3/crowpanel-esp32s3-5-epaper/nicheGraphics.h b/variants/esp32s3/crowpanel-esp32s3-5-epaper/nicheGraphics.h new file mode 100644 index 000000000..3c854adaf --- /dev/null +++ b/variants/esp32s3/crowpanel-esp32s3-5-epaper/nicheGraphics.h @@ -0,0 +1,61 @@ +/* + +NicheGraphics setup for the CrowPanel ESP32-S3 ePaper boards. + +This directory is shared by three envs that each pick a different panel via +CROWPANEL_ESP32S3_{2,4,5}_EPAPER macros in platformio.ini. + +*/ + +#pragma once + +#include "configuration.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "graphics/BaseUIEInkDisplay.h" + +#if defined(CROWPANEL_ESP32S3_2_EPAPER) +#include "graphics/eink/Panels/GDEY029T94.h" +using CrowpanelBasePanel = NicheGraphics::Panels::GDEY029T94; +#elif defined(CROWPANEL_ESP32S3_4_EPAPER) +#include "graphics/eink/Panels/HINK_E042A87.h" +using CrowpanelBasePanel = NicheGraphics::Panels::HINK_E042A87; +#elif defined(CROWPANEL_ESP32S3_5_EPAPER) +#include "graphics/eink/Panels/GDEY0579T93.h" +using CrowpanelBasePanel = NicheGraphics::Panels::GDEY0579T93; +#else +#error "No CROWPANEL_ESP32S3_*_EPAPER macro defined for this env" +#endif + +class CrowpanelPanel : public CrowpanelBasePanel +{ + protected: + SPIClass *beginSpi() override + { + auto *hspi = new SPIClass(HSPI); + hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); + return hspi; + } + void prePowerOn() override + { +#ifdef VEXT_ENABLE + pinMode(VEXT_ENABLE, OUTPUT); + digitalWrite(VEXT_ENABLE, VEXT_ON_VALUE); + delay(10); +#endif + } +}; + +void setupNicheGraphics() {} + +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new CrowpanelPanel(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panel->rotation()); + display->setDisplayResilience(20, 1.5f); + return display; +} + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/esp32s3/crowpanel-esp32s3-5-epaper/platformio.ini b/variants/esp32s3/crowpanel-esp32s3-5-epaper/platformio.ini index 7e37a0eb4..415b7bcfc 100644 --- a/variants/esp32s3/crowpanel-esp32s3-5-epaper/platformio.ini +++ b/variants/esp32s3/crowpanel-esp32s3-5-epaper/platformio.ini @@ -1,5 +1,5 @@ -[env:crowpanel-esp32s3-5-epaper] -extends = esp32s3_base +[crowpanel_epaper_base] +extends = esp32s3_base, niche board_build.arduino.memory_type = qio_opi board_build.flash_mode = qio board_build.psram_type = opi @@ -10,80 +10,32 @@ board = esp32-s3-devkitc-1 ;upload_port = /dev/ttyUSB0 board_level = extra upload_protocol = esptool -build_flags = +build_src_filter = + ${esp32s3_base.build_src_filter} + ${niche.build_src_filter} +build_flags = ${esp32s3_base.build_flags} - -D CROWPANEL_ESP32S3_5_EPAPER + ${niche.build_flags} -I variants/esp32s3/crowpanel-esp32s3-5-epaper -D PRIVATE_HW -DBOARD_HAS_PSRAM - -DEINK_DISPLAY_MODEL=GxEPD2_579_GDEY0579T93 ;https://www.good-display.com/product/439.html - -DEINK_WIDTH=792 - -DEINK_HEIGHT=272 - -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk - -DEINK_LIMIT_FASTREFRESH=100 ; How many consecutive fast-refreshes are permitted - ;-DEINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates - ;-DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 lib_deps = ${esp32s3_base.lib_deps} - # renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master - https://github.com/meshtastic/GxEPD2/archive/c7eb4c3c167cf396ef4f541cc5d4c6aa42f3c46b.zip + +[env:crowpanel-esp32s3-5-epaper] +extends = crowpanel_epaper_base +build_flags = + ${crowpanel_epaper_base.build_flags} + -D CROWPANEL_ESP32S3_5_EPAPER [env:crowpanel-esp32s3-4-epaper] -extends = esp32s3_base -board_build.arduino.memory_type = qio_opi -board_build.flash_mode = qio -board_build.psram_type = opi -board_upload.flash_size = 8MB -board_upload.maximum_size = 8388608 -board_build.partitions = default_8MB.csv -board = esp32-s3-devkitc-1 -;upload_port = /dev/ttyUSB0 -board_level = extra -upload_protocol = esptool -build_flags = - ${esp32s3_base.build_flags} +extends = crowpanel_epaper_base +build_flags = + ${crowpanel_epaper_base.build_flags} -D CROWPANEL_ESP32S3_4_EPAPER - -I variants/esp32s3/crowpanel-esp32s3-5-epaper - -D PRIVATE_HW - -DBOARD_HAS_PSRAM - -DEINK_DISPLAY_MODEL=GxEPD2_420_GYE042A87 ; similar Panel: GDEY042T81 : https://www.good-display.com/product/386.html - -DEINK_WIDTH=400 - -DEINK_HEIGHT=300 - -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk - -DEINK_LIMIT_FASTREFRESH=100 ; How many consecutive fast-refreshes are permitted - ;-DEINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates - ;-DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 -lib_deps = - ${esp32s3_base.lib_deps} - # renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master - https://github.com/meshtastic/GxEPD2/archive/c7eb4c3c167cf396ef4f541cc5d4c6aa42f3c46b.zip [env:crowpanel-esp32s3-2-epaper] -extends = esp32s3_base -board_build.arduino.memory_type = qio_opi -board_build.flash_mode = qio -board_build.psram_type = opi -board_upload.flash_size = 8MB -board_upload.maximum_size = 8388608 -board_build.partitions = default_8MB.csv -board = esp32-s3-devkitc-1 -;upload_port = /dev/ttyUSB0 -board_level = extra -upload_protocol = esptool -build_flags = - ${esp32s3_base.build_flags} +extends = crowpanel_epaper_base +build_flags = + ${crowpanel_epaper_base.build_flags} -D CROWPANEL_ESP32S3_2_EPAPER - -I variants/esp32s3/crowpanel-esp32s3-5-epaper - -D PRIVATE_HW - -DBOARD_HAS_PSRAM - -DEINK_DISPLAY_MODEL=GxEPD2_290_GDEY029T94 ;https://www.good-display.com/product/389.html - -DEINK_WIDTH=296 - -DEINK_HEIGHT=128 - -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk - -DEINK_LIMIT_FASTREFRESH=100 ; How many consecutive fast-refreshes are permitted - ;-DEINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates - ;-DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 -lib_deps = - ${esp32s3_base.lib_deps} - # renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master - https://github.com/meshtastic/GxEPD2/archive/c7eb4c3c167cf396ef4f541cc5d4c6aa42f3c46b.zip diff --git a/variants/esp32s3/diy/my_esp32s3_diy_eink/nicheGraphics.h b/variants/esp32s3/diy/my_esp32s3_diy_eink/nicheGraphics.h new file mode 100644 index 000000000..b9661d67f --- /dev/null +++ b/variants/esp32s3/diy/my_esp32s3_diy_eink/nicheGraphics.h @@ -0,0 +1,45 @@ +/* + +NicheGraphics setup for the DIY ESP32-S3 2.9" e-ink reference board +(as shipped: GDEW029T5D, 128x296, UC8151D controller). + +The UC8151D init/command set is close enough to SSD1680 that GDEY029T94 +(same 128x296 geometry, SSD1680-based) is used as the panel profile. +If the specific UC8151D panel behaves oddly (ghosting, inverted colours, +wrong refresh sequence), swap this panel profile for a UC8151D one. + +Uses the default SPI bus, which is shared with the SX1280 LoRa module. + +*/ + +#pragma once + +#include "configuration.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Panels/GDEY029T94.h" + +class DiyEsp32S3EinkPanel : public NicheGraphics::Panels::GDEY029T94 +{ + protected: + SPIClass *beginSpi() override + { + SPI.begin(); + return &SPI; + } +}; + +void setupNicheGraphics() {} + +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new DiyEsp32S3EinkPanel(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panel->rotation()); + display->setDisplayResilience(7, 1.5f); + return display; +} + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/esp32s3/diy/my_esp32s3_diy_eink/platformio.ini b/variants/esp32s3/diy/my_esp32s3_diy_eink/platformio.ini index 90e4910f4..f2332492f 100644 --- a/variants/esp32s3/diy/my_esp32s3_diy_eink/platformio.ini +++ b/variants/esp32s3/diy/my_esp32s3_diy_eink/platformio.ini @@ -1,6 +1,6 @@ [env:my-esp32s3-diy-eink] board_level = extra -extends = esp32s3_base +extends = esp32s3_base, niche board = my_esp32s3_diy_eink board_build.arduino.memory_type = dio_opi board_build.mcu = esp32s3 @@ -10,19 +10,18 @@ upload_protocol = esptool upload_speed = 921600 lib_deps = ${esp32s3_base.lib_deps} - # renovate: datasource=custom.pio depName=GxEPD2 packageName=zinggjm/library/GxEPD2 - zinggjm/GxEPD2@1.6.8 build_unflags = ${esp32s3_base.build_unflags} -DARDUINO_USB_MODE=1 -build_flags = +build_src_filter = + ${esp32s3_base.build_src_filter} + ${niche.build_src_filter} +build_flags = ${esp32s3_base.build_flags} + ${niche.build_flags} -D PRIVATE_HW -I variants/esp32s3/diy/my_esp32s3_diy_eink -Dmy - -DEINK_DISPLAY_MODEL=GxEPD2_290_T5D - -DEINK_WIDTH=296 - -DEINK_HEIGHT=128 -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue -DARDUINO_USB_MODE=0 diff --git a/variants/esp32s3/esp32-s3-pico/nicheGraphics.h b/variants/esp32s3/esp32-s3-pico/nicheGraphics.h new file mode 100644 index 000000000..fa4c983cd --- /dev/null +++ b/variants/esp32s3/esp32-s3-pico/nicheGraphics.h @@ -0,0 +1,38 @@ +/* + +NicheGraphics setup for the ESP32-S3-Pico board (GDEY029T94 2.9" on the default SPI bus). + +*/ + +#pragma once + +#include "configuration.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Panels/GDEY029T94.h" + +class Esp32S3PicoPanel : public NicheGraphics::Panels::GDEY029T94 +{ + protected: + SPIClass *beginSpi() override + { + // Old GxEPD2 path used the default SPI bus; preserve that to avoid SPI conflicts. + SPI.begin(); + return &SPI; + } +}; + +void setupNicheGraphics() {} + +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new Esp32S3PicoPanel(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panel->rotation()); + display->setDisplayResilience(7, 1.5f); + return display; +} + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/esp32s3/esp32-s3-pico/platformio.ini b/variants/esp32s3/esp32-s3-pico/platformio.ini index 64f50f80e..983549afe 100644 --- a/variants/esp32s3/esp32-s3-pico/platformio.ini +++ b/variants/esp32s3/esp32-s3-pico/platformio.ini @@ -1,7 +1,7 @@ [env:ESP32-S3-Pico] board_level = extra -extends = esp32s3_base +extends = esp32s3_base, niche upload_protocol = esptool board = esp32-s3-pico board_build.partitions = default_16MB.csv @@ -12,15 +12,13 @@ board_upload.require_upload_port = yes ;upload_port = /dev/ttyACM0 +build_src_filter = ${esp32s3_base.build_src_filter} + ${niche.build_src_filter} build_flags = ${esp32s3_base.build_flags} + ${niche.build_flags} -DESP32_S3_PICO ;-DPRIVATE_HW -Ivariants/esp32s3/esp32-s3-pico -DBOARD_HAS_PSRAM - -DEINK_DISPLAY_MODEL=GxEPD2_290_T94_V2 - -DEINK_WIDTH=296 - -DEINK_HEIGHT=128 lib_deps = ${esp32s3_base.lib_deps} - # renovate: datasource=custom.pio depName=GxEPD2 packageName=zinggjm/library/GxEPD2 - zinggjm/GxEPD2@1.6.8 diff --git a/variants/esp32s3/heltec_vision_master_e213/nicheGraphics.h b/variants/esp32s3/heltec_vision_master_e213/nicheGraphics.h index fb0744bc3..8edae09f7 100644 --- a/variants/esp32s3/heltec_vision_master_e213/nicheGraphics.h +++ b/variants/esp32s3/heltec_vision_master_e213/nicheGraphics.h @@ -1,119 +1,130 @@ +/* + +NicheGraphics setup for Heltec Vision Master E-213 (HSPI; runtime-detected 2.13" panel). + +Panel choice depends on detectEInk() output: + - LCMEN213EFC1 (V1, unmarked) + - E0213A367 (V1.1, V1.2) + +Both panels share the same SPI wiring; the difference is the controller/waveform. + +*/ + #pragma once #include "configuration.h" #ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS -// InkHUD-specific components -// --------------------------- -#include "graphics/niche/InkHUD/InkHUD.h" +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Panels/E0213A367.h" +#include "graphics/eink/Panels/LCMEN213EFC1.h" -// Applets -#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h" -#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h" -#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h" -#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h" -#include "graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" +#include "buzz.h" +#include "einkDetect.h" -// Shared NicheGraphics components -// -------------------------------- -#include "graphics/niche/Drivers/EInk/E0213A367.h" -#include "graphics/niche/Drivers/EInk/LCMEN2R13EFC1.h" +#ifdef MESHTASTIC_INCLUDE_INKHUD +#include "graphics/niche/Applets/User/AllMessage/AllMessageApplet.h" +#include "graphics/niche/Applets/User/DM/DMApplet.h" +#include "graphics/niche/Applets/User/FavoritesMap/FavoritesMapApplet.h" +#include "graphics/niche/Applets/User/Heard/HeardApplet.h" +#include "graphics/niche/Applets/User/Positions/PositionsApplet.h" +#include "graphics/niche/Applets/User/RecentsList/RecentsListApplet.h" +#include "graphics/niche/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" +#include "graphics/niche/InkHUD.h" #include "graphics/niche/Inputs/TwoButton.h" +#endif -#include "buzz.h" // Button feedback -#include "einkDetect.h" // Detect display model at runtime +template class VMe213Panel : public Base +{ + protected: + SPIClass *beginSpi() override + { + auto *hspi = new SPIClass(HSPI); + hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); + return hspi; + } +}; +struct VMe213DetectedDriver { + NicheGraphics::Drivers::EInk *driver; + uint8_t fastPerFull; + float stress; + uint8_t rotation; +}; + +static VMe213DetectedDriver detectAndCreateVMe213Driver() +{ + EInkDetectionResult model = detectEInk(); + VMe213DetectedDriver out{nullptr, 10, 1.5f, 3}; + if (model == EInkDetectionResult::LCMEN213EFC1) { + auto *p = new VMe213Panel(); + out.driver = p->create(); + out.rotation = p->rotation(); + out.fastPerFull = 10; + out.stress = 1.5f; + } else { // E0213A367 (V1.1, V1.2) + auto *p = new VMe213Panel(); + out.driver = p->create(); + out.rotation = p->rotation(); + out.fastPerFull = 15; + out.stress = 3.0f; + } + return out; +} + +#ifdef MESHTASTIC_INCLUDE_INKHUD void setupNicheGraphics() { using namespace NicheGraphics; - // Detect E-Ink Model - // ------------------- - - EInkDetectionResult displayModel = detectEInk(); - - // SPI - // ----------------------------- - - // Display is connected to HSPI - SPIClass *hspi = new SPIClass(HSPI); - hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); - - // E-Ink Driver - // ----------------------------- - - Drivers::EInk *driver; - - if (displayModel == EInkDetectionResult::LCMEN213EFC1) // V1 (unmarked) - driver = new Drivers::LCMEN213EFC1; - else if (displayModel == EInkDetectionResult::E0213A367) // V1.1 - driver = new Drivers::E0213A367; - - driver->begin(hspi, PIN_EINK_DC, PIN_EINK_CS, PIN_EINK_BUSY, PIN_EINK_RES); - - // InkHUD - // ---------------------------- + VMe213DetectedDriver detected = detectAndCreateVMe213Driver(); InkHUD::InkHUD *inkhud = InkHUD::InkHUD::getInstance(); + inkhud->setDriver(detected.driver); + inkhud->setDisplayResilience(detected.fastPerFull, detected.stress); - // Set the E-Ink driver - inkhud->setDriver(driver); - - // Set how many FAST updates per FULL update - // Set how unhealthy additional FAST updates beyond this number are - - if (displayModel == EInkDetectionResult::LCMEN213EFC1) // V1 (unmarked) - inkhud->setDisplayResilience(10, 1.5); - else if (displayModel == EInkDetectionResult::E0213A367) // V1.1 - inkhud->setDisplayResilience(15, 3); - - // Select fonts InkHUD::Applet::fontLarge = FREESANS_12PT_WIN1252; InkHUD::Applet::fontMedium = FREESANS_9PT_WIN1252; InkHUD::Applet::fontSmall = FREESANS_6PT_WIN1252; - // Customize default settings - inkhud->persistence->settings.userTiles.maxCount = 2; // How many tiles can the display handle? - inkhud->persistence->settings.rotation = 3; // 270 degrees clockwise - inkhud->persistence->settings.userTiles.count = 1; // One tile only by default, keep things simple for new users - inkhud->persistence->settings.optionalMenuItems.nextTile = false; // Behavior handled by aux button instead + inkhud->persistence->settings.userTiles.maxCount = 2; + inkhud->persistence->settings.rotation = 3; + inkhud->persistence->settings.userTiles.count = 1; + inkhud->persistence->settings.optionalMenuItems.nextTile = false; - // Pick applets - // Note: order of applets determines priority of "auto-show" feature - inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); // Activated, autoshown - inkhud->addApplet("DMs", new InkHUD::DMApplet); // - - inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // - - inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // - - inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated - inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet); // - - inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // - - inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, not autoshown, default on tile 0 + inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); + inkhud->addApplet("DMs", new InkHUD::DMApplet); + inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); + inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); + inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); + inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet); + inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); + inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); - // Start running InkHUD inkhud->begin(); - // Buttons - // -------------------------- - - Inputs::TwoButton *buttons = Inputs::TwoButton::getInstance(); // Shared NicheGraphics component - - // #0: Main User Button + Inputs::TwoButton *buttons = Inputs::TwoButton::getInstance(); buttons->setWiring(0, Inputs::TwoButton::getUserButtonPin()); buttons->setHandlerShortPress(0, [inkhud]() { inkhud->shortpress(); }); buttons->setHandlerLongPress(0, [inkhud]() { inkhud->longpress(); }); - - // #1: Aux Button buttons->setWiring(1, PIN_BUTTON2); buttons->setHandlerShortPress(1, [inkhud]() { inkhud->nextTile(); playChirp(); }); - - // Begin handling button events buttons->start(); } +#else +void setupNicheGraphics() {} +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + VMe213DetectedDriver detected = detectAndCreateVMe213Driver(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(detected.driver, detected.rotation); + display->setDisplayResilience(detected.fastPerFull, detected.stress); + return display; +} #endif + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/esp32s3/heltec_vision_master_e213/platformio.ini b/variants/esp32s3/heltec_vision_master_e213/platformio.ini index bd1b73d2b..b3444806b 100644 --- a/variants/esp32s3/heltec_vision_master_e213/platformio.ini +++ b/variants/esp32s3/heltec_vision_master_e213/platformio.ini @@ -11,26 +11,20 @@ custom_meshtastic_requires_dfu = true custom_meshtastic_partition_scheme = 8MB custom_meshtastic_has_ink_hud = true -extends = esp32s3_base +extends = esp32s3_base, niche board = heltec_vision_master_e213 board_build.partitions = default_8MB.csv -build_flags = +build_src_filter = + ${esp32s3_base.build_src_filter} + ${niche.build_src_filter} +build_flags = ${esp32s3_base.build_flags} - -Ivariants/esp32s3/heltec_vision_master_e213 + ${niche.build_flags} + -Ivariants/esp32s3/heltec_vision_master_e213 -DHELTEC_VISION_MASTER_E213 -DUSE_EINK - -DGXEPD2_DRIVER_0=GxEPD2_213_FC1 - -DGXEPD2_DRIVER_1=GxEPD2_213_E0213A367 - -DEINK_WIDTH=250 - -DEINK_HEIGHT=122 - -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk - -DEINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted - -DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. - -DEINK_HASQUIRK_GHOSTING ; Display model is identified as "prone to ghosting" lib_deps = ${esp32s3_base.lib_deps} - # renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master - https://github.com/meshtastic/GxEPD2/archive/c7eb4c3c167cf396ef4f541cc5d4c6aa42f3c46b.zip upload_speed = 115200 [env:heltec-vision-master-e213-inkhud] @@ -41,7 +35,7 @@ board_build.partitions = default_8MB.csv build_src_filter = ${esp32s3_base.build_src_filter} ${inkhud.build_src_filter} -build_flags = +build_flags = ${esp32s3_base.build_flags} ${inkhud.build_flags} -I variants/esp32s3/heltec_vision_master_e213 diff --git a/variants/esp32s3/heltec_vision_master_e290/nicheGraphics.h b/variants/esp32s3/heltec_vision_master_e290/nicheGraphics.h index a90500b15..0d01fa311 100644 --- a/variants/esp32s3/heltec_vision_master_e290/nicheGraphics.h +++ b/variants/esp32s3/heltec_vision_master_e290/nicheGraphics.h @@ -1,13 +1,14 @@ /* -Most of the Meshtastic firmware uses preprocessor macros throughout the code to support different hardware variants. -NicheGraphics attempts a different approach: +NicheGraphics setup for Heltec Vision Master E-290. -Per-device config takes place in this setupNicheGraphics() method -(And a small amount in platformio.ini) +Supports two UI builds that share this file: + - InkHUD build (MESHTASTIC_INCLUDE_INKHUD defined) + - BaseUI build (MESHTASTIC_INCLUDE_INKHUD not defined) -This file sets up InkHUD for Heltec VM-E290. -Different NicheGraphics UIs and different hardware variants will each have their own setup procedure. +The panel itself (DEPG0290BNS800, SSD1680, 128x296) is defined once in + graphics/eink/Panels/DEPG0290BNS800.h +The variant only overrides what is specific to this board — here, that's the HSPI pin bring-up. */ @@ -17,100 +18,111 @@ Different NicheGraphics UIs and different hardware variants will each have their #ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS -// InkHUD-specific components -// --------------------------- -#include "graphics/niche/InkHUD/InkHUD.h" +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Panels/DEPG0290BNS800.h" -// Applets -#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h" -#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h" -#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h" -#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h" -#include "graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" - -// Shared NicheGraphics components -// -------------------------------- -#include "graphics/niche/Drivers/EInk/DEPG0290BNS800.h" -#include "graphics/niche/Inputs/TwoButton.h" - -// Button feedback #include "buzz.h" +#ifdef MESHTASTIC_INCLUDE_INKHUD +#include "graphics/niche/Applets/User/AllMessage/AllMessageApplet.h" +#include "graphics/niche/Applets/User/DM/DMApplet.h" +#include "graphics/niche/Applets/User/FavoritesMap/FavoritesMapApplet.h" +#include "graphics/niche/Applets/User/Heard/HeardApplet.h" +#include "graphics/niche/Applets/User/Positions/PositionsApplet.h" +#include "graphics/niche/Applets/User/RecentsList/RecentsListApplet.h" +#include "graphics/niche/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" +#include "graphics/niche/InkHUD.h" +#include "graphics/niche/Inputs/TwoButton.h" +#endif + +// Board-specific: display is wired to HSPI with explicit pins. +class VMe290Panel : public NicheGraphics::Panels::DEPG0290BNS800 +{ + protected: + SPIClass *beginSpi() override + { + auto *hspi = new SPIClass(HSPI); + hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); + return hspi; + } +}; + +static NicheGraphics::Drivers::EInk *createVMe290Driver(uint8_t *outRotation) +{ + auto *panel = new VMe290Panel(); + *outRotation = panel->rotation(); + return panel->create(); +} + +#ifdef MESHTASTIC_INCLUDE_INKHUD +static void setupVMe290Buttons(std::function shortPress, std::function longPress, + std::function auxShortPress) +{ + using namespace NicheGraphics; + auto *buttons = Inputs::TwoButton::getInstance(); + + buttons->setWiring(0, Inputs::TwoButton::getUserButtonPin()); + buttons->setHandlerShortPress(0, shortPress); + buttons->setHandlerLongPress(0, longPress); + + buttons->setWiring(1, PIN_BUTTON2); + buttons->setHandlerShortPress(1, auxShortPress); + + buttons->start(); +} + void setupNicheGraphics() { using namespace NicheGraphics; - // SPI - // ----------------------------- - - // Display is connected to HSPI - SPIClass *hspi = new SPIClass(HSPI); - hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); - - // E-Ink Driver - // ----------------------------- - - Drivers::EInk *driver = new Drivers::DEPG0290BNS800; - driver->begin(hspi, PIN_EINK_DC, PIN_EINK_CS, PIN_EINK_BUSY); - - // InkHUD - // ---------------------------- + uint8_t panelRotation; + Drivers::EInk *driver = createVMe290Driver(&panelRotation); + (void)panelRotation; // InkHUD uses its own settings.rotation below InkHUD::InkHUD *inkhud = InkHUD::InkHUD::getInstance(); - - // Set the E-Ink driver inkhud->setDriver(driver); - - // Set how many FAST updates per FULL update - // Set how unhealthy additional FAST updates beyond this number are inkhud->setDisplayResilience(7, 1.5); - // Select fonts InkHUD::Applet::fontLarge = FREESANS_12PT_WIN1252; InkHUD::Applet::fontMedium = FREESANS_9PT_WIN1252; InkHUD::Applet::fontSmall = FREESANS_6PT_WIN1252; - // Customize default settings - inkhud->persistence->settings.userTiles.maxCount = 2; // How many tiles can the display handle? - inkhud->persistence->settings.rotation = 1; // 90 degrees clockwise - inkhud->persistence->settings.userTiles.count = 1; // One tile only by default, keep things simple for new users - inkhud->persistence->settings.optionalMenuItems.nextTile = false; // Behavior handled by aux button instead + inkhud->persistence->settings.userTiles.maxCount = 2; + inkhud->persistence->settings.rotation = 1; + inkhud->persistence->settings.userTiles.count = 1; + inkhud->persistence->settings.optionalMenuItems.nextTile = false; - // Pick applets - // Note: order of applets determines priority of "auto-show" feature - inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); // Activated, autoshown - inkhud->addApplet("DMs", new InkHUD::DMApplet); // - - inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // - - inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // - - inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated - inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet); // - - inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // - - inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, not autoshown, default on tile 0 + inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); + inkhud->addApplet("DMs", new InkHUD::DMApplet); + inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); + inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); + inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); + inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet); + inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); + inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); - // Start running InkHUD inkhud->begin(); - // Buttons - // -------------------------- - - Inputs::TwoButton *buttons = Inputs::TwoButton::getInstance(); // A shared NicheGraphics component - - // #0: Main User Button - buttons->setWiring(0, Inputs::TwoButton::getUserButtonPin()); - buttons->setHandlerShortPress(0, [inkhud]() { inkhud->shortpress(); }); - buttons->setHandlerLongPress(0, [inkhud]() { inkhud->longpress(); }); - - // #1: Aux Button - buttons->setWiring(1, PIN_BUTTON2); - buttons->setHandlerShortPress(1, [inkhud]() { - inkhud->nextTile(); - playChirp(); - }); - - // Begin handling button events - buttons->start(); + setupVMe290Buttons([inkhud]() { inkhud->shortpress(); }, [inkhud]() { inkhud->longpress(); }, + [inkhud]() { + inkhud->nextTile(); + playChirp(); + }); +} +#else // BaseUI path +void setupNicheGraphics() +{ + // BaseUI handles its own input via the default ButtonThread. Nothing extra to set up here. } -#endif +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + uint8_t panelRotation; + NicheGraphics::Drivers::EInk *driver = createVMe290Driver(&panelRotation); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panelRotation); + display->setDisplayResilience(7, 1.5f); + return display; +} +#endif // MESHTASTIC_INCLUDE_INKHUD + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/esp32s3/heltec_vision_master_e290/platformio.ini b/variants/esp32s3/heltec_vision_master_e290/platformio.ini index 9fdb023de..804bd557a 100644 --- a/variants/esp32s3/heltec_vision_master_e290/platformio.ini +++ b/variants/esp32s3/heltec_vision_master_e290/platformio.ini @@ -1,4 +1,4 @@ -; Using the original screen class +; BaseUI build, using NicheGraphics drivers (no GxEPD2). [env:heltec-vision-master-e290] custom_meshtastic_hw_model = 68 custom_meshtastic_hw_model_slug = HELTEC_VISION_MASTER_E290 @@ -12,38 +12,32 @@ custom_meshtastic_requires_dfu = true custom_meshtastic_partition_scheme = 8MB custom_meshtastic_has_ink_hud = true -extends = esp32s3_base +extends = esp32s3_base, niche board = heltec_vision_master_e290 board_build.partitions = default_8MB.csv -build_flags = - ${esp32s3_base.build_flags} +build_src_filter = + ${esp32s3_base.build_src_filter} + ${niche.build_src_filter} +build_flags = + ${esp32s3_base.build_flags} + ${niche.build_flags} -I variants/esp32s3/heltec_vision_master_e290 -D DISPLAY_FLIP_SCREEN ; Orient so the LoRa antenna faces up -D HELTEC_VISION_MASTER_E290 -D BUTTON_CLICK_MS=200 - -D EINK_DISPLAY_MODEL=GxEPD2_290_BN8 - -D EINK_WIDTH=296 - -D EINK_HEIGHT=128 -D USE_EINK - -D USE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk - -D EINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted - -D EINK_HASQUIRK_GHOSTING ; Display model is identified as "prone to ghosting" - - lib_deps = ${esp32s3_base.lib_deps} - # renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master - https://github.com/meshtastic/GxEPD2/archive/c7eb4c3c167cf396ef4f541cc5d4c6aa42f3c46b.zip upload_speed = 115200 [env:heltec-vision-master-e290-inkhud] extends = esp32s3_base, inkhud board = heltec_vision_master_e290 board_build.partitions = default_8MB.csv -build_src_filter = +build_src_filter = ${esp32s3_base.build_src_filter} ${inkhud.build_src_filter} -build_flags = +build_flags = ${esp32s3_base.build_flags} ${inkhud.build_flags} -I variants/esp32s3/heltec_vision_master_e290 diff --git a/variants/esp32s3/heltec_wireless_paper/nicheGraphics.h b/variants/esp32s3/heltec_wireless_paper/nicheGraphics.h index 9e84a541e..d46abf69e 100644 --- a/variants/esp32s3/heltec_wireless_paper/nicheGraphics.h +++ b/variants/esp32s3/heltec_wireless_paper/nicheGraphics.h @@ -1,112 +1,121 @@ +/* + +NicheGraphics setup for Heltec Wireless Paper (HSPI; runtime-detected 2.13" panel). + +Panel choice depends on detectEInk() output: + - LCMEN213EFC1 (V1.1) + - E0213A367 (V1.1.1, V1.2) + +*/ + #pragma once #include "configuration.h" #ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS -// InkHUD-specific components -// --------------------------- -#include "graphics/niche/InkHUD/InkHUD.h" +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Panels/E0213A367.h" +#include "graphics/eink/Panels/LCMEN213EFC1.h" -// Applets -#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h" -#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h" -#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h" -#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h" -#include "graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" +#include "einkDetect.h" -// Shared NicheGraphics components -// -------------------------------- -#include "graphics/niche/Drivers/EInk/E0213A367.h" -#include "graphics/niche/Drivers/EInk/LCMEN2R13EFC1.h" +#ifdef MESHTASTIC_INCLUDE_INKHUD +#include "graphics/niche/Applets/User/AllMessage/AllMessageApplet.h" +#include "graphics/niche/Applets/User/DM/DMApplet.h" +#include "graphics/niche/Applets/User/FavoritesMap/FavoritesMapApplet.h" +#include "graphics/niche/Applets/User/Heard/HeardApplet.h" +#include "graphics/niche/Applets/User/Positions/PositionsApplet.h" +#include "graphics/niche/Applets/User/RecentsList/RecentsListApplet.h" +#include "graphics/niche/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" +#include "graphics/niche/InkHUD.h" #include "graphics/niche/Inputs/TwoButton.h" +#endif -#include "einkDetect.h" // Detect display model at runtime +template class HeltecWPPanel : public Base +{ + protected: + SPIClass *beginSpi() override + { + auto *hspi = new SPIClass(HSPI); + hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); + return hspi; + } +}; +struct HeltecWPDetectedDriver { + NicheGraphics::Drivers::EInk *driver; + uint8_t fastPerFull; + float stress; + uint8_t rotation; +}; + +static HeltecWPDetectedDriver detectAndCreateHeltecWPDriver() +{ + EInkDetectionResult model = detectEInk(); + HeltecWPDetectedDriver out{nullptr, 10, 1.5f, 3}; + if (model == EInkDetectionResult::LCMEN213EFC1) { + auto *p = new HeltecWPPanel(); + out.driver = p->create(); + out.rotation = p->rotation(); + out.fastPerFull = 10; + out.stress = 1.5f; + } else { + auto *p = new HeltecWPPanel(); + out.driver = p->create(); + out.rotation = p->rotation(); + out.fastPerFull = 15; + out.stress = 3.0f; + } + return out; +} + +#ifdef MESHTASTIC_INCLUDE_INKHUD void setupNicheGraphics() { using namespace NicheGraphics; - // Detect E-Ink Model - // ------------------- - - EInkDetectionResult displayModel = detectEInk(); - - // SPI - // ----------------------------- - - // Display is connected to HSPI - SPIClass *hspi = new SPIClass(HSPI); - hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); - - // E-Ink Driver - // ----------------------------- - - Drivers::EInk *driver; - - if (displayModel == EInkDetectionResult::LCMEN213EFC1) // V1.1 - driver = new Drivers::LCMEN213EFC1; - else if (displayModel == EInkDetectionResult::E0213A367) // V1.1.1, V1.2 - driver = new Drivers::E0213A367; - - driver->begin(hspi, PIN_EINK_DC, PIN_EINK_CS, PIN_EINK_BUSY, PIN_EINK_RES); - - // InkHUD - // ---------------------------- + HeltecWPDetectedDriver detected = detectAndCreateHeltecWPDriver(); InkHUD::InkHUD *inkhud = InkHUD::InkHUD::getInstance(); + inkhud->setDriver(detected.driver); + inkhud->setDisplayResilience(detected.fastPerFull, detected.stress); - // Set the E-Ink driver - inkhud->setDriver(driver); - - // Set how many FAST updates per FULL update - // Set how unhealthy additional FAST updates beyond this number are - - if (displayModel == EInkDetectionResult::LCMEN213EFC1) // V1.1 (unmarked) - inkhud->setDisplayResilience(10, 1.5); - else if (displayModel == EInkDetectionResult::E0213A367) // V1.1.1, V1.2 - inkhud->setDisplayResilience(15, 3); - - // Select fonts InkHUD::Applet::fontLarge = FREESANS_12PT_WIN1252; InkHUD::Applet::fontMedium = FREESANS_9PT_WIN1252; InkHUD::Applet::fontSmall = FREESANS_6PT_WIN1252; - // Customize default settings - inkhud->persistence->settings.userTiles.maxCount = 2; // How many tiles can the display handle? - inkhud->persistence->settings.rotation = 3; // 270 degrees clockwise - inkhud->persistence->settings.userTiles.count = 1; // One tile only by default, keep things simple for new users + inkhud->persistence->settings.userTiles.maxCount = 2; + inkhud->persistence->settings.rotation = 3; + inkhud->persistence->settings.userTiles.count = 1; - // Pick applets - // Note: order of applets determines priority of "auto-show" feature - inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); // Activated, autoshown - inkhud->addApplet("DMs", new InkHUD::DMApplet); // - - inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // - - inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // - - inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated - inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // - - inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, not autoshown, default on tile 0 - inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet, false, false); // - + inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); + inkhud->addApplet("DMs", new InkHUD::DMApplet); + inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); + inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); + inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); + inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); + inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); + inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet, false, false); - // Start running InkHUD inkhud->begin(); - // Buttons - // -------------------------- - - Inputs::TwoButton *buttons = Inputs::TwoButton::getInstance(); // Shared NicheGraphics component - - // #0: Main User Button + Inputs::TwoButton *buttons = Inputs::TwoButton::getInstance(); buttons->setWiring(0, Inputs::TwoButton::getUserButtonPin()); buttons->setHandlerShortPress(0, [inkhud]() { inkhud->shortpress(); }); buttons->setHandlerLongPress(0, [inkhud]() { inkhud->longpress(); }); - - // No aux button on this board - - // Begin handling button events buttons->start(); } +#else +void setupNicheGraphics() {} +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + HeltecWPDetectedDriver detected = detectAndCreateHeltecWPDriver(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(detected.driver, detected.rotation); + display->setDisplayResilience(detected.fastPerFull, detected.stress); + return display; +} #endif + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/esp32s3/heltec_wireless_paper/platformio.ini b/variants/esp32s3/heltec_wireless_paper/platformio.ini index acd619c48..f84538e2b 100644 --- a/variants/esp32s3/heltec_wireless_paper/platformio.ini +++ b/variants/esp32s3/heltec_wireless_paper/platformio.ini @@ -1,4 +1,3 @@ -; Using the original screen class [env:heltec-wireless-paper] custom_meshtastic_hw_model = 49 custom_meshtastic_hw_model_slug = HELTEC_WIRELESS_PAPER @@ -11,26 +10,20 @@ custom_meshtastic_tags = Heltec custom_meshtastic_partition_scheme = 8MB custom_meshtastic_has_ink_hud = true -extends = esp32s3_base +extends = esp32s3_base, niche board = heltec_wifi_lora_32_V3 board_build.partitions = default_8MB.csv -build_flags = - ${esp32s3_base.build_flags} +build_src_filter = + ${esp32s3_base.build_src_filter} + ${niche.build_src_filter} +build_flags = + ${esp32s3_base.build_flags} + ${niche.build_flags} -I variants/esp32s3/heltec_wireless_paper - -D HELTEC_WIRELESS_PAPER - -D GXEPD2_DRIVER_0=GxEPD2_213_FC1 - -D GXEPD2_DRIVER_1=GxEPD2_213_E0213A367 - -D EINK_WIDTH=250 - -D EINK_HEIGHT=122 + -D HELTEC_WIRELESS_PAPER -D USE_EINK - -D USE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk - -D EINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted - -D EINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. - -D EINK_HASQUIRK_GHOSTING ; Display model is identified as "prone to ghosting" lib_deps = ${esp32s3_base.lib_deps} - # renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master - https://github.com/meshtastic/GxEPD2/archive/c7eb4c3c167cf396ef4f541cc5d4c6aa42f3c46b.zip upload_speed = 115200 [env:heltec-wireless-paper-inkhud] diff --git a/variants/esp32s3/heltec_wireless_paper_v1/nicheGraphics.h b/variants/esp32s3/heltec_wireless_paper_v1/nicheGraphics.h new file mode 100644 index 000000000..bfd65c94a --- /dev/null +++ b/variants/esp32s3/heltec_wireless_paper_v1/nicheGraphics.h @@ -0,0 +1,38 @@ +/* + +NicheGraphics setup for Heltec Wireless Paper V1.0 (DEPG0213BNS800 on HSPI). + +*/ + +#pragma once + +#include "configuration.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Panels/DEPG0213BNS800.h" + +class HeltecWirelessPaperV1Panel : public NicheGraphics::Panels::DEPG0213BNS800 +{ + protected: + SPIClass *beginSpi() override + { + auto *hspi = new SPIClass(HSPI); + hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); + return hspi; + } +}; + +void setupNicheGraphics() {} + +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new HeltecWirelessPaperV1Panel(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panel->rotation()); + display->setDisplayResilience(5, 1.5f); + return display; +} + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/esp32s3/heltec_wireless_paper_v1/platformio.ini b/variants/esp32s3/heltec_wireless_paper_v1/platformio.ini index b34adfb17..3c6538c34 100644 --- a/variants/esp32s3/heltec_wireless_paper_v1/platformio.ini +++ b/variants/esp32s3/heltec_wireless_paper_v1/platformio.ini @@ -9,22 +9,18 @@ custom_meshtastic_images = heltec-wireless-paper-v1_0.svg custom_meshtastic_tags = Heltec custom_meshtastic_partition_scheme = 8MB -extends = esp32s3_base +extends = esp32s3_base, niche board_level = extra board = heltec_wifi_lora_32_V3 board_build.partitions = default_8MB.csv -build_flags = - ${esp32s3_base.build_flags} - -I variants/esp32s3/heltec_wireless_paper_v1 +build_src_filter = + ${esp32s3_base.build_src_filter} + ${niche.build_src_filter} +build_flags = + ${esp32s3_base.build_flags} + ${niche.build_flags} + -I variants/esp32s3/heltec_wireless_paper_v1 -D HELTEC_WIRELESS_PAPER_V1_0 - -D EINK_DISPLAY_MODEL=GxEPD2_213_BN - -D EINK_WIDTH=250 - -D EINK_HEIGHT=122 - -D USE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk - -D EINK_LIMIT_FASTREFRESH=5 ; How many consecutive fast-refreshes are permitted - -D EINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated lib_deps = ${esp32s3_base.lib_deps} - # renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master - https://github.com/meshtastic/GxEPD2/archive/c7eb4c3c167cf396ef4f541cc5d4c6aa42f3c46b.zip upload_speed = 115200 diff --git a/variants/esp32s3/mini-epaper-s3/nicheGraphics.h b/variants/esp32s3/mini-epaper-s3/nicheGraphics.h index 0cbd6a192..538530563 100644 --- a/variants/esp32s3/mini-epaper-s3/nicheGraphics.h +++ b/variants/esp32s3/mini-epaper-s3/nicheGraphics.h @@ -1,58 +1,79 @@ +/* + +NicheGraphics setup for LILYGO Mini ePaper S3 (GDEW0102T4 1.02" on HSPI). +Shared by both BaseUI and InkHUD envs. + +*/ + #pragma once #include "configuration.h" #ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS -// InkHUD-specific components -#include "graphics/niche/InkHUD/InkHUD.h" +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Drivers/GDEW0102T4.h" +#include "graphics/eink/Panels/GDEW0102T4.h" -// Applets -#include "graphics/niche/InkHUD/Applet.h" -#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h" -#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h" -#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h" -#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h" -#include "graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" -#include "graphics/niche/InkHUD/SystemApplet.h" - -// Shared NicheGraphics components -#include "graphics/niche/Drivers/EInk/GDEW0102T4.h" +#ifdef MESHTASTIC_INCLUDE_INKHUD +#include "graphics/niche/Applet.h" +#include "graphics/niche/Applets/User/AllMessage/AllMessageApplet.h" +#include "graphics/niche/Applets/User/DM/DMApplet.h" +#include "graphics/niche/Applets/User/FavoritesMap/FavoritesMapApplet.h" +#include "graphics/niche/Applets/User/Heard/HeardApplet.h" +#include "graphics/niche/Applets/User/Positions/PositionsApplet.h" +#include "graphics/niche/Applets/User/RecentsList/RecentsListApplet.h" +#include "graphics/niche/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" +#include "graphics/niche/InkHUD.h" #include "graphics/niche/Inputs/TwoButtonExtended.h" +#include "graphics/niche/SystemApplet.h" +#endif +class MiniEpaperS3Panel : public NicheGraphics::Panels::GDEW0102T4 +{ + public: + uint8_t rotation() const override { return 0; } + + protected: + void prePowerOn() override + { + pinMode(PIN_EINK_EN, OUTPUT); + digitalWrite(PIN_EINK_EN, HIGH); + delay(10); + } + SPIClass *beginSpi() override + { + auto *hspi = new SPIClass(HSPI); + hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); + return hspi; + } +}; + +static NicheGraphics::Drivers::EInk *makeMiniEpaperS3Driver() +{ + auto *panel = new MiniEpaperS3Panel(); + auto *driver = panel->create(); + // Tuned fast-refresh values: reg30 reg50 reg82 lutW2 lutB2 = 11 F2 04 11 0D + static_cast(driver)->setFastConfig({0x11, 0xF2, 0x04, 0x11, 0x0D}); + return driver; +} + +#ifdef MESHTASTIC_INCLUDE_INKHUD void setupNicheGraphics() { using namespace NicheGraphics; - // Power-enable the E-Ink panel on this board before any SPI traffic. - pinMode(PIN_EINK_EN, OUTPUT); - digitalWrite(PIN_EINK_EN, HIGH); - delay(10); - - // Display uses HSPI on this board - SPIClass *hspi = new SPIClass(HSPI); - hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); - - Drivers::GDEW0102T4 *displayDriver = new Drivers::GDEW0102T4; - displayDriver->begin(hspi, PIN_EINK_DC, PIN_EINK_CS, PIN_EINK_BUSY, PIN_EINK_RES); - // Tuned fast-refresh values reg30 reg50 reg82 lutW2 lutB2 = 11 F2 04 11 0D - displayDriver->setFastConfig({0x11, 0xF2, 0x04, 0x11, 0x0D}); - Drivers::EInk *driver = displayDriver; + Drivers::EInk *driver = makeMiniEpaperS3Driver(); InkHUD::InkHUD *inkhud = InkHUD::InkHUD::getInstance(); inkhud->setDriver(driver); - // Slightly stricter FAST/FULL inkhud->setDisplayResilience(5, 1.5); inkhud->twoWayRocker = true; - // Fonts InkHUD::Applet::fontLarge = FREESANS_9PT_WIN1252; InkHUD::Applet::fontMedium = FREESANS_6PT_WIN1252; InkHUD::Applet::fontSmall = FREESANS_6PT_WIN1252; - // Small display defaults inkhud->persistence->settings.rotation = 0; inkhud->persistence->settings.userTiles.maxCount = 1; inkhud->persistence->settings.userTiles.count = 1; @@ -60,41 +81,28 @@ void setupNicheGraphics() inkhud->persistence->settings.joystick.aligned = true; inkhud->persistence->settings.optionalMenuItems.nextTile = false; - // Pick applets - // Note: order of applets determines priority of "auto-show" feature - inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, false, false); // - - inkhud->addApplet("DMs", new InkHUD::DMApplet, true, false); // Activated, not autoshown - inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0), true, true); // Activated, Autoshown - inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1), false, false); // - - inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated - inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet, false, false); // - - inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet, false, false); // - - inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, not autoshown, default on tile 0 - // Start running InkHUD + inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, false, false); + inkhud->addApplet("DMs", new InkHUD::DMApplet, true, false); + inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0), true, true); + inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1), false, false); + inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); + inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet, false, false); + inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet, false, false); + inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); inkhud->begin(); - // Enforce two-way rocker behavior regardless of persisted settings. inkhud->persistence->settings.joystick.enabled = true; inkhud->persistence->settings.joystick.aligned = true; inkhud->persistence->settings.optionalMenuItems.nextTile = false; - // Inputs Inputs::TwoButtonExtended *buttons = Inputs::TwoButtonExtended::getInstance(); - - // Center press (boot button) buttons->setWiring(0, INPUTDRIVER_TWO_WAY_ROCKER_BTN, true); - // Match baseUI encoder long-press feel. buttons->setTiming(0, 75, 300); buttons->setHandlerShortPress(0, [inkhud]() { inkhud->shortpress(); }); buttons->setHandlerLongPress(0, [inkhud]() { inkhud->longpress(); }); - // LEFT rocker pin is IO4; RIGHT rocker pin is IO3. buttons->setTwoWayRockerWiring(INPUTDRIVER_TWO_WAY_ROCKER_LEFT, INPUTDRIVER_TWO_WAY_ROCKER_RIGHT, true); buttons->setJoystickDebounce(50); - - // Two-way rocker behavior: - // - when a system applet is handling input (menu, tips, etc): LEFT=up, RIGHT=down - // - otherwise: LEFT=previous applet, RIGHT=next applet buttons->setTwoWayRockerPressHandlers( [inkhud]() { bool systemHandlingInput = false; @@ -104,7 +112,6 @@ void setupNicheGraphics() break; } } - if (systemHandlingInput) inkhud->navUp(); else @@ -118,7 +125,6 @@ void setupNicheGraphics() break; } } - if (systemHandlingInput) inkhud->navDown(); else @@ -127,5 +133,16 @@ void setupNicheGraphics() buttons->start(); } +#else +void setupNicheGraphics() {} +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + NicheGraphics::Drivers::EInk *driver = makeMiniEpaperS3Driver(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, 0); + display->setDisplayResilience(3, 1.5f); + return display; +} #endif + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/esp32s3/mini-epaper-s3/platformio.ini b/variants/esp32s3/mini-epaper-s3/platformio.ini index 3e4802319..103358db7 100644 --- a/variants/esp32s3/mini-epaper-s3/platformio.ini +++ b/variants/esp32s3/mini-epaper-s3/platformio.ini @@ -10,28 +10,23 @@ custom_meshtastic_tags = LilyGo custom_meshtastic_requires_dfu = no custom_meshtastic_has_mui = false -extends = esp32s3_base +extends = esp32s3_base, niche board = mini-epaper-s3 board_check = true upload_protocol = esptool -build_flags = +build_src_filter = + ${esp32s3_base.build_src_filter} + ${niche.build_src_filter} +build_flags = ${esp32s3_base.build_flags} + ${niche.build_flags} -I variants/esp32s3/mini-epaper-s3 -D MINI_EPAPER_S3 -D USE_EINK - -D EINK_DISPLAY_MODEL=GxEPD2_102 - -D EINK_WIDTH=128 - -D EINK_HEIGHT=80 - -D USE_EINK_DYNAMICDISPLAY - -D EINK_LIMIT_FASTREFRESH=3 - -D EINK_BACKGROUND_USES_FAST - -D EINK_HASQUIRK_GHOSTING lib_deps = ${esp32s3_base.lib_deps} - # renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master - https://github.com/meshtastic/GxEPD2/archive/c7eb4c3c167cf396ef4f541cc5d4c6aa42f3c46b.zip # renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib lewisxhe/SensorLib@0.3.4 diff --git a/variants/esp32s3/t-deck-pro/nicheGraphics.h b/variants/esp32s3/t-deck-pro/nicheGraphics.h new file mode 100644 index 000000000..f897f6151 --- /dev/null +++ b/variants/esp32s3/t-deck-pro/nicheGraphics.h @@ -0,0 +1,39 @@ +/* + +NicheGraphics setup for LILYGO T-Deck Pro (GDEQ031T10 3.1" 240x320 on the default SPI bus). + +*/ + +#pragma once + +#include "configuration.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Panels/GDEQ031T10.h" + +class TDeckProPanel : public NicheGraphics::Panels::GDEQ031T10 +{ + protected: + SPIClass *beginSpi() override + { + // Old GxEPD2 path used the global SPI bus on this board. + SPI.begin(); + return &SPI; + } +}; + +void setupNicheGraphics() {} + +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new TDeckProPanel(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panel->rotation()); + // T-Deck Pro types frequently; allow more FAST refreshes between FULLs than the default. + display->setDisplayResilience(15, 1.5f); + return display; +} + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/esp32s3/t-deck-pro/platformio.ini b/variants/esp32s3/t-deck-pro/platformio.ini index 93ef8babf..94b0de6d4 100644 --- a/variants/esp32s3/t-deck-pro/platformio.ini +++ b/variants/esp32s3/t-deck-pro/platformio.ini @@ -11,7 +11,7 @@ custom_meshtastic_requires_dfu = true custom_meshtastic_partition_scheme = 16MB custom_meshtastic_has_mui = false -extends = esp32s3_base +extends = esp32s3_base, niche board = t-deck-pro board_check = true upload_protocol = esptool @@ -19,22 +19,18 @@ upload_protocol = esptool build_src_filter = ${esp32s3_base.build_src_filter} +<../variants/esp32s3/t-deck-pro> + ${niche.build_src_filter} -build_flags = +build_flags = ${esp32s3_base.build_flags} -I variants/esp32s3/t-deck-pro + ${niche.build_flags} -D T_DECK_PRO -D USE_EINK - -D EINK_DISPLAY_MODEL=GxEPD2_310_GDEQ031T10 -D EINK_WIDTH=240 -D EINK_HEIGHT=320 - ;-D USE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk - -D EINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted - -D EINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated lib_deps = ${esp32s3_base.lib_deps} - # renovate: datasource=custom.pio depName=GxEPD2 packageName=zinggjm/library/GxEPD2 - zinggjm/GxEPD2@1.6.8 # renovate: datasource=git-refs depName=CSE_Touch packageName=https://github.com/CIRCUITSTATE/CSE_Touch gitBranch=main https://github.com/CIRCUITSTATE/CSE_Touch/archive/b44f23b6f870b848f1fbe453c190879bc6cfaafa.zip # renovate: datasource=github-tags depName=CSE_CST328 packageName=CIRCUITSTATE/CSE_CST328 diff --git a/variants/esp32s3/t5s3_epaper/nicheGraphics.h b/variants/esp32s3/t5s3_epaper/nicheGraphics.h index 699a82de0..1c362686a 100644 --- a/variants/esp32s3/t5s3_epaper/nicheGraphics.h +++ b/variants/esp32s3/t5s3_epaper/nicheGraphics.h @@ -1,123 +1,117 @@ /* -Most of the Meshtastic firmware uses preprocessor macros throughout the code to support different hardware variants. -NicheGraphics attempts a different approach: +NicheGraphics setup for LILYGO T5 ePaper Pro (ED047TC1, 960x540, parallel via FastEPD). -Per-device config takes place in this setupNicheGraphics() method -(And a small amount in platformio.ini) +Three envs share this directory: + - t5s3-epaper-v1 (BaseUI, BB_PANEL_LILYGO_T5PRO) + - t5s3-epaper-v2 (BaseUI, BB_PANEL_LILYGO_T5PRO_V2 + GPIO-expander setup) + - t5s3_epaper_inkhud (InkHUD on V2) -This file sets up InkHUD for Heltec VM-E290. -Different NicheGraphics UIs and different hardware variants will each have their own setup procedure. +The active panel is selected by the T5_S3_EPAPER_PRO_V1 / T5_S3_EPAPER_PRO_V2 macros from platformio.ini. */ #pragma once #include "configuration.h" -#include "mesh/MeshModule.h" #ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS -// InkHUD-specific components -// --------------------------- -// #include "graphics/niche/InkHUD/InkHUD.h" -#include "graphics/niche/InkHUD/WindowManager.h" +#include "FastEPD.h" -// Applets -#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h" -#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h" -#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h" -#include "graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Drivers/EInkParallel.h" +#include "graphics/eink/Panels/T5Epaper.h" -// Shared NicheGraphics components -// -------------------------------- -#include "graphics/niche/Drivers/Backlight/LatchingBacklight.h" -#include "graphics/niche/Drivers/EInk/DEPG0290BNS800.h" -#include "graphics/niche/Inputs/TwoButton.h" +#ifdef MESHTASTIC_INCLUDE_INKHUD +#include "graphics/niche/Applets/User/AllMessage/AllMessageApplet.h" +#include "graphics/niche/Applets/User/DM/DMApplet.h" +#include "graphics/niche/Applets/User/Heard/HeardApplet.h" +#include "graphics/niche/Applets/User/Positions/PositionsApplet.h" +#include "graphics/niche/Applets/User/RecentsList/RecentsListApplet.h" +#include "graphics/niche/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" +#include "graphics/niche/InkHUD.h" +#endif +#if defined(T5_S3_EPAPER_PRO_V2) +class T5V2Driver : public NicheGraphics::Drivers::EInkParallel +{ + public: + T5V2Driver() : EInkParallel(960, 540, BB_PANEL_LILYGO_T5PRO_V2, 28000000) {} + + protected: + void postPanelInit() override + { + // V2 uses a GPIO expander; raise port-0 pins 0..7 high (as the legacy driver did). + FASTEPD *epd = fastEpd(); + for (int i = 0; i < 8; i++) { + epd->ioPinMode(i, OUTPUT); + epd->ioWrite(i, HIGH); + } + } +}; +#elif defined(T5_S3_EPAPER_PRO_V1) +class T5V1Driver : public NicheGraphics::Drivers::EInkParallel +{ + public: + T5V1Driver() : EInkParallel(960, 540, BB_PANEL_LILYGO_T5PRO, 28000000) {} +}; +#else +#error "t5s3_epaper requires T5_S3_EPAPER_PRO_V1 or T5_S3_EPAPER_PRO_V2" +#endif + +class T5EpaperPanel : public NicheGraphics::Panels::T5EpaperPanel +{ + protected: + NicheGraphics::Drivers::EInkParallel *makeDriver() override + { +#if defined(T5_S3_EPAPER_PRO_V2) + return new T5V2Driver(); +#else + return new T5V1Driver(); +#endif + } +}; + +#ifdef MESHTASTIC_INCLUDE_INKHUD void setupNicheGraphics() { using namespace NicheGraphics; - // SPI - // ----------------------------- - - // Display is connected to HSPI - SPIClass *hspi = new SPIClass(HSPI); - hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); - - // E-Ink Driver - // ----------------------------- - - // Use E-Ink driver - Drivers::EInk *driver = new Drivers::DEPG0290BNS800; - driver->begin(hspi, PIN_EINK_DC, PIN_EINK_CS, PIN_EINK_BUSY); - - // InkHUD - // ---------------------------- + auto *panel = new T5EpaperPanel(); + Drivers::EInk *driver = panel->create(); InkHUD::InkHUD *inkhud = InkHUD::InkHUD::getInstance(); - - // Set the driver inkhud->setDriver(driver); + inkhud->setDisplayResilience(20, 1.5); - // Set how many FAST updates per FULL update - // Set how unhealthy additional FAST updates beyond this number are - inkhud->setDisplayResilience(7, 1.5); - - // Prepare fonts - InkHUD::Applet::fontLarge = FREESANS_9PT_WIN1252; + InkHUD::Applet::fontLarge = FREESANS_12PT_WIN1252; + InkHUD::Applet::fontMedium = FREESANS_9PT_WIN1252; InkHUD::Applet::fontSmall = FREESANS_6PT_WIN1252; - // Init settings, and customize defaults - inkhud->persistence->settings.userTiles.maxCount = 2; // How many tiles can the display handle? - inkhud->persistence->settings.rotation = 1; // 90 degrees clockwise - inkhud->persistence->settings.userTiles.count = 1; // One tile only by default, keep things simple for new users - inkhud->persistence->settings.optionalMenuItems.nextTile = false; // Behavior handled by aux button instead - inkhud->persistence->settings.optionalFeatures.batteryIcon = true; // Device definitely has a battery + inkhud->persistence->settings.userTiles.maxCount = 4; + inkhud->persistence->settings.userTiles.count = 1; - // Setup backlight - // Note: AUX button behavior configured further down - Drivers::LatchingBacklight *backlight = Drivers::LatchingBacklight::getInstance(); - backlight->setPin(PIN_EINK_EN); - - // Pick applets - // Note: order of applets determines priority of "auto-show" feature - // Optional arguments for defaults: - // - is activated? - // - is autoshown? - // - is foreground on a specific tile (index)? - inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); // Activated, autoshown + inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); inkhud->addApplet("DMs", new InkHUD::DMApplet); inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); - inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); - inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated + inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); - inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, not autoshown, default on tile 0 - // inkhud->addApplet("Basic", new InkHUD::BasicExampleApplet); - // inkhud->addApplet("NewMsg", new InkHUD::NewMsgExampleApplet); + inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); - // Start running InkHUD inkhud->begin(); - - // Buttons - // -------------------------- - - Inputs::TwoButton *buttons = Inputs::TwoButton::getInstance(); // A shared NicheGraphics component - - // Setup the main user button (0) - buttons->setWiring(0, BUTTON_PIN); - buttons->setHandlerShortPress(0, []() { InkHUD::InkHUD::getInstance()->shortpress(); }); - buttons->setHandlerLongPress(0, []() { InkHUD::InkHUD::getInstance()->longpress(); }); - - // Setup the aux button (1) - // Bonus feature of VME290 - buttons->setWiring(1, BUTTON_PIN_SECONDARY); - buttons->setHandlerShortPress(1, []() { InkHUD::InkHUD::getInstance()->nextTile(); }); - - buttons->start(); } +#else +void setupNicheGraphics() {} -#endif \ No newline at end of file +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new T5EpaperPanel(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, 0); + display->setDisplayResilience(20, 1.5f); + return display; +} +#endif + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/esp32s3/t5s3_epaper/platformio.ini b/variants/esp32s3/t5s3_epaper/platformio.ini index bad36706c..da0ee467f 100644 --- a/variants/esp32s3/t5s3_epaper/platformio.ini +++ b/variants/esp32s3/t5s3_epaper/platformio.ini @@ -1,26 +1,25 @@ [t5s3_epaper_base] -extends = esp32s3_base +extends = esp32s3_base, niche board = t5-epaper-s3 board_build.partition = default_16MB.csv board_check = true upload_protocol = esptool build_flags = -fno-strict-aliasing - ${esp32_base.build_flags} + ${esp32_base.build_flags} + ${niche.build_flags} -I variants/esp32s3/t5s3_epaper -D T5_S3_EPAPER_PRO -D USE_EINK - -D USE_EINK_PARALLELDISPLAY + -D NICHE_HAS_FASTEPD ; activate NicheGraphics parallel-EPD driver (FastEPD) -D PRIVATE_HW -D TOUCH_THRESHOLD_X=60 -D TOUCH_THRESHOLD_Y=40 -D TIME_LONG_PRESS=500 -; -D EINK_LIMIT_GHOSTING_PX=5000 - -D EPD_FULLSLOW_PERIOD=100 - -D FAST_EPD_PARTIAL_UPDATE_BUG ; use rect area update instead of partial -build_src_filter = +build_src_filter = ${esp32s3_base.build_src_filter} +<../variants/esp32s3/t5s3_epaper> + ${niche.build_src_filter} lib_deps = ${esp32s3_base.lib_deps} # renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib diff --git a/variants/esp32s3/tlora_t3s3_epaper/nicheGraphics.h b/variants/esp32s3/tlora_t3s3_epaper/nicheGraphics.h index 73cc2e235..271cdaf14 100644 --- a/variants/esp32s3/tlora_t3s3_epaper/nicheGraphics.h +++ b/variants/esp32s3/tlora_t3s3_epaper/nicheGraphics.h @@ -1,91 +1,90 @@ +/* + +NicheGraphics setup for LILYGO T-LoRa T3-S3 ePaper (DEPG0213BNS800 on HSPI). +Shared by both BaseUI and InkHUD envs. + +*/ + #pragma once #include "configuration.h" #ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS -// InkHUD-specific components -// --------------------------- -#include "graphics/niche/InkHUD/InkHUD.h" +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Panels/DEPG0213BNS800.h" -// Applets -#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h" -#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h" -#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h" -#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h" -#include "graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" - -// Shared NicheGraphics components -// -------------------------------- -#include "graphics/niche/Drivers/EInk/DEPG0213BNS800.h" +#ifdef MESHTASTIC_INCLUDE_INKHUD +#include "graphics/niche/Applets/User/AllMessage/AllMessageApplet.h" +#include "graphics/niche/Applets/User/DM/DMApplet.h" +#include "graphics/niche/Applets/User/FavoritesMap/FavoritesMapApplet.h" +#include "graphics/niche/Applets/User/Heard/HeardApplet.h" +#include "graphics/niche/Applets/User/Positions/PositionsApplet.h" +#include "graphics/niche/Applets/User/RecentsList/RecentsListApplet.h" +#include "graphics/niche/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" +#include "graphics/niche/InkHUD.h" #include "graphics/niche/Inputs/TwoButton.h" +#endif +class TLoraT3S3Panel : public NicheGraphics::Panels::DEPG0213BNS800 +{ + protected: + SPIClass *beginSpi() override + { + auto *hspi = new SPIClass(HSPI); + hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); + return hspi; + } +}; + +#ifdef MESHTASTIC_INCLUDE_INKHUD void setupNicheGraphics() { using namespace NicheGraphics; - // SPI - // ----------------------------- - - // Display is connected to HSPI - SPIClass *hspi = new SPIClass(HSPI); - hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); - - // E-Ink Driver - // ----------------------------- - - Drivers::EInk *driver = new Drivers::DEPG0213BNS800; - driver->begin(hspi, PIN_EINK_DC, PIN_EINK_CS, PIN_EINK_BUSY, PIN_EINK_RES); - - // InkHUD - // ---------------------------- + auto *panel = new TLoraT3S3Panel(); + Drivers::EInk *driver = panel->create(); InkHUD::InkHUD *inkhud = InkHUD::InkHUD::getInstance(); - - // Set the driver inkhud->setDriver(driver); - - // Set how many FAST updates per FULL update - // Set how unhealthy additional FAST updates beyond this number are inkhud->setDisplayResilience(15, 1.5); - // Select fonts InkHUD::Applet::fontLarge = FREESANS_12PT_WIN1252; InkHUD::Applet::fontMedium = FREESANS_9PT_WIN1252; InkHUD::Applet::fontSmall = FREESANS_6PT_WIN1252; - // Customize default settings - inkhud->persistence->settings.userTiles.maxCount = 2; // How many tiles can the display handle? - inkhud->persistence->settings.rotation = 3; // 270 degrees clockwise - inkhud->persistence->settings.userTiles.count = 1; // One tile only by default, keep things simple for new users + inkhud->persistence->settings.userTiles.maxCount = 2; + inkhud->persistence->settings.rotation = 3; + inkhud->persistence->settings.userTiles.count = 1; - // Pick applets - // Note: order of applets determines priority of "auto-show" feature - inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); // Activated, autoshown - inkhud->addApplet("DMs", new InkHUD::DMApplet); // - - inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // - - inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // - - inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated - inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet); // - - inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // - - inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, not autoshown, default on tile 0 + inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); + inkhud->addApplet("DMs", new InkHUD::DMApplet); + inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); + inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); + inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); + inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet); + inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); + inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); - // Start running InkHUD inkhud->begin(); - // Buttons - // -------------------------- - - Inputs::TwoButton *buttons = Inputs::TwoButton::getInstance(); // Shared NicheGraphics component - - // Setup the main user button + Inputs::TwoButton *buttons = Inputs::TwoButton::getInstance(); buttons->setWiring(0, Inputs::TwoButton::getUserButtonPin(), true); buttons->setHandlerShortPress(0, [inkhud]() { inkhud->shortpress(); }); buttons->setHandlerLongPress(0, [inkhud]() { inkhud->longpress(); }); - buttons->start(); } +#else +void setupNicheGraphics() {} +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new TLoraT3S3Panel(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panel->rotation()); + display->setDisplayResilience(15, 1.5f); + return display; +} #endif + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/esp32s3/tlora_t3s3_epaper/platformio.ini b/variants/esp32s3/tlora_t3s3_epaper/platformio.ini index 8f764bbe6..156824ea4 100644 --- a/variants/esp32s3/tlora_t3s3_epaper/platformio.ini +++ b/variants/esp32s3/tlora_t3s3_epaper/platformio.ini @@ -10,29 +10,23 @@ custom_meshtastic_tags = LilyGo custom_meshtastic_requires_dfu = true custom_meshtastic_has_ink_hud = true -extends = esp32s3_base +extends = esp32s3_base, niche board = tlora-t3s3-v1 board_check = true upload_protocol = esptool -build_flags = +build_src_filter = + ${esp32s3_base.build_src_filter} + ${niche.build_src_filter} +build_flags = ${esp32s3_base.build_flags} + ${niche.build_flags} -D TLORA_T3S3_EPAPER -I variants/esp32s3/tlora_t3s3_epaper -DUSE_EINK - -DEINK_DISPLAY_MODEL=GxEPD2_213_BN - -DEINK_WIDTH=250 - -DEINK_HEIGHT=122 - -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk - -DEINK_LIMIT_FASTREFRESH=20 ; How many consecutive fast-refreshes are permitted //20 - -DEINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates //30 - -DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates - -DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. lib_deps = ${esp32s3_base.lib_deps} - # renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master - https://github.com/meshtastic/GxEPD2/archive/c7eb4c3c167cf396ef4f541cc5d4c6aa42f3c46b.zip [env:tlora-t3s3-epaper-inkhud] extends = esp32s3_base, inkhud diff --git a/variants/nrf52840/Dongle_nRF52840-pca10059-v1/nicheGraphics.h b/variants/nrf52840/Dongle_nRF52840-pca10059-v1/nicheGraphics.h new file mode 100644 index 000000000..5be58443f --- /dev/null +++ b/variants/nrf52840/Dongle_nRF52840-pca10059-v1/nicheGraphics.h @@ -0,0 +1,35 @@ +/* + +NicheGraphics setup for the Nordic pca10059 dongle + 4.2" e-ink DIY build +(as shipped: GDEW042M01, 400x300, UC8276 controller). + +No UC8276 driver exists in the NicheGraphics tree, so this uses the +HINK_E042A87 profile (same 400x300 geometry, SSD1680-based). The command +sets differ; if the panel misbehaves, drop in a UC8276 profile here. + +Uses SPI1 per variant.h (SCLK=P0.09, MOSI=P0.10), which is the default +for nRF52 panel profiles. + +*/ + +#pragma once + +#include "configuration.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Panels/HINK_E042A87.h" + +void setupNicheGraphics() {} + +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new NicheGraphics::Panels::HINK_E042A87(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panel->rotation()); + display->setDisplayResilience(10, 2.0f); + return display; +} + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/nrf52840/Dongle_nRF52840-pca10059-v1/platformio.ini b/variants/nrf52840/Dongle_nRF52840-pca10059-v1/platformio.ini index fd159a6d2..2ce3c0f65 100644 --- a/variants/nrf52840/Dongle_nRF52840-pca10059-v1/platformio.ini +++ b/variants/nrf52840/Dongle_nRF52840-pca10059-v1/platformio.ini @@ -1,16 +1,12 @@ [env:pca10059_diy_eink] board_level = extra -extends = nrf52840_base +extends = nrf52840_base, niche board = nordic_pca10059 build_flags = ${nrf52840_base.build_flags} + ${niche.build_flags} -Ivariants/nrf52840/Dongle_nRF52840-pca10059-v1 -D NORDIC_PCA10059 - -DEINK_DISPLAY_MODEL=GxEPD2_420_M01 - -DEINK_WIDTH=300 - -DEINK_HEIGHT=400 -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/Dongle_nRF52840-pca10059-v1> -lib_deps = +build_src_filter = ${nrf52_base.build_src_filter} ${niche.build_src_filter} +<../variants/nrf52840/Dongle_nRF52840-pca10059-v1> +lib_deps = ${nrf52840_base.lib_deps} - # renovate: datasource=custom.pio depName=GxEPD2 packageName=zinggjm/library/GxEPD2 - zinggjm/GxEPD2@1.6.8 debug_tool = jlink diff --git a/variants/nrf52840/ELECROW-ThinkNode-M1/nicheGraphics.h b/variants/nrf52840/ELECROW-ThinkNode-M1/nicheGraphics.h index 242e5ae49..6390c25c2 100644 --- a/variants/nrf52840/ELECROW-ThinkNode-M1/nicheGraphics.h +++ b/variants/nrf52840/ELECROW-ThinkNode-M1/nicheGraphics.h @@ -1,120 +1,93 @@ +/* + +NicheGraphics setup for Elecrow ThinkNode-M1 (GDEY0154D67 1.54" on SPI1). +Shared by both BaseUI and InkHUD envs. + +*/ + #pragma once #include "configuration.h" #ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS -// InkHUD-specific components -// --------------------------- -#include "graphics/niche/InkHUD/InkHUD.h" +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Backlight/LatchingBacklight.h" +#include "graphics/eink/Panels/GDEY0154D67.h" -// Applets -#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h" -#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h" -#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h" -#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h" -#include "graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" - -// Shared NicheGraphics components -// -------------------------------- -#include "graphics/niche/Drivers/Backlight/LatchingBacklight.h" -#include "graphics/niche/Drivers/EInk/GDEY0154D67.h" -#include "graphics/niche/Inputs/TwoButton.h" - -// Button feedback #include "buzz.h" +#ifdef MESHTASTIC_INCLUDE_INKHUD +#include "graphics/niche/Applets/User/AllMessage/AllMessageApplet.h" +#include "graphics/niche/Applets/User/DM/DMApplet.h" +#include "graphics/niche/Applets/User/FavoritesMap/FavoritesMapApplet.h" +#include "graphics/niche/Applets/User/Heard/HeardApplet.h" +#include "graphics/niche/Applets/User/Positions/PositionsApplet.h" +#include "graphics/niche/Applets/User/RecentsList/RecentsListApplet.h" +#include "graphics/niche/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" +#include "graphics/niche/InkHUD.h" +#include "graphics/niche/Inputs/TwoButton.h" +#endif + +#ifdef MESHTASTIC_INCLUDE_INKHUD void setupNicheGraphics() { using namespace NicheGraphics; - // SPI - // ----------------------------- - - // For NRF52 platforms, SPI pins are defined in variant.h - SPI1.begin(); - - // E-Ink Driver - // ----------------------------- - - Drivers::EInk *driver = new Drivers::GDEY0154D67; - driver->begin(&SPI1, PIN_EINK_DC, PIN_EINK_CS, PIN_EINK_BUSY, PIN_EINK_RES); - - // InkHUD - // ---------------------------- + auto *panel = new Panels::GDEY0154D67(); + Drivers::EInk *driver = panel->create(); InkHUD::InkHUD *inkhud = InkHUD::InkHUD::getInstance(); - - // Set the E-Ink driver inkhud->setDriver(driver); - - // Set how many FAST updates per FULL update - // Set how unhealthy additional FAST updates beyond this number are - // Todo: observe the display's performance in-person and adjust accordingly. - // Currently set to the values given by Elecrow for EInkDynamicDisplay. inkhud->setDisplayResilience(10, 1.5); - // Select fonts InkHUD::Applet::fontLarge = FREESANS_12PT_WIN1252; InkHUD::Applet::fontMedium = FREESANS_9PT_WIN1252; InkHUD::Applet::fontSmall = FREESANS_6PT_WIN1252; - // Customize default settings - inkhud->persistence->settings.userTiles.maxCount = 2; // Two applets side-by-side - inkhud->persistence->settings.optionalFeatures.batteryIcon = true; // Device definitely has a battery + inkhud->persistence->settings.userTiles.maxCount = 2; + inkhud->persistence->settings.optionalFeatures.batteryIcon = true; - // Setup backlight controller - // Note: button is attached further down Drivers::LatchingBacklight *backlight = Drivers::LatchingBacklight::getInstance(); backlight->setPin(PIN_EINK_EN); - // Pick applets - // Note: order of applets determines priority of "auto-show" feature - inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); // Activated, autoshown - inkhud->addApplet("DMs", new InkHUD::DMApplet); // - - inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // - - inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // - - inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated - inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // - - inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, no autoshow, default on tile 0 - inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet, false, false); // - - - // Start running InkHUD + inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); + inkhud->addApplet("DMs", new InkHUD::DMApplet); + inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); + inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); + inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); + inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet); + inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); + inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); inkhud->begin(); - // Buttons - // -------------------------- - - Inputs::TwoButton *buttons = Inputs::TwoButton::getInstance(); // Shared NicheGraphics component - - // Elecrow diagram: https://www.elecrow.com/download/product/CIL12901M/ThinkNode-M1_User_Manual.pdf - - // #0: Main User Button - // Labeled "Page Turn Button" by manual - buttons->setWiring(0, PIN_BUTTON2); - buttons->setTiming(0, 50, 500); // Todo: confirm 50ms is adequate debounce + Inputs::TwoButton *buttons = Inputs::TwoButton::getInstance(); + buttons->setWiring(0, Inputs::TwoButton::getUserButtonPin()); buttons->setHandlerShortPress(0, [inkhud]() { inkhud->shortpress(); }); buttons->setHandlerLongPress(0, [inkhud]() { inkhud->longpress(); }); - - // #1: Aux Button - // Labeled "Function Button" by manual - // Todo: additional features - buttons->setWiring(1, PIN_BUTTON1); - buttons->setTiming(1, 50, 500); // 500ms before latch - buttons->setHandlerDown(1, [backlight]() { backlight->peek(); }); - buttons->setHandlerLongPress(1, [backlight]() { - backlight->latch(); - playBoop(); - }); - buttons->setHandlerShortPress(1, [backlight]() { - backlight->off(); - playChirp(); - }); - - // Begin handling button events +#ifdef PIN_BUTTON2 + buttons->setWiring(1, PIN_BUTTON2); + buttons->setHandlerShortPress(1, [backlight]() { backlight->latch(); }); + buttons->setHandlerLongPress(1, [backlight]() { backlight->off(); }); +#endif buttons->start(); } +#else +void setupNicheGraphics() +{ + // Backlight on by default for the original BaseUI build. + pinMode(PIN_EINK_EN, OUTPUT); + digitalWrite(PIN_EINK_EN, HIGH); +} +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new NicheGraphics::Panels::GDEY0154D67(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panel->rotation()); + display->setDisplayResilience(10, 1.5f); + return display; +} #endif + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/nrf52840/ELECROW-ThinkNode-M1/platformio.ini b/variants/nrf52840/ELECROW-ThinkNode-M1/platformio.ini index c169de8f7..b2a8abd91 100644 --- a/variants/nrf52840/ELECROW-ThinkNode-M1/platformio.ini +++ b/variants/nrf52840/ELECROW-ThinkNode-M1/platformio.ini @@ -10,31 +10,20 @@ custom_meshtastic_images = thinknode_m1.svg custom_meshtastic_tags = Elecrow custom_meshtastic_has_ink_hud = true -extends = nrf52840_base +extends = nrf52840_base, niche board = ThinkNode-M1 board_check = true debug_tool = jlink -# add -DCFG_SYSVIEW if you want to use the Segger systemview tool for OS profiling. build_flags = ${nrf52840_base.build_flags} + ${niche.build_flags} -Ivariants/nrf52840/ELECROW-ThinkNode-M1 -DELECROW_ThinkNode_M1 -DUSE_EINK - -DEINK_DISPLAY_MODEL=GxEPD2_154_D67 - -DEINK_WIDTH=200 - -DEINK_HEIGHT=200 - -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk - -DEINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted //20 - -DEINK_LIMIT_RATE_BACKGROUND_SEC=10 ; Minimum interval between BACKGROUND updates //30 - -DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates -; -DEINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated - -DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/ELECROW-ThinkNode-M1> -lib_deps = +build_src_filter = ${nrf52_base.build_src_filter} ${niche.build_src_filter} +<../variants/nrf52840/ELECROW-ThinkNode-M1> +lib_deps = ${nrf52840_base.lib_deps} - # renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master - https://github.com/meshtastic/GxEPD2/archive/c7eb4c3c167cf396ef4f541cc5d4c6aa42f3c46b.zip # renovate: datasource=custom.pio depName=nRF52_PWM packageName=khoih-prog/library/nRF52_PWM khoih-prog/nRF52_PWM@1.0.1 ;upload_protocol = fs @@ -44,15 +33,15 @@ extends = nrf52840_base, inkhud board = ThinkNode-M1 board_check = true debug_tool = jlink -build_flags = +build_flags = ${nrf52840_base.build_flags} ${inkhud.build_flags} -I variants/nrf52840/ELECROW-ThinkNode-M1 -D ELECROW_ThinkNode_M1 -build_src_filter = - ${nrf52_base.build_src_filter} +build_src_filter = + ${nrf52_base.build_src_filter} ${inkhud.build_src_filter} +<../variants/nrf52840/ELECROW-ThinkNode-M1> -lib_deps = +lib_deps = ${inkhud.lib_deps} ; InkHUD libs first, so we get GFXRoot instead of AdafruitGFX ${nrf52840_base.lib_deps} diff --git a/variants/nrf52840/ME25LS01-4Y10TD_e-ink/nicheGraphics.h b/variants/nrf52840/ME25LS01-4Y10TD_e-ink/nicheGraphics.h new file mode 100644 index 000000000..49961eedd --- /dev/null +++ b/variants/nrf52840/ME25LS01-4Y10TD_e-ink/nicheGraphics.h @@ -0,0 +1,27 @@ +/* + +NicheGraphics setup for ME25LS01-4Y10TD e-ink board (GDEY042T81 4.2" on SPI1). + +*/ + +#pragma once + +#include "configuration.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Panels/GDEY042T81.h" + +void setupNicheGraphics() {} + +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new NicheGraphics::Panels::GDEY042T81(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panel->rotation()); + display->setDisplayResilience(10, 2.0f); + return display; +} + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/nrf52840/ME25LS01-4Y10TD_e-ink/platformio.ini b/variants/nrf52840/ME25LS01-4Y10TD_e-ink/platformio.ini index 39b5dfbd4..dc68ef55b 100644 --- a/variants/nrf52840/ME25LS01-4Y10TD_e-ink/platformio.ini +++ b/variants/nrf52840/ME25LS01-4Y10TD_e-ink/platformio.ini @@ -1,22 +1,18 @@ [env:ME25LS01-4Y10TD_e-ink] -extends = nrf52840_base +extends = nrf52840_base, niche board = me25ls01-4y10td board_level = extra ; platform = https://github.com/maxgerhardt/platform-nordicnrf52#cac6fcf943a41accd2aeb4f3659ae297a73f422e build_flags = ${nrf52840_base.build_flags} + ${niche.build_flags} -Ivariants/nrf52840/ME25LS01-4Y10TD_e-ink -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DME25LS01_4Y10TD - -DEINK_DISPLAY_MODEL=GxEPD2_420_GDEY042T81 - -DEINK_WIDTH=400 - -DEINK_HEIGHT=300 board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/ME25LS01-4Y10TD_e-ink> -lib_deps = +build_src_filter = ${nrf52_base.build_src_filter} ${niche.build_src_filter} +<../variants/nrf52840/ME25LS01-4Y10TD_e-ink> +lib_deps = ${nrf52840_base.lib_deps} - # renovate: datasource=custom.pio depName=GxEPD2 packageName=zinggjm/library/GxEPD2 - zinggjm/GxEPD2@1.6.8 ; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) upload_protocol = nrfutil ;upload_port = /dev/ttyACM1 diff --git a/variants/nrf52840/MakePython_nRF52840_eink/nicheGraphics.h b/variants/nrf52840/MakePython_nRF52840_eink/nicheGraphics.h new file mode 100644 index 000000000..74bd253ef --- /dev/null +++ b/variants/nrf52840/MakePython_nRF52840_eink/nicheGraphics.h @@ -0,0 +1,35 @@ +/* + +NicheGraphics setup for the MakerPython nRF52840 + SX1280 DIY e-ink board +(as shipped: GDEW029T5D, 128x296, UC8151D controller). + +The UC8151D init/command set is close enough to SSD1680 that GDEY029T94 +(same 128x296 geometry, SSD1680-based) is used as the panel profile. +If the panel misbehaves, drop in a UC8151D profile here. + +Uses SPI1 per variant.h (SCLK=P0.02, MOSI=P0.28), which is the default +for nRF52 panel profiles. + +*/ + +#pragma once + +#include "configuration.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Panels/GDEY029T94.h" + +void setupNicheGraphics() {} + +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new NicheGraphics::Panels::GDEY029T94(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panel->rotation()); + display->setDisplayResilience(7, 1.5f); + return display; +} + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/nrf52840/MakePython_nRF52840_eink/platformio.ini b/variants/nrf52840/MakePython_nRF52840_eink/platformio.ini index ebea1ce97..db98a47d4 100644 --- a/variants/nrf52840/MakePython_nRF52840_eink/platformio.ini +++ b/variants/nrf52840/MakePython_nRF52840_eink/platformio.ini @@ -1,20 +1,16 @@ [env:makerpython_nrf52840_sx1280_eink] board_level = extra -extends = nrf52840_base +extends = nrf52840_base, niche board = nordic_pca10059 build_flags = ${nrf52840_base.build_flags} + ${niche.build_flags} -Ivariants/nrf52840/MakePython_nRF52840_eink -D PRIVATE_HW -D PIN_EINK_EN - -DEINK_DISPLAY_MODEL=GxEPD2_290_T5D - -DEINK_WIDTH=296 - -DEINK_HEIGHT=128 -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/MakePython_nRF52840_eink> -lib_deps = +build_src_filter = ${nrf52_base.build_src_filter} ${niche.build_src_filter} +<../variants/nrf52840/MakePython_nRF52840_eink> +lib_deps = ${nrf52840_base.lib_deps} # renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip - # renovate: datasource=custom.pio depName=GxEPD2 packageName=zinggjm/library/GxEPD2 - zinggjm/GxEPD2@1.6.8 debug_tool = jlink ;upload_port = /dev/ttyACM4 \ No newline at end of file diff --git a/variants/nrf52840/TWC_mesh_v4/platformio.ini b/variants/nrf52840/TWC_mesh_v4/platformio.ini index c529caa0b..4d0ec7aaa 100644 --- a/variants/nrf52840/TWC_mesh_v4/platformio.ini +++ b/variants/nrf52840/TWC_mesh_v4/platformio.ini @@ -6,8 +6,6 @@ build_flags = ${nrf52840_base.build_flags} -I variants/nrf52840/TWC_mesh_v4 -D TWC_mesh_v4 build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/TWC_mesh_v4> -lib_deps = +lib_deps = ${nrf52840_base.lib_deps} - # renovate: datasource=custom.pio depName=GxEPD2 packageName=zinggjm/library/GxEPD2 - zinggjm/GxEPD2@1.6.8 debug_tool = jlink diff --git a/variants/nrf52840/diy/nrf52_promicro_diy_tcxo/nicheGraphics.h b/variants/nrf52840/diy/nrf52_promicro_diy_tcxo/nicheGraphics.h index 0a01b613e..50fc723d2 100644 --- a/variants/nrf52840/diy/nrf52_promicro_diy_tcxo/nicheGraphics.h +++ b/variants/nrf52840/diy/nrf52_promicro_diy_tcxo/nicheGraphics.h @@ -6,25 +6,24 @@ // InkHUD-specific components // --------------------------- -#include "graphics/niche/InkHUD/InkHUD.h" +#include "graphics/niche/InkHUD.h" +#include "graphics/niche/Inputs/TwoButton.h" // Applets -#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h" -#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h" -#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h" -#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h" -#include "graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" +#include "graphics/niche/Applets/User/AllMessage/AllMessageApplet.h" +#include "graphics/niche/Applets/User/DM/DMApplet.h" +#include "graphics/niche/Applets/User/FavoritesMap/FavoritesMapApplet.h" +#include "graphics/niche/Applets/User/Heard/HeardApplet.h" +#include "graphics/niche/Applets/User/Positions/PositionsApplet.h" +#include "graphics/niche/Applets/User/RecentsList/RecentsListApplet.h" +#include "graphics/niche/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" // Shared NicheGraphics components // -------------------------------- -#include "graphics/niche/Drivers/EInk/HINK_E0213A289.h" // WeAct 2.13" -#include "graphics/niche/Drivers/EInk/HINK_E042A87.h" // WeAct 4.2" -#include "graphics/niche/Drivers/EInk/ZJY128296_029EAAMFGN.h" // WeAct 2.9" -#include "graphics/niche/Drivers/EInk/ZJY200200_0154DAAMFGN.h" // WeACt 1.54" - -#include "graphics/niche/Inputs/TwoButton.h" +#include "graphics/eink/Drivers/HINK_E0213A289.h" // WeAct 2.13" +#include "graphics/eink/Drivers/HINK_E042A87.h" // WeAct 4.2" +#include "graphics/eink/Drivers/ZJY128296_029EAAMFGN.h" // WeAct 2.9" +#include "graphics/eink/Drivers/ZJY200200_0154DAAMFGN.h" // WeACt 1.54" #if !defined(INKHUD_BUILDCONF_DRIVER) || !defined(INKHUD_BUILDCONF_DISPLAYRESILIENCE) #error If not using a DIY preset, display model and resilience must be set manually diff --git a/variants/nrf52840/heltec_mesh_node_t114-inkhud/nicheGraphics.h b/variants/nrf52840/heltec_mesh_node_t114-inkhud/nicheGraphics.h index ad17e7457..17865996b 100644 --- a/variants/nrf52840/heltec_mesh_node_t114-inkhud/nicheGraphics.h +++ b/variants/nrf52840/heltec_mesh_node_t114-inkhud/nicheGraphics.h @@ -6,25 +6,24 @@ // InkHUD-specific components // --------------------------- -#include "graphics/niche/InkHUD/InkHUD.h" +#include "graphics/niche/InkHUD.h" +#include "graphics/niche/Inputs/TwoButton.h" // Applets -#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h" -#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h" -#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h" -#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h" -#include "graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" +#include "graphics/niche/Applets/User/AllMessage/AllMessageApplet.h" +#include "graphics/niche/Applets/User/DM/DMApplet.h" +#include "graphics/niche/Applets/User/FavoritesMap/FavoritesMapApplet.h" +#include "graphics/niche/Applets/User/Heard/HeardApplet.h" +#include "graphics/niche/Applets/User/Positions/PositionsApplet.h" +#include "graphics/niche/Applets/User/RecentsList/RecentsListApplet.h" +#include "graphics/niche/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" // Shared NicheGraphics components // -------------------------------- -#include "graphics/niche/Drivers/EInk/HINK_E0213A289.h" // WeAct 2.13" -#include "graphics/niche/Drivers/EInk/HINK_E042A87.h" // WeAct 4.2" -#include "graphics/niche/Drivers/EInk/ZJY128296_029EAAMFGN.h" // WeAct 2.9" -#include "graphics/niche/Drivers/EInk/ZJY200200_0154DAAMFGN.h" // WeACt 1.54" - -#include "graphics/niche/Inputs/TwoButton.h" +#include "graphics/eink/Drivers/HINK_E0213A289.h" // WeAct 2.13" +#include "graphics/eink/Drivers/HINK_E042A87.h" // WeAct 4.2" +#include "graphics/eink/Drivers/ZJY128296_029EAAMFGN.h" // WeAct 2.9" +#include "graphics/eink/Drivers/ZJY200200_0154DAAMFGN.h" // WeACt 1.54" #if !defined(INKHUD_BUILDCONF_DRIVER) || !defined(INKHUD_BUILDCONF_DISPLAYRESILIENCE) // cppcheck-suppress preprocessorErrorDirective diff --git a/variants/nrf52840/heltec_mesh_pocket/nicheGraphics.h b/variants/nrf52840/heltec_mesh_pocket/nicheGraphics.h index 187022ea7..5d9ee9b15 100644 --- a/variants/nrf52840/heltec_mesh_pocket/nicheGraphics.h +++ b/variants/nrf52840/heltec_mesh_pocket/nicheGraphics.h @@ -1,92 +1,80 @@ +/* + +NicheGraphics setup for Heltec Mesh Pocket (LCMEN2R13ECC1 2.13" on SPI1). +Shared by both BaseUI and InkHUD envs (5000 and 10000 mAh variants). + +*/ + #pragma once #include "configuration.h" #ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS -// InkHUD-specific components -// --------------------------- -#include "graphics/niche/InkHUD/InkHUD.h" +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Panels/LCMEN2R13ECC1.h" -// Applets -#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h" -#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h" -#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h" -#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h" -#include "graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" - -// Shared NicheGraphics components -// -------------------------------- -#include "graphics/niche/Drivers/EInk/LCMEN2R13ECC1.h" +#ifdef MESHTASTIC_INCLUDE_INKHUD +#include "graphics/niche/Applets/User/AllMessage/AllMessageApplet.h" +#include "graphics/niche/Applets/User/DM/DMApplet.h" +#include "graphics/niche/Applets/User/FavoritesMap/FavoritesMapApplet.h" +#include "graphics/niche/Applets/User/Heard/HeardApplet.h" +#include "graphics/niche/Applets/User/Positions/PositionsApplet.h" +#include "graphics/niche/Applets/User/RecentsList/RecentsListApplet.h" +#include "graphics/niche/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" +#include "graphics/niche/InkHUD.h" #include "graphics/niche/Inputs/TwoButton.h" +#endif +#ifdef MESHTASTIC_INCLUDE_INKHUD void setupNicheGraphics() { using namespace NicheGraphics; - // SPI - // ----------------------------- - - // For NRF52 platforms, SPI pins are defined in variant.h - SPI1.begin(); - - // E-Ink Driver - // ----------------------------- - - Drivers::EInk *driver = new Drivers::LCMEN2R13ECC1; - driver->begin(&SPI1, PIN_EINK_DC, PIN_EINK_CS, PIN_EINK_BUSY, PIN_EINK_RES); - - // InkHUD - // ---------------------------- + auto *panel = new Panels::LCMEN2R13ECC1(); + Drivers::EInk *driver = panel->create(); InkHUD::InkHUD *inkhud = InkHUD::InkHUD::getInstance(); - - // Set the E-Ink driver inkhud->setDriver(driver); - - // Set how many FAST updates per FULL update - // Set how unhealthy additional FAST updates beyond this number are inkhud->setDisplayResilience(10, 1.5); - // Select fonts InkHUD::Applet::fontLarge = FREESANS_12PT_WIN1253; InkHUD::Applet::fontMedium = FREESANS_9PT_WIN1253; InkHUD::Applet::fontSmall = FREESANS_6PT_WIN1253; - // Customize default settings - inkhud->persistence->settings.userTiles.maxCount = 2; // How many tiles can the display handle? - inkhud->persistence->settings.rotation = 3; // 270 degrees clockwise - inkhud->persistence->settings.userTiles.count = 1; // One tile only by default, keep things simple for new users + inkhud->persistence->settings.userTiles.maxCount = 2; + inkhud->persistence->settings.rotation = 3; + inkhud->persistence->settings.userTiles.count = 1; inkhud->persistence->settings.optionalMenuItems.nextTile = true; - // Pick applets - // Note: order of applets determines priority of "auto-show" feature - inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); // Activated, autoshown - inkhud->addApplet("DMs", new InkHUD::DMApplet); // - - inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // - - inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // - - inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated - inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet); // - - inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // - - inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, no autoshow, default on tile 0 + inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); + inkhud->addApplet("DMs", new InkHUD::DMApplet); + inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); + inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); + inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); + inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet); + inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); + inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); - // Start running InkHUD inkhud->begin(); - // Buttons - // -------------------------- - - Inputs::TwoButton *buttons = Inputs::TwoButton::getInstance(); // Shared NicheGraphics component - - // #0: Main User Button + Inputs::TwoButton *buttons = Inputs::TwoButton::getInstance(); buttons->setWiring(0, Inputs::TwoButton::getUserButtonPin()); buttons->setHandlerShortPress(0, [inkhud]() { inkhud->shortpress(); }); buttons->setHandlerLongPress(0, [inkhud]() { inkhud->longpress(); }); - - // Begin handling button events buttons->start(); } +#else +void setupNicheGraphics() {} +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new NicheGraphics::Panels::LCMEN2R13ECC1(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panel->rotation()); + display->setDisplayResilience(10, 1.5f); + return display; +} #endif + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/nrf52840/heltec_mesh_pocket/platformio.ini b/variants/nrf52840/heltec_mesh_pocket/platformio.ini index c9b599382..7cd6d00dc 100644 --- a/variants/nrf52840/heltec_mesh_pocket/platformio.ini +++ b/variants/nrf52840/heltec_mesh_pocket/platformio.ini @@ -4,7 +4,7 @@ custom_meshtastic_support_level = 1 custom_meshtastic_images = heltec_mesh_pocket.svg custom_meshtastic_tags = Heltec -extends = nrf52840_base +extends = nrf52840_base, niche board = heltec_mesh_pocket debug_tool = jlink custom_device_hw_model = 94 @@ -17,29 +17,16 @@ custom_meshtastic_variant = 5000mAh custom_meshtastic_key = heltec_mesh_pocket custom_meshtastic_has_ink_hud = true -# add -DCFG_SYSVIEW if you want to use the Segger systemview tool for OS profiling. build_flags = ${nrf52840_base.build_flags} + ${niche.build_flags} -Ivariants/nrf52840/heltec_mesh_pocket -DHELTEC_MESH_POCKET -DHELTEC_MESH_POCKET_BATTERY_5000 -DUSE_EINK - -DEINK_DISPLAY_MODEL=GxEPD2_213_B74 - -DEINK_WIDTH=250 - -DEINK_HEIGHT=122 - -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk - -DEINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted - -DEINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates - -DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates -; -D EINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated - -DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. - -DEINK_HASQUIRK_GHOSTING ; Display model is identified as "prone to ghosting" - -DEINK_HASQUIRK_WEAKFASTREFRESH ; Pixels set with fast-refresh are easy to clear, disrupted by sunlight -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/heltec_mesh_pocket> -lib_deps = +build_src_filter = ${nrf52_base.build_src_filter} ${niche.build_src_filter} +<../variants/nrf52840/heltec_mesh_pocket> +lib_deps = ${nrf52840_base.lib_deps} - # renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master - https://github.com/meshtastic/GxEPD2/archive/c7eb4c3c167cf396ef4f541cc5d4c6aa42f3c46b.zip [env:heltec-mesh-pocket-5000-inkhud] extends = nrf52840_base, inkhud @@ -52,9 +39,9 @@ custom_meshtastic_actively_supported = true custom_meshtastic_variant = 5000mAh InkHUD custom_meshtastic_key = heltec_mesh_pocket build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/heltec_mesh_pocket> ${inkhud.build_src_filter} -build_flags = +build_flags = ${inkhud.build_flags} - ${nrf52840_base.build_flags} + ${nrf52840_base.build_flags} -I variants/nrf52840/heltec_mesh_pocket -D HELTEC_MESH_POCKET -D HELTEC_MESH_POCKET_BATTERY_5000 @@ -69,7 +56,7 @@ custom_meshtastic_support_level = 1 custom_meshtastic_images = heltec_mesh_pocket.svg custom_meshtastic_tags = Heltec -extends = nrf52840_base +extends = nrf52840_base, niche board = heltec_mesh_pocket debug_tool = jlink custom_meshtastic_hw_model = 94 @@ -81,29 +68,16 @@ custom_meshtastic_variant = 10000mAh custom_meshtastic_key = heltec_mesh_pocket custom_meshtastic_has_ink_hud = true -# add -DCFG_SYSVIEW if you want to use the Segger systemview tool for OS profiling. build_flags = ${nrf52840_base.build_flags} + ${niche.build_flags} -Ivariants/nrf52840/heltec_mesh_pocket -DHELTEC_MESH_POCKET -DHELTEC_MESH_POCKET_BATTERY_10000 -DUSE_EINK - -DEINK_DISPLAY_MODEL=GxEPD2_213_B74 - -DEINK_WIDTH=250 - -DEINK_HEIGHT=122 - -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk - -DEINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted - -DEINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates - -DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates -; -D EINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated - -DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. - -DEINK_HASQUIRK_GHOSTING ; Display model is identified as "prone to ghosting" - -DEINK_HASQUIRK_WEAKFASTREFRESH ; Pixels set with fast-refresh are easy to clear, disrupted by sunlight -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/heltec_mesh_pocket> -lib_deps = +build_src_filter = ${nrf52_base.build_src_filter} ${niche.build_src_filter} +<../variants/nrf52840/heltec_mesh_pocket> +lib_deps = ${nrf52840_base.lib_deps} - # renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master - https://github.com/meshtastic/GxEPD2/archive/c7eb4c3c167cf396ef4f541cc5d4c6aa42f3c46b.zip [env:heltec-mesh-pocket-10000-inkhud] extends = nrf52840_base, inkhud @@ -116,9 +90,9 @@ custom_meshtastic_actively_supported = true custom_meshtastic_variant = 10000mAh InkHUD custom_meshtastic_key = heltec_mesh_pocket build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/heltec_mesh_pocket> ${inkhud.build_src_filter} -build_flags = +build_flags = ${inkhud.build_flags} - ${nrf52840_base.build_flags} + ${nrf52840_base.build_flags} -I variants/nrf52840/heltec_mesh_pocket -D HELTEC_MESH_POCKET -D HELTEC_MESH_POCKET_BATTERY_10000 diff --git a/variants/nrf52840/heltec_mesh_solar/nicheGraphics.h b/variants/nrf52840/heltec_mesh_solar/nicheGraphics.h index 0f4131916..acb8e2892 100644 --- a/variants/nrf52840/heltec_mesh_solar/nicheGraphics.h +++ b/variants/nrf52840/heltec_mesh_solar/nicheGraphics.h @@ -1,92 +1,93 @@ +/* + +NicheGraphics setup for Heltec MeshSolar E-Ink (E0213A367 2.13" on SPI1). +Shared by both BaseUI (heltec-mesh-solar-eink) and InkHUD (heltec-mesh-solar-inkhud). + +*/ + #pragma once #include "configuration.h" #ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS -// InkHUD-specific components -// --------------------------- -#include "graphics/niche/InkHUD/InkHUD.h" +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Panels/E0213A367.h" -// Applets -#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h" -#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h" -#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h" -#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h" -#include "graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" - -// Shared NicheGraphics components -// -------------------------------- -#include "graphics/niche/Drivers/EInk/E0213A367.h" +#ifdef MESHTASTIC_INCLUDE_INKHUD +#include "graphics/niche/Applets/User/AllMessage/AllMessageApplet.h" +#include "graphics/niche/Applets/User/DM/DMApplet.h" +#include "graphics/niche/Applets/User/FavoritesMap/FavoritesMapApplet.h" +#include "graphics/niche/Applets/User/Heard/HeardApplet.h" +#include "graphics/niche/Applets/User/Positions/PositionsApplet.h" +#include "graphics/niche/Applets/User/RecentsList/RecentsListApplet.h" +#include "graphics/niche/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" +#include "graphics/niche/InkHUD.h" #include "graphics/niche/Inputs/TwoButton.h" +#endif +class HeltecMeshSolarPanel : public NicheGraphics::Panels::E0213A367 +{ + protected: + void prePowerOn() override + { +#ifdef PIN_SCREEN_VDD_CTL + pinMode(PIN_SCREEN_VDD_CTL, OUTPUT); + digitalWrite(PIN_SCREEN_VDD_CTL, HIGH); + delay(10); +#endif + } +}; + +#ifdef MESHTASTIC_INCLUDE_INKHUD void setupNicheGraphics() { using namespace NicheGraphics; - // SPI - // ----------------------------- - - // For NRF52 platforms, SPI pins are defined in variant.h - SPI1.begin(); - - // E-Ink Driver - // ----------------------------- - - Drivers::EInk *driver = new Drivers::E0213A367; - driver->begin(&SPI1, PIN_EINK_DC, PIN_EINK_CS, PIN_EINK_BUSY, PIN_EINK_RES); - - // InkHUD - // ---------------------------- + auto *panel = new HeltecMeshSolarPanel(); + Drivers::EInk *driver = panel->create(); InkHUD::InkHUD *inkhud = InkHUD::InkHUD::getInstance(); - - // Set the E-Ink driver inkhud->setDriver(driver); - - // Set how many FAST updates per FULL update - // Set how unhealthy additional FAST updates beyond this number are inkhud->setDisplayResilience(10, 1.5); - // Select fonts InkHUD::Applet::fontLarge = FREESANS_12PT_WIN1252; InkHUD::Applet::fontMedium = FREESANS_9PT_WIN1252; InkHUD::Applet::fontSmall = FREESANS_6PT_WIN1252; - // Customize default settings - inkhud->persistence->settings.userTiles.maxCount = 2; // How many tiles can the display handle? - inkhud->persistence->settings.rotation = 3; // 270 degrees clockwise - inkhud->persistence->settings.userTiles.count = 1; // One tile only by default, keep things simple for new users + inkhud->persistence->settings.userTiles.maxCount = 2; + inkhud->persistence->settings.rotation = 3; + inkhud->persistence->settings.userTiles.count = 1; inkhud->persistence->settings.optionalMenuItems.nextTile = true; - // Pick applets - // Note: order of applets determines priority of "auto-show" feature - inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); // Activated, autoshown - inkhud->addApplet("DMs", new InkHUD::DMApplet); // - - inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // - - inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // - - inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated - inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet); // - - inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // - - inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, no autoshow, default on tile 0 + inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); + inkhud->addApplet("DMs", new InkHUD::DMApplet); + inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); + inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); + inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); + inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet); + inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); + inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); - // Start running InkHUD inkhud->begin(); - // Buttons - // -------------------------- - - Inputs::TwoButton *buttons = Inputs::TwoButton::getInstance(); // Shared NicheGraphics component - - // #0: Main User Button + Inputs::TwoButton *buttons = Inputs::TwoButton::getInstance(); buttons->setWiring(0, Inputs::TwoButton::getUserButtonPin()); buttons->setHandlerShortPress(0, [inkhud]() { inkhud->shortpress(); }); buttons->setHandlerLongPress(0, [inkhud]() { inkhud->longpress(); }); - - // Begin handling button events buttons->start(); } +#else +void setupNicheGraphics() {} +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new HeltecMeshSolarPanel(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panel->rotation()); + display->setDisplayResilience(10, 1.5f); + return display; +} #endif + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/nrf52840/heltec_mesh_solar/platformio.ini b/variants/nrf52840/heltec_mesh_solar/platformio.ini index 1b6f59a68..eaed69d1a 100644 --- a/variants/nrf52840/heltec_mesh_solar/platformio.ini +++ b/variants/nrf52840/heltec_mesh_solar/platformio.ini @@ -11,7 +11,7 @@ build_flags = ${nrf52840_base.build_flags} -DHELTEC_MESH_SOLAR build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/heltec_mesh_solar> -lib_deps = +lib_deps = ${nrf52840_base.lib_deps} # renovate: datasource=git-refs depName=NMIoT-meshsolar packageName=https://github.com/NMIoT/meshsolar gitBranch=main https://github.com/NMIoT/meshsolar/archive/dfc5330dad443982e6cdd37a61d33fc7252f468b.zip @@ -33,8 +33,10 @@ build_flags = ${heltec_mesh_solar_base.build_flags} -DSPI_INTERFACES_COUNT=1 [env:heltec-mesh-solar-eink] -extends = heltec_mesh_solar_base +extends = heltec_mesh_solar_base, niche +build_src_filter = ${heltec_mesh_solar_base.build_src_filter} ${niche.build_src_filter} build_flags = ${heltec_mesh_solar_base.build_flags} + ${niche.build_flags} -DHELTEC_MESH_SOLAR_EINK -DSPI_INTERFACES_COUNT=2 -DUSE_EINK @@ -48,15 +50,6 @@ build_flags = ${heltec_mesh_solar_base.build_flags} -DPIN_SPI1_MISO=-1 -DPIN_SPI1_MOSI=PIN_EINK_MOSI -DPIN_SPI1_SCK=PIN_EINK_SCLK - -DEINK_DISPLAY_MODEL=GxEPD2_213_E0213A367 - -DEINK_WIDTH=250 - -DEINK_HEIGHT=122 - -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk - -DEINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted - -DEINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates - -DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates - -DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. - -DEINK_HASQUIRK_GHOSTING ; Display model is identified as "prone to ghosting" -DENABLE_MESSAGE_PERSISTENCE=0 ; Disable flash persistence for space-limited build -DMESHTASTIC_EXCLUDE_WIFI=1 -DMESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION=1 @@ -67,8 +60,6 @@ build_flags = ${heltec_mesh_solar_base.build_flags} -DMESHTASTIC_EXCLUDE_WAYPOINT=1 lib_deps = ${heltec_mesh_solar_base.lib_deps} - # renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master - https://github.com/meshtastic/GxEPD2/archive/c7eb4c3c167cf396ef4f541cc5d4c6aa42f3c46b.zip [env:heltec-mesh-solar-inkhud] extends = heltec_mesh_solar_base, inkhud diff --git a/variants/nrf52840/meshlink/nicheGraphics.h b/variants/nrf52840/meshlink/nicheGraphics.h new file mode 100644 index 000000000..1db0de9fa --- /dev/null +++ b/variants/nrf52840/meshlink/nicheGraphics.h @@ -0,0 +1,28 @@ +/* + +NicheGraphics setup for MeshLink (LoraItalia) ePaper build (GDEY0213B74 2.13" on SPI1). +Only used by env:meshlink_eink (the env:meshlink build has no display). + +*/ + +#pragma once + +#include "configuration.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Panels/GDEY0213B74.h" + +void setupNicheGraphics() {} + +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new NicheGraphics::Panels::GDEY0213B74(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panel->rotation()); + display->setDisplayResilience(5, 1.5f); + return display; +} + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/nrf52840/meshlink/platformio.ini b/variants/nrf52840/meshlink/platformio.ini index f3dc6185c..5d2770d14 100644 --- a/variants/nrf52840/meshlink/platformio.ini +++ b/variants/nrf52840/meshlink/platformio.ini @@ -21,11 +21,12 @@ debug_tool = jlink ;upload_protocol = jlink [env:meshlink_eink] -extends = nrf52840_base +extends = nrf52840_base, niche board = meshlink board_level = extra ;board_check = true build_flags = ${nrf52840_base.build_flags} + ${niche.build_flags} -I variants/nrf52840/meshlink -D MESHLINK -DRADIOLIB_EXCLUDE_SX128X=1 @@ -33,23 +34,10 @@ build_flags = ${nrf52840_base.build_flags} -DRADIOLIB_EXCLUDE_LR11X0=1 -DRADIOLIB_EXCLUDE_LR2021=1 -D USE_EINK - -D EINK_DISPLAY_MODEL=GxEPD2_213_B74 - -D EINK_WIDTH=250 - -D EINK_HEIGHT=122 - -D USE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk - -D EINK_LIMIT_FASTREFRESH=5 ; How many consecutive fast-refreshes are permitted - -D EINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates - -D EINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates - -D EINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated - -D EINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. - -D EINK_HASQUIRK_VICIOUSFASTREFRESH ; Identify that pixels drawn by fast-refresh are harder to clear - -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/meshlink> -lib_deps = +build_src_filter = ${nrf52_base.build_src_filter} ${niche.build_src_filter} +<../variants/nrf52840/meshlink> +lib_deps = ${nrf52840_base.lib_deps} - # renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master - https://github.com/meshtastic/GxEPD2/archive/c7eb4c3c167cf396ef4f541cc5d4c6aa42f3c46b.zip debug_tool = jlink ; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) ; Note: as of 6/2013 the serial/bootloader based programming takes approximately 30 seconds diff --git a/variants/nrf52840/rak4631_epaper/nicheGraphics.h b/variants/nrf52840/rak4631_epaper/nicheGraphics.h new file mode 100644 index 000000000..125b588ca --- /dev/null +++ b/variants/nrf52840/rak4631_epaper/nicheGraphics.h @@ -0,0 +1,43 @@ +/* + +NicheGraphics setup for RAK4631 + RAK14000 ePaper module (2.13" BN, DEPG0213BNS800 on SPI1). +BaseUI build only - no InkHUD env. + +*/ + +#pragma once + +#include "configuration.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Panels/DEPG0213BNS800.h" + +class RAK4631EpaperPanel : public NicheGraphics::Panels::DEPG0213BNS800 +{ + protected: + SPIClass *beginSpi() override + { + // RAK14000 is wired to SPI1 via pins declared in variant.h. + // Init settings cloned from the legacy GxEPD2 path on this board. + SPI1.begin(); + return &SPI1; + } +}; + +void setupNicheGraphics() +{ + // BaseUI handles buttons through its default ButtonThread. Nothing extra here. +} + +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new RAK4631EpaperPanel(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panel->rotation()); + display->setDisplayResilience(7, 1.5f); + return display; +} + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/nrf52840/rak4631_epaper/platformio.ini b/variants/nrf52840/rak4631_epaper/platformio.ini index f71fb6301..e9a60e352 100644 --- a/variants/nrf52840/rak4631_epaper/platformio.ini +++ b/variants/nrf52840/rak4631_epaper/platformio.ini @@ -1,22 +1,18 @@ ; The very slick RAK wireless RAK 4631 / 4630 board - Firmware for 5005 with the RAK 14000 ePaper [env:rak4631_eink] -extends = nrf52840_base +extends = nrf52840_base, niche board = wiscore_rak4631 build_flags = ${nrf52840_base.build_flags} + ${niche.build_flags} -I variants/nrf52840/rak4631_epaper -D RAK_4631 - -DEINK_DISPLAY_MODEL=GxEPD2_213_BN - -DEINK_WIDTH=250 - -DEINK_HEIGHT=122 -DRADIOLIB_EXCLUDE_SX128X=1 -DRADIOLIB_EXCLUDE_SX127X=1 -DRADIOLIB_EXCLUDE_LR11X0=1 -DRADIOLIB_EXCLUDE_LR2021=1 -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/rak4631_epaper> -lib_deps = +build_src_filter = ${nrf52_base.build_src_filter} ${niche.build_src_filter} +<../variants/nrf52840/rak4631_epaper> +lib_deps = ${nrf52840_base.lib_deps} - # renovate: datasource=custom.pio depName=GxEPD2 packageName=zinggjm/library/GxEPD2 - zinggjm/GxEPD2@1.6.8 # renovate: datasource=custom.pio depName=Melopero RV3028 packageName=melopero/library/Melopero RV3028 melopero/Melopero RV3028@1.2.0 # renovate: datasource=custom.pio depName=RAK NCP5623 RGB LED packageName=rakwireless/library/RAKwireless NCP5623 RGB LED library diff --git a/variants/nrf52840/rak4631_epaper_onrxtx/platformio.ini b/variants/nrf52840/rak4631_epaper_onrxtx/platformio.ini index 670b2c415..b3f400f99 100644 --- a/variants/nrf52840/rak4631_epaper_onrxtx/platformio.ini +++ b/variants/nrf52840/rak4631_epaper_onrxtx/platformio.ini @@ -1,24 +1,20 @@ ; The very slick RAK wireless RAK 4631 / 4630 board - Firmware for 5005 with the RAK 14000 ePaper [env:rak4631_eink_onrxtx] board_level = extra -extends = nrf52840_base +extends = nrf52840_base, niche board = wiscore_rak4631 build_flags = ${nrf52840_base.build_flags} + ${niche.build_flags} -I variants/nrf52840/rak4631_epaper -D RAK_4631 -D PIN_EINK_EN=34 - -D EINK_DISPLAY_MODEL=GxEPD2_213_BN - -D EINK_WIDTH=250 - -D EINK_HEIGHT=122 -D RADIOLIB_EXCLUDE_SX128X=1 -D RADIOLIB_EXCLUDE_SX127X=1 -D RADIOLIB_EXCLUDE_LR11X0=1 -D RADIOLIB_EXCLUDE_LR2021=1 -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/rak4631_epaper_onrxtx> -lib_deps = +build_src_filter = ${nrf52_base.build_src_filter} ${niche.build_src_filter} +<../variants/nrf52840/rak4631_epaper_onrxtx> +lib_deps = ${nrf52840_base.lib_deps} - # renovate: datasource=custom.pio depName=GxEPD2 packageName=zinggjm/library/GxEPD2 - zinggjm/GxEPD2@1.6.8 # renovate: datasource=custom.pio depName=Melopero RV3028 packageName=melopero/library/Melopero RV3028 melopero/Melopero RV3028@1.2.0 # renovate: datasource=custom.pio depName=RAK NCP5623 RGB LED packageName=rakwireless/library/RAKwireless NCP5623 RGB LED library diff --git a/variants/nrf52840/rak4631_eth_gw/platformio.ini b/variants/nrf52840/rak4631_eth_gw/platformio.ini index 0fded96f4..381bdb40e 100644 --- a/variants/nrf52840/rak4631_eth_gw/platformio.ini +++ b/variants/nrf52840/rak4631_eth_gw/platformio.ini @@ -7,9 +7,6 @@ build_flags = ${nrf52840_base.build_flags} -I variants/nrf52840/rak4631_eth_gw -D RAK_4631 -DHAS_UDP_MULTICAST=1 - -DEINK_DISPLAY_MODEL=GxEPD2_213_BN - -DEINK_WIDTH=250 - -DEINK_HEIGHT=122 -DNRF52_USE_JSON=1 -DMESHTASTIC_EXCLUDE_WIFI=1 -DMESHTASTIC_EXCLUDE_SCREEN=1 diff --git a/variants/nrf52840/rak4631_nomadstar_meteor_pro/platformio.ini b/variants/nrf52840/rak4631_nomadstar_meteor_pro/platformio.ini index f1641e7e4..634f0ab19 100644 --- a/variants/nrf52840/rak4631_nomadstar_meteor_pro/platformio.ini +++ b/variants/nrf52840/rak4631_nomadstar_meteor_pro/platformio.ini @@ -15,9 +15,6 @@ board_check = true build_flags = ${nrf52840_base.build_flags} -I variants/nrf52840/rak4631_nomadstar_meteor_pro -D NOMADSTAR_METEOR_PRO - -DEINK_DISPLAY_MODEL=GxEPD2_213_BN - -DEINK_WIDTH=250 - -DEINK_HEIGHT=122 -DRADIOLIB_EXCLUDE_SX128X=1 -DRADIOLIB_EXCLUDE_SX127X=1 -DRADIOLIB_EXCLUDE_LR11X0=1 diff --git a/variants/nrf52840/rak_wismeshtap/platformio.ini b/variants/nrf52840/rak_wismeshtap/platformio.ini index f058d9153..999b6323f 100644 --- a/variants/nrf52840/rak_wismeshtap/platformio.ini +++ b/variants/nrf52840/rak_wismeshtap/platformio.ini @@ -15,9 +15,6 @@ build_flags = ${nrf52840_base.build_flags} -Ivariants/nrf52840/rak_wismeshtap -DWISMESH_TAP -DRAK_4631 - -DEINK_DISPLAY_MODEL=GxEPD2_213_BN - -DEINK_WIDTH=250 - -DEINK_HEIGHT=122 -DMESHTASTIC_EXCLUDE_WIFI=1 -DMESHTASTIC_EXCLUDE_DETECTIONSENSOR=1 -DMESHTASTIC_EXCLUDE_STOREFORWARD=1 diff --git a/variants/nrf52840/seeed_wio_tracker_L1_eink/nicheGraphics.h b/variants/nrf52840/seeed_wio_tracker_L1_eink/nicheGraphics.h index 2a2967f5e..f6624b621 100644 --- a/variants/nrf52840/seeed_wio_tracker_L1_eink/nicheGraphics.h +++ b/variants/nrf52840/seeed_wio_tracker_L1_eink/nicheGraphics.h @@ -1,119 +1,105 @@ +/* + +NicheGraphics setup for Seeed Wio Tracker L1 E-Ink (ZJY122250_0213BAAMFGN 2.13" on SPI1). +Shared by both BaseUI and InkHUD envs. + +*/ + #pragma once #include "configuration.h" #ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS -// InkHUD-specific components -// --------------------------- -#include "graphics/niche/InkHUD/InkHUD.h" +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Panels/ZJY122250_0213BAAMFGN.h" -// Applets -#include "graphics/niche/InkHUD/Applets/Examples/UserAppletInputExample/UserAppletInputExample.h" -#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h" -#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h" -#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h" -#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h" -#include "graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" - -// Shared NicheGraphics components -// -------------------------------- -#include "graphics/niche/Drivers/EInk/ZJY122250_0213BAAMFGN.h" +#ifdef MESHTASTIC_INCLUDE_INKHUD +#include "graphics/niche/Applets/Examples/UserAppletInputExample/UserAppletInputExample.h" +#include "graphics/niche/Applets/User/AllMessage/AllMessageApplet.h" +#include "graphics/niche/Applets/User/DM/DMApplet.h" +#include "graphics/niche/Applets/User/FavoritesMap/FavoritesMapApplet.h" +#include "graphics/niche/Applets/User/Heard/HeardApplet.h" +#include "graphics/niche/Applets/User/Positions/PositionsApplet.h" +#include "graphics/niche/Applets/User/RecentsList/RecentsListApplet.h" +#include "graphics/niche/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" +#include "graphics/niche/InkHUD.h" #include "graphics/niche/Inputs/TwoButtonExtended.h" +#endif +#ifdef MESHTASTIC_INCLUDE_INKHUD void setupNicheGraphics() { using namespace NicheGraphics; - // SPI - // ----------------------------- - - // For NRF52 platforms, SPI pins are defined in variant.h - SPI1.begin(); - - // E-Ink Driver - // ----------------------------- - - Drivers::EInk *driver = new Drivers::ZJY122250_0213BAAMFGN; - driver->begin(&SPI1, PIN_EINK_DC, PIN_EINK_CS, PIN_EINK_BUSY, PIN_EINK_RES); - - // InkHUD - // ---------------------------- + auto *panel = new Panels::ZJY122250_0213BAAMFGN(); + Drivers::EInk *driver = panel->create(); InkHUD::InkHUD *inkhud = InkHUD::InkHUD::getInstance(); - - // Set the E-Ink driver inkhud->setDriver(driver); - - // Set how many FAST updates per FULL update inkhud->setDisplayResilience(15); - - // Select fonts InkHUD::Applet::fontLarge = FREESANS_12PT_WIN1252; InkHUD::Applet::fontMedium = FREESANS_9PT_WIN1252; InkHUD::Applet::fontSmall = FREESANS_6PT_WIN1252; - // Customize default settings - inkhud->persistence->settings.rotation = 1; // 90 degrees clockwise + inkhud->persistence->settings.rotation = 1; #if HAS_TRACKBALL - inkhud->persistence->settings.joystick.enabled = true; // Device uses a joystick - inkhud->persistence->settings.joystick.alignment = 3; // 270 degrees - inkhud->persistence->settings.optionalMenuItems.nextTile = false; // Use joystick instead + inkhud->persistence->settings.joystick.enabled = true; + inkhud->persistence->settings.joystick.alignment = 3; + inkhud->persistence->settings.optionalMenuItems.nextTile = false; #endif - inkhud->persistence->settings.optionalFeatures.batteryIcon = true; // Device definitely has a battery - inkhud->persistence->settings.userTiles.count = 1; // One tile only by default, keep things simple for new users - inkhud->persistence->settings.userTiles.maxCount = 2; // Two applets side-by-side inkhud->persistence->settings.optionalFeatures.batteryIcon = true; + inkhud->persistence->settings.userTiles.count = 1; + inkhud->persistence->settings.userTiles.maxCount = 2; - // Pick applets - // Note: order of applets determines priority of "auto-show" feature - inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); // Activated, autoshown - inkhud->addApplet("DMs", new InkHUD::DMApplet); // - - inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // - - inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // - - inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated - inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // - - inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, no autoshow, default on tile 0 + inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); + inkhud->addApplet("DMs", new InkHUD::DMApplet); + inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); + inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); + inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); + inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); + inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); + inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet, false, false); - inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet, false, false); // - - // Start running InkHUD inkhud->begin(); - // Buttons - // -------------------------- - - Inputs::TwoButtonExtended *buttons = Inputs::TwoButtonExtended::getInstance(); // Shared NicheGraphics component + Inputs::TwoButtonExtended *buttons = Inputs::TwoButtonExtended::getInstance(); #if HAS_TRACKBALL - // #0: Exit Button buttons->setWiring(0, Inputs::TwoButtonExtended::getUserButtonPin()); buttons->setTiming(0, 75, 500); buttons->setHandlerShortPress(0, [inkhud]() { inkhud->exitShort(); }); buttons->setHandlerLongPress(0, [inkhud]() { inkhud->exitLong(); }); - // #1: Joystick Center buttons->setWiring(1, TB_PRESS); buttons->setTiming(1, 75, 500); buttons->setHandlerShortPress(1, [inkhud]() { inkhud->shortpress(); }); buttons->setHandlerLongPress(1, [inkhud]() { inkhud->longpress(); }); - // Joystick Directions buttons->setJoystickWiring(TB_UP, TB_DOWN, TB_LEFT, TB_RIGHT); buttons->setJoystickDebounce(50); buttons->setJoystickPressHandlers([inkhud]() { inkhud->navUp(); }, [inkhud]() { inkhud->navDown(); }, [inkhud]() { inkhud->navLeft(); }, [inkhud]() { inkhud->navRight(); }); #else - // #0: User Button buttons->setWiring(0, Inputs::TwoButtonExtended::getUserButtonPin()); buttons->setTiming(0, 75, 500); buttons->setHandlerShortPress(0, [inkhud]() { inkhud->shortpress(); }); buttons->setHandlerLongPress(0, [inkhud]() { inkhud->longpress(); }); #endif - // Begin handling button events buttons->start(); } +#else +void setupNicheGraphics() {} +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new NicheGraphics::Panels::ZJY122250_0213BAAMFGN(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panel->rotation()); + display->setDisplayResilience(10, 1.5f); + return display; +} #endif + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/nrf52840/seeed_wio_tracker_L1_eink/platformio.ini b/variants/nrf52840/seeed_wio_tracker_L1_eink/platformio.ini index 26f4de565..067769de8 100644 --- a/variants/nrf52840/seeed_wio_tracker_L1_eink/platformio.ini +++ b/variants/nrf52840/seeed_wio_tracker_L1_eink/platformio.ini @@ -10,45 +10,33 @@ custom_meshtastic_tags = Seeed custom_meshtastic_has_ink_hud = true board = seeed_wio_tracker_L1 -extends = nrf52840_base +extends = nrf52840_base, niche ;board_level = extra build_flags = ${nrf52840_base.build_flags} + ${niche.build_flags} -I variants/nrf52840/seeed_wio_tracker_L1_eink -D SEEED_WIO_TRACKER_L1_EINK -D SEEED_WIO_TRACKER_L1 -I src/platform/nrf52/softdevice -I src/platform/nrf52/softdevice/nrf52 -DUSE_EINK - -DEINK_DISPLAY_MODEL=GxEPD2_213_B74 - -DEINK_WIDTH=250 - -DEINK_HEIGHT=122 - -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk - -DEINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted - -DEINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates - -DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates -; -D EINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated - -DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. - -DEINK_HASQUIRK_GHOSTING ; Display model is identified as "prone to ghosting" - -DEINK_HASQUIRK_WEAKFASTREFRESH ; Pixels set with fast-refresh are easy to clear, disrupted by sunlight board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/seeed_wio_tracker_L1_eink> +build_src_filter = ${nrf52_base.build_src_filter} ${niche.build_src_filter} +<../variants/nrf52840/seeed_wio_tracker_L1_eink> lib_deps = ${nrf52840_base.lib_deps} - # renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master - https://github.com/meshtastic/GxEPD2/archive/c7eb4c3c167cf396ef4f541cc5d4c6aa42f3c46b.zip debug_tool = jlink [env:seeed_wio_tracker_L1_eink-inkhud] board = seeed_wio_tracker_L1 extends = nrf52840_base, inkhud -build_flags = +build_flags = ${nrf52840_base.build_flags} ${inkhud.build_flags} -I variants/nrf52840/seeed_wio_tracker_L1_eink -D SEEED_WIO_TRACKER_L1 -D BUTTON_PIN=D13 board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld -build_src_filter = +build_src_filter = ${nrf52_base.build_src_filter} ${inkhud.build_src_filter} +<../variants/nrf52840/seeed_wio_tracker_L1_eink> diff --git a/variants/nrf52840/t-echo-lite/nicheGraphics.h b/variants/nrf52840/t-echo-lite/nicheGraphics.h new file mode 100644 index 000000000..f904702c4 --- /dev/null +++ b/variants/nrf52840/t-echo-lite/nicheGraphics.h @@ -0,0 +1,27 @@ +/* + +NicheGraphics setup for LILYGO T-Echo Lite (GDEH0122T61 1.22" on SPI1). + +*/ + +#pragma once + +#include "configuration.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Panels/GDEH0122T61.h" + +void setupNicheGraphics() {} + +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new NicheGraphics::Panels::GDEH0122T61(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panel->rotation()); + display->setDisplayResilience(20, 1.5f); + return display; +} + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/nrf52840/t-echo-lite/platformio.ini b/variants/nrf52840/t-echo-lite/platformio.ini index 1b725815e..e81272037 100644 --- a/variants/nrf52840/t-echo-lite/platformio.ini +++ b/variants/nrf52840/t-echo-lite/platformio.ini @@ -9,26 +9,18 @@ custom_meshtastic_display_name = LILYGO T-Echo Lite custom_meshtastic_images = techo_lite.svg custom_meshtastic_tags = LilyGo -extends = nrf52840_base +extends = nrf52840_base, niche board = t-echo board_check = true debug_tool = jlink -# add -DCFG_SYSVIEW if you want to use the Segger systemview tool for OS profiling. build_flags = ${nrf52840_base.build_flags} + ${niche.build_flags} -Ivariants/nrf52840/t-echo-lite -D T_ECHO_LITE - -D EINK_DISPLAY_MODEL=GxEPD2_122_T61 - -D EINK_WIDTH=192 - -D EINK_HEIGHT=176 -D USE_EINK - -D USE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk - -D EINK_LIMIT_FASTREFRESH=20 ; How many consecutive fast-refreshes are permitted - -D EINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/t-echo-lite> -lib_deps = +build_src_filter = ${nrf52_base.build_src_filter} ${niche.build_src_filter} +<../variants/nrf52840/t-echo-lite> +lib_deps = ${nrf52840_base.lib_deps} - # renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master - https://github.com/meshtastic/GxEPD2/archive/c7eb4c3c167cf396ef4f541cc5d4c6aa42f3c46b.zip ;upload_protocol = fs diff --git a/variants/nrf52840/t-echo-plus/nicheGraphics.h b/variants/nrf52840/t-echo-plus/nicheGraphics.h index 73067d7a7..733feebb8 100644 --- a/variants/nrf52840/t-echo-plus/nicheGraphics.h +++ b/variants/nrf52840/t-echo-plus/nicheGraphics.h @@ -1,29 +1,39 @@ +/* + +NicheGraphics setup for LILYGO T-Echo Plus (GDEY0154D67 1.54" on SPI1). +Shared by both BaseUI and InkHUD envs. + +*/ + #pragma once #include "configuration.h" #ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS -#include "graphics/niche/Drivers/Backlight/LatchingBacklight.h" -#include "graphics/niche/Drivers/EInk/GDEY0154D67.h" -#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h" -#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h" -#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h" -#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h" -#include "graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" -#include "graphics/niche/InkHUD/InkHUD.h" -#include "graphics/niche/Inputs/TwoButton.h" +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Backlight/LatchingBacklight.h" +#include "graphics/eink/Panels/GDEY0154D67.h" +#ifdef MESHTASTIC_INCLUDE_INKHUD +#include "graphics/niche/Applets/User/AllMessage/AllMessageApplet.h" +#include "graphics/niche/Applets/User/DM/DMApplet.h" +#include "graphics/niche/Applets/User/FavoritesMap/FavoritesMapApplet.h" +#include "graphics/niche/Applets/User/Heard/HeardApplet.h" +#include "graphics/niche/Applets/User/Positions/PositionsApplet.h" +#include "graphics/niche/Applets/User/RecentsList/RecentsListApplet.h" +#include "graphics/niche/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" +#include "graphics/niche/InkHUD.h" +#include "graphics/niche/Inputs/TwoButton.h" +#endif + +#ifdef MESHTASTIC_INCLUDE_INKHUD void setupNicheGraphics() { using namespace NicheGraphics; - SPI1.begin(); - - Drivers::EInk *driver = new Drivers::GDEY0154D67; - driver->begin(&SPI1, PIN_EINK_DC, PIN_EINK_CS, PIN_EINK_BUSY, PIN_EINK_RES); + auto *panel = new Panels::GDEY0154D67(); + Drivers::EInk *driver = panel->create(); InkHUD::InkHUD *inkhud = InkHUD::InkHUD::getInstance(); inkhud->setDriver(driver); @@ -51,12 +61,12 @@ void setupNicheGraphics() inkhud->begin(); Inputs::TwoButton *buttons = Inputs::TwoButton::getInstance(); - buttons->setWiring(0, Inputs::TwoButton::getUserButtonPin()); buttons->setTiming(0, 75, 500); buttons->setHandlerShortPress(0, [inkhud]() { inkhud->shortpress(); }); buttons->setHandlerLongPress(0, [inkhud]() { inkhud->longpress(); }); +#ifdef PIN_BUTTON_TOUCH buttons->setWiring(1, PIN_BUTTON_TOUCH); buttons->setTiming(1, 50, 5000); buttons->setHandlerDown(1, [inkhud, backlight]() { @@ -65,8 +75,21 @@ void setupNicheGraphics() }); buttons->setHandlerLongPress(1, [backlight]() { backlight->latch(); }); buttons->setHandlerShortPress(1, [backlight]() { backlight->off(); }); +#endif buttons->start(); } +#else +void setupNicheGraphics() {} +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new NicheGraphics::Panels::GDEY0154D67(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panel->rotation()); + display->setDisplayResilience(20, 1.5f); + return display; +} #endif + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/nrf52840/t-echo-plus/platformio.ini b/variants/nrf52840/t-echo-plus/platformio.ini index 313eceadc..c4df9d05d 100644 --- a/variants/nrf52840/t-echo-plus/platformio.ini +++ b/variants/nrf52840/t-echo-plus/platformio.ini @@ -10,29 +10,23 @@ custom_meshtastic_tags = LilyGo custom_meshtastic_requires_dfu = true custom_meshtastic_has_ink_hud = true -extends = nrf52840_base +extends = nrf52840_base, niche board = t-echo board_level = pr board_check = true debug_tool = jlink build_flags = ${nrf52840_base.build_flags} + ${niche.build_flags} -DTTGO_T_ECHO_PLUS -Ivariants/nrf52840/t-echo-plus - -DEINK_DISPLAY_MODEL=GxEPD2_154_D67 - -DEINK_WIDTH=200 - -DEINK_HEIGHT=200 -DUSE_EINK - -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk - -DEINK_LIMIT_FASTREFRESH=20 ; How many consecutive fast-refreshes are permitted - -DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. -DI2C_NO_RESCAN -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/t-echo-plus> +build_src_filter = ${nrf52_base.build_src_filter} ${niche.build_src_filter} +<../variants/nrf52840/t-echo-plus> lib_deps = ${nrf52840_base.lib_deps} - https://github.com/meshtastic/GxEPD2/archive/55f618961db45a23eff0233546430f1e5a80f63a.zip # renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library lewisxhe/PCF8563_Library@1.0.1 # renovate: datasource=custom.pio depName=Adafruit DRV2605 packageName=adafruit/library/Adafruit DRV2605 Library diff --git a/variants/nrf52840/t-echo/nicheGraphics.h b/variants/nrf52840/t-echo/nicheGraphics.h index c0b24dea7..3a3b4fce8 100644 --- a/variants/nrf52840/t-echo/nicheGraphics.h +++ b/variants/nrf52840/t-echo/nicheGraphics.h @@ -1,128 +1,99 @@ +/* + +NicheGraphics setup for LILYGO T-Echo (GDEY0154D67 1.54" on SPI1). +Shared by both BaseUI (env:t-echo) and InkHUD (env:t-echo-inkhud). + +*/ + #pragma once #include "configuration.h" #ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS -// InkHUD-specific components -// --------------------------- -#include "graphics/niche/InkHUD/InkHUD.h" +#include "graphics/BaseUIEInkDisplay.h" +#include "graphics/eink/Backlight/LatchingBacklight.h" +#include "graphics/eink/Panels/GDEY0154D67.h" -// Applets -#include "graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.h" -#include "graphics/niche/InkHUD/Applets/User/DM/DMApplet.h" -#include "graphics/niche/InkHUD/Applets/User/FavoritesMap/FavoritesMapApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.h" -#include "graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.h" -#include "graphics/niche/InkHUD/Applets/User/RecentsList/RecentsListApplet.h" -#include "graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" - -// Shared NicheGraphics components -// -------------------------------- -#include "graphics/niche/Drivers/Backlight/LatchingBacklight.h" -#include "graphics/niche/Drivers/EInk/GDEY0154D67.h" +#ifdef MESHTASTIC_INCLUDE_INKHUD +#include "graphics/niche/Applets/User/AllMessage/AllMessageApplet.h" +#include "graphics/niche/Applets/User/DM/DMApplet.h" +#include "graphics/niche/Applets/User/FavoritesMap/FavoritesMapApplet.h" +#include "graphics/niche/Applets/User/Heard/HeardApplet.h" +#include "graphics/niche/Applets/User/Positions/PositionsApplet.h" +#include "graphics/niche/Applets/User/RecentsList/RecentsListApplet.h" +#include "graphics/niche/Applets/User/ThreadedMessage/ThreadedMessageApplet.h" +#include "graphics/niche/InkHUD.h" #include "graphics/niche/Inputs/TwoButton.h" -// Special case - fix T-Echo's touch button -// ---------------------------------------- -// On a handful of T-Echos, LoRa TX triggers the capacitive touch -// To avoid this, we lockout the button during TX #include "mesh/RadioLibInterface.h" +#endif +#ifdef MESHTASTIC_INCLUDE_INKHUD void setupNicheGraphics() { using namespace NicheGraphics; - // SPI - // ----------------------------- - - // For NRF52 platforms, SPI pins are defined in variant.h - SPI1.begin(); - - // E-Ink Driver - // ----------------------------- - - Drivers::EInk *driver = new Drivers::GDEY0154D67; - driver->begin(&SPI1, PIN_EINK_DC, PIN_EINK_CS, PIN_EINK_BUSY, PIN_EINK_RES); - - // InkHUD - // ---------------------------- + auto *panel = new Panels::GDEY0154D67(); + Drivers::EInk *driver = panel->create(); InkHUD::InkHUD *inkhud = InkHUD::InkHUD::getInstance(); - - // Set the E-Ink driver inkhud->setDriver(driver); - - // Set how many FAST updates per FULL update - // Set how unhealthy additional FAST updates beyond this number are inkhud->setDisplayResilience(20, 1.5); - // Select fonts InkHUD::Applet::fontLarge = FREESANS_12PT_WIN1252; InkHUD::Applet::fontMedium = FREESANS_9PT_WIN1252; InkHUD::Applet::fontSmall = FREESANS_6PT_WIN1252; - // Customize default settings - inkhud->persistence->settings.userTiles.maxCount = 2; // Two applets side-by-side - inkhud->persistence->settings.rotation = 3; // 270 degrees clockwise - inkhud->persistence->settings.optionalFeatures.batteryIcon = true; // Device definitely has a battery - inkhud->persistence->settings.optionalMenuItems.backlight = true; // Until proves capacitive button works by touching it + inkhud->persistence->settings.userTiles.maxCount = 2; + inkhud->persistence->settings.rotation = 3; + inkhud->persistence->settings.optionalFeatures.batteryIcon = true; + inkhud->persistence->settings.optionalMenuItems.backlight = true; - // Setup backlight controller - // Note: AUX button attached further down Drivers::LatchingBacklight *backlight = Drivers::LatchingBacklight::getInstance(); backlight->setPin(PIN_EINK_EN); - // Pick applets - // Note: order of applets determines priority of "auto-show" feature - inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); // Activated, autoshown - inkhud->addApplet("DMs", new InkHUD::DMApplet); // - - inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); // - - inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); // - - inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); // Activated - inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet); // - - inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // - - inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, no autoshow, default on tile 0 + inkhud->addApplet("All Messages", new InkHUD::AllMessageApplet, true, true); + inkhud->addApplet("DMs", new InkHUD::DMApplet); + inkhud->addApplet("Channel 0", new InkHUD::ThreadedMessageApplet(0)); + inkhud->addApplet("Channel 1", new InkHUD::ThreadedMessageApplet(1)); + inkhud->addApplet("Positions", new InkHUD::PositionsApplet, true); + inkhud->addApplet("Favorites Map", new InkHUD::FavoritesMapApplet); + inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); + inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); - // Start running InkHUD inkhud->begin(); - // Buttons - // -------------------------- - - Inputs::TwoButton *buttons = Inputs::TwoButton::getInstance(); // Shared NicheGraphics component - - // #0: Main User Button + Inputs::TwoButton *buttons = Inputs::TwoButton::getInstance(); buttons->setWiring(0, Inputs::TwoButton::getUserButtonPin()); buttons->setTiming(0, 75, 500); buttons->setHandlerShortPress(0, [inkhud]() { inkhud->shortpress(); }); buttons->setHandlerLongPress(0, [inkhud]() { inkhud->longpress(); }); - // #1: Aux Button (Capacitive Touch Button) - // - short: momentary backlight - // - long: latch backlight on buttons->setWiring(1, PIN_BUTTON_TOUCH); - buttons->setTiming(1, 50, 5000); // 5 seconds before latch - limited by T-Echo's capacitive touch IC - + buttons->setTiming(1, 50, 5000); buttons->setHandlerDown(1, [inkhud, backlight]() { - // Discard the button press if radio is active - // Rare hardware fault: LoRa activity triggers touch button if (!RadioLibInterface::instance || RadioLibInterface::instance->isSending()) return; - - // Backlight on (while held) backlight->peek(); - - // Handler has run, which confirms touch button wasn't removed as part of DIY build. - // No longer need the fallback backlight toggle in menu. inkhud->persistence->settings.optionalMenuItems.backlight = false; }); - buttons->setHandlerLongPress(1, [backlight]() { backlight->latch(); }); buttons->setHandlerShortPress(1, [backlight]() { backlight->off(); }); - // Begin handling button events buttons->start(); } +#else +void setupNicheGraphics() {} +NicheGraphics::BaseUIEInkDisplay *setupNicheGraphicsBaseUI() +{ + auto *panel = new NicheGraphics::Panels::GDEY0154D67(); + NicheGraphics::Drivers::EInk *driver = panel->create(); + auto *display = new NicheGraphics::BaseUIEInkDisplay(driver, panel->rotation()); + display->setDisplayResilience(20, 1.5f); + return display; +} #endif + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS diff --git a/variants/nrf52840/t-echo/platformio.ini b/variants/nrf52840/t-echo/platformio.ini index 58ad029ae..138d21d58 100644 --- a/variants/nrf52840/t-echo/platformio.ini +++ b/variants/nrf52840/t-echo/platformio.ini @@ -1,4 +1,3 @@ -; Using original screen class [env:t-echo] custom_meshtastic_hw_model = 7 custom_meshtastic_hw_model_slug = T_ECHO @@ -10,28 +9,20 @@ custom_meshtastic_images = t-echo.svg custom_meshtastic_tags = LilyGo custom_meshtastic_has_ink_hud = true -extends = nrf52840_base +extends = nrf52840_base, niche board = t-echo board_level = pr board_check = true debug_tool = jlink -# add -DCFG_SYSVIEW if you want to use the Segger systemview tool for OS profiling. build_flags = ${nrf52840_base.build_flags} + ${niche.build_flags} -Ivariants/nrf52840/t-echo - -DEINK_DISPLAY_MODEL=GxEPD2_154_D67 - -DEINK_WIDTH=200 - -DEINK_HEIGHT=200 -DUSE_EINK - -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk - -DEINK_LIMIT_FASTREFRESH=20 ; How many consecutive fast-refreshes are permitted - -DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/t-echo> -lib_deps = +build_src_filter = ${nrf52_base.build_src_filter} ${niche.build_src_filter} +<../variants/nrf52840/t-echo> +lib_deps = ${nrf52840_base.lib_deps} - # renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master - https://github.com/meshtastic/GxEPD2/archive/c7eb4c3c167cf396ef4f541cc5d4c6aa42f3c46b.zip # renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib lewisxhe/SensorLib@0.3.4 ;upload_protocol = fs @@ -42,15 +33,15 @@ board = t-echo board_level = pr board_check = true debug_tool = jlink -build_flags = +build_flags = ${nrf52840_base.build_flags} ${inkhud.build_flags} -I variants/nrf52840/t-echo -build_src_filter = - ${nrf52_base.build_src_filter} +build_src_filter = + ${nrf52_base.build_src_filter} ${inkhud.build_src_filter} +<../variants/nrf52840/t-echo> -lib_deps = +lib_deps = ${inkhud.lib_deps} ; InkHUD libs first, so we get GFXRoot instead of AdafruitGFX ${nrf52840_base.lib_deps} # renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib