Pages: [1] 2 3 ... 6   Go Down
Author Topic: Arduino-Controlled RC Transmitter  (Read 27681 times)
0 Members and 1 Guest are viewing this topic.
New Jersey
Offline Offline
Full Member
***
Karma: 0
Posts: 193
Ard at work
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I whipped up a little interface yesterday, to connect an Arduino to a Radio Control Transmitter (a Multiplex Royal Evo 12, in my case). This can be used to send commands from the Arduino to the transmitter, which will mix them with the manual input and radio them to the radio-controlled model.

Channels 1, 2, 4 and 5 are moving, sticks centered.

The first application I want this for is to pan a video camera with two servos on the model, with input from head motion tracking on the Arduino, i.e. see from the model's cockpit. (Yes, I know, it's been done... but not by me and not on Arduino AFAIK.) I've got the video goggles, camera and video transmitter/receiver from the Spy Gear RC car Jack of All Trades has made famous here: http://jakeofalltrades.wordpress.com/2007/09/30/25-head-mounted-display/.

It could also be used as a PC-to-RC interface, using Arduino as a USB-to-RC bridge. Any kind of processing could be done on land, and the resulting commands sent to the model. Ultimately, you could close the loop with a data transmitter on board, but that is beyond the scope of this little hack...

First off, had to make a trainer cable for the RC transmitter, which would normally be used to connect a student's radio to a teacher's, in order to share control of the model. For Multiplex Royal Evos, the best info I found was here: http://www.designsoft.com.au/ahome/rc/EVOtraining/, but you should be able to google for whatever works for your radio.


Need to buffer the 5V signal from the Arduino to the 7.2V of the radio, with a transistor.

Next, made a little connector from some headers, perfboard and shrinkwrap for the Arduino end of the cable.

Quick and dirty connector at the Arduino end. Center pin not connected.

Last, needed to write some code to generate the PPM frames, which consist of a series of pulses, one per channel, rounded out by a synchronization pulse to a 20ms frame. The pulses are very similar to servo control pulses, which are a well treated subject in Arduino lore. I started out with todbot's Serial_Servo_Better (http://todbot.com/blog/spookyarduino/) and added the frame wrapper around the pulses, some dirty code to sweep values and some cryptic code comments.

The basic idea is that the Arduino is busy doing precise timing with delayMicroseconds() while the pulses are being sent, but you can do whatever you want for the remainder of the 20ms frame, i.e. the synchronization pulse. If you need more than that to get stuff done (lots of processing, or lots of channels leaving a small synch pulse), I guess this would need to be written with interrupts to make it more 'fire and forget'. Never done interrupts, so will try that later.

I've run out of characters, so I will post the code in a reply to this post.
« Last Edit: November 01, 2007, 09:33:41 am by Syvwlch » Logged

----------
Mathieu

New Jersey
Offline Offline
Full Member
***
Karma: 0
Posts: 193
Ard at work
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

As promised, here it is, in all its quick and dirty glory!

Code:
/*
 * Trainer PPM Interface
 * -------------------
 *
 *
 * Created 31 October 2007
 * copyleft 2007 Mathieu Glachant
 * mathieu@ban-sidhe.com
 * http://ban-sidhe.com/
 *
 * adapted from todbot's Serial-Servo-Better
 */

int servoPin = 12;           // Control pin for trainer interface

                             // A pulse starts with a low signal of fixed width (0.3ms),
                             // followed by a high signal for the remainder of the pulse.
                             // Total pulse width is proportional to servo position (1 to 2ms)
int pulseStart = 300;        // pulse start width in microseconds
int pulseMin = 724;          // pulse minimum width minus start in microseconds
int pulseMax = 2048;         // pulse maximum width in microseconds
int conversionFactor = 5.7;   // (pulseMax - pulseMin - pulseStart)/180

                             // A frame is a succession of pulses, in order of channels,
                             // followed by a synchronisation pulse to fill out the frame.
                             // A frame's total length is fixed (20ms)
int frameLength = 20;        // The duration in millisecs of a frame

long lastFrame = 0;          // The time in millisecs of the last frame
int channelNumber = 2;       // Number of channels to send (keep below frameLength/pulseMax)
int servo[2];                // Values to set for the servos in degrees
int channel[2];              // Values to send on channels (duration of pulse minus start, in microseconds)
int i;                       // Counter in for loop
int j = 0;                   // Counter for servo updates

void setup() {
  pinMode(servoPin, OUTPUT);  // Set servo pin as an output pin
  Serial.begin(9600);         // connect to the serial port
  for ( i = 0; i < channelNumber; i = i + 1 ) {servo[i] = 0;}
  for ( i = 0; i < channelNumber; i = i + 1 ) {channel[i] = pulseMin;}
  Serial.println("Trainer_PPM_Interface ready");
}

