Scratch Built Remote Control

by nickatredbox | December 12, 2012 | (6) Posted in Projects

For those of you who are into electronics, here is my take on an open source Arduino Xbee remote controls system with telemetery Very simple software an the very popular Arduino platform, lots of support from a wide user goup


This is the airframe that flew in the video above. Its a bit too heavy.


The receiver board in Yellow Plane


The whole board and micro weighs in at 22 grams. The supply will be a 3C Lipolly. My Xbee needs around 220mA and the Arduino draws 100 mA driving all 6 servo PWM signals, and not the motors. All up say 750mA in a 750mAh 3C


Its probably not suitable for a small plane due to its weight but is does allow for expansion and data acquisition and live telemetery. There are eight 10 bit analogue inputs and six PWM servo drive outputs.


The test pilot Richard  who flew Yellow Plane thought the control was good on its maiden flight, despite ending in a crash :) which I latter determined was caused an incorrectly configured charger causing the battery to not be fully charged. It is always easy to overlook the simple things in engineering, as my school reports often said must try harder.

Here is my calculation spread sheet for Yellow Plane if anyone sees anything I'm doing wrong in this spread sheet please let me know

This was the setup prior to the maiden flight. I'm more into the tech than flying but am improving so, so deferred the maiden flight to an experienced pilot content to film her instead.


Inside the transmitter box with the Arduino Uno gathering the control positions and sending them via the Xbee to the plane.


The receiver Circuit Diagram

This layout allows me to add components between the Xbee and the Arduino, so If i came up with an autopilot it could catch the data from the TX modify /override it and forward the data to the RX Arduino


This is the Arduino Nano I used in the RX supplied by Hobby King, they are always out of stock now it seems as they are only US$9.95 

 

There is a smaller lighter version without the USB which I will experiment with in due course.


The Software

Here is the current RX code and the TX Code for anyone who is interested, you can figure out the wiring from the code as I really only did a pencil drawing while I was soldering.

Two way comms @ 38,400 baud seems reliable, other folks on the www seem to agree with this. The Xbee's are in the out the box wire replacement mode all set to defaults except baud is set to 38,400 with the X-CTU Utility

The rest is in a pretty simple bit of code shown below.  The electrical circuit can be determined from the code, I will draw it up as its only on a pad soon.

Two Xbee Pro S1 60mW alleged 1500 meter range out doors
TX Arduino Uno
RX Arduino Nano 328

I am a believer in the simplest code/solution to do the job so here is my wee bit of code for. I pass back telemetery every 5 frames from the TX with 20ms frames.

Its nice smooth and responsive

I sample the analogue inputs on power up (wInd off zero or WOZ in the code) so the joysticks can be nulled.

The loop samples all channels MAX_SAMPLE times then make an average.  This method seemed to work better than a rolling average which introduces noticeable lag on the servos. Next the 10 bit values from the analogue controls are scaled to 1790 bananas, this is degrees X 10. The pot values are sampled at 200 micro second-ish intervals and a 10 point average is acquired in around 8mS. 

A couple of other sites seem to concur that a 10mS dead air period with an Xbee is preferable, I have seen this first hand, the TX and RX are nicely in sync with the current regime, servos are nice and quiet and don't jitter when static.  Any jitter I do see seems to be related to analogue noise there are 0.5uF caps between ground and the wiper of the pots, also the pot cans are grounded and the signal cables are screened for the short distance between the transducer and the Arduino.

A packet is constructed including the TX Xbee's rssi and the state of the digital switched on the box. The packet is sent over the air with a simple checksum. There is an LED that flashes faster as the RSSI time increases, should give me warning if going out of range 

  if(rssi == 0)
      digitalWrite(4,HIGH);
    if( (tick % (rssi / 10) ) == 0)
    {
       if(digitalRead(4) == HIGH)
          digitalWrite(4,LOW);
        else
          digitalWrite(4,HIGH);
    } 

At the end of loop I wait for the n microseconds based on how long it took the get to the end of the loop, giving a 20mS frame rate, the typical wait is around 8 mS at the end of the processing section, which gives some free air for the Xbee, and seems to give better responsiveness than transmitting back to back as fast as possible.  

The TX packet is quite small at max of around 70 bytes so it fits in a single Xbee frame which is helpful and preferable.

