Reading data from RC Receiver trouble with noise

I’m using pinchangeint lib to read from pins 2,3

Those are the external interrupt pins. Why are you using the pin change interrupt library to add interrupts to those pins? Use attachInterrupt() instead.

Please post your code in FULL! and any errors you may get. used the "#" button about to insert your code or just put your code within "{your code here}"

lacion:
The problem with this is that when ever the remote if off or the i get out of range the RC car I’m trying to drive just goes crazy.

In this case you need to do a sanity check of the values you're reading to make sure it is a valid signal and not just random noise. For example you could find the minimum and maximum positions of each axis on your controller and detect when all channels are outside the valid range for longer than say a second, and revert to some fail-safe values (i.e. center the steering and stop the motors).

sorry about pins 2,3 it was a mistake i'm using 5,6

my code is in visual studio right now with a bunch of external files, so i'm to use a code example that has exactly the same problem.

from http://rcarduino.blogspot.co.uk, thought it acutally dosent matter as i said i used several diferent methods and i always see the error when the remote if off, as soon as the remote is on the values are what they should be.

knowing the range wont help as when the remote if off the noise goes trought the same ranges.

#include <RCArduinoFastLib.h>
#include <PinChangeInt.h>

#define THROTTLE_IN_PIN 5
#define STEERING_IN_PIN 6
#define AUX_IN_PIN 7

#define THROTTLE_OUT_PIN 8
#define STEERING_OUT_PIN 9
#define AUX_OUT_PIN 10

#define SERVO_THROTTLE 0
#define SERVO_STEERING 1
#define SERVO_AUX 2
#define SERVO_FRAME_SPACE 3

#define THROTTLE_FLAG 1
#define STEERING_FLAG 2
#define AUX_FLAG 4

volatile uint8_t bUpdateFlagsShared;

volatile uint16_t unThrottleInShared;
volatile uint16_t unSteeringInShared;
volatile uint16_t unAuxInShared;

uint16_t unThrottleInStart;
uint16_t unSteeringInStart;
uint16_t unAuxInStart;

uint16_t unLastAuxIn = 0;
uint32_t ulVariance = 0;
uint32_t ulGetNextSampleMillis = 0;
uint16_t unMaxDifference = 0;

void setup()
{
  Serial.begin(115200);

  Serial.println("multiChannels");

  CRCArduinoFastServos::attach(SERVO_THROTTLE,THROTTLE_OUT_PIN);
  CRCArduinoFastServos::attach(SERVO_STEERING,STEERING_OUT_PIN);
  CRCArduinoFastServos::attach(SERVO_AUX,AUX_OUT_PIN);
 
  CRCArduinoFastServos::setFrameSpaceA(SERVO_FRAME_SPACE,7*2000);

  CRCArduinoFastServos::begin();
  
  PCintPort::attachInterrupt(THROTTLE_IN_PIN, calcThrottle,CHANGE);
  PCintPort::attachInterrupt(STEERING_IN_PIN, calcSteering,CHANGE);
  PCintPort::attachInterrupt(AUX_IN_PIN, calcAux,CHANGE);
}

void loop()
{
  static uint16_t unThrottleIn;
  static uint16_t unSteeringIn;
  static uint16_t unAuxIn;
  
  Serial.println(unThrottleIn);
  
  static uint8_t bUpdateFlags;

  if(bUpdateFlagsShared)
  {
    noInterrupts();

    bUpdateFlags = bUpdateFlagsShared;
  
    if(bUpdateFlags & THROTTLE_FLAG)
    {
      unThrottleIn = unThrottleInShared;
    }
  
    if(bUpdateFlags & STEERING_FLAG)
    {
      unSteeringIn = unSteeringInShared;
    }
  
    if(bUpdateFlags & AUX_FLAG)
    {
      unAuxIn = unAuxInShared;
    }
    
    bUpdateFlagsShared = 0;
  
    interrupts()
  }
  
  if(bUpdateFlags & THROTTLE_FLAG)
  {
    CRCArduinoFastServos::writeMicroseconds(SERVO_THROTTLE,unThrottleIn);
  }

  if(bUpdateFlags & STEERING_FLAG)
  {
    CRCArduinoFastServos::writeMicroseconds(SERVO_STEERING,unSteeringIn);
  }

  if(bUpdateFlags & AUX_FLAG)
  {
   CRCArduinoFastServos::writeMicroseconds(SERVO_AUX,unAuxIn);
   }

  bUpdateFlags = 0;
}

