35,453

40

19

RC transmitter is very well suitable for the projects requires a wireless link as it has encrypted link with good range.

All RC receiver is made to drive servo motors. so there are 3 pins for each channel: ground, Vcc, and signal. here special kind of signal is used. It sends pulses at some interval. When servo receives 1.5ms pulse it sets to 90 degrees and by varying this value from 1-2ms servo goes to minimum and maximum value.
(In above photo a 6 channel receiver is shown with PPM pin)

So easy method is to measuring pulse width on each pin and map that data as per requirement. but here problem is that:

• For each channel of the receiver, we need to connect a wire to Arduino pin.
• this not only requires lots of connection but also consume lots of pins on an Arduino.
• Most of Arduino just has 2 interrupt pin so if we uses more than 2 channel read it add some delay to our code which may be problematic for some application.

To solve this problem many receivers comes with an extra pin called PPM. this PPM pin transmits data of all channel in a single signal.

## Step 1: How PPM Pin Signal Composed:

This signal consists of data packets separated by blank space.
Here space in between peaks represents the value of the channel. in this case, I have used 6 channel receiver so there are 7 pulses.

So in our code first, we need to detect separation space and then start recording data from the pulses for each channel.

As can be seen in the second image,

all data is separated with approx 11500 microseconds of time. than 6 values are for each channel.

## Step 2: Making and Using Code:

a=micros(); c=a-b; b=a; x[i]=c; i=i+1;

if(i==15){for(int j=0;j<15;j++) {ch1[j]=x[j];} i=0; } }

this part runs on interrupt pin and take 15 time values and store it in array.

this part look for any space which is higher than 10000microsecond, in our case it detects separation space and as it detects that space code moves to next section.

after this space next 6 values are for each channel position it is stored in array named ch[ channel no],
here ch, ch, ch, ch, ch, ch represents value of each chanel.

## Step 3: Finally...

As we are using PPM signal with interrupt

• This code do not add delay to our main functions
• It consumes just 1 pin from Arduino

## 3 People Made This Project!

• • • ## Recommendations

Abhilash,

I hope you are still monitoring this post.

Maybe you can help me with code that simulates the joysticks
of a FlySky i6 transmitter.

That is to say, to connect the Arduino to the PPMin pin of
the transmitter (on the training port on the back of the transmitter) and have

Thank you,

Ron K

Hi all,
I used this example with a :
- 6 channel AR6100 spektrum receiver
- 1 nano as my PPM encoder ( see this post: https://oscarliang.com/build-pwm-ppm-converter-arduino-2-4ghz-receiver/)
- 1 ATmega to read my PPM from D10 on the Nano

I was facing issues initially as my channel 2 from receiver was hooked to TX0 (which is supposedly D1 according to https://www.mouser.com/pdfdocs/Gravitech_Arduino_Nano3_0.pdf)
The D1 input on my nano was not reading the input from my receiver correctly.

I therefore tweaked the code to read from 8 channels instead and hooked my 6 ch receiver from D2 to D7 -> no gaps in my pins! :).
I had to weak some of the code to make it work though.

---------------START---------------
// Changeset:
// - Tweaked to work with Arduino Nano since D1/TX pin is not reading input.
// - For 6 ch receiver, just hook receivers from D2 to D7
// - line 8 : ch1 array size changed from 15 to 14, ch array size changed from 7 to 9.
// - line 53 : increased for loop limit from 6 to 8
unsigned long int a,b,c;
int x,ch1,ch,i;
//specifing arrays and variables to store values
void setup() {
Serial.begin(9600);
pinMode(2, INPUT_PULLUP);
// enabling interrupt at pin 2
}
void loop() {
Serial.print("Unused");Serial.print("\t");
Serial.print("Unused");Serial.print("\t");
Serial.print("THRO");Serial.print("\t");
Serial.print("AILE");Serial.print("\t");
Serial.print("ELEV");Serial.print("\t");
Serial.print("RUDD");Serial.print("\t");
Serial.print("GEAR");Serial.print("\t");
Serial.print("AUX1");Serial.print("\n");
Serial.print(ch);Serial.print("\t");
Serial.print(ch);Serial.print("\t");
Serial.print(ch);Serial.print("\t");
Serial.print(ch);Serial.print("\t");
Serial.print(ch);Serial.print("\t");
Serial.print(ch);Serial.print("\t");
Serial.print(ch);Serial.print("\t");
Serial.print(ch);Serial.print("\n");
delay(800);
}
//this code reads value from RC reciever from PPM pin (Pin 2 or 3)
//this code gives channel values from 0-1000 values
// -: ABHILASH :- //
a=micros(); //store time value a when pin value falling
c=a-b; //calculating time inbetween two peaks
b=a; //
x[i]=c; //storing 15 value in array
i=i+1; if(i==15){for(int j=0;j<15;j++) {ch1[j]=x[j];}
i=0;}}//copy store all values from temporary array another array after 15 reading
int i,j,k=0;
for(k=14;k>-1;k--){if(ch1[k]>10000){j=k;}} //detecting separation space 10000us in that another array
for(i=1;i<=8;i++){ch[i]=(ch1[i+j]-1000);}} //assign 8 channel values after separation space
---------------END---------------

