Read PWM, Decode RC Receiver Input, and Apply Fail-Safe (2023)

This project contains generic but efficient code that can be used to simply read an RC receiver (or any other PWM signal) on any Arduino input pin, and also apply a fail-safe in the case of the loss of the transmitter signal.

Below is a video showing an Arduino uno acting as a servo mixer using the code PWMread_RCfailsafe.ino available at the bottom of this page.

Helpfully the functions in PWMread_RCfailsafe.ino look after the interrupt registers for you, and can be moved easily between different projects that use different pins.

In the video example below:

  • The fail-safe activates when the receiver has no signal from the transmitter. elevator channel is set to full up, and aileron channel is set to neutral
  • The direction of the blue servo is set by the throttle position
  • The range of movement (rates) of the blue servo is set by the slider on the side of the transmitter
  • The mixing is turned on an off using the gear switch on the transmitter

Contents

  • How are servos controlled by PWM
  • Example uses of Arduino in RC models / robots
  • Code Overview: Decode PWM from RC receiver with fail-safe
  • How to use PWMread_RCfailsafe.ino
  • Display the receiver frame rate and frequency
  • Servo mixing example
  • Rationale for the approach taken
  • Limitations

How are servos and speed controllers controlled by PWM?

It is assumed in the rest of this project that you have an understanding of the PWM signals used to control servos and speed controllers. Here is a good video explaining how these pulse width modulation (PWM) signals work.

You should also have a working knowledge of:

  • The Arduino IDE
  • Float, boolean, and int variables
  • If loops
  • For loops
  • Arrays
  • Servo library

Example uses of Arduino in RC models / robots

You will only be limited by your imagination:

  • Apply servo mixing, switch lights on/off, control pumps/valves, set bespoke sequences...
  • Create a controller (i.e. flight stabilization/ autopilot, heading hold, altitude/depth hold, auto-leveler, sense and avoid, return home...)
  • Have your RC model respond to a loss of signal or low battery voltage...
  • Use the same transmitter for multiple models/ projects without having to change any settings or use a model memory feature.

Code overview: Decode PWM from RC receiver with fail-safe

This code measures PWM (Pulse Width Modulation) signals using pin change interrupts. The functions used automate the set-up of the interrupts and the extraction of data from any digital or analog pin (excluding A6 and A7), on the Arduino Uno, Nano or Pro Mini. This makes the code easy to use even for beginners.

The primary aim of this project was to create a generic RC receiver with fail-safe "module" that can be quickly moved between projects. As such the example code shown in the "how to use" section can just be used as a means to an end.

Note: this code will not work with the software serial or any other library which uses pin change interrupts.

For those interested in how the code works:

  • The input pins are identified in an array. This array can be any length.
  • A setup function enables pin change interrupts by setting the appropriate registers for each pin listed in the pin array.
  • A voltage change on any of the selected pins will trigger one of three Interrupt Service Routes (ISR) depending on which port register the pin belongs to ISR(PCINT0_vect) -> Port B, ISR(PCINT1_vect) -> Port C or ISR(PCINT2_vect) -> Port D.
  • Within each ISR a FOR loop and IF statements are used to determine which pin has changed, and which RC channel it belongs to. The time of the interrupt is noted via the use of micros() before returning back to the main loop().
  • The time intervals between pin changes are used to calculate pulse width, and repetition period.
  • Flags are set in each ISR to indicate when new pulses have been received
  • The flags are then used by the remaining functions to extract and process the data collected by the ISRs

The following YouTube videomadebyJoop Brokking talks about a different projectthat uses the same method for connecting an RC receiver to arduino. During the first 8 minutes Joopclearlyexplains howto use pin change interrupts to measure PWM signals from an RC receiver.

All of this detail is looked after by PWMread_RCfailsafe.ino which can be downloaded at the bottom of this page.

Some useful information on port manipulation can also also be found here:https://tronixstuff.com/2011/10/22/tutorial-arduino-port-manipulation/

