Sunday, November 11, 2012

How To Read RC Receiver PPM Stream

Many RC Transmitters and Receivers provide access to the PPM Stream, this is a single stream of pulses which includes the information for all of the receiver channels in a single connection. 

This stream can often be accessed by removing the receiver case and adding a single wire to an existing connection within the receiver.

If we can gain access to this stream we can write smaller, faster code and only need a single interrupt pin to read all of the receiver channels.

Using a single built in interrupt to read multiple channels is much faster than using pin change interrupts which leads to a visibly smoother output signal for your servos and ESCs.

This post concludes with a library which can be used to read a PPM stream using a single interrupt. The same library is also able to output upto 18 servo signals from a single Arduino UNO with no additional components. This is an increase of 6 servos over the standard library - its also faster leading to fewer glitches.

Scroll down for a video of the library and an RC Receiver hack in action -

What does the PPM Stream Look Like ?




The stream is made up of a series of short pulses, the first pulse is the start marker. The time between the start marker and the start of the next pulse defines the pulse length for channel one. The time to the next pulse defines the pulse length for channel 2 and so on. The end of the pulse stream is marked by a gap know as the frame space. This gap indicates that there are no more channels to receive and the next pulse will be the start of a new frame. Each frame contains the pulse widths for all of your receiver channels.

Note - unlike servo signals, in a PPM Stream it is the gaps between pulses that defines the pulse width for a channel, not the duration of the pulse itself which can be very short.


How do we find the PPM Stream ?
If your equipment provides direct access to the PPM Stream, skip over this part, if it does not, read on.

The PPM Stream is transmitted between your transmitter and receiver as a single data stream. Inside the receiver this signal is de-multiplexed to produce the individual channels signals as seen in the diagram.


Fortunately for us, the de-multiplexer is most often just a simple shift register clocked by the PPM Stream.


The shift register is clearly visible inside this Hitec HFS-03MM Receiver - its the IC in the center.

The PPM Stream is routed to the clock pin (clock A) of the shift register, the PWM Streams for the individual channels are taken from the shift register outputs (Q1a,Q2a,Q3a).

Usually we would try to read these outputs using three separate interrupt pins, however as they are directly derived from the clock signal we can access the same information by reading the (PPM) clock signal directly. This saves us  two interrupt pins and  a lot of code and memory.

The best bit - read on and theres a ready made library at the end of the post.

Example Datasheet for the 4015 Shift Register used to demultiplex the PPM Signal in the pictured receiver
http://docs-europe.electrocomponents.com/webdocs/05f9/0900766b805f9f8d.pdf

Breaking out the PPM Signal
As above, if your receiver already provides access to the PPM Stream, skip ahead, if not, here are two receivers I have hacked. Its as simple as follows -

To access the PPM Stream from Arduino we need to solder an additional wire to the clock pin of the 4015 shift register.

Example PPM Hack - 1

Hitec 27Mhz FM 3 Channel HFS03MM Receiver - tapping the 4015 shift register to access PPM signal.






Example PPM Hack - 2

Different Receiver, Different Manufacturer, Different Technology - Futaba R152JE 27Mhz AM

The Same 4015 Shift Register inside tapped for PPM Output using thin white wire soldered to the clock pin.


A male jumper wire attached to the hacked Hitec receiver ready for connection to Arduino -


VIDEO - The RC Arduino Library and Receiver Hack in action 


Schematic showing connections in the video above


Multiplexing Servos From Arduino Using PPM Style Signals

In a recent RCArduino Post we showed how a 4017 Decade counter IC could be used to control 10 servos from a single Adruino pin. Each time the counter is clocked by the Arduino it sets one servo output low and sets the next one high. We control the clock pulses to control the pulse widths for the individual servo outputs. Demulitplexing a PPM signal is essentially the same process, the RC Receiver applies a clock pulse to a shift register which shifts the pulse from one output to the next, the longer between clock pulses, the longer the servo pulse.

RC Arduino Serial Servos

Introduction and 10 Servos from 2 Pins
http://rcarduino.blogspot.com/2012/08/arduino-serial-servos.html

20 Servos From 4 Pins
http://rcarduino.blogspot.com/2012/10/arduino-serial-servos-20-servos-4-pins.html

How do we read this RC Receiver PPM Pulse stream with a micro controller ?

Now that we have access to the PPM Stream, how do we read it with our Arduino ?

First of all we need to synchronise with the pulse stream, we do this by waiting for the long pause (the frame space) which indicates the end of one frame and the start of the next.


1) Once we have found this space, we can set our channel counter to 0 and record the current time.

2 ) The next pulse that arrives will indicate the end of the channel 1 pulse. We calculate the channel one pulse width by subtract the time recorded in 1) above from the current time. We also store the current time as the starting point for the channel 2 pulse width

3) We repeat the above process - subtract the last pulse time from the current time to get the pulse width for each of the remaining channels

4) When we have received all of the channels we expect, we start again at 1.

At each stage of the process 1-4 we know whether we are expecting a channel signal or a frame space and so we can use this information to confirm synchronization with the PPM Stream.

Sample Arduino Code For Reading RC Receiver PPM Signal

The following code is taken from the RCArduinoFastLib -

