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 -
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.
Duane B
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
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 */
// 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



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?
ReplyDeleteI 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!
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.
ReplyDeleteMaybe 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
Hi
ReplyDeleteTrying 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);
}
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.
ReplyDeleteLet 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
Thanks for the confirmation and I should have said that I am prototyping with the UNO.
ReplyDeletePlanning 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
has any one else received this error when compiling the code?
ReplyDeletecore.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?
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.
DeleteI 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}
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 .
Deletewell not exactly mine says __vector_2 in the top section
Deletecore.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
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.
ReplyDeleteDuane B
I was wondering if the attachInterrupt line in this function in RCArduinoFastLib.cpp
ReplyDeletevoid 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();
}
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.
ReplyDeleteDuane B
Hi Duane,
ReplyDeleteI'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.
Hi Jorge,
ReplyDeleteHow 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
Hi Duane,
Delete"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
Hi JC,
DeleteNot 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
Hi,
ReplyDeleteYour 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
Hi Duane
ReplyDeleteI 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.
Hi,
DeleteI 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
Hi Duane,
ReplyDeleteThis 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
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.
ReplyDeleteDuane B
Thanks Duane,
DeleteI found the more straightforward approach.
Regards
Andrew
Hi,
DeleteYou 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
Hi Duane,
ReplyDeleteI 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.
Hi, The servo check list is -
Delete1) 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
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).
ReplyDeleteThe movement was a series of tiny shaking movements.
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...
ReplyDeleteHi Kai,
DeleteGlad you fixed the receiver, what did you use for power to kill it ? and what did you replace to fix it ?
Duane B
Just the normal power suply - I inverted the lines :-(
DeleteThat 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.
Hi Duane,
ReplyDeletedid 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...
Hi,
DeleteIt 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
Hi Duane,
Deleteat 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.
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
DeleteIf 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
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.
DeleteI 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...
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
ReplyDeletevoid CRCArduinoPPMChannels::begin()
{
m_sOutOfSynchErrorCounter = 0;
// comment out the following line of code in RCArduinoFastLib.cpp //attachInterrupt(0,CRCArduinoPPMChannels::INT0ISR,RISING);
}
Duane B
Hi Duane
ReplyDeleteSorry 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
Hi Stan,
ReplyDeleteThere 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
Hi,
ReplyDeleteI have updated the test sketch to remove the ISR Definition. The ISR is now (always was) attached in the CRCArduinoPPMChannels::begin(); function.
Duane B
My serialPPM stream from the receiver is inverted compared with the pulse train shown above.
ReplyDeleteWould I have to change anything to get the sample sketch to work?
regards Peter
Hi Duane,
ReplyDeleteI'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
Hi,
DeleteA 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
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.
Delete#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);
}
}
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.
DeleteHi,
DeleteI 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
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.
DeleteHi,
ReplyDeleteYou 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
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.
ReplyDeleteOutside 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.
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:
ReplyDelete3000
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.
Hi,
ReplyDeleteYou 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
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:
Deletevoid 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);
}
}
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:
Delete# 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...
Hi,
ReplyDeleteI 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
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:
ReplyDeletehttp://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.
Hey awesome tutorial. I have a question though:
ReplyDeleteI'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
Hi,
DeleteThe 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
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.
DeleteOn 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
Oh that makes sense. I'd forgotten that my Tx/Rx were PCM versus PPM. I'm dumb, haha.
DeleteI 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.