Build Your Own Arduino Radar System with TFT Display
Project Overview
Create a professional-looking radar system using an Arduino Mega, ultrasonic sensor, servo motor, and TFT display. This project displays real-time object detection on a 2.8" color screen with classic radar sweep effects, making it perfect for educational demonstrations or impressive maker projects.
What You'll Build
- Standalone radar unit with built-in display
- 180-degree scanning with servo-mounted ultrasonic sensor
- Real-time object detection shown as red dots on green radar screen
- Professional interface with sweep lines, distance rings, and live data
- Complete portability - no computer required for operation
Components Required
Main Components
- Arduino Mega 2560 - Required for multiple pins and processing power
- 2.8" TFT SPI Display 240x320 (ILI9341 controller) - Shows radar interface
- HC-SR04 Ultrasonic Sensor - Measures distances up to 200cm
- SG90 Servo Motor - Rotates sensor through 180 degrees
Supporting Materials
- Jumper wires (male-to-male and male-to-female)
- Breadboard (half-size minimum)
- USB cable for Arduino programming
- 9V power adapter (recommended for stable servo operation)
- Small screws or strong tape for mounting sensor to servo
Estimated Cost
Total project cost: $35-50 USD depending on supplier and component quality.
Required Software Libraries
Before starting, install these libraries through the Arduino IDE Library Manager:
- Adafruit GFX Library - Provides graphics functions for drawing
- Adafruit ILI9341 - Controls the TFT display communication
- Servo - Built-in Arduino library (no installation needed)
Installation Steps
- Open Arduino IDE
- Navigate to Sketch → Include Library → Manage Libraries
- Search for "Adafruit GFX" and click Install
- Search for "Adafruit ILI9341" and click Install
- Close and restart Arduino IDE
Circuit Wiring Guide
Pin Connection Table
| Component | Pin | Arduino Mega Pin | Notes |
|---|---|---|---|
| TFT Display | |||
| VCC | 3.3V | CRITICAL: Use 3.3V, NOT 5V! | |
| GND | GND | Any ground pin | |
| CS | Pin 10 | Chip select | |
| RESET | Pin 9 | Reset control | |
| DC | Pin 8 | Data/command select | |
| SDI (MOSI) | Pin 11 | Serial data input | |
| SCK | Pin 13 | Serial clock | |
| LED | 3.3V | Backlight power | |
| SDO (MISO) | Pin 12 | Serial data output | |
| Ultrasonic Sensor | |||
| VCC | 5V | Sensor power | |
| GND | GND | Ground connection | |
| TRIG | Pin 4 | Trigger signal | |
| ECHO | Pin 5 | Echo response | |
| Servo Motor | |||
| Red (VCC) | 5V | Motor power | |
| Brown/Black (GND) | GND | Ground connection | |
| Orange (Signal) | Pin 6 | Control signal |
Important Wiring Notes
⚠️ Critical Safety Information:
- TFT Display operates at 3.3V - connecting to 5V will permanently damage it
- Ultrasonic sensor uses 5V - do not connect to 3.3V as it won't function properly
- Double-check all connections before powering on
- Use quality jumper wires to prevent loose connections during servo movement
Physical Assembly Tips
- Mount ultrasonic sensor to servo horn:
- Attach sensor facing forward when servo is at 90°
- Use small screws or strong double-sided tape
- Ensure wires don't interfere with 180° rotation
- Test movement before final assembly
- Secure the servo:
- Mount to breadboard or project base
- Ensure stable platform for accurate scanning
- Position for clear 180° sweep without obstructions
- TFT display placement:
- Mount where screen is easily visible
- Keep wiring neat and away from moving parts
- Consider making an enclosure for professional appearance
Arduino Code
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <Servo.h>
// TFT Display pins - using software SPI for easier wiring
#define TFT_CS 10
#define TFT_RST 9
#define TFT_DC 8
#define TFT_MOSI 11 // SDI on your display
#define TFT_CLK 13 // SCK on your display
#define TFT_MISO 12 // SDO on your display
// Sensor and servo pins
#define TRIG_PIN 4
#define ECHO_PIN 5
#define SERVO_PIN 6
// Display dimensions
#define SCREEN_WIDTH 240
#define SCREEN_HEIGHT 320
// Radar parameters
#define RADAR_CENTER_X 120
#define RADAR_CENTER_Y 280
#define RADAR_RADIUS 100
#define MAX_DISTANCE 200
#define ANGLE_STEP 3
// Colors (16-bit RGB565 format)
#define COLOR_BACKGROUND 0x0020 // Dark green
#define COLOR_GRID 0x0400 // Medium green
#define COLOR_SWEEP 0x07E0 // Bright green
#define COLOR_OBJECT 0xF800 // Red
#define COLOR_TEXT 0x07E0 // Bright green
#define COLOR_TRAIL 0x0200 // Dim green
// Create display using software SPI
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO);
Servo radarServo;
// Radar variables
int currentAngle = 0;
int direction = 1;
int lastSweepAngle = -1;
unsigned long lastScanTime = 0;
const int scanDelay = 120; // Milliseconds between scans
// Object storage
struct RadarObject {
int angle;
int distance;
unsigned long timestamp;
bool active;
};
const int MAX_OBJECTS = 15;
RadarObject objects[MAX_OBJECTS];
int objectCount = 0;
void setup() {
Serial.begin(9600);
Serial.println("Starting TFT Radar System...");
// Initialize TFT display
tft.begin();
tft.setRotation(0); // Portrait mode
tft.fillScreen(COLOR_BACKGROUND);
// Initialize sensor pins
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
// Initialize servo
radarServo.attach(SERVO_PIN);
radarServo.write(0);
delay(1000);
// Draw initial radar screen
drawRadarBase();
drawTitle();
Serial.println("TFT Radar System Ready!");
}
void loop() {
unsigned long currentTime = millis();
if (currentTime - lastScanTime >= scanDelay) {
// Move servo to current angle
radarServo.write(currentAngle);
delay(60); // Wait for servo to reach position
// Measure distance
int distance = measureDistance();
// Clear old sweep line
if (lastSweepAngle >= 0) {
drawSweepLine(lastSweepAngle, COLOR_BACKGROUND);
}
// Add object if detected within range
if (distance > 5 && distance < MAX_DISTANCE) {
addObject(currentAngle, distance);
Serial.print("Object detected at ");
Serial.print(currentAngle);
Serial.print("° - ");
Serial.print(distance);
Serial.println("cm");
}
// Redraw radar components
drawObjects();
drawSweepLine(currentAngle, COLOR_SWEEP);
drawSweepTrail(currentAngle);
updateInfoDisplay(currentAngle, distance);
lastSweepAngle = currentAngle;
// Update angle for next scan
currentAngle += direction * ANGLE_STEP;
if (currentAngle >= 180) {
direction = -1;
currentAngle = 180;
} else if (currentAngle <= 0) {
direction = 1;
currentAngle = 0;
}
lastScanTime = currentTime;
}
// Clean old objects periodically
cleanOldObjects();
}
void drawRadarBase() {
// Clear radar area
tft.fillCircle(RADAR_CENTER_X, RADAR_CENTER_Y, RADAR_RADIUS + 5, COLOR_BACKGROUND);
// Draw concentric circles (distance rings)
for (int i = 1; i <= 4; i++) {
int radius = (RADAR_RADIUS * i) / 4;
drawArc(RADAR_CENTER_X, RADAR_CENTER_Y, radius, 0, 180, COLOR_GRID);
}
// Draw angle lines every 30 degrees
for (int angle = 0; angle <= 180; angle += 30) {
float radian = angle * PI / 180.0;
int x = RADAR_CENTER_X + cos(radian) * RADAR_RADIUS;
int y = RADAR_CENTER_Y - sin(radian) * RADAR_RADIUS;
tft.drawLine(RADAR_CENTER_X, RADAR_CENTER_Y, x, y, COLOR_GRID);
}
// Draw distance labels
tft.setTextColor(COLOR_TEXT);
tft.setTextSize(1);
for (int i = 1; i <= 4; i++) {
int distance = (MAX_DISTANCE * i) / 4;
int radius = (RADAR_RADIUS * i) / 4;
tft.setCursor(RADAR_CENTER_X - 15, RADAR_CENTER_Y - radius - 8);
tft.print(distance);
tft.print("cm");
}
// Draw angle labels
tft.setCursor(RADAR_CENTER_X + RADAR_RADIUS - 15, RADAR_CENTER_Y + 8);
tft.print("0°");
tft.setCursor(RADAR_CENTER_X - 8, RADAR_CENTER_Y - RADAR_RADIUS + 8);
tft.print("90°");
tft.setCursor(RADAR_CENTER_X - RADAR_RADIUS + 5, RADAR_CENTER_Y + 8);
tft.print("180°");
}
void drawArc(int centerX, int centerY, int radius, int startAngle, int endAngle, uint16_t color) {
// Draw arc by plotting points
for (int angle = startAngle; angle <= endAngle; angle += 1) {
float radian = angle * PI / 180.0;
int x = centerX + cos(radian) * radius;
int y = centerY - sin(radian) * radius;
tft.drawPixel(x, y, color);
}
}
void drawSweepLine(int angle, uint16_t color) {
float radian = angle * PI / 180.0;
int x = RADAR_CENTER_X + cos(radian) * RADAR_RADIUS;
int y = RADAR_CENTER_Y - sin(radian) * RADAR_RADIUS;
tft.drawLine(RADAR_CENTER_X, RADAR_CENTER_Y, x, y, color);
}
void drawSweepTrail(int currentAngle) {
// Draw fading trail behind sweep line
for (int i = 1; i <= 8; i++) {
int trailAngle = currentAngle - (direction * i * ANGLE_STEP);
if (trailAngle >= 0 && trailAngle <= 180) {
uint16_t trailColor;
if (i <= 4) {
trailColor = COLOR_TRAIL; // Dim green trail
} else {
trailColor = COLOR_BACKGROUND; // Fade to background
}
drawSweepLine(trailAngle, trailColor);
}
}
}
int measureDistance() {
// Send ultrasonic pulse
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
// Read echo pulse duration
long duration = pulseIn(ECHO_PIN, HIGH, 30000); // 30ms timeout
if (duration == 0) {
return MAX_DISTANCE; // No echo received
}
// Calculate distance in centimeters
int distance = duration * 0.034 / 2;
// Limit to maximum range
if (distance > MAX_DISTANCE) {
return MAX_DISTANCE;
}
return distance;
}
void addObject(int angle, int distance) {
// Find empty slot or replace oldest object
int targetIndex = -1;
unsigned long oldestTime = millis();
for (int i = 0; i < MAX_OBJECTS; i++) {
if (!objects[i].active) {
targetIndex = i;
break;
}
if (objects[i].timestamp < oldestTime) {
oldestTime = objects[i].timestamp;
targetIndex = i;
}
}
if (targetIndex >= 0) {
objects[targetIndex].angle = angle;
objects[targetIndex].distance = distance;
objects[targetIndex].timestamp = millis();
objects[targetIndex].active = true;
if (objectCount < MAX_OBJECTS) objectCount++;
}
}
void drawObjects() {
unsigned long currentTime = millis();
for (int i = 0; i < MAX_OBJECTS; i++) {
if (objects[i].active) {
// Calculate object age for fading effect
unsigned long age = currentTime - objects[i].timestamp;
if (age < 4000) { // Show objects for 4 seconds
float radian = objects[i].angle * PI / 180.0;
int scaledDistance = (objects[i].distance * RADAR_RADIUS) / MAX_DISTANCE;
int x = RADAR_CENTER_X + cos(radian) * scaledDistance;
int y = RADAR_CENTER_Y - sin(radian) * scaledDistance;
// Choose color based on age
uint16_t color = COLOR_OBJECT;
if (age > 2500) {
color = 0x8000; // Dimmer red for older objects
}
// Draw object as filled circle
tft.fillCircle(x, y, 2, color);
// Draw small ring around recent objects
if (age < 1000) {
tft.drawCircle(x, y, 4, color);
}
}
}
}
}
void cleanOldObjects() {
unsigned long currentTime = millis();
for (int i = 0; i < MAX_OBJECTS; i++) {
if (objects[i].active && (currentTime - objects[i].timestamp > 4000)) {
// Clear old object from screen
float radian = objects[i].angle * PI / 180.0;
int scaledDistance = (objects[i].distance * RADAR_RADIUS) / MAX_DISTANCE;
int x = RADAR_CENTER_X + cos(radian) * scaledDistance;
int y = RADAR_CENTER_Y - sin(radian) * scaledDistance;
// Clear the object area
tft.fillCircle(x, y, 6, COLOR_BACKGROUND);
objects[i].active = false;
if (objectCount > 0) objectCount--;
}
}
}
void drawTitle() {
tft.setTextColor(COLOR_TEXT);
tft.setTextSize(2);
tft.setCursor(35, 10);
tft.print("RADAR SCAN");
tft.setTextSize(1);
tft.setCursor(90, 35);
tft.print("SYSTEM");
}
void updateInfoDisplay(int angle, int distance) {
// Clear info area
tft.fillRect(5, 50, 230, 110, COLOR_BACKGROUND);
// Draw info box border
tft.drawRect(5, 50, 230, 110, COLOR_GRID);
tft.setTextColor(COLOR_TEXT);
tft.setTextSize(1);
// Current readings - top row
tft.setCursor(10, 60);
tft.print("Angle: ");
tft.print(angle);
tft.print("°");
tft.setCursor(130, 60);
tft.print("Distance: ");
tft.print(distance);
tft.print("cm");
// Status info - second row
tft.setCursor(10, 80);
tft.print("Objects: ");
tft.print(objectCount);
tft.setCursor(130, 80);
tft.print("Range: ");
tft.print(MAX_DISTANCE);
tft.print("cm");
// Scan direction - third row
tft.setCursor(10, 100);
tft.print("Direction: ");
if (direction == 1) {
tft.print("→ (0 to 180°)");
} else {
tft.print("← (180 to 0°)");
}
// System status - fourth row
tft.setCursor(10, 120);
tft.print("Status: SCANNING");
tft.setCursor(130, 120);
tft.print("Mode: ACTIVE");
// Progress bar
int barWidth = 200;
int barHeight = 6;
int barX = 20;
int barY = 140;
// Draw progress bar background
tft.drawRect(barX - 1, barY - 1, barWidth + 2, barHeight + 2, COLOR_GRID);
tft.fillRect(barX, barY, barWidth, barHeight, COLOR_BACKGROUND);
// Draw current progress
int progress = (angle * barWidth) / 180;
tft.fillRect(barX, barY, progress, barHeight, COLOR_SWEEP);
// Progress percentage
tft.setCursor(10, 155);
tft.print("Progress: ");
tft.print((angle * 100) / 180);
tft.print("%");
}
Step-by-Step Build Instructions
Step 1: Prepare Your Workspace
- Gather all components and verify you have everything listed
- Install required libraries in Arduino IDE
- Set up a clean workspace with good lighting
- Have multimeter ready for testing connections (optional but recommended)
Step 2: Wire the TFT Display
- Double-check voltage requirements - TFT uses 3.3V, not 5V
- Connect power first: VCC and LED to 3.3V, GND to ground
- Connect control pins: CS→10, RESET→9, DC→8
- Connect SPI pins: SDI→11, SCK→13, SDO→12
- Test display by uploading a simple Adafruit test sketch
Step 3: Connect Sensors
- Wire ultrasonic sensor: VCC→5V, GND→ground, TRIG→4, ECHO→5
- Mount sensor to servo horn using screws or strong tape
- Connect servo: Red→5V, Brown→ground, Orange→6
- Test servo movement with simple servo sweep code
Step 4: Upload and Test Code
- Copy the complete radar code into Arduino IDE
- Select "Arduino Mega 2560" from Tools → Board
- Choose correct COM port from Tools → Port
- Upload code and watch for successful compilation
- Open Serial Monitor (9600 baud) to see debug output
Step 5: Calibration and Testing
- Power on system and verify TFT display shows radar screen
- Check servo sweep moves smoothly from 0° to 180°
- Test object detection by placing items at different distances
- Verify red dots appear on display when objects detected
- Fine-tune scan speed by adjusting
scanDelayvalue if needed
Troubleshooting Guide
Display Issues
Problem: TFT screen is blank or black
- Check power connections - ensure 3.3V to VCC and LED
- Verify SPI wiring - confirm SDI→11, SCK→13, SDO→12
- Test with simple display code to isolate the issue
- Check library installation - reinstall Adafruit libraries
Problem: Garbled or corrupted display
- Check connection quality - ensure solid wire connections
- Try shorter wires - long wires can cause signal degradation
- Verify pin assignments match code definitions
- Check for loose connections on breadboard
Sensor Issues
Problem: No distance readings or constant max distance
- Verify sensor power - ultrasonic needs 5V, not 3.3V
- Check TRIG and ECHO connections to pins 4 and 5
- Test sensor separately with simple distance measuring code
- Ensure clear line of sight - sensor needs unobstructed path
Problem: Servo not moving or jerky movement
- Check power supply - servo may need external 5V power source
- Verify signal wire connected to pin 6
- Test servo independently with basic sweep code
- Ensure adequate current from power source
System Performance Issues
Problem: Slow or delayed radar updates
- Reduce scan delay - try lower
scanDelayvalue (minimum ~80ms) - Check for interference - keep wires away from servo motor
- Use external power - USB power may be insufficient for smooth operation
- Optimize code - reduce unnecessary display updates
Problem: Objects not appearing or disappearing quickly
- Check object distance - must be between 5cm and 200cm
- Verify sensor alignment - ensure sensor faces straight ahead
- Adjust detection sensitivity - modify distance thresholds in code
- Test with larger objects - small objects may not reflect enough signal
Customization Options
Visual Customizations
Change Colors:
#define COLOR_SWEEP 0x07FF // Cyan sweep line
#define COLOR_OBJECT 0xFFE0 // Yellow objects
#define COLOR_GRID 0x001F // Blue grid lines
Adjust Radar Size:
#define RADAR_RADIUS 80 // Smaller radar circle
#define RADAR_RADIUS 120 // Larger radar circle
Performance Customizations
Faster Scanning:
const int scanDelay = 80; // Faster updates
#define ANGLE_STEP 5 // Bigger angle steps
Higher Precision:
const int scanDelay = 150; // Slower, more accurate
#define ANGLE_STEP 1 // Smaller angle steps
Extended Range:
#define MAX_DISTANCE 300 // Scan up to 3 meters
#define MAX_DISTANCE 100 // Short range, higher accuracy
Feature Additions
Add Sound Effects:
#define BUZZER_PIN 3
// Add tone() calls for detection sounds
Include Temperature Compensation:
// Adjust sound speed based on temperature
float speedOfSound = 331.4 + (0.6 * temperature);
Educational Applications
STEM Learning Objectives
- Physics concepts: Sound waves, reflection, time-of-flight measurement
- Programming skills: C++, arrays, structures, timing functions
- Electronics: SPI communication, sensor interfacing, servo control
- Mathematics: Trigonometry, coordinate systems, data visualization
Classroom Extensions
- Range accuracy testing - compare readings with actual measurements
- Object shape detection - analyze how different shapes affect readings
- Environmental factors - test performance in different conditions
- Data logging - record and analyze detection patterns over time
Advanced Projects
- Multi-sensor arrays for improved coverage
- WiFi connectivity for remote monitoring
- SD card logging for data collection
- Voice synthesis for audio feedback
- 3D mounting system for vertical scanning
Safety Considerations
Electrical Safety
- Always disconnect power before making wiring changes
- Double-check voltage levels - mixing 3.3V and 5V can damage components
- Use appropriate wire gauges for current requirements
- Avoid short circuits by checking connections carefully
Mechanical Safety
- Secure all moving parts to prevent injury
- Ensure stable mounting to avoid tipping during operation
- Keep fingers away from servo sweep area during operation
- Use appropriate fasteners rated for component weights
General Precautions
- Work in well-ventilated area when soldering
- Wear safety glasses when cutting or drilling
- Keep workspace organized to prevent component loss
- Have fire extinguisher nearby when working with electronics
Conclusion
This Arduino radar project combines multiple technologies to create an impressive demonstration of real-time sensing and visualization. The standalone nature of the TFT display makes it perfect for portable demonstrations, while the modular code structure allows for extensive customization and educational exploration.
Whether used as a learning tool for understanding sensor technology, a impressive maker project for demonstrations, or a foundation for more advanced sensing applications, this radar system provides a solid introduction to embedded systems development and real-time data visualization.
The project's scalability allows beginners to start with basic functionality while providing pathways for advanced users to add sophisticated features like data logging, wireless connectivity, or multi-sensor fusion. Most importantly, it demonstrates practical applications of fundamental STEM concepts in an engaging, hands-on format that makes learning both effective and enjoyable.
Additional Resources
Code Repository
Complete project files, additional examples, and updates can be found in our project repository. The code is open-source and contributions are welcome.
Component Suppliers
- Arduino Mega 2560: Available from Arduino Store, Amazon, Adafruit
- TFT Displays: AliExpress, Amazon, electronic component retailers
- Sensors and Servos: Most electronics hobbyist suppliers
Further Reading
- Adafruit Learning System tutorials on TFT displays
- Arduino official documentation for servo and sensor interfacing
- Electronics tutorials on SPI communication protocols
- Advanced Arduino projects for sensor fusion and data visualization
This project serves as an excellent foundation for exploring embedded systems, sensor technology, and real-time data visualization in educational and maker environments.
Comments