Self Balancing Bot
Theory
A self-balancing bot is very similar to an Inverted pendulum as it needs to balance above its pivot point on the two wheels this system is unstable as it will fall on its own due to the gravitational pull on it, and it needs constant adjustments to keep it from falling.
You balance the bot by creating a counter torque to the torque created by earth gravity, and you can achieve that by moving the motors in the direction of falling.
Execution
You need a way to know the current state of the bot i.e. the tilt angle. This can be achieved using the MPU6050 it has a 3-axis Accelerometer and a 3-axis Gyroscope. It also contains a Digital Motion Processor (DMP) that is used to combine accelerometer and gyroscope data to get a more accurate reading and eliminate yaw drift.
First you need to calibrate the MPU and add the offsets to the code. You calculate the offsets using the example (IMU_Zero) from the MPU library.
We will be using I2Cdevlib-MPU6050 written by Jeff Rowberg to read the data from MPU6050.
After knowing the current state of the bot you need to move the motors in the right amount to create the right counter torque. This can be achieved using a PID controller
Note
The code for this project can be found on GitHub
Hardware
- MPU6050
- ESP32-WROOM
- DC Motors
- Wheels
- L293D Motor driver
- Boost converters × 2
- Li-ion battery
- TP4056 Charging board module
- 8×8 Dot matrix display with the MAX7219 driver × 2
Connection diagram

Software
Used Libraries:
- I2Cdevlib-MPU6050
- L293D
- MD_MAX72XX
- PID
We decided to have the PID code running alone on core 1 to make the bot more responsive.

Issues faced during this project
Can't control motor speeds individually
Using an ESP-32 and the L293D motor driver and L293D library I found that the motor speeds doesn't change they both run at the same speed if I change one motor speed to any value the other one changes as well. The problem was that both motors were running on the same PWM channel and the examples I used as documentation only used one motor and did not include the PWM channel as a parameter. The solution to this problem is simply changing the PWM channel for the motors the ESP has 16 PWM channels this means that it can output 16 different PWM waveforms, The default PWM channel that the L293D library use is 0 so you need to change the channel in one of the motors.
L293D rightmotor(IN1_A,IN2_A,EN_A,0);
L293D leftmotor(IN3_B,IN4_B,EN_B,1);
Can't get the robot to balance
After assembling the robot and making sure every thing works fine we noticed that the bot couldn't balance, but we also notice that wasn't because the motors are weak as they are strong enough to lift the bot from the ground it was because the bot wasn't reacting in time it was taking a long time till it decide to move the motors which will obviously make the bot unstable.
The problem
We found that the reason for the lack of responsiveness is the 10ms delay that was added in the Bluetooth code as all the code was running inside the main loop at first. The delay function blocks the execution of other code till the time delay ends that was the reason for the lack of responsiveness.
The solution
We decided to isolate the PID and motor control code and leave it running on the main loop, but we move all the other code i.e (Bluetooth, Dot Matrix) to the other core (0) and added them inside tasks to let the RTOS handle the multitasking.
Solving gimbal lock issues
The MPU after sometime or a specific movement outputs the wrong angle, and it seems to get stuck at 180ْ or any value larger than 100
Resetting the MPU seems to work, but I would have to reset the ESP manually to fix the gimbal lock issue.
After some trial and error trying to reset the MPU and the DMP I found that clearing the FIFO buffer seems to fix the issue.
So I added a condition to detect gimbal locks and fix them, If the tilt angle (pitch) is larger than 95ْ it resets the FIFO buffer
if (abs(pitch) >= 95) {
mpu.resetFIFO();
}