void calcThrottle()
{
  if(PCintPort::pinState)
  {
    unThrottleInStart = TCNT1;
  }
  else
  {
    unThrottleInShared = (TCNT1 - unThrottleInStart)>>1;
    bUpdateFlagsShared |= THROTTLE_FLAG;
  }
}

void calcSteering()
{
  if(PCintPort::pinState)
  {
    unSteeringInStart = TCNT1;
  }
  else
  {
    unSteeringInShared = (TCNT1 - unSteeringInStart)>>1;

    bUpdateFlagsShared |= STEERING_FLAG;
  }
}

void calcAux()
{
  if(PCintPort::pinState)
  {
    unAuxInStart = TCNT1;
  }
  else
  {
    unAuxInShared = (TCNT1 - unAuxInStart)>>1;
    bUpdateFlagsShared |= AUX_FLAG;  }
}

Did you try to set a fail-safe program for when your remote is off or out or range, maybe to set the break or turn on a circle?

HazardsMind:
Did you try to set a fail-safe program for when your remote is off or out or range, maybe to set the break or turn on a circle?

Not yet i wanted to understand what the issue was first, my reading of several other post and blogs about this dont point to any noise so i'm assuming is something i'm doing wrong.

Your getting those wild values because the arduino is not seeing the correct data, it is probably getting interference from other sources other than the one it wants, so the car goes crazy.
Set a fail safe.

HazardsMind:
Your getting those wild values because the arduino is not seeing the correct data, it is probably getting interference from other sources other than the one it wants, so the car goes crazy.
Set a fail safe.

thats what i dont understand, i'm not reading the data from the PPM Stream i'm reading from the output channels of the receiver, does receivers have thoose fail safes? if they do shouldn’t that be happening at the receiver?

as i said when i connect the motors to the receiver i dont observe any issue.

i can implement a fail safe but i still thing there is a problem somewhere, i seem several project on github controlling rc cars havent seen any fail safe yet.

Can you post a picture of what your doing, and any data values your getting?

lacion:
thats what i dont understand, i'm not reading the data from the PPM Stream i'm reading from the output channels of the receiver,
does receivers have thoose fail safes? if they do shouldn’t that be happening at the receiver?

as i said when i connect the motors to the receiver i dont observe any issue.

Whether the receiver has fails-safes built in is up to the design of the receiver - some do, but in my experience only the more expensive ones.

You seem to be saying that if you use the original RC system and switch off the transmitter, the model just sits inertly. In my experience with RC systems in this situation it's common for them to sit inertly but occasionally take random and extreme actions (presumably in response to radio noise). It is possible that the Arduino is more susceptible to this than an ordinary servo because of the way it is connected to the receiver without any conditioning or smoothing. Although it might be possible to improve that slightly I don't think this solves the fundamental problem that you need your sketch to know whether it is getting a valid signal from the receiver before it acts on it.

On reflection, rather than looking for the pulse length being within the sensible range you should be looking for the pulse being received at the expected interval. You should be receiving the pulse at 40-50 Hz for each channel, and if you miss several pulses then it would be sensible to invoke the failsafe and keep it active until you have received several 'good' pulses.

Hi,
It might be that when your RC is off, your inputs are floating - quick and simple hardware solution, try adding pull down resistors on the inputs.

A software variation on what others have suggested is to pick one of the channels (throttle is a good one) and take a time stamp every time you receive a valid signal on this channel, if the difference between the time stamp and current time is greater than 60ms set the throttle to neutral. In my experience it is better to allow a signal through if its available than it is to enter a failsafe mode - i.e. car is out of range, enters neutral, you start walking towards the car, SUV comes around the corner, your back in range and drive your car away from the SUV - if you entered failsafe, you will need a new RC Car at this point.

In air applications something else might be perferable, but generally if a valid signal is available, try to use it.

Code not tested - I am in Cairo this week.

void loop()
{
  // create local variables to hold a local copies of the channel inputs
  // these are declared static so that thier values will be retained
  // between calls to loop.
  static uint16_t unThrottleIn;
  static uint16_t unSteeringIn;
  static uint16_t unAuxIn;
  // local copy of update flags
  static uint8_t bUpdateFlags;

  static unsigned long ulLastThrottleTime;

  // check shared update flags to see if any channels have a new signal
  if(bUpdateFlagsShared)
  {
    noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables

    // take a local copy of which channels were updated in case we need to use this in the rest of loop
    bUpdateFlags = bUpdateFlagsShared;
   
    // in the current code, the shared values are always populated
    // so we could copy them without testing the flags
    // however in the future this could change, so lets
    // only copy when the flags tell us we can.
   
    if(bUpdateFlags & THROTTLE_FLAG)
    {
      unThrottleIn = unThrottleInShared;
    }
   
    if(bUpdateFlags & STEERING_FLAG)
    {
      unSteeringIn = unSteeringInShared;
    }
   
    if(bUpdateFlags & AUX_FLAG)
    {
      unAuxIn = unAuxInShared;
    }
    
    // clear shared copy of updated flags as we have already taken the updates
    // we still have a local copy if we need to use it in bUpdateFlags
    bUpdateFlagsShared = 0;
   
    interrupts(); // we have local copies of the inputs, so now we can turn interrupts back on
    // as soon as interrupts are back on, we can no longer use the shared copies, the interrupt
    // service routines own these and could update them at any time. During the update, the
    // shared copies may contain junk. Luckily we have our local copies to work with :-)
  }
 
  // do any processing from here onwards
  // only use the local values unAuxIn, unThrottleIn and unSteeringIn, the shared
  // variables unAuxInShared, unThrottleInShared, unSteeringInShared are always owned by
  // the interrupt routines and should not be used in loop
 
  // the following code provides simple pass through
  // this is a good initial test, the Arduino will pass through
  // receiver input as if the Arduino is not there.
  // This should be used to confirm the circuit and power
  // before attempting any custom processing in a project.
 
  // we are checking to see if the channel value has changed, this is indicated 
  // by the flags. For the simple pass through we don't really need this check,
  // but for a more complex project where a new signal requires significant processing
  // this allows us to only calculate new values when we have new inputs, rather than
  // on every cycle.
  if(bUpdateFlags & THROTTLE_FLAG)
  {
    // DB 19/11/2012 added range test and timestamp if valid  - ulLastThrottleTime
    if(unThrottleIn > 1000 && unThrottleIn < 2000)
    {
      if(servoThrottle.readMicroseconds() != unThrottleIn)
      {
        servoThrottle.writeMicroseconds(unThrottleIn);
      }
      ulLastThrottleTime = millis();
    }
  }
 
  bUpdateFlags = 0;

  // if we didnt get a signal for 3 times the expected interval, enter neutral
  // this condition self clears if a signal is available
  if((millis() - ulLastThrottleTime) > 60)
  {
    // set a warning LED or BUZZER so we know whats happening
    // for you to do

    // set throttle to neutral
    servoThrottle.writeMicroseconds(1500);    
  }
}

Duane B

rcarduino.blogspot.com

DuaneB:
In air applications something else might be perferable, but generally if a valid signal is available, try to use it.

The failsafe does not prevent valid signals from being acted on, it places all the outputs to a defined state when there is no valid signal. In other words, your car will stop and wait for you to get back into range, rather than flailing around randomly.

In other words, your car will stop and wait for you to get back into range, rather than flailing around randomly.

Thats what the posted code will do

Duane B

here are some pics of the testings i'm doing right now.

i tryed waiting to get the correct data with the solution posted by Duane with several time variation and i do get less noise but the noise its still there. so i'm implementing a save mode.

here are some reading samples.

Remote Turn On:

