Arduino-Controlled RC Transmitter

sweet! that did it, thanx!

i KNEW i had seen inverting somewhere... just couldn't put my finger on it; that manual is LOT to take in!

the spektrum module is recognizing the PPM train as it lights up and binds to a rx. servos are whilin' out though so i suppose i need to make some tweaks to the pulse or frame lengths. there are some slight ripples in voltage (not sure what that matters) but the pulse timings look solid!

edit - im using your Version_0_2 code

The voltage ripples are tiny and shouldn't make any difference at all.
What is it your sevos are doing? My thinking is this: if you are missing some channels then try increasing the frame length. If the servos are unstable then you could try increasing the padding time (this is the time the signal spends high) it might help to look at the output from your receiver.
Nice scope by the way. I wish mine did that, but it is 25 years old and I count myself lucky if it takes less than five minutes to warm up!

The voltage ripples are tiny and shouldn't make any difference at all.

thats what i thought too... but andykunz of spektrum mentions ac or dc coupling here. not sure how that applies yet though...

What is it your sevos are doing? My thinking is this: if you are missing some channels then try increasing the frame length. If the servos are unstable then you could try increasing the padding time (this is the time the signal spends high) it might help to look at the output from your receiver.

i think it would be best described it as jitter or constant twitching regardless of position. i tried extending the frame length to 25ms as mentioned in this thread but it didn't seem to help. i'll try adding to the padding next.

when i first loaded ian's code i had some jitter from the servos that seemed to clear up after calibrating the sticks. monitoring the rx outputs made setting up the sticks a lot easier! i'll try that in a minute too!

Nice scope by the way. I wish mine did that, but it is 25 years old and I count myself lucky if it takes less than five minutes to warm up!

thanx, lol! i purchased this one after much fighting with my dad's Telequipment scope of the same era! i've had it less than a week and im hooked! its surprisingly well built, is easily converted to 100mhz, small, light, cheap, and has a Auto Button! :wink: heres a link if you're interested. link
cheers!
-sj

its definitely a timing issue! changing values in the timer makes the problem/worse so now it just a matter of locating the proper bits of code and plugging in the right values. this is what i have so far:

//Timer data
#define timer_correction_factor 1.00                       //timer correction factor. This is needed if your arduino is too fast or slow
#define timer_framelength 25000 * timer_correction_factor   //Maximum framelength in counter ticks
#define timer_pause 350 * timer_correction_factor           //Pause between pluses in counter ticks

int timer_accumulator = 0;        //accumulator. Used to calculate the frame padding
int timer_ptr = 0;                 //timer array pointer

i changed the correction factor to 1.0 and this gave me a perfect framelength frequency
i changed the framelength to 25ms
and im getting 0.348ms pulses

i think this is right, but does the following produce a centered pulse if no stick connected to a particular channel?

void stick_check()
{
  for (int i = 0; i < 5; i++) {
    sticks[i].position = 50; 
  }
}

i think all i have left is to widen the LOW so that the pulse ranges are between 1.5 and 2.5ms. looks like currently they are at 1ms at what i think are centered sticks. gotta plug the yoke back in to find out where im at for sure. getting close! :slight_smile:
-sj

Hello,

I have to say, I am very surprised that you are having any problems at all, when I use the code (and I have flown with it on several occasions) i have no problems what so ever; it has been jitter free and rock solid. Admittedly i am on 35mHz rather than 2.4 but I don't see how that would be any different.

The length of the servo pulse is given by the time between the rising edge of one pulse in the ppm chain and the rising edge of the next, not by the length off the low segment in-between.

I know that as i wrote the code I am probably a bit biased, but i assure you it produces a valid ppm stream with accurate pulses for each servo, it must do as i have flown with it. The correction factor is in there because my arduino runs too fast (weird i know, but i have three arduinos and only one of them requires correction!)
I suspect that the problem is coming when the spektrum module you are using is reading the signal. Have you tried scoping the output from a known good transmitter and comparing that to the arduino output. Could it be that the spektrum module is expecting a ppm chain with voltage of 9.6v as most transmitters run on 8 cells? In which case the 5v chain my not be being detected correctly. The reason that the original code produces an inverted ppm chain is because i then ran it through a transistor NOT gate to switch 9.6v as my 35mHz gear needed a higher voltage signal to the rf board.

