Reading a RC Airplane Receiver

Hi All!
I have been working on an autopilot using the arduino since the start of the summer. In order for this project to work correctly, it is essential that I can switch between radio control and arduino (autopilot) control. In a earlier attempt, I used the arduino to throw some 5v relays so the servos signal wire would switch between the radio receiver and the arduino. In an effort to reduce power consumption/simplify the circuit, I am trying to read the receiver directly, and then the arduino gets to choose if it passes the signal through to the servos or if it should be on autopilot.
So when I upload my code to my arduino, the code works perfectly, it as if the receiver was controlling the servos by itself. However, when I switch to battery power, there is no response, even though the arduino is powered. It as if the i need to upload code every time for the arduino to work correctly.

my code comes from the website rcarduino. i stole most of mine from RCArduino: How To Read Multiple RC Channels

I do not have a picture of my circuit right now but I can easily describe it: the servos still receive power from the receiver. The signal wire from the receiver is sent to an arduino input. Another arduino output is then connected to the servo signal input. I have tried powering both the arduino and receiver seperately, and together (ideally i would like to have one battery at the end). So I have 11.1V lipo battery going to the arduino's DC jack, and then the 5v out from the arduino goes to the receiver.
sorry, maybe i can post a picture later

any suggestions would be appreciated!!

here is a copy of the code i am running:

#include <Servo.h>
#include <PinChangeInt.h>

//Declare Receiver Inputs to read channel signals
#define ESC_IN 8
#define ELEVATOR_IN 13
#define AILERON_IN 11
//Declare bit-flags to indicate if which channels have new signals
#define ESC_FLAG 1
#define ELEVATOR_FLAG 2
#define AILERON_FLAG 4
//Holds update flags defined above
volatile uint8_t bUpdateFlagsShared;
//Shared variables are updated by the ISR
volatile uint16_t ESC_IN_SHARED, ELEVATOR_IN_SHARED, AILERON_IN_SHARED;
//These are used to to to record rising edge of a pulse
uint32_t ESC_START, ELEVATOR_START, AILERON_START;
//Declare servos
Servo esc, elevator, aileron;

void setup(){
Serial.begin(9600);
//Attach Servos
esc.attach(7);
elevator.attach(12);
aileron.attach(10);
//Attaches interrupts using PinChangeInt library
PCintPort::attachInterrupt(ESC_IN, escRead, CHANGE);
PCintPort::attachInterrupt(ELEVATOR_IN, elevatorRead, CHANGE);
PCintPort::attachInterrupt(AILERON_IN, aileronRead, CHANGE);
}
//void loop(){}

void loop(){
//local variables to hold channel inputs
static uint16_t ESC, AILERON, ELEVATOR;
//local updated flags
static uint8_t bUpdateFlags;
//check shared flags to see if we have a new signal
if(bUpdateFlagsShared){
noInterrupts();
bUpdateFlags = bUpdateFlagsShared;
if(bUpdateFlags & ESC_FLAG){
ESC = ESC_IN_SHARED;
}
if(bUpdateFlags & ELEVATOR_FLAG){
ELEVATOR = ELEVATOR_IN_SHARED;
}
if(bUpdateFlags & AILERON_FLAG){
AILERON = AILERON_IN_SHARED;
}

bUpdateFlagsShared = 0;
interrupts();

}
if(bUpdateFlags & ESC_FLAG){
esc.writeMicroseconds(ESC);
}
if(bUpdateFlags & ELEVATOR_FLAG){
elevator.writeMicroseconds(ELEVATOR);
}
if(bUpdateFlags & AILERON_FLAG){
aileron.writeMicroseconds(AILERON);
}
bUpdateFlags = 0;
}

void escRead(){
if(digitalRead(ESC_IN) == HIGH){
ESC_START = micros();
}else{
ESC_IN_SHARED = (uint16_t) (micros() - ESC_START);
bUpdateFlagsShared |= ESC_FLAG;
}
}

void elevatorRead(){
if(digitalRead(ELEVATOR_IN) == HIGH){
ELEVATOR_START = micros();
}else{
ELEVATOR_IN_SHARED = (uint16_t) (micros() - ELEVATOR_START);
bUpdateFlagsShared |= ELEVATOR_FLAG;
}
}

void aileronRead(){
if(digitalRead(AILERON_IN) == HIGH){
AILERON_START = micros();
}else{
AILERON_IN_SHARED = (uint16_t) (micros() - AILERON_START);
bUpdateFlagsShared |= AILERON_FLAG;
}
}

I suspect the 5V from Arduino is not delivering enough current for the receiver, I am not certain what the current limit is, but I think it's a mximum of 100mA. I would suggest you power the RC receiver via a 1Amp 7805/7806 voltage regulator taking it's input from the 11.1V LiPo battery, so you still have one battery only.

Cheers,
John

