Enhanced ST7789 display usermod.

This commit is contained in:
Blaz Kristan 2021-08-28 21:59:52 +02:00
parent 849bdc52f6
commit 4875544888
6 changed files with 309 additions and 233 deletions

1
.gitignore vendored
View File

@ -15,3 +15,4 @@
node_modules node_modules
.idea .idea
.direnv .direnv
wled-update.sh

View File

@ -2,11 +2,14 @@
This usermod allow to use 240x240 display to display following: This usermod allow to use 240x240 display to display following:
* current date and time;
* Network SSID; * Network SSID;
* IP address; * IP address;
* WiFi signal strength;
* Brightness; * Brightness;
* Chosen effect; * Chosen effect;
* Chosen palette; * Chosen palette;
* effect speed and intensity;
* Estimated current in mA; * Estimated current in mA;
## Hardware ## Hardware
@ -46,27 +49,29 @@ Add lines to section:
default_envs = esp32dev default_envs = esp32dev
build_flags = ${common.build_flags_esp32} build_flags = ${common.build_flags_esp32}
-D USERMOD_ST7789_DISPLAY -D USERMOD_ST7789_DISPLAY
-DUSER_SETUP_LOADED=1
-DST7789_DRIVER=1
-DTFT_WIDTH=240
-DTFT_HEIGHT=240
-DCGRAM_OFFSET=1
-DTFT_MOSI=21
-DTFT_SCLK=22
-DTFT_DC=27
-DTFT_RST=26
-DTFT_BL=14
-DLOAD_GLCD=1
;optional for WROVER
;-DCONFIG_SPIRAM_SUPPORT=1
``` ```
Save the `platformio.ini` file. Once this is saved, the required library files should be automatically downloaded for modifications in a later step. Save the `platformio.ini` file. Once this is saved, the required library files should be automatically downloaded for modifications in a later step.
### TFT_eSPI Library Adjustments ### TFT_eSPI Library Adjustments
We need to modify a file in the `TFT_eSPI` library. 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` folder. If you are not using PlatformIO you need to modify a file in the `TFT_eSPI` library. If you followed the directions to modify and save the `platformio.ini` file above, the `Setup24_ST7789.h` file can be found in the `/.pio/libdeps/esp32dev/TFT_eSPI/User_Setups/` folder.
Modify the `User_Setup_Select.h` file as follows: Edit `Setup_ST7789.h` file and uncomment nad changep GPIO pin numbers in lines containing `TFT_MOSI`, `TFT_SCLK`, `TFT_RST`, `TFT_DC`.
* Comment out the following line (which is the 'default' setup file): Modify the `User_Setup_Select.h` by uncommentig the line containing `#include <User_Setups/Setup24_ST7789.h>` and commenting out line containing `#include <User_Setup.h>`.
```ini If your display includes backlight enable pin, #define TFT_BL with backlight enable GPIO number.
//#include <User_Setup.h> // Default setup is root library folder
```
* Add following line:
```ini
#include <User_Setups/Setup_ST7789_Display.h> // Setup file for ESP32 ST7789V SPI bus TFT
```
* Copy file `"Setup_ST7789_Display.h"` from usermod folder to `/.pio/libdeps/esp32dev/TFT_eSPI/User_Setups`

View File