In addition to the pin change interrupt handling, a dedicated function RC_decode() has been written to convert a pulse width (1000-2000uS) into a +-100% control signal from a transmitter. A fail-safe checks for a valid transmitter signal using the signal tolerances of 10-330Hz, and 500-2500uS. If the signal is lost then RC_decode() returns a predetermine fail-safe value.

The calibration values for a specific transmitter, and fail-safe positions can be set for each channel in PWMread_RCfailsafe.ino

How to use PWMread_RCfailsafe.ino

Step 1: Example hardware setup with Arduino Uno

If following the example set-up with an Arduino Uno connect your receiver as follows: (otherwise if your using your own project jump straight to step 2)

  • Power the receiver using the 5v and GND pins
  • Connect the signal pins from the receiver to pins 2 to 7 on the Arduino using female to male jumper wires. (If you have a 2 channel receiver connect to only pins 2 and 3)
  • For the servo mixer example attach one servo signal wire to pin 9 and the other to pin 10.
(Video) Reading PWM Receiver Signal Using Arduino

Step 2: Copy PWMread_RCfailsafe.ino into the sketch folder

An example sketch RC_Read_Example has been included for download at the bottom of page. You can use this as you main sketch when following the steps below.

Copy and past the PWMread_RCfailsafe.ino file into the folder containing your main sketch. When you next open the sketch in the IDE, a second tab will appear containing the code within PWMread_RCfailsafe.ino.

Step 3: Specify the input pins

Open or re-open the main sketch in the Arduino IDE.

Click on the PWMread_RCfailsafe tab, scroll down to the "USER DEFINED VARIABLES" title and enter the input pins in the array pwmPIN[].

Note: Any number of pins can be used, and in any order. Just be aware that the more inputs you have the more time the code will spend addressing the interrupt routine. Note A6 and A7 are analog only pins and cannot be used.

The Arduino MEGA is not currently supported, however this could be easily remedied if there was the appetite for it.

Note: the first element in pwmPIN[] is channel 1, the second element channel 2, etc... if your using all of the channels from the receiver it would be a good idea to make sure the receiver channels 1 corresponds to channel 1 in pwmPIN[]...

Step 4: Review the available functions in PWMread_RCfailsafe.ino

Step 5: Print the pulse width data to serial

(Video) Using a radio control receiver PWM servo signals to control a Stepping Motor position

Upload the RC_Read_Example code, turn on your transmitter and print the raw pulse width data to serial.

The RC_avail() function should be used to check when new data has been received on all channels, and then use print_RCpwm() to send the pulse width data to serial.

Step 6: Calibrate the transmitter

Using the pulse width data printed to serial via print_RCpwm() to manually modify the values in the arrays RC_min[], RC_mid[], and RC_max[] in order to calibrate each channel into the range +-100%.

Step 7: Print the calibrated channels to serial

Comment out the print_RCpwm() function

Use the RC_decode(channel) function to calibrate each channel into the range +-1.

Then print each of the calibrated channels to serial using the decimal2percentage() function followed by Serial.println("")

Step 8: Set the fail-safe

Adjust the fail-safe positions in the RC_failsafe[] array for each channel (in the range +-1).

Turn the transmitter on and off to check that the fail-safe operates as desired.

The RC input can now be used in your sketch.

Note: you may have to deactivate any fail-safe feature in the receiver, otherwise the arduino will notbe able to respond to the loss of transmitter signal.

(Video) SBUS Protocol Decoder / Pi Pico / Mecrisp Forth

Display the receiver frame rate and frequency

The receiver pulse repetition period, and frequency can be printed to serial. Check that new data is available on the chosen channel by using the function PWM_read(channel number), before using PWM_period() and PWM_freq() to extract the data for printing. Example code is available in RC_FrameRate.ino.

It's best to use the first channel as this will be the first pulse sent in each receiver frame. PWM_read() uses the same flags as RC_decode(CH) so make sure PWM_read() is called first.

See the screen shot below:

The receiver period can be useful to know as it tells you how much time the code has before the next set of data arrives. If RC_avail() does not detect new RC data after a predetermined time i.e. 21ms then run RC_decode() in order to trigger the fail-safe and or to continue to run the program (which could be a PID controller) at a steady frequency.

