Radio PPM bad frame rejection

Tonight I successfully (after dropping the project for many months) got my VEX radio receiver to decode on my Arduino microcontroller. For the most part, all the values are right, i just need to do a bit of calibration. However, every few readings, one of the last channels to be read will come up with no value, and then continue on normally, apparently without mixing up the channels (astonishingly). So I need to program in bad frame rejection. However, I haven’t really been able to find anything online (probably been searching for the wrong thing), and the few things I’ve tried haven’t really worked.

Has anyone dealt with PPM decoding before and has experience? If not, have any ideas?

Attached is my current code in a C file (note: this is the Arduino project file, and does not include int main(), that is in a different file. Also, the original file was .pde, but VEX forum wouldn’t allow me to upload that file extension). A few details:

-There is an interrupt configured for pin 2 on the Arduino (designated interrupt 0).
-The global volatile variables at the top are modified by the interrupt service routine called by that interrupt (timepulse()).
-The ISR stores the time from the microseconds clock when a new pulse comes in. It compares it to the last measured time, and if it’s greater than 5000 milliseconds, it resets for the new frame. Otherwise, it stores that time in the next variable and advances the index for the next pulse.
-I have the Arduino user LED configured a bit like the RX LED’s on the VEX microcontroller, it comes on when there’s a signal, and goes off when there isn’t one. It mostly follows the “newdata” variable.
-Actual channel values are calculated in the main loop to save processor power. Each channel is calculated by subtracting the time the first pulse for that channel came in from the second pulse. It is then mapped from the 1-2ms pulse to an 8-bit variable (like the VEX code).
-Every 200 ms, it prints out the channel values. If “newdata” is false, it sets all the channel values to neutral (safety feature for the future), and sets “newdata” to 2 as a second false value.

Main questions:

  1. How to do bad frame rejection.
  2. If I use either FALLING or RISING edges for the interrupt trigger, they both seem to work. So which one is “right”? The code is written for the rising edge of the signal (the short pulses), but I don’t know what that translates into on the microcontroller. This is more of an Arduino hardware question.

Thank you for any help you may be able to offer. Also, this page is a good reference for PPM if you haven’t dealt with it before:
PPM_RX.c (2.32 KB)

I’ve never tried to decode PPM, but I’ve written code to encode it. Have a look at this thread, and there is full source code under the name PPM4VEX in the source section of the forum.

I’ll have a closer look at what you are doing tomorrow to see if I can spot any potential trouble. Have you examined the PPM signal directly using a logic analyzer or oscilloscope? If not, I recommend one of these. I recently debugged an interrupt latency issue using one of these that had the symptom of a missing signal transition - pin change interrupts on AVR parts can be tricky to get right, especially if anything disables interrupts for any length of time.


  • Dean

Thanks, I think I’ve looked at that thread briefly before. I may eventually end up writing something like that for a homemade radio control set (using these, or the longer range version from the same site), but for right now, the decoding seems relatively straightforward. It’s just CONSISTENT decoding that’s the problem. I have a theory that would explain the lost channels, but the lack of a shift in the channel data; the ISR is updating the variables in the middle of calculating channel values. In this way, a new value in “chantimes[5]” would be greater than the value in “chantimes[6]” that it’s being subtracted from. To minimize this, I think I’ll try copying all of the data over to a non-volatile array that is not edited by the ISR before calculating the channels. Alternatively, depending on how fast I can get the math to run, I could put channel calculation directly into the ISR itself.

On a separate note, I actually don’t have a logic analyzer/o-scope, but we have a couple in robotics I could get access to if I needed to (full color, can’t wait to play around with them, but they’re for next year’s class of people who aren’t graduating this year). The link you sent from SparkFun seems like something similar to the DSO Nano (also from Seeedstudio, I love them…). It just came out Christmas time of last year, and they sold out within the first month (after having it back-ordered before release). Check it out, it looks pretty sweet, and the price is right for people who have jobs (maybe in a couple years…)

Anyway, thanks for the suggestions, and I’ll let you know if the variable-substitution works out.

P.S. If you ever get the DSO Nano, don’t forget to invest in the Contemporary Minimalism DSO Nano Stand, now on discount, as you can see :smiley:

I just looked through the code, and it looks reasonable to me. However, I’ve had problems with code like this in the past using pin interrupts. If interrupts are disabled for long enough (due to another ISR running, or atomic operations), it would be possible to completely miss one of the pulses. The 5ms frame timeout would ensure you stay in sync, as you are seeing.

If I were doing this, I’d configure Timer1 for input capture mode on PB2 (which is D8 on the Arduino). This will allow the timer to latch its value on each pulse. This gives you a bit more time to process the input capture interrupt, though it could still miss a pulse if interrupts are disabled long enough. You will get more stable values as well, since ISR latency won’t add jitter to your values.

As for rising vs falling edge, I don’t think it matters. The pulse spacing should measure the same regardless of which edge you choose, as long as you consistently use the same edge.

When I debug problems like this, I like to assert digital outputs for each ISR or other function of interest. I then use the logic analyzer to monitor the input signal(s) and the function-marker digital out(s). It is amazing how much you can learn about what is going on inside the microcontroller this way.

The DSO Nano looks pretty sweet, but 1MHz is a bit limiting. I’d jump on it if it went up to 50MHz or so.


  • Dean

I realize this is months later, but I’ve been dealing more in Google than VEX recently. I actually got the code to work just by adding in the non-volatile array, so it was the issue of the ISR updating in the middle of channel calculation. Also, the Arduino interrupt structure libraries actually latch the millis() and micros() timer values for you. It’s really nice as long as you don’t have an ISR that runs for milliseconds.

And I really do need to get some sort of logic analyzer or o-scope… I’ve thought about getting a Bus Pirate at least. Maybe some day when I’m no longer a broke college student.