Page featuring more displays and sketches

Lilygo T-Display S3 and custom fonts



You can use any font from your own PC (or a downloaded font) on the ESP32.
We will try the font "HP Simplified Bold" in this example.
If you don't have this on your PC you can download it from https://fonnts.com/hp-simplified/
Save this font somewhere you can find it.

Then click https://rop.nl/truetype2gfx/
Select and upload the font.

Important: You must rename the font file before uploading it. The name is used in a few places in the library itself, so renaming afterwards will result in error messages. In this example, we rename it "HPSimplified.ttf".

Choose your desired size (e.g. 54 pt)
Then click on the button "Get GFX font file".

You can then download the font as e.g. "HPSimplified54pt7b.h". Place it in the same folder as the sketch. If you wish to compile the sketch below, repeat for sizes 27pt and 20pt: "HPSimplified27pt7b.h" and "HPSimplified20pt7b.h". These names should match those in the sketch.

Next, download the font https://www.dafont.com/wifi.font/. Select and upload it to https://rop.nl/truetype2gfx/.
Choose the size 20pt. Then click on the "Get GFX font file" button.
Place "WIFI20pt7b.h" in the same folder as the sketch.




In the TFT_eSPI library used, a file is provided in the User_Setups folder for this board: Setup206_LilyGo_T_Display_S3.h

Sketch:

#include <WiFi.h>  // Arduino IDE - board: Lilygo T_Display-S3
#include <WebServer.h>
#include <TFT_eSPI.h>  // https://github.com/Bodmer/TFT_eSPI
#include <Preferences.h>
#include "HPSimplified54pt7b.h"  // upload font from your PC, i.e. C:\Windows\Fonts\HPSimplified.ttf to https://rop.nl/truetype2gfx/
#include "HPSimplified27pt7b.h"  // choose the desired size. Then download "HPSimplified54pt7b.h" to the same folder as this sketch.
#include "HPSimplified20pt7b.h"  // for displaying time, a non-proportional (fixed-width) bold font is best
#include "WIFI20pt7b.h"          // several WiFi icons (free): https://www.dafont.com/wifi.font

Preferences flash;
WebServer server(80);
TFT_eSPI tft = TFT_eSPI();  // User_Setup_Select.h: Setup206_LilyGo_T_Display_S3.h
TFT_eSprite sprite = TFT_eSprite(&tft);
TFT_eSprite leftSp = TFT_eSprite(&tft);
TFT_eSprite rightS = TFT_eSprite(&tft);

uint8_t SCREEN_ORIENTATION = 3;  // USB connection: 3 = left side - 1 = right side.
const char* location[] = { "Brussels", "Kolkata", "Shanghai", "Auckland", "Los Angeles", "New York" };
const char* timeZone[] = { "CET-1CEST,M3.5.0,M10.5.0/3", "IST-5:30", "CST-8", "NZST-12NZDT,M9.5.0,M4.1.0/3",
                           "PST8PDT,M3.2.0,M11.1.0", "EST5EDT,M3.2.0,M11.1.0" };  // # of items must match # of locations (line above)