Absolutely - if you are trying to power the servos directly from the 5V output from the Arduino it will simply crash, don't power motors or servos from the logic 5V rail ever - you can damage the logic chips as motors can put out horrible voltage spikes (inductive load).

Normally you would power receiver and servos from a 6V or 7.2V battery pack - some receivers output a clean 5V (BEC), some don't, I believe. Try powering Arduino separately for now - it needs clean power.

Sorry for hijacking your thread.
Im also about to make a rc plane controlled by the arduino processor. But at the moment Ill just go for stabilization and then upgrade with a barometer and such.

However, to the point where Im hijacking ur thread! Does your interrupt work good? I mean, when I try to go for interrupt with reciever it seems like its interrupting every second..
You know ur #include <PinChangeInt.h>, is it highlighted with orange colour? mine is grey but if you compare with others they have orange color..

It should be interrupting at least 50 times per second for each channel.

Try the single channel version first, its all on the blog along with explanations of what you should expect.

Duane B

rcarduino.blogspot.com

You know ur
Code:

#include <PinChangeInt.h>

, is it highlighted with orange colour? mine is grey but if you compare with others they have orange color..

This may mean that you have not installed the pin change int library, see here for details -

Duane B

rcarduino.blogspot.com

John & Mark,
That was exactly the problem. I am now powering the arduino from the brushless motor esc. I previously had to power the receiver through the arduino so that the pulseIn function would work correctly. Now that I am using interrupts it seems that it is fine to power it through the esc.

Freak-
As I said I basically store this code directly from rcarduino. However, it is working much MUCH better than using the pulseIn () function. You may want to set up your interrupts differently depending on how you are trying to schedule your board.
First you should run the code off of rcarduino. What are specifically do you need you arduino to do in your aiplane?
I would highly recommend joining the site www.diydrones.com, you can find me there with the same username.
Please keep posting so we can help you out
I believe that DuaneB may be right, have you installed the library?

Hello again,

Ive now come a bit further with the project.

  • Values from sensors (GYRO AND ACC.)
  • Values from receiver (using a libraries called PPMrcIn and Statistic)
  • The sensor will only do its work when the transmitter is not sending any new values/directions
  • So, if you are flying the plane manually and then put your transmitter away. The plane will stabilize itself (roll and pitch)

I will post a bit of the code, showing my algorithms. And here I need you guys to help me improve my code.

One thing I know that is not working well, is the automatic stabilizer.
Lets say:

  • Plane is straightforward = roll and pitch has a value of 180 degrees.
  • Plane is leaning = roll has a value of 160 (which means leaning 20 degrees to the left, I believe)
    --> Divide value by 2 and insert it in servo.write() function.

The problem is that the indication of the servos gets to small.
Should I make it into several intervals instead, or will it take to much memory?

Servo YAW_SERVO;
Servo PITCH_SERVO;
Servo ROLL_SERVO;

int flag_roll = 0;
int flag_pitch = 0;
int flag_yaw = 0;

//////////////////////////////////////////////
//////     INITIATION OF  SERVOS       ///////
//////////////////////////////////////////////
void init_servo_motor(){
  YAW_SERVO.attach(SERVO_YAW_PIN);
  PITCH_SERVO.attach(SERVO_PITCH_PIN);
  ROLL_SERVO.attach(SERVO_ROLL_PIN);
}


//////////////////////////////////////////////
//////     READING THE RECEIVER AND      /////
//////  MANIPULATING THE SERVOS THROUGH IT ///       
//////////////////////////////////////////////
void read_receiver(){
    YAW.readSignal();
    PITCH.readSignal();
    ROLL.readSignal();
//  THROTTLE.readSignal();

   
          //////////////////////////////////////////////
          ///    CONTROL SERVO THROUGH RECEIVER     ////
          //////////////////////////////////////////////
      if(ROLL.getVersus() != 0){
      ROLL_SERVO.write(map((ROLL.getSignal()), 1000,2000,0,179));
      flag_roll = 0;
      Serial.println("RECEIVER ROLL");
    } 
    if(PITCH.getVersus() != 0){
      PITCH_SERVO.write(map((PITCH.getSignal()), 1000,2000,0,179));
      flag_pitch = 0;
      Serial.println("RECEIVER PITCH");
    }
    if(YAW.getVersus() != 0){
      YAW_SERVO.write(map((YAW.getSignal()), 1000,2000,0,179));
      flag_yaw = 0;
      Serial.println("RECEIVER YAW");
    }

    //CHECKING IF ALL OF THE LEVERS OF THE RECEIVER ARE OFF   
    if(ROLL.getVersus() == 0 && PITCH.getVersus() == 0 && YAW.getVersus() == 0){
      control_servo_sensor();
    }

 
}



