Sweep an ultrasonic sensor across a wide arc with a servo and watch a real sweeping radar-style display build up on screen, the classic green sweeping line, made from parts that already do exactly this job.
4 real parts included
Total for this kit: 968.00 ฿
Doesn't include any order discount you may get at checkout.
This project turns a single distance sensor into a real sweeping radar display: a servo slowly rotates the sensor across a wide arc while the screen draws a line for whatever it detects at each angle, the same sweeping-line effect from old radar screens, built from three parts that already do this job individually.
What the Controller Does
On a loop, the controller commands the servo to a new angle, waits briefly for it to physically arrive, triggers the ultrasonic sensor, reads back the distance, and draws one new line on the display before moving to the next angle. Sweep, measure, draw, repeat.
What the Servo Does
The servo physically rotates the ultrasonic sensor across roughly a 180 degree arc, one angle step at a time, giving every distance reading a real physical direction to be drawn at.
What the Ultrasonic Sensor Does
It sends a short ultrasonic pulse and times how long the echo takes to return, converting that into a distance measurement, the same working principle as a real sonar system.
What the Display Does
The OLED draws each new distance reading as a line at the angle it was measured, building up a genuine radar-style sweep image one reading at a time.
Software and Library Concepts
Get each part working alone first: sweep the servo through its full range and confirm it physically moves correctly, take a single distance reading and print it, then draw one static test line on the display before combining all three into the sweep loop.
Testing and Troubleshooting
The servo jitters or won't hold a stable angle, check its power supply, servos draw more current than a GPIO pin alone should try to provide.
Distance readings are wildly inconsistent, the ultrasonic sensor is sensitive to soft or angled surfaces that scatter the pulse rather than reflecting it straight back, test against a flat hard wall first.
The display only ever shows part of the sweep, confirm the servo is actually completing its full range of motion and the angle-to-pixel math matches the servo's real range.
Optional Upgrade
Add a second display mode that logs only the closest detected object per sweep instead of drawing the full arc, a simple proximity-alert readout built from the same two sensors.
Wiring
HC-SR04 VCC → ESP32-S3 5V
HC-SR04 GND → ESP32-S3 GND
HC-SR04 TRIG → ESP32-S3 GPIO4 (output)
HC-SR04 ECHO → 1kΩ/2kΩ voltage divider → ESP32-S3 GPIO5 (input) — ECHO is 5V logic, the divider protects the 3.3V input pin
SG90 red → ESP32-S3 5V
SG90 brown/black → ESP32-S3 GND
SG90 orange/yellow (signal) → ESP32-S3 GPIO6 (PWM via ESP32Servo)
OLED VCC → ESP32-S3 3.3V
OLED GND → ESP32-S3 GND
OLED SDA → ESP32-S3 GPIO8 (default I2C)
OLED SCL → ESP32-S3 GPIO9 (default I2C)
HC-SR04 gets unreliable under ~2cm and the servo needs a short settle delay after each `write()` before the reading is trustworthy, which is why there's a 150ms pause per step. This draws a simplified single-line "sweep" radar (redrawn each angle) rather than a persistence-style trail — genuinely working, but simpler than a full radar-screen effect; feel free to add fading trail dots once the basic version is running.
Wiring Diagram
Starter Code
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ESP32Servo.h>
// --- Pin definitions: adjust to match your ESP32-S3 board's silkscreen if different ---
#define TRIG_PIN 4 // HC-SR04 trigger (output)
#define ECHO_PIN 5 // HC-SR04 echo (input, through a 1k/2k voltage divider - ECHO is 5V logic)
#define SERVO_PIN 6 // SG90 signal (PWM-capable pin)
#define OLED_SDA 8 // ESP32-S3 default I2C SDA
#define OLED_SCL 9 // ESP32-S3 default I2C SCL
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
Servo radarServo;
const int MAX_DISTANCE_CM = 40; // radar range shown on screen
const int CENTER_X = 64;
const int CENTER_Y = 63;
const int RADIUS_PX = 60;
long readDistanceCM() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH, 25000); // 25ms timeout (~4m max)
if (duration == 0) return -1; // no echo / out of range
return duration * 0.0343 / 2;
}
void setup() {
Serial.begin(115200);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
radarServo.attach(SERVO_PIN);
Wire.begin(OLED_SDA, OLED_SCL);
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
Serial.println("OLED init failed");
while (true) delay(1000);
}
display.clearDisplay();
display.display();
}
void sweepAndDraw(int angle) {
radarServo.write(angle);
delay(150); // let the servo settle before reading
long distance = readDistanceCM();
display.clearDisplay();
// Sweep line from center at current angle
float rad = radians(angle);
int lineLen = RADIUS_PX;
if (distance > 0 && distance < MAX_DISTANCE_CM) {
lineLen = map(distance, 0, MAX_DISTANCE_CM, 0, RADIUS_PX);
}
int x = CENTER_X - cos(rad) * lineLen;
int y = CENTER_Y - sin(rad) * lineLen;
display.drawLine(CENTER_X, CENTER_Y, x, y, SSD1306_WHITE);
// Text readout
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.print("Angle: ");
display.print(angle);
display.print((char)247); // degree symbol
display.setCursor(0, 10);
display.print("Dist: ");
if (distance > 0) {
display.print(distance);
display.print(" cm");
} else {
display.print("--");
}
display.display();
}
void loop() {
for (int angle = 0; angle <= 180; angle += 3) {
sweepAndDraw(angle);
}
for (int angle = 180; angle >= 0; angle -= 3) {
sweepAndDraw(angle);
}
}