The Arduino Uno R4 - available as Minima and WiFi - brings a 48 MHz Renesas RA4M1 processor and more SRAM than the classic Uno, while keeping the same header layout. The hardware SPI pins are in the same physical locations, so any TFT wiring that works on the Uno R3 works directly on the Uno R4 without any changes.
In this guide you will:
Wire an SPI TFT display to the Arduino Uno R4.
Draw shapes and fill areas with color.
Display text and numbers on screen.
Draw bitmap images stored in program memory (PROGMEM).
Draw bitmap images loaded from an SD card.
Render text using a custom external font.
Read raw touch coordinates from an XPT2046 controller.
Draw on screen by dragging a finger across the display.
Create interactive touch buttons.
Calibrate the touch screen.
Use a secondary or custom SPI bus for the display.
This guide covers both touch and non-touch SPI TFT LCD displays. It works with 1.3, 1.54, 2.2, 2.4, 2.8, 3.2, and 3.5 inch panels driven by ILI9341, ILI9488, or ST7789 controller chips.
Tip — simpler and faster alternative: If you are using an Arduino Uno R4, consider the TFT Touch Shield instead. It plugs directly onto the Uno R4 header — no jumper wires required. It uses an 8-bit parallel interface, which transfers pixel data significantly faster than SPI.
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 driven by a controller chip over the SPI bus. The DIYables_TFT_SPI library supports three common driver chips:
ILI9341 - 16-bit RGB565 color, up to 40 MHz SPI.
ILI9488 - 18-bit RGB666 color, up to 24 MHz SPI.
ST7789 - 16-bit RGB565 color, up to 40 MHz SPI.
Recommendation: If you have not yet purchased a display, we recommend the ST7789 driver. It is widely available, runs at full 40 MHz SPI speed, and is the most straightforward choice for new projects.
The library extends Adafruit GFX, so all standard drawing and text functions are available.
Pinout
Most SPI TFT LCD displays have the following pins:
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 (MCU → display)
SCK / CLK
SPI clock
MISO / SDO
SPI data out (display → MCU) — optional for display-only use
LED / BL / BLK
Backlight power — connect to 3.3V or a PWM pin for dimming
SD card pins (if your application needs to access the SD card):
Pin
Function
SD_CS / TF_CS
SD card Chip Select
MOSI / SDI
MOSI — data from MCU to SD card
SCK / CLK
SCK — SPI clock
MISO / SDO
MISO — data from SD card to MCU
For TFT displays that support touch, there are additional touch pins (if your application uses the touch function and the display supports it):
Pin
Function
T_CS
Touch controller Chip Select
T_CLK
SCK — SPI clock
T_DIN
MOSI — data from MCU to touch controller
T_DO
MISO — data from touch controller to MCU
T_IRQ
Touch interrupt — optional; signals when the screen is being touched
Note: Some non-touch display modules also expose T_CS, T_CLK, T_DIN, T_DO, and T_IRQ pins. These are non-functional on those boards — the touch controller IC is not populated. They appear because the PCB reuses the same layout as the touch-enabled version to reduce manufacturing variants.
Wiring Diagram
Note — level converter optional: These SPI TFT display modules include on-board level shifting on their signal lines, so the 5V logic from the Arduino Uno R4 can drive MOSI, SCK, CS, DC, RST, and T_CS (if using touch) directly — this wiring is tested and works. For a permanent build, adding a 5V to 3.3V Level Converter on the MCU-driven signal lines is still the safer choice for long-term reliability. The MISO line (display → MCU) never needs one.
Note — SDO (MISO) is optional: The SDO (MISO) line only carries data from the display back to the board. None of the example code in this tutorial reads from the display, so this pin can be left unconnected. On modules where the touch controller shares the same SPI bus, a connected SDO line can interfere with communication, so leaving it unconnected is recommended.
Without Touch
Connect MOSI to D11, SCK to D13, MISO to D12 on the Uno R4. CS, DC, and RST can be any available GPIO — D10, D8, D9 are used in the examples.
Display:
TFT Pin
Arduino Uno R4 Pin
Description
VCC
3.3V or 5V
Power supply
GND
GND
Ground
CS
D10
Chip Select
DC / RS
D8
Data / Command select
RST
D9
Reset (optional)
MOSI / SDI
D11
Hardware SPI MOSI
SCK
D13
Hardware SPI clock
MISO / SDO
D12
Hardware SPI MISO (optional)
LED / BL
3.3V
Backlight power
SD card (if your application needs to access the SD card):
SD Pin
Arduino Uno R4 Pin
Description
SD_CS / TF_CS
any free GPIO
SD card Chip Select
MOSI / SDI
D11
Shared with display MOSI (D11)
SCK / CLK
D13
Shared with display SCK (D13)
MISO / SDO
D12
Shared with display MISO (D12)
This image is created using Fritzing. Click to enlarge image
With Touch
Connect the XPT2046 touch controller to the Arduino Uno R4 SPI bus, sharing D11, D13, and D12 with the display.
Display:
TFT Pin
Arduino Uno R4 Pin
Description
VCC
3.3V or 5V
Power supply
GND
GND
Ground
CS
D10
Chip Select
DC / RS
D8
Data / Command select
RST
D9
Reset (optional)
MOSI / SDI
D11
Hardware SPI MOSI
SCK
D13
Hardware SPI clock
MISO / SDO
D12
Hardware SPI MISO (optional)
LED / BL
3.3V
Backlight power
Touch controller (if your application uses the touch function and the display supports it):
Touch Pin
Arduino Uno R4 Pin
Description
T_CS
any free GPIO
Touch Chip Select
T_IRQ
any free GPIO
Touch interrupt (optional)
T_DIN
D11
Shared with display MOSI (D11)
T_CLK
D13
Shared with display SCK (D13)
T_DO
D12
Shared with display MISO (D12)
This image is created using Fritzing. Click to enlarge image
If your MCU has two or more hardware SPI interfaces, you can assign each peripheral (display, SD card, touch controller) to its own dedicated SPI bus. If your MCU has only one hardware SPI interface, all three peripherals share the same three data lines (MOSI, SCK, MISO) — on the Uno R4 these are D11, D13, and D12. Each peripheral has its own CS pin, so only one is active at a time. The DIYables_TFT_SPI library manages both the display and the XPT2046 touch controller through a single API — no separate SPI library is needed for the touch side.
How to Install the Library
Plug the Arduino Uno R4 into your computer using a USB Type-C cable.
In the Arduino IDE, verify that the correct board (Arduino Uno R4 Minima or WiFi) and serial port are selected.
Click the Libraries icon in the left sidebar.
Type "DIYables_TFT_SPI" into the search box and locate the library published by DIYables.
Hit Install to add the library. When prompted, also install the Adafruit GFX Library dependency.
Search for DIYables TFTSPI created by DIYables.io and click the Install button.
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
Arduino Uno R4 WiFi
Library Manager
DIYables TFT SPI
Type:
All
Topic:
All
DIYables TFT SPIby 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
voidsetup() {
Output
Serial Monitor
Ln 1, Col 1
Arduino Uno R4 WiFi on COM15
1
Minimal Sketch Structure
All sketches using the DIYables_TFT_SPI library share this skeleton:
#include <DIYables_TFT_SPI.h>#define TFT_CS_PIN 10#define TFT_DC_PIN 8#define TFT_RST_PIN 9// 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);voidsetup() {TFT_display.begin();TFT_display.setRotation(1);}voidloop() {// drawing code here}
Pass the physical pixel dimensions of your panel to the constructor. Modules with the same driver chip come in different sizes.
Draw Shapes
The DrawShapes example demonstrates the Adafruit GFX drawing primitives: circles, triangles, rectangles, rounded rectangles, and lines.
/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-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 (Arduino Uno / Nano)// ---------------------------------------------// TFT module Arduino Uno / Nano// ------------ ---------------------------------// VCC -> 5V// GND -> GND// CS -> D10 (TFT_CS_PIN)// RESET -> D9 (TFT_RST_PIN)// DC / RS -> D8 (TFT_DC_PIN)// SDI / MOSI -> D11 (hardware SPI MOSI)// SCK -> D13 (hardware SPI SCK)// LED -> 3.3V (or any GPIO via initBacklight)// SDO / MISO -> D12 (only needed when reading from display)// =============================================// =============================================// SPI pin definitions (adjust for your board)// =============================================#define TFT_CS_PIN 10#define TFT_DC_PIN 8#define TFT_RST_PIN 9// 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 diamondvoid 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);}voidsetup() {TFT_display.begin();TFT_display.setRotation(1); // Landscape}voidloop() {TFT_display.fillScreen(WHITE);uint16_t w = TFT_display.width();uint16_t h = TFT_display.height();// Scale positions relative to screen size with better spacingint 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 circleTFT_display.drawCircle(col1, row1, 30, RED);// Filled circleTFT_display.fillCircle(col2, row1, 30, RED);// Outlined triangleTFT_display.drawTriangle(col3 - 30, row1 + 25, col3 + 30, row1 + 25, col3, row1 - 25, BLUE);// Filled triangleTFT_display.fillTriangle(col4 - 30, row1 + 25, col4 + 30, row1 + 25, col4, row1 - 25, GREEN);// Outlined rectangleTFT_display.drawRect(col1 - 35, row2 - 20, 70, 40, ORANGE);// Filled rectangleTFT_display.fillRect(col2 - 35, row2 - 20, 70, 40, TURQUOISE);// Outlined round rectangleTFT_display.drawRoundRect(col3 - 35, row2 - 20, 70, 40, 10, VIOLET);// Filled round rectangleTFT_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
Wire the TFT module to the Arduino Uno R4 as shown in the wiring diagram.
Plug in the USB Type-C cable.
Paste the code in Arduino IDE, choose the correct board and port, and press Upload.
The display shows a grid of colored shapes that refreshes every few 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
The ShowTextAndNumber example shows how to print strings and numbers at different sizes, colors, and positions.
/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-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 (Arduino Uno / Nano)// ---------------------------------------------// TFT module Arduino Uno / Nano// ------------ ---------------------------------// VCC -> 5V// GND -> GND// CS -> D10 (TFT_CS_PIN)// RESET -> D9 (TFT_RST_PIN)// DC / RS -> D8 (TFT_DC_PIN)// SDI / MOSI -> D11 (hardware SPI MOSI)// SCK -> D13 (hardware SPI SCK)// LED -> 3.3V (or any GPIO via initBacklight)// SDO / MISO -> D12 (only needed when reading from display)// =============================================// =============================================// SPI pin definitions (adjust for your board)// =============================================#define TFT_CS_PIN 10#define TFT_DC_PIN 8#define TFT_RST_PIN 9// 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)voidsetup() {Serial.begin(9600);Serial.println(F("TFT SPI Display - Show text and numbers"));TFT_display.begin();TFT_display.setRotation(1); // LandscapeTFT_display.fillScreen(WHITE);// Set text color and sizeTFT_display.setTextColor(MAGENTA);TFT_display.setTextSize(3);// Sample valuesfloat temperature = 23.5;float humidity = 78.6;// Display temperatureTFT_display.setCursor(20, 20);TFT_display.print("Temperature: ");TFT_display.print(temperature, 1);TFT_display.print(char(247));TFT_display.println("C");// Display humidityTFT_display.setCursor(20, 60);TFT_display.print("Humidity: ");TFT_display.print(humidity, 1);TFT_display.print("%");}voidloop() {}
How to Run
Wire and upload as above.
The display prints several lines of text and numeric values.
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
The DrawImage example displays a full-color RGB565 bitmap image stored in program memory. The image data is declared in bitmap.h as a constuint16_t array marked PROGMEM. This keeps the pixel data in flash rather than SRAM, so the display works correctly even on boards with limited RAM. Copy bitmap.h into the sketch folder before compiling.
/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-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 (Arduino Uno / Nano)// ---------------------------------------------// TFT module Arduino Uno / Nano// ------------ ---------------------------------// VCC -> 5V// GND -> GND// CS -> D10 (TFT_CS_PIN)// RESET -> D9 (TFT_RST_PIN)// DC / RS -> D8 (TFT_DC_PIN)// SDI / MOSI -> D11 (hardware SPI MOSI)// SCK -> D13 (hardware SPI SCK)// LED -> 3.3V (or any GPIO via initBacklight)// SDO / MISO -> D12 (only needed when reading from display)// =============================================// =============================================// SPI pin definitions (adjust for your board)// =============================================#define TFT_CS_PIN 10#define TFT_DC_PIN 8#define TFT_RST_PIN 9// 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;voidsetup() {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);}voidloop() {delay(2000);TFT_display.invertDisplay(true);delay(2000);TFT_display.invertDisplay(false);}
How to Run
Place bitmap.h in the same folder as the sketch file.
Wire the TFT module to the Arduino Uno R4 as shown in the wiring diagram.
Plug in the USB Type-C cable.
Paste the code in Arduino IDE, confirm the board and port, and press Upload.
The display shows the bitmap image loaded from program memory.
Method Quick Reference
Method
What It Does
Usage
drawRGBBitmap(x,y,bitmap,w,h)
Draw an RGB565 PROGMEM bitmap starting at pixel (x, y).
The DrawImageSDcard example reads a raw RGB565 binary image file from a micro SD card and pushes pixel data directly to the display in small chunks. This approach streams the image without ever holding the full image in RAM, making it practical for large panels.
Connect the SD card module to the same SPI bus as the display. Share MOSI (D11), SCK (D13), and MISO (D12). Assign a separate CS pin to the SD module — define it as SD_CS_PIN in the sketch.
/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-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 (Arduino Uno / Nano)// ---------------------------------------------// TFT + SD module Arduino Uno / Nano// ----------------------- ---------------------------------// VCC -> 5V// GND -> GND// TFT CS -> D10 (TFT_CS_PIN)// TFT RESET -> D9 (TFT_RST_PIN)// TFT DC / RS -> D8 (TFT_DC_PIN)// SD CS -> D4 (SD_CS)// SDI / MOSI (shared) -> D11 (hardware SPI MOSI)// SDO / MISO (shared) -> D12 (hardware SPI MISO)// SCK (shared) -> D13 (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 8#define TFT_RST_PIN 9// Panel resolution in native (portrait) orientation - change to match your module#define TFT_WIDTH 240#define TFT_HEIGHT 320#define SD_CS 4 // 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 20File bmpFile;uint16_t SCREEN_WIDTH;uint16_t SCREEN_HEIGHT;// Helper functions to read BMP file headeruint16_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(constchar *filename, uint32_t &w, uint32_t &h) {File f = SD.open(filename);if (!f) returnfalse;if (read16(f) != 0x4D42) { f.close(); returnfalse; } 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();returntrue;}void drawBMP(constchar *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); // Reserveduint32_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");}voidsetup() {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"); }}voidloop() {}
How to Run
Wire the SD module to the Arduino Uno R4. Share the SPI bus (D11/D13/D12) with the display and connect SD CS to your chosen pin.
Copy a raw RGB565 binary image file to the root of the SD card. Image dimensions must match the panel resolution.
Plug in the USB Type-C cable.
Paste the code in Arduino IDE, confirm board and port, and press Upload.
The image is read from the card and displayed on screen.
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 UseExternalFont example renders on-screen text using a custom Adafruit GFX-compatible font. Compared to the built-in 5×7 bitmap font, custom fonts provide better readability and styling. The font file is included as a header and activated with a single call to setFont().
/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-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 (Arduino Uno / Nano)// ---------------------------------------------// TFT module Arduino Uno / Nano// ------------ ---------------------------------// VCC -> 5V// GND -> GND// CS -> D10 (TFT_CS_PIN)// RESET -> D9 (TFT_RST_PIN)// DC / RS -> D8 (TFT_DC_PIN)// SDI / MOSI -> D11 (hardware SPI MOSI)// SCK -> D13 (hardware SPI SCK)// LED -> 3.3V (or any GPIO via initBacklight)// SDO / MISO -> D12 (only needed when reading from display)// =============================================// =============================================// SPI pin definitions (adjust for your board)// =============================================#define TFT_CS_PIN 10#define TFT_DC_PIN 8#define TFT_RST_PIN 9// 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)voidsetup() {Serial.begin(9600);Serial.println(F("TFT SPI Display - Use external font"));TFT_display.begin();TFT_display.setFont(&FreeSansBold12pt7b);TFT_display.setRotation(1); // LandscapeTFT_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("%");}voidloop() {}
How to Run
Wire the TFT module to the Uno R4 as shown in the wiring diagram.
Plug in the USB Type-C cable.
Paste the code in Arduino IDE, confirm board and port, and press Upload.
The display renders text in the custom font. Notice the improved glyph quality compared to 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
The TouchGetPoint example reads raw ADC coordinates from an XPT2046 touch controller and prints them to the Serial Monitor. Running this first helps you understand the numeric range of your specific panel — data you will use to calibrate the touch in later examples.
Wiring: T_CLK→D13, T_DIN→D11, T_DO→D12 (shared SPI bus with display). T_CS→D7, T_IRQ→D6.
/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-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 (Arduino Uno / Nano)// =============================================// TFT pins (always required)// TFT module Arduino Uno / Nano// ------------ ---------------------------------// VCC -> 5V// GND -> GND// CS -> D10 (TFT_CS_PIN)// RESET -> D9 (TFT_RST_PIN)// DC / RS -> D8 (TFT_DC_PIN)// SDI / MOSI -> D11 (hardware SPI MOSI)// SCK -> D13 (hardware SPI SCK)// SDO / MISO -> D12 (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 Arduino Uno / Nano// ------------ ---------------------------------// T_CS -> D2 (TOUCH_CS_PIN)// T_IRQ -> D2 (TOUCH_IRQ_PIN, optional - use -1 to skip)// T_CLK -> D13 (shared with display SCK)// T_DIN -> D11 (shared with display MOSI)// T_DO -> D12 (shared with display MISO)// =============================================// =============================================// SPI pin definitions (adjust for your board)// =============================================#define TFT_CS_PIN 10#define TFT_DC_PIN 8#define TFT_RST_PIN 9// 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 2 // 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)voidsetup() {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.");}voidloop() {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
Wire the XPT2046 controller to the Uno R4, sharing the SPI bus with the display. T_CS to D7, T_IRQ to D6.
Plug in the USB Type-C cable.
Paste the code, confirm board and port, and press Upload.
Open the Serial Monitor at 9600 baud. Press and drag on the display to see raw X, Y, and pressure 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(7, 6);
readTouchRaw(x, y, z)
Return raw ADC touch values without applying any calibration. Returns true when pressed.
TFT_display.readTouchRaw(x, y, z);
Touch Draw
The TouchDraw example creates a simple finger-drawing application. Calibrated touch coordinates are used to place small filled circles at each point the finger contacts, building up a continuous drawing as it moves.
/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-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 (Arduino Uno / Nano)// =============================================// TFT pins (always required)// TFT module Arduino Uno / Nano// ------------ ---------------------------------// VCC -> 5V// GND -> GND// CS -> D10 (TFT_CS_PIN)// RESET -> D9 (TFT_RST_PIN)// DC / RS -> D8 (TFT_DC_PIN)// SDI / MOSI -> D11 (hardware SPI MOSI)// SCK -> D13 (hardware SPI SCK)// SDO / MISO -> D12 (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 Arduino Uno / Nano// ------------ ---------------------------------// T_CS -> D2 (TOUCH_CS_PIN)// T_IRQ -> D2 (TOUCH_IRQ_PIN, optional - use -1 to skip)// T_CLK -> D13 (shared with display SCK)// T_DIN -> D11 (shared with display MOSI)// T_DO -> D12 (shared with display MISO)// =============================================// =============================================// SPI pin definitions (adjust for your board)// =============================================#define TFT_CS_PIN 10#define TFT_DC_PIN 8#define TFT_RST_PIN 9// 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 2 // 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 3voidsetup() {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);}voidloop() {int x, y;if (TFT_display.getTouch(x, y)) {TFT_display.fillCircle(x, y, PEN_RADIUS, RED); }}
How to Run
Wire the XPT2046 to the Uno R4 as described in the Touch Get Point section above.
Plug in the USB Type-C cable.
Paste the code, confirm board and port, and press Upload.
Drag a finger across the display to paint on screen.
Method Quick Reference
Method
What It Does
Usage
initTouchSPI(cs, irq)
Set up the XPT2046 on the shared SPI bus.
TFT_display.initTouchSPI(7, 6);
setTouchCalibration(minX,maxX,minY,maxY)
Map raw ADC values to screen pixel coordinates. Get these values from the TouchCalibration example.
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
The TouchButton example draws rectangular buttons on the screen and checks touch coordinates against each button's bounds. When a button is tapped it highlights and performs an action. This demonstrates how to build a minimal touch-driven user interface.
The T_IRQ pin is optional for this example. Pass -1 as the irq argument if you prefer not to connect it.
/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-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 (Arduino Uno / Nano)// =============================================// TFT pins (always required)// TFT module Arduino Uno / Nano// ------------ ---------------------------------// VCC -> 5V// GND -> GND// CS -> D10 (TFT_CS_PIN)// RESET -> D9 (TFT_RST_PIN)// DC / RS -> D8 (TFT_DC_PIN)// SDI / MOSI -> D11 (hardware SPI MOSI)// SCK -> D13 (hardware SPI SCK)// SDO / MISO -> D12 (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 Arduino Uno / Nano// ------------ ---------------------------------// T_CS -> D2 (TOUCH_CS_PIN)// T_IRQ -> D2 (TOUCH_IRQ_PIN, optional - use -1 to skip)// T_CLK -> D13 (shared with display SCK)// T_DIN -> D11 (shared with display MOSI)// T_DO -> D12 (shared with display MISO)// =============================================// =============================================// SPI pin definitions (adjust for your board)// =============================================#define TFT_CS_PIN 10#define TFT_DC_PIN 8#define TFT_RST_PIN 9// 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 2 // 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 50bool lastPressed = false;bool stablePressed = false;unsignedlong 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 ");}voidsetup() {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);}voidloop() {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
Connect T_CS to D7. T_IRQ may be left unconnected — set it to -1 in the sketch.
Plug in the USB Type-C cable.
Paste the code, confirm board and port, and press Upload.
Tap the buttons displayed on screen. Each button changes color when pressed.
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(7, -1);
setTouchCalibration(minX,maxX,minY,maxY)
Apply calibration so getTouch() returns display-pixel coordinates.
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
The TouchCalibration example guides you through measuring the raw ADC range of your XPT2046 touch panel. Touch each corner when prompted and observe the minimum and maximum X and Y values printed to the Serial Monitor. Use those four numbers in the setTouchCalibration() call throughout all your other touch sketches.
/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-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 (Arduino Uno / Nano)// =============================================// TFT pins (always required)// TFT module Arduino Uno / Nano// ------------ ---------------------------------// VCC -> 5V// GND -> GND// CS -> D10 (TFT_CS_PIN)// RESET -> D9 (TFT_RST_PIN)// DC / RS -> D8 (TFT_DC_PIN)// SDI / MOSI -> D11 (hardware SPI MOSI)// SCK -> D13 (hardware SPI SCK)// SDO / MISO -> D12 (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 Arduino Uno / Nano// ------------ ---------------------------------// T_CS -> D2 (TOUCH_CS_PIN)// T_IRQ -> D2 (TOUCH_IRQ_PIN, optional - use -1 to skip)// T_CLK -> D13 (shared with display SCK)// T_DIN -> D11 (shared with display MOSI)// T_DO -> D12 (shared with display MISO)// =============================================// =============================================// SPI pin definitions (adjust for your board)// =============================================#define TFT_CS_PIN 10#define TFT_DC_PIN 8#define TFT_RST_PIN 9// 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 2 // 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-leftint cx[4], cy[4];// Captured averaged raw values per corner.int cap_x[4], cap_y[4];// -----------------------------------------------------------------------void drawDot(int corner, boolon) {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 dotif (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 msif (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); }}// -----------------------------------------------------------------------voidsetup() {Serial.begin(9600);TFT_display.begin();TFT_display.setRotation(0); // Always calibrate in rotation 0TFT_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 cornersint min_x = (cap_x[0] + cap_x[3]) / 2; // left edgeint max_x = (cap_x[1] + cap_x[2]) / 2; // right edgeint min_y = (cap_y[0] + cap_y[1]) / 2; // top edgeint max_y = (cap_y[2] + cap_y[3]) / 2; // bottom edgeSerial.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");}voidloop() {}
How to Run
Wire the XPT2046 to the Uno R4 as described in the Touch Get Point section.
Plug in the USB Type-C cable.
Paste the code, confirm board and port, and press Upload.
Open the Serial Monitor at 9600 baud. Follow the on-screen prompts to touch each corner of the display.
Record the four printed values and insert them into setTouchCalibration() in your other touch sketches.
Method Quick Reference
Method
What It Does
Usage
initTouchSPI(cs, irq)
Initialize the XPT2046 touch controller.
TFT_display.initTouchSPI(7, 6);
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.
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
The CustomSPI example shows how to supply an explicit SPIClass instance to the display constructor. Use this approach when the default SPI bus is busy with another peripheral, when you need to share the bus with additional configuration, or when you want to set a custom SPI clock frequency.
The Uno R4 Minima and WiFi each expose a single hardware SPI bus on D11/D13/D12. The example demonstrates passing &SPI explicitly and adjusting the maximum SPI clock speed.
/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-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: HSPI / VSPI - 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 (Arduino Uno / Nano - default SPI bus)// ---------------------------------------------// NOTE: Uno / Nano have only ONE hardware SPI bus, so this example is// most useful on boards with multiple buses (ESP32, Giga, RP2040...).// On Uno / Nano, MY_SPI must remain &SPI and the wiring is the standard// hardware SPI mapping below.//// TFT module Arduino Uno / Nano// ------------ ---------------------------------// VCC -> 5V// GND -> GND// CS -> D10 (TFT_CS_PIN)// RESET -> D9 (TFT_RST_PIN)// DC / RS -> D8 (TFT_DC_PIN)// SDI / MOSI -> D11 (hardware SPI MOSI)// SCK -> D13 (hardware SPI SCK)// LED -> 3.3V (or any GPIO via initBacklight)// SDO / MISO -> D12 (only needed when reading from display)// =============================================// =============================================// SPI pin definitions (adjust for your board)// =============================================#define TFT_CS_PIN 10#define TFT_DC_PIN 8#define TFT_RST_PIN 9// 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: 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)voidsetup() {Serial.begin(9600);// For ESP32 HSPI: optionally remap pins before begin()// hspi.begin(14, 12, 13, -1); // SCK, MISO, MOSI, SSTFT_display.begin();TFT_display.setRotation(1); // LandscapeTFT_display.fillScreen(BLACK);uint16_t w = TFT_display.width();uint16_t h = TFT_display.height();// Draw a simple test patternTFT_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");}voidloop() {// Nothing to do}
How to Run
Wire the TFT display to the Uno R4 as shown in the wiring diagram.
Plug in the USB Type-C cable.
Paste the code, confirm board and port, and press Upload.
The display starts up on the explicit SPI bus and renders a color-bar test pattern.
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.
Black screen - Confirm CS, DC, MOSI, and SCK are all connected. A missing CS line leaves the display inactive.
Wrong driver - Only one constructor line should be active. The others must be commented out.
Shifted or clipped image - The width and height in the constructor must match the physical panel size.
Touch not working - Run the TouchCalibration example to obtain calibration values specific to your panel.
Platform Support
The library relies exclusively on Arduino standard APIs (SPI, digitalWrite, millis) and runs on all Arduino-compatible platforms including the Uno R4 Minima and WiFi.
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!