ESP32 S3 UNO - TFT LCD Touch Display SPI

This ESP32 S3 Uno SPI TFT display tutorial shows you how to connect an ILI9341, ILI9488 or ST7789 color TFT LCD to the ESP32 S3 Uno form board and control it with Arduino code. You will draw shapes, text and pictures with the DIYables_TFT_SPI library, and read finger presses from the XPT2046 touch screen controller. The guide fits both touch and non-touch panels in 1.3, 1.54, 2.2, 2.4, 2.8, 3.2 and 3.5 inch sizes.

ESP32 S3 Uno SPI TFT display

In this tutorial, you will:

  1. Wire an SPI TFT LCD display to the hardware SPI pins of the ESP32 S3 Uno
  2. Draw shapes, colors, text and numbers on the TFT screen
  3. Show bitmap images from program memory and from a micro SD card
  4. Use a custom font for nicer text
  5. Read, calibrate and use XPT2046 touch input, and run the display on a custom SPI bus

※ NOTE THAT:

The ESP32 S3 Uno uses 3.3V logic, and these SPI TFT modules also work at 3.3V. This means the signal wires go straight from the board to the display. You do not need a level converter.

Hardware Preparation

1×ESP32 S3 Uno-form Board
1×USB Cable Type-A to Type-C (for USB-A PC)
1×USB Cable Type-C to Type-C (for USB-C PC)
1×SPI TFT display module
1×Jumper Wires

Or you can buy the following kits:

1×DIYables Sensor Kit (18 sensors/displays)
Disclosure: Some of the links provided in this section are Amazon affiliate links. We may receive a commission for any purchases made through these links at no additional cost to you.
Additionally, some of these links are for products from our own brand, DIYables .

SPI TFT Display Overview

An SPI TFT display is a color LCD panel with a small driver chip on the back. Your ESP32 S3 Uno sends drawing commands to that chip over the SPI bus. Knowing which chip your module has is important, because you must pick the matching driver in the code.

Supported driver chips

The DIYables_TFT_SPI library works with three popular chips. The ILI9341 uses 16-bit RGB565 color and runs at up to 40 MHz SPI. The ILI9488 uses 18-bit RGB666 color and runs at up to 24 MHz SPI. The ST7789 uses 16-bit RGB565 color and runs at up to 40 MHz SPI.

Which one should you buy?

If you do not own a display yet, choose a module with the ST7789 chip. It is easy to find, it runs at the full 40 MHz SPI speed, and it is the simplest choice for a new project.

Drawing functions

The library is built on top of Adafruit GFX. So every common GFX function for lines, shapes and text works out of the box.

Pinout

Before you wire anything, it helps to know what each pin on the module does. Most SPI TFT boards use the same pin names, so the tables below fit almost every module.

Display pins

Pin Function
VCC Power supply
GND Ground
CS Chip Select — pulled low to select the display on the SPI bus
DC / RS Data / Command select — high for pixel data, low for commands
RST Hardware reset — optional; tie to 3.3V if unused
MOSI / SDI / SDA SPI data in (board → display)
SCK / CLK SPI clock
MISO / SDO SPI data out (display → board) — optional for display-only use
LED / BL / BLK Backlight power — connect to 3.3V or a PWM pin for dimming

SD card pins

You only need these pins if your project reads files from the SD card slot on the module.

Pin Function
SD_CS / TF_CS SD card Chip Select
MOSI / SDI MOSI — data from board to SD card
SCK / CLK SCK — SPI clock
MISO / SDO MISO — data from SD card to board

Touch pins

Touch modules have five more pins for the touch controller. You only need them when your project uses touch and your display really has a touch panel.

Pin Function
T_CS Touch controller Chip Select
T_CLK SCK — SPI clock
T_DIN MOSI — data from board to touch controller
T_DO MISO — data from touch controller to board
T_IRQ Touch interrupt — optional; signals when the screen is being touched

※ NOTE THAT:

Some non-touch modules also show T_CS, T_CLK, T_DIN, T_DO and T_IRQ pins. On these boards the touch chip is simply not soldered, so the pins do nothing. The maker uses the same PCB for the touch and non-touch versions to save cost.

TFT SPI Display Pinout

ESP32 S3 Uno Pinout

The image below shows the pinout diagram of the ESP32 S3 Uno form board. Use it to find the Uno header pins (D8, D10, D13…) and their GPIO numbers when you wire the TFT display.

ESP32 S3 Uno pinout diagram

Wiring Diagram

The display, the SD card and the touch controller all share one SPI bus. On the ESP32 S3 Uno this bus sits on D11 (GPIO11, MOSI), D13 (GPIO12, SCK) and D12 (GPIO13, MISO), the same places as on a classic Uno. Each device gets its own CS pin, so only one of them talks at a time.

※ NOTE THAT:

The SDO (MISO) line only sends data from the display back to the board. None of the examples in this tutorial read from the display, so you can leave this pin unconnected. On some modules, the touch controller shares the same bus, and a connected SDO line can disturb it. So it is better to leave it open.

Without Touch

Use this wiring when you only want to show things on the screen. MOSI goes to D11, SCK to D13 and MISO to D12. CS, DC and RST can use other free pins; the examples use D10, D8 and D9.

Display

TFT Pin ESP32 S3 Uno Pin Description
VCC 3.3V Power supply
GND GND Ground
CS D10 (GPIO10) Chip Select
DC / RS D8 (GPIO21) Data / Command select
RST D9 (GPIO46) Reset (optional)
MOSI / SDI D11 (GPIO11) Hardware SPI MOSI
SCK D13 (GPIO12) Hardware SPI clock
MISO / SDO D12 (GPIO13) Hardware SPI MISO (optional)
LED / BL 3.3V Backlight power

SD card

Add these wires if your project reads images from the SD card.

SD Pin ESP32 S3 Uno Pin Description
SD_CS / TF_CS D4 (GPIO19) SD card Chip Select
MOSI / SDI D11 (GPIO11) Shared with display MOSI
SCK / CLK D13 (GPIO12) Shared with display SCK
MISO / SDO D12 (GPIO13) Shared with display MISO
The wiring diagram between ESP32 S3 Uno TFT SPI Display  without touch

This image is created using Fritzing. Click to enlarge image

With Touch

Use this wiring when you also want to read the touch screen. The XPT2046 touch controller shares D11, D13 and D12 with the display and only needs its own CS pin.

Display

TFT Pin ESP32 S3 Uno Pin Description
VCC 3.3V Power supply
GND GND Ground
CS D10 (GPIO10) Chip Select
DC / RS D8 (GPIO21) Data / Command select
RST D9 (GPIO46) Reset (optional)
MOSI / SDI D11 (GPIO11) Hardware SPI MOSI
SCK D13 (GPIO12) Hardware SPI clock
MISO / SDO D12 (GPIO13) Hardware SPI MISO (optional)
LED / BL 3.3V Backlight power

Touch controller

Touch Pin ESP32 S3 Uno Pin Description
T_CS D2 (GPIO18) Touch Chip Select
T_IRQ not connected Touch interrupt (optional, -1 in code)
T_DIN D11 (GPIO11) Shared with display MOSI
T_CLK D13 (GPIO12) Shared with display SCK
T_DO D12 (GPIO13) Shared with display MISO
The wiring diagram between ESP32 S3 Uno TFT SPI Display  with touch

This image is created using Fritzing. Click to enlarge image

WARNING

The RST pin goes to D9, which is GPIO46 on the ESP32 S3 Uno. GPIO46 is a boot strapping pin: the board reads it at power-up to choose its boot mode. Many TFT modules have a pull-up resistor on RST, and this can stop the board from entering upload mode. If the upload fails or the board does not start, unplug the RST wire while you upload. You can also tie the module's RST pin to 3.3V and set TFT_RST_PIN to -1 in the code.

WARNING

ESP32 S3 Uno pins are NOT 5V tolerant. Power the module from 3.3V so that the MISO and T_DO lines also stay at 3.3V. Some modules have a voltage regulator and accept 5V on VCC, but then check that no 5V signal goes back into an ESP32 S3 Uno pin.

One SPI bus or two?

The ESP32 S3 Uno has more than one hardware SPI bus. You can give each device (display, SD card, touch) its own bus, or let all three share the default bus on D11, D13 and D12. Sharing is the easy way, and it is what the wiring above does. The DIYables_TFT_SPI library drives both the display and the XPT2046 touch chip with one API, so you do not need a separate touch library.

How to Install the Library

The DIYables_TFT_SPI library does the hard work of talking to the display chip. You install it once in the Arduino IDE, and then every example below can use it.

  1. Connect the board — plug the ESP32 S3 Uno into your computer with a USB Type-C cable.
  2. Select the board — in the Arduino IDE, choose ESP32S3 Dev Module and the correct COM port.
  3. Open the Library Manager — click the Libraries icon on the left side.
  4. Search — type "DIYables_TFT_SPI" in the search box and find the library by DIYables.
  5. Install — click Install. When the IDE asks, also install the Adafruit GFX Library dependency.
  • Search for DIYables TFT SPI created by DIYables.io and click the Install button.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32S3 Dev Module
