Whether you’re modifying a remote controlled vehicle or creating something completely new, Arduino boards are a great way to expand the functionality of your RC receiver. Adding a microcontroller lets you program complex logic functions, sound effects, lighting animations, and more – all managed from the comfort of a wireless remote.

In this tutorial I’m going to show you how to connect a PWM-based RC receiver to an Arduino and read data from it using the Servo Input library.

Gathering Materials

For this tutorial you’ll need a few components:

If you don’t have an RC controller and receiver handy, you can also use a second Arduino with the Servo library instead. In this case you’ll need male to male jumpers instead of male to female.

Note that the above list contains Amazon affiliate links which help fund the content on this site. Thank you for your support!

Making Connections

The majority of RC receivers are designed to drive servo motors and use what are called pulse-width modulated (PWM) signals. The connections are arranged in a line of 3 pins, repeated by however many channels your receiver has. In order, these are:

  • Signal (⎍)
  • Power (+)
  • Ground (-)

These connections should be labeled somewhere on your receiver. Power will always be in the center, but signal and ground may be ‘flipped’ depending on the orientation of your receiver. Some receivers have a “Futaba” notch so you can’t plug the servos in backwards. This notched cutout will be next to the signal pin.

Caution: Voltage Levels

Before anything else we need to check the acceptable voltage levels for both the receiver and the Arduino. Too little voltage and signals may not be picked up properly. Too much voltage and something might get damaged!

The operating voltage of your receiver should be listed in the manual or on the product page if your purchased it online. My DUMBORC X6F receiver for instance has a voltage range of 4.8V – 10V, so it will work fine with the 5V power from an Uno.

Most Arduinos, like the Uno and Leonardo, run at 5V and accept inputs up to 5V on their I/O pins. Though some boards like the Due, the Teensy, and the ESP variants run at 3.3V and will be damaged if you connect a 5V signal. If you’re unsure, look up the operating voltage for your board before connecting anything!

If the operating voltages do not overlap, meaning that the operating voltage of the receiver is either higher or lower than the Arduino’s, then you will need to power the receiver separately and use a level shifter or a voltage divider on the signal line.

Power

The first connection to make is power. On the receiver this is the middle pin for the 3-pin connector. Most receivers have all of their ‘power’ pins tied together, so it doesn’t matter which row you connect to. Both the Arduino and the receiver need to be powered, though they do not need to share a power supply.

If your receiver is not powered separately it can be powered by the Arduino so long as the operating voltages overlap. For example, a 5V Arduino Uno can power my X4F receiver which works between 4.8V and 10V. Connect one of the servo ‘power’ pins on the receiver, the middle of a 3-pin row, to the ‘5V’ (or equivalent operating voltage) pin on the Arduino.

If the receiver is powered and your Arduino isn’t, you can connect the power from the receiver to the regulated ‘Vin’ pin on the Arduino so long as the supply voltage is less than 12V.

If both the receiver and the Arduino have separate power sources, you do not need to connect a power wire between the two boards.

Ground

Next up is the ground connection. This one is easy: connect the grounds between the two boards – GND on the Arduino to ground on the RC receiver (‘right’ pin in a 3-pin row). As with power it doesn’t matter which row (channel) this connects to.

Connecting the grounds is critical. If the receiver doesn’t turn on or the signal data is corrupt, this is the first place to look.

Servo Signals and Interrupts

BoardDigital Pins Usable for Interrupts
Uno, Nano, Mini, other 328-based 2, 3
Uno WiFi Rev.2 all digital pins
Mega, Mega2560, MegaADK2, 3, 18, 19, 20, 21
Micro, Leonardo, other 32u4-based0, 1, 2, 3, 7
Zeroall digital pins, except 4
MKR Family boards0, 1, 4, 5, 6, 7, 8, 9, A1, A2
Dueall digital pins
1012, 5, 7, 8, 10, 11, 12, 13

This is the trickiest part of the setup: connecting the servo signal pins to the Arduino.

For the best results the servo channel’s signal pin should be connected to a pin on the Arduino that is capable of external interrupts. This allows the Arduino to read the servo’s position in the background without disturbing your program.

The table above shows the interrupt capable pins for some common Arduino boards. On the Uno, pins 2 and 3 are capable of interrupts. Connect the signal pin (‘left’ in the 3-pin row) to an interrupt capable pin on the Arduino. For this tutorial I’m using pin ‘2’ on my Uno.

If you need more channels than the number of available interrupt pins, hope is not lost! You will need to use “pin change” interrupts instead. On the Uno all pins support “pin change” interrupts, although they are slower and less accurate.

