Introduction
Color TFT displays are a fantastic way to add rich visual feedback to your Arduino projects. In this tutorial, we’ll drive a 240x320 ILI9341-based TFT using the Adafruit ILI9341 and Adafruit GFX libraries. You’ll learn how to wire the display, install the required libraries, and write code that draws static UI elements and animates a bouncing ball. All of this runs right in your browser with the Velxio simulator—no hardware needed!
Circuit Walkthrough
Let’s start by examining the circuit. The schematic is straightforward: an Arduino Uno communicates with the ILI9341 display over SPI (Serial Peripheral Interface).

Here’s the complete wiring list:
| Arduino Uno Pin | TFT ILI9341 Pin | Wire Color |
|---|---|---|
| 13 (SCK) | SCK | Orange |
| 11 (MOSI) | MOSI | Orange |
| 10 (CS) | CS | Blue |
| 9 (D/C) | D/C | Green |
| 8 (RST) | RST | Red |
| 7 (LED) | LED | White |
| 5V | VCC | (not shown, assumed) |
| GND | GND | (not shown, assumed) |
- SCK (Serial Clock) and MOSI (Master Out Slave In) are the SPI data lines. They carry clock and data from the Arduino to the display.
- CS (Chip Select) tells the display when to listen to SPI commands. Active low.
- D/C (Data/Command) selects whether the SPI transmission is a command or pixel data.
- RST (Reset) resets the display controller. We connect it to a digital pin so we can perform a hardware reset if needed.
- LED controls the backlight. We’ll drive it HIGH to turn the backlight on.
No external components are required—the display runs at 5V logic (most ILI9341 modules are 5V tolerant) and the SPI pins are directly connected.
Code Walkthrough
Now let’s dive into the code. We’ll use the Adafruit ILI9341 library along with the Adafruit GFX library for graphics primitives. These libraries must be installed via the Library Manager.

Open the Library Manager (Tools > Manage Libraries) and search for “Adafruit ILI9341” and “Adafruit GFX Library”. Install both. The SPI library is included automatically.
Here’s the full code:
// TFT ILI9341 Display Demo (240x320)
// Library: Adafruit ILI9341 + Adafruit GFX
// Connect: CS=10, DC/RS=9, RST=8, LED=7, MOSI=11(SPI), SCK=13(SPI)
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <SPI.h>
#define TFT_CS 10
#define TFT_DC 9
#define TFT_RST 8
#define TFT_LED 7
Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);
// Background color: blue (visible on dark simulator canvas)
#define BG_COLOR 0x001F
// Ball state
int ballX = 120, ballY = 200;
int ballDX = 3, ballDY = 2;
const int BALL_R = 10;
// Play-field boundaries
const int FX = 5, FY = 110, FW = 230, FH = 200;
void drawStaticUI() {
tft.fillScreen(BG_COLOR);
// Title
tft.setTextSize(3);
tft.setTextColor(tft.color565(255, 220, 0));
tft.setCursor(20, 10);
tft.print("VELXIO TFT");
// Subtitle
tft.setTextSize(2);
tft.setTextColor(tft.color565(180, 180, 255));
tft.setCursor(30, 50);
tft.print("ILI9341 Demo");
// Color palette bars
tft.fillRect(10, 82, 70, 18, tft.color565(220, 50, 50));
tft.fillRect(85, 82, 70, 18, tft.color565(50, 200, 50));
tft.fillRect(160, 82, 70, 18, tft.color565(50, 100, 240));
// Play-field border
tft.drawRect(FX, FY, FW, FH, tft.color565(80, 80, 130));
}
void setup() {
pinMode(TFT_LED, OUTPUT);
digitalWrite(TFT_LED, HIGH);
tft.begin();
drawStaticUI();
}
void loop() {
// Erase old ball
tft.fillCircle(ballX, ballY, BALL_R, BG_COLOR);
// Update position
ballX += ballDX;
ballY += ballDY;
// Bounce off field borders
if (ballX < FX + BALL_R + 1 || ballX > FX + FW - BALL_R - 1) ballDX = -ballDX;
if (ballY < FY + BALL_R + 1 || ballY > FY + FH - BALL_R - 1) ballDY = -ballDY;
// Draw ball
tft.fillCircle(ballX, ballY, BALL_R, tft.color565(255, 140, 0));
delay(30);
}
Key Points in the Code
- Pin Definitions: We define
TFT_CS,TFT_DC,TFT_RST, andTFT_LEDas pins 10, 9, 8, and 7 respectively. These match our wiring. - Library Initialization:
Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);creates the display object. The library handles SPI communication internally. - Backlight Control: In
setup(), we set pin 7 HIGH to turn on the backlight. Without this, the screen would be dark. - Static UI:
drawStaticUI()fills the screen with a blue background, prints two lines of text, draws three colored rectangles as a palette, and outlines a play-field rectangle. - Animation Loop: In
loop(), we erase the ball by drawing a filled circle in the background color, update its position, check for collisions with the play-field borders, and redraw the ball in orange. Adelay(30)controls the frame rate. - Color Conversion:
tft.color565(r, g, b)converts 8-bit RGB values to the 16-bit 565 format used by the display.
Key Concepts for Students
- SPI Communication: The display uses SPI, a synchronous serial protocol. The Arduino is the master, the display is the slave. Only three wires (SCK, MOSI, CS) are needed for data; D/C and RST are additional control lines.
- Graphics Primitives: The Adafruit GFX library provides functions like
fillScreen(),fillRect(),drawRect(),fillCircle(),setCursor(),print(), andsetTextSize(). These make it easy to draw shapes and text. - Double Buffering (Concept): We erase the old ball before drawing the new one. This prevents smearing. In more advanced projects, you might use a frame buffer.
- Collision Detection: The bouncing ball logic checks if the ball’s center is within
BALL_Rpixels of the border. If so, the direction is reversed. - Color Depth: The ILI9341 supports 262K colors (18-bit), but the library uses 16-bit (65K colors) for speed.
color565()packs RGB into 5-6-5 bits.
Common Pitfalls & Debugging Tips
- Backlight Off: If the screen stays dark, ensure pin 7 is set HIGH. In the simulator, check the LED pin wire is connected.
- Wrong Pins: Double-check that CS, DC, RST, MOSI, and SCK match the code. A single miswire can cause garbled output or no display.
- Library Not Installed: The code won’t compile without the Adafruit libraries. Use the Library Manager to install them.
- SPI Conflicts: If you have other SPI devices, ensure their CS pins are not active simultaneously. Here, only the TFT uses SPI.
- Ball Disappears: If the ball leaves the play-field, check the collision bounds. The condition uses
FX + BALL_R + 1to account for the ball’s radius.
Suggested Extensions
Once you have the basic demo running, try these enhancements:
- Add a second ball with different speed and color.
- Display sensor data (e.g., from a potentiometer) as a bar graph or number.
- Create a simple game like Pong or a reaction timer.
- Use touch input if your display has a touch controller (e.g., XPT2046).
- Optimize performance by using
startWrite()andendWrite()to batch SPI transactions.
Try It Yourself!
Ready to see it in action? Open the live example in Velxio and hit the Run button.

You can also explore the canvas toolbar to zoom, pan, and inspect the simulation.

If you want to build this from scratch, use the component picker to add an Arduino Uno and an ILI9341 display.

Click the link below to open the example and start experimenting:
Open TFT ILI9341 Display Demo on Velxio
Happy coding!