How to Use an RC Controller with an Arduino - Parts Not Included (2024)

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

  • How to Use an RC Controller with an Arduino - Parts Not Included (1)
  • How to Use an RC Controller with an Arduino - Parts Not Included (2)

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:

#include <ServoInput.h>ServoInputPin<2> servo;void setup() {Serial.begin(115200);servo.attach(); // attaches the servo input interruptwhile (servo.available() == false) {Serial.println("Waiting for servo signal...");delay(500);}}void loop() {float angle = servo.getAngle(); // get angle of servo (0 - 180)Serial.println(angle);}

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:

ServoInputPin<2> ch(1000, 2000);

Or in the setup() function:

ch.setRange(1000, 2000);

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:

#include <ServoInput.h>ServoInputPin<2> ch;const int LED_Pin = LED_BUILTIN; // built-in LED (pin 13 on Uno)void setup() {pinMode(LED_Pin, OUTPUT);ch.attach();}void loop() {boolean state = ch.getBoolean();digitalWrite(LED_Pin, state);}

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:

const float threshold = 0.7; // 70%void loop() {float percent = ch.getPercent();if(percent >= threshold) {digitalWrite(LED_Pin, true);}else {digitalWrite(LED_Pin, false);}}

Or more succinctly:

const float threshold = 0.7; // 70%void loop() {boolean state = (ch.getPercent() >= threshold);digitalWrite(LED_Pin, state);}

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:

#include <ServoInput.h>ServoInputPin<2> ch;const int NumPositions = 3;void setup() {Serial.begin(115200);ch.attach();}void loop() {int position = ch.map(1, NumPositions);Serial.print("Switch Position: ");Serial.println(position);}

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:

#include <ServoInput.h>ServoInputPin<2> ch;void setup() {Serial.begin(115200);ch.attach();}void loop() {int speed = ch.map(-100, 100);if(speed >= 0) {Serial.print("Throttle: ");Serial.print(speed);}else {Serial.print("Brake: ");Serial.print(abs(speed));}Serial.println("%");}

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:

const float Deadzone = 0.15; // 15%void loop() {int speed = ch.mapDeadzone(-100, 100, Deadzone);if(speed == 0) {Serial.println("In deadzone!");}if(speed &gt; 0) {Serial.print("Throttle: ");Serial.print(speed);Serial.println("%");}else {Serial.print("Brake: ");Serial.print(abs(speed));Serial.println("%");}}

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:

#include <ServoInput.h>ServoInputPin<2> ch1;ServoInputPin<3> ch2;void setup() {Serial.begin(115200);ch1.attach();ch2.attach();}void loop() {Serial.print("Ch1: ");Serial.print(ch1.getAngle());Serial.print(" Ch2: ");Serial.print(ch2.getAngle());Serial.println();}

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:

if(ServoInput.allAvailable()) {Serial.println("All channels have data!");}else if(ServoInput.anyAvailable()) {Serial.println("At least one channel has data!");}

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

ServoInputSignal* ptr = ServoInputSignal::getHead();while(ptr != nullptr) {Serial.print(ptr-&gt;getAngle());Serial.print('\t');ptr = ptr-&gt;getNext();}Serial.println();

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!

How to Use an RC Controller with an Arduino - Parts Not Included (2024)
Top Articles
Carita triste símbolo - Emoticones de tristeza
Exercícios de Cálculo de Gotejamento!
Https Paperlesspay Talx Com Boydgaming
Pulse Point Oxnard
Academic Calendar Pbsc
Tampa Lkq Price List
Myud Dbq
/hypno/ - Hypnofa*ggotry
1 Bedroom Apartment For Rent Private Landlord
What retirement account is tax-free?
2320 Pioneer Rd
Kinoprogramm für Berlin und Umland
Apple Nails & Spa, 3429 Toringdon Way, Charlotte, Reviews and Appointments
Telegram Voyeur
James Cameron And Getting Trapped Inside Your Most Successful Creation
Chris Evert Twitter
Smith And Wesson Nra Instructor Discount
1102 E Overland Trail Abilene 79601
Craigslist Hoosick Falls
Noaa Marine Forecast Tampa
Ma.speedtest.rcn/Merlin
Wdef Schedule
What Time Is First Light Tomorrow Morning
Weather Arlington Radar
Taco Bell Fourth Of July Hours
Rugged Gentleman Barber Shop Martinsburg Wv
Used Zero Turn Mowers | Shop Used Zero Turn Mowers for Sale - GSA Equipment
Busted Barren County Ky
Terraria Water Gun
Google Flights Missoula
Kirby D. Anthoney Now
Charlotte North Carolina Craigslist Pets
How to Get Rid of Phlegm, Effective Tips and Home Remedies
Musc Food Truck Schedule
Sdn Ohio State 2023
Melanie, Singer Who Performed at Woodstock and Topped Charts With ‘Brand New Key,’ Dies at 76
Myapps Tesla Ultipro Sign In
Mtb Com Online
John Deere Z355R Parts Diagram
Beauty TikTok Star Mireya Rios' Magical Wedding on the Beaches of Mexico
Bridgeway Diagnostic Auburn Al
ExtraCare Rewards at the Pharmacy – Target | CVS
Inside Dave Grohl's past love life and cheating scandals
Jeep Forum Cj
Download fallout 3 mods pc.10 essential Fallout 3 mods - Modutech
Footfetish Telegram
3143656395
Saryn Prime Build 2023
Tattoo Shops Buckhannon Wv
Firsthealthmychart
Turtle Beach Velocity One Factory Reset
Lharkies
Latest Posts
Article information

Author: Errol Quitzon

Last Updated:

Views: 6526

Rating: 4.9 / 5 (79 voted)

Reviews: 94% of readers found this page helpful

Author information

Name: Errol Quitzon

Birthday: 1993-04-02

Address: 70604 Haley Lane, Port Weldonside, TN 99233-0942

Phone: +9665282866296

Job: Product Retail Agent

Hobby: Computer programming, Horseback riding, Hooping, Dance, Ice skating, Backpacking, Rafting

Introduction: My name is Errol Quitzon, I am a fair, cute, fancy, clean, attractive, sparkling, kind person who loves writing and wants to share my knowledge and understanding with you.