Continuous Graph Plotting pada TFT Module Display 240x320 ILI9341 dan ESP32

Graph plot ILI9341 ESP32
TFT Module Display 240x320 ILI9341 dan ESP32

Hello, balik lagi untuk test perangkat display. Kali ini kita akan test perangkat TFT Module Display 240x320 ILI9341 dengan mikrokontrollernya menggunakan ESP32. Pada umumnya TFT Module Display ILI9341 memiliki banyak varian berdasarkan ukuran dan touch screen. Nah yang dipakai kali ini adalah yang ukuran 240x320 dan tanpa touch screen ya. Case yang sedang kita diskusikan kali ini adalah cara menampilkan/membuat grafik pada display tersebut. 

Continuous Graph Plotting ILI9341

Pada dasarnya grafik dibuat dengan merepresentasikan titik koordinat yang saling terhubung. Nah pada TFT modul  ILI9341 fungsi koordinat merupakan dasar untuk menampilkan gambar atau karakter. Pada point ini dapat disimpulkan bahwa sangat memungkinkan untuk menampilkan grafik di modul ini. Ok langsung cus aja.

Schematic

Continuous Graph Plotting ILI9341 ESP32

TFT_CLK    >>  D18
TFT_MOSI  >>  D23
TFT_MISO  >>  D19
TFT_CS       >>  D16/RX2
TFT_RST     >>  D4
TFT_DC       >>  D5

Library

  • Adafruit_GFX
  • Adafruit_ILI9341
  • DHT Sensor Library by Adafruit

How to plot a graph

Setelah berkelana keliling google dan youtube, akhirnya ketemu juga deh tutorial yang membagikan trik untuk plotting grafik ke display dengan base library adafruit gfx. Thanks to Mr. Kris Kasprzak for the tutorial and graph function. Nah selanjutnya kita dapat mengacu pada tutorial yang sudah dibuat oleh Mr. Kris Kasprzak pada video berikut. Yang berbeda dari tutorial tersebut adalah jenis display yang digunakan yaitu HX8357.


Nah sekarang mari kita sesuaikan dengan display ILI9341. Yang hasilnya kurang lebih seperi ini:
#include <WiFi.h>
#include <HTTPClient.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>

#define TFT_CS     16
#define TFT_DC     5
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

//#define TFT_CLK    18
//#define TFT_MOSI   23
//#define TFT_MISO   19
//#define TFT_CS     16
//#define TFT_RST    4
//#define TFT_DC     5

//Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO);


#define LTBLUE    0xB6DF
#define LTTEAL    0xBF5F
#define LTGREEN   0xBFF7
#define LTCYAN    0xC7FF
#define LTRED     0xFD34
#define LTMAGENTA 0xFD5F
#define LTYELLOW  0xFFF8
#define LTORANGE  0xFE73
#define LTPINK    0xFDDF
#define LTPURPLE  0xCCFF
#define LTGREY    0xE71C

#define BLUE      0x001F
#define TEAL      0x0438
#define GREEN     0x07E0
#define CYAN      0x07FF
#define RED       0xF800
#define MAGENTA   0xF81F
#define YELLOW    0xFFE0
#define ORANGE    0xFC00
#define PINK      0xF81F
#define PURPLE    0x8010
#define GREY      0xC618
#define WHITE     0xFFFF
#define BLACK     0x0000

#define DKBLUE    0x000D
#define DKTEAL    0x020C
#define DKGREEN   0x03E0
#define DKCYAN    0x03EF
#define DKRED     0x6000
#define DKMAGENTA 0x8008
#define DKYELLOW  0x8400
#define DKORANGE  0x8200
#define DKPINK    0x9009
#define DKPURPLE  0x4010
#define DKGREY    0x4A49


#define ADJ_PIN A0

double a1, b1, c1, d1, r2, r1, vo, tempC, tempF, tempK;

//Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC);

// this is the only external variable used by the graph
// it's a flat to draw the coordinate system only on the first pass
boolean display1 = true;
boolean display2 = true;
boolean display3 = true;
boolean display4 = true;
boolean display5 = true;
boolean display6 = true;
boolean display7 = true;
boolean display8 = true;
boolean display9 = true;
double ox , oy ;

