Connecting TFT LCD Touch Screen with Nodemcu/ ESP8266

Many time we need a real time device to monitor and operate remotely. Mobile or laptop may not be suitable for that purpose because we don’t need much interactions and the device need to work 24*7.  We can create such a device by connecting a touch screen with nodemcu. So lets see how can we connect the two and code it for interaction.

Things we need :

  1.  TFT LCD Display Touch Panel SPI (ILI9341)
  2. Nodemcu
  3. 20 jumper wires
  4. Breadboard
  5. Stylus

Connections

Below is the connection between Touch screen and Nodemcu :

  • VCC  to 3.3V
  • GND to GND
  • CS to D8
  • RESET to RST
  • DC to D4
  • SDI (MOSI) to D7
  • SCK to D5
  • LED to 3.3V
  • SDD (MISO) to D6
  • T_CLK to D5
  • T_CS to D2
  • T_DIN to D7
  • T_DO to D6
  • T_IRQ to D1

(Note for the pins that have same connections we will have to use breadboard)

Next, we need to install libraries. Adafruit_GFX and SPI library can be installed from library Manager. Other library that we need you can download here.

Next, we need to find out the calibration parameter of the touch screen. Calibration is matching the graphics on the display to the output from the touch-screen controller. In other words, converting the display coordinates to touch coordinates.  Use the code below to find the calibration parameter :

#include <Arduino.h>
#include <SPI.h>

#include <Adafruit_ILI9341esp.h>
#include <Adafruit_GFX.h>
#include <XPT2046.h>

// For the Esp connection of touch
#define TFT_DC 2
#define TFT_CS 15

Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
XPT2046 touch(/*cs=*/ 4, /*irq=*/ 5);

static void calibratePoint(uint16_t x, uint16_t y, uint16_t &vi, uint16_t &vj) {
  // Draw cross
  
  
  tft.drawFastHLine(x - 8, y, 16,0xff);
  tft.drawFastVLine(x, y - 8, 16,0xff);
  while (!touch.isTouching()) {
    delay(10);
  }
  touch.getRaw(vi, vj);
  // Erase by overwriting with black
  tft.drawFastHLine(x - 8, y, 16, 0);
  tft.drawFastVLine(x, y - 8, 16, 0);
}

void calibrate() {
  uint16_t x1, y1, x2, y2;
  uint16_t vi1, vj1, vi2, vj2;
  touch.getCalibrationPoints(x1, y1, x2, y2);
  calibratePoint(x1, y1, vi1, vj1);
  delay(1000);
  calibratePoint(x2, y2, vi2, vj2);
  touch.setCalibration(vi1, vj1, vi2, vj2);
  
  
  tft.setTextColor(ILI9341_CYAN);
  tft.setTextSize(2);
  tft.println("Calibration Params");
  tft.println("");
  tft.setTextSize(3);
  tft.println(vi1);
  tft.println(vj1);
  tft.println(vi2);
  tft.println(vj2);
}

void setup() {
  delay(1000);  
  SPI.setFrequency(ESP_SPI_FREQ);
  tft.begin();
  touch.begin(tft.width(), tft.height());  // Must be done before setting rotation
  tft.fillScreen(ILI9341_BLACK);  
  calibrate();  // No rotation!!
}