void loop() {
  
     // Save the time of frame start
  lastFrame = millis();

    // This for loop generates the pulse train, one per channel  
  for ( i = 0; i < channelNumber; i = i + 1 ) {
    digitalWrite(servoPin, LOW);   // Initiate pulse start
    delayMicroseconds(pulseStart); // Duration of pulse start
    digitalWrite(servoPin, HIGH);  // Stop pulse start
    delayMicroseconds(channel[i]); // Finish off pulse
  }
    digitalWrite(servoPin, LOW);   // Initiate synchronisation pulse
    delayMicroseconds(pulseStart); // Duration of start of synchronisation pulse
    digitalWrite(servoPin, HIGH);  // Stop synchronisation pulse start

    // We're done generating pulses and using delayMicroseconds()
    // Time to do some other processing before the next frame
    // How much time depends on how many channels you are running

    // Let's change the servo positions
    j=j+1;
    if (j==4) {j=0;}
    
    if  (j==0) {
      for ( i = 0; i < channelNumber; i = i + 1 ) {
        servo[i] = servo[i]+1;
        if (servo[i] >= 360) {servo[i]=0;}
      }
    }
        
    // Calculate pulse durations from servo positions                                  
  for ( i = 0; i < channelNumber; i = i + 1 ) {
    channel[i] = abs(servo[i]-180);
    channel[i] = int(channel[i]*conversionFactor)+pulseMin;
  }  
  
  //if (j==0) {
  //  Serial.println(channel[0]+pulseStart);
  //}
  
  // We're ready to wait for the next frame
  // Some jitter is allowed, so to the closest ms
  while (millis() - lastFrame < frameLength) {  
    delay(1);
  }
}
Logged

----------
Mathieu

New Jersey
Offline Offline
Full Member
***
Karma: 0
Posts: 193
Ard at work
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Mashed it up with some code I'd made to generate the pitch and bank angles from an accelerometer (based off of Dave Mellis Smoothing Tutorial) and I can now control two servos on the model by tilting the arduino forwards/backwards or left/right.

Also cleaned up the ugly bit where I defined 5.7 as an integer in the previous code  :-[ and made better use of defined constants in my variable declarations.

The new code doesn't fit in 5500 characters, so I can't quote it here, but I posted it here instead: http://ban-sidhe.com/blog/?p=1467
Logged

----------
Mathieu

0
Offline Offline
Newbie
*
Karma: 0
Posts: 10
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Whoa, Nice job buddy  smiley i like it lol. It will be more then i will ever do =D

Nice job again btw smiley
Logged

New Jersey
Offline Offline
Full Member
***
Karma: 0
Posts: 193
Ard at work
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Thank you :-)
Logged

----------
Mathieu

0
Offline Offline
Full Member
***
Karma: 0
Posts: 173
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Awesome! smiley-grin I've been looking for something exactly like this.

I'll be expanding on your brilliant code.

What I want to do is use the wii nunchuck to control one of my helicopters.

On my futaba running mode 2.

The wii nunchuck tophat for cyclic, channels 1 and 2.

The nunchuck C and Z buttons for channel 3(throttle)

Tilt from the nunchuck for channel 4(rudder)

I'm already able to read in the nunchuk data, now to pair the two!!!
Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have been playing around with this kind of thing myself. You are a brave man if you will risk your helicopter to nunchuck control. The challenge you will find is that it is very difficult to sense how to physically hold the nunchuck so that the inputs are zeroed. Any slight tilt of the nunchuck causes gravity to change one or more of the outputs (even when I think I am holding the nunchuck in a fixed position) You can't relay on the orientation of the heli to provide feedback because there is always a lag in the helis response (particularly if it has fixed pitch rotors, which it sounds like your is).

In short, it is very very much easier to overcontrol using accelerometer sensor input then it is using a conventional stick.

But, it would be great if you can overcome these obstacles so please post your progress.  But be careful.

When you have your project working, try plugging the arduino output into a simulator that takes trainer port input,  so you can see what its like before risking life and limb and your helicopter.  

Good luck.
« Last Edit: January 05, 2008, 07:56:39 am by mem » Logged

New Jersey
Offline Offline
Full Member
***
Karma: 0
Posts: 193
Ard at work
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Aballen, thanks! That's exactly the kinda thing I wrote this for :-)

Mem, from my limited experience with helicopters on FMS, I see your point... however, it shouldn't be too hard to include a dead zone and some exponential either in the code on the Arduino or in the radio if it allows for that on student inputs, which should help considerably. Also, the arduino could provide feedback on the control position either via rumbling, tone/pitch or anything else you add to the circuit.