This is achieved in the RC_Read_Example.ino by the following if statement.

now = millis();if(RC_avail() || now - rc_update > 21) rc_update = now; // update RC input data using RC_decode() // run a PID controller  // apply servo mixing // position the servos}

Servo mixing example

I've included RC_ServoMixer_Example.ino to show how you could mix two receiver channels (in this case channels 2 and 3, elevator and aileron). The sketch also shows a method for setting servo direction, rate, and sub trim. The servo library is used to control the servos via pins 9 and 10.

Below is a screen shot of the servo mixing section of the code:

The mix is achieved by simply adding and subtracting the two channels together, and limiting the output to the range -1 to +1. When applying elevator and aileron mixing you create two outputs one for each servo.

mix1 = channel 2 - channel3 (elv - ail)

mix2 = channel 2 + channel3 (elv - ail)

Before positioning the servos you will need to convert the +-100% (+-1) signal to an equivalent pulse width in microseconds for the servo. In the RC_ServoMixer_Example.ino I use a function calc_uS() to do this. This function is placed at the bottom of the sketch and is shown in the screen shot below.

(Video) Servo direction, rates and mixing on an Arduino

The direction, rate, and sub trim specified for each servo is used to calculate an appropriate pulse width for the servo.

The standard neutral pulse is 1500uS, and the normal range either side of neutral is +-500uS. This gives a min pulse width of 1000uS (-100%) and max of 2000uS (+100%). The pulse with rates, direction and sub trim applied can therefore be calculated as follows.

pulse, uS = 1500 + (servo_position_% * rates * direction + sub trim) * 500

The servo direction, rate and sub trim can be static or modified dynamically by the sketch in response to an input from another receiver channel, or by some other means.

Rationale for the approach taken

It is possible to read an RC receiver using the pulseIn(PIN, HIGH) function, however pulseIn() blocks the code in loop() while it waits for a pulse to start and then to finish, wasting precious processing time. If there is more than one input data could also be lost.

For speed it is best to use the pin change interrupt feature of the Arduino along with direct port manipulation to allow the code in loop() to run with the minimum of delay. This however is more involved and time consuming than simply calling pulseIn(PIN, HIGH).

Therefore I wanted to get the advantages of both worlds by writing some generic code that I can move between projects. All that is needed is to copy and paste an.ino file (containing the functions and interrupt routines) into the main sketch folder, specify the input pins, and then use the functions in the sketch.

Limitations

The micros()function

The microsecond timing on the arduino is carried out using the micros() function. This function counts in 4uS steps. This means we have a 4 microsecond level of precision when we measure the 1000-2000uS pulses. From a practical point of view this is more than adequate.

If desired It is possible to improve this resolution to 0.5uS by using timer interrupts. see link below:

https://www.instructables.com/id/How-to-get-an-Arduino-micros-function-with-05us-pr/

Efficiency of PWMread_RCfailsafe.ino

If your using PWMread_RCfailsafe.ino to read a 6 or 9 channel receiver 1.4-2.0% of the processing time is spent running the pin change interrupt routines, which I would argue is more than acceptable.

However it's always to good to understand the limitations of the code, and how it could be sped up if needed.

Below is a list of the time it takes to run each ISR depending on the number of selected input channels.

1 channel < 8uS

2 channels < 12uS

3 channels < 16uS

4 channels < 20uS

5 channels < 20uS

6 channels < 24uS

Note: the more channels used the longer each ISR takes to run. This is becausea for loopruns through each channel every time the ISR is called.

This extra time (inefficiency) is negligible when measuring low frequency (i.e.50hz) RCsignals.

On top of the above it takes ~4uS to enter and exit an ISR. For one pulse the ISR runs twice, once at the start of a pulse (LOW to HIGH) and then again at the end (HIGH to LOW).

The time taken to measure 1 pulse when using 6 RC inputs is

2 * (4us to enter ISR + 24uS to run ISR) = 2 * 28 = 48uS.

Note: this is the minimum pulse width than can be measured.

The time taken to read all 6 channels is 288uS (6 * 48uS)