Let me know how you get on.

Chris

i think all i have left is to widen the LOW so that the pulse ranges are between 1.5 and 2.5ms. looks like currently they are at 1ms at what i think are centered sticks

Sorry, I ment to say in my last post; the correct timing for standard is 1 to 2ms with 1.5 being centred. I found a PDF file that explains exactly how the ppm stream works that is very very helpful http://www.omegaco.demon.co.uk/mectnpdf/mectn004.pdf
Most modern servoes will work over an extended range of around 0.6 to 2.4ms

Chris

Hi, i am trying to get it running using a Serial connection to my PC. For now its possible to send Channel ID and Position via Serial to the Arduino and the PPM will be adjusted.

The BIG problem is that it my Serial connection is not reliable - i think its because of a timing problem.

Maybe someone of you guys/girls have an idea or solution?

I used Ver 1.6 of Ian´s code and removed all but the most important parts:

? Code should have been here, but i need to post a "normal" message first .... so code in next message (i hope)...

If you start sending - for example- S010800 ( Set Channel 1 to pos 800) via Serial to the Arduino quite fast you will end up with wrong Serial commands.
I have no real clue why.... :-[

Here is the code i use:

//Serial to PPM based on Ian´s:
//// RC Joystick 430mHz
//// For use with Arduino Nano V3.0
//// Ian Johnston 28/04/2010
////
//// If you would like to help support future projects like these then please
//// make a small PayPal donation via my website www.ianjohnston.com
//// Thanks!
////
//// Ver 1.6 - Now using Timer1 to set 22mS refresh for PPM output.
////           Dual rates are implemented but need custom setup.
////           Trimming is not working.
////           Battery monitor sub tweaked
////           Adjusted timing off various subs
////           Various tweaks to LCD & battery monitor
////
// Serial to PPM ??? SEND: S010800 to set Channel 1 to Pos 800

int tick = 0; // Used for various timing of subs
int tick2 = 0; // Used for various timing
int slowflag;

int CH1= 700;
int CH2 = 700;
int CH3 = 700;
int CH4 = 700;
int CH5 = 700;
int CH6 = 700;

int RatesHIMIDLO = 2; // Default is MID rates
int TrimSetting = 0;
int Fixed_uS = 300;       // PPM frame fixed LOW phase
int pulseMin = 700;          // pulse minimum width in uS
int pulseMax = 1700;      // pulse maximum width in uS
int outPinTEST = 8;       // digital pin 8
int outPinPPM = 10;       // digital pin 10


//*** Serial COM&Comand
const int MSG_LENGTH = 7;  // message is char 'S' followed by 6 digits
const char HEADER  = 'S';
//***

ISR(TIMER1_COMPA_vect) {

  ppmoutput(); // Jump to ppmoutput subroutine
  tick = tick + 1;  // update timing tick for subs
  tick2 = tick2 + 1;  // update tick

}

void setup() {

  // setup I/O
  pinMode(outPinPPM, OUTPUT);   // sets the digital pin as output


  // ** SERIAL SETUP
  Serial.begin(57600);         // connect to the serial port
  Serial.println("Serial to PPM");
  //****


  // Setup timer
  TCCR1A = B00110001; // Compare register B used in mode '3'
  TCCR1B = B00010010; // WGM13 and CS11 set to 1
  TCCR1C = B00000000; // All set to 0
  TIMSK1 = B00000010; // Interrupt on compare B
  TIFR1  = B00000010; // Interrupt on compare B
  OCR1A = 22000; // 22mS PPM output refresh
  OCR1B = 1000;
}


void loop() { // Main loop
  processSerial();
  //readanainputsmap(); // Run sub - read ana inputs & map
  checklimits();      // Run sub - check individual channel PPM limits
  //
  //  if (tick >= 11) {     // only run certain subs every 1/4 sec or so (22mS * 11 = 242mS)
  //      tick = 0;
  //      //switchesRates();    // Run sub - read panel switches
  //      //batterymonitor();   // Run sub - check battery
  //  }
  //  
  //  // generate slow changing flag, about 2sec on/off
  //  if (tick2 <= 50) {
  //      slowflag = 0;
  //  }
  //  if (tick2 >= 50 && tick2 <=100) {
  //      slowflag = 1;
  //  }
  //  if (tick2 >= 100) {
  //      slowflag = 0;
  //      tick2 = 0;
  //  } 

}


void processSerial() {
  // Process a message available on serial port
  while(Serial.available() >=  MSG_LENGTH )  // process messages when all characters are received: "S" as start indicator, 2 digits as channel number, 4 digits for the value
  {
    if(Serial.read() == HEADER ) //Serial.Read() will "use up" the chars, so we can forget about the "S" now...
    {
      int val = 0;
      int sval =0;
      //First 2 digits are the Channel...
      for(int i =0; i < MSG_LENGTH-5; i++)//7-5=2
      {

        char ch = Serial.read();
        if(ch >= '0' && ch <= '9'){            // is ch a number?
          sval = sval * 10 + ch - '0';           // yes, accumulate the value
        }

      }
      Serial.print("CH: ");
      Serial.println(sval); // For debug


      //Next 4 Digits should be the position/
      for(int i =0; i < MSG_LENGTH-3; i++) // 7-3=4 ... 2+4=6 ... S-12-3456
      {
        char ch = Serial.read();
        if(ch >= '0' && ch <= '9'){            // is ch a number?
          val = val * 10 + ch - '0';           // yes, accumulate the value
        }
      }
      Serial.print("POS: ");
      Serial.println(val); // for debug
      Serial.flush();//clear buffer, there might be some rubbish inside...
      //Serial.println(sval);
      //Serial.println(val);
      // For now its just Channel 1 which will get its new value.
      if (sval==1){
        CH1=val;
        Serial.print("CH1 Set to ");
        Serial.println(CH1);
      }


    }
  }

}


void checklimits() { // check limits sub
  if (CH1 < 700) CH1 = 700;     // Min
  if (CH1 > 1700) CH1 = 1700;   // Max   
  if (CH2 < 700) CH2 = 700;   // Min
  if (CH2 > 1700) CH2 = 1700; // Max 
  if (CH3 < 700) CH3 = 700;   // Min
  if (CH3 > 1700) CH3 = 1700; // Max 
  if (CH4 < 700) CH4 = 700;       // Min
  if (CH4 > 1700) CH4 = 1700;     // Max   
  if (CH5 < 700) CH5 = 700;               // Min
  if (CH5 > 1700) CH5 = 1700;             // Max
  if (CH6 < 700) CH6 = 700;               // Min
  if (CH6 > 1700) CH6 = 1700;             // Max 
}


void ppmoutput() { // PPM output sub
  // test pulse - used to trigger scope
  //digitalWrite(outPinTEST, LOW);
  //delayMicroseconds(100);    // Hold
  // digitalWrite(outPinTEST, HIGH);

  // Channel 1 - Aeleron
  digitalWrite(outPinPPM, LOW);
  delayMicroseconds(Fixed_uS);    // Hold
  digitalWrite(outPinPPM, HIGH);
  delayMicroseconds(CH1);  // Hold for Aeleron_uS microseconds      

  // Channel 2 - Elevator 
  digitalWrite(outPinPPM, LOW);
  delayMicroseconds(Fixed_uS);    // Hold
  digitalWrite(outPinPPM, HIGH);
  delayMicroseconds(CH2); // Hold for Elevator_uS microseconds      

  // Channel 3 - Throttle
  digitalWrite(outPinPPM, LOW);
  delayMicroseconds(Fixed_uS);    // Hold
  digitalWrite(outPinPPM, HIGH);
  delayMicroseconds(CH3); // Hold for Throttle_uS microseconds      

  // Channel 4 - Rudder
  digitalWrite(outPinPPM, LOW);
  delayMicroseconds(Fixed_uS);    // Hold
  digitalWrite(outPinPPM, HIGH);
  delayMicroseconds(CH4);   // Hold for Rudder_uS microseconds

  // Channel 5 - TI Switch
  digitalWrite(outPinPPM, LOW);
  delayMicroseconds(Fixed_uS);    // Hold
  digitalWrite(outPinPPM, HIGH);
  delayMicroseconds(CH5);     // Hold for TIsw_uS microseconds        

  // Channel 6 - TI pot
  digitalWrite(outPinPPM, LOW);
  delayMicroseconds(Fixed_uS);    // Hold
  digitalWrite(outPinPPM, HIGH);
  delayMicroseconds(CH6);       // Hold for TI_uS microseconds  

  // Synchro pulse
  digitalWrite(outPinPPM, LOW);
  delayMicroseconds(Fixed_uS);    // Hold
  digitalWrite(outPinPPM, HIGH);  // Start Synchro pulse

}

;D

I used Chris Code today and Lo and behold: Its working like a charm now ::slight_smile:

Well, i guess it was all about the Interrupt in the timer ... now there is plenty of time for the UART AND no more kick in while the UART is working...

Only drawback is that i blew my PIN 10 on my Arduino Mega, but work is in progress on an Duemilano now.

You guys Rock!

Question: I like to buy a new Joystick for Mac/PC, the one i have at the moment only puts out values between -127 and +127, so 8Bit resulution. Not that good even i doubt that many RC Servos have a better resolution.
Does anyone know a good (not too expencive) Joystick with atlast a 10Bit Resolution?

Thanks!

would you consider a Wii Nunchuck?

Wii Nunchuck - mmm... its Bluetooth and i try to avoid 2.4GHz equiptment since it have my 2.4GHz RC Transceiver next to my PC and i don´t like to debug errors which might occure because of that. As soon as everything else works fine, i might try a Nunchuck too :slight_smile:

An other Question: As i allready wrote i blew up my Pin 10 on my Adruino Mega.. What would be the best way to protect that Pin since it seems to be quite fragile? A fast Diode?

No, you are mixing up the Wii remote (wireless) with the Wii Nunchuck (wired). Here is an image:

You can get them for a few dollars, for example here: http://www.dealextreme.com/details.dx/sku.24529

fmkit has the nunchuck transmitter mastered.

http://www.fmtv.us/wii_nunchuck_rc.html

This is a pretty good idea I wanted to do this for my project

Hi, i bought a Wii Remote and a Nunchuck and use them together with OSCulator (OSCulator is simulating a Joystick HID on my MAC)
Both devices are full 16 Bit resolution (technically) and are working as input for my Arduino via Serial Port.
They gyros are way to sensitive as control for RC Servos. If you flatten out the jitter from the Gyros the reaction time for the servos goes down (maybe the use of vectors could help but i will not dig into that).

The Joystick on the nunchuck gives great results! It has a stable and good resolution and its possible to control Servos very fast and precise. But it has be be kept in mind that RC Servos (altlast all Analog ones that i own) have at best a resolution of 1.5 to 2 Degrees.

Next will be a test of a good "normal" Joystick. The old 8-Bit USB which i own is not really useable because its just wobbly and the values i read are not stable (Value - Postion of Joystick).

Anyone knows which Diode i should use to protect PIN 10 on my Arduinos?

PS: Thanks for the tip with the Wii! :slight_smile:

No thanks for the tip on the Wii, glad you like them.

You shouldn't have problems with the sensitivity on the Nunchuck. I know they are being used for controlling r/c aircraft, so you maybe the problem is in your code? Can you show the code you tested with?

Even if the controller is just sitting on my desk i get readings from 316651 to 31995 and as a result an output of 1035-1045 for the PPM on that channel.
I now changed the Serial Data from distinct channels to one array for all channels and with the "smooth" function on OSCulator at 40 the gyros are usable as control for a Servo :slight_smile:

Super :slight_smile: you get A for this :slight_smile:

This one helped me! Ty matt!