Originally published on ShillehTek IoT & Beyond.
Project Overview
Arduino Uno + HC-SR04 obstacle-avoiding robot: Build a 2WD rover that uses an HC-SR04 ultrasonic sensor on a servo to scan left and right, compare distances, and steer toward the open path instead of getting stuck in corners.
- Time: ~2 hours
- Skill level: Intermediate
- What you will build: A 2WD autonomous rover with an L298N driver, ultrasonic ranging, servo look-around, and clean movement functions you can reuse in any robot.
Look, decide, drive - the loop every autonomous robot runs.
Parts List
From ShillehTek
- Arduino Uno R3 Super Starter Kit - Uno, HC-SR04, and an SG90 servo for the sensor head
- L298N Motor Driver - drives two DC motors from the battery
- N20 Gear Motors ×2 - or the yellow TT motors that come with most chassis kits
- TB6612FNG Driver - a smaller, cooler-running alternative to the L298N
- 400-Point Breadboard - easy prototyping for the sensor and servo connections
- Dupont Jumper Wires - wiring between the Uno, sensor, servo, and driver
External
- A 2WD robot chassis with two wheels and a caster
- A 7.4-12 V battery pack (2×18650 or 6×AA) - the L298N drops about 2 V, so give it headroom
Note: Power the motors from the battery through the L298N and let the L298N's 5 V output (jumper on) feed the Uno's 5V pin - one battery, common ground. Never run motors from the Uno's own regulator; the resets that follow are the number-one beginner robot bug.
Step-by-Step Guide
Step 1 - Assemble the Chassis
Goal: Something that rolls.
What to do: Bolt the motors and caster to the chassis, fit the wheels, and mount the Uno and L298N on top with the battery pack underneath or behind.
Fix the servo at the front with the HC-SR04 on its horn (a hot-glued bracket or a small 3D-printed holder works), pointing forward at the servo's 90° position.
Motors below, electronics on top, sensor up front.
Expected result: A rolling platform with the sensor head centered.
Step 2 - Wire Driver, Sensor, and Servo
Goal: Every signal on the right pin.
What to do: L298N: IN1 → D5, IN2 → D4 (left motor), IN3 → D7, IN4 → D6 (right motor); leave the ENA/ENB jumpers on for full speed.
Battery + → L298N 12V, battery - → L298N GND, L298N 5V → Uno 5V, L298N GND → Uno GND.
HC-SR04: TRIG → D9, ECHO → D10, VCC → 5V, GND → GND. Servo: signal → D3, power from 5V/GND.
Motor inputs on D4-D7, ultrasonic on D9/D10 - add the servo on D3.
Expected result: A fully wired rover.
Step 3 - Upload the Sketch
Goal: Program the robot to drive forward, detect obstacles, scan left and right, and turn toward the clearer side.
Code:
#include <Servo.h>
const int TRIG = 9, ECHO = 10, SERVO_PIN = 3;
const int L_FWD = 5, L_REV = 4, R_FWD = 7, R_REV = 6;
const int STOP_CM = 20;
Servo head;
long readCm() {
digitalWrite(TRIG, LOW); delayMicroseconds(2);
digitalWrite(TRIG, HIGH); delayMicroseconds(10);
digitalWrite(TRIG, LOW);
long us = pulseIn(ECHO, HIGH, 25000);
return us == 0 ? 400 : us * 0.034 / 2; // no echo = treat as far away
}
void drive(bool lf, bool lr, bool rf, bool rr) { // one function for every motion
digitalWrite(L_FWD, lf); digitalWrite(L_REV, lr);
digitalWrite(R_FWD, rf); digitalWrite(R_REV, rr);
}
void forward() { drive(1, 0, 1, 0); }
void backward() { drive(0, 1, 0, 1); }
void left() { drive(0, 1, 1, 0); } // spin in place
void right() { drive(1, 0, 0, 1); }
void stopAll() { drive(0, 0, 0, 0); }
long lookAt(int angle) { // aim the sensor, settle, measure
head.write(angle); delay(350);
long d = readCm(); head.write(90); return d;
}
void setup() {
pinMode(TRIG, OUTPUT); pinMode(ECHO, INPUT);
for (int p : {L_FWD, L_REV, R_FWD, R_REV}) pinMode(p, OUTPUT);
head.attach(SERVO_PIN); head.write(90);
delay(1000); // a second to put it down
}
void loop() {
long ahead = readCm();
if (ahead > STOP_CM) { forward(); delay(50); return; }
stopAll(); delay(200); // obstacle: back off and look around
backward(); delay(400); stopAll(); delay(200);
long leftCm = lookAt(160), rightCm = lookAt(20);
if (leftCm > rightCm) left(); else right();
delay(450); // ~90 degrees; tune for your chassis
stopAll(); delay(100);
}
What to do: Upload with the robot on a stand (wheels free), check that forward really is forward (swap a motor's two wires if it runs backwards), then set it on the floor.
Expected result: It drives until something is within 20 cm, backs up, glances left and right, and turns toward the clearer direction.
Step 4 - Tune the Turn
Goal: Ninety degrees, every time.
What to do: The 450 ms turn delay depends on your motors, battery, and floor. Adjust until a turn is roughly a right angle.
For finer control, remove the ENA/ENB jumpers and drive them with PWM: move the ultrasonic to A0/A1 and the motor inputs to D4, D7, D8, D12 so that D5 and D6 are free for ENA and ENB, then use analogWrite to set speed. Slower robots avoid better, and you can creep up to obstacles instead of charging them.
Expected result: Clean turns and no more scraping along walls.
Step 5 - Level Up
Goal: From wanderer to robot.
What to do: Add a three-point scan (left, center, right) for smarter path choice; a stuck detector that reverses and spins if the front reading hasn't changed in three seconds; an HC-05 so a phone can take over; or the line-follower sensors from our other guide for a robot that does both. Swap in the TB6612 driver for a lighter, more efficient build.
Expected result: A platform for every robot idea you have next.
Conclusion
This Arduino Uno obstacle-avoiding robot combines an HC-SR04 ultrasonic sensor with a servo scan and an L298N motor driver to create a simple sense-decide-drive loop that can explore without constantly getting stuck.
Want the exact parts used in this build? Grab them from ShillehTek.com. If you want help customizing this project or building something for your product, check out our IoT consulting services.
Credits
All photos and images in this tutorial are credited to Raushan Kumar on Hackster.io. The original guide by Raushan Kumar served as the reference for this ShillehTek version (shared under Apache-2.0). We thank him for his excellent work in the maker community.



Top comments (0)