Throttle: 1488.00 Yaw: 1484.00
Throttle: 1488.00 Yaw: 1484.00
Throttle: 1488.00 Yaw: 1485.00
Throttle: 1488.00 Yaw: 1487.00
Throttle: 1488.00 Yaw: 1485.00
Throttle: 1488.00 Yaw: 1488.00
Throttle: 1488.00 Yaw: 1488.00
Throttle: 1487.00 Yaw: 1488.00
Throttle: 1484.00 Yaw: 1488.00
Throttle: 1485.00 Yaw: 1488.00
Throttle: 1489.00 Yaw: 1487.00
Throttle: 1490.00 Yaw: 1485.00
Throttle: 1486.00 Yaw: 1488.00
Throttle: 1490.00 Yaw: 1488.00
Throttle: 1485.00 Yaw: 1487.00
Throttle: 1489.00 Yaw: 1484.00
Throttle: 1491.00 Yaw: 1484.00
Throttle: 1488.00 Yaw: 1484.00
Throttle: 1489.00 Yaw: 1485.00
Throttle: 1491.00 Yaw: 1488.00
Throttle: 1488.00 Yaw: 1488.00
Throttle: 1488.00 Yaw: 1488.00
Throttle: 1488.00 Yaw: 1487.00
Throttle: 1488.00 Yaw: 1485.00
Throttle: 1488.00 Yaw: 1488.00
Throttle: 1489.00 Yaw: 1488.00
Throttle: 1490.00 Yaw: 1489.00
Throttle: 1485.00 Yaw: 1490.00
Throttle: 1487.00 Yaw: 1485.00
Throttle: 1486.00 Yaw: 1487.00
Throttle: 1492.00 Yaw: 1485.00
Throttle: 1490.00 Yaw: 1488.00
Throttle: 1486.00 Yaw: 1487.00
Throttle: 1492.00 Yaw: 1485.00
Throttle: 1491.00 Yaw: 1486.00
Throttle: 1488.00 Yaw: 1482.00
Throttle: 1489.00 Yaw: 1487.00
Throttle: 1491.00 Yaw: 1485.00
Throttle: 1488.00 Yaw: 1488.00
Throttle: 1489.00 Yaw: 1487.00
Throttle: 1491.00 Yaw: 1484.00
Throttle: 1489.00 Yaw: 1484.00
Throttle: 1491.00 Yaw: 1484.00
Throttle: 1489.00 Yaw: 1484.00
Throttle: 1491.00 Yaw: 1485.00
Throttle: 1489.00 Yaw: 1487.00

Remote Turn Off:

Throttle: 3992.00 Yaw: 5611.00
Throttle: 10314.00 Yaw: 4931.00
Throttle: 12025.00 Yaw: 6948.00
Throttle: 4272.00 Yaw: 990.00
Throttle: 6472.00 Yaw: 1386.00
Throttle: 4270.00 Yaw: 3195.00
Throttle: 2598.00 Yaw: 2532.00
Throttle: 2041.00 Yaw: 318.00
Throttle: 3748.00 Yaw: 2936.00
Throttle: 8518.00 Yaw: 8635.00
Throttle: 942.00 Yaw: 3337.00
Throttle: 1402.00 Yaw: 4885.00
Throttle: 2865.00 Yaw: 2906.00
Throttle: 1350.00 Yaw: 1680.00
Throttle: 1600.00 Yaw: 2080.00
Throttle: 1002.00 Yaw: 1560.00
Throttle: 724.00 Yaw: 2724.00
Throttle: 1846.00 Yaw: 1053.00
Throttle: 2152.00 Yaw: 2206.00
Throttle: 2688.00 Yaw: 3835.00
Throttle: 2435.00 Yaw: 751.00

also tried the pull down resistor with no luck.

I think I understand whats going on here, but to help clearify for everyone else I'll break it down.
When the remote is on, your values are steady, that we know. Now when the remote is off the values go crazy, I may have an explanation for this.

For the normal wiring to just the servos (with the controller on), the servos are steady, but to anyone who owns those kind of RC receivers (any kind, they all do the same thing) when the radio is off, the servos begin to vibrate or twitch. This of course is due to interference, we know that, HOWEVER, because the data is going into the arduino, it takes time for the arduino to process the data. Now most amature programmers, (no offense to lacion or anyone else, just new programmers in general) think that when a program is running, the program is instantaneous, but it is not. The arduino uno/mega is rather slow when it comes to sorting it's data and sending it where it needs to go. But a receiver do not have to sort THAT MUCH data, so it is very quick to respond to a command, and that is where the problem lies.

