radio control reciever spektrum ar6100 ar6200 signal noise

Yes u need pwm ports cause the signal varies in length. The problem I was getting is that the measurement of e length of signal varied pretty drastically while reading to the point that if I were to use them the program would just be jittery and frankly very hard to use. Thanx for the links but I have read those. I have used a smoothing algorithm to smoothe the signals but unfortubately it makes the programs response laggy and very unsmooth when user input is attempted.

Hi Guys,

I am also reading RC Receiver signals in my projects. Its very simple using an interrupt, my function that reads the input has very little code.

Sample interrupt routine -

// this is the interrupt service routine, note that is called on pin CHANGE, we use
// HIGH to catch the start of a pulse, LOW to catch the end
// nThrottleIn must be declared as volatile as its used in both this function and loop

void calcInput()
{
  // read the PIN, figure out if we are starting HIGH or ending LOW
  if(digitalRead(THROTTLE_SIGNAL_IN_PIN) == HIGH)
  { 
    // NEW PULSE
    ulStartPeriod = micros();
  }
  else
  {
    // END OF PULSE
    ulEndPeriod = micros();
    // trap the first pulse which is often a falling pulse and otherwise causes an unexpected value error
    if(ulStartPeriod)
    {
      nThrottleIn = (int)(ulEndPeriod - ulStartPeriod);
    }
  }
}

I am seeing the signal wander by a few ms, I am assuming that this is originating in my RC Equipment, not the Arduino. I am not using a smoothing function as the project is ultimately going to end up in my radio controlled race car. Instead of smoothing I am using 'confidence', I have a very simple algorithm that compares the most recently read input to the last input, if the difference is large, the assumption is 'this guy really wants to turn, accelerate, brake' and the input is used. If the input is very close to the last value I increase a confidence value, if I see enough consecutive increases in confidence I adopt the new value, however if the input at any point reverts to the original value, confidence in the intervening values resets to 0. This way I ignore wandering around the current value, but can still react instantly to meaningful changes in input.

Sample Confidence code -

NOTE - code is cut and paste from a wider project, please check my blog over the weekend for the full context and final version.

     // If we assume a range of 1000 to 2000 to cover 60 degrees, 4 microseconds = .25 of a degree
     // Due to the transmitter quality or arduino timing, we often get readings that drift around by 4 or 8ms.
     // if the input signal is 16 or more ms different that the current output, change immediatley
     // if the input signal is 8 or more different get 2 consecutive readings before change
     // if the input signal is 4 or more different get 4 consecurity readings before change
     int nDifference = nThrottleIn - nThrottleOut;
     if((nDifference > 16) || (nDifference < -16))
     {
      nConfidence = 16;
     }
     else
     {
      if(nDifference == 0)
      {
        // previous deviations can be discarded as noise, set confidence back to 0
        nConfidence = 0;
      }
      else
      {
        if((nDifference > 8) || (nDifference < -8))
        {
          nConfidence ++;
        }
        if((nDifference >= 4) || (nDifference <= -4))
        {
          nConfidence ++;
        }
      }
     }

      // we only want to call this for meaningful changes
      // repeated calling causes servo chatter which cost power, causes heat, wear and tear

     // if confidence >= 4 we are considering this as a meaningful change so 
     //  will put whatever work we want to do with the input into this section
     if(nConfidence >= 4) // we should move all of the confidence handling to a seperate function and fine tune
     {
       // Reset the confidence level back to 0
       nConfidence = 0;

       // Useful work/ Your code goes in here .... i.e.       throttleServo.writeMicroseconds(nThrottleIn);
      }

[EDIT - moved throttleServo.writeMicroseconds into last if block ]

I will post the whole project with code and write up on my blog this weekend see -

DuaneB

Thank you Duane. I like the aproach you're taking with your code. I'm thinking I can take your approach and generalize it into a function so it can effectively monitor all 4 channels from the receiver without having to write four separate interrupt handlers confidence code.

I have a two other channels that are really binary in nature. One of these will be used to switch between autonomous and manual control. I really needed the receiver data for manual control mode.

Like your blog. I'll have to keep an eye on it!

Hi,
I have spent the evening trying to eliminate noise in my system, I have reduced rogue pulses to about 1 in 50, mostly through decoupling capacitors in the power lines. It will eventually be necessary to update the confidence algorithm to handle rogue pulses but while its on the bench I want to get the hardware as clean as possible.

I am still on breadboard at the moment, hopefully a move to a strip board shield will clean up some of the remaining noise.

