Go Down

Topic: Decoding Radio Control signal pulses (Read 71361 times) previous topic - next topic


> Maybe the voltage level is not high enough?

You could try to use a transistor to condition the signal. If you have an NPN transistor (almost anything will do)  connect the output of the transmitter to the base of the transistor, the emitter to arduino Gnd, The collector to a resistor (any thing from 1k to 10k ohms would work).  Also connect the arduino pin 8 to the junction of the resistor and the transistor. Connect the other side of the resistor to +5v.

Try that with the library  configured for the inverted signal ( the transistor will invert the signal again).


Could I use a scope to see if voltage level is too low? I have this scope, but not very experienced in using scopes:



Yes, that should tell you a lot about the pulses. If you can take a picture and post it that will be even better.


Apr 28, 2009, 10:20 pm Last Edit: Apr 28, 2009, 10:21 pm by jds Reason: 1
Sorry about quality, but this is the best I could get now:

I also have a movie posted at flickr:

Thanks for your suggestions/support mem!


It looks like the pulses are just under two divisions high, what is the scale set for?

It also looks inverted, that is the widths of spaces between the higher pulses is changing, I expect to  see the high pulse width change.  

Perhap you can touch the scope probe the the Arduino +5v pin to check that the polarity of the scop is not reversed.


Apr 28, 2009, 11:01 pm Last Edit: Apr 28, 2009, 11:01 pm by jds Reason: 1
Thanks again mem!

>It looks like the pulses are just under two divisions high, what is the scale set for?

I was afraid you were going to ask questions like these :) I am not an experienced scope user. All that I can tell is:
- There is a switch with x1, x2 and x5, I have it set to X5
- 2ns switch has GND, 1V and 0,1V, I have it set to 1V
- 3rd switch has AC, DC, Freq M, I have it set to DC
Also, the LCD mentions 1MS

Does that ring a bell to you?

>Perhap you can touch the scope probe the the Arduino +5v pin to check that the polarity of the scop is not reversed
I just did that and all I see is a nice sine/wave, difference between high and low is not too big.

I hope this enough information to get me a step closer to a working environment.


You should see the trace move when you touch the scope probe to Gnd and to +5v. If its set for 1v per division and x5 then I would expect it to move up 5 divisions above the poisition when the probe is connecte to Gnd. have a play around and see if you can get the hang of how the trace behaves when connected between Gnd and +5v and use that to get the approx value of the waveforms you see from your Transmitter


Shoot me!

Suddenly, it started working without changing anything.

It seems that the serial speed is lacking, there is a time difference between a change of a stick and seeing the change in the serial window. Maybe this is caused by my Windows 7 x64 notebook?

I will check on other pc's



have you already found any time to adapt the library to the Mega board? Would it would together with the servo-library for that board?


At 9600 baud, serial will be working flat out to send 20 characters in each frame.

anyway, good to hear you have it going.

Have fun!


Apr 30, 2009, 10:36 pm Last Edit: May 01, 2009, 06:57 am by mem Reason: 1
have you already found any time to adapt the library to the Mega board? Would it would together with the servo-library for that board?

Unfortunately I have not had the time to complete the code for a version that works with both, but here is a first pass if you want to test it. It should work with the existing MegaServo library (up to 36 servos instead of 48) but untested and this version hard coded for mega only.  
Code: [Select]
* ServoDecode.cpp
* this version for Mega only

#include "ServoDecode.h"
#include <wiring.h>
#include <avr/interrupt.h>

#define ACQUISITION_COUNT  8  // must have this many consecutive valid frames to transition to the ready state.
volatile byte pulseEnd = PULSE_START_ON_RISING_EDGE ; // default value
static volatile unsigned int Pulses[ MAX_CHANNELS + 1]; // array holding channel pulses width value in microseconds
static volatile unsigned int Failsafe[MAX_CHANNELS + 1]; // array holding channel fail safe values
static volatile byte Channel;      // number of channels detected so far in the frame (first channel is 1)
static volatile byte NbrChannels; // the total number of channels detected in a complete frame
static volatile decodeState_t State;         // this will be one of the following states:
static volatile byte stateCount;         // counts the number of times this state has been repeated

static void processSync(){
// Sync was detected so reset the channel to 1 and update the system state      
  Pulses[0] = ICR4 / TICKS_PER_uS;  // save the sync pulse duration for debugging
  if(State == READY_state) {  
        if( Channel != NbrChannels){  // if the number of channels is unstable, go into failsafe
              State = FAILSAFE_state;
    if(State == NOT_SYNCHED_state){
            State = ACQUIRING_state;        // this is the first sync pulse, we need one more to fill the channel data array    
            stateCount = 0;
      else if( State == ACQUIRING_state)      {
         if(++stateCount > ACQUISITION_COUNT) {
           State = READY_state;           // this is the second sync and all channel data is ok so flag that channel data is valid    
               NbrChannels = Channel; // save the number of channels detected
      else if( State == FAILSAFE_state)      {  
            if(Channel == NbrChannels){  // did we get good pulses on all channels
                 State = READY_state;
  Channel = 0;       // reset the channel counter

 if(State == READY_state){
   State = FAILSAFE_state;  // use fail safe values if signal lost  
   Channel = 0; // reset the channel count

 // we want to measure the time to the end of the pulse
 if( (_SFR_BYTE(TCCR4B) & (1<<ICES4)) == pulseEnd ){         
   TCNT1 = 0;       // reset the counter
   if(ICR4 >= SYNC_GAP_LEN){   // is the space between pulses big enough to be the SYNC
   else if(Channel < MAX_CHANNELS) {  // check if its a valid channel pulse and save it
     if( (ICR4 >= MIN_IN_PULSE_WIDTH)  && (ICR4 <= MAX_IN_PULSE_WIDTH) ){ // check for valid channel data                  
       Pulses[++Channel] = ICR4 / TICKS_PER_uS;  // store pulse length as microsoeconds
     else if(State == READY_state){
       State = FAILSAFE_state;  // use fail safe values if input data invalid  
       Channel = 0; // reset the channel count


void ServoDecodeClass::begin(){
 Channel = 0;            
 State = NOT_SYNCHED_state;
 TCCR4A = 0x00;         // COM1A1=0, COM1A0=0 => Disconnect Pin OC from Timer/Counter  -- PWM11=0,PWM10=0 => PWM Operation disabled
 TCCR4B = 0x02;         // 16MHz clock with prescaler means TCNT increments every .5 uS (cs11 bit set
 TIMSK4 = _BV(ICIE4)|_BV (TOIE4);   // enable input capture and overflow interrupts for timer 1
 for(byte chan = 1; chan <=  MAX_CHANNELS; chan++)
       Failsafe[chan] = Pulses[chan]= 1500; // set midpoint as default values for pulse and failsafe

decodeState_t ServoDecodeClass::getState(){
 return State;

byte ServoDecodeClass::getChanCount(){
 return NbrChannels;

void  ServoDecodeClass::setFailsafe(byte chan, int value){
// pulse width to use if invalid data, value of 0 uses last valid data
 if( (chan > 0) && (chan <=  MAX_CHANNELS)  ) {
      Failsafe[chan] = value;
void  ServoDecodeClass::setFailsafe(){
// setFailsafe with no arguments sets failsafe for all channels to their current values
// usefull to capture current tx settings as failsafe values
 if(State == READY_state)
   for(byte chan = 1; chan <=  MAX_CHANNELS; chan++) {
       Failsafe[chan] = Pulses[chan];

int ServoDecodeClass::GetChannelPulseWidth( uint8_t channel)
 // this is the access function for channel data
 int result = 0; // default value
 if( channel <=  MAX_CHANNELS)  {
    if( (State == FAILSAFE_state)&& (Failsafe[channel] > 0 ) )
        result = Failsafe[channel]; // return the channels failsafe value if set and State is Failsafe
      else if( (State == READY_state) || (State == FAILSAFE_state) ){
        cli();       //disable interrupts
        result =  Pulses[channel] ;  // return the last valid pulse width for this channel
        sei(); // enable interrupts
 return result;
// make one instance for the user
ServoDecodeClass ServoDecode = ServoDecodeClass() ;

In the .h, you need to change icpPin from 8 to 49 // Input is on digital pin 49 on Mega

Will try to the the version with conditional defines next week


ubuntucat : what are your settings in the servodecoder  for the vex transmitter?


mem, have you done anything like ServoDecode when you don't have access to the combined receiver pulse train?  I'd like to basically recreate the data for a pulse train by spying on the individual servo lines.  This is for data logging or analysis.

I am guessing that it's typical that only one servo output of the receiver unit is getting its pulse at any given time, at least until you get up to the models with more channels than would fit in the 20ms servo clock.


I have had mixed results. For receivers that have a break between each channel you can connect the channels to an 'or' gate and monitor that. But many receivers don't provide any break between channels so the decoder can't tell when one pulse ends and the other begins. Also, many digital receivers  overlap the pulses (starting all channels at the same time).

I found the easiest solution was to tap into the composit signal in a non-digital receiver, but if you have a scope its worth looking at the channel outputs to see if you can produce a composite signal.  


mem, thanks for that.

I just happened to find someone who displayed timing for the Futaba R617FS which is one rx I'm using.  http://www.rcgroups.com/forums/showthread.php?p=11002684#post11002684

I haven't found a "where to solder" guide to find the composite ppm signal.

Go Up