Thanks to the precise control via the PS5 controller, our ESP32 robot car was already a real eye-catcher. So the next unavoidable upgrade was due: it needs an arm with a gripper!
We mounted the robot arm firmly on the holder at the rear of the robot car. We also moved the control of the robot car and the robot arm into clean C++ class structures. Now we can drive and grab at the same time. The result is an impressive, remote-controlled multitool.
The moment the code for the arm control ran for the first time was priceless. My 8-year-old daughter asked in amazement: “Dad, how fast can you program?” I proudly explained: “The AI assistant GEMINI on my computer programmed that!” A perfect example of how explaining ideas becomes the new superpower in combination with AI assistants in the maker world.

ESP32 robot car with robot arm
💡 Why this project is ideal for learning
This project is more than just an exciting upgrade. It is the perfect playground to master servo motors, their exact control and the I2C protocol.
1. Servo motor control: precision instead of speed
With classic DC motors, we control the speed. With servo motors, we control the exact position in degrees, for example from 0° to 180° depending on the servo motor.
The learning process is essential here:
- PWM mapping: You learn how to translate a desired angle (e.g. $45°) into a concrete PWM pulse width value (e.g. 150 to 600 out of 4096 maximum). This happens in our
RobotArmclass with the functionmapServoAngleToPWM(int angle). - Mechanical limits (constrain): You have to define minimum and maximum angles (
constrain) for each joint of the robot arm. This is the only way to prevent the arm from hitting its mechanical stops and damaging the servos.
2. Offloading the ESP32 with the PCA9685
We use a PCA9685 PWM driver for the four servos. Why?
- The ESP32 only has a limited number of PWM channels for the motors and LEDs.
- The PCA9685 takes over generating the precise 50 Hz PWM signals for the servos.
- All communication runs efficiently over the I2C bus (SDA 21, SCL 22), which we share with the OLED display. You only send a short I2C command to the PCA9685, and it handles the time-critical pulse generation. This keeps the ESP32 main processor free for processing the gamepad.
🏗️ The architectural setup
To manage the complexity, we split the project into clear, modular C++ classes: RobotCar, RobotArm, ControllerManager and DisplayManager.
Core components at a glance:
- Controller: PS5 controller (via Bluepad32).
- Arm control: PCA9685 (I2C address
0x40) for the four servos (base, arm 1, arm 2, gripper). - Vehicle control: ESP32 LEDC PWM channels (0 to 3) for the L298N motor driver.
- Display: SSD1306 OLED display (SDA 21, SCL 22) shares the I2C bus with the PCA9685.
The magic lies in the ControllerManager class. It forwards the input of the left joystick to the car control (RobotCar::processControllerInput) and the buttons and the right joystick to the arm control (RobotArm::processControllerInput).
🎬 Video: Optimizing the robot arm code with Gemini
Even when the robot arm is fully assembled mechanically, constant optimization is needed. In this video you can see how I use the AI assistant Gemini to analyze and fix a button conflict in the Bluepad32 control. The AI provides precise instructions for adjusting the code. Now every function has its own conflict-free button for perfect control of the robot.
With this robust and modular setup, our robot car is ready for any gripping job!
What are your robot upgrade plans? Let me know in the comments!
Happy robot car building!












Recent Comments