radar 3

By Paul , 15 September 2025

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:

  1. Adafruit GFX Library - Provides graphics functions for drawing
  2. Adafruit ILI9341 - Controls the TFT display communication
  3. Servo - Built-in Arduino library (no installation needed)

Installation Steps

  1. Open Arduino IDE
  2. Navigate to Sketch → Include Library → Manage Libraries
  3. Search for "Adafruit GFX" and click Install
  4. Search for "Adafruit ILI9341" and click Install
  5. Close and restart Arduino IDE

Circuit Wiring Guide

Pin Connection Table

ComponentPinArduino Mega PinNotes
TFT Display   
VCC 3.3VCRITICAL: Use 3.3V, NOT 5V!
GND GNDAny ground pin
CS Pin 10Chip select
RESET Pin 9Reset control
DC Pin 8Data/command select
SDI (MOSI) Pin 11Serial data input
SCK Pin 13Serial clock
LED 3.3VBacklight power
SDO (MISO) Pin 12Serial data output
Ultrasonic Sensor   
VCC 5VSensor power
GND GNDGround connection
TRIG Pin 4Trigger signal
ECHO Pin 5Echo response
Servo Motor   
Red (VCC) 5VMotor power
Brown/Black (GND) GNDGround connection
Orange (Signal) Pin 6Control 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

  1. 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
  2. Secure the servo:
    • Mount to breadboard or project base
    • Ensure stable platform for accurate scanning
    • Position for clear 180° sweep without obstructions
  3. 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

  1. Gather all components and verify you have everything listed
  2. Install required libraries in Arduino IDE
  3. Set up a clean workspace with good lighting
  4. Have multimeter ready for testing connections (optional but recommended)

Step 2: Wire the TFT Display

  1. Double-check voltage requirements - TFT uses 3.3V, not 5V
  2. Connect power first: VCC and LED to 3.3V, GND to ground
  3. Connect control pins: CS→10, RESET→9, DC→8
  4. Connect SPI pins: SDI→11, SCK→13, SDO→12
  5. Test display by uploading a simple Adafruit test sketch

Step 3: Connect Sensors

  1. Wire ultrasonic sensor: VCC→5V, GND→ground, TRIG→4, ECHO→5
  2. Mount sensor to servo horn using screws or strong tape
  3. Connect servo: Red→5V, Brown→ground, Orange→6
  4. Test servo movement with simple servo sweep code

Step 4: Upload and Test Code

  1. Copy the complete radar code into Arduino IDE
  2. Select "Arduino Mega 2560" from Tools → Board
  3. Choose correct COM port from Tools → Port
  4. Upload code and watch for successful compilation
  5. Open Serial Monitor (9600 baud) to see debug output

Step 5: Calibration and Testing

  1. Power on system and verify TFT display shows radar screen
  2. Check servo sweep moves smoothly from 0° to 180°
  3. Test object detection by placing items at different distances
  4. Verify red dots appear on display when objects detected
  5. Fine-tune scan speed by adjusting scanDelay value 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 scanDelay value (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

  1. Range accuracy testing - compare readings with actual measurements
  2. Object shape detection - analyze how different shapes affect readings
  3. Environmental factors - test performance in different conditions
  4. 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