lacion is getting data from the receiver and is going into the arduino, not fully aware, (again no offense), that the amount of time it takes the arduino to process one command, the receiver has already put out 5 or even 10 commands. SO when the remote is off, those twitchs are not being sampled correctly and because your also sending data to a monitor or LCD, that is even more processing time the arduino has to do before it is able to get a new command. And all that processing time puts out a very, almost seziure like movement, and your values show that on your display.

Now it is a different story when the remote is on, because the data is MUCH more steady and so the arduino appears to be working hand and hand, but really is it still not able to fully catch up.

So the bottom line is, the arduino is unable to fully keep up with the data from the receiver, so now lacion has to take that into consideration, and set limiters, constrain the data and hopefully that will make it work a little better; appear to work better.

Any objections?

Hi,

This is not correct, the arduino is easily able to process rc signal, there are only a few hundred per second.

What is happening is that the servo library will continue to send the last signal you gave it, so if you set it for full speed ahead, that is what it will do. Now if you turn the receiver off, it will still go full speed ahead because now there is nothing to tell it not to.

The code sample i provided will set the throttle to neutral if not valid signals are received for a short period it will also auto recover if a good signal is received.

Looking at the no receiver values i suugest that you build on the code provided with one of the suggestion provided by PeterH which is to count five of six good pulses consecutive pulses before allowing incoming signals to pass out to the channels.

Just out of interest, what transmitter and receiver are you using ?

Duane B

HazardsMind:
I think I understand whats going on here, but to help clearify for everyone else I'll break it down.
When the remote is on, your values are steady, that we know. Now when the remote is off the values go crazy, I may have an explanation for this.

For the normal wiring to just the servos (with the controller on), the servos are steady, but to anyone who owns those kind of RC receivers (any kind, they all do the same thing) when the radio is off, the servos begin to vibrate or twitch. This of course is due to interference, we know that, HOWEVER, because the data is going into the arduino, it takes time for the arduino to process the data. Now most amature programmers, (no offense to lacion or anyone else, just new programmers in general) think that when a program is running, the program is instantaneous, but it is not. The arduino uno/mega is rather slow when it comes to sorting it's data and sending it where it needs to go. But a receiver do not have to sort THAT MUCH data, so it is very quick to respond to a command, and that is where the problem lies.

lacion is getting data from the receiver and is going into the arduino, not fully aware, (again no offense), that the amount of time it takes the arduino to process one command, the receiver has already put out 5 or even 10 commands. SO when the remote is off, those twitchs are not being sampled correctly and because your also sending data to a monitor or LCD, that is even more processing time the arduino has to do before it is able to get a new command. And all that processing time puts out a very, almost seziure like movement, and your values show that on your display.

Now it is a different story when the remote is on, because the data is MUCH more steady and so the arduino appears to be working hand and hand, but really is it still not able to fully catch up.

So the bottom line is, the arduino is unable to fully keep up with the data from the receiver, so now lacion has to take that into consideration, and set limiters, constrain the data and hopefully that will make it work a little better; appear to work better.

Any objections?

I've been involved with R/C for decades and arduinos for around three years and what you say above makes absolutely no sense to me at all. Not saying what your wrote is wrong or not wrong, just makes no sense. There is nothing about the speed of the arduino that would factor in the symptoms being seen.

I would strongly suspect that the problem is just that the R/C receiver with no valid RF signal being received (transmitter turned off or out of range) is just randomly generating false intermittent pulses on it's servo output pins due to random RF noise. Keep in mind that such R/C receivers use AGC in their receiver circuitry so when there is no valid RF carrier signal on the frequency channel the receiver's internal gain is at maximum and subject to any random RF noise that might be around. And yes a better receiver design should or could 'clamp' all the servo channel outputs off unless there is some minimum signal level being received, but there is no information of this specific receivers features.

Note that it's standard R/C practice to always first turn on the transmitter, ensure the controls are in the desired position and then turn on the receiver/model. Having a 'hot' receiver on with no valid signal is an invitation for servo glitches.
Lefty

Something like this, you will need to declare the variables somewhere in your code but this is the basic idea -

