Obstacle-avoiding car on an Arduino Uno
A 2WD robot car on an L298N with an HC-SR04 on the front. It drives ahead, and when something comes within 25 cm it backs up, turns and carries on. The whole build, an Arduino Uno and 3 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
Teaching? Assign it to your class in one click.
Intermediate About 30 minutes to build and run. Runs in your browser. Free, and no account needed.
Build it as the Robot Car Kit: the parts in numbered bags and a booklet that checks every step. Retails for about $50, free on Mokxi.
Step by step, with the wiring and the common mistakes: Arduino Obstacle Avoiding Robot: Code and Wiring
What you'll learn
- Measuring distance with an ultrasonic sensor
- Driving two DC motors with an L298N
- Direction pins plus a PWM speed pin
- A simple look-then-decide robot loop
- Difficulty: Intermediate
- Time: About 30 minutes
- Board: Arduino Uno R3
- Parts: 5
How it works
An HC-SR04 ultrasonic ranger on the front measures the distance ahead. The sketch pings it about every 60 ms. If the road is clear, both motors run at CRUISE (200 out of 255).
If something is closer than STOP_MM (250 mm), the car stops, backs up for 400 ms, then turns right on the spot for 450 ms and looks again. A reading of 0 means no echo, so the car treats it as nothing there.
An L298N driver runs the two motors from a 7.4 V pack. Each side has two direction pins and one speed pin.
Parts list
5 parts, plus the jumper wires. They retail for about $45; on Mokxi they're free.
- 1 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Motor driver module, L298NIn Robot Car Kit.
- 1 × Robot car chassis, 2WDIn Robot Car Kit.
- 1 × Ultrasonic rangerIn Halloween Prop Kit, Robot Car Kit.
- 1 × Power, 7.4 V
Wiring table
Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.
| Part and pin | Connects to |
|---|---|
| Arduino Uno R3 pin GND | Ground |
| Ultrasonic ranger pin TRIG | Arduino Uno R3 pin 12 |
| Ultrasonic ranger pin ECHO | Arduino Uno R3 pin 11 |
| Motor driver module, L298N pin ENB | Arduino Uno R3 pin 10 |
| Motor driver module, L298N pin IN4 | Arduino Uno R3 pin 9 |
| Motor driver module, L298N pin IN3 | Arduino Uno R3 pin 8 |
| Motor driver module, L298N pin IN2 | Arduino Uno R3 pin 7 |
| Motor driver module, L298N pin IN1 | Arduino Uno R3 pin 6 |
| Motor driver module, L298N pin ENA | Arduino Uno R3 pin 5 |
| Robot car chassis, 2WD pin L+ | Motor driver module, L298N pin OUT1 |
| Robot car chassis, 2WD pin L- | Motor driver module, L298N pin OUT2 |
| Motor driver module, L298N pin 12V | 7.4 V |
| Motor driver module, L298N pin GND | Ground |
| Ultrasonic ranger pin VCC | Motor driver module, L298N pin 5V |
| Robot car chassis, 2WD pin R+ | Motor driver module, L298N pin OUT3 |
| Robot car chassis, 2WD pin R- | Motor driver module, L298N pin OUT4 |
| Ultrasonic ranger pin GND | Ground |
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Obstacle-avoiding car: a 2WD chassis, an L298N and an HC-SR04.
//
// L298N ENA pin 5, IN1 pin 6, IN2 pin 7 left motor (ENA jumper off)
// ENB pin 10, IN3 pin 8, IN4 pin 9 right motor (ENB jumper off)
// 12V from the 7.4 V pack, GND to the Uno's GND, regulator jumper on
// HC-SR04 TRIG pin 12, ECHO pin 11, VCC to 5V, GND
//
// The whole robot is one loop: look, then decide. It pings every 60 ms,
// because the datasheet asks for that much quiet between pings so the last
// echo has died away. Clear ahead, it drives. Something closer than STOP_MM,
// it stops, backs off, turns right on the spot and looks again.
//
// On the canvas, drag the HC-SR04's slider to put something in front of the
// car, and watch it back away and turn on the floor drawn in the chassis.
// A reading of 0 means no echo at all, which is "nothing there", not "right
// against the sensor".
#include "mokxi_hcsr04.h"
const int ENA = 5;
const int IN1 = 6;
const int IN2 = 7;
const int ENB = 10;
const int IN3 = 8;
const int IN4 = 9;
const int TRIG = 12;
const int ECHO = 11;
const uint16_t STOP_MM = 250;
const int CRUISE = 200;
Sonar sonar(TRIG, ECHO);
// One side of the car, as a signed speed from -255 to 255. Zero brakes.
void side(int en, int a, int b, int speed) {
digitalWrite(a, speed > 0 ? HIGH : LOW);
digitalWrite(b, speed < 0 ? HIGH : LOW);
if (speed < 0) speed = -speed;
analogWrite(en, speed == 0 ? 255 : (speed > 255 ? 255 : speed));
}
void drive(int left, int right) {
side(ENA, IN1, IN2, left);
side(ENB, IN3, IN4, right);
}
void setup() {
const int pins[] = {ENA, IN1, IN2, ENB, IN3, IN4};
for (int i = 0; i < 6; i++) {
pinMode(pins[i], OUTPUT);
digitalWrite(pins[i], LOW);
}
sonar.begin();
Serial.begin(115200);
Serial.println("obstacle-avoiding car: ready");
}
void loop() {
uint16_t mm = sonar.read();
if (mm != 0 && mm < STOP_MM) {
Serial.print("obstacle at ");
Serial.print(mm);
Serial.println(" mm: back up and turn");
drive(0, 0);
delay(200);
drive(-CRUISE, -CRUISE);
delay(400);
drive(CRUISE, -CRUISE);
delay(450);
drive(0, 0);
delay(100);
} else {
drive(CRUISE, CRUISE);
}
delay(60);
}
Code walkthrough
The key functions of the sketch, and what each one does.
side sketch.ino
Controls one side of the car with a signed speed from -255 to 255. Positive sets one direction pin HIGH and negative sets the other. The enable pin gets the PWM speed. A speed of zero brakes.
// One side of the car, as a signed speed from -255 to 255. Zero brakes.
void side(int en, int a, int b, int speed) {
digitalWrite(a, speed > 0 ? HIGH : LOW);
digitalWrite(b, speed < 0 ? HIGH : LOW);
if (speed < 0) speed = -speed;
analogWrite(en, speed == 0 ? 255 : (speed > 255 ? 255 : speed));
}drive sketch.ino
Calls side for the left motor (pins 5, 6, 7) and the right motor (pins 10, 8, 9). Two speeds with opposite signs spin the car on the spot.
void drive(int left, int right) {
side(ENA, IN1, IN2, left);
side(ENB, IN3, IN4, right);
}loop sketch.ino
Reads the distance in millimeters. A nonzero reading under STOP_MM triggers the escape: stop, reverse, turn right, stop. Otherwise the car drives forward. A 60 ms delay ends each pass.
void loop() {
uint16_t mm = sonar.read();
if (mm != 0 && mm < STOP_MM) {
Serial.print("obstacle at ");
Serial.print(mm);
Serial.println(" mm: back up and turn");
drive(0, 0);
delay(200);
drive(-CRUISE, -CRUISE);
delay(400);
drive(CRUISE, -CRUISE);
delay(450);
drive(0, 0);
delay(100);
} else {
drive(CRUISE, CRUISE);
}
delay(60);
}Troubleshooting
The usual mistakes with this circuit, and the fix for each.
- The car spins or reverses when it should go forward This project
Swap the two wires of that motor on the L298N output, or swap its two direction pins. - The car never reacts to a wall This project
Check that TRIG goes to pin 12 and ECHO goes to pin 11. In the simulator, drag the ranger's slider below 250 mm. - Nothing moves This project
The L298N needs the 7.4 V supply on its 12V pin, and its GND must join the Uno's GND. - The motors do not run or run weakly. Motor driver module
Check the power jumpers and supply. Motor voltage goes on the +12V terminal, and ground must join the Arduino's ground. The driver drops about 2 V, so use a supply above the motor voltage. - Speed control with analogWrite does nothing. Motor driver module
Remove the ENA and ENB jumpers and connect those pins to PWM pins. With the jumpers on, the channel is always at full speed. - The car drives in circles or goes backwards. Robot car chassis
Motor polarity decides the direction. Swap the two wires on a motor that spins the wrong way. For a straight run, the motors are never identical, so trim the speed on one side. - The motors do not run, or the board resets when they start. Robot car chassis
Motors draw far more current than a pin can give. Use a motor driver such as an L298N or L293D. Power the motors from a separate battery pack and join its ground with the Arduino ground. - Distance reads zero, or never changes. Ultrasonic ranger
Power it from 5 V and share ground. Send a 10 microsecond HIGH pulse on TRIG, then time ECHO with pulseIn. Check TRIG and ECHO are not swapped.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.