Interestingly my rogue pulses are concentrated around neutral, with any amount of acceleration or braking the rogue pulses drop to one every few seconds (better than 1 in 200 or 300) , I have observed the same results for two different electronic speed controllers a Tamiya TEU101BK and a MTroniks IPSport6.

Both of my speed controllers are budget orientated, if you are using better quality electronics you may not have a problem.

I will let you know how I get on after the move to strip board.

Duane.

My son's R/C truck, the recipient of the Arduino brain, has a Rooster rock crawling speed controller, so it is a higher-end controller. I'm probably a few days away from being able to test anything that combines the Arduino with the receiver, but I'll let you know how the testing goes.

I'll start with something simple with one channel, and take it from there.

Duane,

I'm looking through your code, but I'm struggling with the interrupts. Can I generate an interrupt with any input, or do I have to used one of the built-in hardware interrupt pins, such as pins 2 and 3?

ThanX!

EDIT:

I think I'll have to wait to see your code on your blog. I'm having a difficult time figuring out how all the pieces fit together at this point.

As for the interrupts question above, I'm pretty sure I need to use the built-in hardware interrupts, of which there are 6 on the Arduino Mega.

Does this reciever have a ground test port on the power connector? JR recievers map the detector output to the signal pin of the power connector so you can inject controls via a cable from the trainer port on the transmitter, it also allows a second reciever to decode a master's bitstream. If raw data is available you should be able to snoop the bitstream with the Serial object.

Does this reciever have a ground test port on the power connector? JR recievers map the detector output to the signal pin of the power connector so you can inject controls via a cable from the trainer port on the transmitter, it also allows a second reciever to decode a master's bitstream. If raw data is available you should be able to snoop the bitstream with the Serial object.

After reading your post, I did some research and got conflicting information. Officially, it seems that the AR6200 receiver does not have a flight log port, but I've read on some forums that it does. If it does, I'm not sure how I would be able to separate the data for the channels. I've read somewhere that the longest low pulse would precede the timing bit and that the channel signals should follow sequentially. On that basis I would assume that the I would receive 6 pulses (6 channel receiver) of varying lengths each one representing the pulse width that would be sent to it's respective channel.

I guess I could run a test by connecting that batt/bind port to a input on the Arduino and do some data logging.

Hi,
My thinking is that for most projects its sufficient to let the receiver handle all of the channel decoding. It is very simple to read a channel once it has been decoded.

The following code sample is a stripped down extract from my current project, its very simple all it does is -

  1. Create a Servo object and set the servo position to centered - Servos and ESCs use the same signals, so a centered servo is the same signal as neutral throttle - a pulse width of 1500ms.

  2. Create an interrupt and a service routine (calcInput) , the service routine calculates the input pulse width by reading from the receiver.

The simplest test you can do with this code is to connect the PWM output pin to the interrupt PIN and read your own signal, maybe then add a potentiometer on an analogue input which you can read and adjust the PWM output in your code to simulate a controller.

Once you are happy with that its time to connect a real receiver and ESC. I would suggest that when you connect to a real ESC you initially fix the output to 1500ms or NEUTRAL_THROTTLE as I have defined it, this will reduce the risk of any unexpected behavior.

Notes -

The code has been stripped down to show only the bare minimum, there is no error handling and no means to trap rogue signals. Before sending anything other than the NEUTRAL_THROTTLE signal you should adapt the code to handle errors, out of range conditions etc.

While the code has been compiled, it has not been uploaded and tested, it is a cut and paste from a larger project, the attached code has not been tested outside of the wider project but is expected to work for the purpose of generating a PWM output suitable for a servo and reading the same output back in through an interrupt.

Please give some consideration to protecting your Arduino when interfacing to external systems, particularly noisy ones such as RC Systems with motors attached.

Stripped down sample -

#include <Servo.h>


// Digital Output
#define THROTTLE_SIGNAL_OUT 9 // early versions of Arduino only support the Servo Library on digital pin 9 and 10 so lets stay with this convention 

// Interupts
#define THROTTLE_SIGNAL_IN 0 // INTERRUPT 0 = DIGITAL PIN 2
#define THROTTLE_SIGNAL_IN_PIN 2 // INTERRUPT 0 = DIGITAL PIN 2

#define NEUTRAL_THROTTLE 1500

// Global Variables and Resources
Servo throttleServo;

