We’ve all seen that classic image in submarine movies: a green screen, a rotating line, and a bright blip indicating where the enemy is.
Today we are going to build exactly that: an ultrasonic radar with Arduino.
This project connects two worlds. On one side, the Hardware (Arduino moving a sensor and measuring distances). On the other side, the PC Software (Processing), which will collect that data and draw a real-time graphical interface.
From Polar to Cartesian Coordinates
We have an ultrasonic sensor mounted on a servo.
- The servo tells us where we are looking: the Angle (
). - The sensor tells us how far the object is: the Radius (r).
This is a polar coordinate system
This is where trigonometry becomes our best friend:
The computer will receive the pair (angle, distance) via the USB cable, apply these formulas, and draw a green dot at the calculated position.
Components
- [1x|blue] Arduino Uno | The project’s brain
- [1x|orange] HC-SR04 Ultrasonic Sensor | Measures distances
- [1x|green] Micro Servo Motor (SG90) | Sensor sweeping
- [1x|gray] Mount for sensor on servo | 3D print or tape
- [Several|gray] Breadboard and wires | Flexible connections for the sensor
Wiring Diagram
The assembly is simple, but we must ensure the sensor cables are long or flexible enough to allow the servo’s sweeping motion without strain.
Sweep Servo
The servo moves the sensor from side to side. If powered with an external source, connect its GND to the Arduino’s GND.
[ { “from”: “Servo Red (VCC)”, “to”: “External regulated 5V source”, “color”: “red” }, { “from”: “Servo Brown (GND)”, “to”: “Arduino GND”, “color”: “black” }, { “from”: “Servo Orange (Signal)”, “to”: “Pin 12”, “color”: “green” } ]
HC-SR04 Sensor
The sensor measures the distance at each angular position of the servo.
[ { “from”: “HC-SR04 VCC”, “to”: “Arduino 5V”, “color”: “red” }, { “from”: “HC-SR04 GND”, “to”: “Arduino GND”, “color”: “black” }, { “from”: “HC-SR04 Trig”, “to”: “Pin 10”, “color”: “orange”, “note”: “Arduino Output” }, { “from”: “HC-SR04 Echo”, “to”: “Pin 11”, “color”: “yellow”, “note”: “Arduino Input” } ]
Arduino Firmware
Arduino’s job is simple: Move -> Measure -> Send. It won’t calculate anything complex; it will just spit out raw data over the Serial Port.
The transmission format is important. Processing needs to understand where each reading ends. We will use a CSV line per sample: angle,distance.
#include <Servo.h>
const int trigPin = 10;
const int echoPin = 11;
const int servoPin = 12;
Servo myServo;
void setup() {
Serial.begin(9600); // Start Serial communication
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
myServo.attach(servoPin);
}
void loop() {
// Forward sweep (from 15° to 165°)
// Avoiding 0° and 180° because the servo sometimes jitters at the extremes
for(int i = 15; i <= 165; i++){
myServo.write(i);
delay(30); // Give the servo time to reach and stabilize
int distance = calculateDistance();
// Send to PC: "angle,distance\n"
Serial.print(i);
Serial.print(",");
Serial.println(distance);
}
// Return sweep (from 165° to 15°)
for(int i = 165; i > 15; i--){
myServo.write(i);
delay(30);
int distance = calculateDistance();
Serial.print(i);
Serial.print(",");
Serial.println(distance);
}
}
int calculateDistance(){
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
unsigned long duration = pulseIn(echoPin, HIGH, 30000UL);
// No echo within max time: out of visual range
if (duration == 0) return 41;
int distance = duration * 0.0343 / 2;
// Limit max distance to 40cm for good screen display
// If it measures more, clip it
if (distance > 40) distance = 41;
return distance;
}
Visualization with Processing
We will do the visualization with Processing, a creative environment based on Java with a structure similar to the Arduino IDE.
- Download and install Processing.
- Copy the following code.
This sketch reads the serial port, extracts the angle and distance, and draws a fading green line (trail) simulating the phosphor of old radars.
import processing.serial.*; // Import Serial library
import java.awt.event.KeyEvent;
import java.io.IOException;
Serial myPort;
String angle="";
String distance="";
String data="";
int iAngle, iDistance;
int index1=0;
void setup() {
size(1200, 700); // Window size
smooth();
// Find your Arduino port (usually the first or second in the list)
// Change the [0] if it doesn't connect
myPort = new Serial(this, Serial.list()[0], 9600);
myPort.bufferUntil('\n'); // One sample per line
}
void draw() {
fill(0,4); // Semi-transparent black fill to create the fading effect (trail)
rect(0, 0, width, height);
fill(98,245,31); // Radar Green Color
// Translate origin (0,0) to the center-bottom of the screen
translate(width/2, height);
// --- DRAW RADAR LINES (AESTHETICS) ---
strokeWeight(2);
stroke(98,245,31); // Green
noFill();
// Distance arcs (10cm, 20cm, 30cm, 40cm)
// Multiply by ratio to scale to screen
float pixelsPerCm = 15;
arc(0,0, 20*pixelsPerCm, 20*pixelsPerCm, PI, TWO_PI);
arc(0,0, 40*pixelsPerCm, 40*pixelsPerCm, PI, TWO_PI);
arc(0,0, 60*pixelsPerCm, 60*pixelsPerCm, PI, TWO_PI);
arc(0,0, 80*pixelsPerCm, 80*pixelsPerCm, PI, TWO_PI);
// --- DRAW DETECTED OBJECT ---
// Convert Polar Coordinates to Cartesian
float pixDistance = iDistance * pixelsPerCm;
// NOTE: Processing uses radians. And the servo goes from 15 to 165.
// Also we invert the direction because Processing draws Y downwards by default
// but we have done translate/rotate. Let's simplify:
float rads = radians(iAngle);
// If distance is less than 40 (max range), draw object
if(iDistance < 40){
// Polar math:
// x = r * cos(theta)
// y = r * sin(theta) -> Negative because in Processing Y increases downwards
float x = pixDistance * cos(rads);
float y = -pixDistance * sin(rads);
strokeWeight(9);
stroke(255,10,10); // Red for the object
point(x, y);
}
// Sweep line (Scanner)
strokeWeight(3);
stroke(30,250,60);
line(0,0, 40*pixelsPerCm * cos(rads), -40*pixelsPerCm * sin(rads));
}
// Event that runs when a line arrives via USB
void serialEvent (Serial myPort) {
try {
String line = myPort.readStringUntil('\n');
if (line == null) return;
data = trim(line);
// Look for the comma
index1 = data.indexOf(",");
// Split the string
angle = data.substring(0, index1);
distance = data.substring(index1 + 1, data.length());
// Convert to integers
iAngle = int(angle);
iDistance = int(distance);
// Correct servo orientation to match the screen
// (Depends on how you mounted the motor)
iAngle = 180 - iAngle;
} catch (Exception e) {
// If garbage arrives on the port, ignore it
}
}
How the Trail Works
When you run the program, you will see something fascinating:
- The
draw()function in Processing runs in a loop (like Arduino’sloop). - By setting
fill(0, 4)and drawing a black rectangle over the entire screen at the beginning of each frame, we are “darkening a little bit” what was drawn before. - This creates the trail effect. The current green sweep line shines brightly, but the line from 1 second ago appears dim.
This is not just aesthetic; it allows us to see the “history” of the sweep, just like the phosphor of a real CRT radar screen does.
Challenges and Improvements
Did it work? Now I challenge you to improve it:
- Reduce the detection field: The HC-SR04 integrates echoes within a relatively wide beam. You could try a Time-of-Flight optical sensor like the VL53L0X, keeping in mind that it measures differently and also has range and reflectivity limits.
- Intruder detection: Program Processing so that if it detects something less than 10cm away, it plays an alarm sound (
.wav) using the sound library. - 360 Radar: Modify the mechanics to use a slip ring and allow full rotations, mapping 360 degrees on the screen.