skip navigational linksPJRC
Shopping Cart Download Website
Home Products Teensy Blog Forum
You are here: Teensy Teensyduino Libraries PulsePosition

PJRC Store
Teensy 4.0, $19.95
Teensy 3.6, $29.25
Teensy 3.5, $24.25
Teensy 3.2, $19.80
Teensy LC, $11.65
Teensy 2.0, $16.00
Teensy++ 2.0, $24.00
Main Page
Getting Started
How-To Tips
Code Library

PulsePosition Library

PulsePosition can transmit and receive PPM (Pulse Position Modulated) signals commonly used to control RC aircraft and servo motors. Up to 8 simultaneous input and/or output PPM streams may be used, with each stream conveying up to 16 signals.

Download: Included with the Teensyduino Installer
Latest Developments on Github

PulsePosition is designed for 0.02 µs accuracy (approx 24X better than most Arduino implementations using AVR Timer1) with tolerance for significant interrupt latency caused by other libraries. All output waveforms are generated by hardware timer compare and all input waveforms are read using hardware timer input capture, for extremely precise timing. Pin change interrupts, which add error due to interrupt latency, are never used.

Please use the PJRC Forum to discuss PulsePosition.


BoardPins (Input or Output)
Teensy 3.6, 3.55, 6, 9, 10, 20, 21, 22, 23
Teensy 3.2, 3.15, 6, 9, 10, 20, 21, 22, 23
Teensy LC6, 9, 10, 20, 22, 23

Teensy 3.1 supports up to 8 simulaneous PPM streams with up to 16 signals per stream.

Teensy-LC supports up to 6 simulaneous PPM streams with up to 16 signals per stream.

Remote controlled OP Shark is reactive to, rather than merely controlled by RC steering and speed. Tail moves back and forth to appear to be swimming, and fins animate while turning.

Usage: Receiving PPM Encoded Signals

PulsePositionInput myInput;

Create a PPM input object. You may create as many inputs as necessary, each with its own name. PulsePosition supports a total of 8 simultaneous inputs or outputs.

PulsePositionInput myInput(polarity);

Create a PPM input object with configurable polarity. The polarity may be RISING for a normal waveform with positive pulses that begin with rising edges, or FALLING for a inverted waveform where each pulse begins with a falling edge.


Assign an input to a pin and begin receiving pulses. rxPin may be pins 5, 6, 9, 10, 20, 21, 22, or 23.


Returns the number of channels recieved, or zero when no new data is available.;

Read one of the PPM encoded channels. The returned value is a float representing the number of microseconds between rising edges. The input "channel" ranges from 1 to the number of channels indicated by available(). Reading the last channel causes the data to be cleared (available will return zero until a new frame of PPM data arrives).

Usage: Transmitting PPM Encoded Signals

PulsePositionOutput myOutput;

Create a PPM input object. You may create as many outputs as necessary, each with its own name. PulsePosition supports a total of 8 simultaneous inputs or outputs.

PulsePositionOutput myOutput(polarity);

Create a PPM input object. The polarity may be RISING for a normal waveform with positive pulses where timing is measured between rising edges, or FALLING for a inverted waveform where pulses begin with falling edges.


Assign an output to a pin and begin sending pulses. txPin may be pins 5, 6, 9, 10, 20, 21, 22, or 23. In single pin mode, a space time is used to indicate the end of frame, after transmitting all the pulses. See the Timing section below for details.

myOutput.begin(txPin, framePin);

Assign an output to 2 pins and begin sending pulses. txPin may be pins 5, 6, 9, 10, 20, 21, 22, or 23. framePin may be any other digital pin. In 2 pin mode, framePin indicates when a new data frame begins, which eliminates the need for extra delay. Two pin mode is intended to connect to 1 or 2 74HCT164 chips.

myOutput.write(channel, microseconds);

