How to Build a Self-Balancing Rover with ESP32

IMU-based stabilization with live battery and tilt stats on OLED

ESP32VehiclesIntermediate60 minutes3 components

Updated

How to Build a Self-Balancing Rover with ESP32
For illustrative purposes only
On this page

What you'll build

This guide walks you through building a self-balancing two-wheeled rover that stays upright using an ESP32, an MPU6050 inertial measurement unit, a pair of DC gear motors with an L298N driver, and a compact OLED screen for real-time telemetry. The rover continuously reads accelerometer and gyroscope data, fuses them with a complementary filter, and feeds the resulting tilt angle into a PID control loop that adjusts motor speed dozens of times per second. The OLED displays live tilt angle, battery voltage, and PID tuning values so you can observe the control system at work without needing a serial monitor.

PID control is one of the most important concepts in robotics and industrial automation, and a self-balancing robot is the classic hands-on way to learn it. You will implement proportional, integral, and derivative terms from scratch, understand how each one contributes to stability, and develop an intuition for tuning gains by watching the rover respond in real time. The project also covers reading raw IMU registers over I2C, converting them into meaningful orientation data, and managing two independent motor channels through an H-bridge driver -- skills that underpin everything from drones to robotic arms.

When you are finished you will have a rover that balances on two wheels, recovers from gentle pushes, and displays its internal state on screen. The modular code structure makes it straightforward to add remote control over Bluetooth or Wi-Fi, introduce obstacle avoidance with an ultrasonic sensor, or swap in stepper motors for finer control. This is a satisfying intermediate project that bridges the gap between simple sensor demos and full autonomous robotics platforms. The same MPU6050 IMU used here also features in the fitness wristband, where accelerometer data drives step counting instead of balance control.

Wiring diagram

Wiring diagram

Interactive wiring diagram

Components needed

ComponentTypeQtyBuy
MPU6050 IMUsensor1€3.35
SSD1306 OLEDdisplay1€4.30
L298N Motor Driveractuator1€2.15

Prices and availability are indicative and may have been updated by the supplier. Schematik may earn a commission from purchases made through affiliate links.

Assembly

1

Connect IMU and OLED on I2C bus

Wire MPU6050 and SSD1306 to shared SDA GPIO21 and SCL GPIO22.

2

Wire motor driver control lines

Connect L298N IN1-IN4 to GPIO25, GPIO26, GPIO32, and GPIO33.

Pin assignments

PinConnectionType
GPIO 21mpu6050-1 SDAI2C
GPIO 22mpu6050-1 SCLI2C
GPIO 21oled-1 SDAI2C
GPIO 22oled-1 SCLI2C
GPIO 25motor-driver-1 IN1PWM
GPIO 26motor-driver-1 IN2PWM
GPIO 32motor-driver-1 IN3PWM
GPIO 33motor-driver-1 IN4PWM

Code

#include <Wire.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_SSD1306.h>

#define SDA_PIN 21
#define SCL_PIN 22
#define IN1 25
#define IN2 26
#define IN3 32
#define IN4 33

Adafruit_MPU6050 mpu;
Adafruit_SSD1306 display(128, 64, &Wire, -1);
float targetAngle = 0.0f;
float kp = 24.0f, kd = 0.85f;
float lastError = 0.0f;

void setup() {
  Wire.begin(SDA_PIN, SCL_PIN);
  mpu.begin();
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  pinMode(IN1, OUTPUT); pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT); pinMode(IN4, OUTPUT);
}

void loop() {
  sensors_event_t a, g, t;
  mpu.getEvent(&a, &g, &t);
  float angle = atan2(a.acceleration.x, a.acceleration.z) * 57.2958f;
  float error = targetAngle - angle;
  float control = kp * error + kd * (error - lastError);
  lastError = error;

  int pwm = constrain((int)abs(control), 0, 255);
  bool forward = control > 0;
  analogWrite(IN1, forward ? pwm : 0);
  analogWrite(IN2, forward ? 0 : pwm);
  analogWrite(IN3, forward ? pwm : 0);
  analogWrite(IN4, forward ? 0 : pwm);

  display.clearDisplay();
  display.setCursor(0, 0);
  display.printf("Angle: %.2f\nCTRL: %.1f", angle, control);
  display.display();
  delay(20);
}

// Run this and build other cool things at schematik.io
Libraries: Adafruit MPU6050, Adafruit SSD1306, Adafruit GFX Library