I have posted what I know since it may help someone else but I'm also curious why did TX0 not read the input as intended by the PPM encoder tutorial?

Cheers,
Wayne

Hi I used this code with a fs-i6x radio and a fs-iA6b reciever. there was a lot of noise in the code and a delay and inaccurate readings, so I added some one milisecond delays. Here is the modified code:

unsigned long int a,b,c;
int x,ch1,ch,i;
//specifing arrays and variables to store values
void setup() {
Serial.begin(9600);
pinMode(2, INPUT_PULLUP);
// enabling interrupt at pin 2
}
void loop() {
Serial.print(ch);Serial.print("\t");
Serial.print(ch);Serial.print("\t");
Serial.print(ch);Serial.print("\t");
Serial.print(ch);Serial.print("\t");
Serial.print(ch);Serial.print("\t");
Serial.print(ch);Serial.print("\n");
delay(100);
}
//this code reads value from RC reciever from PPM pin (Pin 2 or 3)
//this code gives channel values from 0-1000 values
// -: ABHILASH :- //
a=micros(); //store time value a when pin value falling
c=a-b; //calculating time inbetween two peaks
b=a; //
x[i]=c; //storing 15 value in array
i=i+1;
if(i==15){
for(int j=0;j<15;j++){
ch1[j]=x[j];
delay(1);
}
i=0;}}//copy store all values from temporary array another array after 15 reading
int i,j,k=0;
for(k=14;k>-1;k--){//k = 14
if(ch1[k]>10000){
j=k;
delay(1);}}//detecting separation space 10000us in that another array //delay
for(i=1;i<=6;i++){ //i = 6
ch[i]=(ch1[i+j]-1000);
delay(1);}}
//assign 6 channel values after separation

The reading were much better after the delays but the problem was for the second to last channel, it was completely irresponsive. It only read -1000. Also, the last channel read the negative inverse values of the first values (so if first ch = 0, the lastlast ch = -1000).

Is there a way to fix this problem? Maybe by adding 2 more channels?

Thanks a lot! Good idea! I optimised it a litte. Maybe this is interesting for someone else:

#define CHANNEL_AMOUNT 8
#define DETECTION_SPACE 2500
#define METHOD RISING

int ch[CHANNEL_AMOUNT + 1];

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

void loop()
{
ppm_write();
}

void ppm_write()
{
static unsigned long int t;
if (millis() - t < 100)
return 0;
for (byte i = 0; i < CHANNEL_AMOUNT + 1; i++)
{
Serial.print(ch[i]);
Serial.print("\t");
}
Serial.print("\n");
t = millis();
}

void ppm_interrupt()
{
static byte i;
static unsigned long int t_old;
unsigned long int t = micros(); //store time value a when pin value falling/rising
unsigned long int dt = t - t_old; //calculating time inbetween two peaks
t_old = t;

if ((dt > DETECTION_SPACE) || (i > CHANNEL_AMOUNT))
{
i = 0;
}
ch[i++] = dt;
}

a,b and c is used to measure the spacing between two peaks in term of time.

when a new data comes we store it in "b" (last data already there as old data named: "a")
than take difference of "a" and "b" which is spacing in between two peaks.
now as we have used latest data it is not new data (which we store as "b"), so we update old data. and make value of "a" equal to "b".
now we again take new reading store it as "b".

...

..
so this process keep happens hundred of times per second.

Thanks for the guide it's great, although I couldn't get it to work.

I'm using Flysky TGY-iA6C RX - https://hobbyking.com/en_us/turnigy-ia6c-ppm-sbus-... (PPM mode)

I tested first the pulseIn(3,HIGH) * had to set it to HIGH in order to get values, and it works fine, my separator is >6000 && < 9000.

When trying to use your full code example I only get -1000 values

Looking back at pulseIn values I noticed the number of values between separators change to somewhere between 8,9 even 12, so I don't know what's going on.

Is there any way to solve or address that ? thanks :)

hi...

Can please confirm that signal from ppm pin goes to any interrupt enable pin ..

if that is ok. go to read_me function and check the output for variable "c". as mentioned in the code it is spacing between two peaks and should be similar spacing and channel value as data you capture using pulseIn function. if data in c is right than there may be some problem in code.

hi...
My receiver (of 6 channels) sends PPM for 8 channel (out of 2 are meaningless). I have chosen 15 just to make coding easy. so that, in any case, there is one set of 8 values which represents a value of each channel.
for an example
(here each number represents a channel number)
2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
7 8 1 2 3 4 5 6 7 8 1 2 3 4 5
5 6 7 8 1 2 3 4 5 6 7 8 1 2 3
8 1 2 3 4 5 6 7 8 1 2 3 4 5 6
here we can just pick 8 value after channel 1

if you use only 8 values it will become
6 7 8 1 2 3 4 5
1 2 3 4 5 6 7 8
8 1 2 3 4 5 6 7
so, in this case, we need to make rearrangement which is also not very difficult but I preferred easier approach.

hope now you are clear about it. If you have any query let me know.

Yes, It should work.
if it not works, just check signal and do modification in code as per separation (step 1).

If you don't have any problem with a delay in the reading channel (around 15milliseconds/channel) or do not wont to purchase PPM encoder you can directly use the pulseIn function for each channel.