Lastly, he's only planning to have the rudder on the tilt, which seems to me to be the lowest risk choice here, not the cyclic.

In any case, I look forward to hearing about it!
Logged

----------
Mathieu

New Jersey
Offline Offline
Full Member
***
Karma: 0
Posts: 193
Ard at work
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Which all reminds me... if you have one of those usb thingies to plug your radio into a computer as a joystick for a simulator (like FMS mentioned above), the arduino with the same code, plugged into it, would let you try your control scheme in the safety of the virtual world.

Bound to save you time and money as you iron out the kinks.

This would also be a good quick 'n dirty way to make your arduino speak USB-joystick to your computer, by the way...
Logged

----------
Mathieu

0
Offline Offline
Full Member
***
Karma: 0
Posts: 113
Building our robotic overlords
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Nice code!  I'm building my way up to telepresence on a crawler bot, so I may end up using it as well - please keep us posted on this COOL project!
Logged

0
Offline Offline
Full Member
***
Karma: 0
Posts: 113
Building our robotic overlords
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I wanted to add to Aballen - it takes a brave soul to put an RC heli under microcontroller control.  Wear your armor.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 9
a r d u i n o
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm new to the arduino and I think I basically understand how this is working, however I do not understand how he is controlling multiple channels on the transmitter from a single output on the arduino? Are the Pulse widths sent out in series?

If anyone could take a second to explain how that works I would really appreciate it.
Thanks,
Casey
Logged


New Jersey
Offline Offline
Full Member
***
Karma: 0
Posts: 193
Ard at work
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes, that's exactly what I'm doing.  smiley-wink

In essence, the radio transmitter is modulating the carrier signal with a series of servo pulses, channel one first, then two, three, etc... End with a synch pulse that is recognizably different from a servo pulse to keep everyone's understanding of which channel is which, and you're all set.

The receiver demodulates to get the series of pulses, then sends the pulse in channel one to the servo connected to output one, and so on.

All the trainer interface does is substitute the pulses for certain channels from the teacher's radio for the ones from the pupil's. Replace the pupil's radio with an arduino and all it takes is a single signal to pass signals for 12 or more servos to the teacher's radio.
Logged

----------
Mathieu

0
Offline Offline
Newbie
*
Karma: 0
Posts: 9
a r d u i n o
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Wow,
A thousand thanks matt,

I think I've got a better understanding,

http://www.mftech.de/ppm_en.htm

Was also helpful,
Just a few other questions, if you have the time.

Are all transmitters designed to operate with a 20 millisecond frame? (it seems like they are working with a 22.5 millisecond frame in the above link)

If I only need to address 3 of the 4 channels in my transmitter (I'm working with a Futaba T4YF) does the protocol require me to have 4 pulses within each frame or can I only encode the first three and ignore the 4th?



This is the schematic for the controller jack on the transmitter I'm using, Can I just connect the arduino output pin to pin 1 on the transmitter and the arduino ground to pin 2 on the transmitter? Or does the output of the arduino need to be altered before it gets to the transmitter?

Thanks again,
Casey
Logged


New Jersey
Offline Offline
Full Member
***
Karma: 0
Posts: 193
Ard at work
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I think most transmitters and receivers are actually quite flexible on how long the frame is. They seem to care more about finding a synch pulse after some reasonable length of time, but that length of time is not crucial as long as it somewhat close to 20ms. Your mileage may vary depending on hardware, of course.

I'm also pretty sure that once you have given all the pulses (or channels) that you care about, you can omit any others up to whatever the capacity of the transmitter or receiver is. In my case, if I only want to send channels one and two, I just wait for the end of the frame and give the synch pulse. My Royal Evo transmitter and my Futuba receiver don't seem to mind.

Now, the tricky bit is the hardware connection between the radio and the arduino. This is where you could, conceivably, do some permanent damage if you connect wires the wrong way, and you'll notice that in the case of the Multiplex radios like mine, you have to add a transistor between the two. From what I dimly remember reading on other radios, I think this is required only for Multiplex, tho, and it's not for protection, it's just to keep the signal high enough so that the radio can detect it.

I strongly recommend reading up as much as you can find on the web on the particularities of your transmitter and its trainer interface... I don't have any experience with the Futuba radios, for example.  smiley-razz If you're nervous about your radio, perhaps you can find a cheap, non-programmable radio that accepts pupil input over a trainer interface from the same brand, and try it first with that hardware?

If you do get it to work with a Futaba radio, the next chap who's trying to do this will appreciate it if you post the details of the connection here, of course. ;-)
Logged

----------
Mathieu

Pages: [1] 2 3 ... 6   Go Up
Jump to: