Reading PWM signal from RC Receiver

Hi, I'm currently having trouble reading a solid stable signal from the PWM coming out of an RC Receiver into arduino as an input.

I'm getting a reading, however the reading I'm getting isn't what I need. The code I'm using is utilizing pulseIn, but I find that the number I'm getting is bouncing around to quickly and way to wide of a value. For instance if my stick stays neutral it should technically be getting 1500 solid not moving. However it comes back reporting anywhere from 1430 to say 1444 constantly going back and forth.

What I need is it to give me a solid number that is consistent and doesn't take up to much time processing to minimize a lagged input.

I was starting to think, well what if I can take the max value that it is giving me then average that out to make it more stable. The problem with that thought is it would take to long to process giving me a delayed response from when I turn the wheel on the transmitter (or pull the throttle for that matter) and get the correct value through serial. I mean I can deal with a 1-2 number flux. However its moving around way to much.

If anyone knows a way to do this, It'd be greatly appreciated.

I assume you'll have to average. And, you might want to correct the offset too (add ~63 or multiply by ~1.044 to make your average nearer to 1500).

Averaging shouldn't take too long when you compare processor speed to mechanical stick speed. You'll probably want to take a reading every-few milliseconds and calculate a moving average. You'll just have to experiment with the number of readings to average, and the time delay between readings (if any) to see what it takes. (See the [u]Smoothing Example[/u].) And, don't use delay(). If you want to allow some time between readings, follow the Bink Without Delay example so your program can do other things between taking readings.

I think the delay is more related to the signal variation and how much averaging/filtering is needed , rather than how fast the processor works.

Maybe this will be of interest:

Could you show your sketch. When I use puseIn() on a single channel I get pretty precise results .

I've pretty much torn my sketch apart. As it wasn't working for me. I want to be using UnoJoy to switch the arduino over from a programming board, to a PC Joystick. Even when I average out the input that it reads it wills till jump around when in 'joystick' mode.

The sketch I'm using for the UnoJoy joystick is below.

#include "UnoJoy.h"

const int numReadings = 16;

int ch1index = 0;
int ch2index = 0;
int ch1t = 0; //Channel 1 Total
int ch2t = 0; //Channel 2 Total
int ch1a = 0; //Channel 1 Average
int ch2a = 0; //Channel 2 Average
int ch1raw[numReadings]; //Raw Ch1 Data
int ch2raw[numReadings]; //Raw Ch2 Data
int ch1 = 0; //Final Ammount /255
int ch2 = 0; //Same for Ch2

void setup(){
  setupPins();
  setupUnoJoy();
  for (int ch1read = 0; ch1read < numReadings; ch1read++)
    ch1raw[ch1read] = 0;
  for (int ch2read = 0; ch2read < numReadings; ch2read++)
    ch2raw[ch2read] = 0;
}

void loop(){
  // Always be getting fresh data
  dataForController_t controllerData = getControllerData();
  setControllerData(controllerData);
  ch1t= ch1t - ch1raw[ch1index];
  ch2t= ch2t - ch2raw[ch2index];
  ch1raw[ch1index] = ch1;
  ch2raw[ch2index] = ch2;
  ch1 = pulseIn(2, HIGH, 20000);
  ch2 = pulseIn(3, HIGH, 20000);
  ch1 = map(ch1, 965, 1965, 0, 255);
  ch2 = map(ch2, 960, 1955, 0, 255);
  
  //Adding the total.
  ch1t= ch1t + ch1raw[ch1index];
  ch2t= ch2t + ch2raw[ch2index];
  
  //Indexing + 1
  ch1index = ch1index + 1;
  ch2index = ch2index + 1;
  
  if (ch1index >= numReadings)
    ch1index =0;
  if (ch2index >= numReadings)
    ch2index =0;
    
  ch1a = ch1t / numReadings;
  ch2a = ch2t / numReadings;
  

  
  //Serial.print("Channel 1:");
  //Serial.println(ch1a);
  
  //Serial.print("Channel 2:");
  Serial.println(ch2a);
  
  //delay(10);
  
}

void setupPins(void){
  // Set all the digital pins as inputs
  // with the pull-up enabled, except for the 
  // two serial line pins
 pinMode(2, INPUT);
 pinMode(3, INPUT);
}

dataForController_t getControllerData(void){
  
  // Set up a place for our controller data
  //  Use the getBlankDataForController() function, since
  //  just declaring a fresh dataForController_t tends
  //  to get you one filled with junk from other, random
  //  values that were in those memory locations before
  dataForController_t controllerData = getBlankDataForController();
  // Since our buttons are all held high and
  //  pulled low when pressed, we use the "!"
  //  operator to invert the readings from the pins
  
  // Set the analog sticks
  //  Since analogRead(pin) returns a 10 bit value,
  //  we need to perform a bit shift operation to
  //  lose the 2 least significant bits and get an
  //  8 bit number that we can use  
  controllerData.leftStickX = ch1a;
  //controllerData.leftStickY = analogRead(A1) >> 2;
  controllerData.rightStickX = ch2a;
  //controllerData.rightStickY = analogRead(A3) >> 2;
  // And return the data!
  return controllerData;
}

