Introduction
Traditionally, RC plane/heli/quadcopter pilots use a dual thumb stick type transmitter, however and especially quadcopter pilots, it's not a very intuitive way to fly especially for the beginner.
We've all seen the movies and at least have a vague idea that a pilot of a real aircraft/spaceship uses a joystick. So why not with RC aircraft.
The basic idea of my project was to implement a joystick solution in the easiest way possible, but one that fulfills my exact requirements.
Background
Arduino is an open-source electronics prototyping platform based on flexible, easy-to-use hardware and software.
For this project, I chose the Arduino NANO for its small size and I/O requirements. It interfaces with the joystick potentiometers (0-5vdc), switches and Serial LCD. A serial data stream (PPM) is then output and interfaced to an off-the-shelf 430Mhz transmitter module/pcb.
Photos
External - This is a hybrid of two different PC gaming joysticks, the base from one and the hand grip from another. The hand grip is a 3D type, i.e. provides rudder control by twisting the grip as well as the usual aeleron/elevator controls (forward/back/left/right).
Internal - Home projects aren't supposed to be neat & tidy (leave that to the day job!).
Here you'll see the Arduino NANO pcb, 430Mhz Tx pcb, Lipo battery in the base, and the LCD module & switches in the lid.
PPM Data stream - The whole project revolves around providing a PPM stream compatible with the input on the 430Mhz Tx board. It's an industry standard (more or less!) amongst traditional RC Transmitter manufacturers who use this interface to hook up 2 transmitters together in a Master/Buddy scenario for new pilots. Ref. Futaba.
The manufacturer of the 430MHz transmitter pcb designed his board to fit in with this scenario, so it was relatively well understood what I needed to send to the transmitter in order for the servo's etc on the aircraft to react accordingly.
PPM is a type of PWM. In the photo below, there are 6 channels which make up the PPM stream and which is repeated every 22ms. So, starting with the long sync pulse, you'll then get a fixed LO pulse of 300uS followed by the channel pulse HI (range 700 to 1700 uS), then the next fixed LO pulse again and so on.
P.S. Ignore the red trace, it was only used to sync my scope.
Video
Using the Code
There are two parts to the code, the main PDE and one include (LCD routines):
- RCJoystick_IanJ_V2.pde
- LCD0821.pde
Most of the VAR setup is done at the top of the main code, however, there is additional setup required for joystick rates selection near the bottom.
Main routine = RCJoystick_IanJ_V2.pde.
ISR which calls the ppmoutput
routine at the required 22mS intervals, also updates the ticks used for timing elsewhere.
ISR(TIMER1_COMPA_vect) {
ppmoutput(); tick = tick + 1; tick2 = tick2 + 1; }
Timer setup. This is the heart of the whole thing, it defines a 22ms timer which creates the recurring PPM stream. Some RC Transmitters are very sensitive to the 22ms timing so using an accurate timer as opposed to on the fly timing is crucial.
TCCR1A = B00110001; TCCR1B = B00010010; TCCR1C = B00000000; TIMSK1 = B00000010; TIFR1 = B00000010; OCR1A = 22000; OCR1B = 1000;
}
Part of the main loop, this is where the ticks are used to generate the LCD refresh and timing for the calling of the panel switches and battery monitor subroutines.
if (tick >= 11) { tick = 0;
switchesRates(); batterymonitor(); }
if (tick2 <= 50) {
slowflag = 0;
}
if (tick2 >= 50 && tick2 <=100) {
slowflag = 1;
}
if (tick2 >= 100) {
slowflag = 0;
tick2 = 0;
}
}
After reading in the raw values from the joystock potentiometers, they are now scaled using Y=MX+C to values suitable for direct output on the PPM stream.
Using MAP, it's easy to reverse the direction in cases where the physical potentiometer is reversed. There is no elaborate/easy calibration routine, it all has to be hard coded in, compiled and uploaded to the Arduino every time a change is made.
AI_TIpot = map(AI_Raw_TIpot, 0, 1023, 1023, 0) + 0; AI_Throt = map(AI_Raw_Throt, 0, 1023, -25, 1150) - 0; AI_Rudde = map(AI_Raw_Rudde, 0, 1023, 0, 1023) + 0; AI_Eleva = map(AI_Raw_Eleva, 0, 1023, 0, 1023) + 0; AI_Aeler = map(AI_Raw_Aeler, 0, 1023, 0, 1023) + 0; AI_Batte = AI_Raw_Batte / 36.2;
Add in offsets and additionally scaling. This is the "rates" select which gives the joystick mild to coarse functionality.
Aeleron_uS = (AI_Aeler * DualrateMultAel) + pulseMin + DualrateAdjAel;
Elevator_uS = (AI_Eleva * DualrateMultEle) + pulseMin + DualrateAdjEle;
Throttle_uS = (AI_Throt * DualrateMultThr) + pulseMin + DualrateAdjThr;
Rudder_uS = (AI_Rudde * DualrateMultRud) + pulseMin + DualrateAdjRud;
TI_uS = (AI_TIpot * DualrateMultTI) + pulseMin + DualrateAdjTI;
}
This is part of the PPM output sub. Simply build up the output stream sequentially. Of course, there are much more elaborate ways of coding such a data stream, such as using arrays, however as per my brief, the idea was to get this up & running in a short period of time.
void ppmoutput() {
digitalWrite(outPinPPM, LOW);
delayMicroseconds(Fixed_uS); digitalWrite(outPinPPM, HIGH);
delayMicroseconds(Aeleron_uS);
digitalWrite(outPinPPM, LOW);
delayMicroseconds(Fixed_uS); digitalWrite(outPinPPM, HIGH);
delayMicroseconds(Elevator_uS);
digitalWrite(outPinPPM, LOW);
delayMicroseconds(Fixed_uS); digitalWrite(outPinPPM, HIGH);
delayMicroseconds(Throttle_uS);
digitalWrite(outPinPPM, LOW);
delayMicroseconds(Fixed_uS); digitalWrite(outPinPPM, HIGH);
delayMicroseconds(Rudder_uS);
digitalWrite(outPinPPM, LOW);
delayMicroseconds(Fixed_uS); digitalWrite(outPinPPM, HIGH);
delayMicroseconds(TIsw_uS);
digitalWrite(outPinPPM, LOW);
delayMicroseconds(Fixed_uS); digitalWrite(outPinPPM, HIGH);
delayMicroseconds(TI_uS);
The final part of building the PPM stream is to generate the synchro pulse which makes up the time from the last of the PPM channels (which will vary in timing) and to the start of the 1st one again next time round.
So, start the pulse but don't finish it yet because this is where the housekeeping can now be done such as update the LCD, read the panel switches, etc.
digitalWrite(outPinPPM, LOW);
delayMicroseconds(Fixed_uS); digitalWrite(outPinPPM, HIGH); }
Points of Interest
Flying with the joystick is so much easier for us RC newbies, and to add an extra dimension, just try it in the dark at night with some high power LEDs fitted. Great fun!
History