These devices are able to display a useful graph. Not accurate enough for measurements but good to see trends. In my case I use it to display various data trends from the household solar installation: PV output, Household usage, Feed-in, Battery state and so on.
The attached code is very straightforward.
The process involves:
- Initialise the device, screen and Wifi
- Enable HTTPclient
- Using HTTP, connect to a remote server and retrieve the data in JSON format
- Deserialize the JSON and assign the values to variables
- Put on screen a graph background
- Draw graphlines using the data point variables

The Data
Any source is of course useful. My path was to utilise the mySQL database that has the household solar data.
Using PHP, the database can be queried and wanted results output as a JSON string. This is “agricultural” (!) but easy to implement and trace.
In this example the following JSON string is received by the ESP:
{"PV"[407,442,536,785,896,1672,1601,2267,1440,2388,1533,1103,3663,2813,2369,1991,3371,2320,2044,1715,2743,2410,1756,2303,587,685]}
This leaves many options. I used a Raspberry Pi running PHP for the trial.
// Updated!!!
//Snippet 1 After SQL query make an array out of the data
// This is not a complete example.
<?php
/// attach to dB
/// query for your results
$db1 = new mysqli($hostname,$username,$password,$database);
if ($db1->connect_errno > 0) {
die('Unable to connect to database [' . $db1->connect_error . ']');
}
$sql1="SELECT * FROM `solarinverter` WHERE (minute = 0 OR minute=15 OR minute=30 OR minute = 45) AND id mod 2=0 ORDER by id DESC LIMIT 26"; // about SIX HOURS
if(!$result1 = $db1->query($sql1)){
die('There was an error running the first query [' . $db1->error . ']');
}
$i=26; // Go backwards because the sort... 26 values pv[26]newest to [1]
while($row = $result1->fetch_assoc()){
$pv[$i] = $row['total_pv_power'];
$i--;
}
$i =2;
$pvarray = $pv[1];
while ($i<27) {
$pvarray = $pvarray .",". ($pv[$i]);
$i++;
}
// echo $pvarray;
$jsonstring = "{\"PV\":[" . $pvarray ."]}";
echo $jsonstring;
?>
Note that the example gets 26 data points from the dB taken at 15 minute intervals.
The code
Nothing special here, I prefer wifimanager.h to get wifi going.
In drawing the graphs, I have assumed a maximum y value of 6000. Obviously if you have different scales, you can adjust the line “/44” to suit (yourmaxvalue /135) considering there are 135 pixels vertical. (Change the scale numbers too I guess… ๐ )
Note 1: this example uses the TFT_eSPI.h graphics routines. I believe that this is a superior library, but an alternative using the Adafruit_ST_7789.h libraries is also documented in the code. Uncomment or delete as required.
Note 2: the TFT_eSPI modified code can be implemented on the LILYGO S2, the TTGO -T-Display and T-Watch 2020 boards. Simply change the ‘#definitions’ or User_Setup.h (or Select) to reflect the correct display pin numbers. Further advice is in my article Differing ESP32 Display boards.