int nThrottleOut = NEUTRAL_THROTTLE;
volatile int nThrottleIn = NEUTRAL_THROTTLE; // volatile, we set this in the Interrupt
volatile unsigned long ulStartPeriod = 0;
volatile unsigned long ulEndPeriod = 0;

volatile boolean bNewThrottleSignal = false;

void setup()
{
  throttleServo.attach(THROTTLE_SIGNAL_OUT);
  
  attachInterrupt(THROTTLE_SIGNAL_IN,calcInput,CHANGE);
}

boolean bInit = false;

void loop()
{
  if(bNewThrottleSignal)
  {
    bNewThrottleSignal = false;
    Serial.println(nThrottleIn);
  }
  
  if(false == bInit)
  {
    bInit = true;

    // Note that we only do this once, the function sets the hardware counters within the arduino to output
    // a pulse of 1500ms about 50 times a second, this will continue automatically in the background until we 
    // explicitly tell it to stop or change the value - very useful !
    throttleServo.writeMicroseconds(NEUTRAL_THROTTLE);
    Serial.println("Done Init");
  }
}

void calcInput()
{
  // read the PIN, figure out if we are starting HIGH or ending LOW
  if(digitalRead(THROTTLE_SIGNAL_IN_PIN) == HIGH)
  { 
    // NEW PULSE
    ulStartPeriod = micros();
  }
  else
  {
    // END OF PULSE
    ulEndPeriod = micros();
    // trap the first pulse which is often a falling pulse and otherwise causes an unexpected value error
    if(ulStartPeriod)
    {
      nThrottleIn = (int)(ulEndPeriod - ulStartPeriod);
      ulStartPeriod = 0;
      // set on updating nThrottleIn, reset by loop when read
      // this way we only pay attention to new signals, leaving us free to do more work in loop as the project develops
      bNewThrottleSignal = true;
    }
  }
}

Now that I have located the source of the noise in my current project I hope to make some rapid progress over the next week or so, see here for a small update - http://rcarduino.blogspot.com/

DuaneB

There is no neutral on throttle. ESC arming requires stick full back with no trim, this is to prevent lost fingers, sudden fly aways ect. 0 is actually -45 degrees for a standard servo, also note transmitters support axis reversal on the transmit side, as not all throttle applications on fueled engines have the throttle movement in the same direction. Most of us that fly seriously try to build our installations for an engine shutdown on a reciever failure/undervoltage.

Hi,
It makes perfect sense that there would be no neutral on air ESCs, however car ESCs have a neutral at 1500, halfway between full brake/reverse and full throttle. Similar to air ESCs, car ESCs need to arm, in order to do this the neutral signal should be sent for a few seconds.

Whether you intend to use a land/air/marine ESC, you will still be able to monitor the pulse width being sent from you receiver for any single channel by using the code sample provided and by connecting INT1 (Pin2 on my UNO) to the white signal line from your receiver.

Duane.

Hi,

Just a quick follow up to my post above - does anyone have a tried and tested protection circuit for interfacing between the receiver signal (6v or more in some applications) and the 5V Arduino ?

Duane.

Duane,

First of all, thank you for posting that code. Working with what you posted,i generalized the code so that the same interrupt handler can be used for all the receiver channels. I've posted it below in case in can help others.

The loop() is only working with one of the 6 channels as that's all I connected for testing, but I think the principle is pretty clear.

#include <Servo.h>

// Digital Output - Define pins attached to Servos
#define SV_DIG_OUT 8          // define Dig Servo output
#define SV_THROTTLE_OUT 9     // define Throttle Servo output
#define SV_FRONT_STEER_OUT 10 // define Front Steering output 
#define SV_REAR_STEER_OUT 11  // define Rear Seteering output

// Digital Inputs - Define Inputs from Receiver
#define RX_AUX1 2 // AUX1 Pin (maps to Interrupt 0)
#define RX_GEAR 3 // GEAR Pin (maps to Interrupt 1)
#define RX_THRO 21 // THRO Pin (maps to Interrupt 2)
#define RX_AILE 20 // AILE Pin (maps to Interrupt 3)
#define RX_ELEV 19 // ELEV Pin (maps to Interrupt 4)
#define RX_RUDD 18 // RUDD Pin (maps to Interrupt 5)

