Obstacle-Avoiding Robot with HC-SR04 and Arduino
This is the project that makes people say “it’s alive”: the car drives around a room, stops before hitting things, looks left and right, and picks the open path. It builds directly on the 2WD robot car — same chassis, same drive code, plus one sensor and one servo.
How the HC-SR04 works
The HC-SR04 is sonar: pulse the TRIG pin for 10 µs and it emits an ultrasonic chirp at 40 kHz; the ECHO pin then goes HIGH until the reflection returns. Sound covers about 1 cm in 58 µs round-trip, so distance_cm = echo_time_µs / 58. Practical range is 2 cm to ~3–4 m, in a cone roughly 15° wide. Soft, angled, or very thin objects (curtains, chair legs) reflect poorly — that is physics, not a bug, and the code below treats “no echo” as open space for that reason.
Extra parts needed
| Part | Notes | Indicative price (India) |
|---|---|---|
| HC-SR04 ultrasonic sensor | The 4-pin classic | ₹60–120 |
| SG90 micro servo (9 g) | Pan the sensor left/right | ₹90–150 |
| Sensor bracket / mount for HC-SR04 + servo horn | Or a zip-tie-and-hot-glue solution | ₹40–100 |
| Female–female jumpers ×7 | Sensor and servo leads | ₹40–70 |
Prices shown are indicative online street prices in India as of mid-2026. They vary by seller and stock — treat them as a budgeting guide, not a quote.
Mounting and wiring
Mount the servo at the front centre of the chassis and fix the HC-SR04 to the servo horn facing forward at the 90° position — eyes level, not tilted at the floor.
| Module pin | Connects to | Note |
|---|---|---|
| HC-SR04 VCC | Arduino 5V | |
| HC-SR04 GND | Arduino GND | |
| HC-SR04 TRIG | Arduino D12 | Output |
| HC-SR04 ECHO | Arduino D11 | Input |
| Servo brown | GND | |
| Servo red | L298N 5V rail / 5 V supply | See warning below |
| Servo orange | Arduino D9 | Signal |
On an Uno, attaching the Servo library disables analogWrite on pins 9 and 10. That is exactly why the Hobby Robots pinout puts motor PWM on D5 and D6 — if you wired ENA/ENB to 9/10 from another tutorial, your motors will mysteriously stop working the moment the servo attaches.
A moving SG90 can draw a few hundred mA in spikes. Powering one small servo from the L298N’s 5 V regulator output is generally OK; powering it from the Arduino’s 5 V pin while on USB is asking for resets. If the Arduino reboots whenever the servo moves, that is your clue.
Test the sensor alone
Wheels off the ground, upload a minimal sketch that prints readDistanceCm() (copy the function from the code below) every 200 ms and watch the Serial Monitor while moving a book toward and away from the sensor. You should see stable centimetre readings from ~3 cm to a couple of metres. Do not proceed until this works — debugging sonar and driving logic at the same time is misery.
The full roaming code
// Hobby Robots — obstacle-avoiding robot
// Drive pinout as per the robot car guide. Servo on D9, HC-SR04 on D11/D12.
#include <Servo.h>
const int ENA = 5, IN1 = 7, IN2 = 8;
const int ENB = 6, IN3 = 4, IN4 = 2;
const int TRIG = 12, ECHO = 11;
const int SERVO_PIN = 9;
const int SAFE_CM = 25; // closer than this = obstacle
const int CRUISE = 170;
Servo neck;
void drive(int left, int right) {
digitalWrite(IN1, left >= 0 ? HIGH : LOW);
digitalWrite(IN2, left >= 0 ? LOW : HIGH);
analogWrite (ENA, min(abs(left), 255));
digitalWrite(IN3, right >= 0 ? HIGH : LOW);
digitalWrite(IN4, right >= 0 ? LOW : HIGH);
analogWrite (ENB, min(abs(right), 255));
}
long readDistanceCm() {
digitalWrite(TRIG, LOW); delayMicroseconds(2);
digitalWrite(TRIG, HIGH); delayMicroseconds(10);
digitalWrite(TRIG, LOW);
long us = pulseIn(ECHO, HIGH, 25000UL); // ~4 m timeout
if (us == 0) return 400; // no echo: treat as clear
return us / 58; // microseconds -> cm
}
long lookAt(int angle) { // 0 = right, 90 = ahead, 180 = left
neck.write(angle);
delay(350); // let the servo arrive
return readDistanceCm();
}
void setup() {
pinMode(ENA, OUTPUT); pinMode(IN1, OUTPUT); pinMode(IN2, OUTPUT);
pinMode(ENB, OUTPUT); pinMode(IN3, OUTPUT); pinMode(IN4, OUTPUT);
pinMode(TRIG, OUTPUT); pinMode(ECHO, INPUT);
neck.attach(SERVO_PIN);
neck.write(90);
delay(2000);
}
void loop() {
if (readDistanceCm() > SAFE_CM) {
drive(CRUISE, CRUISE); // path is clear
delay(60);
return;
}
// Obstacle: stop, back off, look both ways, take the open side.
drive(0, 0); delay(150);
drive(-150, -150); delay(350);
drive(0, 0);
long right = lookAt(15);
long left = lookAt(165);
neck.write(90);
if (left > right) drive(-160, 160); // rotate left
else drive(160, -160); // rotate right
delay(420); // ~90 degrees; tune this
drive(0, 0);
}Tuning and behaviour tweaks
- Stops too late: raise
SAFE_CM, or lowerCRUISE— stopping distance grows with speed. - Turn angle wrong: the
delay(420)spin is open-loop; tune it per surface (carpet needs more, tile less). Wheel encoders fix this properly. - Twitchy near walls: average 3 readings and act on the median to reject the HC-SR04’s occasional ghost echo.
- Make it smarter: scan 5 angles instead of 2 and steer proportionally toward the most open direction.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Distance always 0 or 400 | TRIG/ECHO swapped, or 3.3 V supply | HC-SR04 wants 5 V; re-check D11/D12 |
| Arduino resets when servo moves | Servo powered from Uno 5 V on USB | Feed the servo from the L298N 5 V rail |
| Motors die when servo attaches | ENA/ENB wired to pins 9/10 | Move motor PWM to D5/D6 as per this guide |
| Robot circles endlessly | One motor reversed or much weaker | Re-run the robot car spin test; swap motor wires |
| Ignores thin chair legs | Narrow objects reflect little ultrasound | Normal — add IR “whisker” sensors or a bumper switch |