@ -7,27 +7,48 @@
#include <TFT_eSPI.h> #include <TFT_eSPI.h>
#include <SPI.h> #include <SPI.h>
#define USERMOD_ST7789_DISPLAY 97 #ifndef USER_SETUP_LOADED
#ifndef ST7789_DRIVER
#ifndef TFT_DISPOFF #error Please define ST7789_DRIVER
#define TFT_DISPOFF 0x28 #endif
#ifndef TFT_WIDTH
#error Please define TFT_WIDTH
#endif
#ifndef TFT_HEIGHT
#error Please define TFT_HEIGHT
#endif
#ifndef TFT_MOSI
#error Please define TFT_MOSI
#endif
#ifndef TFT_SCLK
#error Please define TFT_SCLK
#endif
#ifndef TFT_DC
#error Please define TFT_DC
#endif
#ifndef TFT_RST
#error Please define TFT_RST
#endif
#ifndef LOAD_GLCD
#error Please define LOAD_GLCD
#endif
#endif
#ifndef TFT_BL
#define TFT_BL -1
#endif #endif
#ifndef TFT_SLPIN #define USERMOD_ID_ST7789_DISPLAY 97
#define TFT_SLPIN 0x10
#endif
#define TFT_MOSI 21 TFT_eSPI tft = TFT_eSPI(TFT_WIDTH, TFT_HEIGHT); // Invoke custom library
#define TFT_SCLK 22
#define TFT_DC 18
#define TFT_RST 5
#define TFT_BL 26 // Display backlight control pin
TFT_eSPI tft = TFT_eSPI(240, 240); // Invoke custom library // Extra char (+1) for null
#define LINE_BUFFER_SIZE 20
// How often we are redrawing screen // How often we are redrawing screen
#define USER_LOOP_REFRESH_RATE_MS 1000 #define USER_LOOP_REFRESH_RATE_MS 1000
extern int getSignalQuality(int rssi);
//class name. Use something descriptive and leave the ": public Usermod" part :) //class name. Use something descriptive and leave the ": public Usermod" part :)
class St7789DisplayUsermod : public Usermod { class St7789DisplayUsermod : public Usermod {
@ -45,9 +66,70 @@ class St7789DisplayUsermod : public Usermod {
uint8_t knownBrightness = 0; uint8_t knownBrightness = 0;
uint8_t knownMode = 0; uint8_t knownMode = 0;
uint8_t knownPalette = 0; uint8_t knownPalette = 0;
uint8_t tftcharwidth = 19; // Number of chars that fit on screen with text size set to 2 uint8_t knownEffectSpeed = 0;
uint8_t knownEffectIntensity = 0;
uint8_t knownMinute = 99;
uint8_t knownHour = 99;
const uint8_t tftcharwidth = 19; // Number of chars that fit on screen with text size set to 2
long lastUpdate = 0; long lastUpdate = 0;
void center(String &line, uint8_t width) {
int len = line.length();
if (len<width) for (byte i=(width-len)/2; i>0; i--) line = ' ' + line;
for (byte i=line.length(); i<width; i++) line += ' ';
}
/**
* Display the current date and time in large characters
* on the middle rows. Based 24 or 12 hour depending on
* the useAMPM configuration.
*/
void showTime() {
if (!ntpEnabled) return;
char lineBuffer[LINE_BUFFER_SIZE];
updateLocalTime();
byte minuteCurrent = minute(localTime);
byte hourCurrent = hour(localTime);
byte secondCurrent = second(localTime);
knownMinute = minuteCurrent;
knownHour = hourCurrent;
byte currentMonth = month(localTime);
sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(currentMonth), day(localTime));
tft.setTextColor(TFT_SILVER);
tft.setCursor(84, 0);
tft.setTextSize(2);
tft.print(lineBuffer);
byte showHour = hourCurrent;
boolean isAM = false;
if (useAMPM) {
if (showHour == 0) {
showHour = 12;
isAM = true;
} else if (showHour > 12) {
showHour -= 12;
isAM = false;
} else {
isAM = true;
}
}
sprintf_P(lineBuffer, PSTR("%2d:%02d"), (useAMPM ? showHour : hourCurrent), minuteCurrent);
tft.setTextColor(TFT_WHITE);
tft.setTextSize(4);
tft.setCursor(60, 24);
tft.print(lineBuffer);
tft.setTextSize(2);
tft.setCursor(186, 24);
//sprintf_P(lineBuffer, PSTR("%02d"), secondCurrent);
if (useAMPM) tft.print(isAM ? "AM" : "PM");
//else tft.print(lineBuffer);
}
public: public:
//Functions called by WLED //Functions called by WLED
@ -57,6 +139,9 @@ class St7789DisplayUsermod : public Usermod {
*/ */
void setup() void setup()
{ {
PinManagerPinType pins[] = { { TFT_MOSI, true }, { TFT_MISO, false}, { TFT_SCLK, true }, { TFT_CS, true}, { TFT_DC, true}, { TFT_RST, true }, { TFT_BL, true } };
if (!pinManager.allocateMultiplePins(pins, 5, PinOwner::UM_FourLineDisplay)) { return; }
tft.init(); tft.init();
tft.setRotation(0); //Rotation here is set up for the text to be readable with the port on the left. Use 1 to flip. tft.setRotation(0); //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.fillScreen(TFT_BLACK);
@ -65,10 +150,10 @@ class St7789DisplayUsermod : public Usermod {
tft.setTextDatum(MC_DATUM); tft.setTextDatum(MC_DATUM);
tft.setTextSize(2); tft.setTextSize(2);
tft.print("Loading..."); tft.print("Loading...");
if (TFT_BL > 0) if (TFT_BL >= 0)
{ // TFT_BL has been set in the TFT_eSPI library {
pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode
digitalWrite(TFT_BL, HIGH); // Turn backlight on. digitalWrite(TFT_BL, HIGH); // Turn backlight on.
} }
} }
@ -91,174 +176,189 @@ class St7789DisplayUsermod : public Usermod {
* Instead, use a timer check as shown here. * Instead, use a timer check as shown here.
*/ */
void loop() { void loop() {
// Check if we time interval for redrawing passes. char buff[LINE_BUFFER_SIZE];
if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS)
// Check if we time interval for redrawing passes.
if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS)
{ {
return; return;
} }
lastUpdate = millis(); lastUpdate = millis();
// Turn off display after 5 minutes with no change. // Turn off display after 5 minutes with no change.
if(!displayTurnedOff && millis() - lastRedraw > 5*60*1000) if (!displayTurnedOff && millis() - lastRedraw > 5*60*1000)
{ {
digitalWrite(TFT_BL, LOW); // Turn backlight off. if (TFT_BL >= 0) digitalWrite(TFT_BL, LOW); // Turn backlight off.
displayTurnedOff = true; displayTurnedOff = true;
} }
// Check if values which are shown on display changed from the last time. // Check if values which are shown on display changed from the last time.
if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) if ((((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) ||
{ (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : Network.localIP())) ||
needRedraw = true; (knownBrightness != bri) ||
} (knownEffectSpeed != effectSpeed) ||
else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP())) (knownEffectIntensity != effectIntensity) ||
{ (knownMode != strip.getMode()) ||
needRedraw = true; (knownPalette != strip.getSegment(0).palette))
}
else if (knownBrightness != bri)
{
needRedraw = true;
}
else if (knownMode != strip.getMode())
{
needRedraw = true;
}
else if (knownPalette != strip.getSegment(0).palette)
{
needRedraw = true;
}
if (!needRedraw)
{
return;
}
needRedraw = false;
if (displayTurnedOff)
{
digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); // Turn backlight on.
displayTurnedOff = false;
}
lastRedraw = millis();
// Update last known values.
#if defined(ESP8266)
knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
#else
knownSsid = WiFi.SSID();
#endif
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
knownBrightness = bri;
knownMode = strip.getMode();
knownPalette = strip.getSegment(0).palette;
tft.fillScreen(TFT_BLACK);
tft.setTextSize(2);
// First row with Wifi name
tft.setTextColor(TFT_SILVER);
tft.setCursor(3, 40);
tft.print(knownSsid.substring(0, tftcharwidth > 1 ? tftcharwidth - 1 : 0));
// Print `~` char to indicate that SSID is longer, than our dicplay
if (knownSsid.length() > tftcharwidth)
tft.print("~");
// Second row with AP IP and Password or IP
tft.setTextColor(TFT_GREEN);
tft.setTextSize(2);
tft.setCursor(3, 64);
// Print AP IP and password in AP mode or knownIP if AP not active.
if (apActive)
{
tft.setTextColor(TFT_YELLOW);
tft.print("AP IP: ");
tft.print(knownIp);
tft.setCursor(3,86);
tft.setTextColor(TFT_YELLOW);
tft.print("AP Pass:");
tft.print(apPass);
}
else
{
tft.setTextColor(TFT_GREEN);
tft.print("IP: ");
tft.print(knownIp);
tft.setCursor(3,86);
//tft.print("Signal Strength: ");
//tft.print(i.wifi.signal);
tft.setTextColor(TFT_WHITE);
tft.print("Bri: ");
tft.print(((float(bri)/255)*100),0);
tft.print("%");
}
// Third row with mode name
tft.setCursor(3, 108);
uint8_t qComma = 0;
bool insideQuotes = false;
uint8_t printedChars = 0;
char singleJsonSymbol;
// Find the mode name in JSON
for (size_t i = 0; i < strlen_P(JSON_mode_names); i++)
{
singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i);
switch (singleJsonSymbol)
{ {
needRedraw = true;
}
if (!needRedraw)
{
return;
}
needRedraw = false;
if (displayTurnedOff)
{
digitalWrite(TFT_BL, HIGH); // Turn backlight on.
displayTurnedOff = false;
}
lastRedraw = millis();
// Update last known values.
#if defined(ESP8266)
knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
#else
knownSsid = WiFi.SSID();
#endif
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
knownBrightness = bri;
knownMode = strip.getMode();
knownPalette = strip.getSegment(0).palette;
knownEffectSpeed = effectSpeed;
knownEffectIntensity = effectIntensity;
tft.fillScreen(TFT_BLACK);
showTime();
tft.setTextSize(2);
// Wifi name
tft.setTextColor(TFT_GREEN);
tft.setCursor(0, 60);
String line = knownSsid.substring(0, tftcharwidth-1);
// Print `~` char to indicate that SSID is longer, than our display
if (knownSsid.length() > tftcharwidth) line = line.substring(0, tftcharwidth-1) + '~';
center(line, tftcharwidth);
tft.print(line.c_str());
// Print AP IP and password in AP mode or knownIP if AP not active.
if (apActive)
{
tft.setCursor(0, 84);
tft.print("AP IP: ");
tft.print(knownIp);
tft.setCursor(0,108);
tft.print("AP Pass:");
tft.print(apPass);
}
else
{
tft.setCursor(0, 84);
line = knownIp.toString();
center(line, tftcharwidth);
tft.print(line.c_str());
// percent brightness
tft.setCursor(0, 120);
tft.setTextColor(TFT_WHITE);
tft.print("Bri: ");
tft.print((((int)bri*100)/255));
tft.print("%");
// signal quality
tft.setCursor(124,120);
tft.print("Sig: ");
if (getSignalQuality(WiFi.RSSI()) < 10) {
tft.setTextColor(TFT_RED);
} else if (getSignalQuality(WiFi.RSSI()) < 25) {
tft.setTextColor(TFT_ORANGE);
} else {
tft.setTextColor(TFT_GREEN);
}
tft.print(getSignalQuality(WiFi.RSSI()));
tft.setTextColor(TFT_WHITE);
tft.print("%");
}
// mode name
tft.setTextColor(TFT_CYAN);
tft.setCursor(0, 144);
uint8_t qComma = 0;
bool insideQuotes = false;
uint8_t printedChars = 0;
char singleJsonSymbol;
// Find the mode name in JSON
for (size_t i = 0; i < strlen_P(JSON_mode_names); i++)
{
singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i);
switch (singleJsonSymbol)
{
case '"':
insideQuotes = !insideQuotes;
break;
case '[':
case ']':
break;
case ',':
qComma++;
default:
if (!insideQuotes || (qComma != knownMode))
break;
tft.print(singleJsonSymbol);
printedChars++;
}
if ((qComma > knownMode) || (printedChars > tftcharwidth - 1))
break;
}
// palette name
tft.setTextColor(TFT_YELLOW);
tft.setCursor(0, 168);
qComma = 0;
insideQuotes = false;
printedChars = 0;
// Looking for palette name in JSON.
for (size_t i = 0; i < strlen_P(JSON_palette_names); i++)
{
singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i);
switch (singleJsonSymbol)
{
case '"': case '"':
insideQuotes = !insideQuotes; insideQuotes = !insideQuotes;
break;
case '[':
case ']':
break;
case ',':
qComma++;
default:
if (!insideQuotes || (qComma != knownMode))
break; break;
tft.setTextColor(TFT_MAGENTA); case '[':
tft.print(singleJsonSymbol); case ']':
printedChars++;
}
if ((qComma > knownMode) || (printedChars > tftcharwidth - 1))
break;
}
// Fourth row with palette name
tft.setTextColor(TFT_YELLOW);
tft.setCursor(3, 130);
qComma = 0;
insideQuotes = false;
printedChars = 0;
// Looking for palette name in JSON.
for (size_t i = 0; i < strlen_P(JSON_palette_names); i++)
{
singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i);
switch (singleJsonSymbol)
{
case '"':
insideQuotes = !insideQuotes;
break;
case '[':
case ']':
break;
case ',':
qComma++;
default:
if (!insideQuotes || (qComma != knownPalette))
break; break;
tft.print(singleJsonSymbol); case ',':
printedChars++; qComma++;
default:
if (!insideQuotes || (qComma != knownPalette))
break;
tft.print(singleJsonSymbol);
printedChars++;
}
// The following is modified from the code from the u8g2/u8g8 based code (knownPalette was knownMode)
if ((qComma > knownPalette) || (printedChars > tftcharwidth - 1))
break;
} }
// The following is modified from the code from the u8g2/u8g8 based code (knownPalette was knownMode)
if ((qComma > knownPalette) || (printedChars > tftcharwidth - 1)) tft.setCursor(0, 192);
break; tft.setTextColor(TFT_SILVER);
} sprintf_P(buff, PSTR("FX Spd:%3d Int:%3d"), effectSpeed, effectIntensity);
// Fifth row with estimated mA usage tft.print(buff);
tft.setTextColor(TFT_SILVER);
tft.setCursor(3, 152); // Fifth row with estimated mA usage
// Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate). tft.setTextColor(TFT_SILVER);
tft.print("Current: "); tft.setCursor(0, 216);
tft.print(strip.currentMilliamps); // Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate).
tft.print("mA"); tft.print("Current: ");
tft.setTextColor(TFT_ORANGE);
tft.print(strip.currentMilliamps);
tft.print("mA");
} }
/* /*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. * 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. * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
@ -295,7 +395,7 @@ class St7789DisplayUsermod : public Usermod {
*/ */
void readFromJsonState(JsonObject& root) void readFromJsonState(JsonObject& root)
{ {
userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value //userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
//if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!")); //if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
} }
@ -316,8 +416,8 @@ class St7789DisplayUsermod : public Usermod {
*/ */
void addToConfig(JsonObject& root) void addToConfig(JsonObject& root)
{ {
JsonObject top = root.createNestedObject("exampleUsermod"); //JsonObject top = root.createNestedObject("exampleUsermod");
top["great"] = userVar0; //save this var persistently whenever settings are saved //top["great"] = userVar0; //save this var persistently whenever settings are saved
} }
@ -329,10 +429,11 @@ class St7789DisplayUsermod : public Usermod {
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup. * but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :) * If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
*/ */
void readFromConfig(JsonObject& root) bool readFromConfig(JsonObject& root)
{ {
JsonObject top = root["top"]; //JsonObject top = root["top"];
userVar0 = top["great"] | 42; //The value right of the pipe "|" is the default value in case your setting was not present in cfg.json (e.g. first boot) //userVar0 = top["great"] | 42; //The value right of the pipe "|" is the default value in case your setting was not present in cfg.json (e.g. first boot)
return true;
} }
@ -342,7 +443,7 @@ class St7789DisplayUsermod : public Usermod {
*/ */
uint16_t getId() uint16_t getId()
{ {
return USERMOD_ST7789_DISPLAY; return USERMOD_ID_ST7789_DISPLAY;
} }
//More methods can be added in the future, this example will then be extended. //More methods can be added in the future, this example will then be extended.

View File

@ -1,39 +0,0 @@
// Setup for the ESP32 board with 1.5" 240x240 display
// See SetupX_Template.h for all options available
#define ST7789_DRIVER
#define TFT_SDA_READ // Display has a bidirectionsl SDA pin
#define TFT_WIDTH 240
#define TFT_HEIGHT 240
#define CGRAM_OFFSET // Library will add offsets required
//#define TFT_MISO -1
#define TFT_MOSI 21
#define TFT_SCLK 22
//#define TFT_CS 5
#define TFT_DC 18
#define TFT_RST 5
#define TFT_BL 26 // Display backlight control pin
#define TFT_BACKLIGHT_ON HIGH // HIGH or LOW are options
#define LOAD_GLCD
#define LOAD_FONT2
#define LOAD_FONT4
#define LOAD_FONT6
#define LOAD_FONT7
#define LOAD_FONT8
#define LOAD_GFXFF
//#define SMOOTH_FONT
//#define SPI_FREQUENCY 27000000
#define SPI_FREQUENCY 40000000 // Maximum for ILI9341
#define SPI_READ_FREQUENCY 6000000 // 6 MHz is the maximum SPI read speed for the ST7789V

View File

@ -86,6 +86,10 @@
#include "../usermods/rgb-rotary-encoder/rgb-rotary-encoder.h" #include "../usermods/rgb-rotary-encoder/rgb-rotary-encoder.h"
#endif #endif
#ifdef USERMOD_ST7789_DISPLAY
#include "../usermods/ST7789_display/ST7789_Display.h"
#endif
void registerUsermods() void registerUsermods()
{ {
/* /*
@ -167,4 +171,8 @@ void registerUsermods()
#ifdef RGB_ROTARY_ENCODER #ifdef RGB_ROTARY_ENCODER
usermods.add(new RgbRotaryEncoderUsermod()); usermods.add(new RgbRotaryEncoderUsermod());
#endif #endif
#ifdef USERMOD_ST7789_DISPLAY
usermods.add(new St7789DisplayUsermod());
#endif
} }

View File

@ -8,7 +8,7 @@
*/ */
// version code in format yymmddb (b = daily build) // version code in format yymmddb (b = daily build)
#define VERSION 2108271 #define VERSION 2108281
//uncomment this if you have a "my_config.h" file you'd like to use //uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG //#define WLED_USE_MY_CONFIG