// Interupts - Define Interrupts associated with RX inputs
#define INT_AUX1 0 // INTERRUPT 0 = DIGITAL PIN 2
#define INT_GEAR 1 // INTERRUPT 1 = DIGITAL PIN 3
#define INT_THRO 2 // INTERRUPT 2 = DIGITAL PIN 21
#define INT_AILE 3 // INTERRUPT 3 = DIGITAL PIN 20
#define INT_ELEV 4 // INTERRUPT 4 = DIGITAL PIN 19
#define INT_RUDD 5 // INTERRUPT 5 = DIGITAL PIN 18


#define NEUTRAL 1500

// Global Variables and Resources
Servo svFrontSteer;
Servo svRearSteer;
Servo svThrottle;
Servo svDig;

boolean bInit = false;

int nThrottleOut = NEUTRAL;

// We have 6 channels to measure, so we need these setup as arrays.
volatile int nChannelIn[6];
volatile unsigned long ulStartPeriod[6];
volatile unsigned long ulEndPeriod[6];

volatile boolean bNewSignal[6];

void setup()
{
  // Attach the servos. I'm only using one for testng.
  svFrontSteer.attach(SV_FRONT_STEER_OUT);
  
  attachInterrupt(INT_AUX1,handleINT0,CHANGE);
  attachInterrupt(INT_GEAR,handleINT1,CHANGE);
  attachInterrupt(INT_THRO,handleINT2,CHANGE);
  attachInterrupt(INT_AILE,handleINT3,CHANGE);
  attachInterrupt(INT_ELEV,handleINT4,CHANGE);
  attachInterrupt(INT_RUDD,handleINT5,CHANGE);
  
  Serial.begin(115200);
  
    // Initialize all the servos to center positions.
    svFrontSteer.writeMicroseconds(NEUTRAL);
    
    Serial.println("Done Init");
}

void loop()
{
  if(bNewSignal[INT_THRO])
  {
    bNewSignal[INT_THRO] = false;
    Serial.println(nChannelIn[INT_THRO]);
    svFrontSteer.writeMicroseconds(nChannelIn[INT_THRO]);
  }
  

}

void handleINT0() {calcInput(RX_AUX1, INT_AUX1);}
void handleINT1() {calcInput(RX_GEAR, INT_GEAR);}
void handleINT2() {calcInput(RX_THRO, INT_THRO);}
void handleINT3() {calcInput(RX_AILE, INT_AILE);}
void handleINT4() {calcInput(RX_ELEV, INT_ELEV);}
void handleINT5() {calcInput(RX_RUDD, INT_RUDD);}

void calcInput(int rxChannel, int rxInt)
{
  // read the PIN, figure out if we are starting HIGH or ending LOW
  if(digitalRead(rxChannel) == HIGH)
  { 
    // NEW PULSE
    ulStartPeriod[rxInt] = micros();
  }
  else
  {
    // END OF PULSE
    ulEndPeriod[rxInt] = micros();
    // trap the first pulse which is often a falling pulse and otherwise causes an unexpected value error
    if(ulStartPeriod[rxInt])
    {
      nChannelIn[rxInt] = (int)(ulEndPeriod[rxInt] - ulStartPeriod[rxInt]);
      ulStartPeriod[rxInt] = 0;
      // set on updating nThrottleIn, reset by loop when read
      // this way we only pay attention to new signals, leaving us free to do more work in loop as the project develops
      bNewSignal[rxInt] = true;
    }
  }
}

It works well in testing with one channel, and I hope to test it with 4 channels in the next couple of days. I also dropped the bInit code by moving it to the setup() section of the code.

Hi,

Glad you found the code useful, don't forget to go back and look at the 'confidence' algorithm before connecting a servo or ESC.

I found that without the confidence I would be sending 10 or 20 commands to the servo every second, this caused a lot of unnecessary jitter. I am not sure whether this jitter is caused by the stream of small changes or whether at some low level the new command resets the signal timing - either way its easy to avoid with the 'confidence' trick.

