Wiring:
Note: If TFT device is a 3.3V device, do not connect pins to 5V; use level shifters
TFT Vcc---->3.3V
TFT GND--->GND
TFT CS-----Level Shifter----->Pin 10
TFT RESET-----Lev el Shifter--->Pin 8
TFT DC/RS-----Level Shifter---->Pin 9
TFT SDI/MOSI----->Level Shifter--->Pin 11
TFT SCK------>Level Shifter---->Pin 13
TFT LCD-------->3.3V
TFT Device: 2pcs 2.4" SPI TFT LCD Display Screen Module ST7735 240×320 TFT LCD Screen Shield 3.3V RGB 65K for Arduino
Recommend: XIITIA 2pcs 2.4" SPI TFT LCD Display Touch Panel ILI9341 240x320 TFT LCD Touch Screen Shield 5V/3.3V STM32 Display Module SPI Serial with Touch Pen ( 5V tolerant)
Level Shifters: 5pcs 4 Channels IIC I2C Logic Level Converter Bi-Directional 3.3V-5V Shifter Module
Full Size Breadboard
10K Potentiometers
Based on: https://docs.arduino.cc/retired/library-examples/tft-library/TFTPong/ ; changed library used to Adafruit_ILI9341.h ; added level shifters
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <SPI.h>
// Pin Definitions
#define TFT_CS 10
#define TFT_RST 8
#define TFT_DC 9
#define PADDLE_1 A0
#define PADDLE_2 A1
// Initialize TFT for 2.4" Screen
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
// Game Variables
int paddle1Y, paddle2Y;
int ballX, ballY;
int ballXDir = 4; // Increased speed for larger screen
int ballYDir = 4;
const int PADDLE_WIDTH = 6; // Made slightly wider for 2.4" screen
const int PADDLE_HEIGHT = 40; // Made taller for higher resolution
void setup() {
tft.begin();
tft.setRotation(1); // Landscape mode
tft.fillScreen(ILI9341_BLACK);
pinMode(PADDLE_1, INPUT);
pinMode(PADDLE_2, INPUT);
// Center the ball based on actual screen size
ballX = tft.width() / 2;
ballY = tft.height() / 2;
}
void loop() {
// 1. ERASE using old coordinates
tft.fillRect(10, paddle1Y, PADDLE_WIDTH, PADDLE_HEIGHT, ILI9341_BLACK);
tft.fillRect(tft.width() - 15, paddle2Y, PADDLE_WIDTH, PADDLE_HEIGHT, ILI9341_BLACK);
tft.fillCircle(ballX, ballY, 4, ILI9341_BLACK);
// 2. UPDATE positions
paddle1Y = map(analogRead(PADDLE_1), 0, 1023, 0, tft.height() - PADDLE_HEIGHT);
paddle2Y = map(analogRead(PADDLE_2), 0, 1023, 0, tft.height() - PADDLE_HEIGHT);
ballX += ballXDir;
ballY += ballYDir;
// Wall Collision (Top/Bottom)
if (ballY <= 0 || ballY >= tft.height()) ballYDir *= -1;
// Paddle Collision
if (ballX <= 10 + PADDLE_WIDTH && ballY >= paddle1Y && ballY <= paddle1Y + PADDLE_HEIGHT) ballXDir *= -1;
if (ballX >= (tft.width() - 15) - PADDLE_WIDTH && ballY >= paddle2Y && ballY <= paddle2Y + PADDLE_HEIGHT) ballXDir *= -1;
// 3. RESET if out of bounds
if (ballX < 0 || ballX > tft.width()) {
ballX = tft.width() / 2;
ballY = tft.height() / 2;
ballXDir *= -1;
}
// 4. DRAW new positions
tft.fillRect(10, paddle1Y, PADDLE_WIDTH, PADDLE_HEIGHT, ILI9341_WHITE);
tft.fillRect(tft.width() - 15, paddle2Y, PADDLE_WIDTH, PADDLE_HEIGHT, ILI9341_WHITE);
tft.fillCircle(ballX, ballY, 4, ILI9341_WHITE);
delay(10); // Faster refresh for smoother gameplay
}
A TFT (Thin-Film Transistor) display is a type of active-matrix LCD that uses tiny transistors attached to each pixel to provide fast, high-quality, and sharp, colorful images. By controlling individual pixels, TFT screens, explained by this YouTube video, offer superior contrast and responsiveness compared to older passive displays, and are commonly used in smartphones, televisions, and industrial panels.
Riverdi +4
Key Features of TFT Displays
Active Matrix Technology: Unlike passive screens, every pixel (or sub-pixel) has its own transistor (or capacitor), providing improved image stability, faster refresh rates, and better response times.
High Quality & Brightness: TFTs are known for vibrant colors, high resolution, and, as noted on newhavendisplay.com, excellent readability, especially for text-heavy content
Transmissive Technology: They rely on a backlight to function, meaning they are "transmissive," which can make them power-hungry compared to emissive technologies like OLED.
Thin-Film Construction: The transistors are made from a thin layer of amorphous silicon deposited on a glass panel, as described on Wikipedia.
Another version, using an ESP32 and a 1.8" TFT Screen:
Another version, using an ESP32 and a 1.8" TFT Screen:
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include <SPI.h>
// Pin Definitions
// Vcc: 3.3V
//GND: GND
//BL: 3.3V
#define TFT_CS 5
#define TFT_RST 4
#define TFT_DC 2
#define TFT_MOSI 23 // SDA
#define TFT_SCLK 18 // SCL
// Potentiometer or Button for Paddle Control
#define PADDLE_INPUT 34 // Analog pin (e.g., GPIO 34 on ESP32)
// Initialize Adafruit ST7735
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
// Game Constants
const int PADDLE_W = 4;
const int PADDLE_H = 20;
const int BALL_SIZE = 3;
int score = 0;
// Game Variables
int paddleY, oldPaddleY;
int ballX, ballY, ballDX, ballDY;
int screenW, screenH;
void setup() {
tft.initR(INITR_BLACKTAB); // Initialize ST7735S chip, black tab
tft.setRotation(1); // Landscape mode
tft.fillScreen(ST77XX_BLACK);
screenW = tft.width();
screenH = tft.height();
resetBall();
tft.setTextColor(ST77XX_WHITE);
tft.setTextSize(1);
}
void loop() {
// 1. Erase old positions
tft.fillRect(10, oldPaddleY, PADDLE_W, PADDLE_H, ST77XX_BLACK);
tft.fillCircle(ballX, ballY, BALL_SIZE, ST77XX_BLACK);
// 2. Read Paddle Input (Potentiometer)
// Maps 0-4095 (ESP32 ADC) to screen height minus paddle height
int val = analogRead(PADDLE_INPUT);
paddleY = map(val, 0, 4095, 0, screenH - PADDLE_H);
// 3. Update Ball Position
ballX += ballDX;
ballY += ballDY;
// 4. Wall Collisions (Top, Bottom, Right)
if (ballY <= 0 || ballY >= screenH) ballDY *= -1;
if (ballX >= screenW) ballDX *= -1;
// 5. Paddle Collision
if (ballX <= 10 + PADDLE_W && ballY >= paddleY && ballY <= paddleY + PADDLE_H) {
ballDX *= -1;
ballX = 10 + PADDLE_W + 1; // Prevent sticking
score++;
drawScore();
}
// 6. Game Over (Ball goes past left side)
if (ballX < 0) {
gameOver();
}
// 7. Draw new positions
tft.fillRect(10, paddleY, PADDLE_W, PADDLE_H, ST77XX_WHITE);
tft.fillCircle(ballX, ballY, BALL_SIZE, ST77XX_GREEN);
oldPaddleY = paddleY;
delay(50); // Adjust for game speed
}
void resetBall() {
ballX = screenW / 2;
ballY = screenH / 2;
ballDX = 2;
ballDY = 2;
tft.fillScreen(ST77XX_BLACK);
drawScore();
}
void drawScore() {
tft.fillRect(screenW - 30, 0, 30, 10, ST77XX_BLACK);
tft.setCursor(screenW - 25, 0);
tft.print(score);
}
void gameOver() {
tft.fillScreen(ST77XX_RED);
tft.setCursor(screenW/4, screenH/2);
tft.setTextSize(2);
tft.print("GAME OVER");
delay(2000);
score = 0;
resetBall();
}