Assuming that the receiver repetition period is 20 milliseconds, then the interrupt will be running for 1.44% (0.000288/0.02) of the time. This is significantly better than using the pulseIn()function. pulseIn() would block the code for up to 20 milliseconds for each pin.

FYI: if the arduino had only 2 RC inputs then the ISR will run for just 0.16% of the time (0.000032/0.02)

Maximum practical frequency (Hz)

If using this code for any other purpose I would suggest that a the maximum practical frequency is 2.5kHz. This give 100 steps of resolution from the micros() function (+- 0.025kHz).

(Video) Dev Conf 2022 Robert Moore Redundancy Systems

If using one input pin at this frequency 3% of the time is spent in the interrupt, which means that the minimum duty that can be measured is 0.03. This equates to a minimum pulse with of 12uS.

For higher frequencies rewrite the ISR to suit your application.

FAQs

How do I decrypt a PWM signal? ›

The easiest way to do this is with arduino's pulse in function start by defining rc pin to 2 and

What is PWM RC? ›

Servo control is a method of controlling many types of RC/hobbyist servos by sending the servo a PWM (pulse-width modulation) signal, a series of repeating pulses of variable width where either the width of the pulse (most common modern hobby servos) or the duty cycle of a pulse train (less common today) determines the ...

How do you convert PWM to volts? ›

Fortunately, it is easy to convert a PWM output to an analog voltage level, producing a true DAC. All that is needed is a simple low-pass filter made from a resistor and a ceramic capacitor. The simple RC low-pass filter shown in the third photo converts the PWM signal to a voltage proportional to the duty cycle.

What is ppm in RC radio? ›

PPM - Pulse Position Modulation

PPM is also an analogue signal, but instead of using a separate wire for each channel, PPM stacks each signal one after another to send them all along the same wire. This makes wiring your R/C Receiver to your autopilot much easier!

What is Mcpwm? ›

The MCPWM peripheral is a versatile PWM generator, which contains various submodules to make it a key element in power electronic applications like motor control, digital power and so on.

Do RC cars use PWM? ›

I know that RC applications, such as a drone, use PWM signals to drive the motors. This PWM signal is mostly 50 Hz (0.02 s). The pulse itself varies from 1 ms to 2 ms.

How does an RC receiver work? ›

Once the RC toy receives the radio waves, the motors kick into life to cause a specific action to occur. The power source sends power to all working parts, including the motor. The transmitter enables control through radio waves and the receiver activates the motors.

Do all servos use PWM? ›

Servo motors are controlled by via a pulse width modulated (PWM) signal. Servo motors usually have three wires: power, ground and the control signal.

How is PWM signal generated? ›

Pulse Width Modulator - Principle of operation

One of the simplest methods of generating a PWM signal is to compare two control signals, a carrier signal and a modulation signal. This is known as carrier-based PWM. The carrier signal is a high frequency (switching frequency) triangular waveform.

What are the types of PWM techniques? ›

The different PWM techniques are Single pulse width modulation, Multiple pulse width modulation, Phase displacement control, Sinusoidal pulse width modulation, Harmonic Injection modulation, Space Vector pulse width modulation, Hysteresis (Delta) pulse width modulation, Selective Harmonic Elimination and Current ...

What are the disadvantages of PWM? ›

Stroboscopic effect evident in fast moving environments when the driver frequency is low. Electromagnetic Interference (EMI) issues due to rise and fall of the current in PWM dimming.

Can I use a PWM to control voltage? ›

Whether analog circuit or digital circuit, the PWM period or PWM technique can serve to improve power supply regulation in analog device design or DC motor control. PWM signals help to regulate the voltage of digital pulses.

How do you convert PWM to analog signal? ›

PWM signals can be transformed into analog signals using a simple RC type low-pass filter. The PWM duty cycle determines the magnitude of the filter's voltage output. As the duty cycle increases, the average voltage output increases, and vice versa.

What is a PWM converter? ›

Description: PWM Converters provide a compact solution for converting current into digital pulse width modulated (PWM) signal. A reference voltage is provided on the PWM output. All specifications typical at nominal input voltage and 25 degrees C unless otherwise specified.

