In this tutorial you will learn how to control a 2.8 inch 240×320 TFT ILI9341 Touch Display with an WEMOS Lolin32 lite (ESP32) using the TFT_eSPI library.
The instructions and code will work with some minor changes for other ESP32’s and TFT’s as well, as long as the display uses the ILI9341 display driver and the XPT2046 touch controller.
Required Parts
You will need an ESP32 and an 2.8 inch TFT Touch Display with a resolution of 240×320 pixels and an ILI9341 display driver IC. Some cables and a breadboard might come in handy as well.
2.8 inch TFT ILI9341 Touch Display
ESP32 lite
USB Data Cable
Dupont Wire Set
Breadboard
Makerguides.com is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program designed to provide a means for sites to earn advertising fees by advertising and linking to products on Amazon.com. As an Amazon Associate we earn from qualifying purchases.
2.8″ TFT ILI9341 Touch Display Module
There are a few variations and clones of this 2.8 inch Display Module but they tend to be very similar and all/most of them should work. The TFT display has a resolution of 240×320 pixels with 65K RGB colors and is controlled via SPI using the ILI9341 display driver chip.
In addition, the display has a resistive touchscreen attached with a XPT2046 touch controller IC. Furthermore, there is also a MicroSD card socket at the back, which you could use to store images, for instance. The picture below shows the front and back of the display module.
The display module has has a built in voltage regulator and you can power it with 3.3V … 5V on VCC. However, there is no logic level shifter, which means you cannot connect directly connect an Arduino Uno that operates with 5V logic to the SPI interface of the module that operates with 3.3V!
Connect TFT ILI9341 Display Module to Arduino
If you want to use the display with an Arduino you will need a level shifter. You can use voltage dividers or a proper level shifter module. As a hack Techtonics suggests to add 10k resistors on the SPI lines:
This will probably work (I did not try this) but using a proper level shifter module would be a more reliable and safer solution. Alternatively, simply employ a microcontroller that operates on 3.3V logic, such as the ESP32 that we are going to use here.
Run TFT ILI9341 Touch Display Module on 3.3V
If you know that you will run the Display Module on 3.3V (VCC=3.3V), you can bypass the voltage regulator (U1) by closing (soldering) the jumper pads labeled J1 at the back of the module. See the picture below:
This potentially will make the display to run more stably since you avoid the voltage drop of the regulator and it also will reduce power consumption by a tiny bit. See the schematics of the voltage regulator below and how the J1 jumper affects the connection:
I did not close J1 (left it open as it is) and the display was working fine but should you have stability issues, you can try closing J1.
However, once you soldered (closed) J1 you cannot provide 5V to VCC anymore! You must use 3.3V on VCC!
Pinout of TFT ILI9341 Touch Display Module
The picture below shows the back of the Display Module with the connector pins. You can see two groups: the pins for the touch controller and the SPI pins for the display below:
The following table taken from lcdwiki lists the individual pins and their functions:
Number | Pin Label | Description |
---|---|---|
1 | VCC | 5V/3.3V power input |
2 | GND | Ground |
3 | CS | LCD chip select signal, low level enable |
4 | RESET | LCD reset signal, low level reset |
5 | DC/RS | LCD register / data selection signal,high level: register, low level: data |
6 | SDI(MOSI) | SPI bus write data signal |
7 | SCK | SPI bus clock signal |
8 | LED | Backlight control, if not controlled, connect 3.3V |
9 | SDO(MISO) | SPI bus read data signal, if you do not need to the read function, don’t connect it |
10 | T_CLK | Touch SPI bus clock signal |
11 | T_CS | Touch screen chip select signal, low level enable |
12 | T_DIN | Touch SPI bus input |
13 | T_DO | Touch SPI bus output |
14 | T_IRQ | Touch screen interrupt signal, low level when touch is detected |
Connecting the TFT ILI9341 Touch Display to ESP32
The following picture shows how to connect the Touch Display module to an WEMOS Lolin32 lite (ESP32):
There are quite a few connections to make and the following table should help. Note that the SDI(MOSI)
and SCK
lines of the SPI interface are shared between the touch and the TFT display controller:
ESP32 | TFT | Touch |
---|---|---|
5 | CS | – |
4 | – | T_CS |
17 | RESET | – |
16 | DC | – |
23 | SDI(MOSI) | T_DIN |
18 | SCK | T_CLK |
19 | – | T_DO |
22 | LED | – |
You could connect pin 19 of the ESP32 to SDO(MISO)
as well but since we don’t read data from the TFT display controller we won’t need it and there has been reports that in some cases this causes issues. I did not connect pin 19 to SDO(MISO)
and the display was working fine.
Note that we also leave T_IRQ
of the display module unconnected. This pin signals if a touch on the display was detected and you could use it to wake up the ESP32 from deep-sleep, for instance. However, in this tutorial we don’t implement this functionality.
The wiring is quite complex and I connected two breadboards to place the ESP32 and the TFT display on it. The good news is, apart from ground (GND) all connections are on one side of the ESP32, which simplifies the wiring a bit. The picture below shows my setup:
Backlight Control for TFT ILI9341 Touch Display
Note that the backlight LED of the module is switched via a transistor and we therefore can control it directly via a GPIO, in our case pin 22 of the ESP32 is used. See the schematic of the LED control circuit below:
Touch Controller for TFT ILI9341 Touch Display
As mentioned before the touch controller for the display module is a XPT2046. The schematic below shows how the controller is connected within the display module.
SD Card Socket for TFT ILI9341 Touch Display
Finally, the display module has an SD Card Socket. The picture below shows the schematics for this SD Card Socket. Internally it is only connected to VCC and GND.
The SPI interface (SD_CS
, SD_MOSI
, SD_CLK
) is accessible via external pins on the back of the module. But note that SD_MISO
is not connected:
However, we will not use the SD Card Socket in this tutorial.
Code for TFT ILI9341 Touch Display with TFT_eSPI Library
In the this section, we are going to utilize the TFT_eSPI library to control the display and the touch interface. To install this library open the Library Manager, search for “TFT_eSPI” and press “INSTALL”. After a successful installation it should look like this:
Next we need to create the correct project folder structure. Open your Arduino IDE and create a project “tft_test
” and save it (Save As …). This will create a folder “tft_test
” with the file “tft_test.ino
” in it. In this folder create another file named “tft_setup.h
“. Your project folder should look like this
If you want to learn more about this setup and other options to configure a TFT display for the TFT_eSPI library have a look at the How to configure TFT_eSPI Library for TFT display tutorial.
tft_setup.h for TFT ILI9341 Touch Display
After the project folder with the two files is created, copy the following configuration code for the TFT display into the tft_setup.h
file:
// tft_setup.h // 2.8" TFT Touch Display // 240x 320, Driver: ILI9341 #define ILI9341_DRIVER //#define ILI9341_2_DRIVER #define TFT_WIDTH 240 #define TFT_HEIGHT 320 #define TFT_RGB_ORDER TFT_BGR // WEMOLS Lolin32 lite #define TFT_CS 5 #define TFT_RST 17 #define TFT_DC 16 #define TFT_MOSI 23 // SDA // HW MOSI #define TFT_SCLK 18 // SCL // HW SCLK #define TFT_MISO 19 // HW MISO #define TFT_BL 22 // LED back-light #define TFT_BACKLIGHT_ON HIGH #define TOUCH_CS 4 #define TOUCH_CLK TFT_SCLK #define TOUCH_DIN TFT_MOSI #define TOUCH_DO TFT_MISO #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_READ_FREQUENCY 20000000 #define SPI_TOUCH_FREQUENCY 2500000
The most important part of this configuration file is to select the correct display driver. In our case this is the ILI9341. Note that there is an alternative definition:
#define ILI9341_DRIVER //#define ILI9341_2_DRIVER
If you have issues with your display, try the ILI9341_2_DRIVER
instead of the ILI9341_DRIVER
.
Also important are the constants for the width and height of the display and the order of the color channels.
#define TFT_WIDTH 240 #define TFT_HEIGHT 320 #define TFT_RGB_ORDER TFT_BGR
If your content appears cut off, or the colors of the displayed content are wrong, make sure that you have the dimensions (TFT_WIDTH
, TFT_HEIGHT
) and the TFT_RGB_ORDER
correct. The TFT_RGB_ORDER
can be TFT_BGR
or TFT_RGB
.
Note, that there is also a definition to invert black and white (#define TFT_INVERSION_ON
), should you encounter this problem. For all the possible settings see the User_Setup.h
file.
Next we have the pin definitions. I am using a WEMOS Lolin32 lite (ESP32) and the pins for Hardware SPI are (MOSI=23, MSIO=19, SCK=18
):
#define TFT_CS 5 #define TFT_RST 17 #define TFT_DC 16 #define TFT_MOSI 23 // SDA // HW MOSI #define TFT_SCLK 18 // SCL // HW SCLK #define TFT_MISO 19 // HW MISO #define TFT_BL 22 // LED back-light #define TFT_BACKLIGHT_ON HIGH #define TOUCH_CS 4 #define TOUCH_CLK TFT_SCLK #define TOUCH_DIN TFT_MOSI #define TOUCH_DO TFT_MISO
Depending on your microcontroller these pins may differ. You should find and use the pins for Hardware SPI, since it allows a faster communication and therefore a faster display. The other pins you can select freely.
The constants for the fonts you usually don’t have to change. However, should you run out of memory, you can remove unused fonts from the list.
#define LOAD_GLCD ... #define SMOOTH_FONT
The constants for SPI_FREQUENCY
and SPI_TOUCH_FREQUENCY
are somewhat critical. If there are two high you will see distorted content and the touch recognition will be brittle. The values here worked for me, but for a different microcontroller or display you may have to lower them.
#define SPI_FREQUENCY 27000000 #define SPI_READ_FREQUENCY 20000000 #define SPI_TOUCH_FREQUENCY 2500000
Test code for TFT ILI9341 Touch Display
In this section I’ll show you some test code you can use to try out the display and the touch detection. Just copy the following code into the tft_test.ino
file:
// tft_test.ino #include "tft_setup.h" #include "TFT_eSPI.h" TFT_eSPI tft = TFT_eSPI(); uint16_t cal[5] = { 0, 0, 0, 0, 0 }; void calibrate_touch() { if (!cal[1]) { tft.fillScreen(TFT_BLACK); tft.calibrateTouch(cal, TFT_YELLOW, TFT_BLACK, 20); Serial.printf("cal[5] = {%d, %d, %d, %d, %d};\n", cal[0], cal[1], cal[2], cal[3], cal[4]); } } void setup(void) { Serial.begin(115200); tft.init(); tft.setRotation(1); calibrate_touch(); tft.setTouch(cal); tft.fillScreen(TFT_BLACK); tft.setTextFont(1); tft.setTextSize(2); tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextDatum(CC_DATUM); tft.drawString("Makerguides", TFT_HEIGHT / 2, TFT_WIDTH / 2); } void loop() { uint16_t x, y; if (tft.getTouch(&x, &y)) { Serial.printf("%d %d\n", x, y); tft.fillCircle(x, y, 2, TFT_YELLOW); } }
The code starts by including the required library and the configuration file.
#include "tft_setup.h" #include "TFT_eSPI.h"
Next we create the TFT display object and an array to store the calibration parameters for the touch screen:
TFT_eSPI tft = TFT_eSPI(); uint16_t cal[5] = { 0, 0, 0, 0, 0 };
calibrate_touch Function
Initially the calibration parameters are set to zero but we will fill them in later. The calibrate_touch()
function is used to retrieve these calibration parameters:
void calibrate_touch() { if (!cal[1]) { tft.fillScreen(TFT_BLACK); tft.calibrateTouch(cal, TFT_YELLOW, TFT_BLACK, 20); Serial.printf("cal[5] = {%d, %d, %d, %d, %d};\n", cal[0], cal[1], cal[2], cal[3], cal[4]); } }
If they are not yet set (!cal[1]
), the function clears the screen and then calls tft.calibrateTouch()
, which fills the cal
array. This function draws arrows (in yellow color with a black background and a size of 20 pixels) in the corners of the display and the user has to touch the corners to calibrate the display. More about that later. Once we have the cal
parameters, we print them to the Serial Monitor.
setup Function
In the setup()
function we initialize the Serial Monitor and the TFT screen, calibrate the touch screen if necessary and then print the text “Makerguides” on the display:
void setup(void) { Serial.begin(115200); tft.init(); tft.setRotation(1); calibrate_touch(); tft.setTouch(cal); tft.fillScreen(TFT_BLACK); ... tft.drawString("Makerguides", TFT_HEIGHT / 2, TFT_WIDTH / 2); }
loop Function
In the loop()
function we call getTouch to check if touch was detected. If so, we print the coordinates of the touch to the Serial Monitor and draw a small yellow circle at the touch point:
void loop() { uint16_t x, y; if (tft.getTouch(&x, &y)) { Serial.printf("%d %d\n", x, y); tft.fillCircle(x, y, 2, TFT_YELLOW); } }
Backlight Control
Finally, if you want to switch of the backlight LED of the display to reduce power consumption when the display is unused, you can set the TFT_BL
pin to LOW
:
pinMode(TFT_BL, OUTPUT); digitalWrite(TFT_BL, LOW); // Switch off Backlight
I don’t use this in this code but it works. This could be useful to conserve power when putting the ESP32 in deep-sleep and only wake up when a touch was detected.
In the next section, we will learn how to calibrate the touch screen.
Calibration of TFT ILI9341 Touch Display
If you upload and run the code the display will start in calibration mode. This is needed to calibrate the touch screen so that the tft.getTouch(&x, &y)
function returns the correct screen coordinates for a touch.
In calibration mode the display first shows a yellow arrow in the left upper corner. Use the pen and touch the display at the corner the arrow is pointing to. Note, if you want bigger/smaller arrows, or a different color or background you can change the parameters for the tft.calibrateTouch()
function:
tft.calibrateTouch(cal, TFT_YELLOW, TFT_BLACK, 20);
If the touch was registered successfully, the display show an arrow in the right upper corner next. Touch this corner and repeat the process until all four corners have been touched. The picture below shows the four arrows during the four steps of the calibration process:
After the fourth touch the display will show the text “Makerguides in the center (without any arrows):
Most importantly the code will also print out the calibration parameters cal
on the Serial Monitor. You should see a print out similar to the following:
cal[5] = {397, 3495, 294, 3495, 7};
Copy these values and replace the zero calibration parameters for the cal
constant (cal[5] = { 0, 0, 0, 0, 0 }
) in the tft_test.ino
sketch by the values you read on the Serial Monitor:
// tft_test.ino ... uint16_t cal[5] = {397, 3495, 294, 3495, 7}; void calibrate_touch() { ...
Then compile and upload the code again.
Detecting touches with TFT ILI9341 Touch Display
When the cal
parameters are set, the code will skip the calibration step, directly print “Makerguides” and will be ready to detect touch inputs. Use your pen and a yellow dot should appear where ever you touch the display. Below a picture of the display with me fooling around with touch inputs:
If the yellow dots appear offset from the place where you touched the display, the calibration is incorrect. You can redo the calibration be setting the cal
parameters back to zero (cal[5] = { 0, 0, 0, 0, 0 }
).
Note that the Serial Monitor will print out the detected touch coordinates once the calibration is completed. You can use this to check the calibration or to add buttons that react on touch, for instance.
241 51 240 54 240 53 241 47 243 46 ...
See the Digital Clock with CrowPanel 3.5″ ESP32 Display tutorial for an example.
Conclusions
In this tutorial you learned how to control a 2.8 inch 240×320 TFT ILI9341 Touch Display with an WEMOS Lolin32 lite (ESP32) using the TFT_eSPI library.
If you have difficulties with TFT_eSPI the library, our How to configure TFT_eSPI Library for TFT display might help. There is also a lengthy discussion here, regarding issues with this specific display.
Should you have a different display with a ST7735 driver IC or want to learn how to use the Adafruit Library, the Interface TFT ST7735 Display with ESP32 tutorial might be useful for you.
And for round TFT displays have a look at the Digital Clock on CrowPanel 1.28″ Round Display tutorial.
If you have any comments, feel free to leave them in the comment section.
Happy Tinkering ; )
Stefan is a professional software developer and researcher. He has worked in robotics, bioinformatics, image/audio processing and education at Siemens, IBM and Google. He specializes in AI and machine learning and has a keen interest in DIY projects involving Arduino and 3D printing.
Maf70
Sunday 23rd of February 2025
Many thanks for the high quality of this article ! Just a remark: on the SD connecteur on my ili9341 board, MISO is connected via a track on the screen side of the pcb (without 1K resistor) . I suppose it is the same on your board.
Stefan Maetschke
Sunday 23rd of February 2025
Thanks for the info. Much appreciated!