const char* weekDays[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
const char* startTxt[] = { "Connecting to WiFi", "WiFi OK: Time sync" };
const char* noConnec[] = { " WiFi: no connection.", " Connect to hotspot 'TD-S3'", " and open a browser",
                           " at adres 192.168.4.1", " to enter network name", " and password." };
String webText, buttons, ssid, pasw;
bool freshStart = true;
uint8_t count;

void setup() {
  pinMode(15, OUTPUT);  // PWD
  digitalWrite(15, HIGH);
  pinMode(14, INPUT_PULLUP);  // button for changing time zone
  pinMode(0, INPUT_PULLUP);   // flash button
  initDisplay();
  showStartUpLogo();
  connect_to_WiFi();
  showConnected();
  setTimeZoneFromFlash();
}

void loop() {
  display_Time(TFT_YELLOW, TFT_GREEN);  // colors of weekday & date
  if (!digitalRead(14)) switchTimeZone();
  if (!digitalRead(0)) screen_Saver();
}

void initDisplay() {
  tft.init(), tft.setRotation(SCREEN_ORIENTATION);
  sprite.createSprite(320, 170);  // sprite for faster rendering
}

void showStartUpLogo() {  // use functions of library to draw = smaller than a graphic file
  tft.fillScreen(TFT_BLACK);
  sprite.fillSprite(sprite.color565(100, 100, 100));
  for (uint16_t i = 0; i < 12000; i++) {  // create surface with fine texture
    uint8_t j = random(100) + 50;
    sprite.drawPixel(random(320), random(170), sprite.color565(j, j, j));
  }
  sprite.setTextColor(tft.color565(48, 48, 48));
  for (uint8_t i = 0; i < 8; i++) {  // draw IC legs
    sprite.drawRect(131 + (i * 10), 10, 2, 115, sprite.color565(240, 240, 240));
    sprite.fillRoundRect(129 + (i * 10), 18, 6, 99, 2, sprite.color565(240, 240, 240));
    sprite.drawRect(110, 32 + (i * 10), 115, 2, sprite.color565(240, 240, 240));
    sprite.fillRoundRect(118, 30 + (i * 10), 99, 6, 2, sprite.color565(240, 240, 240));
  }
  sprite.fillRoundRect(122, 22, 91, 91, 3, TFT_BLACK);  // draw IC
  sprite.drawRoundRect(122, 22, 91, 91, 3, TFT_DARKGREY);
  sprite.setTextFont(1);
  sprite.drawCentreString("ESP32", 157, 74, 1), sprite.drawCentreString("1732S019", 164, 86, 1);
  sprite.fillCircle(200, 34, 3, sprite.color565(16, 16, 16));
  sprite.setFreeFont(&FreeSans18pt7b);
  sprite.setTextColor(TFT_BLACK);  // embossed text below IC
  sprite.drawCentreString(startTxt[0], 161, 132, 1);
  sprite.setTextColor(TFT_WHITE);
  sprite.drawCentreString(startTxt[0], 159, 130, 1);
  sprite.setTextColor(sprite.color565(100, 100, 100));
  sprite.drawCentreString(startTxt[0], 160, 131, 1);
  sprite.pushSprite(0, 0);
}

void show_Message_No_Connection() {
  tft.fillScreen(TFT_NAVY);
  tft.setTextColor(TFT_YELLOW), tft.setTextFont(4), tft.setCursor(0, 0, 4);
  for (uint8_t count = 0; count < 6; count++) tft.println(noConnec[count]);
}

void showConnected() {
  leftSp.createSprite(320, 58);
  leftSp.fillSprite(sprite.color565(100, 100, 100));
  leftSp.setFreeFont(&FreeSans18pt7b);
  for (uint16_t i = 0; i < 4000; i++) {
    uint8_t j = random(100) + 50;
    leftSp.drawPixel(random(320), random(58), sprite.color565(j, j, j));  // random greyscale
  }
  leftSp.setTextColor(TFT_BLACK);  // embossed text "connected"
  leftSp.drawCentreString(startTxt[1], 161, 6, 1);
  leftSp.setTextColor(TFT_WHITE);
  leftSp.drawCentreString(startTxt[1], 159, 4, 1);
  leftSp.setTextColor(sprite.color565(100, 100, 100));
  leftSp.drawCentreString(startTxt[1], 160, 5, 1);
  for (uint16_t i = 850; i > 650; i--) leftSp.pushSprite(0, i / 5);  // slide text upwards
  leftSp.pushToSprite(&sprite, 0, 131);                              // add new text to existing sprite
  leftSp.deleteSprite();
}

void setTimeZoneFromFlash() {
  flash.begin("my-clock", true);       // read from flash (true = read only)
  count = flash.getInt("counter", 0);  // retrieve the last set time zone - default to first in the array [0]
  flash.end();
  count = count % (sizeof(timeZone) / sizeof(timeZone[0]));  // modulo (# of elements in array) = prevent errors
  configTzTime(timeZone[count], "pool.ntp.org");             // clock will automatically adjust to daylight saving time
}

void display_Time(uint16_t WD_COL, uint16_t DT_COL) {  // show time information on the screen
  struct tm tInfo;                                     // https://cplusplus.com/reference/ctime/tm/
  uint8_t TIME_TOP = 82;                               // distance between top edge of screen and bottom of font
  getLocalTime(&tInfo);                                // time sync during startup and every 3 hours thereafter
  if (freshStart) splitScreen(true);
  sprite.fillSprite(TFT_BLACK), sprite.setTextColor(WiFi.isConnected() ? TFT_GREEN : TFT_RED);
  sprite.setFreeFont(&WIFI20pt7b), sprite.setCursor(267, 38), sprite.print("b");  // https://www.dafont.com/wifi.font = WiFi logo
  sprite.setFreeFont(&HPSimplified54pt7b), sprite.setTextColor(TFT_CYAN);
  sprite.setCursor(0, TIME_TOP), sprite.printf("%02d:%02d", tInfo.tm_hour, tInfo.tm_min);
  sprite.setFreeFont(&HPSimplified27pt7b);
  sprite.setCursor(258, TIME_TOP), sprite.printf("%02d", tInfo.tm_sec);
  sprite.setTextColor(WD_COL), sprite.setFreeFont(&HPSimplified20pt7b);
  sprite.drawCentreString(weekDays[tInfo.tm_wday], 160, 94, 1);
  char theDate[11];
  sprintf(theDate, "%02d-%02d-%04d", tInfo.tm_mday, 1 + tInfo.tm_mon, 1900 + tInfo.tm_year);
  sprite.setTextColor(DT_COL), sprite.drawCentreString(theDate, 160, 137, 1);
  if (freshStart) splitScreen(false), freshStart = false;  // before "pushSprite" because the background must be black
  sprite.pushSprite(0, 0);
}

void splitScreen(bool split) {  // split (true) or merge (false) sprite horizontally
  leftSp.createSprite(160, 170), rightS.createSprite(160, 170);
  for (uint8_t ver = 0; ver < 170; ver++) {  // divide the sprite into 2 pieces & write data to two smaller sprites
    for (uint16_t hor = 0; hor < 160; hor++) leftSp.drawPixel(hor, ver, sprite.readPixel(hor, ver));
    for (uint16_t hor = 160; hor < 320; hor++) rightS.drawPixel(hor - 160, ver, sprite.readPixel(hor, ver));
  }
  if (split) {  // avoid sloppy lines: make sure the remaining part of the screen is black
    leftSp.drawFastVLine(159, 0, 170, TFT_BLACK), leftSp.drawFastVLine(158, 0, 170, TFT_BLACK);
    rightS.drawFastVLine(0, 0, 170, TFT_BLACK), rightS.drawFastVLine(1, 0, 170, TFT_BLACK);
  }
  for (uint16_t hor = 0; hor < 160; hor += 2) {
    if (split) leftSp.pushSprite(0 - hor, 0), rightS.pushSprite(hor + 160, 0);  // move both sprites to the outer edge
    else leftSp.pushSprite(hor - 160, 0), rightS.pushSprite(320 - hor, 0);      // merge both sprites
  }
  leftSp.deleteSprite(), rightS.deleteSprite();
}

void switchTimeZone() {
  leftSp.createSprite(320, 75), leftSp.setFreeFont(&HPSimplified27pt7b);
  leftSp.fillSprite(TFT_BLACK), leftSp.setTextColor(TFT_RED);
  leftSp.drawCentreString(location[count], 160, 3, 1);                     // old location
  for (uint16_t tel = 0; tel < 320; tel += 2) leftSp.pushSprite(tel, 95);  // animation (slow) = UI debouncing
  count = (count + 1) % (sizeof(timeZone) / sizeof(timeZone[0]));          // increase modulo (number of elements in char array)
  configTzTime(timeZone[count], "pool.ntp.org");                           // set time zone (and DST data) for new location
  display_Time(TFT_BLACK, TFT_BLACK), leftSp.fillSprite(TFT_BLACK);        // weekday & date not visible this time
  leftSp.drawCentreString(location[count], 160, 3, 1);                     // new location
  for (int16_t tel = -320; tel < 1; tel += 2) leftSp.pushSprite(tel, 95);
  for (uint8_t tel = 0; tel < 64; tel++) leftSp.pushSprite(0, 95);  // Keep text on the screen for about 500 mSec.
  leftSp.deleteSprite();
}

void screen_Saver() {  // flash button pressed: toggle display backlight on/off
  uint8_t savedZone;
  flash.begin("my-clock");                                 // flash memory (also write since 2nd param = not set)
  savedZone = flash.getInt("counter", 0);                  // retrieve the last set time zone - default to first in the array [0]
  if (savedZone != count) flash.putInt("counter", count);  // only write the time zone to flash memory when it was changed
  flash.end();                                             // to prevent chip wear from excessive writing
  digitalWrite(TFT_BL, !digitalRead(TFT_BL));
  delay(300);
}

void connect_to_WiFi() {  // connect to WiFi, if not successful: start web server
  WiFi.mode(WIFI_MODE_STA);
  flash.begin("login_data", true);  // true = read only
  ssid = flash.getString("ssid", "");
  pasw = flash.getString("pasw", "");
  flash.end();
  WiFi.begin(ssid.c_str(), pasw.c_str());
  for (uint8_t i = 0; i < 50; ++i) {  // we try for about 8 seconds to connect
    if (WiFi.isConnected()) {
      WiFi.setAutoReconnect(true);
      WiFi.persistent(true);
      return;  // jumps out of this function when WiFi connection succeeds
    }
    delay(160);
  }
  show_Message_No_Connection();  // script lands on this line only when the connection fails
  int n = WiFi.scanNetworks();
  for (int i = 0; i < n; ++i) {  // html to put found networks on buttons on web page
    buttons += "\n<button onclick='scrollNaar(this.id)' id='" + WiFi.SSID(i) + "'>" + WiFi.SSID(i) + "</button><br>";
  }
  WiFi.mode(WIFI_MODE_AP);
  WiFi.softAP("TD-S3", "", 1);
  server.on("/", server_Root);
  server.on("/setting", server_Setting);
  server.begin();
  for (;;) server.handleClient();  // infinite loop until the WiFi credentials are inserted
}

void server_Root() {
  webText = "<!DOCTYPE HTML>\n<html lang='en'>\n<head><title>Setup</title>\n<meta name='viewport' ";
  webText += "content='width=device-width, initial-scale=1.0'>";
  webText += "\n<style>\np {\n  font-family: Arial, Helvetica, sans-serif;\n  font-size: 18px;\n  margin: 0;\n  text-align: ";
  webText += "center;\n}\n\nbutton, input[type=submit] {\n  width: 250px;\n  border-radius: 5px;\n  color: White;\n  padding:";
  webText += " 4px 4px;\n  margin-top: 16px;\n  margin: 0 auto;\n  display:block;\n  font-size: 18px;\n  font-weight: 600;";
  webText += "\n  background: DodgerBlue;\n}\n\ninput {\n  width: 250px;\n  font-size: 18px;\n  font-weight: 600;\n}";
  webText += "\n</style>\n</head>\n<body><p style='font-family:arial; ";
  webText += "font-size:240%;'>WiFi setup\n</p><p style='font-family:arial; font-size:160%;'>\n<br>";
  webText += "Networks found:<br> Click on item to select or<br>Enter your network data<br> in the boxes below:</p><br>";
  webText += buttons;
  webText += "\n<form method='get' action='setting'>\n<p><b>\nSSID: <br>\n<input id='ssid' name='ssid'>";
  webText += "<br>PASW: </b><br>\n<input type='password' name='pass'><br><br>\n<input type='submit' value='Save'>";
  webText += "\n</p>\n</form>\n<script>\nfunction scrollNaar(tekst) {\n  document.getElementById('ssid')";
  webText += ".value = tekst;\n  window.scrollTo(0, document.body.scrollHeight);\n}\n</script>\n</body>\n</html>";
  server.send(200, "\ntext/html", webText);
}

void server_Setting() {
  webText = "<!DOCTYPE HTML>\n<html lang='en'>\n<head><title>Setup</title>\n<meta name='viewport' ";
  webText += "content='width=device-width, initial-scale=1.0'>\n<style>\n* {\n  font-family: Arial, Helvetica";
  webText += ", sans-serif;\n  font-size: 45px;\n  font-weight: 600;\n  margin: 0;\n  text-align: center;\n}";
  webText += "\n\n@keyframes op_en_neer {\n  0%   {height:  0px;}\n  50%  {height:  40px;}\n  100% {height:  0px;}\n}";
  webText += "\n\n.opneer {\n  margin: auto;\n  text-align: center;\n  animation: op_en_neer 2s infinite;\n}";
  webText += "\n</style>\n</head>\n<body>\n<div class=\"opneer\"></div>\nESP will reboot<br>Close this window";
  webText += "\n</body>\n</html>";
  String myssid = server.arg("ssid");  // we want to store this in flash memory
  String mypasw = server.arg("pass");
  server.send(200, "\ntext/html", webText);
  delay(500);
  if (myssid.length() > 0 && mypasw.length() > 0) {
    flash.begin("login_data", false);
    flash.putString("ssid", myssid);
    flash.putString("pasw", mypasw);
    flash.end();
    ESP.restart();
  }
}


Page featuring more displays and sketches