If the receiver is being powered with a higher voltage than the Arduino’s you will need to ‘shift’ the signal to a lower voltage. To do this you can either use a level shifter or a simple voltage divider. How to wire a level shifter or voltage divider is beyond the scope of this tutorial, although our friends over at SparkFun have some useful resources.

Because it bears repeating: if your receiver is powered by a higher voltage than the Arduino, you must shift the signal voltage. Not doing so will permanently damage or destroy the Arduino.


That’s it for hardware. Now let’s take a look at the software needed to allow the Arduino to “talk” to the RC receiver.

The Servo Input Library

To make this easier we’re going to use a library called ServoInput, which was created specifically to read signals from PWM-based RC receivers. It takes away all of the effort in having to program the signal reading code yourself. It’s also open source so you can use it in your own projects without issue.

The Servo Input library can be downloaded through the Arduino IDE libraries manager or directly from GitHub and installed via .zip.

If you are not using an interrupt capable pin (see ‘Servo Signals and Interrupts’ above) you will also need to download NicoHood’s PinChangeInterrupt library.

“But what if I don’t want to use a library?”

In that case you have two options: you can either use the blocking function pulseIn which will take up to 20 ms to read each channel, or you can write your own interrupt function to read the pulse width. Both of those methods are beyond the scope of this tutorial.

“Hello World”

Let’s start with a simple example to get the ball rolling. Open up the “BasicAngle” example from the ServoInput examples folder. It should look something like this:

You can see that pin <2> is defined at the top. This should match the pin number you connected the signal wire to.

If you are using an Uno but are not using an interrupt capable pin, you need to include the PinChangeInterrupt library mentioned above to add support for other pins. See the PinChangeLib.ino example from the library for more detail.

Upload this sketch to the Arduino and open up the serial monitor, then turn on your RC remote and try changing the control for the connected channel. If all is well, you should see the virtual servo angle being printed to the serial monitor and changing with your controller.

Calibration

The next step once everything is up and running is to calibrate the input range. By default ServoInput assumes servo pulses to be between 1000 µs and 2000 µs long, with the ‘center’ position at 1500 µs. Many receivers can go beyond this range though, and calibrating to the exact range of your receiver makes the output more accurate.

You can use the “Calibration” example from the library to get these min and max values – just run the example, move the input to its extremes, and then write down output from the serial console. The range can then be set either in the constructor:

Or in the setup() function:

If you want the output to reliably hit the extremes, it’s helpful to be conservative here and take a few microseconds off of either end.

Examples

Now let’s go through a few examples for how to use an Arduino and the Servo Input library to parse data from an RC receiver.

Turning on an LED / Pin

We’ll start simple: lighting an LED based off of an RC receiver input:

This works by reading the servo’s position and mapping it to a boolean. If it’s above the midpoint of the range the output is ‘true’, if it’s below the midpoint it’s ‘false’.

If you need finer control over when the output is triggered, just read the position as a percentage and compare it against a threshold. Using the same setup and global variables as above:

Or more succintly:

Because this just writes the output state of a pin, you could also use this for remotely controlling any digital (on/off) device with a transistor or relay: DC motors, buzzers, oil slicks, control surfaces, etc.

Reading a Multi-Position Switch

Many RC controllers, particularly advanced ones, include switches with multiple positions to give you finer control over a motor’s setting. The library can easily read these switches using the built-in map function:

Edit the NumPositions variable to change the output range. If you find that this is inconsistent or doesn’t properly match your switch positions, your timing calibration may be off (see “Calibration” above).

Separating Throttle and Brake

Another common problem is parsing the data from a bidirectional analog input. That is to say, an analog input that has two possible directions like a steering wheel (left/right) or a speed control (throttle/brake). This is easy to do in the library by using the map function and a symmetrical range:

This maps the position from -100 to 100. Positive values are for throttle, while negative values are for braking.

There’s a slight problem here, as the position will flip-flop between ‘throttle’ and ‘brake’ when it’s around the center. To fix this we can replace the plain map function with mapDeadzone, which will center (zero) the output within a percentage range of the middle without affecting the overall output range:

Multiple Channel Inputs

ServoInput makes it just as easy to read from multiple channels as it is to read from just one. All you need is a second ServoInputPin object set to a different pin:

The library also features a ‘manager’ class (ServoInput) that has utility functions like allAvailable or anyAvailable if you want to check all of the channels at the same time:

For advanced users, all ServoInputPin objects use a base class called ServoInputSignal which includes a linked list for iterative access:

Further Reading

This hopefully gave you a primer for how to get started with adding RC receivers to your Arduino projects. For more info, I would recommend reading the Servo Input library documentation and source code on GitHub.

What have you made with Arduino and RC controllers? Share in the comments below!


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Would you like to know more?