1) Keep the packet size to < 100 bytes in either direction
2) Allow for 50% dead air on 20mS at 38,400 my TX packet is sent in 8mS so 12ms dead air, this value is sent in the TX packet header in a single byte
3) Only send back a telemetry packet every other TX cycle minimum or you will crowd the airwaves, this causes drop oust and variable latency. I have chose (packcount %5) which is a good margin, and a packet size limit that keeps the transmission clear of the next TX update by 5mS minimum.
4) Not really Xbee but have another CPU handle the telemetry packets not the TX micro. I am re-using my Netduino Plus as it has Ethernet & TCP/IP and a flash card socket just because i have one.
5) Have your own checksum, the 802.15.4 will handle things in the air but you still have a wee bit of wire between you micro and the Xbee. Belt an braces is the best approach here.
6) The Xbee should be treated as a simplex regardless device as I do it gives you better real-time behaviour overall 
7) Learn about the Xbee config and configure sensibly, to many items to list here, however is set the 802.15.4 retry limit to 3 and the packetiztsion threshold to 3 also

My timing chart

The next flight will use gyro stability utilizing and old KK board and the programmable RX.  This is Yellow plane 1.5 which is now replaced with YP 2.0 an altogether prettier lighter twin boom inverted V tail design.


In most systems the TX does all the clever stuff like mixing and variable rates. In this design the RX can do it which means if you have a stability algorithm it can be used by an auto pilot or any other mad scheme you can dram up.


Here is the gyro working on Yellow Plane 2 during the build


Again very simple code.

This routine calculates the servo positions and adds the gyro compensation

void UpdateServos()
{
      
    //Digital inputs TX code helper
    //TxVal[8] |= (digitalRead(5) << 0);//joy 2 push
    //TxVal[8] |= (digitalRead(6) << 1);//pb
    //TxVal[8] |= (digitalRead(7) << 2);//slide
    //TxVal[8] |= (digitalRead(8) << 3);//toggle
     
    //Throttle TxVal[1] 
    //Rotary pot TxVal[2]  
    //Joy 1 X TxVal[3] 
    //Joy 1 Y TxVal[4] 
    //Joy 2 X TxVal[5] 
    //Joy 2 Y TxVal[6] 
    //rssi TxVal[7]
    //digital TxVal[8]
    //micros() TxVal[9]
        
    //Use the pot as the gain for all channels for now    
    float GainPot = (float)(TxVal[2]) * 0.001f;    
      
    //Get the target values from the TX    
    int PitchTarg =  (TxVal[3] / 10);
    int RollTarg =  (TxVal[4] / 10);
    int YawTarg =  (TxVal[6] / 10);
    
    
    //Prime the Target WOZ values  
    if(PitchTargWOZ == 9999)
      PitchTargWOZ = PitchTarg;
      
    if(RollTargWOZ == 9999)
      RollTargWOZ = RollTarg;
      
    if(YawTargWOZ == 9999)
      YawTargWOZ = YawTarg;
      
    
    //Get the Centered target values   
    float PitchTargCentred = (float)(PitchTarg - PitchTargWOZ);
    float RollTargCentred =  (float)(RollTarg - RollTargWOZ);
    float YawTargCentred =  (float)(YawTarg - YawTargWOZ);
      
    //Calculate gains
    float PitchGain = GainPot * 1.0f;
    float RollGain = GainPot * 1.0f;
    float YawGain = GainPot * 1.0f;
    
    //Get Gyro values
    float PitchGyro = (float)(AnIn[2] - AnInWOZ[2]);
    float RollGyro = (float)(AnIn[1] - AnInWOZ[1]);
    float YawGyro = (float)(AnIn[0] - AnInWOZ[0]);
    
    //Calc P error
    float PitchError = (float)PitchTargCentred + PitchGyro;
    float RollError = (float)RollTargCentred + RollGyro;
    float YawError = (float)YawTargCentred + YawGyro;
        
    //Apply gains
    int PitchTrim = (int)(PitchError * PitchGain);    
    int RollTrim = (int)(RollError * RollGain);    
    int YawTrim = (int)(YawError * YawGain);
    
    //Constaring trim authority
    PitchTrim = constrain(PitchTrim, -30, 30);
    RollTrim = constrain(RollTrim, -30, 30);
    YawTrim = constrain(YawTrim, -30, 30);
    
    //Dump the trim value
    if((TxVal[9] & 0x4) == 0)
    {        
      PitchTrim = 0;
      RollTrim = 0;
      YawTrim = 0;      
    }
    
    
    
    //Calc flap anglke
    int Flaps = 0;
    
    //Apply flaps
    if((TxVal[9] & 0x8) == 0)
        Flaps = -25;
        
        
  
    //Throttle  
    val = TxVal[1] / 10;    
    val = map(val, 1, 179, 30, 179);
    val = constrain(val, 1, 165);         // scale it to use it with the servo (value between 0 and 180) 
    servo[0].write(val);                  // sets the servo position according to the scaled value 
       
       
    //Vee tail
    
    //Left Elevator Joy 1 Y TxVal[4]
    val = (YawTarg + YawTrim) + (PitchTargCentred + PitchTrim);   
    val = constrain(val, 15, 165); 
    val = map(val, 0, 179, 135, 45);       // scale it to use it with the servo (value between 0 and 180) 
    servo[1].write(val);                  // sets the servo position according to the scaled value 
    
    
    //Right Elevator Joy 1 Y TxVal[4]
    val =  (YawTarg + YawTrim) - (PitchTargCentred + PitchTrim);   
    val = constrain(val, 15, 165); 
    val = map(val, 0, 179, 135, 45);       // scale it to use it with the servo (value between 0 and 180) 
    servo[2].write(val);                  // sets the servo position according to the scaled value 
    
    
    
    //Left Flaperon           
    val = 90 + (RollTargCentred + Flaps) + RollTrim;  
    val = constrain(val, 15, 165); 
    val = map(val, 0, 179, 165, 15);      // scale it to use it with the servo (value between 0 and 180) 
    servo[3].write(val);                  // sets the servo position according to the scaled value 
    
    //Right Flaperon 
    val = 90 + (RollTargCentred - Flaps) + RollTrim;  
    val = constrain(val, 15, 165); 
    val = map(val, 0, 179, 165, 15);      // scale it to use it with the servo (value between 0 and 180) 
    servo[4].write(val);                  // sets the servo position according to the scaled value 
    
    
    //Joy 2 x nose Wheel
    val = (TxVal[6] / 10);  
    val = map(val, 0, 179, 55, 125); 
    servo[5].write(val);                 // sets the servo position according to the scaled value 
      
}