void setup() {

  Serial.begin(9600);

  pinMode(34, INPUT);
  tft.begin();
  tft.fillScreen(BLACK);

  tft.setRotation(2);
  a1 = 3.354016E-03 ;
  b1 = 2.569850E-04 ;
  c1 = 2.620131E-06 ;
  d1 = 6.383091E-08 ;


  double x, y;


  tft.setRotation(1);


  for (x = 0; x <= 6.3; x += .1) {

    y = sin(x);
    Graph(tft, x, y, 50, 210, 260, 180, 0, 6.5, 1, -1, 1, .25, "Sin Function", "x", "sin(x)", DKBLUE, RED, YELLOW, WHITE, BLACK, display1);

  }

  delay(2000);

  tft.fillScreen(BLACK);
  for (x = 0; x <= 6.3; x += .1) {

    y = sin(x);
    Graph(tft, x, y, 50, 210, 100, 180, 0, 6.5, 3.25, -1, 1, .25, "Sin Function", "x", "sin(x)", GREY, GREEN, RED, YELLOW, BLACK, display9);

  }

    delay(1000);

  tft.fillScreen(BLACK);
  for (x = 0; x <= 25.2; x += .1) {

    y = sin(x);
    Graph(tft, x, y, 50, 210, 240, 60, 0, 25, 5, -1, 1, .5, "Sin Function", "x", "sin(x)", DKYELLOW, YELLOW, GREEN, WHITE, BLACK, display8);

  }

  delay(1000);

  tft.fillScreen(BLACK);
  for (x = 0.001; x <= 10; x += .1) {

    y = log(x);
    Graph(tft, x, y, 50, 210, 230, 180, 0, 10, 1, -10, 5, 1, "Natural Log Function", "x", "ln(x)", BLUE, RED, WHITE, WHITE, BLACK, display2);

  }

  

  delay(1000);
  tft.fillScreen(BLACK);

  for (x = 0; x <= 10; x += 1) {

    y = x * x;
    Graph(tft, x, y, 50, 210, 220, 180, 0, 10, 1, 0, 100, 10, "Square Function", "x", "x^2", DKRED, RED, YELLOW, WHITE, BLACK, display3);

  }

  delay(1000);
  tft.fillScreen(BLACK);

  for (x = 0.00; x <= 20; x += .01) {

    y = ((sin(x)) * x + cos(x)) - log(x);
    Graph(tft, x, y, 50, 210, 220, 180, 0, 20, 1, -20, 20, 5, "Weird Function", "x", " y = sin(x) + cos(x) - log(x)", ORANGE, YELLOW, CYAN, WHITE, BLACK, display4);

  }

  delay(1000);
  tft.fillScreen(BLACK);
  tft.setRotation(2);
  for (x = 0; x <= 12.6; x += .1) {

    y = sin(x);
    Graph(tft, x, y, 50, 210, 150, 150, 0, 13, 3.5, -1, 1, 1, "Sin(x)", "x", "sin(x)", DKBLUE, RED, YELLOW, WHITE, BLACK, display5);

  }
  tft.setRotation(1);
  delay(1000);
  tft.fillScreen(WHITE);

  for (x = 0; x <= 6.3; x += .05) {

    y = cos(x);
    Graph(tft, x, y, 50, 210, 230, 180, 0, 6.5, 3.25, -1, 1, 1, "Cos Function", "x", "cos(x)", DKGREY, GREEN, BLUE, BLACK, WHITE, display6);

  }

  delay(1000);
  tft.fillScreen(BLACK);


  for (x = 0; x <= 60; x += 1) {
    vo = analogRead(ADJ_PIN) / 204.6;
    r1 = 9940;
    r2 = ( vo * r1) / (5 - vo);

    //equation from data sheet
    tempK = 1.0 / (a1 + (b1 * (log(r2 / 10000.0))) + (c1 * pow(log(r2 / 10000.0), 2)) + (d1 * pow(log(r2 / 10000.0), 3)));
    tempC  = ((tempK - 273.15) );
    y = tempF  = (tempC * 1.8000) + 32.00;

    Graph(tft, x, y, 50, 210, 230, 180, 0, 60, 10, 70, 90, 5, "Room Temperature", " Time [s]", "Temperature [deg F]", DKBLUE, RED, GREEN, WHITE, BLACK, display7);
    delay(250);
  }

 delay(1000);
  tft.fillScreen(BLACK);

}


void loop(void) {

}