void loop() {
  // Do nothing
  delay(1000);

After uploading the code touch the cross sign with stylus. Repeat it again with another cross sign you see after that. Now take note down of calibration parameter.

Finally we will upload the code below that creates a simple interface of phone calling UI :

#include <Arduino.h>
#include <SPI.h>

#include "Adafruit_ILI9341esp.h"
#include "Adafruit_GFX.h"
#include "XPT2046.h"

#define TFT_DC 2
#define TFT_CS 15

/******************* UI details */
#define BUTTON_X 40
#define BUTTON_Y 100
#define BUTTON_W 60
#define BUTTON_H 30
#define BUTTON_SPACING_X 20
#define BUTTON_SPACING_Y 20
#define BUTTON_TEXTSIZE 2

// text box where numbers go
#define TEXT_X 10
#define TEXT_Y 10
#define TEXT_W 220
#define TEXT_H 50
#define TEXT_TSIZE 3
#define TEXT_TCOLOR ILI9341_MAGENTA

// the data (phone #) we store in the textfield
#define TEXT_LEN 12
char textfield[TEXT_LEN+1] = "";
uint8_t textfield_i=0;

Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
XPT2046 touch(/*cs=*/ 4, /*irq=*/ 5);

/* create 15 buttons, in classic candybar phone style */
char buttonlabels[15][5] = {"Send", "Clr", "End", "1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "0", "#" };
uint16_t buttoncolors[15] = {ILI9341_DARKGREEN, ILI9341_DARKGREY, ILI9341_RED, 
                             ILI9341_BLUE, ILI9341_BLUE, ILI9341_BLUE, 
                             ILI9341_BLUE, ILI9341_BLUE, ILI9341_BLUE, 
                             ILI9341_BLUE, ILI9341_BLUE, ILI9341_BLUE, 
                             ILI9341_ORANGE, ILI9341_BLUE, ILI9341_ORANGE};
Adafruit_GFX_Button buttons[15];

void setup() {
  // put your setup code here, to run once:
  delay(1000);
  
  Serial.begin(115200);
  SPI.setFrequency(ESP_SPI_FREQ);

  tft.begin();
  touch.begin(tft.width(), tft.height());  // Must be done before setting rotation
  Serial.print("tftx ="); Serial.print(tft.width()); Serial.print(" tfty ="); Serial.println(tft.height());
  tft.fillScreen(ILI9341_BLACK);
  
  // Replace these for your screen module
  touch.setCalibration(1832, 262, 264, 1782);

  // create buttons
  for (uint8_t row=0; row<5; row++) {
    for (uint8_t col=0; col<3; col++) {
      buttons[col + row*3].initButton(&tft, BUTTON_X+col*(BUTTON_W+BUTTON_SPACING_X), 
                 BUTTON_Y+row*(BUTTON_H+BUTTON_SPACING_Y),    // x, y, w, h, outline, fill, text
                  BUTTON_W, BUTTON_H, ILI9341_WHITE, buttoncolors[col+row*3], ILI9341_WHITE,
                  buttonlabels[col + row*3], BUTTON_TEXTSIZE); 
      buttons[col + row*3].drawButton();
    }
  }

  // create 'text field'
  tft.drawRect(TEXT_X, TEXT_Y, TEXT_W, TEXT_H, ILI9341_WHITE);

}

void loop() {
  // put your main code here, to run repeatedly:
  uint16_t x, y;
  if (touch.isTouching()) 
    touch.getPosition(x, y);

  // go thru all the buttons, checking if they were pressed
  for (uint8_t b=0; b<15; b++) {
    if (buttons[b].contains(x, y)) {
      //Serial.print("Pressing: "); Serial.println(b);
      buttons[b].press(true);  // tell the button it is pressed
    } else {
      buttons[b].press(false);  // tell the button it is NOT pressed
    }
  }

    // now we can ask the buttons if their state has changed
  for (uint8_t b=0; b<15; b++) {
    if (buttons[b].justReleased()) {
      // Serial.print("Released: "); Serial.println(b);
      buttons[b].drawButton();  // draw normal
    }
    
    if (buttons[b].justPressed()) {
        buttons[b].drawButton(true);  // draw invert!
        
        // if a numberpad button, append the relevant # to the textfield
        if (b >= 3) {
          if (textfield_i < TEXT_LEN) {
            textfield[textfield_i] = buttonlabels[b][0];
            textfield_i++;
      textfield[textfield_i] = 0; // zero terminate
            
            //fona.playDTMF(buttonlabels[b][0]);
          }
        }

        // clr button! delete char
        if (b == 1) {
          
          textfield[textfield_i] = 0;
          if (textfield > 0) {
            textfield_i--;
            textfield[textfield_i] = ' ';
          }
        }

        // update the current text field
        Serial.println(textfield);
        tft.setCursor(TEXT_X + 2, TEXT_Y+10);
        tft.setTextColor(TEXT_TCOLOR, ILI9341_BLACK);
        tft.setTextSize(TEXT_TSIZE);
        tft.print(textfield);

        // its always OK to just hang up
        if (b == 2) {
          //status(F("Hanging up"));
          //fona.hangUp();
        }
        // we dont really check that the text field makes sense
        // just try to call
        if (b == 0) {
          //status(F("Calling"));
          //Serial.print("Calling "); Serial.print(textfield);
          
          //fona.callPhone(textfield);
        }
        
      delay(100); // UI debouncing
    }
  }

}