Ever since I tried turning the Raspberry Pi into an FM transmitter, I had wondered if it would be possible to make it drive a radio-controlled car. It turns out that it is! With no modifications to your Pi, you can be driving around a toy-grade RC car iFe, and I’ve read about a few other ones online that should work as well. Any RC toy that works at a frequency in the range of 1-250 MHz should be controllable by the Pi once you figure out the command signals.
I’m going to talk a little bit about how RC cars work, how the code and hardware works on the Pi, and about RC controls in general. If you just want to start driving around, you can jump to the end.
Raspberry Pi FM radio
The first version of the Raspberry Pi FM transmitter was written by Oliver Mattos and Oskar Weigl. It works by using the hardware that is normally meant to produce spread spectrum signal clock signals on the GPIO pins to generate a radio frequency. The GPIO pin itself acts as an antenna, which you can extend by attaching a length of wire such as a jumper cable. By reading samples from an audio file and writing them out to the GPIO pin at a certain rate, it is possible to tune in a radio and hear the audio.
The original version of this code had a small problem though. FM requires a continuous stream of radio signals, but the Raspberry Pi normally runs Linux, which is not a real-time operating system. This means that you can start, stop, and run different programs at the same time, but because the Pi only has a single CPU core, it has to simulate multiple programs running at the same time by constantly switching between them. Whenever there was a context switch away from the program that was writing to the GPIO pins, the radio signal would skip and pop. Richard Hirst came up with a great solution to this problem: use the DMA controller to continually write data to the GPIO pins. Because DMA bypasses the CPU altogether, it is not affected by context switches, and it has the side effect of reducing the total CPU load because the controlling program now only has to periodically refill a circular buffer. Unfortunately, it also means that it’s not possible to stop broadcasting for short periods of time (at least not that I’ve been able to figure out), which will be problematic later when we try to control an RC car.
Frequency Modulation, Amplitude Modulation, and Pulse Modulation
There are two common ways of encoding audio over a radio signal: frequency modulation (FM) and amplitude modulation (AM). FM works by continually varying the frequency of the signal in small amounts while keeping the amplitude the same, while AM keeps the frequency the same while varying the amplitude. The difference is best described with a picture:
Animated diagram representing the difference between radio waves modulated by amplitude and by frequency. By Berserkus, from Wikipedia. Used under CC BY-SA 2.5.
The Raspberry Pi FM program uses the fractional divider to control and change the frequency of the generated signal. This allows it to generate an FM audio signal. Because the Pi is just writing values to the GPIO pin, I don’t think that it’s possible to control the amplitude of the signal, so I don’t think that it’s possible to have the Pi generate AM audio signals.
There are a few different control schemes that remote control toys use. Most toy-grade RC cars (ones with forward/back + left/right control) use pulse modulation. The radio controller broadcasts a signal at a constant frequency for a certain amount of time, and then stops broadcasting for another amount of time. It then repeats this cycle with varying lengths of broadcast and pause time in a particular pattern. The RC car recognizes this pattern as a command, such as “forward” or “reverse right” and starts moving.
The specific pattern typically consists of some base time length, such as 400 microseconds, and all pulses and pauses are multiples of that length. A control signal starts with a synchronization pattern consisting of a broadcast for some length of time and a pause, which is repeated a certain number of times. Then a number of bursts and pauses are repeated for the command. For example, the New Bright 1:24 truck sends synchronization signals of 1200 microsecond bursts with 400 microsecond pauses repeated 4 times, followed by a number of 400 microsecond bursts and 400 microsecond pauses. If that signal is repeated 40 times, the vehicle drives forward, while 28 bursts tell it to drive reverse and right.
Remember earlier when we had to use DMA to ensure a constant signal? Because of this, I haven’t been able to figure out how to make the Pi stop broadcasting for a consistent amount of time, which is needed for the signal pauses. However, think back to how FM radio signals work. Because the Pi send FM, it can very quickly change the frequency that it is broadcasting on. To simulate a broadcast pause, it instead broadcasts at a different frequency for a length of time. Because the RC car is only listening for signals at one particular frequency, as far as it is concerned, the Pi has stopped broadcasting altogether.
A step up from toy-grade cars usually have proportional steering and throttle control, so you can more precisely control the vehicle. Most high-end hobby grade devices use pulse-position modulation. Andrew Hazelden has an excellent explanation of how PPM works. I wanted to get a car with proportional steering and throttle control, but most RC cars that I looked at operate in gigahertz range which is too high for the Pi, so I instead bought a RadioShack Dune Warrior which runs in the 27 MHz range.
Finding the signal
With toy-grade cars, I’ve found that the easiest way to find the signals that control them is to just use brute force. They mostly use very similar signal patterns, so you can simply iterate through different control pattens until the car responds. For more complex patterns, you’ll probably need to use an oscilloscope to analyze the patterns.
For toy-grade RC cars, you can just turn on the car and iterate through different control patterns until you find one that causes the car to move. Every car that I’ve looked at uses one synchronization pattern for all of the controls that it can respond to, so once you find one signal, it’s just a matter of guessing how many signal repeats control the remaining actions.
Iterating through all possible patterns can take a few hours, and I didn’t want to wait around for the car to move, so I instead pointed a webcam at the car and then waited until the image changed. Once the car moves, the computer will see that the image has changed and will save the image and the pattern.
I tried a few different techniques to monitor the webcam to decide if the car has moved. First, I tried just computing the percent difference of the color values of the pixels between a base photo and the most recent photo. This generated a lot of false positives due to changing lighting conditions, such as when a cloud passes overhead. If you run this program, I recommend placing the car in a closet or some other place where you can keep the lighting consistent.
Next, I converted the image to greyscale and then normalized the colors so that the darkest color was black and the lightest color was white. This reduced the noise between pictures a lot and helped with my varying lighting conditions, but I was still worried about false positives. I tried one more thing: reducing the image depth to 1 bit, so it was only black and white.
This reduced the noise to a really low level, and because the wall behind the car was white, once it moved, the difference was very clear. This reduced my false positives to 0, so I stopped here. I had considered running edge detection on the base image and then ignoring differences along the detected edges because that’s where most of the difference between photos appeared but I ended up not needing to.
For more complex vehicles, it’s easiest to hook an oscilloscope up to the radio controller and then observing the changes in the command signal as you move the controller. This is what we ended up doing with the Dune Warrior.
Dune Warrior, straight and full throttle
We hooked up the oscilloscope and watched how the signal changed as we adjusted the throttle and the steering. If you want to do this yourself, I strongly suggest recording the signal to video so that it can be reviewed later. The signal starts with a 500 microsecond synchronization burst and pause, followed by 22 more bursts. Each burst is either 127 microseconds or 200 microseconds, with a pause of the same length. The burst lengths are used to encode a binary pattern, where the shorter bursts are a 0 and the longer bursts are a 1. 6 bits are for steering, 5 are used for the throttle, and 1 is used as an even parity bit; the remainder never appear to change and I’m not sure what their function is. What’s strange is that the bits for steering are not contiguous; bits 7-8 form the 2 most significant bits, while 1-4 form the rest. 32 is centered for steering, while 0 is sharp left and 63 is sharp right. Similarly, 16 is idle for throttle while 0 is full reverse and 31 is full forward.
Running your own car
If you have a cheap toy-grade RC car (i.e. one that only has controls for forward/backward and left/right) then you should be able to find the command signals and control the car pretty easily. Clone the repository from my GitHub on your Raspberry Pi and compile pi_pcm by running scons. Run pi_pcm as root. By default, the program listens for JSON commands on port 5432.
Turn on the car and put it in a location where you can control the lighting (such as a closet). Point a computer that has a webcam at the car (it can be the same computer or a separate one), run python watch.py -f [frequency] -s [Pi IP address] and enter the frequency of your RC car (this should normally be 27 or 49 MHz). You’ll need to have gstreamer installed to capture the webcam stream. Most cars operate on one of a several different channels in the 27 or 49 MHz range; if you don’t know the exact channel, then just enter 27 or 49 and the program will iterate through each channel for you.
Once the program sees the car move, it will save a picture and rename it according to the command it just sent. The filename format is frequency-microseconds-synchronizationBurstLength-synchronizationBurstCount-signalCount. From there, you can run python control.py -f [frequency] -s [Pi IP address]. It will prompt you for the command information and ask you for different signal bursts; try different ones (1 – 100 or so) and observe how the RC car reacts. Once you have all of the signals, save the information in a JSON file (see control-specs/pro-dirt.json for an example). Now you should be able to run python interactive_control.py -s [Pi IP address] [JSON control file] and drive the car using the arrow keys.
There are a few other projects that you cna build from here. For one, I’m planning on entering the SparkFun Autonomous Vehicle Competition with my Dune Warrior. You can make an autonomous vehicle yourself by buying a USB battery pack for the Pi and taping both to the top of your car and writing a simple control program. I’m also planning on buying a Raspberry Pi camera, slapping on a WiFi USB dongle, and implementing Twitch drives your car in my apartment.