From d20713df54bd617c819e960870981a8f9f8909de Mon Sep 17 00:00:00 2001 From: spiff72 Date: Tue, 24 Dec 2024 00:19:35 -0500 Subject: [PATCH 01/23] Updated to fix compilation errors, adapt to some of the new WLED details, and force an older version of dependent library TFT_eSPI. --- usermods/TTGO-T-Display/README.md | 38 ++++---------- .../TTGO-T-Display/platformio_override.ini | 51 +++++++++++++++++-- usermods/TTGO-T-Display/usermod.cpp | 51 ++++++++++++------- 3 files changed, 89 insertions(+), 51 deletions(-) diff --git a/usermods/TTGO-T-Display/README.md b/usermods/TTGO-T-Display/README.md index 439f9832dd..d999748879 100644 --- a/usermods/TTGO-T-Display/README.md +++ b/usermods/TTGO-T-Display/README.md @@ -15,6 +15,8 @@ I have designed a 3D printed case around this board and an ["ElectroCookie"](htt Based on a rework of the ssd1306_i2c_oled_u8g2 usermod from the WLED repo. +In December 2024, I made some changes to fix compilation errors, and modified the platformio_override.ini file to include ALL changes needed (no more need to edit the origiginal "stock" platformio.ini file). I am also forcing an older version of the TFT_eSPI library. + ## Hardware ![Hardware](assets/ttgo_hardware1.png) ![Hardware](assets/ttgo-tdisplay-enclosure1a.png) @@ -37,38 +39,18 @@ Functionality checked with: * As with all usermods, copy the usermod.cpp file from the TTGO-T-Display usermod folder to the wled00 folder (replacing the default usermod.cpp file). ## Platformio Requirements -### Platformio.ini changes -Under the root folder of the project, in the `platformio.ini` file, uncomment the `TFT_eSPI` line within the [common] section, under `lib_deps`: -```ini -# platformio.ini -... -[common] -... -lib_deps = - ... - #For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line - #TFT_eSPI -... -``` -In the `platformio.ini` file, you must change the environment setup to build for just the esp32dev platform as follows: +### Platformio_override.ini (added) +Copy the `platformio_override.ini` file which is contained in the `usermods/TTGO-T-Display/` folder into the root of your project folder. This file contains an override that remaps the button pin of WLED to use the on-board button to the right of the USB-C connector (when viewed with the port oriented downward - see hardware photo). -Comment out the line described below: -```ini -# Release binaries -; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, esp32s2_saola, esp32c3 -``` -and uncomment the following line in the 'Single binaries' section: -```ini -default_envs = esp32dev -``` -Save the `platformio.ini` file. Once saved, the required library files should be automatically downloaded for modifications in a later step. +### Platformio.ini changes +This file isn't actually changed, but it is helpful to save the file in the VS Code application. This should trigger the download of the library dependencies. -### Platformio_overrides.ini (added) -Copy the `platformio_overrides.ini` file which is contained in the `usermods/TTGO-T-Display/` folder into the root of your project folder. This file contains an override that remaps the button pin of WLED to use the on-board button to the right of the USB-C connector (when viewed with the port oriented downward - see hardware photo). +### Change to the WLED_T-Display environment +This should appear as an option in the bottom toolbar. ### TFT_eSPI Library Adjustments (board selection) -You need to modify a file in the `TFT_eSPI` library to select the correct board. If you followed the directions to modify and save the `platformio.ini` file above, the `User_Setup_Select.h` file can be found in the `/.pio/libdeps/esp32dev/TFT_eSPI_ID1559` folder. +You need to modify a file in the `TFT_eSPI` library to select the correct board. If you followed the directions to modify and save the `platformio.ini` file above, the `User_Setup_Select.h` file can be found in the `/.pio/libdeps/WLED_T-Display/TFT_eSPI` folder. Modify the `User_Setup_Select.h` file as follows: * Comment out the following line (which is the 'default' setup file): @@ -87,5 +69,7 @@ xtensa-esp32-elf-g++: fatal error: no input files ``` try building again. Sometimes this happens on the first build attempt and subsequent attempts build correctly. +Once the compilation is done and loaded onto the TTGO T-Display module, you may need to complete the initial setup of WLED via the AP interface, configure the WiFi settings, let it restart and then go to the LED preferences option in the WLED web interface. The Data GPIO pin needs to be set to pin 2 (or whatever pin you used), and then reboot WLED on the device. This should get the display working. I am unable to get the overrides to change the initial pin setting of 16 (which conflicts with the display). + ## Arduino IDE - UNTESTED diff --git a/usermods/TTGO-T-Display/platformio_override.ini b/usermods/TTGO-T-Display/platformio_override.ini index 7e42d9a54a..5142875336 100644 --- a/usermods/TTGO-T-Display/platformio_override.ini +++ b/usermods/TTGO-T-Display/platformio_override.ini @@ -1,8 +1,49 @@ -[env:esp32dev] -build_flags = ${common.build_flags_esp32} -; PIN defines - uncomment and change, if needed: -; -D LEDPIN=2 - -D BTNPIN=35 +# Example PlatformIO Project Configuration Override +# ------------------------------------------------------------------------------ +# Copy to platformio_override.ini to activate overrides +# ------------------------------------------------------------------------------ +# Please visit documentation: https://docs.platformio.org/page/projectconf.html + +[platformio] +#default_envs = WLED_tasmota_1M # define as many as you need +default_envs = WLED_T-Display # define as many as you need +#---------- +# SAMPLE +#---------- +[env:WLED_T-Display] + +board = esp32dev +upload_speed = 921600 +monitor_speed = 115200 +platform = ${esp32.platform} +platform_packages = ${esp32.platform_packages} +build_unflags = ${common.build_unflags} +build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32\" + ${esp32.AR_build_flags} ; -D IRPIN=4 ; -D RLYPIN=12 ; -D RLYMDE=1 +; -D WLED_DISABLE_BROWNOUT_DET +;PIN defines - uncomment and change, if needed: + -D DATA_PINS=2 +;or use this for multiple outputs + ;-D DATA_PINS=1,3 + -D BTNPIN=35 +; -D IRPIN=4 +; -D RLYPIN=12 +; -D RLYMDE=1 +; -D RLYODRAIN=0 +; -D LED_BUILTIN=2 # GPIO of built-in LED +lib_deps = ${esp32.lib_deps} + #For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line + TFT_eSPI @ 2.5.33 + ;https://github.com/Bodmer/TFT_eSPI/archive/refs/tags/2.1.4.zip + ;https://github.com/Bodmer/TFT_eSPI/archive/refs/tags/2.1.9.zip + kosme/arduinoFFT @ 2.0.1 +monitor_filters = esp32_exception_decoder +board_build.partitions = ${esp32.default_partitions} + +; # additional build flags for audioreactive +; AR_build_flags = -D USERMOD_AUDIOREACTIVE +; -D sqrt_internal=sqrtf ;; -fsingle-precision-constant ;; forces ArduinoFFT to use float math (2x faster) +; AR_lib_deps = kosme/arduinoFFT @ 2.0.1 diff --git a/usermods/TTGO-T-Display/usermod.cpp b/usermods/TTGO-T-Display/usermod.cpp index d8dcb29996..39673fde66 100644 --- a/usermods/TTGO-T-Display/usermod.cpp +++ b/usermods/TTGO-T-Display/usermod.cpp @@ -21,27 +21,28 @@ //Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t) #include "wled.h" -#include #include +#include #include "WiFi.h" #include -#ifndef TFT_DISPOFF -#define TFT_DISPOFF 0x28 -#endif +// #ifndef TFT_DISPOFF +// #define TFT_DISPOFF 0x28 +// #endif -#ifndef TFT_SLPIN -#define TFT_SLPIN 0x10 -#endif +// #ifndef TFT_SLPIN +// #define TFT_SLPIN 0x10 +// #endif -#define TFT_MOSI 19 -#define TFT_SCLK 18 -#define TFT_CS 5 -#define TFT_DC 16 -#define TFT_RST 23 +// #define TFT_MOSI 19 +// #define TFT_SCLK 18 +// #define TFT_CS 5 +// #define TFT_DC 16 +// #define TFT_RST 23 -#define TFT_BL 4 // Display backlight control pin -#define ADC_EN 14 // Used for enabling battery voltage measurements - not used in this program +//#define TFT_BL 4 // Display backlight control pin +//#define ADC_EN 14 // Used for enabling battery voltage measurements - not used in this program +//#define WLED_WATCHDOG_TIMEOUT 3 TFT_eSPI tft = TFT_eSPI(135, 240); // Invoke custom library @@ -53,11 +54,14 @@ void userSetup() { tft.setRotation(3); //Rotation here is set up for the text to be readable with the port on the left. Use 1 to flip. tft.fillScreen(TFT_BLACK); tft.setTextSize(2); + //tft.setTextSize(1); tft.setTextColor(TFT_WHITE); tft.setCursor(1, 10); tft.setTextDatum(MC_DATUM); tft.setTextSize(3); + //tft.setTextSize(1); tft.print("Loading..."); + Serial.println("Loading..."); if (TFT_BL > 0) { // TFT_BL has been set in the TFT_eSPI library in the User Setup file TTGO_T_Display.h pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode @@ -83,7 +87,7 @@ uint8_t knownMode = 0; uint8_t knownPalette = 0; uint8_t tftcharwidth = 19; // Number of chars that fit on screen with text size set to 2 -long lastUpdate = 0; +long lastUpdate_mod = 0; long lastRedraw = 0; bool displayTurnedOff = false; // How often we are redrawing screen @@ -92,10 +96,10 @@ bool displayTurnedOff = false; void userLoop() { // Check if we time interval for redrawing passes. - if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS) { + if (millis() - lastUpdate_mod < USER_LOOP_REFRESH_RATE_MS) { return; } - lastUpdate = millis(); + lastUpdate_mod = millis(); // Turn off display after 5 minutes with no change. if(!displayTurnedOff && millis() - lastRedraw > 5*60*1000) { @@ -140,6 +144,7 @@ void userLoop() { knownPalette = strip.getMainSegment().palette; tft.fillScreen(TFT_BLACK); + //tft.setTextColor(TFT_WHITE); tft.setTextSize(2); // First row with Wifi name tft.setCursor(1, 1); @@ -156,7 +161,7 @@ void userLoop() { // tft.print(apPass); // else // tft.print(knownIp); - + Serial.println("Print known AP"); if (apActive) { tft.print("AP IP: "); tft.print(knownIp); @@ -165,6 +170,7 @@ void userLoop() { tft.print(apPass); } else { + Serial.println("Print IP"); tft.print("IP: "); tft.print(knownIp); tft.setCursor(1,46); @@ -180,16 +186,23 @@ void userLoop() { char lineBuffer[tftcharwidth+1]; extractModeName(knownMode, JSON_mode_names, lineBuffer, tftcharwidth); tft.print(lineBuffer); + Serial.println("Print mode name"); // Fourth row with palette name tft.setCursor(1, 90); extractModeName(knownPalette, JSON_palette_names, lineBuffer, tftcharwidth); tft.print(lineBuffer); + Serial.println("Print palette"); // Fifth row with estimated mA usage tft.setCursor(1, 112); // Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate). - tft.print(strip.currentMilliamps); + //tft.print(strip.currentMilliamps); + tft.print(BusManager::currentMilliamps()); + Serial.print(BusManager::currentMilliamps()); + Serial.println(" mA (estimated)"); + //tft.print("test "); tft.print("mA (estimated)"); + Serial.println("Print estimated current"); } From e5abc4670ba11e8a332debd8b3da5d1c66e86316 Mon Sep 17 00:00:00 2001 From: spiff72 Date: Tue, 24 Dec 2024 01:22:51 -0500 Subject: [PATCH 02/23] fixed forced version number of TFT_eSPI in platformio_override.ini --- usermods/TTGO-T-Display/platformio_override.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usermods/TTGO-T-Display/platformio_override.ini b/usermods/TTGO-T-Display/platformio_override.ini index 5142875336..2a9f62d5e6 100644 --- a/usermods/TTGO-T-Display/platformio_override.ini +++ b/usermods/TTGO-T-Display/platformio_override.ini @@ -36,8 +36,8 @@ build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\" ; -D LED_BUILTIN=2 # GPIO of built-in LED lib_deps = ${esp32.lib_deps} #For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line - TFT_eSPI @ 2.5.33 - ;https://github.com/Bodmer/TFT_eSPI/archive/refs/tags/2.1.4.zip + ;TFT_eSPI @ 2.5.33 + https://github.com/Bodmer/TFT_eSPI/archive/refs/tags/2.1.4.zip ;https://github.com/Bodmer/TFT_eSPI/archive/refs/tags/2.1.9.zip kosme/arduinoFFT @ 2.0.1 monitor_filters = esp32_exception_decoder From ef7c80793b48a9d8923bea7c4d7c67acc3f8b62d Mon Sep 17 00:00:00 2001 From: spiff72 Date: Tue, 24 Dec 2024 13:04:38 -0500 Subject: [PATCH 03/23] Update platformio_override.ini Revised WLED_RELEASE_NAME to "WLED_T-Display" --- usermods/TTGO-T-Display/platformio_override.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usermods/TTGO-T-Display/platformio_override.ini b/usermods/TTGO-T-Display/platformio_override.ini index 2a9f62d5e6..6db9f276fa 100644 --- a/usermods/TTGO-T-Display/platformio_override.ini +++ b/usermods/TTGO-T-Display/platformio_override.ini @@ -18,7 +18,7 @@ monitor_speed = 115200 platform = ${esp32.platform} platform_packages = ${esp32.platform_packages} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32\" +build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"WLED_T-Display\" ${esp32.AR_build_flags} ; -D IRPIN=4 ; -D RLYPIN=12 From 76a08c76788f323d9748c9867ec68e2f1159b360 Mon Sep 17 00:00:00 2001 From: spiff72 Date: Sat, 4 Jan 2025 22:29:18 -0500 Subject: [PATCH 04/23] adjustments based on feedback --- .../TTGO-T-Display/platformio_override.ini | 25 ++++++++++++------- usermods/TTGO-T-Display/usermod.cpp | 18 ++++++------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/usermods/TTGO-T-Display/platformio_override.ini b/usermods/TTGO-T-Display/platformio_override.ini index 6db9f276fa..8b1b28ee2f 100644 --- a/usermods/TTGO-T-Display/platformio_override.ini +++ b/usermods/TTGO-T-Display/platformio_override.ini @@ -23,17 +23,17 @@ build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\" ; -D IRPIN=4 ; -D RLYPIN=12 ; -D RLYMDE=1 -; -D WLED_DISABLE_BROWNOUT_DET +; -D WLED_DISABLE_BROWNOUT_DET ;PIN defines - uncomment and change, if needed: -D DATA_PINS=2 ;or use this for multiple outputs - ;-D DATA_PINS=1,3 +; -D DATA_PINS=1,3 -D BTNPIN=35 -; -D IRPIN=4 -; -D RLYPIN=12 -; -D RLYMDE=1 -; -D RLYODRAIN=0 -; -D LED_BUILTIN=2 # GPIO of built-in LED +; -D IRPIN=4 +; -D RLYPIN=12 +; -D RLYMDE=1 +; -D RLYODRAIN=0 +; -D LED_BUILTIN=2 # GPIO of built-in LED lib_deps = ${esp32.lib_deps} #For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line ;TFT_eSPI @ 2.5.33 @@ -44,6 +44,13 @@ monitor_filters = esp32_exception_decoder board_build.partitions = ${esp32.default_partitions} ; # additional build flags for audioreactive -; AR_build_flags = -D USERMOD_AUDIOREACTIVE -; -D sqrt_internal=sqrtf ;; -fsingle-precision-constant ;; forces ArduinoFFT to use float math (2x faster) +; Use Audioreactive usermod and configure I2S microphone +; ${esp32.AR_build_flags} ;; default flags required to properly configure ArduinoFFT +; ;; don't forget to add ArduinoFFT to your libs_deps: ${esp32.AR_lib_deps} +; -D AUDIOPIN=-1 +; -D DMTYPE=1 # 0-analog/disabled, 1-I2S generic, 2-ES7243, 3-SPH0645, 4-I2S+mclk, 5-I2S PDM +; -D I2S_SDPIN=36 +; -D I2S_WSPIN=23 +; -D I2S_CKPIN=19 +; ${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE ; AR_lib_deps = kosme/arduinoFFT @ 2.0.1 diff --git a/usermods/TTGO-T-Display/usermod.cpp b/usermods/TTGO-T-Display/usermod.cpp index 39673fde66..e3c78b8c7e 100644 --- a/usermods/TTGO-T-Display/usermod.cpp +++ b/usermods/TTGO-T-Display/usermod.cpp @@ -49,7 +49,7 @@ TFT_eSPI tft = TFT_eSPI(135, 240); // Invoke custom library //gets called once at boot. Do all initialization that doesn't depend on network here void userSetup() { Serial.begin(115200); - Serial.println("Start"); + DEBUG_PRINTLN("Start"); tft.init(); tft.setRotation(3); //Rotation here is set up for the text to be readable with the port on the left. Use 1 to flip. tft.fillScreen(TFT_BLACK); @@ -61,7 +61,7 @@ void userSetup() { tft.setTextSize(3); //tft.setTextSize(1); tft.print("Loading..."); - Serial.println("Loading..."); + DEBUG_PRINTLN("Loading..."); if (TFT_BL > 0) { // TFT_BL has been set in the TFT_eSPI library in the User Setup file TTGO_T_Display.h pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode @@ -161,7 +161,7 @@ void userLoop() { // tft.print(apPass); // else // tft.print(knownIp); - Serial.println("Print known AP"); + DEBUG_PRINTLN("Print known AP"); if (apActive) { tft.print("AP IP: "); tft.print(knownIp); @@ -170,7 +170,7 @@ void userLoop() { tft.print(apPass); } else { - Serial.println("Print IP"); + DEBUG_PRINTLN("Print IP"); tft.print("IP: "); tft.print(knownIp); tft.setCursor(1,46); @@ -186,23 +186,23 @@ void userLoop() { char lineBuffer[tftcharwidth+1]; extractModeName(knownMode, JSON_mode_names, lineBuffer, tftcharwidth); tft.print(lineBuffer); - Serial.println("Print mode name"); + DEBUG_PRINTLN("Print mode name"); // Fourth row with palette name tft.setCursor(1, 90); extractModeName(knownPalette, JSON_palette_names, lineBuffer, tftcharwidth); tft.print(lineBuffer); - Serial.println("Print palette"); + DEBUG_PRINTLN("Print palette"); // Fifth row with estimated mA usage tft.setCursor(1, 112); // Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate). //tft.print(strip.currentMilliamps); tft.print(BusManager::currentMilliamps()); - Serial.print(BusManager::currentMilliamps()); - Serial.println(" mA (estimated)"); + DEBUG_PRINTLN(BusManager::currentMilliamps()); + DEBUG_PRINTLN(" mA (estimated)"); //tft.print("test "); tft.print("mA (estimated)"); - Serial.println("Print estimated current"); + DEBUG_PRINTLN("Print estimated current"); } From 64d5d07534b973c7f5dc91daf993cdc476cd67a1 Mon Sep 17 00:00:00 2001 From: spiff72 Date: Sun, 5 Jan 2025 00:33:45 -0500 Subject: [PATCH 05/23] Updated readme file and tweaked indents on the platformio_override.ini file. --- usermods/TTGO-T-Display/README.md | 4 ++-- usermods/TTGO-T-Display/platformio_override.ini | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/usermods/TTGO-T-Display/README.md b/usermods/TTGO-T-Display/README.md index d999748879..84aed5eabd 100644 --- a/usermods/TTGO-T-Display/README.md +++ b/usermods/TTGO-T-Display/README.md @@ -44,7 +44,7 @@ Functionality checked with: Copy the `platformio_override.ini` file which is contained in the `usermods/TTGO-T-Display/` folder into the root of your project folder. This file contains an override that remaps the button pin of WLED to use the on-board button to the right of the USB-C connector (when viewed with the port oriented downward - see hardware photo). ### Platformio.ini changes -This file isn't actually changed, but it is helpful to save the file in the VS Code application. This should trigger the download of the library dependencies. +Once the platformio_override.ini file has been copied as described above, the platformio.ini file isn't actually changed, but it is helpful to save the platformio.ini file in the VS Code application. This should trigger the download of the library dependencies. ### Change to the WLED_T-Display environment This should appear as an option in the bottom toolbar. @@ -69,7 +69,7 @@ xtensa-esp32-elf-g++: fatal error: no input files ``` try building again. Sometimes this happens on the first build attempt and subsequent attempts build correctly. -Once the compilation is done and loaded onto the TTGO T-Display module, you may need to complete the initial setup of WLED via the AP interface, configure the WiFi settings, let it restart and then go to the LED preferences option in the WLED web interface. The Data GPIO pin needs to be set to pin 2 (or whatever pin you used), and then reboot WLED on the device. This should get the display working. I am unable to get the overrides to change the initial pin setting of 16 (which conflicts with the display). +Once the compilation is done and loaded onto the TTGO T-Display module, the display should show "Loading...", and then it will show the IP of the WLED access point. ## Arduino IDE - UNTESTED diff --git a/usermods/TTGO-T-Display/platformio_override.ini b/usermods/TTGO-T-Display/platformio_override.ini index 8b1b28ee2f..9f2597cb7b 100644 --- a/usermods/TTGO-T-Display/platformio_override.ini +++ b/usermods/TTGO-T-Display/platformio_override.ini @@ -27,7 +27,7 @@ build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\" ;PIN defines - uncomment and change, if needed: -D DATA_PINS=2 ;or use this for multiple outputs -; -D DATA_PINS=1,3 +; -D DATA_PINS=1,3 -D BTNPIN=35 ; -D IRPIN=4 ; -D RLYPIN=12 From 5502d37d7172197f3e9cee6bec5534a8d60d8f70 Mon Sep 17 00:00:00 2001 From: spiff72 Date: Tue, 11 Mar 2025 21:22:47 -0400 Subject: [PATCH 06/23] Update usermod.cpp Removed Serial.begin statement from userSetup --- usermods/TTGO-T-Display/usermod.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/usermods/TTGO-T-Display/usermod.cpp b/usermods/TTGO-T-Display/usermod.cpp index e3c78b8c7e..803727ea2e 100644 --- a/usermods/TTGO-T-Display/usermod.cpp +++ b/usermods/TTGO-T-Display/usermod.cpp @@ -48,7 +48,6 @@ TFT_eSPI tft = TFT_eSPI(135, 240); // Invoke custom library //gets called once at boot. Do all initialization that doesn't depend on network here void userSetup() { - Serial.begin(115200); DEBUG_PRINTLN("Start"); tft.init(); tft.setRotation(3); //Rotation here is set up for the text to be readable with the port on the left. Use 1 to flip. From 987bf1f76e01027954b6cf8f64b169d6031b9e3c Mon Sep 17 00:00:00 2001 From: Will Miles Date: Sun, 16 Mar 2025 21:05:49 -0400 Subject: [PATCH 07/23] Make usermod v1 calls weak This allows a v1 usermod to be linked as a library, too. Of course, only one such usermod can be included, or a linker error will result. --- wled00/usermod.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wled00/usermod.cpp b/wled00/usermod.cpp index 40fda83b07..60199d09c6 100644 --- a/wled00/usermod.cpp +++ b/wled00/usermod.cpp @@ -11,19 +11,19 @@ //Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t) //gets called once at boot. Do all initialization that doesn't depend on network here -void userSetup() +__attribute__((weak)) void userSetup() { } //gets called every time WiFi is (re-)connected. Initialize own network interfaces here -void userConnected() +__attribute__((weak)) void userConnected() { } //loop. You can use "if (WLED_CONNECTED)" to check for successful connection -void userLoop() +__attribute__((weak)) void userLoop() { } From c7a35a75597220ef21b5a38fac5a26a86454e028 Mon Sep 17 00:00:00 2001 From: Will Miles Date: Mon, 17 Mar 2025 19:14:52 -0400 Subject: [PATCH 08/23] usermods/TTGO-T-Display: Make locals static Avoids any collisions with other source files. --- usermods/TTGO-T-Display/usermod.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/usermods/TTGO-T-Display/usermod.cpp b/usermods/TTGO-T-Display/usermod.cpp index 803727ea2e..2f1cf004f7 100644 --- a/usermods/TTGO-T-Display/usermod.cpp +++ b/usermods/TTGO-T-Display/usermod.cpp @@ -75,20 +75,20 @@ void userSetup() { void userConnected() {} // needRedraw marks if redraw is required to prevent often redrawing. -bool needRedraw = true; +static bool needRedraw = true; // Next variables hold the previous known values to determine if redraw is // required. -String knownSsid = ""; -IPAddress knownIp; -uint8_t knownBrightness = 0; -uint8_t knownMode = 0; -uint8_t knownPalette = 0; -uint8_t tftcharwidth = 19; // Number of chars that fit on screen with text size set to 2 - -long lastUpdate_mod = 0; -long lastRedraw = 0; -bool displayTurnedOff = false; +static String knownSsid = ""; +static IPAddress knownIp; +static uint8_t knownBrightness = 0; +static uint8_t knownMode = 0; +static uint8_t knownPalette = 0; +static uint8_t tftcharwidth = 19; // Number of chars that fit on screen with text size set to 2 + +static long lastUpdate_mod = 0; +static long lastRedraw = 0; +static bool displayTurnedOff = false; // How often we are redrawing screen #define USER_LOOP_REFRESH_RATE_MS 5000 From 5e6f243e739747555111c0db9bb74b4ba0bd05db Mon Sep 17 00:00:00 2001 From: Will Miles Date: Mon, 17 Mar 2025 19:57:58 -0400 Subject: [PATCH 09/23] usermods/TTGO-T-Display: Make into library Convert to a library-based usermod, allowing easy inclusion with the custom_usermods feature. The BTNPIN override and display model selection are handled via a build python script, so it is no longer necessary modify the TFT_eSPI library install. --- usermods/TTGO-T-Display/README.md | 22 ------------------- usermods/TTGO-T-Display/library.json | 9 ++++++++ .../TTGO-T-Display/platformio_override.ini | 19 +--------------- usermods/TTGO-T-Display/set_build_flags.py | 21 ++++++++++++++++++ 4 files changed, 31 insertions(+), 40 deletions(-) create mode 100644 usermods/TTGO-T-Display/library.json create mode 100644 usermods/TTGO-T-Display/set_build_flags.py diff --git a/usermods/TTGO-T-Display/README.md b/usermods/TTGO-T-Display/README.md index 84aed5eabd..78eddc8c5b 100644 --- a/usermods/TTGO-T-Display/README.md +++ b/usermods/TTGO-T-Display/README.md @@ -49,27 +49,5 @@ Once the platformio_override.ini file has been copied as described above, the pl ### Change to the WLED_T-Display environment This should appear as an option in the bottom toolbar. -### TFT_eSPI Library Adjustments (board selection) -You need to modify a file in the `TFT_eSPI` library to select the correct board. If you followed the directions to modify and save the `platformio.ini` file above, the `User_Setup_Select.h` file can be found in the `/.pio/libdeps/WLED_T-Display/TFT_eSPI` folder. - -Modify the `User_Setup_Select.h` file as follows: -* Comment out the following line (which is the 'default' setup file): -```ini -//#include // Default setup is root library folder -``` -* Uncomment the following line (which points to the setup file for the TTGO T-Display): -```ini -#include // Setup file for ESP32 and TTGO T-Display ST7789V SPI bus TFT -``` - -Build the file. If you see a failure like this: -```ini -xtensa-esp32-elf-g++: error: wled00\wled00.ino.cpp: No such file or directory -xtensa-esp32-elf-g++: fatal error: no input files -``` -try building again. Sometimes this happens on the first build attempt and subsequent attempts build correctly. - Once the compilation is done and loaded onto the TTGO T-Display module, the display should show "Loading...", and then it will show the IP of the WLED access point. -## Arduino IDE -- UNTESTED diff --git a/usermods/TTGO-T-Display/library.json b/usermods/TTGO-T-Display/library.json new file mode 100644 index 0000000000..0bc683235d --- /dev/null +++ b/usermods/TTGO-T-Display/library.json @@ -0,0 +1,9 @@ +{ + "name:": "TTGO-T-Display", + "dependencies": { + "TFT_eSPI" : "https://github.com/Bodmer/TFT_eSPI/archive/refs/tags/2.1.4.zip" + }, + "build": { + "extraScript": "set_build_flags.py" + } +} diff --git a/usermods/TTGO-T-Display/platformio_override.ini b/usermods/TTGO-T-Display/platformio_override.ini index 9f2597cb7b..b85b450e51 100644 --- a/usermods/TTGO-T-Display/platformio_override.ini +++ b/usermods/TTGO-T-Display/platformio_override.ini @@ -19,7 +19,6 @@ platform = ${esp32.platform} platform_packages = ${esp32.platform_packages} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"WLED_T-Display\" - ${esp32.AR_build_flags} ; -D IRPIN=4 ; -D RLYPIN=12 ; -D RLYMDE=1 @@ -35,22 +34,6 @@ build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\" ; -D RLYODRAIN=0 ; -D LED_BUILTIN=2 # GPIO of built-in LED lib_deps = ${esp32.lib_deps} - #For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line - ;TFT_eSPI @ 2.5.33 - https://github.com/Bodmer/TFT_eSPI/archive/refs/tags/2.1.4.zip - ;https://github.com/Bodmer/TFT_eSPI/archive/refs/tags/2.1.9.zip - kosme/arduinoFFT @ 2.0.1 +custom_usermods = audioreactive TTGO-T-Display monitor_filters = esp32_exception_decoder board_build.partitions = ${esp32.default_partitions} - -; # additional build flags for audioreactive -; Use Audioreactive usermod and configure I2S microphone -; ${esp32.AR_build_flags} ;; default flags required to properly configure ArduinoFFT -; ;; don't forget to add ArduinoFFT to your libs_deps: ${esp32.AR_lib_deps} -; -D AUDIOPIN=-1 -; -D DMTYPE=1 # 0-analog/disabled, 1-I2S generic, 2-ES7243, 3-SPH0645, 4-I2S+mclk, 5-I2S PDM -; -D I2S_SDPIN=36 -; -D I2S_WSPIN=23 -; -D I2S_CKPIN=19 -; ${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE -; AR_lib_deps = kosme/arduinoFFT @ 2.0.1 diff --git a/usermods/TTGO-T-Display/set_build_flags.py b/usermods/TTGO-T-Display/set_build_flags.py new file mode 100644 index 0000000000..80b9919d30 --- /dev/null +++ b/usermods/TTGO-T-Display/set_build_flags.py @@ -0,0 +1,21 @@ +# Select target board +# Ref: https://github.com/Bodmer/TFT_eSPI/wiki/Installing-on-PlatformIO#4-configure-library +# These flags are passed to this library and the TFT_eSPI library only + +Import("env") + +def add_display_setup(tgt_env): + tgt_env.Append(CPPDEFINES=[("USER_SETUP_LOADED", "1")]) + tgt_env.Append(CCFLAGS="-include $PROJECT_LIBDEPS_DIR/$PIOENV/TFT_eSPI/User_Setups/Setup25_TTGO_T_Display.h") + +add_display_setup(env) +for lb in env.GetLibBuilders(): + if lb.name == "TFT_eSPI": + add_display_setup(lb.env) + break + +# Add pin definitions to whole project environment if they haven't otherwise been specified +global_env = DefaultEnvironment() +global_defines = global_env.get("CPPDEFINES", []) +if "BTNPIN" not in [v[0] for v in global_defines if isinstance(v, tuple)]: + global_env.Append(CPPDEFINES=[("BTNPIN", "35")]) From 3338efc42641bb342ea93ff06817aef2ff00af31 Mon Sep 17 00:00:00 2001 From: Will Miles Date: Fri, 18 Jul 2025 18:38:38 -0400 Subject: [PATCH 10/23] TTGO-T-Display: Add libArchive --- usermods/TTGO-T-Display/library.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/usermods/TTGO-T-Display/library.json b/usermods/TTGO-T-Display/library.json index 0bc683235d..6def2d6288 100644 --- a/usermods/TTGO-T-Display/library.json +++ b/usermods/TTGO-T-Display/library.json @@ -4,6 +4,7 @@ "TFT_eSPI" : "https://github.com/Bodmer/TFT_eSPI/archive/refs/tags/2.1.4.zip" }, "build": { - "extraScript": "set_build_flags.py" + "extraScript": "set_build_flags.py", + "libArchive": false } } From d6dcb96b465d043a5be27522b4f67e8b87d4da17 Mon Sep 17 00:00:00 2001 From: Will Miles Date: Fri, 18 Jul 2025 18:38:59 -0400 Subject: [PATCH 11/23] TTGO-T-Display: Use TFT_BACKLIGHT_ON --- usermods/TTGO-T-Display/usermod.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usermods/TTGO-T-Display/usermod.cpp b/usermods/TTGO-T-Display/usermod.cpp index 2f1cf004f7..a3354605e3 100644 --- a/usermods/TTGO-T-Display/usermod.cpp +++ b/usermods/TTGO-T-Display/usermod.cpp @@ -64,7 +64,7 @@ void userSetup() { if (TFT_BL > 0) { // TFT_BL has been set in the TFT_eSPI library in the User Setup file TTGO_T_Display.h pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode - digitalWrite(TFT_BL, HIGH); // Turn backlight on. + digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); // Turn backlight on. } // tft.setRotation(3); @@ -102,7 +102,7 @@ void userLoop() { // Turn off display after 5 minutes with no change. if(!displayTurnedOff && millis() - lastRedraw > 5*60*1000) { - digitalWrite(TFT_BL, LOW); // Turn backlight off. + digitalWrite(TFT_BL, !TFT_BACKLIGHT_ON); // Turn backlight off. displayTurnedOff = true; } From 4bf64099a52f3edf3a1ffed427e70c5e41a534a2 Mon Sep 17 00:00:00 2001 From: Will Miles Date: Fri, 18 Jul 2025 18:40:46 -0400 Subject: [PATCH 12/23] TTGO-T-Display: Support custom_display_setup Allow specifying the display setup file as an environment parameter. Files are looked for anywhere in the include path of WLED itself, and the direct dependencies of the TTGO-T-Display module (ie. TFT_eSPI). --- usermods/TTGO-T-Display/set_build_flags.py | 41 +++++++++++++++++----- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/usermods/TTGO-T-Display/set_build_flags.py b/usermods/TTGO-T-Display/set_build_flags.py index 80b9919d30..18eb342ef2 100644 --- a/usermods/TTGO-T-Display/set_build_flags.py +++ b/usermods/TTGO-T-Display/set_build_flags.py @@ -3,19 +3,44 @@ # These flags are passed to this library and the TFT_eSPI library only Import("env") +import os + + +# Add pin definitions to whole project environment if they haven't otherwise been specified +# WM: This is a feature of the original usermod; I'm not sure if it's globally applicable.. +global_env = DefaultEnvironment() +global_defines = global_env.get("CPPDEFINES", []) +if "BTNPIN" not in [v[0] for v in global_defines if isinstance(v, tuple)]: + global_env.Append(CPPDEFINES=[("BTNPIN", "35")]) + +# Select display setup +display_setup = global_env.GetProjectOption("custom_display_setup","User_Setups/Setup25_TTGO_T_Display.h") + +# Resolve this file's path +def find_in_paths(filename, paths): + for path in paths: + fullpath = os.path.join(str(path), filename) + if os.path.isfile(fullpath): + return fullpath + return None + +lib_builders = env.GetLibBuilders() +search_paths = [global_env["PROJECT_SRC_DIR"], + global_env["PROJECT_INCLUDE_DIR"], + *[incdir for lb in lib_builders for incdir in lb.get_include_dirs()]] + +display_setup_path = find_in_paths(display_setup, search_paths) +if display_setup_path is None: + print(f"Unable to find {display_setup} in any include path - {[str(path) for path in search_paths]}!") + raise RuntimeError("Missing display setup; use 'User_Setups/something.h' for setups out of TFT_eSPI, or put your setup in wled00") def add_display_setup(tgt_env): tgt_env.Append(CPPDEFINES=[("USER_SETUP_LOADED", "1")]) - tgt_env.Append(CCFLAGS="-include $PROJECT_LIBDEPS_DIR/$PIOENV/TFT_eSPI/User_Setups/Setup25_TTGO_T_Display.h") + tgt_env.Append(CCFLAGS=f"-include {display_setup_path}") +# Add it for this library and the TFT_eSPI dependency add_display_setup(env) -for lb in env.GetLibBuilders(): +for lb in lib_builders: if lb.name == "TFT_eSPI": add_display_setup(lb.env) break - -# Add pin definitions to whole project environment if they haven't otherwise been specified -global_env = DefaultEnvironment() -global_defines = global_env.get("CPPDEFINES", []) -if "BTNPIN" not in [v[0] for v in global_defines if isinstance(v, tuple)]: - global_env.Append(CPPDEFINES=[("BTNPIN", "35")]) From e37db433aec03b6fac69a61b417549c1daa6a5ac Mon Sep 17 00:00:00 2001 From: Will Miles Date: Fri, 18 Jul 2025 18:41:15 -0400 Subject: [PATCH 13/23] TTGO-T-Display: Use newer TFT_eSPI Adds better support for newer CPUs. --- usermods/TTGO-T-Display/library.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usermods/TTGO-T-Display/library.json b/usermods/TTGO-T-Display/library.json index 6def2d6288..812478fce9 100644 --- a/usermods/TTGO-T-Display/library.json +++ b/usermods/TTGO-T-Display/library.json @@ -1,7 +1,7 @@ { "name:": "TTGO-T-Display", "dependencies": { - "TFT_eSPI" : "https://github.com/Bodmer/TFT_eSPI/archive/refs/tags/2.1.4.zip" + "TFT_eSPI" : "https://github.com/Bodmer/TFT_eSPI.git#5793878" }, "build": { "extraScript": "set_build_flags.py", From 2f6723ac29cbf067dcecea9b81eedf1cea0faec5 Mon Sep 17 00:00:00 2001 From: Will Miles Date: Thu, 14 Aug 2025 22:18:39 -0400 Subject: [PATCH 14/23] TTGO-T-Display: Convert to v2 usermod --- usermods/TTGO-T-Display/usermod.cpp | 59 ++++++++++++++++------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/usermods/TTGO-T-Display/usermod.cpp b/usermods/TTGO-T-Display/usermod.cpp index a3354605e3..524767823e 100644 --- a/usermods/TTGO-T-Display/usermod.cpp +++ b/usermods/TTGO-T-Display/usermod.cpp @@ -17,9 +17,6 @@ * when a change in the dipslayed info is detected (within a 5 second interval). */ - -//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t) - #include "wled.h" #include #include @@ -44,10 +41,35 @@ //#define ADC_EN 14 // Used for enabling battery voltage measurements - not used in this program //#define WLED_WATCHDOG_TIMEOUT 3 -TFT_eSPI tft = TFT_eSPI(135, 240); // Invoke custom library +// How often we are redrawing screen +#define USER_LOOP_REFRESH_RATE_MS 5000 + +class TTGO_T_DisplayMod : public Usermod { + + // Member variables + TFT_eSPI tft; // Invoke custom library + // needRedraw marks if redraw is required to prevent often redrawing. + bool needRedraw = true; + // Next variables hold the previous known values to determine if redraw is + // required. + String knownSsid = ""; + IPAddress knownIp; + uint8_t knownBrightness = 0; + uint8_t knownMode = 0; + uint8_t knownPalette = 0; + uint8_t tftcharwidth = 19; + + long lastUpdate_mod = 0; + long lastRedraw = 0; + bool displayTurnedOff = false; + +public: + +TTGO_T_DisplayMod() : Usermod() +{} //gets called once at boot. Do all initialization that doesn't depend on network here -void userSetup() { +void setup() override { DEBUG_PRINTLN("Start"); tft.init(); tft.setRotation(3); //Rotation here is set up for the text to be readable with the port on the left. Use 1 to flip. @@ -70,29 +92,8 @@ void userSetup() { // tft.setRotation(3); } -// gets called every time WiFi is (re-)connected. Initialize own network -// interfaces here -void userConnected() {} - -// needRedraw marks if redraw is required to prevent often redrawing. -static bool needRedraw = true; - -// Next variables hold the previous known values to determine if redraw is -// required. -static String knownSsid = ""; -static IPAddress knownIp; -static uint8_t knownBrightness = 0; -static uint8_t knownMode = 0; -static uint8_t knownPalette = 0; -static uint8_t tftcharwidth = 19; // Number of chars that fit on screen with text size set to 2 - -static long lastUpdate_mod = 0; -static long lastRedraw = 0; -static bool displayTurnedOff = false; -// How often we are redrawing screen -#define USER_LOOP_REFRESH_RATE_MS 5000 -void userLoop() { +void loop() override { // Check if we time interval for redrawing passes. if (millis() - lastUpdate_mod < USER_LOOP_REFRESH_RATE_MS) { @@ -205,3 +206,7 @@ void userLoop() { DEBUG_PRINTLN("Print estimated current"); } +}; // TTGO_T_DisplayMod + +static TTGO_T_DisplayMod usermod_ttgo_t_display; +REGISTER_USERMOD(usermod_ttgo_t_display); From c0dbe63e4b892448ba8b07a45e43dfd425f7b18e Mon Sep 17 00:00:00 2001 From: Will Miles Date: Thu, 14 Aug 2025 22:24:40 -0400 Subject: [PATCH 15/23] TTGO-T-Display: Allow full path for display_setup Check if the path is fully qualified before searching; this allows using files outside of the WLED folder. --- usermods/TTGO-T-Display/set_build_flags.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/usermods/TTGO-T-Display/set_build_flags.py b/usermods/TTGO-T-Display/set_build_flags.py index 18eb342ef2..c88bcbfaa9 100644 --- a/usermods/TTGO-T-Display/set_build_flags.py +++ b/usermods/TTGO-T-Display/set_build_flags.py @@ -25,11 +25,15 @@ def find_in_paths(filename, paths): return None lib_builders = env.GetLibBuilders() -search_paths = [global_env["PROJECT_SRC_DIR"], - global_env["PROJECT_INCLUDE_DIR"], - *[incdir for lb in lib_builders for incdir in lb.get_include_dirs()]] - -display_setup_path = find_in_paths(display_setup, search_paths) +if os.path.isfile(display_setup): + display_setup_path = display_setup +else: + search_paths = [global_env["PROJECT_SRC_DIR"], + global_env["PROJECT_INCLUDE_DIR"], + *[incdir for lb in lib_builders for incdir in lb.get_include_dirs()]] + + display_setup_path = find_in_paths(display_setup, search_paths) + if display_setup_path is None: print(f"Unable to find {display_setup} in any include path - {[str(path) for path in search_paths]}!") raise RuntimeError("Missing display setup; use 'User_Setups/something.h' for setups out of TFT_eSPI, or put your setup in wled00") From b084bb3cd7693612d8a7a351ee554c667e2a4dd5 Mon Sep 17 00:00:00 2001 From: Will Miles Date: Sun, 17 May 2026 23:03:31 -0400 Subject: [PATCH 16/23] usermods/TTGO-T-Display: Update lib info - Use latest TFT-ESPI - Fix include in script - Fix name --- usermods/TTGO-T-Display/library.json | 4 ++-- usermods/TTGO-T-Display/set_build_flags.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/usermods/TTGO-T-Display/library.json b/usermods/TTGO-T-Display/library.json index 812478fce9..fbd6a5afa1 100644 --- a/usermods/TTGO-T-Display/library.json +++ b/usermods/TTGO-T-Display/library.json @@ -1,7 +1,7 @@ { - "name:": "TTGO-T-Display", + "name": "TTGO-T-Display", "dependencies": { - "TFT_eSPI" : "https://github.com/Bodmer/TFT_eSPI.git#5793878" + "TFT_eSPI" : "https://github.com/Bodmer/TFT_eSPI.git#16e37595040eac69cd628e4bffb56fc30cad6299" }, "build": { "extraScript": "set_build_flags.py", diff --git a/usermods/TTGO-T-Display/set_build_flags.py b/usermods/TTGO-T-Display/set_build_flags.py index c88bcbfaa9..ccd1e02e6d 100644 --- a/usermods/TTGO-T-Display/set_build_flags.py +++ b/usermods/TTGO-T-Display/set_build_flags.py @@ -40,7 +40,7 @@ def find_in_paths(filename, paths): def add_display_setup(tgt_env): tgt_env.Append(CPPDEFINES=[("USER_SETUP_LOADED", "1")]) - tgt_env.Append(CCFLAGS=f"-include {display_setup_path}") + tgt_env.Append(CCFLAGS=[f"-include{display_setup_path}"]) # Add it for this library and the TFT_eSPI dependency add_display_setup(env) From f2cff53832a0dd738a489bf713723e5d711ce9c1 Mon Sep 17 00:00:00 2001 From: Will Miles Date: Sun, 17 May 2026 23:03:49 -0400 Subject: [PATCH 17/23] usermod/TTGO-T-Display: Register pins --- usermods/TTGO-T-Display/usermod.cpp | 38 +++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/usermods/TTGO-T-Display/usermod.cpp b/usermods/TTGO-T-Display/usermod.cpp index 524767823e..feb6892449 100644 --- a/usermods/TTGO-T-Display/usermod.cpp +++ b/usermods/TTGO-T-Display/usermod.cpp @@ -47,6 +47,7 @@ class TTGO_T_DisplayMod : public Usermod { // Member variables + bool enabled = true; TFT_eSPI tft; // Invoke custom library // needRedraw marks if redraw is required to prevent often redrawing. bool needRedraw = true; @@ -70,7 +71,39 @@ TTGO_T_DisplayMod() : Usermod() //gets called once at boot. Do all initialization that doesn't depend on network here void setup() override { - DEBUG_PRINTLN("Start"); + // Reserve pins + if ( +#ifdef TFT_MISO + !PinManager::allocatePin(TFT_MISO, true, (PinOwner) getId()) || +#endif + +#ifdef TFT_MOSI + !PinManager::allocatePin(TFT_MOSI, true, (PinOwner) getId()) || +#endif +#ifdef TFT_SCLK + !PinManager::allocatePin(TFT_SCLK, true, (PinOwner) getId()) || +#endif +#ifdef TFT_CS + !PinManager::allocatePin(TFT_CS, true, (PinOwner) getId()) || +#endif +#ifdef TFT_DC + !PinManager::allocatePin(TFT_DC, true, (PinOwner) getId()) || +#endif +#ifdef TFT_RST + !PinManager::allocatePin(TFT_RST, true, (PinOwner) getId()) || +#endif +#ifdef TFT_BL + !PinManager::allocatePin(TFT_BL, true, (PinOwner) getId()) || +#endif +#ifdef TOUCH_CS + !PinManager::allocatePin(TOUCH_CS, true, (PinOwner) getId()) || +#endif + 0) { + DEBUG_PRINTLN("TFT: Failed to allocate pins!"); + enabled = false; + return; + } + tft.init(); tft.setRotation(3); //Rotation here is set up for the text to be readable with the port on the left. Use 1 to flip. tft.fillScreen(TFT_BLACK); @@ -82,7 +115,7 @@ void setup() override { tft.setTextSize(3); //tft.setTextSize(1); tft.print("Loading..."); - DEBUG_PRINTLN("Loading..."); + DEBUG_PRINTLN("TFT Loading..."); if (TFT_BL > 0) { // TFT_BL has been set in the TFT_eSPI library in the User Setup file TTGO_T_Display.h pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode @@ -94,6 +127,7 @@ void setup() override { void loop() override { + if (!enabled) return; // Check if we time interval for redrawing passes. if (millis() - lastUpdate_mod < USER_LOOP_REFRESH_RATE_MS) { From a0a472d639336af9172f2d98eb5121daa807d621 Mon Sep 17 00:00:00 2001 From: Will Miles Date: Wed, 27 May 2026 16:03:51 +0000 Subject: [PATCH 18/23] TTGO-T-Display: Add info and disable feature --- usermods/TTGO-T-Display/usermod.cpp | 52 ++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/usermods/TTGO-T-Display/usermod.cpp b/usermods/TTGO-T-Display/usermod.cpp index feb6892449..a61b3b8fb2 100644 --- a/usermods/TTGO-T-Display/usermod.cpp +++ b/usermods/TTGO-T-Display/usermod.cpp @@ -64,6 +64,10 @@ class TTGO_T_DisplayMod : public Usermod { long lastRedraw = 0; bool displayTurnedOff = false; + // string that are used multiple time (this will save some flash memory) + static const char _name[]; + static const char _enabled[]; + public: TTGO_T_DisplayMod() : Usermod() @@ -127,7 +131,13 @@ void setup() override { void loop() override { - if (!enabled) return; + if (!enabled) { + if (!displayTurnedOff) { + digitalWrite(TFT_BL, !TFT_BACKLIGHT_ON); // Turn backlight off. + displayTurnedOff = true; + } + return; + } // Check if we time interval for redrawing passes. if (millis() - lastUpdate_mod < USER_LOOP_REFRESH_RATE_MS) { @@ -240,7 +250,47 @@ void loop() override { DEBUG_PRINTLN("Print estimated current"); } + + +/* + * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. + * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. + * Below it is shown how this could be used for e.g. a light sensor + */ +void addToJsonInfo(JsonObject& root) { + JsonObject user = root["u"]; + if (user.isNull()) user = root.createNestedObject("u"); + + auto state = user.createNestedArray(FPSTR(_name)); + if (enabled) { + state.add(displayTurnedOff ? "Off" : "On"); + } else { + state.add("Disabled"); + } +} + + +void addToConfig(JsonObject &root) override { + JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname + top[FPSTR(_enabled)] = enabled; +} + +bool readFromConfig(JsonObject& root) override +{ + // default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor + // setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed) + JsonObject top = root[FPSTR(_name)]; + bool configComplete = !top.isNull(); + + configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled); + return configComplete; +} + }; // TTGO_T_DisplayMod +// add more strings here to reduce flash memory usage +const char TTGO_T_DisplayMod::_name[] PROGMEM = "TFT Display"; +const char TTGO_T_DisplayMod::_enabled[] PROGMEM = "enabled"; + static TTGO_T_DisplayMod usermod_ttgo_t_display; REGISTER_USERMOD(usermod_ttgo_t_display); From 64252ffbb40d65850199aa678860422cd09fc7a9 Mon Sep 17 00:00:00 2001 From: Will Miles Date: Wed, 1 Jul 2026 20:36:11 +0000 Subject: [PATCH 19/23] usermod/TTGO-T-Display: Fix pin mapping Ensure pins are registered together to avoid partial success. --- usermods/TTGO-T-Display/usermod.cpp | 93 +++++++++++++++-------------- 1 file changed, 48 insertions(+), 45 deletions(-) diff --git a/usermods/TTGO-T-Display/usermod.cpp b/usermods/TTGO-T-Display/usermod.cpp index a61b3b8fb2..25072a82d7 100644 --- a/usermods/TTGO-T-Display/usermod.cpp +++ b/usermods/TTGO-T-Display/usermod.cpp @@ -23,24 +23,6 @@ #include "WiFi.h" #include -// #ifndef TFT_DISPOFF -// #define TFT_DISPOFF 0x28 -// #endif - -// #ifndef TFT_SLPIN -// #define TFT_SLPIN 0x10 -// #endif - -// #define TFT_MOSI 19 -// #define TFT_SCLK 18 -// #define TFT_CS 5 -// #define TFT_DC 16 -// #define TFT_RST 23 - -//#define TFT_BL 4 // Display backlight control pin -//#define ADC_EN 14 // Used for enabling battery voltage measurements - not used in this program -//#define WLED_WATCHDOG_TIMEOUT 3 - // How often we are redrawing screen #define USER_LOOP_REFRESH_RATE_MS 5000 @@ -48,6 +30,7 @@ class TTGO_T_DisplayMod : public Usermod { // Member variables bool enabled = true; + bool ready = false; TFT_eSPI tft; // Invoke custom library // needRedraw marks if redraw is required to prevent often redrawing. bool needRedraw = true; @@ -62,7 +45,9 @@ class TTGO_T_DisplayMod : public Usermod { long lastUpdate_mod = 0; long lastRedraw = 0; +#ifdef TFT_BL bool displayTurnedOff = false; +#endif // string that are used multiple time (this will save some flash memory) static const char _name[]; @@ -75,36 +60,39 @@ TTGO_T_DisplayMod() : Usermod() //gets called once at boot. Do all initialization that doesn't depend on network here void setup() override { - // Reserve pins - if ( + if (!ready) { + // Reserve pins. allocateMultiplePins() validates every pin before allocating + // any of them, so a failure here leaves no pins allocated (no manual rollback needed). + // Pins the active TFT_eSPI User_Setup doesn't define (e.g. parallel-bus displays, + // or lines hardwired to GND/3V3) are simply left out of the array below. + const int8_t pins[] = { #ifdef TFT_MISO - !PinManager::allocatePin(TFT_MISO, true, (PinOwner) getId()) || + TFT_MISO, #endif - #ifdef TFT_MOSI - !PinManager::allocatePin(TFT_MOSI, true, (PinOwner) getId()) || + TFT_MOSI, #endif #ifdef TFT_SCLK - !PinManager::allocatePin(TFT_SCLK, true, (PinOwner) getId()) || + TFT_SCLK, #endif #ifdef TFT_CS - !PinManager::allocatePin(TFT_CS, true, (PinOwner) getId()) || + TFT_CS, #endif #ifdef TFT_DC - !PinManager::allocatePin(TFT_DC, true, (PinOwner) getId()) || + TFT_DC, #endif #ifdef TFT_RST - !PinManager::allocatePin(TFT_RST, true, (PinOwner) getId()) || + TFT_RST, #endif #ifdef TFT_BL - !PinManager::allocatePin(TFT_BL, true, (PinOwner) getId()) || -#endif + TFT_BL, +#endif #ifdef TOUCH_CS - !PinManager::allocatePin(TOUCH_CS, true, (PinOwner) getId()) || + TOUCH_CS, #endif - 0) { + }; + if (!PinManager::allocateMultiplePins(pins, sizeof(pins)/sizeof(pins[0]), (PinOwner) getId(), true)) { DEBUG_PRINTLN("TFT: Failed to allocate pins!"); - enabled = false; return; } @@ -121,21 +109,29 @@ void setup() override { tft.print("Loading..."); DEBUG_PRINTLN("TFT Loading..."); +#ifdef TFT_BL if (TFT_BL > 0) { // TFT_BL has been set in the TFT_eSPI library in the User Setup file TTGO_T_Display.h pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); // Turn backlight on. } +#endif // tft.setRotation(3); + ready = true; + } } void loop() override { + if (!ready) return; + if (!enabled) { +#ifdef TFT_BL if (!displayTurnedOff) { digitalWrite(TFT_BL, !TFT_BACKLIGHT_ON); // Turn backlight off. displayTurnedOff = true; } +#endif return; } @@ -145,12 +141,6 @@ void loop() override { } lastUpdate_mod = millis(); - // Turn off display after 5 minutes with no change. - if(!displayTurnedOff && millis() - lastRedraw > 5*60*1000) { - digitalWrite(TFT_BL, !TFT_BACKLIGHT_ON); // Turn backlight off. - displayTurnedOff = true; - } - // Check if values which are shown on display changed from the last time. if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) { needRedraw = true; @@ -165,23 +155,28 @@ void loop() override { } if (!needRedraw) { + // Turn off display after 5 minutes with no change. +#ifdef TFT_BL + if(!displayTurnedOff && millis() - lastRedraw > 5*60*1000) { + digitalWrite(TFT_BL, !TFT_BACKLIGHT_ON); // Turn backlight off. + displayTurnedOff = true; + } +#endif return; } needRedraw = false; +#ifdef TFT_BL if (displayTurnedOff) { digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); // Turn backlight on. displayTurnedOff = false; } +#endif lastRedraw = millis(); // Update last known values. - #if defined(ESP8266) - knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID(); - #else - knownSsid = WiFi.SSID(); - #endif + knownSsid = apActive ? apSSID : WiFi.SSID(); knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP(); knownBrightness = bri; knownMode = strip.getMainSegment().mode; @@ -262,10 +257,18 @@ void addToJsonInfo(JsonObject& root) { if (user.isNull()) user = root.createNestedObject("u"); auto state = user.createNestedArray(FPSTR(_name)); + if (!ready) { + state.add(PSTR("Pin allocation failure")); + return; + } if (enabled) { - state.add(displayTurnedOff ? "Off" : "On"); +#ifdef TFT_BL + state.add(displayTurnedOff ? PSTR("Off") : PSTR("On")); +#else + state.add(PSTR("On")); +#endif } else { - state.add("Disabled"); + state.add(PSTR("Disabled")); } } From a9f5d25fef10409ddbdd1c6ee4c63d705a586c50 Mon Sep 17 00:00:00 2001 From: Will Miles Date: Thu, 2 Jul 2026 02:29:09 +0000 Subject: [PATCH 20/23] usermod/TTGO-T-Display: Backlight handling fixes --- usermods/TTGO-T-Display/usermod.cpp | 38 +++++++++++++++-------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/usermods/TTGO-T-Display/usermod.cpp b/usermods/TTGO-T-Display/usermod.cpp index 25072a82d7..8ed3ad661c 100644 --- a/usermods/TTGO-T-Display/usermod.cpp +++ b/usermods/TTGO-T-Display/usermod.cpp @@ -23,6 +23,8 @@ #include "WiFi.h" #include +#define DISPLAY_HAS_BACKLIGHT defined(TFT_BL) && defined(TFT_BACKLIGHT_ON) && (TFT_BL >= 0) + // How often we are redrawing screen #define USER_LOOP_REFRESH_RATE_MS 5000 @@ -45,7 +47,7 @@ class TTGO_T_DisplayMod : public Usermod { long lastUpdate_mod = 0; long lastRedraw = 0; -#ifdef TFT_BL +#if DISPLAY_HAS_BACKLIGHT bool displayTurnedOff = false; #endif @@ -65,33 +67,33 @@ void setup() override { // any of them, so a failure here leaves no pins allocated (no manual rollback needed). // Pins the active TFT_eSPI User_Setup doesn't define (e.g. parallel-bus displays, // or lines hardwired to GND/3V3) are simply left out of the array below. - const int8_t pins[] = { + const static PinManagerPinType pins[] PROGMEM = { #ifdef TFT_MISO - TFT_MISO, + { TFT_MISO, INPUT }, #endif #ifdef TFT_MOSI - TFT_MOSI, + { TFT_MOSI, OUTPUT }, #endif #ifdef TFT_SCLK - TFT_SCLK, + { TFT_SCLK, OUTPUT }, #endif #ifdef TFT_CS - TFT_CS, + { TFT_CS, OUTPUT }, #endif #ifdef TFT_DC - TFT_DC, + { TFT_DC, OUTPUT }, #endif #ifdef TFT_RST - TFT_RST, + { TFT_RST, OUTPUT }, #endif #ifdef TFT_BL - TFT_BL, + { TFT_BL, OUTPUT }, #endif #ifdef TOUCH_CS - TOUCH_CS, + { TOUCH_CS, OUTPUT }, #endif }; - if (!PinManager::allocateMultiplePins(pins, sizeof(pins)/sizeof(pins[0]), (PinOwner) getId(), true)) { + if (!PinManager::allocateMultiplePins(pins, sizeof(pins)/sizeof(pins[0]), (PinOwner) getId())) { DEBUG_PRINTLN("TFT: Failed to allocate pins!"); return; } @@ -109,8 +111,8 @@ void setup() override { tft.print("Loading..."); DEBUG_PRINTLN("TFT Loading..."); -#ifdef TFT_BL - if (TFT_BL > 0) { // TFT_BL has been set in the TFT_eSPI library in the User Setup file TTGO_T_Display.h +#if DISPLAY_HAS_BACKLIGHT + if (TFT_BL >= 0) { // TFT_BL has been set in the TFT_eSPI library in the User Setup file TTGO_T_Display.h pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); // Turn backlight on. } @@ -126,7 +128,7 @@ void loop() override { if (!ready) return; if (!enabled) { -#ifdef TFT_BL +#if DISPLAY_HAS_BACKLIGHT if (!displayTurnedOff) { digitalWrite(TFT_BL, !TFT_BACKLIGHT_ON); // Turn backlight off. displayTurnedOff = true; @@ -156,7 +158,7 @@ void loop() override { if (!needRedraw) { // Turn off display after 5 minutes with no change. -#ifdef TFT_BL +#if DISPLAY_HAS_BACKLIGHT if(!displayTurnedOff && millis() - lastRedraw > 5*60*1000) { digitalWrite(TFT_BL, !TFT_BACKLIGHT_ON); // Turn backlight off. displayTurnedOff = true; @@ -166,7 +168,7 @@ void loop() override { } needRedraw = false; -#ifdef TFT_BL +#if DISPLAY_HAS_BACKLIGHT if (displayTurnedOff) { digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); // Turn backlight on. @@ -252,7 +254,7 @@ void loop() override { * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. * Below it is shown how this could be used for e.g. a light sensor */ -void addToJsonInfo(JsonObject& root) { +void addToJsonInfo(JsonObject& root) override { JsonObject user = root["u"]; if (user.isNull()) user = root.createNestedObject("u"); @@ -262,7 +264,7 @@ void addToJsonInfo(JsonObject& root) { return; } if (enabled) { -#ifdef TFT_BL +#if DISPLAY_HAS_BACKLIGHT state.add(displayTurnedOff ? PSTR("Off") : PSTR("On")); #else state.add(PSTR("On")); From 68808f47eda216cb3efb3038d710c6c57a872ce2 Mon Sep 17 00:00:00 2001 From: Will Miles Date: Sun, 21 Jun 2026 03:42:00 +0000 Subject: [PATCH 21/23] Add TFT_eSPI_compat Add a shim layer for TFT_espi that works around the missing SPIFFS.h dependency. Integrate some logic for accepting display definitions via a PlatformIO custom property as an alternative to a dozen command line switches; this improves cache viability, too. --- lib/TFT_eSPI_compat/README.md | 89 ++++++++++++++ lib/TFT_eSPI_compat/configure.py | 136 +++++++++++++++++++++ lib/TFT_eSPI_compat/library.json | 11 ++ lib/TFT_eSPI_compat/shims/SPIFFS.h | 18 +++ lib/TFT_eSPI_compat/shims/User_Setup.h | 12 ++ usermods/TTGO-T-Display/library.json | 2 +- usermods/TTGO-T-Display/set_build_flags.py | 49 +------- 7 files changed, 270 insertions(+), 47 deletions(-) create mode 100644 lib/TFT_eSPI_compat/README.md create mode 100644 lib/TFT_eSPI_compat/configure.py create mode 100644 lib/TFT_eSPI_compat/library.json create mode 100644 lib/TFT_eSPI_compat/shims/SPIFFS.h create mode 100644 lib/TFT_eSPI_compat/shims/User_Setup.h diff --git a/lib/TFT_eSPI_compat/README.md b/lib/TFT_eSPI_compat/README.md new file mode 100644 index 0000000000..6640ffa660 --- /dev/null +++ b/lib/TFT_eSPI_compat/README.md @@ -0,0 +1,89 @@ +# TFT_eSPI_compat + +A compatibility wrapper for the [TFT_eSPI](https://github.com/Bodmer/TFT_eSPI) library in WLED usermods. Handles two things that every TFT_eSPI usermod would otherwise need to sort out independently: + +1. **SPIFFS stub** — TFT_eSPI pulls in `SPIFFS.h` when `SMOOTH_FONT` is enabled, but SPIFFS is not available on modern ESP32 Arduino platforms. A minimal stub header is injected automatically, but placed last on the include search path. This ensures that, if present, the real header is found first and used instead. + +2. **Display setup** — TFT_eSPI requires a *User Setup* header to be processed before any of its headers. This wrapper generates a `tft_setup.h` at build time (using TFT_eSPI's own `__has_include` hook) and injects it into the compiler's search path, so usermods don't need their own build scripts for this. + +--- + +## Using TFT_eSPI_compat in a usermod + +Depend on `TFT_eSPI_compat` instead of `TFT_eSPI` directly. In your usermod's `library.json`: + +```json +{ + "name": "my_display_usermod", + "build": { "libArchive": false }, + "dependencies": { + "TFT_eSPI_compat": "*" + } +} +``` + +TFT_eSPI itself is pulled in transitively. In your source, include it normally: + +```cpp +#include +``` + +--- + +## Configuring the display setup + +TFT_eSPI needs to know which display driver and pin mapping to use. Set `custom_display_setup` in your environment's `platformio_override.ini`: + +```ini +custom_display_setup = User_Setups/Setup23_ST7789.h +``` + +The value can be: +- A path relative to any include directory (e.g. a filename inside TFT_eSPI's `User_Setups/` folder), or +- An absolute path to a custom setup header anywhere on disk. + +If `custom_display_setup` is not set, no display setup is applied — your usermod must either set a default (see below) or configure the display another way (e.g. via global `build_flags`). + +--- + +## Providing a default setup (for usermod authors) + +If your usermod targets a specific display, you can declare a preferred default that users can still override via `custom_display_setup`. Add a short `extraScript` to your `library.json`: + +```json +"build": { + "extraScript": "set_build_flags.py", + "libArchive": false +} +``` + +```python +# set_build_flags.py +Import("env") + +global_env = DefaultEnvironment() +global_env.SetDefault(TFT_DISPLAY_SETUP_DEFAULT="User_Setups/Setup25_TTGO_T_Display.h") + +# Add any other usermod-specific build flags here (pin defaults, etc.) +``` + +`SetDefault` only takes effect when neither `custom_display_setup` nor `TFT_DISPLAY_SETUP_DEFAULT` has already been set, so it never overrides a user's explicit configuration. + +`TFT_DISPLAY_SETUP_DEFAULT` is read at build time (when `tft_setup.h` is generated), after all library extraScripts have run. + +--- + +## How it works + +`configure.py` appends two directories to the compiler search path for all libraries (including TFT_eSPI) — appended rather than prepended, so a real `SPIFFS.h` found anywhere else on the path always takes priority over the stub: + +- `shims/` — contains `SPIFFS.h` (stub) and `User_Setup.h` (dependency-tracking shim) +- `$BUILD_DIR/TFT_eSPI_compat/` — contains the generated `tft_setup.h` + +TFT_eSPI.h checks `__has_include()` at compile time; because `tft_setup.h` is built by a SCons `Command` node before any TFT_eSPI source is compiled, it is always present. `User_Setup.h` carries a plain `#include ` that gives SCons's header scanner the dependency edge it needs to guarantee ordering. + +--- + +## TFT_eSPI version + +The pinned TFT_eSPI version is declared in `library.json`. All usermods depending on `TFT_eSPI_compat` share this version; update it in one place when needed. diff --git a/lib/TFT_eSPI_compat/configure.py b/lib/TFT_eSPI_compat/configure.py new file mode 100644 index 0000000000..4c02720fbe --- /dev/null +++ b/lib/TFT_eSPI_compat/configure.py @@ -0,0 +1,136 @@ +Import("env") +import json +import os +from os.path import realpath + +import SCons.Errors +from SCons.Script import Action + +global_env = DefaultEnvironment() + +shims_dir = realpath("shims") +# BUILD_DIR is itself defined as "$PROJECT_BUILD_DIR/$PIOENV" (see PlatformIO's +# builder/main.py) — env["BUILD_DIR"] returns that raw, unexpanded template +# string, not a real path. Without subst(), os.path.join() below would create +# a literal "./shims/$PROJECT_BUILD_DIR/$PIOENV/TFT_eSPI_compat" directory. +build_shims_dir = os.path.join(global_env.subst("$BUILD_DIR"), "TFT_eSPI_compat") + +# Create the generated-file directory now so get_include_dirs() reports it +# and so SCons's FindFile can locate Command nodes inside it. +os.makedirs(build_shims_dir, exist_ok=True) + +# Inject shims/ and build_shims_dir into DefaultEnvironment(), so they'll be +# visible anywhere in the build. They're appended, not prepended, so any +# real SPIFFS.h or tft_setup.h will take precedence over these. +global_env.AppendUnique(CPPPATH=[shims_dir, build_shims_dir]) + +# Also add to our own lib env so load_usermods.py propagates them to usermods +# via cached_add_includes → dep.get_include_dirs(). +env.AppendUnique(CPPPATH=[shims_dir, build_shims_dir]) + +# --------------------------------------------------------------------------- +# Generate tft_setup.h — proper SCons target +# --------------------------------------------------------------------------- +# TFT_eSPI.h checks __has_include(); providing the file lets us +# inject the display config via TFT_eSPI's own hook without patching it. +# +# The file is generated from the resolved display-setup configuration so +# SCons knows to rebuild it (and therefore recompile TFT_eSPI) exactly when +# that configuration changes — not on every build. +# +# SCons's CScanner does not parse __has_include(), so shims/User_Setup.h +# carries a plain #include that gives the scanner a normal +# include directive to follow, ensuring the generated file is built before +# any TFT_eSPI source is compiled. + +_lib_env = env # capture before the function definition shadows the name + +# Enumerating every file that could influence custom_display_setup / +# TFT_DISPLAY_SETUP_DEFAULT is unbounded, so we depend on the *resolved +# value* instead, via a real intermediate file (SCons Value() nodes don't +# track post-construction writes for signature purposes, so they can't be +# used as a build-time-computed dependency). Resolution needs +# GetLibBuilders(), which isn't populated until build time, so it happens +# in a build action rather than here at parse time. AlwaysBuild reruns the +# resolution every build, but we only rewrite the file when the resolved +# value actually changes. +def _resolve_display_setup(target, source, env): # argument names are required by SCons + genv = DefaultEnvironment() + all_lbs = _lib_env.GetLibBuilders() + + display_setup = ( + genv.GetProjectOption("custom_display_setup", "") + or genv.get("TFT_DISPLAY_SETUP_DEFAULT", "") + ) + + setup_path = None + if display_setup: + if os.path.isfile(display_setup): + setup_path = display_setup + else: + search_paths = [ + genv["PROJECT_SRC_DIR"], + genv["PROJECT_INCLUDE_DIR"], + *[d for lb in all_lbs for d in lb.get_include_dirs()], + ] + setup_path = next( + ( + os.path.join(str(p), display_setup) + for p in search_paths + if os.path.isfile(os.path.join(str(p), display_setup)) + ), + None, + ) + + target_path = str(target[0]) + new_contents = json.dumps([display_setup, setup_path]) + if os.path.isfile(target_path): + with open(target_path) as f: + if f.read() == new_contents: + return + with open(target_path, "w") as f: + f.write(new_contents) + + +_resolved_display_setup = global_env.Command( + os.path.join(build_shims_dir, "display_setup.resolved.json"), + [], + Action(_resolve_display_setup, cmdstr=None), +) +global_env.AlwaysBuild(_resolved_display_setup) +# NoCache: this target has no real sources, so its CacheDir signature never +# changes — without this, CacheDir would keep serving the first build's +# file instead of re-running _resolve_display_setup. +global_env.NoCache(_resolved_display_setup) +# Precious: SCons deletes a target before rebuilding it by default, which +# would defeat the write-if-changed check above (it needs to read the +# previous build's file to compare against). +global_env.Precious(_resolved_display_setup) + + +def _generate_tft_setup(target, source, env): # argument names are required by SCons + with open(str(source[0])) as f: + display_setup, setup_path = json.load(f) + + lines = ["// Generated by TFT_eSPI_compat — do not edit\n", "#pragma once\n"] + + if display_setup: + if setup_path: + escaped = setup_path.replace("\\", "\\\\") + lines.append(f'#include "{escaped}"\n') + else: + raise SCons.Errors.StopError( + f"TFT_eSPI_compat: display setup '{display_setup}' not found " + "in any include path" + ) + + os.makedirs(os.path.dirname(str(target[0])), exist_ok=True) + with open(str(target[0]), "w") as f: + f.writelines(lines) + + +global_env.Command( + os.path.join(build_shims_dir, "tft_setup.h"), + _resolved_display_setup, + Action(_generate_tft_setup, cmdstr="Generating tft_setup.h"), +) diff --git a/lib/TFT_eSPI_compat/library.json b/lib/TFT_eSPI_compat/library.json new file mode 100644 index 0000000000..c096b2edfb --- /dev/null +++ b/lib/TFT_eSPI_compat/library.json @@ -0,0 +1,11 @@ +{ + "name": "TFT_eSPI_compat", + "build": { + "extraScript": "configure.py", + "libArchive": false + }, + "platforms": ["espressif32"], + "dependencies": { + "TFT_eSPI": "https://github.com/Bodmer/TFT_eSPI.git#16e37595040eac69cd628e4bffb56fc30cad6299" + } +} diff --git a/lib/TFT_eSPI_compat/shims/SPIFFS.h b/lib/TFT_eSPI_compat/shims/SPIFFS.h new file mode 100644 index 0000000000..5787479a71 --- /dev/null +++ b/lib/TFT_eSPI_compat/shims/SPIFFS.h @@ -0,0 +1,18 @@ +#pragma once +// Stub: TFT_eSPI includes SPIFFS.h when SMOOTH_FONT is defined, but the +// usermods that use it don't load fonts from a filesystem. This shim +// satisfies the include and the `fs::FS &fontFS = SPIFFS` declaration in +// Smooth_font.h without pulling in the SPIFFS Arduino library. +// SPIFFS-backed font loading will silently do nothing at runtime. +// +// shims/ is appended (not prepended) to CPPPATH in configure.py, so this is +// only reached by the compiler's #include search when no real SPIFFS.h +// exists earlier on the include path. +#include + +class SPIFFSStub : public fs::FS { +public: + SPIFFSStub() : fs::FS(nullptr) {} +}; + +inline SPIFFSStub SPIFFS; diff --git a/lib/TFT_eSPI_compat/shims/User_Setup.h b/lib/TFT_eSPI_compat/shims/User_Setup.h new file mode 100644 index 0000000000..a1696d3ff5 --- /dev/null +++ b/lib/TFT_eSPI_compat/shims/User_Setup.h @@ -0,0 +1,12 @@ +#pragma once +// TFT_eSPI_compat: dependency shim. +// +// At compile time this file is NOT used — TFT_eSPI.h's __has_include() +// fires first, so USER_SETUP_LOADED is defined before User_Setup_Select.h ever +// reaches the fallback #include . +// +// The only purpose of this file is to give SCons's C/C++ header scanner a plain +// #include directive that it can follow to discover tft_setup.h as a build +// dependency, ensuring the Command that generates tft_setup.h runs before any +// TFT_eSPI source file is compiled. +#include diff --git a/usermods/TTGO-T-Display/library.json b/usermods/TTGO-T-Display/library.json index fbd6a5afa1..4eb025968e 100644 --- a/usermods/TTGO-T-Display/library.json +++ b/usermods/TTGO-T-Display/library.json @@ -1,7 +1,7 @@ { "name": "TTGO-T-Display", "dependencies": { - "TFT_eSPI" : "https://github.com/Bodmer/TFT_eSPI.git#16e37595040eac69cd628e4bffb56fc30cad6299" + "TFT_eSPI_compat": "*" }, "build": { "extraScript": "set_build_flags.py", diff --git a/usermods/TTGO-T-Display/set_build_flags.py b/usermods/TTGO-T-Display/set_build_flags.py index ccd1e02e6d..f5d202ce30 100644 --- a/usermods/TTGO-T-Display/set_build_flags.py +++ b/usermods/TTGO-T-Display/set_build_flags.py @@ -1,50 +1,7 @@ -# Select target board -# Ref: https://github.com/Bodmer/TFT_eSPI/wiki/Installing-on-PlatformIO#4-configure-library -# These flags are passed to this library and the TFT_eSPI library only - Import("env") -import os - -# Add pin definitions to whole project environment if they haven't otherwise been specified -# WM: This is a feature of the original usermod; I'm not sure if it's globally applicable.. global_env = DefaultEnvironment() -global_defines = global_env.get("CPPDEFINES", []) -if "BTNPIN" not in [v[0] for v in global_defines if isinstance(v, tuple)]: - global_env.Append(CPPDEFINES=[("BTNPIN", "35")]) - -# Select display setup -display_setup = global_env.GetProjectOption("custom_display_setup","User_Setups/Setup25_TTGO_T_Display.h") - -# Resolve this file's path -def find_in_paths(filename, paths): - for path in paths: - fullpath = os.path.join(str(path), filename) - if os.path.isfile(fullpath): - return fullpath - return None - -lib_builders = env.GetLibBuilders() -if os.path.isfile(display_setup): - display_setup_path = display_setup -else: - search_paths = [global_env["PROJECT_SRC_DIR"], - global_env["PROJECT_INCLUDE_DIR"], - *[incdir for lb in lib_builders for incdir in lb.get_include_dirs()]] - - display_setup_path = find_in_paths(display_setup, search_paths) - -if display_setup_path is None: - print(f"Unable to find {display_setup} in any include path - {[str(path) for path in search_paths]}!") - raise RuntimeError("Missing display setup; use 'User_Setups/something.h' for setups out of TFT_eSPI, or put your setup in wled00") - -def add_display_setup(tgt_env): - tgt_env.Append(CPPDEFINES=[("USER_SETUP_LOADED", "1")]) - tgt_env.Append(CCFLAGS=[f"-include{display_setup_path}"]) -# Add it for this library and the TFT_eSPI dependency -add_display_setup(env) -for lb in lib_builders: - if lb.name == "TFT_eSPI": - add_display_setup(lb.env) - break +# Provide TTGO's preferred display setup as the default for TFT_eSPI_compat. +# Users can override this by setting custom_display_setup in platformio_override.ini. +global_env.SetDefault(TFT_DISPLAY_SETUP_DEFAULT="User_Setups/Setup25_TTGO_T_Display.h") From 454a40949e794966dfb5b827e390a7fba2a21fb4 Mon Sep 17 00:00:00 2001 From: Will Miles Date: Wed, 1 Jul 2026 20:23:55 +0000 Subject: [PATCH 22/23] usermod/TTGO-T-Display: Fix up readme --- usermods/TTGO-T-Display/README.md | 111 ++++++++++++++---- .../TTGO-T-Display/platformio_override.ini | 28 +---- 2 files changed, 91 insertions(+), 48 deletions(-) diff --git a/usermods/TTGO-T-Display/README.md b/usermods/TTGO-T-Display/README.md index 78eddc8c5b..3f4d2a6494 100644 --- a/usermods/TTGO-T-Display/README.md +++ b/usermods/TTGO-T-Display/README.md @@ -1,53 +1,114 @@ # TTGO T-Display ESP32 with 240x135 TFT via SPI with TFT_eSPI -This usermod enables use of the TTGO 240x135 T-Display ESP32 module -for controlling WLED and showing the following information: + +This usermod is a TFT display driver for WLED, using the TFT_eSPI library, originally designed for the TTGO 240x135 T-Display ESP32 module. It can drive any TFT display with sufficient resolution that is supported by TFT_eSPI. The supplied `platformio_override.ini` provides a ready-made configuration for the TTGO T-Display board specifically — see [Using with other boards](#using-with-other-boards) if you have different hardware. + +When running, the display shows: + * Current SSID -* IP address, if obtained - * If connected to a network, current brightness percentage is shown - * In AP mode, AP, IP and password are shown +* IP address + * In station mode: IP address and current brightness percentage + * In AP mode: AP SSID, IP address, and password * Current effect * Current palette -* Estimated current in mA (NOTE: for this to be a reasonable value, the correct LED type must be specified in the LED Prefs section) +* Estimated current in mA (requires correct LED type in LED preferences) + +The display turns off automatically after 5 minutes with no state change, and wakes on the next detected change. The usermod can also be fully disabled via the WLED config UI, which turns off the backlight and skips all display work. Display on/off status is shown in the WLED Info panel under **TFT Display**. -Button pin is mapped to the onboard button adjacent to the reset button of the TTGO T-Display board. +When using the T-Display environment, button pin 35 is mapped to the onboard button adjacent to the reset button (right side of the USB-C connector when oriented downward — see hardware photo). The button can be configured via the Macros section of the WLED web UI, e.g. to cycle presets. I have designed a 3D printed case around this board and an ["ElectroCookie"](https://amzn.to/2WCNeeA) project board, a [level shifter](https://amzn.to/3hbKu18), a [buck regulator](https://amzn.to/3mLMy0W), and a DC [power jack](https://amzn.to/3phj9NZ). I use 12V WS2815 LED strips for my projects, and power them with 12V power supplies. The regulator supplies 5V for the ESP module and the level shifter. If there is any interest in this case which elevates the board and display on custom extended standoffs to place the screen at the top of the enclosure (with accessible buttons), let me know, and I will post the STL files. It is a bit tricky to get the height correct, so I also designed a one-time use 3D printed solder fixture to set the board in the right location and at the correct height for the housing. (It is one-time use because it has to be cut off after soldering to be able to remove it). I didn't think the effort to make it in multiple pieces was worthwhile. Based on a rework of the ssd1306_i2c_oled_u8g2 usermod from the WLED repo. -In December 2024, I made some changes to fix compilation errors, and modified the platformio_override.ini file to include ALL changes needed (no more need to edit the origiginal "stock" platformio.ini file). I am also forcing an older version of the TFT_eSPI library. - ## Hardware + ![Hardware](assets/ttgo_hardware1.png) -![Hardware](assets/ttgo-tdisplay-enclosure1a.png) -![Hardware](assets/ttgo-tdisplay-enclosure2a.png) -![Hardware](assets/ttgo-tdisplay-enclosure3a.png) -![Hardware](assets/ttgo-tdisplay-enclosure3a.png) +![Enclosure](assets/ttgo-tdisplay-enclosure1a.png) +![Enclosure](assets/ttgo-tdisplay-enclosure2a.png) +![Enclosure](assets/ttgo-tdisplay-enclosure3a.png) +![Enclosure](assets/ttgo-tdisplay-enclosure4a.png) -## Github reference for TTGO-Tdisplay +## Github reference for TTGO-T-Display * [TTGO T-Display](https://github.com/Xinyuan-LilyGO/TTGO-T-Display) ## Requirements + Functionality checked with: -* TTGO T-Display + +* TTGO T-Display (ESP32, 240×135 ST7789 display) * PlatformIO -* Group of 4 individual Neopixels from Adafruit and several full strings of 12v WS2815 LEDs. +* A group of 4 individual Neopixels from Adafruit and several full strings of 12V WS2815 LEDs. * The hardware design shown above should be limited to shorter strings. For larger strings, I use a different setup with a dedicated 12v power supply and power them directly from said supply (in addition to dropping the 12v to 5v with a buck regulator for the ESP module and level shifter). -## Setup Needed: -* As with all usermods, copy the usermod.cpp file from the TTGO-T-Display usermod folder to the wled00 folder (replacing the default usermod.cpp file). +## Setup (TTGO T-Display board) -## Platformio Requirements +The following steps are specific to the TTGO T-Display board. For other hardware, see [Using with other boards](#using-with-other-boards). -### Platformio_override.ini (added) -Copy the `platformio_override.ini` file which is contained in the `usermods/TTGO-T-Display/` folder into the root of your project folder. This file contains an override that remaps the button pin of WLED to use the on-board button to the right of the USB-C connector (when viewed with the port oriented downward - see hardware photo). +### 1. Copy `platformio_override.ini` + +Copy `usermods/TTGO-T-Display/platformio_override.ini` to the root of your WLED project folder. + +This file: +- Sets the active build environment to `WLED_T-Display` +- Sets `DATA_PINS=2` and `BTNPIN=35` for the T-Display's onboard button and LED data pin +- Includes this usermod via `custom_usermods` -### Platformio.ini changes Once the platformio_override.ini file has been copied as described above, the platformio.ini file isn't actually changed, but it is helpful to save the platformio.ini file in the VS Code application. This should trigger the download of the library dependencies. -### Change to the WLED_T-Display environment -This should appear as an option in the bottom toolbar. +### 2. Build and flash + +Select the `WLED_T-Display` environment in PlatformIO (it should appear in the bottom toolbar) and build. The display will show **Loading…** on first boot, then the connection info. + +## Using with other boards + +The display driver itself requires only a TFT_eSPI-compatible display with at least 240×135 pixels. To adapt it to different hardware: + +### Quick setup + +1. **Create a `platformio_override.ini` file** with a new environment for your board. Use `extends` to reference one of the pre-existing board environments from `platformio.ini` to avoid copy-and-pasting the complete configuration. + +2. **Add this usermod** to your environment by appending `TTGO-T-Display` to your `custom_usermods` line. + +3. **Set `custom_display_setup` to point to your display setup**. See the detailed discussion below. + +### Selecting a display setup + +TFT_eSPI requires a hardware configuration header. The default for this usermod is `User_Setups/Setup25_TTGO_T_Display.h` from the TFT_eSPI library, which has the definitions for the standard TTGO T-Display board. + +To use a different display, add `custom_display_setup` to your `[env:]` section in `platformio_override.ini`: + +```ini +# Use a different built-in TFT_eSPI setup: +custom_display_setup = User_Setups/Setup23_ST7789.h + +# Or point to a custom header anywhere on disk or in wled00/: +custom_display_setup = /absolute/path/to/MyDisplay.h +custom_display_setup = MyDisplay.h +``` + +The value is resolved in this order: +1. As an absolute path (used directly if the file exists) +2. Relative to any include directory: `wled00/`, project include dir, and all TFT_eSPI include paths + +A custom header must define all required TFT_eSPI `#define`s for your hardware (driver, pins, SPI frequency, etc.). See the `User_Setups/` folder of TFT_eSPI for examples. + +You do not need to set `USER_SETUP_LOADED`. + +> **Note:** Display setup injection is handled by the `TFT_eSPI_compat` shim library in `lib/`. This library also provides workarounds for some library bugs, such as a hard dependency on the SPIFFS stub so SMOOTH_FONT can be compiled on platforms without SPIFFS support. + +> **Note:** Many TFT_eSPI usage examples suggest defining the setup as `-D XX_PIN=N` settings in `build_flags`. That approach will not work correctly with the `TFT_eSPI_compat` library; use of a display setup `.h` file is required. + +## Pin conflict detection + +During `setup()`, the usermod registers all TFT pins (MOSI, SCLK, CS, DC, RST, BL, and optionally MISO/TOUCH_CS) with WLED's PinManager. If any pin is already claimed by another usermod or WLED core function, the display will be disabled at startup and a debug message printed. + +## Runtime configuration + +The usermod exposes one config key visible in the WLED settings UI: -Once the compilation is done and loaded onto the TTGO T-Display module, the display should show "Loading...", and then it will show the IP of the WLED access point. +| Key | Default | Description | +|-----|---------|-------------| +| `enabled` | `true` | Enable or disable the display. When disabled, the backlight is turned off immediately. | +Settings are persisted in `cfg.json` under the `"TFT Display"` key. diff --git a/usermods/TTGO-T-Display/platformio_override.ini b/usermods/TTGO-T-Display/platformio_override.ini index b85b450e51..cd44a60a92 100644 --- a/usermods/TTGO-T-Display/platformio_override.ini +++ b/usermods/TTGO-T-Display/platformio_override.ini @@ -7,33 +7,15 @@ [platformio] #default_envs = WLED_tasmota_1M # define as many as you need default_envs = WLED_T-Display # define as many as you need + #---------- # SAMPLE #---------- [env:WLED_T-Display] - -board = esp32dev +extends = env:esp32dev upload_speed = 921600 monitor_speed = 115200 -platform = ${esp32.platform} -platform_packages = ${esp32.platform_packages} -build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"WLED_T-Display\" -; -D IRPIN=4 -; -D RLYPIN=12 -; -D RLYMDE=1 -; -D WLED_DISABLE_BROWNOUT_DET -;PIN defines - uncomment and change, if needed: - -D DATA_PINS=2 -;or use this for multiple outputs -; -D DATA_PINS=1,3 - -D BTNPIN=35 -; -D IRPIN=4 -; -D RLYPIN=12 -; -D RLYMDE=1 -; -D RLYODRAIN=0 -; -D LED_BUILTIN=2 # GPIO of built-in LED -lib_deps = ${esp32.lib_deps} -custom_usermods = audioreactive TTGO-T-Display -monitor_filters = esp32_exception_decoder -board_build.partitions = ${esp32.default_partitions} + -D DATA_PINS=2 ; Data pin on TTGO T-Display board + -D BTNPIN=35 ; Button on TTGO T-Display board +custom_usermods = ${env:esp32dev.custom_usermods} TTGO-T-Display From fd2c84f12caed2753f5807952ff2134c75ebd011 Mon Sep 17 00:00:00 2001 From: Will Miles Date: Sat, 4 Jul 2026 23:44:08 +0000 Subject: [PATCH 23/23] usermod/TTGO-T-Display: debug cleanup --- usermods/TTGO-T-Display/usermod.cpp | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/usermods/TTGO-T-Display/usermod.cpp b/usermods/TTGO-T-Display/usermod.cpp index 8ed3ad661c..c57c0f5112 100644 --- a/usermods/TTGO-T-Display/usermod.cpp +++ b/usermods/TTGO-T-Display/usermod.cpp @@ -202,7 +202,6 @@ void loop() override { // tft.print(apPass); // else // tft.print(knownIp); - DEBUG_PRINTLN("Print known AP"); if (apActive) { tft.print("AP IP: "); tft.print(knownIp); @@ -211,7 +210,6 @@ void loop() override { tft.print(apPass); } else { - DEBUG_PRINTLN("Print IP"); tft.print("IP: "); tft.print(knownIp); tft.setCursor(1,46); @@ -227,25 +225,17 @@ void loop() override { char lineBuffer[tftcharwidth+1]; extractModeName(knownMode, JSON_mode_names, lineBuffer, tftcharwidth); tft.print(lineBuffer); - DEBUG_PRINTLN("Print mode name"); // Fourth row with palette name tft.setCursor(1, 90); extractModeName(knownPalette, JSON_palette_names, lineBuffer, tftcharwidth); tft.print(lineBuffer); - DEBUG_PRINTLN("Print palette"); // Fifth row with estimated mA usage tft.setCursor(1, 112); // Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate). - //tft.print(strip.currentMilliamps); tft.print(BusManager::currentMilliamps()); - DEBUG_PRINTLN(BusManager::currentMilliamps()); - DEBUG_PRINTLN(" mA (estimated)"); - //tft.print("test "); - tft.print("mA (estimated)"); - DEBUG_PRINTLN("Print estimated current"); - + tft.print("mA (estimated)"); }