First, I want to thank you for making this post. I have a few remote-controlled cars/boats/helicopters, and I've often wondered how the remote control works. :)
To answer your question - and because I was too lazy to work out how to connect to an actual controller - I made a "remote simulator" like this:
// PPM generator
const byte SIGNAL_PIN = 3;
const int SIGNAL_COUNT = 6;
const unsigned long PULSE_WIDTH = 300;
const int widths [SIGNAL_COUNT] = { 600, 2000, 1000, 1500, 2200, 2400 };
void setup ()
{
pinMode (SIGNAL_PIN, OUTPUT);
} // end of setup
void loop ()
{
delay (20); // blank time
digitalWrite (SIGNAL_PIN, HIGH); // start
delayMicroseconds (PULSE_WIDTH); // pulse width
digitalWrite (SIGNAL_PIN, LOW); // end of start pulse
for (byte i = 0; i < SIGNAL_COUNT; i++)
{
delayMicroseconds (widths [i] - PULSE_WIDTH);
digitalWrite (SIGNAL_PIN, HIGH); // start
delayMicroseconds (PULSE_WIDTH); // pulse width
digitalWrite (SIGNAL_PIN, LOW); // end of start pulse
}
delay (1000);
} // end of loop
This is intended to generate a series of pulses (six in this case) which might be generated by some fancy toy fun device.
Output from it:
As you can see from the cursor, it is doing what it is bid. The pulse width shown by the scope cursor is indeed 2000 µs, as per the table of widths.
Note: Output on pin 3 on the Uno.
Now we need a decoder. So let's use interrupts:
const byte SIGNAL_PIN = 2;
const int SIGNAL_COUNT = 6;
volatile unsigned long widths [SIGNAL_COUNT + 1];
volatile byte count;
unsigned long lastPulse;
// ISR
void gotPulse ()
{
unsigned long now = micros ();
// a long gap means we start again
if ((now - lastPulse) >= 25000)
count = 0;
lastPulse = now;
if (count >= (SIGNAL_COUNT + 1))
return;
widths [count++] = now;
}
void setup ()
{
Serial.begin (115200);
Serial.println ();
attachInterrupt (0, gotPulse, RISING);
EIFR = bit (INTF0); // clear flag for interrupt 0
} // end of setup
void loop ()
{
if (count >= SIGNAL_COUNT)
{
Serial.println (F("Got sequence."));
for (int i = 1; i < SIGNAL_COUNT + 1; i++)
{
Serial.print ("Channel ");
Serial.print (i);
Serial.print (" = ");
Serial.println (widths [i] - widths [i - 1]);
}
count = 0;
}
} // end of loop
This uses an interrupt (on pin 2 on the Uno) on the rising edge to detect the start of the pulses. It stores them, and then works out their widths later.
Results?
Got sequence.
Channel 1 = 612
Channel 2 = 2020
Channel 3 = 1016
Channel 4 = 1512
Channel 5 = 2220
Channel 6 = 2428
Got sequence.
Channel 1 = 612
Channel 2 = 2020
Channel 3 = 1016
Channel 4 = 1512
Channel 5 = 2220
Channel 6 = 2424
Got sequence.
Channel 1 = 616
Channel 2 = 2020
Channel 3 = 1012
Channel 4 = 1516
Channel 5 = 2220
Channel 6 = 2424
Got sequence.
Channel 1 = 616
Channel 2 = 2020
Channel 3 = 1012
Channel 4 = 1516
Channel 5 = 2216
Channel 6 = 2432
Highly consistent results, with a small error factor of around 16 µs compared to 600, 2000, 1000, 1500, 2200, 2400