/*

  function to draw a cartesian coordinate system and plot whatever data you want
  just pass x and y and the graph will be drawn

  huge arguement list
  &d name of your display object
  x = x data point
  y = y datapont
  gx = x graph location (lower left)
  gy = y graph location (lower left)
  w = width of graph
  h = height of graph
  xlo = lower bound of x axis
  xhi = upper bound of x asis
  xinc = division of x axis (distance not count)
  ylo = lower bound of y axis
  yhi = upper bound of y asis
  yinc = division of y axis (distance not count)
  title = title of graph
  xlabel = x asis label
  ylabel = y asis label
  gcolor = graph line colors
  acolor = axi ine colors
  pcolor = color of your plotted data
  tcolor = text color
  bcolor = background color
  &redraw = flag to redraw graph on fist call only
*/


void Graph(Adafruit_ILI9341 &d, double x, double y, double gx, double gy, double w, double h, double xlo, double xhi, double xinc, double ylo, double yhi, double yinc, String title, String xlabel, String ylabel, unsigned int gcolor, unsigned int acolor, unsigned int pcolor, unsigned int tcolor, unsigned int bcolor, boolean &redraw) {

  double ydiv, xdiv;
  // initialize old x and old y in order to draw the first point of the graph
  // but save the transformed value
  // note my transform funcition is the same as the map function, except the map uses long and we need doubles
  //static double ox = (x - xlo) * ( w) / (xhi - xlo) + gx;
  //static double oy = (y - ylo) * (gy - h - gy) / (yhi - ylo) + gy;
  double i;
  double temp;
  int rot, newrot;

  if (redraw == true) {

    redraw = false;
    ox = (x - xlo) * ( w) / (xhi - xlo) + gx;
    oy = (y - ylo) * (gy - h - gy) / (yhi - ylo) + gy;
    // draw y scale
    for ( i = ylo; i <= yhi; i += yinc) {
      // compute the transform
      temp =  (i - ylo) * (gy - h - gy) / (yhi - ylo) + gy;

      if (i == 0) {
        d.drawLine(gx, temp, gx + w, temp, acolor);
      }
      else {
        d.drawLine(gx, temp, gx + w, temp, gcolor);
      }

      d.setTextSize(1);
      d.setTextColor(tcolor, bcolor);
      d.setCursor(gx - 40, temp);
      // precision is default Arduino--this could really use some format control
      d.println(i);
    }
    // draw x scale
    for (i = xlo; i <= xhi; i += xinc) {

      // compute the transform

      temp =  (i - xlo) * ( w) / (xhi - xlo) + gx;
      if (i == 0) {
        d.drawLine(temp, gy, temp, gy - h, acolor);
      }
      else {
        d.drawLine(temp, gy, temp, gy - h, gcolor);
      }

      d.setTextSize(1);
      d.setTextColor(tcolor, bcolor);
      d.setCursor(temp, gy + 10);
      // precision is default Arduino--this could really use some format control
      d.println(i);
    }

    //now draw the labels
    d.setTextSize(2);
    d.setTextColor(tcolor, bcolor);
    d.setCursor(gx , gy - h - 30);
    d.println(title);

    d.setTextSize(1);
    d.setTextColor(acolor, bcolor);
    d.setCursor(gx , gy + 20);
    d.println(xlabel);

    d.setTextSize(1);
    d.setTextColor(acolor, bcolor);
    d.setCursor(gx - 30, gy - h - 10);
    d.println(ylabel);


  }

  //graph drawn now plot the data
  // the entire plotting code are these few lines...
  // recall that ox and oy are initialized as static above
  x =  (x - xlo) * ( w) / (xhi - xlo) + gx;
  y =  (y - ylo) * (gy - h - gy) / (yhi - ylo) + gy;
  d.drawLine(ox, oy, x, y, pcolor);
  d.drawLine(ox, oy + 1, x, y + 1, pcolor);
  d.drawLine(ox, oy - 1, x, y - 1, pcolor);
  ox = x;
  oy = y;

}

/*
  End of graphing functioin
*/

teman-teman dapat melihat simulasinya via wokwi di link berikut: https://wokwi.com/projects/370273594170141697

Code Explained:

Untuk membuat grafik, maka dipanggillah fungsi Graph() seperti potongan kode berikut dengan mengacu pada arguement list yang sudah dituliskan

Graph(tft, x, y, 50, 210, 230, 180, 0, 60, 10, 70, 90, 5, "Room Temperature", " Time [s]", "Temperature [deg F]", DKBLUE, RED, GREEN, WHITE, BLACK, display7);

Continuous Graph Plotting ILI9341 ESP32
Ok, plot data grafik sudah dapat dilakukan ya.. nah selanjutnya adalah bagaimana biar bisa plot data secara continyu ala-ala serial plotter. Cus ke segment selanjutnya..

