DIY: Receiving the PPM signals from RC receivers

You only need the PPM input, not seven discrete I/O inputs if I’m following this.

Kurt is right on the serial output stream, more work to input if input is software driven. Hardware I2C or SPI would probably be better.

Should probably “sync” any output to the 20mS PPM input.

Could interrupt driven PPI input be “interleaved” into an I2C or SPI output? They are the masters, they can determine the output rate (might have answered the previous “sync” statement.

Alan KM6VV

Alan,

Well the way I have it now is Receiver --> PPM converter “that Atiny 2313 device” then Kurts assembly code on the phoenix… Which I still have not tested quite yet! as I’m at work and other things came up last night.

However.

My idea is a plug and play device

Receiver 7 wires to Arduino Nano - > i2c/spi whatever format is agreeable… or maybe all formats at once!? at different pins if it does not slow down the cpu… Honestly 20ms per output is 50 outputs per second, if this went down to 30 or even 20 it would still be quite usable! my three cents :slight_smile: … 1 cent better then the competition!

–Aaron

If the receiver already splits up the PPM, why do you need the Atiny to do it? (I must be missing something). Except for the additional pins, (or just use the PPM), an interrupt driven decode routine could run in the background of the main uP. No serial to read!

I suppose if you don’t need the extra I/O lines, then 7 in would be OK. Then you don’t need the PPM line! but simply running a counter and using the interrupt to “parse off” the individual pulse widths might be easier then trying to time 7 INDIVIDUAL pulse I/O inputs. But then they probably come in sequential, so no problem to then read one at a time…

An AtMega 328 chip would be an easy replacement, doesn’t need many support parts, and can be programmed on a UNO.

Comments?

Alan KM6VV

His receiver splits up the signals as you mentioned. You can simply use pulsin to read in the pulses, but you can not do it in one pass, as even if they were just sequential, they come to fast to exit the first one and enter the 2nd one before the 2nd pulse started. You can do it typically in 2 complete RC passes, depending on when you start your input could take 3… So for every pass of reading the data it could eat up something like 60ms, during which time nothing else is happening in your main code. I wrote an assembly language version a long time ago that could do it in one pass, which worked fine for the Hitec and spectrum receivers were things were sequential (not necessarily 1,2,3,4,5,6…), but no overlap. Thing comes along the Futaba, and it turns out some of these signals overlap, which screwed up the code.

Then later, I did the hacking of the Hitec receiver, which intercepted the clock signal going to the chip generating the pulses. This allows you to get all of the information on one IO pin, which is where the this whole thread comes along. Aaron then tries out a new board, that outputs a signal that looks like it is inverted from my hacked receiver. I think he might be able to use that code or the basic version of it and see if it gets him anywhere. Still have not heard anything back on that…

Now if I were to receive the RC data on an Arduino or other Atmega based board, I would probably simply use the Pin Change interrupt for the 6 or 7 pins and be done with it. In fact here is an example of how it has been done: arduino.cc/playground/Code/ReadReceiver

Kurt

Kurt. I will test this soon and provide you with what works… I wish you had skype or something as we could communicate a bit quicker.

Alan, Yes that chip looks like it would be fantastic! and at only $4.95 its a cheep CHEEP solution, this might be better then the Arduino Nano… This would be easy to build and put on a through hole circuit board and attach a bunch of pins to it… As for coding for this is there a solution I could potentially start to poke around with? perhaps make an led blink?
I have a spare 2313 handy not sure if the code is the same thing… I’d love to help in some form or another I can certainly build a bunch of these and ship them out if people have requests for them… maybe if we beta something lynxmotion will sell the kits pre programmed…?? any thought to this… The cost to build this device would be sub 10 bucks each… Im thinking maybe around $15-20 per if a pcb was etched and it looked all professional?

I’m jumping the gun a bit… anyone have any thoughts?

Hi Kurt,

I’m looking at the ReadReceiver example, but so far the TimerOne lib is not compiling. A good example of using the pin change interrupts, as you said.

Could be a candidate, if the compile errors can be resolved.

I know why he flips back and forth between rising/falling edges, maybe he needed to do that for the particular XCVR he choose? What was the “overlap” you mentioned, and on which XCVR(s)? I’m thinking of getting the Futaba 7C.

Alan KM6VV

Alan,

This was Kurts post


This is My photo

This is a photo of the PPM output of that Atiny2313 device…

You know come to think about it, if you want me to program one for you and build you the same circuit I did for my t7c I can do that for you! I have some spare parts laying around.

Should I pick up an Atmega328? Would love to learn this coding just need some direction as to where to start.
I think that chip as a base product would be pretty slick as its cheep, just need an external crystal attached to run it at 20mhz are there better options? If we could simply get a really fast chip and have pins output every type of data to satisfy everyone’s needs that seems like it would work well right?

–Aaron

EDIT – you know come to think about it the Atiny2313 also runs at the same speed with an external crystal… its 2k of program space vs 32k but really is this code more then 2k??? does not seem like it… the assembly code Jorg provided was 328bytes… SO not sure if C code is more bloated? or if on something like this if it makes a difference… anyway both of you guys would know more then me… I just cook up ideas!

The 2313 is probably fine. 168’s and 328’s are on the Arduino boards, easy to use if you want C, or go for BASIC on the BB II. Each can now be run as a stand-alone chip.

Thanks for the offer, I need a 7C R/C first.

Do you want to work in BASIC or C?

So both you and Kurt have 7Cs? I can’t see any way for overlap. So, every positive going edge could be used to advance a state, record the count, and generate a set of counts, the deltas of which represent the needed mS pulse widths.

Alan KM6VV

Hi guys,

Personally, I would try to simply use some currently built Arduino boards… Could work with the Uno or the Botboarduino or the like. If you want a small size one, you can always get one of the Sparkfun ones like: store.nkcelectronics.com/arduino … 28516.html (Can get from sparkfun as well, but used the NKCElectronics link as the sell lots of different Arduinos…).

Note: I don’t have a futaba remote either and probably will not pick one up… I do have a Hitec 6ch and the Spectrum pieces from the first DIY… If I were to pick up another transmitter, it would probably be one that Lynxmotion sells… As you can probably tell, I am more interested in the XBee solutions as I like the bidirectional communications.

However I am willing to help out. If we were to code this properly on the Arduino, I think it would work for most any type receiver… As you can see from Aarons post of the trace of a futaba receiver (I think that came originally from Zenta…) some of the signals overlap each other, which my first assembly language code did not handle. At one point I thought I had the code reworked to handle it, but it was never tested…

Now with the ReadReceiver example. I think that was OK for a quick and dirty version of the code. Which for most cases may be good enough. It may take 3 or 4 passes to get a complete set, but that may be good enough… But if I were doing it for real, I would probably completely rework how the Pin Change Interrupt is used, such that we are not looking at one pin at a time…

My understanding of the the Pin Change Interrupt(s) is that there are 3 interrupts. One for pins 0-7, another for 8-15 and the third for 16-23 (Atmega pins not Arduino pins, need to map…). There is a register that allows you to enable any or all of these interrupts. There is also a register associated with each of these interrupts that you can configure which pins you want interrupts on…

When one or more of the pins changes state and the interrupt is enabled on that pin(s), the interrupt is generated. You then have to figure out which pin changed. One way is to only have one enabled, which is what the example did. Another way would be to have the pin states before the interrupt and the new pin states and see what changed. This is what I would probably do. If you can get all N pins on one data register you could simply read it, XOR it with the previous one and any that changed would be a 1… Grab the timer value, see if value is high or low…

So I might dig up my Hitec receiver and try it on the UNO I have sitting around.

Kurt

I downloaded and built the program. The problem with TimeOne was the .CPP file was including the file; “TimerOneTesting.h”, which does not exist. It should be, “TimerOne.h”. I made that change it it now builds.

Kurt

That’s all it was? I was close then. After chasing down the libraries, I ran out of time to play with it any further. Shoulda known! good starting point.

Yup! just edited the CPP and built it!

Thanks!

Alan KM6VV

Yep,

I looked over the code some and you can register multiple pins to monitor at once. I was thinking of plugging in my Hitec receiver and see what I got. I was also thinking of changing/extending this semi-library. In particular I would like to be able to pass a token to the AttachInterrupt function that gets passed back when the interrupt is called. That way you could for example give it a pin number or an Array index number that gets passed back, so you can figure out which of the 6 or 7 interrupts you are being called for. Alternatively I could structure it like:

    PCintPort::attachInterrupt(pin[0], rise0,RISING); // attach a PinChange Interrupt to our first pin
    PCintPort::attachInterrupt(pin[1], rise1,RISING); // attach a PinChange Interrupt to our first pin
    PCintPort::attachInterrupt(pin[2], rise2,RISING); // attach a PinChange Interrupt to our first pin
    PCintPort::attachInterrupt(pin[3], rise3,RISING); // attach a PinChange Interrupt to our first pin
    PCintPort::attachInterrupt(pin[4], rise4,RISING); // attach a PinChange Interrupt to our first pin
    PCintPort::attachInterrupt(pin[5], rise5,RISING); // attach a PinChange Interrupt to our first pin

...
void rise0()        //on the rising edge of the currently intresting pin
{
    rise(0);
}

Could do the same for fall, or could just have one set and change the the RISING to CHANGE and have the function to a digitalRead of the pin to get the actual state at that time…

Kurt

I didn’t think you could either pass or return a value from an interrupt?

Or do you just mean the attachment function?

But you’re still planning on having multiple channel inputs, not just the single PPM?

And we’re sure they overlap? Otherwise a “summing” network could combine them all into one I/O line. We did that for sampling spark signatures for an automotive test instrument; that’s what this reminds me of.

I need an R/C and my scope…

Alan KM6VV

Yes, I don’t see any reason to chain board to board to board… That is RC receiver to another board to convert to PPM to another board to convert PPM to I2C or Serial or SPI to then go to the BB2…

Yep the attachment function. The actual interrupt function is already going through the lists of pins that are registered and then grabbing the function associated with it and calling it. I will probably start off without modifying their code and simply do what I mentioned above and register a different function per pin. It is sort of a kludge but it would be a proof of concept… Yep the Logic Analyzer comes in handy for this type of stuff! May play around a little with this as I do have enough hardware to get it up and running…

Kurt

As I mentioned in the previous post, I have the hitec receiver and a spare UNO sitting around so I thought I would see what I might get. I plugged in jumpers between the output pins for the signals, plus a power and ground pin… I started off with the Read Receiver test program and have hacked it quite a bit. I got rid of the Timer1 library and currently simply using the system timer. May go back and use a hardware timer directly later. I am currently using the stock Pin Change Interrupt code from the Arduino Playground, but if I continue may use my own version with the mods I mentioned earlier. I appear to be getting valid data on 4 out of the 6 channels, need to see why the 2 are not changing like I think the should…

In case you wish to play along:

[code]#include <digitalWriteFast.h>

//======================================================================================
// Kurt’s hacked up RC receiver Program
//======================================================================================
#include <PinChangeInt.h> // http://www.arduino.cc/playground/Main/PinChangeInt
#include <PinChangeIntConfig.h>

// Currently I am only using 6 pins, 2-7 so they are all on PortD, if we go up to 7 pins will need
// to enable PortB
// BUGBUG:: THis is pointless? Need to define in the actual file (that is used by PCChangInt.cpp…)

#define PIN_COUNT 6 //number of channels attached to the reciver
#if PIN_COUNT < 7
#define NO_PORTB_PINCHANGES //PinChangeInt setup
#endif
#define NO_PORTC_PINCHANGES //only port D pinchanges (see: http://www.arduino.cc/playground/Learning/Pins)

#define MAX_PIN_CHANGE_PINS PIN_COUNT

// Note: will need to change for Mega…
#define PIN0 2
#define PIN1 3
#define PIN2 4
#define PIN3 5
#define PIN4 6
#define PIN5 7

static byte s_abPins[PIN_COUNT] = {PIN0, PIN1, PIN2, PIN3, PIN4, PIN5};
unsigned long g_ulLastPinChange;
unsigned long g_aulStartTime[PIN_COUNT]; // Needed to hold when a pulse started… Often 0
word g_awPulseWidth[PIN_COUNT]; // The last pulse width for each pin.
word g_awPulseWidthPrev[PIN_COUNT]; // The last pulse width for each pin.
boolean g_fPulsesValid;
boolean g_fPulsesValidPrev;
byte g_cValidSigs; // Counter to know if we have a full set of valid signals…

void setup() {
Serial.begin(115200);
Serial.println(“Kurt’s ReciverReading test”);

// This is garbage but I am having seperate PinChange Interrupt functions for each
// pin as there is no way to know which pin I am being called for...  Maybe later
// will update the pinchange interrupt to pass back some info...

g_fPulsesValidPrev = false;

for (byte i=0; i<PIN_COUNT; i++) {
    pinMode(s_abPins*, INPUT);     //set the pin to input
    digitalWrite(s_abPins*, HIGH); // enable PU - In case nothing connected...
    g_awPulseWidth* = 0;
    g_aulStartTime* = 0;
}

// Here is the first garbage of it...
g_ulLastPinChange = 0;
PCintPort::attachInterrupt(s_abPins[0], PinChange0, CHANGE); 
PCintPort::attachInterrupt(s_abPins[1], PinChange1, CHANGE); 
PCintPort::attachInterrupt(s_abPins[2], PinChange2, CHANGE); 
PCintPort::attachInterrupt(s_abPins[3], PinChange3, CHANGE); 
PCintPort::attachInterrupt(s_abPins[4], PinChange4, CHANGE); 
PCintPort::attachInterrupt(s_abPins[5], PinChange5, CHANGE); 

}

void loop() {
boolean fChanged;
g_fPulsesValid = ((micros() - g_ulLastPinChange) < 50000) && (g_cValidSigs >= PIN_COUNT); // Have a timeout to know if something went wrong…

if (g_fPulsesValid != g_fPulsesValidPrev) {
    g_fPulsesValidPrev = g_fPulsesValid;
    if (g_fPulsesValid)
        Serial.println("Pulses Valid...");
    else {
        Serial.println("Pulses Not Valid...");
        cli();
        for (byte i=0; i<PIN_COUNT; i++) {
            g_awPulseWidth* = 0;
            g_aulStartTime* = 0;
        }
        g_cValidSigs = 0;    // assume bad
        sei();
    }
}

if (g_fPulsesValid) {
       
    fChanged = false;
    for (byte j=0; j< PIN_COUNT; j++) {
        if (g_awPulseWidth[j] != g_awPulseWidthPrev[j]) {
            g_awPulseWidthPrev[j] = g_awPulseWidth[j];
            fChanged = true;
        }
    }
    if (fChanged) {
        Serial.print("time:\t");
        for (byte i=0; i<PIN_COUNT;i++) {
            Serial.print(g_awPulseWidth*,DEC);
            Serial.print("\t");
        }
        Serial.println();
    }
}
delay(100);

}

void PinChange0() {
// Hack up version to know which interrupt happened and what the state is…
PinChange(0, digitalReadFast(PIN0));
}

void PinChange1() {
// Hack up version to know which interrupt happened and what the state is…
PinChange(1, digitalReadFast(PIN1));
}

void PinChange2() {
// Hack up version to know which interrupt happened and what the state is…
PinChange(2, digitalReadFast(PIN2));
}

void PinChange3() {
// Hack up version to know which interrupt happened and what the state is…
PinChange(3, digitalReadFast(PIN3));
}

void PinChange4() {
// Hack up version to know which interrupt happened and what the state is…
PinChange(4, digitalReadFast(PIN4));
}

void PinChange5() {
// Hack up version to know which interrupt happened and what the state is…
PinChange(5, digitalReadFast(PIN5));
}

void PinChange(byte iPin, byte bPinState) {
unsigned long ulTime = micros(); // get the time when we entered here
g_ulLastPinChange = ulTime;
if (bPinState) {
g_aulStartTime[iPin] = ulTime;
} else {
ulTime -= g_aulStartTime[iPin]; // Get the delta time
if ((ulTime >= 750) && (ulTime <= 2250)) { // Do some validation of information.
g_awPulseWidth[iPin] = ulTime; //
if (g_cValidSigs < PIN_COUNT)
g_cValidSigs++; // keep a count of valid items up to count of pins…
}
else
g_cValidSigs = 0; // reset count of valid signals back to zero
}
}
[/code]
There were also issues with the example code up on the playground, like they modify some defines for how many interrupts to process and which ports to process, but they do it in their own source code, example: #define NO_PORTB_PINCHANGES //PinChangeInt setup
Is defined in the main sketch file, but I don’t know how they expect to see this define in the source file: pinchangeint.cpp which is a library file? Maybe I am missing something.

Now back to playing.

Kurt*******

Well, I played around with the code a little, but no PPM yet to test with.

I did run into this site, which appears to be a version for Quads of the Tiny code:

66.163.168.225/babelfish/translate_url_content?.intl=us&lp=de_en&trurl=http%3a%2f%2fwww.qc-copter.de%2fwiki%2findex.php%3ftitle%3dBedienung_quadroppm

Again, shouldn’t be needed if one can get to the PPM signal on the XCVR.

The Arduino ServoDecode library looks like a good approach (has it been mentioned), and is being used in a demo for the ServoCommander board (I just got) w/ Arduino.

Next step is to get a Futaba 7C 2.4 GHz R/C. And I just realized; there is a 2.4 GHz Ham Band, and it sounds like this radio can be put on the Ham Band, although I have yet to find that mentioned anywhere.

The Ebay Futaba 7C 2.4 GHz we like of course doesn’t mention it:

ebay.com/itm/Futaba-7C-7-Channel-2-4GHz-Heli-Helicopter-Radio-w-R617FS-Transmitter-Mode-2-Rx-/370540797105?pt=Radio_Control_Parts_Accessories&hash=item5645f3dcb1#shId

But things are tying together, at least a little.

Alan KM6VV

Alan,

With all this happening the reason why I went with PPM originally was due to the fact that getting Kurts help on code to read 7 channels of pwm would be more challenging for him to do since this transmitter/receiver combo was so unique to this topic. PPM is a more standardized format.

we can throw PPM out of the picture if this arduino option pans out better which it sounds like both of you have your mits deep in it right now! I’m in the process of ordering a Arduino Nano and some pins to try out the code, learn how to make an led blink and such and maybe actually help you guys!

Maybe if this code turns out great we can make some out of the box devices that will allow others without both of your skill sets to use more off the shelf RC products for their projects… like a really REALLY cheep product.

I spoke with some guys in #AVR an irc channel, they had mentioned if we want something really good use a pic capable of 8 mhz use two filtering caps and a resonator and use the /8 option and the output as they say will be really “Neat” and no complicated math as the output will be exactly what we need, they had also mentioned using spi as it can be bit banged out and minimal overhead… I think Kurt mentioned that above as well.

I’m thinking this device once developed on a arduino can be converted to a pic perhaps? something about an 1x1.5 inches and a small circuit board.

Sorry, but I am not a PIC expert… So can not comment there. Could probably do it with one of the NANOs I have sitting around…

Yep, as I mentioned made progress with an Arduino UNO, although my Hitec Laser 6 Transmitter or receiver appears to be acting up some. That is I am not getting any change in the pulse widths on 2 of the channels (Left joystick left/right and the AUX know on top. I am not seeing any differences showing up in the code, nor on the logic analyzer looking at the pulses or with my hacked clock signal looking at the receiver… So I am not sure if this can be resolved or not, or in my case if that is overly important…

Doing the next step of having it output data over I2C should not be hard. The wire library is already setup to be either Master or Slave, so simply need to start up with a slave address and then wait for a request and then simply output the bytes back to the caller… On the BAP side could probably use the bit bang I2C functions, could potentially try to use the hardware one as well (P6/P7 may need PU resistor, or maybe simply enable built-in one on Arduino…).

Or could do SPI. On Arduino the main SPI library is for being the master, but there are examples of setting it up as the slave. SPI (like PS2), typically takes 4 IO pins (MISO, MOSI, CLK, Select). On Bap side would probably require code like we use to read PS2…

So either way would work fine. Now back to other projects…

Kurt

Do it with a PIC chip, or an ATmel chip, either could work.

Use either C or BASIC.

The product already exists!

radrotary.com/OMMrxconverter/OMMrxconverter.html

Alan KM6VV

Alan,
You might also want to consider this encoder which works with Spektrum receivers: store.diydrones.com/product_p/br-ppme.htm
Regards,
TCIII