esp32 advanced 60 min

ESP32: build a 2WD robot base with motor control

Two DC motors, an L298N motor driver, an ESP32, and ultrasonic distance sensors. The base you build a robot on top of.

Code available for: ESP32 Arduino
Published Aug 25, 2026

A 2WD (two-wheel drive) robot base. Two DC motors, an L298N motor driver, an ESP32 controller, ultrasonic distance sensors for obstacle avoidance. The base is the foundation; you can add a sensor, an arm, or a camera on top.

This is the project that ties the HC-SR04, LEDC PWM, and BME280 (well, not BME280, but other sensors) tutorials together.

What you need

  • ESP32 dev board
  • 2WD robot chassis (the kit with motors, wheels, and a platform; about $10-20)
  • L298N motor driver module (the standard H-bridge; about $2)
  • 2 HC-SR04 ultrasonic distance sensors (front and back; about $2 each)
  • 18650 battery pack (7.4V, 2 cells in series; about $10)
  • Wires, screws, hot glue

Wiring

L298N IN1 -- ESP32 GPIO 25
L298N IN2 -- ESP32 GPIO 26
L298N IN3 -- ESP32 GPIO 27
L298N IN4 -- ESP32 GPIO 14
L298N ENA -- ESP32 GPIO 32 (PWM channel for left motor)
L298N ENB -- ESP32 GPIO 33 (PWM channel for right motor)

L298N +12V -- 7.4V battery +
L298N GND  -- ESP32 GND, battery -

HC-SR04 (front) TRIG -- ESP32 GPIO 5
HC-SR04 (front) ECHO -- ESP32 GPIO 18 (use voltage divider: 1k + 2k ohm)
HC-SR04 (back)  TRIG -- ESP32 GPIO 19
HC-SR04 (back)  ECHO -- ESP32 GPIO 23 (use voltage divider)

The L298N’s 5V logic output can power the ESP32’s 5V pin (with a diode or voltage regulator for safety). Or power the ESP32 from a separate 5V regulator.

The code

const int IN1 = 25;
const int IN2 = 26;
const int IN3 = 27;
const int IN4 = 14;
const int ENA = 32;
const int ENB = 33;

const int FRONT_TRIG = 5;
const int FRONT_ECHO = 18;
const int BACK_TRIG = 19;
const int BACK_ECHO = 23;

const int FRONT_OBSTACLE_DISTANCE = 25;   // cm
const int BACK_OBSTACLE_DISTANCE = 15;
const unsigned long OBSTACLE_CHECK_INTERVAL = 100;
const int MOTOR_SPEED = 200;   // 0-255

unsigned long lastObstacleCheck = 0;

void setup() {
  Serial.begin(115200);
  delay(1000);

  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
  pinMode(ENA, OUTPUT);
  pinMode(ENB, OUTPUT);

  pinMode(FRONT_TRIG, OUTPUT);
  pinMode(FRONT_ECHO, INPUT);
  pinMode(BACK_TRIG, OUTPUT);
  pinMode(BACK_ECHO, INPUT);

  // Setup LEDC PWM at 25 kHz for the motors (above audible)
  ledcSetup(0, 25000, 8);   // ENA on channel 0
  ledcSetup(1, 25000, 8);   // ENB on channel 1
  ledcAttachPin(ENA, 0);
  ledcAttachPin(ENB, 1);
}

void loop() {
  if (millis() - lastObstacleCheck > OBSTACLE_CHECK_INTERVAL) {
    lastObstacleCheck = millis();
    int frontDist = readDistance(FRONT_TRIG, FRONT_ECHO);
    int backDist = readDistance(BACK_TRIG, BACK_ECHO);

    if (frontDist < FRONT_OBSTACLE_DISTANCE) {
      stopMotors();
      reverse();
      delay(500);
      turnRight();
      delay(400);
    } else if (backDist < BACK_OBSTACLE_DISTANCE) {
      stopMotors();
      forward();
      delay(500);
      turnLeft();
      delay(400);
    } else {
      forward();
    }
  }
}

int readDistance(int trigPin, int echoPin) {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  long duration = pulseIn(echoPin, HIGH, 30000);
  if (duration == 0) return 999;   // out of range
  return duration * 0.0343 / 2;
}

void forward() {
  digitalWrite(IN1, HIGH);
  digitalWrite(IN2, LOW);
  digitalWrite(IN3, HIGH);
  digitalWrite(IN4, LOW);
  ledcWrite(0, MOTOR_SPEED);
  ledcWrite(1, MOTOR_SPEED);
}

void reverse() {
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, HIGH);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, HIGH);
  ledcWrite(0, MOTOR_SPEED);
  ledcWrite(1, MOTOR_SPEED);
}

void turnRight() {
  digitalWrite(IN1, HIGH);
  digitalWrite(IN2, LOW);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, HIGH);
  ledcWrite(0, MOTOR_SPEED);
  ledcWrite(1, MOTOR_SPEED);
}

void turnLeft() {
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, HIGH);
  digitalWrite(IN3, HIGH);
  digitalWrite(IN4, LOW);
  ledcWrite(0, MOTOR_SPEED);
  ledcWrite(1, MOTOR_SPEED);
}

void stopMotors() {
  ledcWrite(0, 0);
  ledcWrite(1, 0);
}

Upload. Place the robot on the floor. It should drive forward, stop when it sees an obstacle, reverse, turn right, and continue.

The “where am I going” problem

The obstacle-avoidance pattern above is a random walk: the robot bounces off things. It will eventually reach most areas but takes a long time.

For real navigation:

  • Wall following: use one sensor to track a wall on the left or right.
  • Mapping: record where obstacles are and build a map.
  • GPS / outdoor navigation: use a GPS module for outdoor waypoints.

The book ESP32 Robotics Projects covers SLAM (Simultaneous Localization and Mapping) on the ESP32.

The “stuck in a corner” problem

If the robot gets stuck in a corner, it can keep reversing and turning without escaping. Add an escape pattern: if the robot has been reversing for more than 3 times in a row, do a 180-degree turn.

int reverseCount = 0;

void loop() {
  // ...
  if (frontDist < FRONT_OBSTACLE_DISTANCE) {
    stopMotors();
    reverse();
    delay(500);
    reverseCount++;
    if (reverseCount > 3) {
      turnRight();
      delay(800);   // turn 180
      reverseCount = 0;
    } else {
      turnRight();
      delay(400);
    }
  } else {
    reverseCount = 0;
    forward();
  }
}

The battery

A 7.4V LiPo or 2x 18650 pack is the right choice. The L298N’s motor supply takes 5-35V, so 7.4V is in range. The motors typically draw 200-500 mA each, so the pack needs to supply at least 1A.

Battery life: about 1-2 hours of continuous driving. For longer runs, use a larger battery or sleep the motors between movements.

What you learned

  • A 2WD robot base with obstacle avoidance.
  • The L298N motor driver for bidirectional motor control.
  • LEDC PWM at 25 kHz for silent motor operation.
  • The “stuck in a corner” escape pattern.

What to build next

  • The HC-SR04 tutorial covers the distance sensor.
  • The LEDC PWM tutorial covers the motor speed control.
  • The book ESP32 Robotics Projects covers the full robotics stack (sensors, motors, mapping, navigation).