I thought you mentioned that you are using a Mega which has sufficient interrupt pins for your purpose, I am using a UNO which does not have enough pins for my project, luckily there are a few libraries which enable the underlying ATMega interrupt functionality on any Arduino PIN. I will be investigating PCInt for this purpose, you might also want to have a look over it, its doing something similar to your interrupt mapping but possibly more efficiently (note I have not looked at it in detail yet, so maybe it isn't )

I would also suggest that you change the method you use to track which PIN a new signal is available on. If you are using 8 or less channels, a single BYTE combined with BIT masks will allow you to track 8 channels, this is more efficient that using 8 Bytes and array operations. Its trivial, but to those of us in the UNO world, its a criminal waste of memory.

Example of bit flags-

BYTE bChannelActive = 0;
#define CHANNEL1_ACTIVE 0x1
#define CHANNEL2_ACTIVE 0x10
#define CHANNEL3_ACTIVE 0x100
#define CHANNEL4_ACTIVE 0x1000
etc etc

if(bChannelActive) // if its anything but 0 we have a new signal
{
if(bChannelActive & CHANNEL1_ACTIVE)
{
// channel 1 was active
}
// here I am using if to give every channel equal priority, we could use if else to implement a simple priority system
if(bChannelActive & CHANNEL2_ACTIVE)
{
// channel 2 was active
}
}

You could also get rid of the ulEndPeriod array as well, its only used as a temporary holder for the end period value in calcInput, if you make it a locally declared variable there, thats another 20 Bytes saved -

in the calc input function -

unsigned long ulEndPeriod = micros();

I believe (need to read up to be sure) volatile variables are slower to operate on so by removing the array references and the volatile keyword, we get two optimisations in one.

This lot might not matter to you much at the moment, but by the time you have code managing 6 channels you will wish you started with size and speed in mind - hope it helps

Good call on the bInit by the way,

Duane.

I like the changes you propose, particularly with the ulEndPeriod. That should be a local.

I'll have to spend a little more time thinking about the binary register. What I'm passing to the Interrupt handler is the Interrupt index which I use as the index for multiple arrays (although probably more than necessary). I'll have to spend some time in front of the code to see what I could do to optimize.

ThanX for the feedback.

I'll also look into PCint.

I was looking at your example with bit flags, and I'm not sure I can save any memory with that.

The way I structured the program, I'm passing the active channel to the calcInput() function using a constant, which would be replaced during compilation. Once we get to calcInput, the channel (rxChannel) and interrupt (rxInt) identifier variables are local variables. Since I'm using rxInt to index the arrays, I don't think I could use bit flags without increasing the code a fair bit, and since they are local, I'm not sure I really gain any space.

I did modify the code to remove the ulEndPeriod Array. I replaced it with a local variable ulTimeStamp that stores the value of micros() at the start of the function and assigns it either to the ulStartPeriod[] or uses it to calculate the pulse width depending on the condition. Seems more efficient and more accurate to do it this way.

The only arrays I have remaining are:

nChannelIn[] - This stores the pusle width that was calculated for each channel.

ulStartPeriod[] - This stores the starting time stamp for each interrupt. I understand that with most R/C systems, the signal pulses are sequential and that I could probably get away with using only one variable for this, but I've read that some of the newer spread spectrum radios don't observe this convention and I could be receiving overlapping signals. This seemed to be the safer approach since I'm using a spread spectrum radio system.

bNewSignal[] - Used to confirm a new signal was received for each channel.

I'll start digging into PCint now.

EDIT: Take a look at the following link:

I think this will simplify your need for additional interrupts.

http://arduino.cc/playground/Main/PinChangeInt

I pulled out some Futaba 1270's and dug for quite a while to find a matching tx and rx crystal pair. The futaba and hitech recievers do not provide a ground test port, on the JR radios have this feature. When they correct the site issues and I remember, I'll upload some screenshots of the waveforms if you all would like. You could pack alot of additional control data past the 7th servo signal and well before the frame boundary.

We finally received some cables we were waiting for to connect all the Rx channels and servos to the Arduino Mega. What we discovered is that the I/O shield isolates the Vcc for the various sensor/servo ports from the +5V of the Arduino. As a result, the ESC was able to provide power for the Rx and the other servos as it would if the Arduino wasn't there, but that it won't power the Arduino.

Fortunately, the ESC has a auxilliary power output connector which I was able to wire into the barrel plug on the Arduino Mega. The Mega takes care of regulating that to +5V.

I have noticed an increase in servo jitter since connecting the ESC. I'm further assuming that noise is on the Vcc line, as it is affecting some of the other servos. I'm debating whether to try to filter it or simply write the code to smooth it out. It seems as though I'll either have to sacrifice responsiveness or accuracy if I do it in code.

Other than that, my echo code takes what it receives from the radio, and passes it on to the servos. This is exactly what the robot will do in manual mode, so that's half the battle. Once we smooth out the signals, we can start on the autonomous functions.

Let me know what you think.

ThanX!

Hi,
I would work on eliminating issues at the hardware level rather than code around them. In my projects I have added decoupling capacitors, .01 .1 1 and 47uf across the power into the arduino, probably overkill but seems to help.

Duane B

rcarduino.blogspot.com