Transmit pulses, on channel 1 to 16, with duration of "microseconds" between rising edges. The microseconds is a float, so you can specify sub-microsecond precision. PulsePosition can generate output with approximately 0.02 µs accuracy. The output is transmitted in frames. All writes take effect in the next frame.

Timing Parameters

Several timing parameters may be edited. Currently the only way to change these is by editing PulsePosition.cpp. Future versions may make some of these configurable from Arduino sketches.

TODO: add a timing diagram and some explantion

// The shortest time allowed between any 2 rising edges. This should be at
// least double TX_PULSE_WIDTH.
#define TX_MINIMUM_SIGNAL 300.0

// The longest time allowed between any 2 rising edges for a normal signal.
#define TX_MAXIMUM_SIGNAL 2500.0

// The default signal to send if nothing has been written.
#define TX_DEFAULT_SIGNAL 1000.0

// When transmitting with a single pin, the minimum space signal that marks
// the end of a frame. Single wire receivers recognize the end of a frame
// by looking for a gap longer than the maximum data size. When viewing the
// waveform on an oscilloscope, set the trigger "holdoff" time to slightly
// less than TX_MINIMUM_SPACE, for the most reliable display. This parameter
// is not used when transmitting with 2 pins.
#define TX_MINIMUM_SPACE 5000.0

// The minimum total frame size. Some servo motors or other devices may not
// work with pulses the repeat more often than 50 Hz. To allow transmission
// as fast as possible, set this to the same as TX_MINIMUM_SIGNAL.
#define TX_MINIMUM_FRAME 20000.0

// The length of all transmitted pulses. This must be longer than the worst
// case interrupt latency, which depends on how long any other library may
// disable interrupts. This must also be no more than half TX_MINIMUM_SIGNAL.
// Most libraries disable interrupts for no more than a few microseconds.
// The OneWire library is a notable exception, so this may need to be lengthened
// if a library that imposes unusual interrupt latency is in use.
#define TX_PULSE_WIDTH 100.0

// When receiving, any time between rising edges longer than this will be
// treated as the end-of-frame marker.
#define RX_MINIMUM_SPACE 3500.0

Shift Register Output

The 2 wire outout mode is designed to drive 74HCT164 shift registers. One chip may be used for up to 8 outputs, or two chips may be used for up to 16 outputs.

74HCT164 is recommended for 5V output signals. For 3.3V output signals, a 74HC164 (without the "T") should be used, powered from 3.3V instead of 5V.

PulsePosition supports up to 8 simultaneous output objects, for a total of 128 possible output signals.

Connection details may be found in File > Examples > PulsePosition > ShiftRegisterOutput.

// Two wire output is intended to connect to a 74HCT164 shift register chip
// 74HCT164 pinout:
//  pins   Function       Recommended Connection
//  ----   --------       ----------------------
//  1,2    Serial Input   Connect to framePin
//    8    Clock Input    Connect to txPin
//    9    Clear Input    Connect to pin 2 and a 10K resistor to ground
//  3-6    Outputs        
// 10-13   Outputs
//    7    Ground
//   14    +5V            VIN or VUSB on Teensy 3.1
// The clear pin (pin 9) on the 74HCT164 can be used to assure proper startup
// without false pulses.  Connect a 10K resistor to ground, so the pin will
// be low (holding all the outputs low) until Teensy begins sending updates.
// If startup behavior is not a concern, pin 9 may be +3.3V or +5V.
// For 16 outputs, two 74HCT164 chips may be used.  Connect pin 13 from the
// first chip to pins 1+2 on the second chip.  Both should receive the same
// Clock and Clear signals.

Traditionally, other Arduino libraries have used 74HC4017 chips for 10 output signals. Because the '4017 clear pin is an asychronous input, a small error error in the first pulse is created by software delay associated with generating the clear signal.

PulsePosition is designed for very precise inupt and output, without software delay errors. The 74HCT164 chip allows all output pulses to be created sychronously, which prevents a small output error based on the software timing to generate the framePin output.

About PPM Signals

Update: PencilGeek has published an alternate PPM library.