Hope you folks out there find this project interesting, as you can imagine I was thrilled (if that's not too nerdy) to see her fly at all crash of no crash 





COMMENTS

tramsgar on December 13, 2012
Very interesting, lots of material! I'll set some time aside to read it thoroughly - thanks!
Log In to reply
nickatredbox on December 13, 2012
Glad you found it interesting hope to fly Yellow Plane 2 this weekend with the stability code. What I think I need to look at is how to utilize Arduino with a normal RX i.e. pickup the PWM from a standard RX like a KK or i86 board does.
Log In to reply
tramsgar on December 14, 2012
I'd look here first:
http://code.google.com/p/nextcopterplus/source/browse/#svn%2Ftrunk%2FOpenAero2%2Fsrc
Log In to reply
nickatredbox on December 14, 2012
Interesting stuff thanks
Log In to reply
earthsciteach on December 13, 2012
I'd really like to call you out on putting way too much effort into something that can be bought for relatively little money, but I'm a big fan of those giant, pumpkin hurling devices which exist for no justifiable reason. Therefore, I say to you, well done, sir! This is VERY cool stuff!
Log In to reply
nickatredbox on December 13, 2012
LOL you are quite correct sir :) I have spent way more than its worth. I have a $25 6 channel system from HK for my knockabout models works fine.

This was more of a learning engineering excise, my background is in data acquisition so I like to keep some skills in that area. Raspberry Pi, Arduino & Xbee are all over the web and so well priced. I want to build a device like an i86 or KK board that can utilize low cost radio gear and still facilitate some fancy control systems, just to see what happens.

I love the x19 Hummingbird VTOL quad/plane on this site works so well that guy is a great designer I think, maybe for my next plane ?
Log In to reply
Jake Wells on December 18, 2012
Very cool!!!! Thanks for sharing that is a grip of really impressive work!
Log In to reply
nickatredbox on December 20, 2012
Thanks Jake,

Its been a bit busy here with Christmas, and a berivement hoping to fly the new V tail variant over the break
Log In to reply

You need to log-in to comment on articles.


Scratch Built Remote Control