void loop()
{
  // create local variables to hold a local copies of the channel inputs
  // these are declared static so that thier values will be retained
  // between calls to loop.
  static uint16_t unThrottleIn;
  static uint16_t unSteeringIn;
  static uint16_t unAuxIn;
  // local copy of update flags
  static uint8_t bUpdateFlags;

  static unsigned long ulLastThrottleTime;

  // check shared update flags to see if any channels have a new signal
  if(bUpdateFlagsShared)
  {
    noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables

    // take a local copy of which channels were updated in case we need to use this in the rest of loop
    bUpdateFlags = bUpdateFlagsShared;
   
    // in the current code, the shared values are always populated
    // so we could copy them without testing the flags
    // however in the future this could change, so lets
    // only copy when the flags tell us we can.
   
    if(bUpdateFlags & THROTTLE_FLAG)
    {
      unThrottleIn = unThrottleInShared;
    }
   
    if(bUpdateFlags & STEERING_FLAG)
    {
      unSteeringIn = unSteeringInShared;
    }
   
    if(bUpdateFlags & AUX_FLAG)
    {
      unAuxIn = unAuxInShared;
    }
    
    // clear shared copy of updated flags as we have already taken the updates
    // we still have a local copy if we need to use it in bUpdateFlags
    bUpdateFlagsShared = 0;
   
    interrupts(); // we have local copies of the inputs, so now we can turn interrupts back on
    // as soon as interrupts are back on, we can no longer use the shared copies, the interrupt
    // service routines own these and could update them at any time. During the update, the
    // shared copies may contain junk. Luckily we have our local copies to work with :-)
  }
 
  // do any processing from here onwards
  // only use the local values unAuxIn, unThrottleIn and unSteeringIn, the shared
  // variables unAuxInShared, unThrottleInShared, unSteeringInShared are always owned by
  // the interrupt routines and should not be used in loop
 
  // the following code provides simple pass through
  // this is a good initial test, the Arduino will pass through
  // receiver input as if the Arduino is not there.
  // This should be used to confirm the circuit and power
  // before attempting any custom processing in a project.
 
  // we are checking to see if the channel value has changed, this is indicated 
  // by the flags. For the simple pass through we don't really need this check,
  // but for a more complex project where a new signal requires significant processing
  // this allows us to only calculate new values when we have new inputs, rather than
  // on every cycle.
  if(bUpdateFlags & THROTTLE_FLAG)
  {
    // DB 19/11/2012 added range test and timestamp if valid  - ulLastThrottleTime
    if(unThrottleIn > 1000 && unThrottleIn < 2000)
    {
      if(gMode == MODE_FAIL_SAFE)
      {
       if(sGoodPulseCounter > 6)
       {
        gMode = MODE_NORMAL;
       }
      }
      if(gMode == MODE_NORMAL)
      {
        servoThrottle.writeMicroseconds(unThrottleIn);
      }

      ulLastThrottleTime = millis();
      sGoodPulseCounter++;
    }
    else
    {
      sGoodPulseCounter = 0;
    }
  }
 
  bUpdateFlags = 0;

  // if we didnt get a signal for 3 times the expected interval, enter neutral
  // this condition self clears if a signal is available
  if((millis() - ulLastThrottleTime) > 60)
  {
    // set a warning LED or BUZZER so we know whats happening
    // for you to do

    // set throttle to neutral
    servoThrottle.writeMicroseconds(1500);    
    gMode = MODE_FAIL_SAFE;
  }
}

Just to be clear, if your receiver decides to output junk when it does not have a signal, we cannot change that, what we are try to do is to contain it by not sending the junk signals to the servos or ESC.

I will add a full example which deals with all channels to the rcarduino blog when I get home tonight.

Duane B

Thought I had it. =(
Anyway the reason I thought it was the arduino's processing time was because I used an accelerometer going into two arduino's. both controlled the speed of a motor, but the one that was sending the data to the screen was a little slower to change the speed of the motor, as opposed to the one that just controlled the motor. So I thought that is what was happening here because the first arduino had that extra data to process.

HazardsMind:
the one that was sending the data to the screen was a little slower to change the speed of the motor, as opposed to the one that just controlled the motor.

Of course - the Serial port is glacially slow in Arduino terms, and if you sketch stops and waits for messages to dribble out over the serial port then it will run much slower. This has nothing to do with what is going on inside an R/C car unless, for some strange reason, you decided to run a serial cable out to it and have your controller stop and print stuff over the serial port.