I wanted to build a small RC car, just for kicks. My idea was to have a little platform to play around with different steering mechanisms and types.

As didn’t want to buy a full blown RC controller, which also needs a special kind of receiver, and is a pretty expensive subject, if you don’t just want to axes. My plan was to let a microcontroller do the steering on the car, I used an ESP32 and my PS5 Controller and went with Bluetooth.

I’m mainly going to use the RC car in my living room, so a high range was not important.

The hardware

The car is composed of a 3d printed chassis, which houses two servos for steering both axes and four pods, enclosing the motors, rotating on a ball bearing press fitted to a brass pipe. The motor leads run through the pipe into the pods.
On top of the chassis is a breadboard, holding the ESP and the 5V power supply, as the car was intended to run on 9V blocks.

Connecting the controller to the controller

Googling for libraries to help me use my PS5 Controller I found the PS4-ESP Repo by aed3, which was everything I wanted, with a catch. It didn’t output anything useful. So I kept searching and found USB-Host Controller, which was additional hardware, which I didn’t want to buy, so it was out.
Later I found Bluepad32, a really neat library to use all kinds of Bluetooth based game controllers with an ESP32. It was a bit cumbersome to get into the control-loop of this library, as the most examples are pretty long. The library seems to be well layed out, but in the end, I could not get it to compile and turned to the PS4 code again.
It was already receiving packages and displaying data, the data was just garbage. So I tried a basic approach and counted bytes. I made the ESP put out the full package as hex numbers, let it connect to the PS5 Controller and moved the axes. There it was. One 00 went up to FF and back down as I moved the throttle. Then I compared the counted index to the index in the parser. It was off by -2, optimistically I applied this offset to all the indexes. It worked. All the Joysticks and the buttons did their job.

Power and Resistance

As stated, the cars power source is a 9V block, not the eco friendly electric car one would buy, but as a RC it should be enough, I figured. I used a voltage regulator I had lying around to power the ESP, the Servos and the motor driver.
It worked great, as long as it was plugged to the Computer I flashed it with. After turning the PS controller on it lightened its blue LEDs and the car would move its steering for a split second before disconnecting from the controller, which it conveniently showed by turning off.

I hooked up my multimeter to find out, what was going on and sure enough found myself back in school 10 years ago. We learned about the internal resistance of power sources. Especially batteries are prone to voltage drop, when a load is applied. So the 9V printed on the package are the idle voltage, which can drop to under 5V when a high current is drawn from the battery. As I lacked more 9V clips, I hooked up my powerbank to the controller. This made it work, sort of. The wheels were not turning.
As I tested the motors before with PWM inputs, generated based on the PS Controllers input, I knew it had to work. Plugging the Motors signal pins to 5V made them spin in the right direction, but made the throttle binary. The steering on the other hand worked fine, I confirmed the throttle value really was written to the right PWM channel, which it apparently was.

PWM in channels

Unlike other microcontrollers the ESP32 does not have a one to one relationship, when it comes to PWM pins and channels. In most Atmel-based cores there is an analogWrite(pin, dutyCycle), which directly sets the PWM timer for the pin and starts it. The ESP32 has channels, so you can let multiple pins have the same output value by assigning them to the same channel. There are 16 channels.
The Arduino-Servo Library is not available on the ESP32, but there are plenty of alternatives. I used the first one I saw, assuming it worked like the one I knew. It did turn my servos correctly. It took a while, until I changed the PWM channels for the motors from 0 and 1 to 14 and 15. The servo library simply uses the first PWM channel it can get its hands on and counts up.
You can, however, also tell it, which channel to use.

It’s rolling

In the end all the work payed off. I now have an RC car, I can mess around with.
The first thing, that comes to my mind, is to give it some sensors. A set of accelerometers and gyros, to let it calculate its speed and be able to turn on command.
A LIDAR would be a nice addition. Equipped with that, it could map its surroundings and avoid obstacles.
A GPS chip could give it the ability to navigate larger distances.
I could equip every wheel with one or more small magnets and add an hall effect sensor to the motor pods. This way I could keep track of the wheels speeds and might detect slip and reduce the motor current…

It won’t get boring, that’s for sure.