What is the difference between PPM and PWM? ›

PWM is an abbreviation for Pulse Width Modulation. PPM is an abbreviation for Pulse Position Modulation. These are the types of pulse modulation.

What is the output of an RC receiver? ›

RC receiver outputs are simply a ~1.5ms pulse about every 20ms. This 20ms is measured from the rising edge of the signal to the rising edge of the next. This allows for wireless communication to your servo. The 1.5ms pulse is varied from ~1.0ms to ~2.0ms to give the full range of your device.

What is SBUS on RC receiver? ›

Aka S. BUS or Serial BUS, is commonly used by Futaba and FrSky. It supports up to 16 channels using only one signal wire. SBUS signal should be connected to the RX pin of an UART.

What is PWM channel in ESP32? ›

ESP32 LED PWM Controller (LEDC)

16 independent PWM Channels, divided into group of two with 8 channels per group. Programmable resolution between 1-bit and 16-bits. Frequency of the PWM wave depends on the resolution of PWM. Automatically increases / decreases duty cycle without processor intervention.

How do I match my RC transmitter and receiver? ›

Bind an RC Receiver | A Simple 2 Minute Process - YouTube

Does receiver get power from ESC? ›

Most modern-day electric-powered radio control vehicles, whether airplanes, boats, or cars, have something built into their electronic speed controls (ESCs) that shunts power from the main battery to the receiver to power the radio system.

How do I connect my RC receiver? ›

To bind it you power up the transmitter, power up the receiver (turn on the car), push and hold the 'link' button (found on the receiver box) until it turns red before shortly turning off. The receiver LED will then flash once and then return to a solid on.

How many volts can a RC receiver take? ›

A good, clean power supply is important. Our RC radios typically require a DC voltage of 4.8 to 6.0 volts. On an oscilloscope, this DC voltage would appear as a straight line, so in this case, a flat line is a good thing. An oscilloscope is a voltage meter that displays the voltage with respect to time.

How many channels does an RC car need? ›

Normally an RC car requires two channels, one for steering (ch1) and one for throttle (ch2). However, in most cases, controllers have 3 or 4 channels.

How do you bind a 2.4 Ghz RC car? ›

How to Bind Your Transmitter to an RC Car - YouTube

Why do servos have 3 wires? ›

The servo has three wires: power, ground, plus a third wire to carry the command pulses.

Can you turn a servo motor by hand? ›

You shouldn't be turning a servo of any kind by hand. Just as the gearing takes a relatively high rpm motor and slows it down for the final output shaft, when you turn the servo by hand you are putting huge loads on the gears. What you are doing can easily strip most servos.

Does a servo motor need a PWM pin? ›

Servos does not use PWM, it uses PPM: PWM = Pulse width modulation, The pulse start at 0ms, and is high for the percentage of the time compared to the percentage of the voltage you want, 5v signal, if you want 1v, the pulse is 20ms High and 80ms Low.

How does Arduino measure PWM? ›

To measure a PWM frequency and duty cycle, you can code a Timer Counter in capture mode. Then pull frequency and duty from Timer internal counters in the TC Handler or by polling TC satus register in loop().

What is the frequency of Arduino PWM? ›

Description
BoardPWM PinsPWM Frequency
Uno, Nano, Mini3, 5, 6, 9, 10, 11490 Hz (pins 5 and 6: 980 Hz)
Mega2 - 13, 44 - 46490 Hz (pins 4 and 13: 980 Hz)
Leonardo, Micro, Yún3, 5, 6, 9, 10, 11, 13490 Hz (pins 3 and 11: 980 Hz)
Uno WiFi Rev2, Nano Every3, 5, 6, 9, 10976 Hz
7 more rows
7 Sept 2020

What is analogWrite in Arduino? ›

analogWrite() (PWM) analogWrite. Writes an analog value to a pin as a digital PWM (pulse-width modulated) signal. The default frequency of the PWM signal is 500 Hz.

Is PWM analog or digital? ›