How to plot a continuous graph

Dalam proses plotting data secara kontinyu, hal yang diperhatikan adalah nilai X yang akan bertambah dalam periode waktu yang telah ditentukan. Nah kelemahan dari ESP32 dalam penggunaannya di project ini adalah clock speed komunikasi SPI nya sehingga saat melakukan refresh display terdapat jeda yang sangat terlihat. Kali ini kita kombinasikan dengan data suhu dari sensor DHT 22.
Continuous Graph Plotting ILI9341 ESP32

#include <SPI.h>
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"

#include "DHT.h"

#define DHTPIN 15
#define DHTTYPE DHT22 
DHT dht(DHTPIN, DHTTYPE);

#define TFT_CS     16
#define TFT_DC     5
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

//#define TFT_CLK    18
//#define TFT_MOSI   23
//#define TFT_MISO   19
//#define TFT_CS     16
//#define TFT_RST    4
//#define TFT_DC     5

//Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO);

#define LTBLUE    0xB6DF
#define LTTEAL    0xBF5F
#define LTGREEN   0xBFF7
#define LTCYAN    0xC7FF
#define LTRED     0xFD34
#define LTMAGENTA 0xFD5F
#define LTYELLOW  0xFFF8
#define LTORANGE  0xFE73
#define LTPINK    0xFDDF
#define LTPURPLE  0xCCFF
#define LTGREY    0xE71C

#define BLUE      0x001F
#define TEAL      0x0438
#define GREEN     0x07E0
#define CYAN      0x07FF
#define RED       0xF800
#define MAGENTA   0xF81F
#define YELLOW    0xFFE0
#define ORANGE    0xFC00
#define PINK      0xF81F
#define PURPLE    0x8010
#define GREY      0xC618
#define WHITE     0xFFFF
#define BLACK     0x0000

#define DKBLUE    0x000D
#define DKTEAL    0x020C
#define DKGREEN   0x03E0
#define DKCYAN    0x03EF
#define DKRED     0x6000
#define DKMAGENTA 0x8008
#define DKYELLOW  0x8400
#define DKORANGE  0x8200
#define DKPINK    0x9009
#define DKPURPLE  0x4010
#define DKGREY    0x4A49

double a1, b1, c1, d1, r2, r1, tempC,humid ;

// this is the only external variable used by the graph
// it's a flat to draw the coordinate system only on the first pass
boolean display1 = true;
boolean display2 = true;
double ox , oy ;
double y;
int x, xmin, xmax;

void setup() {
  x = 0; xmin = 0; xmax=60;
  Serial.begin(115200);
  dht.begin();
  tft.begin();
  tft.fillScreen(BLACK);
  tft.setRotation(1);
  
  a1 = 3.354016E-03 ;
  b1 = 2.569850E-04 ;
  c1 = 2.620131E-06 ;
  d1 = 6.383091E-08 ;

  /*
  for (x = 0; x <= 240; x += 1) {
    tempC = dht.readTemperature();
    humid = dht.readHumidity();
    y = tempC;

    Graph(tft, x, y, 50, 210, 250, 180, 0, 60, 10, 0, 50, 10, "     Temperature", " Time [s]", "Temperature [deg c]", DKBLUE, RED, GREEN, WHITE, BLACK, display1);
    delay(250);
  }
  */
  delay(1000);
  //tft.fillScreen(BLACK);

}


void loop() {
  tempC = dht.readTemperature();
  //humid = dht.readHumidity();
  y = tempC;
  //y = 25;
  
  if (x < xmax ){
    x++;
  }else{
    xmin = xmax;
    xmax = xmax+60;
    display1 = true;
    tft.fillScreen(BLACK);
    delay(500);
  }

  if (x == 240){
    x = 0;
    xmin = 0;
    xmax = 60;
    display1 = true;
    tft.fillScreen(BLACK);
  }
  
  Graph(tft, x, y, 30, 210, 270, 180, xmin, xmax, 10, 0, 50, 10, "     Temperature", " Time [s]", "Temperature [deg c]", DKBLUE, RED, GREEN, WHITE, BLACK, display1); 
  Serial.print("Temp:");Serial.println(y);
  delay(1000);
  
}

