Introduction

Autonomous robots are no longer confined to research labs or industrial floors. With affordable microcontrollers and sensors, anyone can build a simple obstacle‑avoiding robot at home. This project teaches you the fundamentals of embedded C++ programming, sensor integration, and motor control. By the end, you’ll have a wheeled robot that navigates a room, backs away from walls, and chooses a new direction when it senses an obstruction. The skills you learn here apply directly to more complex systems like self‑driving cars, robotic vacuums, and mobile manipulators.

Why Build an Obstacle Avoidance Robot?

Obstacle avoidance is a core capability in robotics. It combines real‑world sensing with decision‑making logic, all running on a small microcontroller. This project forces you to think about timing, sensor noise, and reliable actuator commands. It also gives you a tangible result: a machine that reacts to its environment. Once you master the basic pattern – read sensor, decide, move – you can extend it to follow lines, map a room, or even chase a light.

Prerequisites and Required Components

Microcontroller

The brain of your robot can be an Arduino Uno, a Nano, or a NodeMCU. Arduino boards are well‑supported, have ample community examples, and work directly with the Arduino IDE for C++ programming. If you choose an ESP32 or STM32, you can use the same logic but may need to adapt pin mapping and PWM resolution.

Ultrasonic Distance Sensor

The HC‑SR04 is the most common choice. It measures distance from 2 cm to 400 cm by sending a 40 kHz ultrasound pulse and timing the echo. Its four pins (VCC, Trig, Echo, GND) connect easily to the microcontroller. For better accuracy, you can use an IR distance sensor or a LiDAR, but the ultrasonic sensor offers a good balance of cost and reliability for indoor use.

Motor Driver

An L298N dual H‑bridge motor driver is ideal for two DC motors. It can control direction and speed (via PWM) and handles up to 2 A per channel. Make sure you have separate power for the motors (a battery pack) and for the logic (usually 5 V from the Arduino). Alternatively, the L293D driver works for low‑power motors, but the L298N is more robust for a beginner robot.

Motors, Wheels, and Chassis

Two DC motors with wheels (e.g., 4.5 V to 6 V rated) and a simple acrylic or 3D‑printed chassis form the base. A third castor wheel or a ball caster provides stability. Ensure the wheels have good grip – rubber tires are preferable for carpets and smooth floors.

Power Supply

A 6‑cell AA battery holder (9 V total) or a dedicated 7.4 V Li‑Po pack powers the motors through the driver. The Arduino can be powered via USB or its own battery input (7–12 V). Always connect the grounds together to ensure a common reference.

Wiring the Circuit

Ultrasonic Sensor Connections

Connect the HC‑SR04 as follows:

  • VCC to 5 V on the Arduino
  • Trig to digital pin 9 (or any output‑capable pin)
  • Echo to digital pin 10 (must be interrupt‑ or pulse‑compatible)
  • GND to common ground

A 1 kΩ resistor between the Echo pin and the Arduino can protect the microcontroller, as the sensor outputs 5 V logic even on 3.3 V boards. For an Arduino Uno (5 V logic), this step is optional but recommended.

Motor Driver Connections

The L298N driver has two channels. Wire one motor to OUT1/OUT2 and the other to OUT3/OUT4. Connect the driver’s input pins to Arduino digital pins (e.g., IN1→pin 3, IN2→pin 4, IN3→pin 5, IN4→pin 6). For speed control, connect the driver’s ENA and ENB pins to PWM‑capable Arduino pins (e.g., pin 9 and pin 10 – but note pin 9 and 10 are used for the sensor? Use pins 11 and 3 for PWM). Better to choose different PWM pins. The code example later uses pins 3 and 11 for PWM, and pins 4,5 for direction.

Power wiring:

  • Motor power (12 V input on L298N) from battery pack
  • Logic power (5 V) from Arduino’s 5 V output (or from an on‑board regulator on the driver – check jumper settings)
  • Common ground between battery, Arduino, and driver

Complete Circuit Diagram

For clarity, here’s a table of connections (Arduino Uno pins):