In a nutshell, PWM is a way of digitally encoding analog signal levels. Through the use of high-resolution counters, the duty cycle of a square wave is modulated to encode a specific analog signal level.

Does PWM change voltage? ›

PWM does not change the value of voltage or current. It changes the amount of time a voltage is applied which effectively changes average power over time.

How does PWM work? ›

Pulse width modulation turns a digital signal into an analog signal by changing the timing of how long it stays on and off. The term “duty cycle” is used to describe the percentage or ratio of how long it stays on compared to when it turns off.

How do you make a PWM signal with Arduino? ›

A PWM signal can be generated on any of the Arduino's digital pins simply by turning the pin on and off the desired number of times per second.

What is PWM output pins in Arduino? ›

On an Arduino Uno, PWM output is possible on digital I/O pins 3, 5, 6, 9, 10 and 11. On these pins the analogWrite function is used to set the duty cycle of a PWM pulse train that operates at approximately 500 Hz2. Thus, with a frequency fc = 500Hz, the period is τc = 1/fc ∼ 2ms.

How many PWM outputs are present for Arduino? ›

PWM in Arduino is a technique or method to control analog devices using digital signals. All the Arduino boards have PWM pins on board. 6 PWM pins are present in Uno out of total 14 digital pins.

Does PWM change frequency? ›

PWM (Pulse width modulation) is one of the most useful feature used in many applications. PWM is used by using function like "analog Write". With this function although width of the PWM cycle(Duty Cycle) can be changes but frequency remains constant.

What is the other name of PWM? ›

April 2009. Pulse-width modulation (PWM), or pulse-duration modulation (PDM), is a method of reducing the average power delivered by an electrical signal, by effectively chopping it up into discrete parts.

Can Arduino output 0 to 5V? ›

Use analogWrite(pin, val); and use a low pass filter to turn the PWM to a voltage. For true analog output you will need to you use an DAC ( Digital to Analog Converter)... You cannot get pure analog signal from arduino output, only pulse-width modulated (PWM) signal. PWM gives an average voltage value from 0 to 5 V.

Why is PWM used? ›

For example, the PWM is commonly used to control the speed of electric motors, the brightness of lights, in ultrasonic cleaning applications, and many more. A PWM is basically a digital unipolar square wave signal where the duration of the ON time can be adjusted (or modulated) as desired.

What is the difference between digitalWrite () and analogWrite ()? ›

digitalWrite is a simple function. Simply put, it sets the desired pin to maximum voltage(3.3 V) or 0 volt, in other words, turns it on or off. analogWrite is similar to digitalWrite, but it can provide desired any voltage between 0V to 3.3V. It provides 256 different voltage levels including 0V.

Why do we use PWM pins? ›

PWM stands for Pulse Width Modulation and it is a technique used in controlling the brightness of LED, speed control of DC motor, controlling a servo motor or where you have to get analog output with digital means.

Videos

1. Code run down for DIY 2.4ghz RC transmitter/receiver
(Team Panic)
2. Jeti Servo Telemetry using Arduino
(Worldspawn)
3. RC Quick Tip - PWM, PPM, CPPM, S-BUS and Sat. explained for beginners
(Painless360)
4. Review: Motive-RC R8SB FrSky-compatible D8 receiver
(RCModelReviews)
5. How to Access Secret "Service Menu" for All Samsung TVs
(WorldofTech)
6. PPM monitor
(ceptimus)
Top Articles
Latest Posts
Article information

Author: Duncan Muller

Last Updated: 01/13/2023

Views: 5851

Rating: 4.9 / 5 (59 voted)

Reviews: 90% of readers found this page helpful

Author information

Name: Duncan Muller

Birthday: 1997-01-13

Address: Apt. 505 914 Phillip Crossroad, O'Konborough, NV 62411

Phone: +8555305800947

Job: Construction Agent

Hobby: Shopping, Table tennis, Snowboarding, Rafting, Motor sports, Homebrewing, Taxidermy

Introduction: My name is Duncan Muller, I am a enchanting, good, gentle, modern, tasty, nice, elegant person who loves writing and wants to share my knowledge and understanding with you.