/*

  function to draw a cartesian coordinate system and plot whatever data you want
  just pass x and y and the graph will be drawn

  huge arguement list
  &d name of your display object
  x = x data point
  y = y datapont
  gx = x graph location (lower left)
  gy = y graph location (lower left)
  w = width of graph
  h = height of graph
  xlo = lower bound of x axis
  xhi = upper bound of x asis
  xinc = division of x axis (distance not count)
  ylo = lower bound of y axis
  yhi = upper bound of y asis
  yinc = division of y axis (distance not count)
  title = title of graph
  xlabel = x asis label
  ylabel = y asis label
  gcolor = graph line colors
  acolor = axi ine colors
  pcolor = color of your plotted data
  tcolor = text color
  bcolor = background color
  &redraw = flag to redraw graph on fist call only
*/


void Graph(Adafruit_ILI9341 &d, double x, double y, double gx, double gy, double w, double h, double xlo, double xhi, double xinc, double ylo, double yhi, double yinc, String title, String xlabel, String ylabel, unsigned int gcolor, unsigned int acolor, unsigned int pcolor, unsigned int tcolor, unsigned int bcolor, boolean &redraw) {

  double ydiv, xdiv;
  // initialize old x and old y in order to draw the first point of the graph
  // but save the transformed value
  // note my transform funcition is the same as the map function, except the map uses long and we need doubles
  //static double ox = (x - xlo) * ( w) / (xhi - xlo) + gx;
  //static double oy = (y - ylo) * (gy - h - gy) / (yhi - ylo) + gy;
  double i;
  double temp;
  int rot, newrot;

  if (redraw == true) {

    redraw = false;
    ox = (x - xlo) * ( w) / (xhi - xlo) + gx;
    oy = (y - ylo) * (gy - h - gy) / (yhi - ylo) + gy;
    // draw y scale
    for ( i = ylo; i <= yhi; i += yinc) {
      // compute the transform
      temp =  (i - ylo) * (gy - h - gy) / (yhi - ylo) + gy;

      if (i == 0) {
        d.drawLine(gx, temp, gx + w, temp, acolor);
      }
      else {
        d.drawLine(gx, temp, gx + w, temp, gcolor);
      }

      d.setTextSize(1);
      d.setTextColor(tcolor, bcolor);
      d.setCursor(gx - 20, temp);
      // precision is default Arduino--this could really use some format control
      int ay = i;
      //d.println(i);
      d.println(ay);
    }
    // draw x scale
    for (i = xlo; i <= xhi; i += xinc) {

      // compute the transform

      temp =  (i - xlo) * ( w) / (xhi - xlo) + gx;
      if (i == 0) {
        d.drawLine(temp, gy, temp, gy - h, acolor);
      }
      else {
        d.drawLine(temp, gy, temp, gy - h, gcolor);
      }

      d.setTextSize(1);
      d.setTextColor(tcolor, bcolor);
      d.setCursor(temp - 5, gy + 10);
      // precision is default Arduino--this could really use some format control
      int ax= i;
      d.println(ax);
    }

    //now draw the labels
    d.setTextSize(2);
    d.setTextColor(tcolor, bcolor);
    d.setCursor(gx , gy - h - 30);
    d.println(title);

    d.setTextSize(1);
    d.setTextColor(acolor, bcolor);
    d.setCursor(gx , gy + 20);
    d.println(xlabel);

    d.setTextSize(1);
    d.setTextColor(acolor, bcolor);
    d.setCursor(gx - 30, gy - h - 10);
    d.println(ylabel);


  }

  //graph drawn now plot the data
  // the entire plotting code are these few lines...
  // recall that ox and oy are initialized as static above
  x =  (x - xlo) * ( w) / (xhi - xlo) + gx;
  y =  (y - ylo) * (gy - h - gy) / (yhi - ylo) + gy;
  d.drawLine(ox, oy, x, y, pcolor);
  d.drawLine(ox, oy + 1, x, y + 1, pcolor);
  d.drawLine(ox, oy - 1, x, y - 1, pcolor);
  ox = x;
  oy = y;

}

/*
  End of graphing functioin
*/

Kode diatas dapat disimulasikan via project berikut ya: https://wokwi.com/projects/371363417738730497

Nah kunci dari ploting grafik secara kontinyu ada pada fungsi loop pada program. Dalam program loop dapat dilihat saat nilai X berubah baik X min atau X max sebagai handling dari pergeseran grafik. Saat nilai X sudah menapai beberapa digit, maka diperlukan refresh kembali ke titik 0 agar label X tidak terlalu penuh.

Ok itu dulu untuk tips Continuous Graph Plotting pada TFT Module Display 240x320 ILI9341. Semoga bermanfaat.