HC-SR04:
  VCC → Arduino 5V
  Trig → D9
  Echo → D10
  GND → GND

L298N:
  +12V → Battery positive (7.4-12V)
  GND → Battery negative & Arduino GND
  +5V → Arduino 5V (remove jumper if using separate 5V)
  ENA → D11 (PWM)
  IN1 → D4
  IN2 → D5
  IN3 → D6
  IN4 → D7
  ENB → D3 (PWM)

Motors:
  Motor A → L298N OUT1 & OUT2
  Motor B → L298N OUT3 & OUT4

Double‑check all connections before powering on. A miswired motor driver can damage the Arduino or drain the battery quickly.

Programming the Robot with C++

Setting Up the Arduino IDE

Download and install the Arduino IDE (or use the web editor). Select your board and port from the Tools menu. The code is written in C++ with Arduino‑specific functions like digitalWrite() and pulseIn(). Understanding these functions is key to reading sensor data and controlling motors.

Core Logic: Sense‑Think‑Act

Every obstacle avoidance robot follows a simple loop:

  1. Sense: Trigger the ultrasonic sensor and measure the echo time. Convert the time to distance (in cm).
  2. Think: Compare the distance to a threshold (typically 20–30 cm). If an obstacle is closer than the threshold, decide a new direction (turn left or right). Otherwise, continue forward.
  3. Act: Set the motor direction pins and PWM values to execute the chosen movement.

This cycle repeats every 100–200 ms. A shorter delay makes the robot more responsive but may cause jittery movements.

Sample Code with Explanation

// Pin definitions
const int trigPin = 9;
const int echoPin = 10;
const int enA = 11;   // PWM speed for Motor A (left)
const int in1 = 4;
const int in2 = 5;
const int enB = 3;    // PWM speed for Motor B (right)
const int in3 = 6;
const int in4 = 7;

const int threshold = 25; // cm – distance that triggers avoidance

void setup() {
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(enA, OUTPUT);
  pinMode(enB, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);
  
  // Set initial speed (0-255)
  analogWrite(enA, 150);
  analogWrite(enB, 150);
  
  Serial.begin(9600); // for debugging
}

float readDistance() {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  
  long duration = pulseIn(echoPin, HIGH);
  // Distance = (speed of sound * time) / 2, where speed ≈ 0.0343 cm/µs
  float distance = duration * 0.0343 / 2;
  return distance;
}

void moveForward() {
  digitalWrite(in1, HIGH);
  digitalWrite(in2, LOW);
  digitalWrite(in3, HIGH);
  digitalWrite(in4, LOW);
}

void turnLeft() {
  // Stop right motor, run left motor forward (pivot)
  digitalWrite(in1, HIGH);
  digitalWrite(in2, LOW);
  digitalWrite(in3, LOW);
  digitalWrite(in4, HIGH); // reverse right motor for sharper turn
}

void turnRight() {
  // Stop left motor, run right motor forward
  digitalWrite(in1, LOW);
  digitalWrite(in2, HIGH);
  digitalWrite(in3, HIGH);
  digitalWrite(in4, LOW);
}

void stop() {
  digitalWrite(in1, LOW);
  digitalWrite(in2, LOW);
  digitalWrite(in3, LOW);
  digitalWrite(in4, LOW);
}

void loop() {
  float distance = readDistance();
  
  Serial.print("Distance: ");
  Serial.print(distance);
  Serial.println(" cm");
  
  if (distance < threshold) {
    stop();
    delay(200);
    turnLeft();   // You can randomize left/right later
    delay(500);   // Turn for 0.5 seconds
    stop();
  } else {
    moveForward();
  }
  
  delay(100);
}

Key Functions and Tuning

The readDistance() function uses pulseIn() to measure the echo pulse width. The formula duration * 0.0343 / 2 converts microseconds to centimeters. For inches, multiply duration by 0.0135. The threshold variable sets the minimum safe distance. Adjust it based on your robot’s speed and the size of obstacles. A high threshold (40 cm) makes the robot cautious; a low one (15 cm) requires faster reactions.