/*
Demonstration only...
Graphing Data with ESP32-S2 T8 or T-Display (or T-Watch 2020) with ST7789 screen
Prototype gets data by calling a pHp file on some server.
The pHp file queries MySQL dB and returns a JSON string like this:
{"PV":[407,442,536,785,896,1672,1601,2267,1440,2388,1533,1103,3663,2813,2369,1991,3371,2320,2044,1715,2743,2410,1756,2303,587,685]}
Note that the example graph is designed to deal with values 0-6000 adjust line 157 as necessary.
Refer to https://www.lecity.edu.au
This sample code shows how either the TFT_eSPI library or Adafruit graphics libraries can be used with these hardware types. Delete / uncomment as required. Also, change the color settings as needed by whichever graphics library : Adafruit drivers expect ST77XX_GREEN instead of TFT_GREEN
*/
#include <TFT_eSPI.h> // TFT_eSPI variant
//#include <Adafruit_GFX.h> // Adafruit variant
//#include <Adafruit_ST7789.h> // Adafruit variant
//#define TFT_CS 34 // Adafruit variant
//#define TFT_RST 38 // Adafruit variant
//#define TFT_DC 37 // Adafruit variant
//#define TFT_MOSI 35 // Adafruit variant
//#define TFT_SCLK 36 // Adafruit variant
#include <SPI.h>
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager
#include <HTTPClient.h>
#include <ArduinoJson.h>
String PVData, swVersion = "v.0.3";
String httpServer("https://yourserver/yourfile.php"); //See notes
int pv[26]; //Array of variables that will be assigned values after http to the server
int xcoord, ycoord; // Graph moving coordinate
//Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST); // Adafruit variant
TFT_eSPI tft = TFT_eSPI(); // TFT_eSPI variant
WiFiClient espclient;
void setup() {
// pinMode(33, OUTPUT); // Adafruit variant
// digitalWrite(33, HIGH); // Adafruit variant// Turn on Backlight LED
// tft.init(135, 240); // Adafruit variant
// tft.setRotation(3); // Adafruit variant
tft.begin(); // TFT_eSPI variant
tft.setRotation(1); // TFT_eSPI variant
WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP
Serial.begin(115200);
Serial.println("-----------------------");
WiFiManager wm;
// wm.resetSettings(); // reset settings - wipe credentials for testing
bool res;
res = wm.autoConnect("AutoConnectAP"); // anonymous ap // res = wm.autoConnect(); // auto generated AP name from chipid
// res = wm.autoConnect("AutoConnectAP","password"); // password protected ap
if (!res) {
Serial.println("Failed to connect");
// ESP.restart();
}
else {
Serial.println("Connected!");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
tft.fillScreen(TFT_BLACK);
tft.setTextWrap(true);
tft.setTextSize(3);
tft.setCursor(0, 0);
tft.setTextColor(TFT_YELLOW);
tft.print("ESP32 S2 ...");
tft.setCursor(0, 30);
tft.setTextColor(TFT_GREEN);
tft.setTextSize(2);
tft.print(swVersion);
tft.setCursor(0, 60);
tft.setTextColor(TFT_WHITE);
tft.print(WiFi.localIP());
}
tft.setCursor(20, 90);
tft.setTextColor(TFT_WHITE);
tft.print("Loading ...");
tft.setCursor(20, 110);
tft.setTextColor(TFT_WHITE);
tft.print("Getting data...");
getHTTP(httpServer); // Initial display: Get data payload from server SQL URL
drawPVGraph("PV output");
}
void loop() {
// Put stuff in the loop to get updated data from time to time / draw different graphs etc....
}
void getHTTP(String url) {
HTTPClient http;
http.begin(url);
int httpCode = http.GET();
if (httpCode > 0) {
Serial.printf("[HTTP] GET... code: %d\n", httpCode); // HTTP header sent
if (httpCode == 200) { //// file found at server
Serial.println("[HTTP] JSON data received.");
PVData = http.getString();
Serial.println(PVData);
StaticJsonDocument<2048> doc;
DeserializationError error = deserializeJson(doc, PVData);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.c_str());
tft.fillCircle(232, 8, 8, TFT_RED);
delay(1500);
return;
}
/// Setup variables////
for (int y = 0; y < 26; y++) { // Make the 3 * 26 variables
pv[y + 1] = doc["PV"][y];
}
tft.fillCircle(232, 8, 8, TFT_GREEN);
} else {
Serial.println("File not found.");
}
} else {
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
}
void drawPVGraph(String graphLabel) {
//Draw graph lines
tft.fillScreen(TFT_BLACK);
tft.setTextSize(2);
tft.setCursor(26, 0);
tft.setTextColor(TFT_GREEN);
tft.print(graphLabel);
xcoord = ycoord = 0;
for (k = 1; k < 17; k++) { //Draw Grid
for (n = 1; n < 10; n++) { //Grid
tft.drawPixel(xcoord, ycoord, TFT_YELLOW);
ycoord = ycoord + 15;
}
ycoord = 0;
xcoord = xcoord + 15;
}
////// PV graph
tft.drawLine(0, 0, 0, 135, TFT_WHITE);
tft.drawLine(0, 135, 240, 135, TFT_WHITE);
tft.setTextColor(TFT_WHITE);
tft.setCursor(5, 0);
tft.print("6");
tft.setCursor(5, 60);
tft.print("3");
tft.setCursor(5, 120);
tft.print("0");
xcoord = 4;
for (k = 1; k < 26; k++) {
if (pv[k] > 6000) {
pv[k] = 6000;
}
tft.drawLine(xcoord, 135 - pv[k] / 44, xcoord + 9, 135 - pv[k + 1] / 44, TFT_YELLOW); // div by 44 since max 6000/44 suitable for 135 pixels vertical
xcoord = xcoord + 9;
}
}
…more to follow…

Put it in a box
Here is a 3D printed box suitable for the LILYGO S2 T8 board and an 18650 battery. I printed up a couple of triangular struts to hold it up at an angle…

It’s just a box https://lecity.edu.au/images/T8Box6.stl.zip

A very good box for the T-Display has been designed by ‘vmensk’ at Thingiverse.
See https://www.thingiverse.com/thing:3777859.