Or well I was using it. I've been experimenting with different codes to see what kind of results I get as a Serial Print. My overall goal is to get an 8bit number I can spit back out to the controllerData that doesn't move if I don't move my controller.

What I found strange while doing some testing, was that if I connected my ESC to the receiver for power, and just take the PWM signal to the arduino it'll give me a reading of min 0, max ~1470 so its pulsing up and down between 0 and 1470. While if I use the power from the 5v on the arduino it'll just bounce around from ~1430-1470. Don't really know what that means.

As for that page, I've looked over it a few times, I didn't quite understand it. I've got very basic knowledge when it comes to coding, even more so when it comes to arduino.

I would suggest you start with a simple example like the one on the reference page:

http://arduino.cc/en/Reference/PulseIn

Which RC receiver?
An analog receiver will have jitter on its output, that is how things are.

I had never thought about that, so i tried:

  • Analog solution: Futaba 35MHz ppm tx and rx
  • Digital solution: FrSky Taranis -> X8R 2.4 GHz

But the variation was much the same: 7 microseconds.
I used pulseIn()

Update

If I use DuaneB interrupt method from the link in previous post, the measurement is almost spot on.

I get 1500 microseconds for neutral and a few 1504.

The TX/RX I'm using is the Traxxas TQ system that came with my Traxxas Slash. I've done simple readings with pulseIn() doesn't really help me figure anything out since I've seen the differences and the range in which I get values.

Did you try DuaneB's sketch?

It gives a more stable reading:

// First Example in a series of posts illustrating reading an RC Receiver with
// micro controller interrupts.
//
// Subsequent posts will provide enhancements required for real world operation
// in high speed applications with multiple inputs.
//
// http://rcarduino.blogspot.com/ 
//
// Posts in the series will be titled - How To Read an RC Receiver With A Microcontroller

// See also http://rcarduino.blogspot.co.uk/2012/04/how-to-read-multiple-rc-channels-draft.html  

#define THROTTLE_SIGNAL_IN 0 // INTERRUPT 0 = DIGITAL PIN 2 - use the interrupt number in attachInterrupt
#define THROTTLE_SIGNAL_IN_PIN 2 // INTERRUPT 0 = DIGITAL PIN 2 - use the PIN number in digitalRead

#define NEUTRAL_THROTTLE 1500 // this is the duration in microseconds of neutral throttle on an electric RC Car

volatile int nThrottleIn = NEUTRAL_THROTTLE; // volatile, we set this in the Interrupt and read it in loop so it must be declared volatile
volatile unsigned long ulStartPeriod = 0; // set in the interrupt
volatile boolean bNewThrottleSignal = false; // set in the interrupt and read in the loop
// we could use nThrottleIn = 0 in loop instead of a separate variable, but using bNewThrottleSignal to indicate we have a new signal 
// is clearer for this first example

void setup()
{
  // tell the Arduino we want the function calcInput to be called whenever INT0 (digital pin 2) changes from HIGH to LOW or LOW to HIGH
  // catching these changes will allow us to calculate how long the input pulse is
  attachInterrupt(THROTTLE_SIGNAL_IN,calcInput,CHANGE);

  Serial.begin(9600); 
}

void loop()
{
 // if a new throttle signal has been measured, lets print the value to serial, if not our code could carry on with some other processing
 if(bNewThrottleSignal)
 {

   Serial.println(nThrottleIn);  

   // set this back to false when we have finished
   // with nThrottleIn, while true, calcInput will not update
   // nThrottleIn
   bNewThrottleSignal = false;
 }

 // other processing ... 
}

void calcInput()
{
  // if the pin is high, its the start of an interrupt
  if(digitalRead(THROTTLE_SIGNAL_IN_PIN) == HIGH)
  { 
    // get the time using micros - when our code gets really busy this will become inaccurate, but for the current application its 
    // easy to understand and works very well
    ulStartPeriod = micros();
  }
  else
  {
    // if the pin is low, its the falling edge of the pulse so now we can calculate the pulse duration by subtracting the 
    // start time ulStartPeriod from the current time returned by micros()
    if(ulStartPeriod && (bNewThrottleSignal == false))
    {
      nThrottleIn = (int)(micros() - ulStartPeriod);
      ulStartPeriod = 0;

      // tell loop we have a new signal on the throttle channel
      // we will not update nThrottleIn until loop sets
      // bNewThrottleSignal back to false
      bNewThrottleSignal = true;
    }
  }
}