The motor control functions use a simple forward/reverse setup. turnLeft() and turnRight() can be improved by using PWM to control turn speed – a slower turn is often more effective. Experiment with different PWM values (e.g., 120 for forward, 200 for turning) to achieve smooth motion.

Adding a Random Turn Direction

To avoid getting stuck in a loop (e.g., always turning left), use the Arduino’s random() function. After detecting an obstacle, generate a random number and choose left or right accordingly. This simple addition makes the robot’s behavior unpredictable and more effective at clearing corners.

Testing and Debugging

Using the Serial Monitor

Open the Serial Monitor (Tools → Serial Monitor) set to 9600 baud. You will see the distance readings in real time. Place a book 10 cm from the sensor; the reading should be close to 10 cm. If the reading is erratic (e.g., 0 cm or 500 cm), check wiring, especially the Echo pin. Ensure the sensor is not facing a sound‑absorbing surface (e.g., curtains).

Common Issues and Fixes

  • Robot doesn’t move: Verify motor driver power and ground. Check that ENA/ENB jumpers are installed (or PWM pins are set). Listen for motor hum.
  • Robot moves backward: Reverse the motor polarity in the motor driver connections or swap the direction pins in code.
  • Sensor always reads 0: Ensure Trig is set as OUTPUT and Echo as INPUT. The pulseIn() function waits for the echo; a timeout value (default 1 second) can be set as a second argument.
  • Robot spins uncontrollably: The sensor may be misaligned or threshold too low. Test with a longer delay after turning.

Calibration

Measure the actual speed of sound in your environment: it varies with temperature (331 m/s at 0°C, plus 0.6 m/s per degree Celsius). For most indoor conditions, the constant 0.0343 is accurate enough. If you need precision, use a temperature sensor and calculate 0.0343 * sqrt((temp+273)/273).

Going Further: Advanced Features

Once your basic robot works reliably, consider these enhancements:

Pan‑and‑Tilt Sensor Mount

Mount the ultrasonic sensor on a servo motor that sweeps left and right (0°–180°). Before moving, the robot scans the environment to find the clearest path. This reduces collisions with side objects and makes navigation much smoother. The servo adds only a few lines of code and a small power draw.

Proportional Speed Control

Instead of a binary stop‑turn‑go, vary the speed based on distance. For example, if the obstacle is 30 cm away, reduce forward speed; if it’s 15 cm, start turning while still moving slowly. This mimics how you would approach a wall – slowing down before a sharp turn. Implement this by mapping the distance to a PWM value using map() or a simple if‑else chain.

Multiple Sensors

Add two or three ultrasonic sensors covering the front, left, and right. This gives the robot a better “field of view” and allows it to plan a path. You can also integrate a line‑following sensor (e.g., IR reflector) to switch between obstacle avoidance and line tracking modes.

PID Control for Straight Movement

Even with identical motors, the robot tends to drift due to friction and voltage differences. Implement a simple proportional‑integral‑derivative (PID) loop that compares the desired speed of each wheel and adjusts PWM values. The error can be derived from gyroscope data (IMU) or from wheel encoders if you have them.

Integration with ROS or Custom Protocols

For larger projects, you can replace the simple logic with a ROS (Robot Operating System) node running on a Raspberry Pi that communicates with the Arduino over serial. The Arduino handles low‑level motor control while the Pi does sensor fusion and path planning. This separation makes the system more modular and easier to debug.

Conclusion

Building an obstacle avoidance robot with C++ is a rewarding step into the world of autonomous systems. You have learned how to interface an ultrasonic sensor, drive DC motors, and write decision‑making logic. The basic pattern you coded – read, decide, act – is the foundation of every autonomous vehicle, from a robot vacuum to a self‑driving car. Keep iterating: add more sensors, improve the algorithm, and test in different environments. The skills you gain here will serve you well in any embedded or robotics project.

For further reading, consult the Arduino Language Reference, the HC‑SR04 tutorial by SparkFun, and the L298N driver tutorial on How to Mechatronics. Happy building!