Arduino Obstacle Avoiding Robot with an Ultrasonic Sensor (Full Code)
An Arduino obstacle-avoiding robot drives forward, uses an ultrasonic sensor (the HC-SR04) to measure the distance to whatever is in front of it, and when something gets closer than a set limit it stops, backs up and turns before driving on. You can build one with an Arduino Uno, a two-wheel robot chassis, an L298N motor driver and about 90 lines of code. Below is the full sketch, the wiring, and how to tune it.
This is a great second or third Arduino project for kids around 10 and up. It mixes three big ideas: reading a sensor, controlling motors, and making decisions in code.
What you need
Arduino Uno (or a compatible board) and a USB cable
A two-wheel-drive robot chassis with two DC gear motors and a caster wheel
L298N dual motor driver board
HC-SR04 ultrasonic distance sensor
A battery pack for the motors (for example 4–6 AA cells), plus jumper wires
Not sure which kit to buy? See our kit guide. Many beginner robot car kits include every part on this list.
How the ultrasonic sensor works
The HC-SR04 works like a bat. When the Arduino sends a short 10-microsecond pulse to the TRIG pin, the sensor fires a burst of sound too high for us to hear. The ECHO pin then stays HIGH until the echo bounces back. The Arduino times that pulse with pulseIn().
Sound travels about 343 meters per second, so it covers one centimeter there and back in roughly 58 microseconds. That gives a simple formula: distance in cm = echo time in microseconds ÷ 58.
Two details make the reading more reliable. First, pulseIn() gets a 30 ms timeout, so the robot never freezes waiting for an echo that doesn't come (we treat that as "nothing ahead"). Second, we take three readings and keep the middle one, which throws away the occasional wild value.
Wiring
These are the pins the code expects. Change the constants at the top of the sketch if your wiring is different.
HC-SR04: VCC → 5V, GND → GND, TRIG → pin 9, ECHO → pin 10
L298N: ENA → pin 5, IN1 → pin 7, IN2 → pin 8 (left motor)
L298N: ENB → pin 6, IN3 → pin 11, IN4 → pin 12 (right motor)
Motor battery + → L298N 12V/VIN input, battery − → L298N GND, and L298N GND → Arduino GND (the grounds must be shared)
Remove the jumper caps on ENA and ENB so the Arduino can control speed with PWM
Safety tip: always unplug the battery before changing wires, and never connect the motor battery directly to the Arduino's 5V pin.
The full Arduino code
We compiled and tested the logic of this sketch with simulated sensor readings before publishing. It uses only built-in Arduino functions, so there are no libraries to install.
// Obstacle-avoiding robot: Arduino Uno + HC-SR04 + L298N, 2 DC motors
const int TRIG_PIN = 9;
const int ECHO_PIN = 10;
// L298N pins (ENA/ENB must be PWM pins)
const int ENA = 5; // left motor speed
const int IN1 = 7;
const int IN2 = 8;
const int ENB = 6; // right motor speed
const int IN3 = 11;
const int IN4 = 12;
const int SAFE_CM = 25; // closer than this = obstacle
const int CRUISE = 170; // forward speed (0-255)
const int TURN_SPD = 160;
void setMotor(int en, int inA, int inB, int speed) {
// speed: -255 (reverse) to 255 (forward)
if (speed >= 0) { digitalWrite(inA, HIGH); digitalWrite(inB, LOW); }
else { digitalWrite(inA, LOW); digitalWrite(inB, HIGH); speed = -speed; }
analogWrite(en, constrain(speed, 0, 255));
}
void drive(int left, int right) {
setMotor(ENA, IN1, IN2, left);
setMotor(ENB, IN3, IN4, right);
}
void stopMotors() { drive(0, 0); }
// One distance reading in cm, or 999 if nothing echoes back
long readCm() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long us = pulseIn(ECHO_PIN, HIGH, 30000UL); // 30 ms timeout (~5 m)
if (us == 0) return 999;
return us / 58; // round trip at ~343 m/s
}
// Median of three readings filters out random spikes
long distanceCm() {
long a = readCm(); delay(10);
long b = readCm(); delay(10);
long c = readCm();
if ((a <= b && b <= c) || (c <= b && b <= a)) return b;
if ((b <= a && a <= c) || (c <= a && a <= b)) return a;
return c;
}
void setup() {
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
int outs[] = {ENA, IN1, IN2, ENB, IN3, IN4};
for (int i = 0; i < 6; i++) pinMode(outs[i], OUTPUT);
Serial.begin(9600);
stopMotors();
delay(2000); // time to set the robot down
}
void loop() {
long d = distanceCm();
Serial.println(d);
if (d > SAFE_CM) {
drive(CRUISE, CRUISE); // path clear: go
} else {
stopMotors();
delay(150);
drive(-TURN_SPD, -TURN_SPD); // back up a little
delay(300);
// alternate turn direction so it doesn't get stuck in corners
static bool turnLeft = true;
if (turnLeft) drive(-TURN_SPD, TURN_SPD);
else drive(TURN_SPD, -TURN_SPD);
turnLeft = !turnLeft;
delay(400); // tune for ~90 degrees
stopMotors();
delay(100);
}
delay(50);
}How the program decides what to do
Measure. distanceCm() returns the filtered distance in centimeters.
Path clear? If the distance is more than SAFE_CM (25 cm), both motors run forward at CRUISE speed.
Obstacle! The robot stops, backs up for 0.3 seconds, then spins in place by running one motor forward and the other backward.
Alternate. The static turnLeft flag flips every time, so the robot turns left, then right, then left. That simple trick keeps it from getting trapped in a corner.
Repeat. loop() runs again about 20 times a second.
The setMotor() helper takes a speed from −255 to 255, so "backward" is just a negative number. That keeps the main logic short and easy for kids to read.
Tuning tips
If the robot curves when it should go straight, lower the speed of the faster motor (for example drive(170, 160)). Our PID line-follower post shows how feedback can correct this automatically.
If it hits walls, raise SAFE_CM or lower CRUISE. A faster robot needs more room to stop.
If the turn is too small or too big, change the 400 ms turn delay. Time matters, and so does battery level.
If it reacts to "ghost" obstacles, check the sensor isn't tilted toward the floor.
Soft things like curtains and very thin chair legs absorb or miss the sound, so test on solid walls and boxes first.
Ideas to level up
Mount the sensor on a small servo and look left and right, then turn toward the side with more space.
Slow down gradually as the obstacle gets closer instead of stopping all at once.
Add an LED or buzzer that warns when something is near.
Rewrite the loop as a state machine (DRIVING, BACKING, TURNING) without delay(), so the robot can keep sensing while it moves.
Common questions
What age is this project good for?
Most kids around 10 and up can build it with some help, especially if they have done a few basic Arduino lessons first. Younger kids usually do better starting with block coding.
Do I need to install any libraries?
No. The sketch uses only built-in functions like pulseIn(), digitalWrite() and analogWrite(). Select "Arduino Uno" in the Arduino IDE and upload.
Why does my robot only work when it's plugged into USB?
Usually the grounds aren't connected, or the motor battery is too weak. Make sure the L298N GND and Arduino GND are wired together and use fresh batteries.
Keep learning with Fusion Robotics Education
Want a guided path? Our Arduino Basics for Beginners course walks kids through LEDs, sensors and motors step by step. Browse the full course catalog (including free self-paced courses), start the All-Access plan with a 7-day free trial ($39/mo), or book Remote 1-on-1 live lessons ($149/mo) by texting (619) 786-5731.
Live in San Diego? Join our weekend group sessions in Scripps Ranch: 1.5 hours a week, ages 6–18, max 10 kids, with every child working on their own project at their own pace. Your first session is free — text (619) 786-5731 to book.



Comments