void control_servo_sensor(){
  
    //////////////////////////////////
    ///    CONTROLLING THE SERVOS ////
    ///          WITH SENSOR      ////
    //////////////////////////////////
    
  get_sensor_values();
    
  // THIS IS FOR THE ROLL SERVO  //
    if(kalAngleX < 177 || kalAngleX > 182){ 
      
      Serial.println("SENSOR ROLL ");
      Serial.print(kalAngleX);
      
      int value_roll = kalAngleX/2;    
      ROLL_SERVO.write(value_roll);
      
      flag_roll = 0;
  }
  
    // THIS IS FOR THE PITCH SERVO // 
  if(kalAngleY < 182 || kalAngleY > 185){   
    
    Serial.println("SENSOR PITCH ");
    Serial.print(kalAngleY);
    
    int value_pitch = kalAngleY/2;
    PITCH_SERVO.write(value_pitch);
    
    flag_pitch = 0;
  }
    
       ///////////////////////////////////////////////////////////////////////  
       // RESET TO ZERO POSITION JUST FOR SAFETY (ROLL, PITCH, YAW SERVOS) // 
       ///////////////////////////////////////////////////////////////////////
       
     if(flag_roll == 0 && ROLL.getVersus() == 0 && kalAngleX > 177 && kalAngleX < 182){  
       Serial.println("RESETING ROLL");
       
       ROLL_SERVO.write(90);
       flag_roll = 1;
   }

   if(flag_pitch == 0 && PITCH.getVersus() == 0 && kalAngleY > 182 && kalAngleY < 185){
     Serial.println("RESETING PTCH");
     PITCH_SERVO.write(90);
     flag_pitch = 1;
   }
   
     if(flag_yaw == 0 && YAW.getVersus() == 0){
     Serial.println("RESETING PTCH");
     YAW_SERVO.write(90);
     flag_yaw = 1;
   }
  

}

PS. Yes, I have the pinchangeint library installed. But lets forget about that since Ive found a quite good library now that satisfies me.
Cheers!

Bump!

I'm still a bit of an Arduino n00b, but I have been flying RC planes and helis for years. I have considered an autopilot/drone project myself, the Arduino is ideal for this purpose.

The main advice I can offer on your approach is to program the autopilot a little more like you fly the plane. Halving the roll error as you're doing, for instance, means your plane theoretically will never actually level itself, as it just keeps halving the error without actually eliminating it. In The Real World (tm) of course, chances are the plane will get bounced past neutral as you get the error smaller and smaller. But the concept still holds, it seems like this would yield sluggish recovery response, possibly not enough to overcome the airplane's actual upset. This depends largely on the geometry and control throws of your plane, though...if you are not getting the recovery you want, why not just increase your rate of correct? Go 1:1 on the roll angle vs servo position, rather than 2:1.

But back to my point on making the autopilot fly more like you: when you want to change bank angle, it really doesn't matter if the desired change is 20* or 120*, you are probably going to throw in a similar amount of aileron to get the response you want. And you won't gradually reduce aileron deflection with bank angle until you are very close to the bank angle you want....actually for a lot of flying types, you may leave the aileron angle at a pretty extreme deflection, and quickly release it when you reach your desired angle (nice how our RC models flit around and respond like they're on rails most of the time). Perhaps you should decide on a narrow range of aileron deflection (say between 50% and 75%) and adjust your aileron correction within that range based on the amount of roll angle error. In that example, the smallest aileron deflection you'd see would be 50%, until you are within some very close range of your desired bank angle, at which point you'd release the ailerons (depending on your model you would want some dampening code, to prevent "pilot"-induced oscillations...this could be something like what you're doing now, but with a little more sophistication).

Quick question: is your gyro (and other sensors) giving you absolute values, or relative values (ergo, relative to the previous cycle)?

Hope this gives you some ideas...I'm looking forward to exploring this area myself once I get into my own autopilot system!

Matt

Have you looked at the Ardupilot project?

The Ardupilot Mega already does this. It uses a separate ATmega328 (i.e. and arduino processor) to decode RC receiver outputs.

The design team decided to use a separate processor for this decoding. IIRC the reason was to increase accuracy and offload computation to make more resources available for the rest of the flight code.

-j

"The signal wire from the receiver is sent to an arduino input. Another arduino output is then connected to the servo signal input. "

An important part of this is digital logic levels.

Many of the receivers out there are 3.3 V logic, so their 'HIGH' isn't a guaranteed high for the 5V arduinos who differentiate at 3 volts. This will lead to many false signal interpretations (you can see if you rig your code to print an alert if the PPM signal length is outside the range of the normal ~1000-2000 uS of servos).

A simple solution is to use a 3.3V uC, but you can also use an analog comparator circuit to condition the receiver's output into a more recognizable 0-5V

  • just be careful that your opAmp or comparator can go close to your rails, or provide the comparator/opAmp that much more voltage so that its output is the desired 5V.