Library Manager
Type:
All
Topic:
All
DIYables TFT SPI by DIYables.io
Works with both touch and non-touch versions of the same SPI TFT modules. Supports ILI9341 (240x320, 16-bit RGB565), ILI9488 (320x480, 18-bit RGB666), and ST7789 (240x320, 16-bit RGB565) displays over SPI. Includes built-in driver for XPT2046 / HR2046 / ADS7843 SPI touch controllers and 4-wire resistive touch panels - no separate touch library required. Use the display-only API for non-touch panels, or add initTouchSPI() to enable touch on modules that include a touch controller. Extends Adafruit GFX for full graphics support. Works with any Arduino-compatible board that has SPI. More info
1.0.3
INSTALL
Newbiely.ino
···
1 void setup() {
Output
Serial Monitor
Ln 1, Col 1
ESP32S3 Dev Module on COM15
1

Minimal Sketch Structure

Every sketch in this tutorial starts from the same small skeleton. Once you know it, the longer examples are easy to read.

#include <DIYables_TFT_SPI.h> #define TFT_CS_PIN 10 // D10 #define TFT_DC_PIN 21 // D8 #define TFT_RST_PIN 46 // D9 // Uncomment the line that matches your driver chip: // DIYables_ILI9341_SPI TFT_display(240, 320, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); // DIYables_ILI9488_SPI TFT_display(320, 480, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); DIYables_ST7789_SPI TFT_display(240, 320, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); void setup() { TFT_display.begin(); TFT_display.setRotation(1); } void loop() { // drawing code here }

The pin numbers in the code are GPIO numbers, not the D labels on the header. Give the constructor the real pixel size of your panel, because modules with the same chip come in different sizes.

Quick Steps to run any example below

  1. New to the ESP32 S3 Uno? Follow ESP32 S3 Uno - Getting Started first.
  2. Wire it up as shown in the wiring diagram.
  3. Connect the board to your computer with a USB Type-C cable.
  4. Open Arduino IDE, choose the ESP32S3 Dev Module board and the correct COM port.
  5. Install the library as shown above, if you have not done it yet.
  6. Choose your driver — keep only the constructor line for your chip (ILI9341, ILI9488 or ST7789) and set the right width and height.
  7. Copy the code of the example, paste it into Arduino IDE and click Upload.
  8. Tip: if the upload does not start, unplug the RST wire from D9 (GPIO46), upload again, and then plug it back in.

Draw Shapes

Shapes are the fastest way to check that your wiring and driver choice are right. This example draws circles, triangles, rectangles, rounded rectangles and lines in many colors.

/* * This ESP32 S3 Uno code was developed by newbiely.com * * This ESP32 S3 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3-uno/esp32-s3-uno-tft-lcd-touch-display-spi */ /* Created by DIYables This example code is in the public domain Product page: https://diyables.io */ // ============================================= // Single include brings in the base class plus all driver classes. // ============================================= #include <DIYables_TFT_SPI.h> // ============================================= // Wiring (ESP32 S3 Uno) // --------------------------------------------- // TFT module ESP32 S3 Uno // ------------ --------------------------------- // VCC -> 3.3V // GND -> GND // CS -> D10 (GPIO10) (TFT_CS_PIN) // RESET -> D9 (GPIO46) (TFT_RST_PIN) - boot strapping pin // DC / RS -> D8 (GPIO21) (TFT_DC_PIN) // SDI / MOSI -> D11 (GPIO11) (hardware SPI MOSI) // SCK -> D13 (GPIO12) (hardware SPI SCK) // LED -> 3.3V (or any GPIO via initBacklight) // SDO / MISO -> D12 (GPIO13) (only needed when reading from display) // ============================================= // ============================================= // SPI pin definitions (adjust for your board) // ============================================= #define TFT_CS_PIN 10 #define TFT_DC_PIN 21 #define TFT_RST_PIN 46 // D9 = GPIO46 (boot strapping pin). Use -1 and tie RST to 3.3V if upload fails // Panel resolution in native (portrait) orientation - change to match your module #define TFT_WIDTH 240 #define TFT_HEIGHT 320 // MOSI and SCK use default hardware SPI pins // ============================================= // Create display object (uncomment matching driver) // ============================================= // DIYables_ILI9341_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); // DIYables_ILI9488_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); DIYables_ST7789_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); #define BLACK DIYables_TFT_SPI::colorRGB(0, 0, 0) #define BLUE DIYables_TFT_SPI::colorRGB(0, 0, 255) #define RED DIYables_TFT_SPI::colorRGB(255, 0, 0) #define GREEN DIYables_TFT_SPI::colorRGB(0, 255, 0) #define ORANGE DIYables_TFT_SPI::colorRGB(255, 165, 0) #define PINK DIYables_TFT_SPI::colorRGB(255, 192, 203) #define VIOLET DIYables_TFT_SPI::colorRGB(148, 0, 211) #define TURQUOISE DIYables_TFT_SPI::colorRGB(64, 224, 208) #define WHITE DIYables_TFT_SPI::colorRGB(255, 255, 255) // Helper to draw a filled diamond void fillDiamond(int cx, int cy, int h, int v, uint16_t color) { int x0 = cx, y0 = cy - v; int x1 = cx + h, y1 = cy; int x2 = cx, y2 = cy + v; int x3 = cx - h, y3 = cy; TFT_display.fillTriangle(x0, y0, x1, y1, x2, y2, color); TFT_display.fillTriangle(x0, y0, x2, y2, x3, y3, color); } void setup() { TFT_display.begin(); TFT_display.setRotation(1); // Landscape } void loop() { TFT_display.fillScreen(WHITE); uint16_t w = TFT_display.width(); uint16_t h = TFT_display.height(); // Scale positions relative to screen size with better spacing int col1 = w / 8; int col2 = w * 3 / 8; int col3 = w * 5 / 8; int col4 = w * 7 / 8; int row1 = h / 4; int row2 = h / 2; int row3 = h * 3 / 4; // Outlined circle TFT_display.drawCircle(col1, row1, 30, RED); // Filled circle TFT_display.fillCircle(col2, row1, 30, RED); // Outlined triangle TFT_display.drawTriangle(col3 - 30, row1 + 25, col3 + 30, row1 + 25, col3, row1 - 25, BLUE); // Filled triangle TFT_display.fillTriangle(col4 - 30, row1 + 25, col4 + 30, row1 + 25, col4, row1 - 25, GREEN); // Outlined rectangle TFT_display.drawRect(col1 - 35, row2 - 20, 70, 40, ORANGE); // Filled rectangle TFT_display.fillRect(col2 - 35, row2 - 20, 70, 40, TURQUOISE); // Outlined round rectangle TFT_display.drawRoundRect(col3 - 35, row2 - 20, 70, 40, 10, VIOLET); // Filled round rectangle TFT_display.fillRoundRect(col4 - 35, row2 - 20, 70, 40, 10, PINK); // Outlined diamond (centered between col1 and col2) int diamond1_x = (col1 + col2) / 2; TFT_display.drawLine(diamond1_x, row3 - 30, diamond1_x + 25, row3, GREEN); TFT_display.drawLine(diamond1_x + 25, row3, diamond1_x, row3 + 30, GREEN); TFT_display.drawLine(diamond1_x, row3 + 30, diamond1_x - 25, row3, GREEN); TFT_display.drawLine(diamond1_x - 25, row3, diamond1_x, row3 - 30, GREEN); // Filled diamond (centered between col3 and col4) int diamond2_x = (col3 + col4) / 2; fillDiamond(diamond2_x, row3, 25, 30, BLUE); delay(10000); }

How to Run

  1. Wire the TFT module to the ESP32 S3 Uno as shown in the wiring diagram.
  2. Connect the board with the USB Type-C cable.
  3. Upload — paste the code, check the board and port, and press Upload.
  4. Watch the screen — a grid of colored shapes appears and is redrawn every 10 seconds.

Method Quick Reference

Method What It Does Usage
begin() Initialize the display controller. Call once in setup(). TFT_display.begin();
setRotation(r) Rotate the coordinate system. 0=portrait, 1=landscape. TFT_display.setRotation(1);
fillScreen(color) Clear screen with a solid color. TFT_display.fillScreen(WHITE);
colorRGB(r,g,b) Build a 16-bit RGB565 color from 8-bit components. colorRGB(0, 128, 255)
fillCircle(x,y,r,color) Draw a solid circle. TFT_display.fillCircle(80, 80, 40, RED);
fillRect(x,y,w,h,color) Draw a solid rectangle. TFT_display.fillRect(10, 10, 60, 40, BLUE);
drawRoundRect(x,y,w,h,r,color) Draw a rounded rectangle outline. TFT_display.drawRoundRect(5,5,100,50,10,GREEN);

Show Text and Number

Most projects need to show readings like temperature or humidity. This example prints text and numbers in a chosen size, color and position.

/* * This ESP32 S3 Uno code was developed by newbiely.com * * This ESP32 S3 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3-uno/esp32-s3-uno-tft-lcd-touch-display-spi */ /* Created by DIYables This example code is in the public domain Product page: https://diyables.io */ // ============================================= // Single include brings in the base class plus all driver classes. // ============================================= #include <DIYables_TFT_SPI.h> // ============================================= // Wiring (ESP32 S3 Uno) // --------------------------------------------- // TFT module ESP32 S3 Uno // ------------ --------------------------------- // VCC -> 3.3V // GND -> GND // CS -> D10 (GPIO10) (TFT_CS_PIN) // RESET -> D9 (GPIO46) (TFT_RST_PIN) - boot strapping pin // DC / RS -> D8 (GPIO21) (TFT_DC_PIN) // SDI / MOSI -> D11 (GPIO11) (hardware SPI MOSI) // SCK -> D13 (GPIO12) (hardware SPI SCK) // LED -> 3.3V (or any GPIO via initBacklight) // SDO / MISO -> D12 (GPIO13) (only needed when reading from display) // ============================================= // ============================================= // SPI pin definitions (adjust for your board) // ============================================= #define TFT_CS_PIN 10 #define TFT_DC_PIN 21 #define TFT_RST_PIN 46 // D9 = GPIO46 (boot strapping pin). Use -1 and tie RST to 3.3V if upload fails // Panel resolution in native (portrait) orientation - change to match your module #define TFT_WIDTH 240 #define TFT_HEIGHT 320 // ============================================= // Create display object (uncomment matching driver) // ============================================= // DIYables_ILI9341_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); // DIYables_ILI9488_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); DIYables_ST7789_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); #define MAGENTA DIYables_TFT_SPI::colorRGB(255, 0, 255) #define WHITE DIYables_TFT_SPI::colorRGB(255, 255, 255) void setup() { Serial.begin(9600); Serial.println(F("TFT SPI Display - Show text and numbers")); TFT_display.begin(); TFT_display.setRotation(1); // Landscape TFT_display.fillScreen(WHITE); // Set text color and size TFT_display.setTextColor(MAGENTA); TFT_display.setTextSize(3); // Sample values float temperature = 23.5; float humidity = 78.6; // Display temperature TFT_display.setCursor(20, 20); TFT_display.print("Temperature: "); TFT_display.print(temperature, 1); TFT_display.print(char(247)); TFT_display.println("C"); // Display humidity TFT_display.setCursor(20, 60); TFT_display.print("Humidity: "); TFT_display.print(humidity, 1); TFT_display.print("%"); } void loop() { }

How to Run

  1. Wire and upload the same way as the Draw Shapes example.
  2. Check the screen — it shows a temperature line and a humidity line in large magenta text.

Method Quick Reference

Method What It Does Usage
setTextColor(color) Set text foreground color. TFT_display.setTextColor(BLACK);
setTextSize(size) Scale text. Size 1 is 6x8 pixels. TFT_display.setTextSize(2);
setCursor(x, y) Position the text cursor. TFT_display.setCursor(10, 30);
print(value) Print a string or number at the cursor. TFT_display.print("Temp: ");
println(value) Print and advance to next line. TFT_display.println(25.4);

Draw Image

Pictures make a project look finished, like a logo on a start screen. This example shows a full-color RGB565 image that is stored in program memory.

The image data lives in bitmap.h as a const uint16_t array marked PROGMEM. This keeps the pixels in flash instead of RAM. Put bitmap.h in the same folder as the sketch before you compile.

/* * This ESP32 S3 Uno code was developed by newbiely.com * * This ESP32 S3 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3-uno/esp32-s3-uno-tft-lcd-touch-display-spi */ /* Created by DIYables This example code is in the public domain Product page: https://diyables.io */ // ============================================= // Single include brings in the base class plus all driver classes. // ============================================= #include <DIYables_TFT_SPI.h> #include "bitmap.h" // ============================================= // Wiring (ESP32 S3 Uno) // --------------------------------------------- // TFT module ESP32 S3 Uno // ------------ --------------------------------- // VCC -> 3.3V // GND -> GND // CS -> D10 (GPIO10) (TFT_CS_PIN) // RESET -> D9 (GPIO46) (TFT_RST_PIN) - boot strapping pin // DC / RS -> D8 (GPIO21) (TFT_DC_PIN) // SDI / MOSI -> D11 (GPIO11) (hardware SPI MOSI) // SCK -> D13 (GPIO12) (hardware SPI SCK) // LED -> 3.3V (or any GPIO via initBacklight) // SDO / MISO -> D12 (GPIO13) (only needed when reading from display) // ============================================= // ============================================= // SPI pin definitions (adjust for your board) // ============================================= #define TFT_CS_PIN 10 #define TFT_DC_PIN 21 #define TFT_RST_PIN 46 // D9 = GPIO46 (boot strapping pin). Use -1 and tie RST to 3.3V if upload fails // Panel resolution in native (portrait) orientation - change to match your module #define TFT_WIDTH 240 #define TFT_HEIGHT 320 // ============================================= // Create display object (uncomment matching driver) // ============================================= // DIYables_ILI9341_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); // DIYables_ILI9488_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); DIYables_ST7789_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); #define WHITE DIYables_TFT_SPI::colorRGB(255, 255, 255) int img_width = 120; int img_height = 53; void setup() { Serial.begin(9600); Serial.println(F("TFT SPI Display - Draw Image")); TFT_display.begin(); uint16_t SCREEN_WIDTH = TFT_display.width(); uint16_t SCREEN_HEIGHT = TFT_display.height(); int x = (SCREEN_WIDTH - img_width) / 2; int y = (SCREEN_HEIGHT - img_height) / 2; TFT_display.fillScreen(WHITE); TFT_display.drawRGBBitmap(x, y, myBitmap, img_width, img_height); } void loop() { delay(2000); TFT_display.invertDisplay(true); delay(2000); TFT_display.invertDisplay(false); }

How to Run

  1. Add the image file — place bitmap.h in the same folder as the sketch.
  2. Wire the TFT module to the ESP32 S3 Uno as shown in the wiring diagram.
  3. Connect the board with the USB Type-C cable.
  4. Upload — paste the code, check the board and port, and press Upload.
  5. Watch the screen — the image appears in the center, and the colors invert every 2 seconds.

Method Quick Reference

Method What It Does Usage
drawRGBBitmap(x,y,bitmap,w,h) Draw an RGB565 PROGMEM bitmap starting at pixel (x, y). TFT_display.drawRGBBitmap(0, 0, myImage, 240, 320);
fillScreen(color) Clear the screen before drawing the image. TFT_display.fillScreen(BLACK);

Draw Image SD Card

Flash memory is limited, so large or many images are better kept on a micro SD card. This example reads a 24-bit BMP file from the card and draws it on the screen a few pixels at a time. The whole image never has to fit in RAM, so it works well on big panels.

The SD card shares the SPI bus with the display: MOSI on D11 (GPIO11), SCK on D13 (GPIO12) and MISO on D12 (GPIO13). It needs its own CS pin, which is SD_CS in the sketch. The example uses D4 (GPIO19).

/* * This ESP32 S3 Uno code was developed by newbiely.com * * This ESP32 S3 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3-uno/esp32-s3-uno-tft-lcd-touch-display-spi */ /* Created by DIYables This example code is in the public domain Product page: https://diyables.io */ // ============================================= // Single include brings in the base class plus all driver classes. // ============================================= #include <DIYables_TFT_SPI.h> #include <SD.h> // ============================================= // Wiring (ESP32 S3 Uno) // --------------------------------------------- // TFT + SD module ESP32 S3 Uno // ----------------------- --------------------------------- // VCC -> 3.3V // GND -> GND // TFT CS -> D10 (GPIO10) (TFT_CS_PIN) // TFT RESET -> D9 (GPIO46) (TFT_RST_PIN) - boot strapping pin // TFT DC / RS -> D8 (GPIO21) (TFT_DC_PIN) // SD CS -> D4 (GPIO19) (SD_CS) // SDI / MOSI (shared) -> D11 (GPIO11) (hardware SPI MOSI) // SDO / MISO (shared) -> D12 (GPIO13) (hardware SPI MISO) // SCK (shared) -> D13 (GPIO12) (hardware SPI SCK) // LED -> 3.3V (or any GPIO via initBacklight) // ============================================= // ============================================= // SPI pin definitions (adjust for your board) // ============================================= #define TFT_CS_PIN 10 #define TFT_DC_PIN 21 #define TFT_RST_PIN 46 // D9 = GPIO46 (boot strapping pin). Use -1 and tie RST to 3.3V if upload fails // Panel resolution in native (portrait) orientation - change to match your module #define TFT_WIDTH 240 #define TFT_HEIGHT 320 #define SD_CS 19 // SD card chip select (must differ from TFT_CS_PIN) // ============================================= // Create display object (uncomment matching driver) // ============================================= // DIYables_ILI9341_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); // DIYables_ILI9488_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); DIYables_ST7789_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); #define WHITE DIYables_TFT_SPI::colorRGB(255, 255, 255) #define BUFFPIXEL 20 File bmpFile; uint16_t SCREEN_WIDTH; uint16_t SCREEN_HEIGHT; // Helper functions to read BMP file header uint16_t read16(File &f) { uint16_t result; result = f.read(); result |= (f.read() << 8); return result; } uint32_t read32(File &f) { uint32_t result; result = f.read(); result |= ((uint32_t)f.read() << 8); result |= ((uint32_t)f.read() << 16); result |= ((uint32_t)f.read() << 24); return result; } int32_t readS32(File &f) { int32_t result; result = f.read(); result |= ((uint32_t)f.read() << 8); result |= ((uint32_t)f.read() << 16); result |= ((uint32_t)f.read() << 24); return result; } bool getBMPDimensions(const char *filename, uint32_t &w, uint32_t &h) { File f = SD.open(filename); if (!f) return false; if (read16(f) != 0x4D42) { f.close(); return false; } read32(f); // file size read32(f); // reserved read32(f); // image offset read32(f); // DIB header size w = read32(f); int32_t sh = readS32(f); h = (sh < 0) ? -sh : sh; f.close(); return true; } void drawBMP(const char *filename, int x, int y) { bmpFile = SD.open(filename); if (!bmpFile) { Serial.println("File not found"); return; } if (read16(bmpFile) != 0x4D42) { Serial.println("Not a BMP file"); bmpFile.close(); return; } uint32_t fileSize = read32(bmpFile); read32(bmpFile); // Reserved uint32_t imageOffset = read32(bmpFile); uint32_t dibHeaderSize = read32(bmpFile); uint32_t bmpWidth = read32(bmpFile); int32_t bmpHeight = readS32(bmpFile); bool topDown = false; if (bmpHeight < 0) { bmpHeight = -bmpHeight; topDown = true; } if (read16(bmpFile) != 1) { Serial.println("Invalid BMP file"); bmpFile.close(); return; } uint16_t depth = read16(bmpFile); if (depth != 24) { Serial.println("Only 24-bit BMP is supported"); bmpFile.close(); return; } if (read32(bmpFile) != 0) { Serial.println("Unsupported BMP compression"); bmpFile.close(); return; } bmpFile.seek(imageOffset); uint8_t sdbuffer[3 * BUFFPIXEL]; uint16_t color; uint32_t rowSize = (bmpWidth * 3 + 3) & ~3; if (x >= SCREEN_WIDTH || y >= SCREEN_HEIGHT) return; uint32_t maxRow = min((uint32_t)bmpHeight, (uint32_t)(SCREEN_HEIGHT - y)); uint32_t maxCol = min(bmpWidth, (uint32_t)(SCREEN_WIDTH - x)); for (uint32_t row = 0; row < maxRow; row++) { int32_t rowPos = topDown ? row : bmpHeight - 1 - row; uint32_t filePosition = imageOffset + rowPos * rowSize; bmpFile.seek(filePosition); for (uint32_t col = 0; col < maxCol; col += BUFFPIXEL) { uint32_t pixelsToRead = min((uint32_t)BUFFPIXEL, maxCol - col); bmpFile.read(sdbuffer, 3 * pixelsToRead); for (uint32_t i = 0; i < pixelsToRead; i++) { uint8_t b = sdbuffer[i * 3]; uint8_t g = sdbuffer[i * 3 + 1]; uint8_t r = sdbuffer[i * 3 + 2]; color = DIYables_TFT_SPI::colorRGB(r, g, b); if ((x + col + i) < SCREEN_WIDTH && (y + row) < SCREEN_HEIGHT) { TFT_display.drawPixel(x + col + i, y + row, color); } } } } bmpFile.close(); Serial.println("BMP drawn"); } void setup() { Serial.begin(9600); if (!SD.begin(SD_CS)) { Serial.println("SD card initialization failed!"); return; } Serial.println("SD card initialized."); TFT_display.begin(); TFT_display.setRotation(1); // Landscape SCREEN_WIDTH = TFT_display.width(); SCREEN_HEIGHT = TFT_display.height(); TFT_display.fillScreen(WHITE); uint32_t imgWidth, imgHeight; if (getBMPDimensions("/diyables.bmp", imgWidth, imgHeight)) { int x = (SCREEN_WIDTH - imgWidth) / 2; int y = (SCREEN_HEIGHT - imgHeight) / 2; drawBMP("/diyables.bmp", x, y); } else { Serial.println("Failed to get BMP dimensions"); } } void loop() { }

How to Run

  1. Wire the SD card — share D11, D13 and D12 with the display, and connect SD_CS to D4 (GPIO19).
  2. Prepare the card — copy a 24-bit BMP file named diyables.bmp to the root of the micro SD card, and insert the card.
  3. Connect the board with the USB Type-C cable.
  4. Upload — paste the code, check the board and port, and press Upload.
  5. Open the Serial Monitor at 9600 baud to see the progress. The image then shows up in the center of the screen.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32S3 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32S3 Dev Module' on 'COM15')
New Line
9600 baud
SD card initialized. BMP drawn
Ln 11, Col 1
ESP32S3 Dev Module on COM15
2

Method Quick Reference

Method What It Does Usage
startWrite() Begin a direct SPI transaction, holding CS asserted. TFT_display.startWrite();
setAddrWindow(x0,y0,x1,y1) Define the rectangular pixel region to write. TFT_display.setAddrWindow(0, 0, 239, 319);
pushColors(buf, len) Send a buffer of RGB565 pixel values to the display. TFT_display.pushColors(buf, 512);
endWrite() End the SPI transaction and release CS. TFT_display.endWrite();

Use External Font

The built-in 5×7 font is small and blocky. This example uses a custom font that works with Adafruit GFX, so the text looks smoother and easier to read. You include the font header once and turn it on with a single setFont() call.

/* * This ESP32 S3 Uno code was developed by newbiely.com * * This ESP32 S3 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3-uno/esp32-s3-uno-tft-lcd-touch-display-spi */ /* Created by DIYables This example code is in the public domain Product page: https://diyables.io */ // ============================================= // Single include brings in the base class plus all driver classes. // ============================================= #include <DIYables_TFT_SPI.h> #include <Fonts/FreeSansBold12pt7b.h> // ============================================= // Wiring (ESP32 S3 Uno) // --------------------------------------------- // TFT module ESP32 S3 Uno // ------------ --------------------------------- // VCC -> 3.3V // GND -> GND // CS -> D10 (GPIO10) (TFT_CS_PIN) // RESET -> D9 (GPIO46) (TFT_RST_PIN) - boot strapping pin // DC / RS -> D8 (GPIO21) (TFT_DC_PIN) // SDI / MOSI -> D11 (GPIO11) (hardware SPI MOSI) // SCK -> D13 (GPIO12) (hardware SPI SCK) // LED -> 3.3V (or any GPIO via initBacklight) // SDO / MISO -> D12 (GPIO13) (only needed when reading from display) // ============================================= // ============================================= // SPI pin definitions (adjust for your board) // ============================================= #define TFT_CS_PIN 10 #define TFT_DC_PIN 21 #define TFT_RST_PIN 46 // D9 = GPIO46 (boot strapping pin). Use -1 and tie RST to 3.3V if upload fails // Panel resolution in native (portrait) orientation - change to match your module #define TFT_WIDTH 240 #define TFT_HEIGHT 320 // ============================================= // Create display object (uncomment matching driver) // ============================================= // DIYables_ILI9341_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); // DIYables_ILI9488_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); DIYables_ST7789_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); #define MAGENTA DIYables_TFT_SPI::colorRGB(255, 0, 255) #define WHITE DIYables_TFT_SPI::colorRGB(255, 255, 255) void setup() { Serial.begin(9600); Serial.println(F("TFT SPI Display - Use external font")); TFT_display.begin(); TFT_display.setFont(&FreeSansBold12pt7b); TFT_display.setRotation(1); // Landscape TFT_display.fillScreen(WHITE); TFT_display.setTextColor(MAGENTA); TFT_display.setTextSize(1); float temperature = 23.5; float humidity = 78.6; TFT_display.setCursor(20, 30); TFT_display.print("Temperature: "); TFT_display.print(temperature, 1); TFT_display.print(char(247)); TFT_display.println("C"); TFT_display.setCursor(20, 70); TFT_display.print("Humidity: "); TFT_display.print(humidity, 1); TFT_display.print("%"); } void loop() { }

How to Run

  1. Wire the TFT module to the ESP32 S3 Uno as shown in the wiring diagram.
  2. Connect the board with the USB Type-C cable.
  3. Upload — paste the code, check the board and port, and press Upload.
  4. Compare the text — the screen shows the values in the FreeSansBold font, which is much smoother than the built-in font.

Method Quick Reference

Method What It Does Usage
setFont(&FontName) Activate a custom GFX-compatible font. Pass NULL to restore the built-in 5×7 font. TFT_display.setFont(&FreeSans12pt7b);
setCursor(x, y) Move the text cursor to the given position. TFT_display.setCursor(10, 40);
setTextColor(color) Set the text foreground color. TFT_display.setTextColor(WHITE);
print(text) Print a string at the current cursor position using the active font. TFT_display.print("Hello!");

Touch Get Point

This is the first touch example, and a good test that the touch controller is wired right. Each time you press the screen, the sketch prints the X and Y position to the Serial Monitor and draws a red dot at that spot.

The touch wiring shares the SPI bus with the display: T_CLK to D13 (GPIO12), T_DIN to D11 (GPIO11) and T_DO to D12 (GPIO13). T_CS goes to D2 (GPIO18). T_IRQ is not used, so TOUCH_IRQ_PIN is -1 in the code.

/* * This ESP32 S3 Uno code was developed by newbiely.com * * This ESP32 S3 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3-uno/esp32-s3-uno-tft-lcd-touch-display-spi */ /* Touch Get Point Example ----------------------- This example demonstrates how to read and display touch coordinates using a DIYables SPI TFT display with a 4-wire resistive touch panel. When you touch the screen, the sketch prints the mapped (screen) X and Y coordinates to the Serial Monitor and draws a red dot at the touched location. NOTE: Run the TouchCalibration example first and paste the calibration values into setTouchCalibration() below if the touch coordinates are inaccurate. Created by DIYables This example code is in the public domain Product page: https://diyables.io */ // ============================================= // Single include brings in the base class plus all driver classes. // ============================================= #include <DIYables_TFT_SPI.h> // ============================================= // Wiring (ESP32 S3 Uno) // ============================================= // TFT pins (always required) // TFT module ESP32 S3 Uno // ------------ --------------------------------- // VCC -> 3.3V // GND -> GND // CS -> D10 (GPIO10) (TFT_CS_PIN) // RESET -> D9 (GPIO46) (TFT_RST_PIN) - boot strapping pin // DC / RS -> D8 (GPIO21) (TFT_DC_PIN) // SDI / MOSI -> D11 (GPIO11) (hardware SPI MOSI) // SCK -> D13 (GPIO12) (hardware SPI SCK) // SDO / MISO -> D12 (GPIO13) (only needed when reading from display) // LED -> 3.3V (or any GPIO via initBacklight) // // XPT2046 / HR2046 / ADS7843 SPI touch controller // (modules with pins: T_CS, T_CLK, T_DIN, T_DO, T_IRQ) // Touch pin ESP32 S3 Uno // ------------ --------------------------------- // T_CS -> D2 (GPIO18) (TOUCH_CS_PIN) // T_IRQ -> not connected (TOUCH_IRQ_PIN, optional - use -1 to skip) // T_CLK -> D13 (GPIO12) (shared with display SCK) // T_DIN -> D11 (GPIO11) (shared with display MOSI) // T_DO -> D12 (GPIO13) (shared with display MISO) // ============================================= // ============================================= // SPI pin definitions (adjust for your board) // ============================================= #define TFT_CS_PIN 10 #define TFT_DC_PIN 21 #define TFT_RST_PIN 46 // D9 = GPIO46 (boot strapping pin). Use -1 and tie RST to 3.3V if upload fails // Panel resolution in native (portrait) orientation - change to match your module #define TFT_WIDTH 240 #define TFT_HEIGHT 320 // MOSI and SCK use default hardware SPI pins // ============================================= // Touch pin definitions (XPT2046 / HR2046 SPI touch controller) // ============================================= #define TOUCH_CS_PIN 18 // T_CS (any GPIO) #define TOUCH_IRQ_PIN -1 // T_IRQ (any GPIO, or -1 if not connected) // ============================================= // ============================================= // Calibration values. // Run the TouchCalibration example and update these if touch is inaccurate. // Typical raw ranges: // - XPT2046 / HR2046 : ~200..3900 (default below) // - 4-wire resistive : ~100..900 // ============================================= #define TOUCH_LEFT_X 300 #define TOUCH_RIGHT_X 3700 #define TOUCH_TOP_Y 300 #define TOUCH_BOT_Y 3700 // ============================================= // Create display object (uncomment matching driver) // ============================================= // DIYables_ILI9341_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); // DIYables_ILI9488_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); DIYables_ST7789_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); #define RED DIYables_TFT_SPI::colorRGB(255, 0, 0) #define WHITE DIYables_TFT_SPI::colorRGB(255, 255, 255) void setup() { Serial.begin(9600); TFT_display.begin(); TFT_display.setRotation(0); TFT_display.fillScreen(WHITE); TFT_display.initTouchSPI(TOUCH_CS_PIN, TOUCH_IRQ_PIN); // If touch X is mirrored on your board, uncomment: //TFT_display.setTouchInvertX(true); // If touch Y is mirrored on your board, uncomment: //TFT_display.setTouchInvertY(false); TFT_display.setTouchCalibration(TOUCH_LEFT_X, TOUCH_RIGHT_X, TOUCH_TOP_Y, TOUCH_BOT_Y); Serial.println("Touch the screen to see coordinates."); } void loop() { int x, y; if (TFT_display.getTouch(x, y)) { Serial.print("Touch at: "); Serial.print(x); Serial.print(", "); Serial.println(y); TFT_display.fillCircle(x, y, 4, RED); delay(200); } }

How to Run

  1. Wire the touch controller — share the SPI bus with the display and connect T_CS to D2 (GPIO18).
  2. Connect the board with the USB Type-C cable.
  3. Upload — paste the code, check the board and port, and press Upload.
  4. Open the Serial Monitor at 9600 baud, then press the screen in different places.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32S3 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32S3 Dev Module' on 'COM15')
New Line
9600 baud
Touch the screen to see coordinates. Touch at: 42, 61 Touch at: 120, 160 Touch at: 201, 287
Ln 11, Col 1
ESP32S3 Dev Module on COM15
2

If the dot does not appear where your finger is, run the Touch Calibration example below and use its values.

Method Quick Reference

Method What It Does Usage
initTouchSPI(cs, irq) Initialize the XPT2046 on the shared SPI bus. Use -1 for irq if the interrupt pin is not connected. TFT_display.initTouchSPI(18, -1);
readTouchRaw(x, y, z) Return raw ADC touch values without applying any calibration. Returns true when pressed. TFT_display.readTouchRaw(x, y, z);
getTouch(x, y) Retrieve calibrated touch coordinates in screen pixels. Returns true when the screen is being pressed. if (TFT_display.getTouch(x, y)) { ... }

Touch Draw

Now you can turn the screen into a small drawing pad. The sketch reads the calibrated touch position and puts a small red dot there, so moving your finger leaves a line behind it.

/* * This ESP32 S3 Uno code was developed by newbiely.com * * This ESP32 S3 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3-uno/esp32-s3-uno-tft-lcd-touch-display-spi */ /* Touch Draw Lines Example ------------------------- Draws lines on the screen following the pen. - Touch and drag on the screen to draw. - Lift the pen to stop drawing. - Touch again to start a new line from the last point. NOTE: Run the TouchCalibration example and update setTouchCalibration() below if the touch coordinates are inaccurate. Created by DIYables This example code is in the public domain Product page: https://diyables.io */ // ============================================= // Single include brings in the base class plus all driver classes. // ============================================= #include <DIYables_TFT_SPI.h> // ============================================= // Wiring (ESP32 S3 Uno) // ============================================= // TFT pins (always required) // TFT module ESP32 S3 Uno // ------------ --------------------------------- // VCC -> 3.3V // GND -> GND // CS -> D10 (GPIO10) (TFT_CS_PIN) // RESET -> D9 (GPIO46) (TFT_RST_PIN) - boot strapping pin // DC / RS -> D8 (GPIO21) (TFT_DC_PIN) // SDI / MOSI -> D11 (GPIO11) (hardware SPI MOSI) // SCK -> D13 (GPIO12) (hardware SPI SCK) // SDO / MISO -> D12 (GPIO13) (only needed when reading from display) // LED -> 3.3V (or any GPIO via initBacklight) // // XPT2046 / HR2046 / ADS7843 SPI touch controller // (modules with pins: T_CS, T_CLK, T_DIN, T_DO, T_IRQ) // Touch pin ESP32 S3 Uno // ------------ --------------------------------- // T_CS -> D2 (GPIO18) (TOUCH_CS_PIN) // T_IRQ -> not connected (TOUCH_IRQ_PIN, optional - use -1 to skip) // T_CLK -> D13 (GPIO12) (shared with display SCK) // T_DIN -> D11 (GPIO11) (shared with display MOSI) // T_DO -> D12 (GPIO13) (shared with display MISO) // ============================================= // ============================================= // SPI pin definitions (adjust for your board) // ============================================= #define TFT_CS_PIN 10 #define TFT_DC_PIN 21 #define TFT_RST_PIN 46 // D9 = GPIO46 (boot strapping pin). Use -1 and tie RST to 3.3V if upload fails // Panel resolution in native (portrait) orientation - change to match your module #define TFT_WIDTH 240 #define TFT_HEIGHT 320 // ============================================= // Touch pin definitions (XPT2046 / HR2046 SPI touch controller) // ============================================= #define TOUCH_CS_PIN 18 // T_CS (any GPIO) #define TOUCH_IRQ_PIN -1 // T_IRQ (any GPIO, or -1 if not connected) // ============================================= // ============================================= // Calibration values. // Run the TouchCalibration example and update these if touch is inaccurate. // Typical raw ranges: // - XPT2046 / HR2046 : ~200..3900 (default below) // - 4-wire resistive : ~100..900 // ============================================= #define TOUCH_LEFT_X 300 #define TOUCH_RIGHT_X 3700 #define TOUCH_TOP_Y 300 #define TOUCH_BOT_Y 3700 // ============================================= // Create display object (uncomment matching driver) // ============================================= // DIYables_ILI9341_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); // DIYables_ILI9488_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); DIYables_ST7789_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); #define RED DIYables_TFT_SPI::colorRGB(255, 0, 0) #define WHITE DIYables_TFT_SPI::colorRGB(255, 255, 255) #define PEN_RADIUS 3 void setup() { TFT_display.begin(); TFT_display.setRotation(0); TFT_display.fillScreen(WHITE); TFT_display.initTouchSPI(TOUCH_CS_PIN, TOUCH_IRQ_PIN); // If touch X is mirrored on your board, uncomment: //TFT_display.setTouchInvertX(true); // If touch Y is mirrored on your board, uncomment: //TFT_display.setTouchInvertY(false); TFT_display.setTouchCalibration(TOUCH_LEFT_X, TOUCH_RIGHT_X, TOUCH_TOP_Y, TOUCH_BOT_Y); } void loop() { int x, y; if (TFT_display.getTouch(x, y)) { TFT_display.fillCircle(x, y, PEN_RADIUS, RED); } }

How to Run

  1. Wire the touch controller as described in the Touch Get Point section.
  2. Connect the board with the USB Type-C cable.
  3. Upload — paste the code, check the board and port, and press Upload.
  4. Draw — drag your finger or a stylus across the screen to paint in red.

Method Quick Reference

Method What It Does Usage
initTouchSPI(cs, irq) Set up the XPT2046 on the shared SPI bus. TFT_display.initTouchSPI(18, -1);
setTouchCalibration(minX,maxX,minY,maxY) Map raw ADC values to screen pixel coordinates. Get these values from the TouchCalibration example. TFT_display.setTouchCalibration(200, 3800, 300, 3700);
setTouchInvertX(invert) / setTouchInvertY(invert) Flips the touch axis when X or Y is mirrored on your specific panel or batch. Call BEFORE setTouchCalibration(). TFT_display.setTouchInvertY(true);
getTouch(x, y) Retrieve calibrated touch coordinates in screen pixels. Returns true when the screen is being pressed. if (TFT_display.getTouch(x, y)) { ... }
fillCircle(x, y, r, color) Draw a small dot at the touch point. TFT_display.fillCircle(x, y, 3, RED);

Touch Button

Buttons on the screen let you build a real touch menu without any extra parts. This example draws one rectangle button and checks if your finger is inside it. The button turns gray and shows "PRESSED" while you hold it, and goes back to red when you let go. A short debounce delay keeps the state stable.

T_IRQ is not needed here. The sketch passes -1 for the interrupt pin and simply polls the touch controller.

/* * This ESP32 S3 Uno code was developed by newbiely.com * * This ESP32 S3 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3-uno/esp32-s3-uno-tft-lcd-touch-display-spi */ /* Touch Button Press/Release Example ------------------------------------ This example shows how to detect press and release events on a rectangular button using a DIYables SPI TFT display with a 4-wire resistive touch panel. When you touch inside the button, it changes colour and shows "PRESSED". When you release, it returns to its original state. NOTE: Run the TouchCalibration example and update setTouchCalibration() below if the touch coordinates are inaccurate. Created by DIYables This example code is in the public domain Product page: https://diyables.io */ // ============================================= // Single include brings in the base class plus all driver classes. // ============================================= #include <DIYables_TFT_SPI.h> // ============================================= // Wiring (ESP32 S3 Uno) // ============================================= // TFT pins (always required) // TFT module ESP32 S3 Uno // ------------ --------------------------------- // VCC -> 3.3V // GND -> GND // CS -> D10 (GPIO10) (TFT_CS_PIN) // RESET -> D9 (GPIO46) (TFT_RST_PIN) - boot strapping pin // DC / RS -> D8 (GPIO21) (TFT_DC_PIN) // SDI / MOSI -> D11 (GPIO11) (hardware SPI MOSI) // SCK -> D13 (GPIO12) (hardware SPI SCK) // SDO / MISO -> D12 (GPIO13) (only needed when reading from display) // LED -> 3.3V (or any GPIO via initBacklight) // // XPT2046 / HR2046 / ADS7843 SPI touch controller // (modules with pins: T_CS, T_CLK, T_DIN, T_DO, T_IRQ) // Touch pin ESP32 S3 Uno // ------------ --------------------------------- // T_CS -> D2 (GPIO18) (TOUCH_CS_PIN) // T_IRQ -> not connected (TOUCH_IRQ_PIN, optional - use -1 to skip) // T_CLK -> D13 (GPIO12) (shared with display SCK) // T_DIN -> D11 (GPIO11) (shared with display MOSI) // T_DO -> D12 (GPIO13) (shared with display MISO) // ============================================= // ============================================= // SPI pin definitions (adjust for your board) // ============================================= #define TFT_CS_PIN 10 #define TFT_DC_PIN 21 #define TFT_RST_PIN 46 // D9 = GPIO46 (boot strapping pin). Use -1 and tie RST to 3.3V if upload fails // Panel resolution in native (portrait) orientation - change to match your module #define TFT_WIDTH 240 #define TFT_HEIGHT 320 // ============================================= // Touch pin definitions (XPT2046 / HR2046 SPI touch controller) // ============================================= #define TOUCH_CS_PIN 18 // T_CS (any GPIO) #define TOUCH_IRQ_PIN -1 // T_IRQ (any GPIO, or -1 if not connected) // ============================================= // ============================================= // Calibration values. // Run the TouchCalibration example and update these if touch is inaccurate. // Typical raw ranges: // - XPT2046 / HR2046 : ~200..3900 (default below) // - 4-wire resistive : ~100..900 // ============================================= #define TOUCH_LEFT_X 300 #define TOUCH_RIGHT_X 3700 #define TOUCH_TOP_Y 300 #define TOUCH_BOT_Y 3700 // ============================================= // Create display object (uncomment matching driver) // ============================================= // DIYables_ILI9341_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); // DIYables_ILI9488_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); DIYables_ST7789_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); #define BLACK DIYables_TFT_SPI::colorRGB( 0, 0, 0) #define WHITE DIYables_TFT_SPI::colorRGB(255, 255, 255) #define GRAY DIYables_TFT_SPI::colorRGB(128, 128, 128) #define RED DIYables_TFT_SPI::colorRGB(255, 0, 0) #define BUTTON_X 30 #define BUTTON_Y 100 #define BUTTON_W 180 #define BUTTON_H 60 #define DEBOUNCE_DELAY 50 bool lastPressed = false; bool stablePressed = false; unsigned long lastDebounceTime = 0; void drawButton(bool pressed) { uint16_t bg = pressed ? GRAY : RED; TFT_display.fillRect(BUTTON_X, BUTTON_Y, BUTTON_W, BUTTON_H, bg); TFT_display.drawRect(BUTTON_X, BUTTON_Y, BUTTON_W, BUTTON_H, BLACK); TFT_display.setTextColor(WHITE, bg); TFT_display.setTextSize(3); TFT_display.setCursor(BUTTON_X + 10, BUTTON_Y + 16); TFT_display.print(pressed ? "PRESSED" : " PRESS "); } void setup() { Serial.begin(9600); TFT_display.begin(); TFT_display.setRotation(0); TFT_display.fillScreen(WHITE); TFT_display.initTouchSPI(TOUCH_CS_PIN, TOUCH_IRQ_PIN); // If touch X is mirrored on your board, uncomment: //TFT_display.setTouchInvertX(true); // If touch Y is mirrored on your board, uncomment: //TFT_display.setTouchInvertY(false); TFT_display.setTouchCalibration(TOUCH_LEFT_X, TOUCH_RIGHT_X, TOUCH_TOP_Y, TOUCH_BOT_Y); drawButton(false); } void loop() { int x, y; bool pressed = false; if (TFT_display.getTouch(x, y)) { if (x >= BUTTON_X && x < (BUTTON_X + BUTTON_W) && y >= BUTTON_Y && y < (BUTTON_Y + BUTTON_H)) { pressed = true; } } if (pressed != lastPressed) { lastDebounceTime = millis(); } lastPressed = pressed; if ((millis() - lastDebounceTime) > DEBOUNCE_DELAY) { if (pressed != stablePressed) { stablePressed = pressed; drawButton(stablePressed); Serial.println(stablePressed ? "Button PRESSED" : "Button RELEASED"); } } }

How to Run

  1. Wire the touch controller — T_CS to D2 (GPIO18). Leave T_IRQ unconnected (it is -1 in the sketch).
  2. Connect the board with the USB Type-C cable.
  3. Upload — paste the code, check the board and port, and press Upload.
  4. Tap the button on the screen. Open the Serial Monitor at 9600 baud to see each press and release.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32S3 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32S3 Dev Module' on 'COM15')
New Line
9600 baud
Button PRESSED Button RELEASED Button PRESSED Button RELEASED
Ln 11, Col 1
ESP32S3 Dev Module on COM15
2

Method Quick Reference

Method What It Does Usage
initTouchSPI(cs, irq) Initialize the XPT2046. Pass -1 for irq to poll without the interrupt pin. TFT_display.initTouchSPI(18, -1);
setTouchCalibration(minX,maxX,minY,maxY) Apply calibration so getTouch() returns display-pixel coordinates. TFT_display.setTouchCalibration(200, 3800, 300, 3700);
setTouchInvertX(invert) / setTouchInvertY(invert) Flips the touch axis when X or Y is mirrored on your specific panel or batch. Call BEFORE setTouchCalibration(). TFT_display.setTouchInvertY(true);
getTouch(x, y) Get the current calibrated touch position. Returns true while the screen is pressed. if (TFT_display.getTouch(x, y)) { ... }
fillRect(x, y, w, h, color) Draw a solid colored rectangle for the button background. TFT_display.fillRect(10, 10, 120, 60, BLUE);

Touch Calibration

Every resistive touch panel is a bit different, so the raw numbers from one screen do not fit another. This example measures your own panel. It shows a blinking red dot in each of the four corners, one after the other. You press and hold each dot until the Serial Monitor says "Captured!". At the end, the sketch prints a ready-made setTouchCalibration() line that you copy into your other touch sketches.

/* * This ESP32 S3 Uno code was developed by newbiely.com * * This ESP32 S3 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3-uno/esp32-s3-uno-tft-lcd-touch-display-spi */ /* Touch Screen Calibration Example --------------------------------- This example measures the raw touch coordinates at all four screen corners and prints ready-to-use calibration values to the Serial Monitor. It uses readTouchRaw() directly — it does NOT rely on getTouch() or any existing calibration values, so it works even when touch is completely broken. INSTRUCTIONS: 1. Upload this sketch to your board. 2. Open the Serial Monitor (Ctrl+Shift+M) and set baud rate to 9600. 3. The screen shows a blinking red dot in each corner, numbered 1–4: 1 = Top-left 2 = Top-right 3 = Bottom-right 4 = Bottom-left 4. Press and HOLD firmly on the blinking dot. Keep holding until the Serial Monitor prints "Captured!" for that corner. 5. Release, then wait for the next dot to appear and repeat. 6. After all 4 corners, the Serial Monitor prints the calibration values and a ready-to-use setTouchCalibration() call. Copy it into your sketch. NOTE: While waiting, the Serial Monitor continuously prints the live raw Z/X/Y readings so you can confirm that touch is being detected. Created by DIYables This example code is in the public domain Product page: https://diyables.io */ // ============================================= // Single include brings in the base class plus all driver classes. // ============================================= #include <DIYables_TFT_SPI.h> // ============================================= // Wiring (ESP32 S3 Uno) // ============================================= // TFT pins (always required) // TFT module ESP32 S3 Uno // ------------ --------------------------------- // VCC -> 3.3V // GND -> GND // CS -> D10 (GPIO10) (TFT_CS_PIN) // RESET -> D9 (GPIO46) (TFT_RST_PIN) - boot strapping pin // DC / RS -> D8 (GPIO21) (TFT_DC_PIN) // SDI / MOSI -> D11 (GPIO11) (hardware SPI MOSI) // SCK -> D13 (GPIO12) (hardware SPI SCK) // SDO / MISO -> D12 (GPIO13) (only needed when reading from display) // LED -> 3.3V (or any GPIO via initBacklight) // // XPT2046 / HR2046 / ADS7843 SPI touch controller // (modules with pins: T_CS, T_CLK, T_DIN, T_DO, T_IRQ) // Touch pin ESP32 S3 Uno // ------------ --------------------------------- // T_CS -> D2 (GPIO18) (TOUCH_CS_PIN) // T_IRQ -> not connected (TOUCH_IRQ_PIN, optional - use -1 to skip) // T_CLK -> D13 (GPIO12) (shared with display SCK) // T_DIN -> D11 (GPIO11) (shared with display MOSI) // T_DO -> D12 (GPIO13) (shared with display MISO) // ============================================= // ============================================= // SPI pin definitions (adjust for your board) // ============================================= #define TFT_CS_PIN 10 #define TFT_DC_PIN 21 #define TFT_RST_PIN 46 // D9 = GPIO46 (boot strapping pin). Use -1 and tie RST to 3.3V if upload fails // Panel resolution in native (portrait) orientation - change to match your module #define TFT_WIDTH 240 #define TFT_HEIGHT 320 // ============================================= // Touch pin definitions (XPT2046 / HR2046 SPI touch controller) // ============================================= #define TOUCH_CS_PIN 18 // T_CS (any GPIO) #define TOUCH_IRQ_PIN -1 // T_IRQ (any GPIO, or -1 if not connected) // ============================================= // ============================================= // Create display object (uncomment matching driver) // ============================================= // DIYables_ILI9341_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); // DIYables_ILI9488_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); DIYables_ST7789_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN); // Minimum pressure to count as a valid touch. #define TOUCH_Z_MIN 10 // How many consecutive valid samples required before a corner is accepted. #define SAMPLES_NEEDED 10 // Delay between samples (ms). #define SAMPLE_DELAY_MS 30 #define DOT_RADIUS 12 #define BLACK DIYables_TFT_SPI::colorRGB( 0, 0, 0) #define WHITE DIYables_TFT_SPI::colorRGB(255, 255, 255) #define RED DIYables_TFT_SPI::colorRGB(255, 0, 0) // Corner pixel positions — filled in setup() once display size is known. // Order: 0=top-left, 1=top-right, 2=bottom-right, 3=bottom-left int cx[4], cy[4]; // Captured averaged raw values per corner. int cap_x[4], cap_y[4]; // ----------------------------------------------------------------------- void drawDot(int corner, bool on) { uint16_t color = on ? RED : WHITE; TFT_display.fillCircle(cx[corner], cy[corner], DOT_RADIUS, color); TFT_display.setTextSize(2); TFT_display.setTextColor(BLACK, color); TFT_display.setCursor(cx[corner] - 6, cy[corner] - 8); TFT_display.print(corner + 1); } void captureCorner(int corner) { const char* names[] = { "Top-left", "Top-right", "Bottom-right", "Bottom-left" }; Serial.println(); Serial.print("Corner "); Serial.print(corner + 1); Serial.print(" ("); Serial.print(names[corner]); Serial.println(")"); Serial.println(" Press and HOLD firmly on the blinking dot."); Serial.println(" Keep holding until you see 'Captured!'"); unsigned long lastBlink = 0; unsigned long lastPrint = 0; bool dotOn = false; int goodSamples = 0; long sumX = 0, sumY = 0; while (true) { // Blink the dot if (millis() - lastBlink > 400) { lastBlink = millis(); dotOn = !dotOn; drawDot(corner, dotOn); } int raw_x, raw_y, z; TFT_display.readTouchRaw(raw_x, raw_y, z); // Print live readings every 500 ms if (millis() - lastPrint > 500) { lastPrint = millis(); Serial.print(" Z="); Serial.print(z); Serial.print(" X="); Serial.print(raw_x); Serial.print(" Y="); Serial.println(raw_y); } if (z >= TOUCH_Z_MIN) { sumX += raw_x; sumY += raw_y; goodSamples++; if (goodSamples >= SAMPLES_NEEDED) { cap_x[corner] = sumX / goodSamples; cap_y[corner] = sumY / goodSamples; Serial.print(" Captured! raw_x="); Serial.print(cap_x[corner]); Serial.print(" raw_y="); Serial.println(cap_y[corner]); drawDot(corner, false); delay(500); return; } } else { goodSamples = 0; sumX = 0; sumY = 0; } delay(SAMPLE_DELAY_MS); } } // ----------------------------------------------------------------------- void setup() { Serial.begin(9600); TFT_display.begin(); TFT_display.setRotation(0); // Always calibrate in rotation 0 TFT_display.fillScreen(WHITE); TFT_display.initTouchSPI(TOUCH_CS_PIN, TOUCH_IRQ_PIN); // If touch X is mirrored on your board, uncomment the line below // BEFORE calibrating (so the printed values match your panel): //TFT_display.setTouchInvertX(true); // If touch Y is mirrored on your board, uncomment: //TFT_display.setTouchInvertY(false); int w = TFT_display.width(); int h = TFT_display.height(); int m = DOT_RADIUS + 4; cx[0] = m; cy[0] = m; cx[1] = w - m; cy[1] = m; cx[2] = w - m; cy[2] = h - m; cx[3] = m; cy[3] = h - m; Serial.println("=== Touch Calibration ==="); for (int i = 0; i < 4; i++) { captureCorner(i); } // Derive calibration values from the four corners int min_x = (cap_x[0] + cap_x[3]) / 2; // left edge int max_x = (cap_x[1] + cap_x[2]) / 2; // right edge int min_y = (cap_y[0] + cap_y[1]) / 2; // top edge int max_y = (cap_y[2] + cap_y[3]) / 2; // bottom edge Serial.println(); Serial.println("=== Calibration Results ==="); Serial.print(" Left X (min_x): "); Serial.println(min_x); Serial.print(" Right X (max_x): "); Serial.println(max_x); Serial.print(" Top Y (min_y): "); Serial.println(min_y); Serial.print(" Bot Y (max_y): "); Serial.println(max_y); Serial.println(); Serial.println("Copy this line into your sketch:"); Serial.print(" TFT_display.setTouchCalibration("); Serial.print(min_x); Serial.print(", "); Serial.print(max_x); Serial.print(", "); Serial.print(min_y); Serial.print(", "); Serial.print(max_y); Serial.println(");"); TFT_display.fillScreen(WHITE); TFT_display.setTextColor(BLACK); TFT_display.setTextSize(2); TFT_display.setCursor(10, 10); TFT_display.println("Done! Check"); TFT_display.setCursor(10, 35); TFT_display.println("Serial Monitor"); } void loop() {}

How to Run

  1. Wire the touch controller as described in the Touch Get Point section.
  2. Connect the board with the USB Type-C cable.
  3. Upload — paste the code, check the board and port, and press Upload.
  4. Open the Serial Monitor at 9600 baud and follow the dots. Press and hold each corner dot until you see "Captured!".
  5. Copy the result — paste the printed setTouchCalibration() line into your other touch sketches.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32S3 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32S3 Dev Module' on 'COM15')
New Line
9600 baud
=== Touch Calibration === Corner 1 (Top-left) Press and HOLD firmly on the blinking dot. Keep holding until you see 'Captured!' Z=0 X=0 Y=0 Captured! raw_x=362 raw_y=341 ... === Calibration Results === Left X (min_x): 358 Right X (max_x): 3712 Top Y (min_y): 336 Bot Y (max_y): 3689 Copy this line into your sketch: TFT_display.setTouchCalibration(358, 3712, 336, 3689);
Ln 11, Col 1
ESP32S3 Dev Module on COM15
2

Method Quick Reference

Method What It Does Usage
initTouchSPI(cs, irq) Initialize the XPT2046 touch controller. TFT_display.initTouchSPI(18, -1);
readTouchRaw(x, y, z) Read raw ADC values to measure the calibration range. TFT_display.readTouchRaw(x, y, z);
setTouchCalibration(minX,maxX,minY,maxY) Store the calibration values so getTouch() maps correctly to pixel coordinates. TFT_display.setTouchCalibration(200, 3800, 300, 3700);
setTouchInvertX(invert) / setTouchInvertY(invert) Flips the touch axis when X or Y is mirrored on your specific panel or batch. Call BEFORE running calibration so the stored values match your panel. TFT_display.setTouchInvertY(true);

Custom SPI

Sometimes you want to choose the SPI bus yourself, for example when another device already uses the default bus, or when you want a different SPI speed. This example passes an SPIClass object to the display constructor.

The ESP32 S3 Uno has its default SPI bus (FSPI) on D11, D13 and D12. It also has a second bus, HSPI, which has no fixed pins. To use it, uncomment the HSPI lines and give your own pins to hspi.begin(). As written, the example passes &SPI, so it runs on the default bus with the normal wiring.

/* * This ESP32 S3 Uno code was developed by newbiely.com * * This ESP32 S3 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3-uno/esp32-s3-uno-tft-lcd-touch-display-spi */ /* Created by DIYables This example code is in the public domain Product page: https://diyables.io This example demonstrates how to use a custom (non-default) SPI bus with the DIYables TFT SPI library. This is useful on boards that have multiple SPI interfaces, such as: - ESP32 S3 Uno: FSPI (default SPI) / HSPI - Arduino Giga / Portenta: SPI1 - Raspberry Pi Pico: SPI1 */ // ============================================= // Single include brings in the base class plus all driver classes. // ============================================= #include <DIYables_TFT_SPI.h> // ============================================= // Wiring (ESP32 S3 Uno - default SPI bus) // --------------------------------------------- // NOTE: On the ESP32 S3 Uno, the default SPI bus (FSPI) is on D11/D13/D12 // (GPIO11/GPIO12/GPIO13). A second bus (HSPI) is also available - it has // no fixed pins, so you choose free GPIOs and pass them to hspi.begin(). // // TFT module ESP32 S3 Uno // ------------ --------------------------------- // VCC -> 3.3V // GND -> GND // CS -> D10 (GPIO10) (TFT_CS_PIN) // RESET -> D9 (GPIO46) (TFT_RST_PIN) - boot strapping pin // DC / RS -> D8 (GPIO21) (TFT_DC_PIN) // SDI / MOSI -> D11 (GPIO11) (hardware SPI MOSI) // SCK -> D13 (GPIO12) (hardware SPI SCK) // LED -> 3.3V (or any GPIO via initBacklight) // SDO / MISO -> D12 (GPIO13) (only needed when reading from display) // ============================================= // ============================================= // SPI pin definitions (adjust for your board) // ============================================= #define TFT_CS_PIN 10 #define TFT_DC_PIN 21 #define TFT_RST_PIN 46 // D9 = GPIO46 (boot strapping pin). Use -1 and tie RST to 3.3V if upload fails // Panel resolution in native (portrait) orientation - change to match your module #define TFT_WIDTH 240 #define TFT_HEIGHT 320 // ============================================= // Select alternate SPI bus (uncomment for your board) // ============================================= // --- ESP32 S3 Uno: use HSPI --- // SPIClass hspi(HSPI); // #define MY_SPI &hspi // --- Arduino Giga / Portenta / RP2040: use SPI1 --- // #define MY_SPI &SPI1 // --- Default SPI (fallback) --- #define MY_SPI &SPI // ============================================= // Create display object with custom SPI bus // (uncomment matching driver) // ============================================= // DIYables_ILI9341_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN, MY_SPI); // DIYables_ILI9488_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN, MY_SPI); DIYables_ST7789_SPI TFT_display(TFT_WIDTH, TFT_HEIGHT, TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN, MY_SPI); #define BLACK DIYables_TFT_SPI::colorRGB(0, 0, 0) #define WHITE DIYables_TFT_SPI::colorRGB(255, 255, 255) #define RED DIYables_TFT_SPI::colorRGB(255, 0, 0) #define GREEN DIYables_TFT_SPI::colorRGB(0, 255, 0) #define BLUE DIYables_TFT_SPI::colorRGB(0, 0, 255) void setup() { Serial.begin(9600); // For ESP32 S3 Uno HSPI: set the bus pins before begin() // hspi.begin(15, 16, 35, -1); // SCK, MISO, MOSI, SS (extra pin holes) TFT_display.begin(); TFT_display.setRotation(1); // Landscape TFT_display.fillScreen(BLACK); uint16_t w = TFT_display.width(); uint16_t h = TFT_display.height(); // Draw a simple test pattern TFT_display.fillRect(0, 0, w / 3, h, RED); TFT_display.fillRect(w / 3, 0, w / 3, h, GREEN); TFT_display.fillRect(w * 2 / 3, 0, w / 3, h, BLUE); TFT_display.setTextColor(WHITE); TFT_display.setTextSize(2); TFT_display.setCursor(10, h / 2 - 10); TFT_display.print("Custom SPI bus OK"); } void loop() { // Nothing to do }

How to Run

  1. Wire the TFT display to the ESP32 S3 Uno as shown in the wiring diagram.
  2. Connect the board with the USB Type-C cable.
  3. Upload — paste the code, check the board and port, and press Upload.
  4. Check the screen — it shows red, green and blue bars with the text "Custom SPI bus OK".

Method Quick Reference

Method What It Does Usage
DIYables_ILI9341_SPI(w,h,cs,dc,rst,spi) Constructor variant that accepts a pointer to any SPIClass. Defaults to &SPI when the argument is omitted. DIYables_ILI9341_SPI tft(240, 320, 10, 21, 46, &SPI);
begin() Initialize the display on the configured SPI bus. TFT_display.begin();

Platform Support

The library only uses standard Arduino APIs (SPI, digitalWrite, millis). So it runs on all Arduino-compatible boards, including the ESP32 S3 Uno with the esp32 by Espressif Systems core.

FAQ

Which ESP32 S3 Uno pins does the SPI TFT display use?

The display uses the default hardware SPI bus: MOSI on D11 (GPIO11), SCK on D13 (GPIO12) and MISO on D12 (GPIO13). CS, DC and RST use D10 (GPIO10), D8 (GPIO21) and D9 (GPIO46). In code, always write the GPIO number, for example 21 for D8.

Do I need a level converter between the ESP32 S3 Uno and the TFT?

No. The ESP32 S3 Uno already uses 3.3V logic, which is what the TFT chip expects. Just make sure no 5V signal goes back into the board, because ESP32 S3 Uno pins are not 5V tolerant. Powering the module from 3.3V is the safest way.

Why does my board not upload when the display is connected?

The RST wire goes to D9, which is GPIO46, a boot strapping pin. A pull-up on the display's RST line can block upload mode. Unplug the RST wire during upload, or tie RST to 3.3V and set TFT_RST_PIN to -1 in the code.

Can I use the same code as on the UNO R4?

Almost. The library and drawing code are the same. Only the pin numbers change, because the ESP32 S3 Uno uses GPIO numbers: D8 becomes 21, D9 becomes 46, D2 becomes 18 and D4 becomes 19. D10 stays 10. In the Arduino IDE, select ESP32S3 Dev Module instead of an UNO R4 board.

Can I use an 8-bit parallel TFT shield on the ESP32 S3 Uno instead?

A parallel shield is faster, but it uses many pins and expects the classic Uno pin functions. The SPI module in this tutorial needs only a few wires and works well with the ESP32 S3 Uno. Check the shield's voltage before you plug it in, because ESP32 S3 Uno pins are not 5V tolerant.

What if my project needs more pins than the Uno headers give?

The ESP32 S3 Uno has two extra rows of holes with more GPIOs. GPIO15 and GPIO16 support I/O, PWM and analog. GPIO45 and GPIO35 to GPIO42 support I/O and PWM. GPIO47 and GPIO48 are output only, with PWM. Solder pin headers to use them and write the GPIO number in code. For this project, GPIO35 to GPIO42 are good for extra CS pins (SD card, second display) or for T_IRQ, and they also work as HSPI bus pins. Be careful with two things. First, GPIO47 and GPIO48 may run at 1.8V instead of 3.3V on some modules (especially ones with "V" in the name, like R8V or R16V), so 3.3V devices can be damaged or act strangely. Second, GPIO0, GPIO3 (D6), GPIO45 and GPIO46 (D9) are boot strapping pins, and a wrong connection can stop the board from booting or uploading code. Use the other extra pins first.

Troubleshooting

Problem Possible Cause Solution
No COM port in Arduino IDE USB driver missing or board not in upload mode Install the CP210x or CH340 driver, try another USB cable, or hold BOOT while you press RESET
Upload fails only when the display is wired TFT RST on D9 (GPIO46) holds a boot strapping pin high Unplug the RST wire during upload, or tie RST to 3.3V and set TFT_RST_PIN to -1
Black or white screen CS, DC, MOSI or SCK not connected, or no power Check every wire, and make sure VCC, GND and the backlight (LED) are connected
Wrong colors or garbled picture Wrong driver line is active Keep only the constructor for your chip (ILI9341, ILI9488 or ST7789) and comment out the others
Shifted or clipped image Width and height in the constructor do not match the panel Set TFT_WIDTH and TFT_HEIGHT to the real panel size
Touch does nothing T_CS not on D2 (GPIO18) or panel has no touch chip Check the T_CS wire and the TOUCH_CS_PIN value, and make sure your module is a touch version
Touch dot appears in the wrong place Calibration values do not fit your panel Run the TouchCalibration example, and use setTouchInvertX() or setTouchInvertY() if an axis is mirrored
"SD card initialization failed!" SD_CS wire wrong, card not inserted or wrong format Connect SD_CS to D4 (GPIO19), insert the card and format it as FAT32

※ OUR MESSAGES

  • As freelancers, We are AVAILABLE for HIRE. See how to outsource your project to us
  • Please feel free to share the link of this tutorial. However, Please do not use our content on any other websites. We invested a lot of effort and time to create the content, please respect our work!