// we could save a few micros by writting this directly in the signal handler rather than using attach interrupt
void CRCArduinoPPMChannels::INT0ISR()
{
  // only ever called for rising edges, so no need to check the pin state
 
  // calculate the interval between this pulse and the last one we received which is recorded in m_unChannelRiseTime
  uint16_t ulInterval = TCNT1 - m_unChannelRiseTime;
 
  // if all of the channels have been received we should be expecting the frame space next, lets check it
  if(m_sCurrentInputChannel == RC_CHANNEL_IN_COUNT)
  {
    // we have received all the channels we wanted, this should be the frame space
    if(ulInterval < MINIMUM_FRAME_SPACE)
    {
     // it was not so we need to resynch
     forceResynch();
    }
    else
    {
      // it was the frame space, next interval will be channel 0
      m_sCurrentInputChannel = 0;
    }
  }
  else
  {
    // if we were expecting a channel, but found a space instead, we need to resynch
    if(ulInterval > MAXIMUM_PULSE_SPACE)
    {
      forceResynch();
    }
    else
    {
     // its a good signal, lets record it and move onto the next channel
     m_unChannelSignalIn[m_sCurrentInputChannel++] = ulInterval;
    }
  }
  // record the current time
  m_unChannelRiseTime = TCNT1; 



Reading the PPM stream with the RCArduinoFastLib

One of the main features of the RCArduinoFastLib is a servo library, why do we need another servo library when the existing servo library is well know, widely used and reliable ?

The standard Arduino Servo library has one major flaw - it resets timer1 at the start of every frame. This means that timer1 cannot be used for timing as easily as you might want. A common approach to overcoming this is to use the micros() function for timing, but this is many times slower than accessing TCNT1 directly.

As our channel input timing and servo output timing is interrupt driven, we really care about speed, every little bit of inefficiency leads to more and bigger interrupt clashes which introduce the ticks and jitter often seen in Arduino based RC projects.

By using the RCArduinoFastServos library we avoid resetting timer1, gain an additional six servos and also the added benefit of being able to user timer1 for very fast timing of our input signals. These lead to a measurable increase in output signal quality with fewer and smaller clashes in short - glitch free projects.

You can find the RCArduinoFastServos library in this post -
http://rcarduino.blogspot.com/2012/11/how-to-read-rc-channels-rcarduinofastlib.html

Sample Sketch - Read PPM Input and Output To Mulitple Servos

A sample sketch you can use to read three channels or PPM is presented below. The sketch can easily be modified to read upto 10 channels.

If you need help or want to ask a question - ask away.

#include <RCArduinoFastLib.h>

 // MultiChannels
//
// rcarduino.blogspot.com
//
// A simple approach for reading three RC Channels using pin change interrupts
//
// See related posts -
// http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html
// http://rcarduino.blogspot.co.uk/2012/03/need-more-interrupts-to-read-more.html
// http://rcarduino.blogspot.co.uk/2012/01/can-i-control-more-than-x-servos-with.html
//
// rcarduino.blogspot.com
//

// Assign your channel out pins
#define THROTTLE_OUT_PIN 8
#define STEERING_OUT_PIN 9
#define AUX_OUT_PIN 10
#define OTHER_OUT_PIN 11

// Assign servo indexes
#define SERVO_THROTTLE 0
#define SERVO_STEERING 1
#define SERVO_AUX 2
#define SERVO_OTHER 3
#define SERVO_FRAME_SPACE 4

volatile uint32_t ulCounter = 0;

void setup()
{
  Serial.begin(115200);

  // attach servo objects, these will generate the correct
  // pulses for driving Electronic speed controllers, servos or other devices
  // designed to interface directly with RC Receivers

  CRCArduinoFastServos::attach(SERVO_THROTTLE,THROTTLE_OUT_PIN);
  CRCArduinoFastServos::attach(SERVO_STEERING,STEERING_OUT_PIN);
  CRCArduinoFastServos::attach(SERVO_AUX,AUX_OUT_PIN);
  CRCArduinoFastServos::attach(SERVO_OTHER,OTHER_OUT_PIN);
 
  // lets set a standard rate of 50 Hz by setting a frame space of 10 * 2000 = 3 Servos + 7 times 2000
  CRCArduinoFastServos::setFrameSpaceA(SERVO_FRAME_SPACE,6*2000);

  CRCArduinoFastServos::begin();
  CRCArduinoPPMChannels::begin();
}

void loop()
{
  // Pass the signals straight through - 

 
  uint16_t unThrottleIn =  CRCArduinoPPMChannels::getChannel(SERVO_THROTTLE);
  if(unThrottleIn)
  {
    CRCArduinoFastServos::writeMicroseconds(SERVO_THROTTLE,unThrottleIn);
  }

  uint16_t unSteeringIn =  CRCArduinoPPMChannels::getChannel(SERVO_STEERING);
  if(unSteeringIn)
  {
    CRCArduinoFastServos::writeMicroseconds(SERVO_STEERING,unSteeringIn);
  }

  uint16_t unAuxIn =  CRCArduinoPPMChannels::getChannel(SERVO_AUX);
  if(unAuxIn)
  {
   CRCArduinoFastServos::writeMicroseconds(SERVO_AUX,unAuxIn);
  }
}



/* DB 04/02/2012 REMOVED The interrupt service routine definition here, it clashes with the attachInterrupt in the cpp file */
/* REMOVE BEGIN 
ISR(INT0_vect) {
 CRCArduinoPPMChannels::INT0ISR();
}

REMOVE END */

Duane B

99 comments:

  1. Thanks for posting this! It seems that the inputs to the Arduino have to come from servo pins on the receiver, is that how it should be?

    I thought this code should be able to get a single PPM input and decode all separate channels...

    Really want to figure this out, thank you!

    ReplyDelete
  2. The code allows you to read a PPM Stream and access the individual channel values. The pictures and videos show a receiver being hacked to access the PPM Stream, you only need to do this if your receiver does not already provide access to the PPM. In the video the car is being controlled using the code from the blog to read the single PPM Stream through the single orange wire.

    Maybe your confusion is coming from the code which looks like this -

    uint16_t unThrottleIn = CRCArduinoPPMChannels::getChannel(SERVO_THROTTLE);

    This is used to access the values of individual channels within the PPM Stream.

    Once you have called the function -
    CRCArduinoPPMChannels::begin();

    The code will efficiently read the PPM Stream in the background without you having to do anything.

    Whenever you want to access a channel value, you call -

    uint16_t unAuxIn = CRCArduinoPPMChannels::getChannel(SERVO_AUX);

    If a new value is available it unAuxIn will contain the value, if no new value has been received it will contain 0. This is useful for creating your fail safe routine.

    Duane B

    ReplyDelete
  3. Hi
    Trying to figure you which pin on the arduino that the PPM stream is attached to. I didnt see it mentioned in the tutorial. I am thinking it is digital pin 2 because of this function in RCArduinoFastLib.cpp is using interupt '0' and the chart at http://arduino.cc/en/Reference/AttachInterrupt says interrupt '0' is digital pin '2'
    thanks for all your help and code.

    void CRCArduinoPPMChannels::begin()
    {
    m_sOutOfSynchErrorCounter = 0;
    attachInterrupt(0,CRCArduinoPPMChannels::INT0ISR,RISING);
    }

    ReplyDelete
  4. Digital Pin 2 is correct assuming your using an Arduino UNO, if your using another model of Arduino it INT0 may be on a different pin.

    Let me know how many channels you are reading and writing, this set through two defines in the header files at compile time - its a less easy to use interface, but slightly faster/smaller code.

    Duane

    ReplyDelete
  5. Thanks for the confirmation and I should have said that I am prototyping with the UNO.

    Planning on reading 4 channels. Writing 0 channels to servos. The output it connected to an IR LED. I wrote a rough class for the Syma S107 that utilizes this library for the output section of my project.
    http://www.open.com.au/mikem/arduino/IRrc/

    Are these are the constants I would modify to set the number of channels to be read and the details of the incoming pulse. I found this in the RCArduinoFastLib.h file.

    #define RC_CHANNEL_IN_COUNT 3
    // two ticks per us, 3000 us * 2 ticks = 6000 minimum frame space
    #define MINIMUM_FRAME_SPACE 6000
    #define MAXIMUM_PULSE_SPACE 5000

    Even though the IR helicopter only uses 3 channels for flight control, I am going to send 4 channels of data from the TX to the module. The original IR TX has a switch on it to allow 2 helicopters to fly in the same room at the same time. The plan is to use this 4th channel from the TX to implement this feature of the original TX.

    thanks for your help

    ReplyDelete
  6. has any one else received this error when compiling the code?

    core.a(WInterrupts.c.o): In function `__vector_1':
    C:\Arduino\hardware\arduino\cores\arduino/WInterrupts.c:273: multiple definition of `__vector_1'
    Read_PPM_Input.cpp.o:C:\Users\AppData\Local\Temp\build8882354692202141392.tmp/Read_PPM_Input.cpp:78: first defined here

    The issue seems to be with

    ISR(INT0_vect) {
    CRCArduinoPPMChannels::INT0ISR();
    }

    when commented out the code will compile flawlessly.
    I am using Arduino SW version 1.01 and an Uno Board, Pin change Library 2.19.

    any ideas?

    ReplyDelete
    Replies
    1. I copied the test sketch(rcarduino.pde), RCArduinoFastLib.cpp RCArduinoFastLib.h, from the blog at http://rcarduino.blogspot.com/2012/11/how-to-read-rc-channels-rcarduinofastlib.html. It compiled after changing a lowercase 'a' to uppercase as posted in the comments on that page. I also used PinChangeInt 2.19. Using Ubuntu arduino 1:1.0.1+dfsg , which I assume is 1.01 I havent uploaded any code to my arduino so cant comment on that yet.

      I have had issues with the directory structure in arduino. This worked for me.
      looks like your on windows. Wherever your sketchbook directory is this should help you relatively from there.
      ~/{location}/sketchbook/rcarduino/rcarduino.pde
      ~/{location}/sketchbook/libraries/rcarduino/RCArduinoFastLib.cpp
      ~/{location}/sketchbook/libraries/rcarduino/RCArduinoFastLib.h
      ~/{location}/sketchbook/libraries/PinChangeInt/{_the_downloaded_library}

      Delete
    2. uh nevermind. I just tried to compile the test sketch on this page and have the same error you got . we'll have to see what shakes out .

      Delete
    3. well not exactly mine says __vector_2 in the top section

      core.a(WInterrupts.c.o): In function `__vector_2':
      /usr/share/arduino/hardware/arduino/cores/arduino/WInterrupts.c:278: multiple definition of `__vector_1'
      sketch_dec02b.cpp.o:sketch_dec02b.cpp:78: first defined here
      collect2: error: ld returned 1 exit status

      Delete
  7. Send me a zip file of your code, user Duane B on the Arduino forum and I will try to compile it alongside the version posted here.

    Duane B

    ReplyDelete
  8. I was wondering if the attachInterrupt line in this function in RCArduinoFastLib.cpp

    void CRCArduinoPPMChannels::begin()
    {
    m_sOutOfSynchErrorCounter = 0;
    attachInterrupt(0,CRCArduinoPPMChannels::INT0ISR,RISING);
    }

    do the same thing as this function in the test sketch ?

    ISR(INT0_vect) {
    CRCArduinoPPMChannels::INT0ISR();
    }

    ReplyDelete
  9. Hi All, Randy is right, the ISR function line should have been removed. The attachInterrupt does exactly the same thing, so please comment out the ISR(INT_0){ ... } function and you should be good to go. Not sure why this made it onto the blog code I will check my versions when I get home this evening.

    Duane B

    ReplyDelete
  10. Hi Duane,

    I've been attempting to run this PPM code, but have been unsuccessful. I am able to upload your code as is and have it compile and upload with no error. I've changed the output from writing to servos, to serially printing the channel value. The issue is, the values printed do not make sense and they do not change as I adjust the throttle. Any help or hints will be greatly appreciated. Thank you.

    ReplyDelete
  11. Hi Jorge,
    How many channels is your transmitter sending and how many outputs do you want to drive, you will need to change two lines of the code to these numbers.

    Also which board are you using, do you have a common ground connection between the Arduino and the receiver and which pin is the receiver connected to ?

    Duane B

    ReplyDelete
    Replies
    1. Hi Duane,

      "How many channels is your transmitter sending?"
      I'm a using a Spektrum Satellite DX7S RC system that is apparently transmitting 7 channels at 2.4Ghz. However, in truth I have been unable to measure the output on an oscilloscope. I noticed your test allows up to 4 channels to be read w/o modification so figured it would work for me. However, I realize that I would need to modify the maximum number of RC_Channels_In_Count in order to accurately read a frame. What value would I need?

      "How many outputs do you want to drive"?
      Currently my project calls for driving 4 servos and 1 set of motors. However, I would definitely like the option of having all available channels.

      "Also which board are you using"?
      I am using an Arduino 1280 Mega board called a Seeeduino Mega.

      "Do you have a common ground connection between the Arduino and the receiver?"
      Yes.

      "Which pin is the receiver connected to?"
      It's connected to Digital Pin 2.

      Again, your help is greatly appreciated.

      Thank you,

      JC

      Delete
    2. Hi JC,

      Not sure if your still looking for help with this, but I have had a look at the differences between the Arduino processors relating to interrupts -

      http://rcarduino.blogspot.ae/2013/04/the-problem-and-solutions-with-arduino.html

      I am confident that I can now get you up and running on the 1280. Let me know if you still need help

      Duane B

      Delete
  12. Hi,
    Your correct that its RC_CHANNEL_IN_COUNT that you need to change, try making it 7, you could also change RC_CHANNEL_OUT_COUNT to 8 to drive as many channels as you are reading. The extra output channel (7+1=8) is an optional frame space that is added after all of the channels are output. you can see how its used in the setup function above. For 7 channels, a frame space of 6000 would be good.

    Duane B

    ReplyDelete
  13. Hi Duane

    I am currently busy with an RC project, upgrading a Tamiya Bullhead monstrer truck with a new Arduino and a 433MHz long range RF link module on each end, RX and TX. [url]http://www.seeedstudio.com/wiki/index.php?title=2KM_Long_Range_RF_link_kits_w/_encoder_and_decoder[/url]
    the problem is that I'm unable to send and receive data reliably up till now, i was using Easy transfer and got ALOT of inconsistent readings (timing issue i think) then after that i tried the VirtualWire library, as you probably know, doesn't work with Servo, and SoftwareServo doesn't work on the new Arduino IDE (which i tried with VirtualWire)
    After a week of internet searches and reading forums i finally came accross your blogspot :)

    Great Collection of info you have here b.t.w thanx for that!!
    i'm really interested in the explanation of PPM as i think tat is going to solve my problems,however, to do that i will need to prgram my transmitter to output a PPM signal in the first place, do you have any advice on how to do that?

    My setup: TXAruino is connected to 2 Joysticks and a couple switches. RXArduino connected to a Motor shield and a Servo
    TXArduino-->4 TX channels-->Transmitter--------->>--------Receiver-->4RX channels-->RXArduino

    the Detailed poost is here
    [url]http://arduino.cc/forum/index.php/topic,139560.0.html[/url]

    i think PPM might be a good solution to this problem.

    ReplyDelete
    Replies
    1. Hi,
      I will have a look at the tx/rx specs, I have a library tjat i will publish Shortly for generating PPM but depending on the capabilities of your rx/tx you might want a digital protocol instead

      Duane B

      Delete
  14. Hi Duane,

    This probably sounds like a silly question, but I will ask it anyway. My maim hobby is building large scale radio controlled (2.4gHz) model warships from scratch. In my current project I am planning to animate the Gun Fire Control Radars and the Gun Turrets and have them behave as they are controlled by a Fire Control System as they are in real life. My plan is to use an Arduino to read two channels of my 10 channel TX/RX system. One will be a three way switch, low, mid and high to set the mode of the Fire Control System and the second will be rotary switch to set the target bearing. After all that, my question is, having wired a cable to the clock pin of the receiver, will the other receiver outputs continue to work?

    Cheers and happy new year,

    Andrew

    ReplyDelete
  15. Hi, if I understand correctly all you want is to have two out of your ten channels read by the Arduino ? If these two channels arevalready available through servo type connectors you can read them directly with no effect on any of the other channels, this is a lot easier than Accessing the receiver internals. The explanation and code for this approach can be found in the series of posts with 'reading an RC Receiver' in the title.

    Duane B

    ReplyDelete
    Replies
    1. Thanks Duane,

      I found the more straightforward approach.

      Regards

      Andrew

      Delete
    2. Hi,
      You can find a more or less upto date index of projects and tutorials here - http://rcarduino.blogspot.com/p/project-index.html

      The index of RC Receiver posts is half way down the page under the title 'Interfacing With And Reading RC Equipment'

      Duane B

      Delete
  16. Hi Duane,

    I got the sketch to work - somehow... The servos (I used only two) move when I use the controls on the remote but they are constantly trembling.

    Any idea what this might be? I am using a GWS 4 channel pico receiver and found out the ppm signal.

    ReplyDelete
    Replies
    1. Hi, The servo check list is -

      1) Do you have a ground connection between your receiver, servos and Arduino. They must all share the same ground.

      2) Do you have separate power for the Servos, the Arduino can control many servos but cannot power even a single servo reliably, see this post for more - http://rcarduino.blogspot.com/2012/04/servo-problems-with-arduino-part-1.html

      3) Have you changes the RC_CHANNEL_IN_COUNT to 4 ?

      If you have addressed 1-3, is the tremble a tiny movement from time to time or a large powerful movement ?

      Duane B

      Delete
  17. I had common ground but not discrete power supply. Unfortunately I burned my receiver whe I connected different power sources (I hope I can repair it - maybe its just a cap to replace).

    The movement was a series of tiny shaking movements.

    ReplyDelete
  18. I repaired my receiver and hooked up a separate power supply. Now it is much better but still the servos acting a bit nervous and you can hear a humming souns all the time. I posted a video http://www.youtube.com/watch?v=0cEO8qT-Z8s - when you turn up the volume you can hear the background humming...

    ReplyDelete
    Replies
    1. Hi Kai,

      Glad you fixed the receiver, what did you use for power to kill it ? and what did you replace to fix it ?

      Duane B

      Delete
    2. Just the normal power suply - I inverted the lines :-(
      That killed a small cap - it was quite obvious to see wich one (it lighted up impressively). Luckily I found a source online to find out what kind of cap it was and luckily I had a similar type at home. So I exchanged it an its working again.

      Delete
  19. Hi Duane,

    did you rewrite your L293-Example withe the new library? Dis you publish ist?

    I am planing to use your code in a tiny rc submarine to control two pump motors (pwm only forward) and a diving tank motor forward reverse no pwm.

    I want to add some security functions later (water sensor, battery power and rc signal quality) and a led lamp.

    My Arduino skills are stil inferior so any help would be appreceated...

    ReplyDelete
    Replies
    1. Hi,
      It looks as if your refresh rate is too high, easy to fix. Send me your main sketch in a PM on the Arduino forum and I will have a look at it. As for the 293 example - it does not use servos so interrupt clashes are much less of a problem, for this reason there is very little advantage to using PPM or the fastlib.

      Duane B

      Delete
    2. Hi Duane,

      at this moment I didnt change anything from your sketch. But I am planning to use the 293 example in the end. If you think there is no gain in using ppm or fastlib I will stick to the original version.

      Delete
    3. Hi, The PPM/FastLib combination solves the problem of very slow interrupt functions causing glitches when you have to 1) Use pin change interrupts 2) Use the servo library. As your project does not need to drive servos, you will not see much benefit over the original 293 sketch here - http://rcarduino.blogspot.com/2012/05/interfacing-rc-channels-to-l293d-motor.html

      If you were using the servo library to drive ESCs or planned to use more than 2 or 3 channels then PPM and the fastlib have a lot of performance advantages.

      Duane B

      Delete
    4. I am planing to read 4 channels: 2 for the movement motors, one for the pump (only up/down/stop) and one for the light (on/off). For all 3 motors I can use the 293.

      I would like to add some functionality like desribed above. A - and I am planing to use an arduino mini pro (http://arduino.cc/en/Main/ArduinoBoardProMini) so performance may be an issue as well as memory...

      Delete
  20. The last few lines of the sketch attach an interrupt. For this to work with the library you will need to comment out a single line in RCArduinoFastLib.cpp

    void CRCArduinoPPMChannels::begin()
    {
    m_sOutOfSynchErrorCounter = 0;

    // comment out the following line of code in RCArduinoFastLib.cpp //attachInterrupt(0,CRCArduinoPPMChannels::INT0ISR,RISING);
    }

    Duane B

    ReplyDelete
  21. Hi Duane

    Sorry for the confusion, and thanks for the fast reply. Works like charm.
    How will that effect the lib in other sketches being commented out, sorry
    if this is a dumb question.

    Thanks again Stan

    ReplyDelete
  22. Hi Stan,
    There are two sections of code that try to attach an interrupt to INT0. The method which I suggested you should comment out in the library allows users to use the library without manually including the interrupt in thier sketch, however as you are using the example sketch, I have already include the following code in the sketch -

    ISR(INT0_vect) {
    CRCArduinoPPMChannels::INT0ISR();
    }

    This has the same effect as attachInterrupt, but works at a slightly lower level for a very small gain in performance.

    Duane B

    ReplyDelete
  23. Hi,
    I have updated the test sketch to remove the ISR Definition. The ISR is now (always was) attached in the CRCArduinoPPMChannels::begin(); function.

    Duane B

    ReplyDelete
  24. My serialPPM stream from the receiver is inverted compared with the pulse train shown above.

    Would I have to change anything to get the sample sketch to work?

    regards Peter

    ReplyDelete
  25. Hi Duane,
    I've successfully read the PPM stream coming from my transmitter. I'm trying to use that to run a small Tamiya motor similar to the one in your robot project using a motor shield from DF Robot. The motor shield just wants an analogWrite from 0-255 on pin 5. If I use a print statement, I can see the correct values coming from my receiver. However, when I use the analogWrite the motor just goes full speed. If I leave both the print and the analogWrite I can see it going above 255 and then it stops printing. If I limit the speed to 255 it does the same. Any suggestions? (I realize this is overkill for running a single motor, but I eventually would like to run a couple of motors and a few servos.)

    #include

    #define THROTTLE_OUT_PIN 5
    #define SERVO_THROTTLE 0
    #define SERVO_STEERING 1
    #define SERVO_FRAME_SPACE 2

    volatile uint32_t ulCounter = 0;

    void setup()
    {
    Serial.begin(115200);
    CRCArduinoPPMChannels::begin();
    }

    void loop()
    {
    uint16_t unThrottleIn = CRCArduinoPPMChannels::getChannel(SERVO_THROTTLE);
    if(unThrottleIn)
    {
    if (unThrottleIn >= 1500)
    {
    digitalWrite(4, HIGH);
    unThrottleIn = map(unThrottleIn,1500,2000,0,255);
    }
    else
    {
    digitalWrite(4, LOW);
    unThrottleIn = map(unThrottleIn,1000,1499,255,0);
    }
    //Serial.println(unThrottleIn);
    analogWrite(5, unThrottleIn);
    }

    uint16_t unSteeringIn = CRCArduinoPPMChannels::getChannel(SERVO_STEERING);
    if(unSteeringIn)
    {
    //CRCArduinoFastServos::writeMicroseconds(SERVO_STEERING,unSteeringIn);
    }
    }

    Thanks,
    Ken

    ReplyDelete
    Replies
    1. Hi,
      A very quick and easy test is to connect an LED and current limiting resistor to pin 5, does the LED get brighter as you move the throttle away from the center ? If so you might have a wiring issue with the motor, if not we will try something else.

      Let me know

      Duane B

      Delete
    2. Thanks Duane; appreciate the help. I tried what you suggested and the LED does get brighter and dimmer as I move the throttle. However, when the throttle is centered the LED flashes instead of staying off even though the print statement is reporting the output as 0 (or very close to it). I altered the sample code for the motor shield http://www.dfrobot.com/wiki/index.php?title=Arduino_Motor_Shield_(L298N)_(SKU:DRI0009) so it runs in one direction, stops for 3 seconds and then runs in the opposite direction and the motor works and the LED does not flash with the motor is stopped. I stripped out all I could from your sample code and this is what I'm running now.

      #include

      #define THROTTLE_OUT_PIN 5
      #define SERVO_THROTTLE 0
      #define SERVO_FRAME_SPACE 2

      volatile uint32_t ulCounter = 0;

      void setup()
      {
      Serial.begin(115200);

      CRCArduinoFastServos::attach(SERVO_THROTTLE,THROTTLE_OUT_PIN);
      CRCArduinoFastServos::setFrameSpaceA(SERVO_FRAME_SPACE,6*2000);
      CRCArduinoFastServos::begin();
      CRCArduinoPPMChannels::begin();
      }

      void loop()
      {
      uint16_t unThrottleIn = CRCArduinoPPMChannels::getChannel(SERVO_THROTTLE);
      if(unThrottleIn)
      {
      if (unThrottleIn >= 1500)
      {
      digitalWrite(4, HIGH);
      unThrottleIn = map(unThrottleIn,1500,2000,0,255);
      }
      else
      {
      digitalWrite(4, LOW);
      unThrottleIn = map(unThrottleIn,1000,1499,255,0);
      }
      Serial.println(unThrottleIn);
      analogWrite(5, unThrottleIn);
      }
      }

      Delete
    3. I also tried removing the motor shield and the analogWrite statement and the LED was still pulsing. Seems like something is sending output to pin 5 besides my code.

      Delete
    4. Hi,
      I cant see any reason for something else writting to pin 5, but just in case, try the built in LED on pin 13.

      Also make sure that you have RC_CHANNEL_IN_COUNT and RC_CHANNEL_OUT_COUNT set correctly as in the faq here - http://rcarduino.blogspot.com/2013/02/rcarduino-libraries-faq.html

      You can also use the library function

      CRCArduinoPPMChannels::getSynchErrorCounter

      To see if the library has found any errors in the PPM Stream - this would normally be a result of an inverted signal or the transmitter having more channels in the PPM than are output by the receiver.

      Try these and let me know what happens

      Duane B

      Delete
    5. When I output to the LED on pin 13 I get the same results; it pulses even with nothing connected to it (no motor shield, no receiver, etc.) and only running "uint16_t unThrottleIn = CRCArduinoPPMChannels::getChannel(SERVO_THROTTLE);" in my loop. I then reconnected everything, uncommented the code in my loop and set RC_CHANNEL_IN_COUNT to 9 as my radio is sending 9 channels and the receiver is picking them all up in ppm mode as confirmed on my oscilloscope. I set RC_CHANNEL_OUT_COUNT to 2 for the 1 motor plus 1 for the frame space. I also made this change CRCArduinoFastServos::setFrameSpaceA(SERVO_FRAME_SPACE,18000); I got 18000 from the formula in the FAQ: frame space = 20,000 - (2,000 * (RC_CHANNEL_OUT_COUNT - 1) ). Making these changes seems to have had an effect. At low speeds I can control the motor; making it go backwards and forwards. Once it gets to a certain speed though the motor just runs full speed for a while. Then for no apparent reason it stops and I again have control. Right now I have #define SERVO_FRAME_SPACE 9, but I'm not sure if that's correct or even matters. I tried to use CRCArduinoPPMChannels::getSynchErrorCounter, but am not sure if I'm doing it correctly. I tried setting a variable to that and then printing that and it was giving me mostly 0's with the occasional 1.

      Delete
  26. Hi,
    You have it almost right. The framespace is really a special channel type of channel that doesnt have an output signal. So to output a single channel you would set #define RC_CHANNEL_OUT_COUNT 2 // 1 for motor, 1 for frame space. This creates two channels numbered 0 and 1. To set the frame space we need to write to channel 1, so you should change the 9 to a 1 as below -

    #define SERVO_FRAME_SPACE 1

    I started a faq to try and better explain the thinking behind this, the main reason for leaving the framespace as an optional channel is to allow much higher refresh rates to equipment that supports these rates - most people are not using that type of equipment so I will soon add a helper function which will hide these details and allow everyone with standard equipment to get up and running straigh away.

    Duane B

    ReplyDelete
  27. Okay, I did that. I still get some jittering if I use a servo and if I use a motor and shield the motor will end up going full speed and seems to get stuck like that; sometimes for 20 seconds or so and sometimes it never recovers. When I use a serial print I can see the values of unThrottleIn sometimes going down to single digits and sometimes over 2000 on the other end. I tried the same setup without using PPM and got perfect results with both the motor and the servo.

    Outside of the code, the only difference between those 2 setups was the receiver. I have a Hitec Aurora 9 radio and they don't make a receiver with PPM output, so I bought a compatible receiver from Hobby King to use when I want the PPM stream. When plugging in the USB cable to power the Arduino I noticed the signal from the receiver on my oscilloscope seemed to get very noisy. I swapped out the USB for a wallwart to power my arduino and powered my receiver with a battery instead of my DC PSU. That seemed to nearly do the trick. I can now make the motor go both direction fairly reliably, but there is still some jitter and lag. I guess the HobbyKing receiver is more sensitive than the Hitec receiver. Maybe I'll try to mod my Hitec receiver.

    ReplyDelete
  28. I'm trying to get this code to read the PPM stream from a 6 channel Spektrum DX4e transmitter with an Arduino Uno. Ultimately I want to control a Blade MCPX helicopter, which has 4 servos and 2 ESCs (upgraded to brushless motors). The code compiled and uploaded just fine, but the output looks odd and stops after printing the following:
    3000
    3000
    3000
    3000
    Steering: 12000

    What modifications should I make in order to make it read at least 5 of the 6 channels? In the header file, I changed #define RC_CHANNEL_IN_COUNT to 5 and then to 6 and I still get the same output. In the .cpp file, I changed #include "arduino.h" to #include "Arduino.h" I soldered a wire to the Spektrum AR6115e microlite park flyer 6ch receiver's satellite's PPM output and connected that wire to the Uno's digital pin 2.

    ReplyDelete
  29. Hi,

    You do not mention if you also have a ground connection between the receiver and the Arduino, you will need one if you dont.

    The code above outputs servo signals rather than serial prints, but if you want to add a serial print for testing, add it inside the if(unSterringIn) block

    Duane B

    ReplyDelete
    Replies
    1. Yeah, right after I posted I realized I forgot to mention, I'm powering the receiver off 5v and ground from the Arduino, which is currently powered off my laptop's USB port and yes I added in the serial.println command that made it print out that steering value. I made the loop look like this:
      void loop() {
      // Pass the signals straight through -
      uint16_t unThrottleIn = CRCArduinoPPMChannels::getChannel(SERVO_THROTTLE);
      if(unThrottleIn) {
      //CRCArduinoFastServos::writeMicroseconds(SERVO_THROTTLE,unThrottleIn);
      Serial.print("Throttle: ");
      Serial.println(unThrottleIn);
      }
      uint16_t unSteeringIn = CRCArduinoPPMChannels::getChannel(SERVO_STEERING);
      if(unSteeringIn) {
      //CRCArduinoFastServos::writeMicroseconds(SERVO_STEERING,unSteeringIn);
      Serial.print("Steering: ");
      Serial.println(unSteeringIn);
      }
      uint16_t unAuxIn = CRCArduinoPPMChannels::getChannel(SERVO_AUX);
      if(unAuxIn) {
      //CRCArduinoFastServos::writeMicroseconds(SERVO_AUX,unAuxIn);
      Serial.print("Auxilary: ");
      Serial.println(unAuxIn);
      }
      }

      Delete
    2. Sorry to post a bunch of times, but I have made some progress. I realized that my brother told me the wrong pin to solder the wire to and changed it to the other pin. The original pin was V+ (3.3V). Now that I switched to the other pin, the output is better, but still obviously wrong:

      # Input Channels: 7
      # Output Channels: 4
      3000
      3000
      3000
      3000
      Steering: 12000
      Throttle: 25
      Steering: 36
      Auxilary: 11
      Throttle: 25
      Steering: 36
      Auxilary: 12
      Throttle: 25
      Steering: 33
      Auxilary: 15
      Throttle: 24
      Steering: 33
      Auxilary: 19

      .
      .
      .
      The values continue on forever and remain much lower than expected regardless of what I do with the transmitter. At this point I'm wondering if what I'm measuring here is actually a PPM stream (I have no oscilloscope or way of telling for sure) and I'm also wondering if it matters that the pulses are at 3.3V rather than 5V. I'm powering the receiver off of 5V, but the (satellite?) board attached to it is running at 3.3V, and based on my multimeter, I think the pulses are 3.3V as well. Does this mean that I need a logic level coverter? I have one nearby that I can use if necessary.
      Also, I have other code from RCArduino that simply uses an interrupt to detect a change on D2 and then outputs the pulse width and when I run that code on the six individual channels, it seems to work correctly, outputting values between 1000-2000 for each channel. The values seem appropriate for what I do to the transmitter, so I know there's nothing wrong with the transmitter or receiver...

      Delete
  30. Hi,
    I cannot find anything online which indicates that your receiver provides a PPM Output, can you send me a link to what you are using as a reference.

    Duane B

    ReplyDelete
  31. Yeah.. I decided last night it's most likely not PPM, and today I learned that it's definitely serial at a baud of around 115200, so I'm working on reading it as serial until we get another transmitter/receiver that is PPM. Sorry. Hopefully anyone having the same problem (like the previous poster, Jorge C.) will be able to skip all the headache by reading my posts at least. Here are some links for reading the Spektrum serial for anyone interested:

    http://arduino.cc/forum/index.php/topic,113523.0.html
    http://www.dogfight.no/2011/01/spectrum-receiver-satellite-to-arduino.html
    http://diydrones.ning.com/profiles/blog/show?id=705844%3ABlogPost%3A64228&page=2#comments

    Duane, feel free to delete my comments now if you want.. I'm sure I will end up using your PPM code soon on a new TX/RX.

    ReplyDelete
  32. Hey awesome tutorial. I have a question though:

    I'm using a FP-R148DP - 8 Channel Micro Receiver - PCM 1024, and since I'm no expert I don't really know where to pull off the PPM stream from it. Do you know where on the chip I can pull it from? Is it pin 1 like the others in the tutorial?

    Picture of the receiver: http://imageshack.us/a/img818/5632/photo4rzh.jpg

    Thanks

    ReplyDelete
    Replies
    1. Hi,
      The small chip appears to be a comparator so will not have anything to do with a PPM Signal. If you search for information about the large Futaba FP6302B chip you can find out whether Futaba has provided a PPM Output from this chip.

      Let us know what you find

      Duane B

      Delete
    2. Ok, I will save you the time, it looks as if your Rx uses PCM and as far as I can tell, there is no PPM signal available within the Rx.

      On the upside, its easy to read the individual channels like so -

      http://rcarduino.blogspot.ae/2012/04/how-to-read-multiple-rc-channels-draft.html

      Which Arduino are you using and how many channels in and out are you planning ?

      Duane B

      Delete
    3. Oh that makes sense. I'd forgotten that my Tx/Rx were PCM versus PPM. I'm dumb, haha.

      I have a nano, and I was hoping to read up to 8 (i'm trying to use my Rx and arduino along with some mosfets to act like a remotely controlled relay board). I was also hoping that I could have just one stream coming into the arduino, and have the arduino switch on/off ports depending on the incoming singals, however it looks like i'll have to read each channel individually...unless its possible to input the PCM stream and decipher that...? Do you know how well can an arduino deal with reading that many channels?

      I was also thinking about writing the code in C because I just took an embedded computing course using that and it'd probably be good practice...but that might be biting off a little more than I can chew right now...


      Thanks for the info btw.

      Delete
  33. This tutorial saved a lot of time for me. Thank you!

    I have a question though:

    I have three servos and my Arduino pro mini 3.3v sends them to serial, where a Raspberry pi reads them and prints them out. The three channel outputs range from about 550 to 960 in values, and react correctly to RC transmitter controls.

    However, when I connect the servos, they start a rhythmic movement and don't react to the control signals.

    Are the 550 to 960 values reasonable? I write them using e.g.

    if(rollIn) {
    CRCArduinoFastServos::writeMicroseconds(SERVO_ROLL, rollIn);
    }

    where rollIn is in that range.

    I have checked that the grounds to receiver, servos and arduino are all connected.

    ReplyDelete
    Replies
    1. Looking into RCArduinoFastLib.h helped. I changed RCARDUINO_SERIAL_SERVO_MIN and RCARDUINO_SERIAL_SERVO_MAX to that range.

      Now the servos react to the controls. However, there's still a rhythmic error movement in addition to the control movements. Any ideas what might be the cause?

      Thanks!

      Delete
    2. Its probably due to the frame space. Your servos are using a narrow pulse range so they probably operate at a higher frequency meaning you should use a shorted framespace - lots of people have used the library for this type of servo so let me know how many servos and or escs you intend to control and also what make and model they are and what make and model the transmitter and receiver are.

      The make and model will allow me to double check and make sure that I do not give you any incorrect advice.

      Duane.

      Delete
    3. Hi Duane,

      I'm controlling two Futaba S3010 servos and an esc (Rite Wing Brushless ESC).

      Teemu T

      Delete
    4. Receiver is FrSky TFRSP (with PPM) and transmitter is Futaba T7C.

      Delete
    5. The transmitter looks as if it outputs standard pulse widths 1000us to 2000us. Have you made any configuration changes or firmware updates to change this ?

      Do the transmitter and receiver control the servos correctly when they are plugged directly into the transmitter PWM/Channel outputs ?

      Duane.

      Delete
    6. Hi,
      There are also lots of reports of problems with the frsky receiver CPPM output if more than 6 channels are being used, this might be a problem for you in the future, but I do not think it is the reason for your narrow pulse widths.

      Let me know about the RX configuration and whether you can reliably control servos that are directly connected.

      Duane

      Delete
    7. Hi Duane,

      Yeah, direct control works fine.

      Regards,
      Teemu

      Delete
    8. I've been trying out different combinations of parameters, but my setup is not working properly yet. The servos react to controls as expected, but there's an erroneous rhythmic to and fro movement, @ about 4Hz.

      In RCArduinoFastLib.h I have the following parameters defined

      #define RC_CHANNEL_OUT_COUNT 4
      #define RCARDUINO_SERIAL_SERVO_MIN 500
      #define RCARDUINO_SERIAL_SERVO_MAX 1000
      #define RCARDUINO_SERIAL_SERVO_DEFAULT 750

      #define RC_CHANNEL_IN_COUNT 3
      #define MINIMUM_FRAME_SPACE 6000
      #define MAXIMUM_PULSE_SPACE 5000

      In my own source code I have

      #define SERVO_FRAME_SPACE 3
      CRCArduinoFastServos::setFrameSpaceA(SERVO_FRAME_SPACE,14000); #20000-3*2000=14000


      Without RCARDUINO_SERIAL_SERVO_MIN and RCARDUINO_SERIAL_SERVO_MAX set between 500-1000, it doesn't work at all.

      If the problem is due to higher frequency used, I wonder what setFrameSpaceA time parameter should be, and what should MINIMUM_FRAME_SPACE and MAXIMUM_PULSE_SPACE be?

      I've made no firmware upgrades to my RC gear.


      Delete
    9. Hi,
      I am not convinced that your PPM stream should be showing the pulse widths 500 to 1000us, so I have two suggestions -

      1) Add this function to your loop and output the result to serial -

      CRCArduinoPPMChannels::getSynchErrorCounter()

      This will tell you if there are any problems synchronising with the PPM Stream

      2) Use the code in this post to measure the output from one of the channel outputs on your receiver, this will confirm the pulse width we should expect in the PPM Stream

      http://rcarduino.blogspot.ae/2012/01/how-to-read-rc-receiver-with.html

      Let me know what you find

      Duane B

      Delete
    10. HI Duane,

      1) Prints zero when the loop runs. A lot of them.
      2) Prints 968 all the time.

      That 968 seems to indicate that the pulse widths are between the strange range of 500-1000, even when not read from PPM?

      Delete
    11. Ok, the zeros in 1) are good

      For 2) Did you try moving the controls, 968 could easily be the bottom of the range 1000-2000.

      Try 2) on each of the individual channel outputs and make a note of the range when you move the controls through thier full range.

      Duane

      Delete
    12. You're right. The values range from 968..2072 when read directly from channel output.

      Teemu

      Delete
  34. Hi, Nice Blog. I need to read a cPPM stream, change the channel order and output a new cPPM stream.

    Could you make an example?

    ReplyDelete
    Replies
    1. Thats very easy to do, I will make a new post that outputs PPM in the next few days, I will let you figure out how to read channel 1, write it to channel 2 etc, its very easy because the channel inputs and outputs are separate within the code so that you can read one value but output another - for example one that you read from a different channel.

      Duane B

      Delete
  35. Last week I was able to use the library and the PPM code; everything was straight forward and the servo responded to the transmitter commands. I dont know why but I can't get it to work again. I believe I changed something to the code when I first got it to work, but can't remember what it was.

    Right now I'm using the stock library and PPM code from above. I'm using an Arduino Mega 1280 and a VEX Transmitter / Receiver. I'm using the 5V from the Arduino to power the receiver and a separate usb cable to provide the 5V to the servo. The servo GND is shared with the Arduino and the PPM singal is connected to Pin D2. I can see the values in the serial window changing as I move channels 1 to 3 which means the PPM signal is being decoded, but the servo isn't moving. Any idea of why this might be happening? TIA

    ReplyDelete
  36. can you help me to find ppm sum pin in the wfly wfr04s receiver ? plz hellllllllllllp

    ReplyDelete
  37. Hi. I have been folllowing all your guides, learning a lot. Thanks for your work. Is the code on github the latest with ppm output modification? I'd like to do a simple ppm encoder.
    Best regards.
    Luciano

    ReplyDelete
  38. Hi Luciano,

    I am guess its you that I sent to code to today through Arduino forum, if not, message me and include your email address for me to send the folder.

    Duane B

    ReplyDelete
  39. Hi Duane

    I like your post very useful , could you please send me the library as well ? I have a hitec Rx that outputs PPM stream . And would really like to use a Arduino pro mini as a PPM " decoder" to servo outputs . I know this is a strange question

    ReplyDelete
  40. I WANT TO CREATE SOMETHING WIRELESS I DON'T KNOW ANYTHING ABOUT THIS BUT I GOT THE IDEA..... I WANT SOMEBODY TO CREATE THIS FOR ME I LIVE IN ATLANTA GA IF YOU CAN HELP I'LL PAY FOR THIS YOU CAN CONTAC ME AT 2266juan@gmil.com

    ReplyDelete
  41. Great post! I learned a lot from this page and I thank you. I have your example compiling and am now wiring up a load.

    ReplyDelete
  42. Hi Duane. I hope you're still checking this site..
    I loaded your sketch on a arduino pro mini. I have a multiplex servo that jitters a lot when connected to the output. I connected the scope to the arduino and noticed a refresh rate of 180Hz that's way to much for the poor servos and can damage them. Also the refresh rate for a given channel changes when you move other channels. that doesn't seem right. My question is, Is it possible to program a fixed refresh rate of 50Hz? i would like to output 8 channels.
    Thanks,

    João

    ReplyDelete
  43. I am trying to read a PCM signal but I am not having much luck. I changed RC_CHANNEL_IN_COUNT to 8 as I have an eight channel FrSky PCM with 18ms total frame and 300 microsecond stops..
    Do I need to change MINIMUM_FRAME_SPACE and MAXIMUM_PULSE_SPACE??

    Is the MINIMUM_FRAME_SPACE the extra frame space with all channels at max value
    8*(1.7ms + .3ms = 2ms) 16ms channel space. so 18ms frame - 16ms channels = 2ms frame space?

    Is MAXIMUM_PULSE_SPACE the space just one pulse? 1.7ms?

    Thanks for any input guys.

    ReplyDelete
  44. Do you know whether your signal is PCM or PPM, they are different to the extent that PPM Code will not read a PCM Stream. Please clarify which you have and then come back.

    Duane.

    ReplyDelete
  45. Hi Duane,

    First of all thank you so much for your great job!

    i'm working on it for several days now and still can't be succesful. I use a Graupner gr12 5 Channels receiver, already checked with an other program that i am able to catch the ppm stream on all channels.

    I use an arduino Mega 2560, checked that arduino/receiver/servos have common ground, power for receiver and servos come from external supply (when servos work they work fine).

    I first try to work with the sample code and with the given modifications, but was only able to make two servos/channels move(channels 3/4 of the receiver, servos pin 10/11).

    Being pretty noob, i supposed it came from the number of channels, and made the modifications following.

    The pinning:
    -receiver stream on pin 2
    -servos 1,2,3,4 and 5 on pins 10,11,12,13 and 14
    -common ground,

    RCArduinoFastlib modifications:

    "...// Change to set the number of servos/ESCs
    #define RC_CHANNEL_OUT_COUNT 6
    ...

    ...// Change to set the number of channels in PPM Input stream
    #define RC_CHANNEL_IN_COUNT 5
    // two ticks per us, 3000 us * 2 ticks = 6000 minimum frame space
    #define MINIMUM_FRAME_SPACE 7000
    #define MAXIMUM_PULSE_SPACE 4000
    ..."

    Program modification:

    "...// Assign your channel out pins
    #define THROTTLE_OUT_PIN 10
    #define STEERING_OUT_PIN 11
    #define AUX_OUT_PIN 12
    #define OTHER_OUT_PIN 13
    #define OTHER_OTHER_OUT_PIN 14


    // Assign servo indexes
    #define SERVO_THROTTLE 0
    #define SERVO_STEERING 1
    #define SERVO_AUX 2
    #define SERVO_OTHER 3
    #define SERVO_OTHER_OTHER 4

    #define SERVO_FRAME_SPACE 5

    volatile uint32_t ulCounter = 0;

    void setup()
    {
    Serial.begin(115200);

    // attach servo objects, these will generate the correct
    // pulses for driving Electronic speed controllers, servos or other devices
    // designed to interface directly with RC Receivers

    CRCArduinoFastServos::attach(SERVO_THROTTLE,THROTTLE_OUT_PIN);
    CRCArduinoFastServos::attach(SERVO_STEERING,STEERING_OUT_PIN);
    CRCArduinoFastServos::attach(SERVO_AUX,AUX_OUT_PIN);
    CRCArduinoFastServos::attach(SERVO_OTHER,OTHER_OUT_PIN);
    CRCArduinoFastServos::attach(SERVO_OTHER_OTHER,OTHER_OTHER_OUT_PIN);


    // lets set a standard rate of 50 Hz by setting a frame space of 10 * 2000 = 3 Servos + 7 times 2000
    CRCArduinoFastServos::setFrameSpaceA(SERVO_FRAME_SPACE,10000);

    CRCArduinoFastServos::begin();
    CRCArduinoPPMChannels::begin();
    }

    void loop()
    {
    // Pass the signals straight through -

    uint16_t unThrottleIn = CRCArduinoPPMChannels::getChannel(SERVO_THROTTLE);
    if(unThrottleIn)
    {
    CRCArduinoFastServos::writeMicroseconds(SERVO_THROTTLE,unThrottleIn);
    }

    uint16_t unSteeringIn = CRCArduinoPPMChannels::getChannel(SERVO_STEERING);
    if(unSteeringIn)
    {
    CRCArduinoFastServos::writeMicroseconds(SERVO_STEERING,unSteeringIn);
    }

    uint16_t unAuxIn = CRCArduinoPPMChannels::getChannel(SERVO_AUX);
    if(unAuxIn)
    {
    CRCArduinoFastServos::writeMicroseconds(SERVO_AUX,unAuxIn);
    }
    uint16_t unOtherIn = CRCArduinoPPMChannels::getChannel(SERVO_OTHER);
    if(unOtherIn)
    {
    CRCArduinoFastServos::writeMicroseconds(SERVO_OTHER,unOtherIn);
    }
    uint16_t unOtherOtherIn = CRCArduinoPPMChannels::getChannel(SERVO_OTHER_OTHER);
    if(unOtherOtherIn)
    {
    CRCArduinoFastServos::writeMicroseconds(SERVO_OTHER,unOtherOtherIn);
    }
    }..."

    Do you have an idea what my mistake is? sorry for the english, and thanks in advance for any clue!!

    Guillaume

    ReplyDelete
  46. ...Forgot to say what is happening in this configuration: after multiple frame space tryouts, best i can do is have the channel 3 and five working, other are not responding.

    thanks in advance,

    Guillaume

    ReplyDelete
  47. You do not say which Arduino you are using ?

    Duane B

    ReplyDelete
    Replies
    1. Oh sorry, I'm using an Arduino board Mega 2560, thanks for a such fast answer :-)

      Guillaume

      Delete
  48. Me again.. just saw my mistake at :"uint16_t unOtherOtherIn = CRCArduinoPPMChannels::getChannel(SERVO_OTHER_OTHER);
    if(unOtherOtherIn)
    {
    CRCArduinoFastServos::writeMicroseconds(SERVO_OTHER,unOtherOtherIn);", replaced by SERVO_OTHER_OTHER...

    Now I have channel 3and 4 working (servo on pins 10 and 11), but not the 3 other, plus if i change the pin for exemple from 10 to 8 it doesn't work anymore, should i choose specific pins on mega for servos?

    ReplyDelete
  49. Thank you for sharing this information this is very nice blog thank you for giving this info.
    I share your post link in my new site ,

    ReplyDelete
  50. Hi Duane,

    Is it possible to map a single channel on more than one output on the Arduino and switch between the two outputs using an aux switch on my transmitter? So, for example, with aux switch in position one, the channel one pulse is sent to pin 8 and when aux switch in position two channel one pulse is sent to pin 9...

    I got the code working without problem in it's stock form but I'm stuck with the best way to accomplish what I have above. I think the problem is that I'm assigning more than one pin to a single servo index. I'm pretty inexperienced with this, hoping you can shed some light on what my problem is. Here is how I have the code set up:
    #define F_ROLL_OUT_PIN 8
    #define F_PITCH_OUT_PIN 9
    #define F_THROTTLE_OUT_PIN 10
    #define F_YAW_OUT_PIN 11
    #define F_AUX2_OUT_PIN 12

    #define V_ROLL_OUT_PIN 3
    #define V_PITCH_OUT_PIN 4
    #define V_THROTTLE_OUT_PIN 5
    #define V_YAW_OUT_PIN 6
    #define V_AUX2_OUT_PIN 7

    #define F_SERVO_ROLL 0
    #define F_SERVO_PITCH 1
    #define F_SERVO_THROTTLE 2
    #define F_SERVO_YAW 3
    #define MODE_CHANGE 4
    #define F_SERVO_AUX2 5
    #define SERVO_FRAME_SPACE 6

    #define V_SERVO_ROLL 0
    #define V_SERVO_PITCH 1
    #define V_SERVO_THROTTLE 2
    #define V_SERVO_YAW 3
    #define V_SERVO_AUX2 5

    volatile uint32_t ulCounter = 0;

    void setup()
    {
    Serial.begin(115200);

    CRCArduinoFastServos::attach(F_SERVO_ROLL,F_ROLL_OUT_PIN);
    CRCArduinoFastServos::attach(F_SERVO_PITCH,F_PITCH_OUT_PIN);
    CRCArduinoFastServos::attach(F_SERVO_THROTTLE,F_THROTTLE_OUT_PIN);
    CRCArduinoFastServos::attach(F_SERVO_YAW,F_YAW_OUT_PIN);
    CRCArduinoFastServos::attach(F_SERVO_AUX2,F_AUX2_OUT_PIN);

    CRCArduinoFastServos::attach(V_SERVO_ROLL,V_ROLL_OUT_PIN);
    CRCArduinoFastServos::attach(V_SERVO_PITCH,V_PITCH_OUT_PIN);
    CRCArduinoFastServos::attach(V_SERVO_THROTTLE,V_THROTTLE_OUT_PIN);
    CRCArduinoFastServos::attach(V_SERVO_YAW,V_YAW_OUT_PIN);
    CRCArduinoFastServos::attach(V_SERVO_AUX2,V_AUX2_OUT_PIN);

    CRCArduinoFastServos::setFrameSpaceA(SERVO_FRAME_SPACE,6*2000);

    CRCArduinoFastServos::begin();
    CRCArduinoPPMChannels::begin();
    }

    void loop()
    {

    uint16_t unModeChange = CRCArduinoPPMChannels::getChannel(MODE_CHANGE);
    if(unModeChange > 1500)
    {

    uint16_t unRollIn = CRCArduinoPPMChannels::getChannel(F_SERVO_ROLL);
    if(unRollIn)
    {
    CRCArduinoFastServos::writeMicroseconds(F_SERVO_ROLL,unRollIn);
    }

    uint16_t unPitchIn = CRCArduinoPPMChannels::getChannel(F_SERVO_PITCH);
    if(unPitchIn)
    {
    CRCArduinoFastServos::writeMicroseconds(F_SERVO_PITCH,unPitchIn);
    }

    uint16_t unThrottleIn = CRCArduinoPPMChannels::getChannel(F_SERVO_THROTTLE);
    if(unThrottleIn)
    {
    CRCArduinoFastServos::writeMicroseconds(F_SERVO_THROTTLE,unThrottleIn);
    }

    uint16_t unYawIn = CRCArduinoPPMChannels::getChannel(F_SERVO_YAW);
    if(unYawIn)
    {
    CRCArduinoFastServos::writeMicroseconds(F_SERVO_YAW,unYawIn);
    }

    uint16_t unAux2In = CRCArduinoPPMChannels::getChannel(F_SERVO_AUX2);
    if(unAux2In)
    {
    CRCArduinoFastServos::writeMicroseconds(F_SERVO_AUX2,unAux2In);
    }
    }
    else
    {
    uint16_t unRollIn = CRCArduinoPPMChannels::getChannel(V_SERVO_ROLL);
    if(unRollIn)
    {
    CRCArduinoFastServos::writeMicroseconds(V_SERVO_ROLL,unRollIn);
    }

    uint16_t unPitchIn = CRCArduinoPPMChannels::getChannel(V_SERVO_PITCH);
    if(unPitchIn)
    {
    CRCArduinoFastServos::writeMicroseconds(V_SERVO_PITCH,unPitchIn);
    }

    uint16_t unThrottleIn = CRCArduinoPPMChannels::getChannel(V_SERVO_THROTTLE);
    if(unThrottleIn)
    {
    CRCArduinoFastServos::writeMicroseconds(V_SERVO_THROTTLE,unThrottleIn);
    }

    uint16_t unYawIn = CRCArduinoPPMChannels::getChannel(V_SERVO_YAW);
    if(unYawIn)
    {
    CRCArduinoFastServos::writeMicroseconds(V_SERVO_YAW,unYawIn);
    }

    uint16_t unAux2In = CRCArduinoPPMChannels::getChannel(V_SERVO_AUX2);
    if(unAux2In)
    {
    CRCArduinoFastServos::writeMicroseconds(V_SERVO_AUX2,unAux2In);
    }
    }
    }

    Thanks, hope I'm not missing something obvious...
    Andrew

    ReplyDelete
    Replies
    1. Lots of ways to do that, but to suggest the best way - why do you want to do this ?

      Duane.

      Delete
    2. I working on a project to develop an aircraft like the one seen here: http://images.gizmag.com/hero/hquav.JPG

      A type of hybrid quad copter and fixed wing aircraft capable of vertical take off/landing/hovering with the benefit of extended range forward flight. I plan on implementing this by using your code on an Arduino which is connected to two separate flight controllers. One FC for forward flight and one for hovering. This is just the first step, I wanted to see if I could get six channels of my transmitter to transfer control from one flight controller to the other via an aux switch. In the future, I'll be adding more code to transition from one mode to the next (start flying forward, once x speed is met, begin slowing down quad motors...etc). As the code is now, it always defaults to the else statement. I have used serial monitor to debug the aux switch and I can see that it is changing between a value of 1000 and 2000...

      Thanks for the response!

      Delete
  51. Its almost as if the library is ready made for what you want. If you look in the header file you will see this -

    // COMMENT OR UNCOMMENT THIS LINE TO ENABLE THE SECOND BANK OF SERVOS
    //#define MORE_SERVOS_PLEASE 1

    remove the comments and you will enable a second independent bank of servos.

    When you need to control smooth the transition from one bank to another, the library will give you full control of both.

    Duane.

    ReplyDelete
    Replies
    1. Oh, great! I didn't even think about that. Thanks for the heads up, I'll give it a shot.

      Delete
    2. I had a look at the second bank option and I don't think its what I need. My transmitter never outputs no more than 9 channels. I need six of those to control either the aircraft part of the craft or the quadcopter part (not at the same time). What I need is to be able to switch what pins those nine channels go to. In other words, with an aux switch in one position, use channels one through six coming from my transmitter to control pins three through eight on the arduino. With the aux switch in the other position, have those same six channels control pins nine through 13 on the arduino. Hope this makes sense. If I'm not mistaken, in my case, enabling the MORE_SERVOS_PLEASE option would cause the arduino to expect 12 channels (in my case) to be coming from the transmitter. Am I correct on this or am I missing something?

      Thanks!

      Delete
    3. Yes, I think your missing something. The 20 channels that you can output do not know or care how many inputs there are. They will output whatever signal you tell them to which could be a default signal (1500), something from your transmitter or something from an internal control algorithm. Are there two sets of Servos/ESCs in your project - so 12 outputs ? or do you just want to treat the same six outputs in different ways based on Aux ?

      Duane

      Delete
    4. Oh, ok... Wouldn't be the first time I missed something! Yes, there are two sets of outputs, ie, 12 outputs total. Do you have an example sketch posted somewhere demonstrating the use of the second bank of servos?

      Delete
    5. Just thought I'd check in and let you know how things panned out. It took some time, but I got my problem figured out. I didn't need to use the second bank of servos option. All I had to do was put the attach statements under the conditional statements in the main loop. This way a given input pulse will attach to one of two output pins based on the aux switch position. I also improved reliability of the switching by using constrain and map to narrow down the values. Thanks for posting this library, it's turning out to be very useful! If you are interested in my code, I can post it.

      Delete
  52. hello, i was curious about PPM/PWM (due to CNC trying to investigate use of ESC and the 3phased motors)

    doesnt each "pulse bit" consists of a minimum 1ms high? to denote a channel? and if in theory 50Hz is the standard framerate, "analogue" PPMs are limited to 10 channels? or there is a 3-4ms standard blanking in all PPM streams as a international "protocol"? and that effectively allows modern PPM stream to have more than 10 channel?

    i am trying to figure out if a high "bit" width is an actual control width OR is it the rise edge to rise edge?

    ReplyDelete
  53. PPM Is a very old protocol that had the advantage that it could easily be decoded with very in expensive components. The standard pulse widths limit the protocol to about 10 channels. There are narrow versions where the pulse width is divided by 2, but you would need receiving equipment which supported this non standard version.
    For your second question, its leading edge to leading edge, the actual pulse width in my libraries is a few micro seconds.

    Duane

    ReplyDelete