For my latest project I needed to connect an RC receiver to an Arduino and read the state of the PWM servo signals. I couldn’t find a library I liked that was interrupt-based, robust, and supported flexible output ranges and remapping. So I decided to build my own.

The Servo Input Library

The result is the Servo Input library, an open source solution for reading PWM servo inputs with an Arduino. This allows you to read servo positions from robotics, RC receivers, or any other devices with servo motors.

In comparison to the ‘typical’ method to read these signals, the built-in Arduino pulseIn() function, this library allows servo inputs to be:

  • Non-blocking (asynchronous)
  • Range limited
  • Flexibly remapped
  • Buffered / noise resilient

Servo signals can be easily read in the background and remapped to a given output (USB, LEDs, motors, etc.) without any hand holding by the user. Add the library, connect the signal wires, and go.

Features

Non-Blocking

All ServoInputPin objects make use of external interrupts to gather the PWM data from the servos. This means that the data collection happens in the background and is available instantaneously. No delays, no waiting.

It also means that you can execute other, blocking code with long delays while the servos positions are being read in the background. The servo data will be available whenever you need it.

Range Rescaling

Typical PWM servo signals have a 50 Hz update frequency (20 ms) and vary the motor angle with the duty cycle. The majority of motors will move to their center position with a 1500 µs pulse width and have a +/- 500 µs range (1000 – 2000 µs).

The library starts with these defaults but adds dynamic range rescaling. At any point the user can specify the minimum, maximum, or total range in microseconds. These range values are used whenever retrieving the pulse duration, so the reported angle or position will match the set angle of the motor.

What’s more is that these values are used for limiting the remapped output, so pulse durations that are outside of your servo’s physical range can be ‘clipped’ and ignored. This also helpfully restrains the output ranges when remapping to other values which prevents overflow.

Flexible Remapping

Speaking of, the library features flexible remapping functions to work the positional data into whatever form you need it.

In addition to the standard angle (degree) and percentage functions, the library also features a generic remapping function that will map the servo’s positional data between any two long values. Great for controlling addressable LEDs (8 bit unsigned), a USB joystick (16-bit signed), or any other conceivable range.

Signal Buffering

If the signal is noisy then the data will get corrupted and none of these features matter. That’s why ServoInput includes signal validation and buffering.

When a new pulse has been detected and its value retrieved by the user, the pulse width is checked against a set range to see whether it’s valid. This position is then saved for future reference and used in the event that a noisy, invalid pulse is observed.

This keeps the input values stable and prevents hiccups from disrupting your project.

How It Works

The library’s core functionality relies on external interrupts and the standard Arduino micros() function. For each ServoInputPin object created by the user, the library creates a ‘change’ interrupt on the specified pin that triggers whenever the PWM signal pulses. On a change state, the interrupt triggers and the pin’s state is checked to determine the direction of the pulse (rising or falling). The interrupt routine then calls the micros() function, based on AVR Timer0, to calculate the pulse width of the servo signal in microseconds.

When a user calls one of the functions to read back the data (e.g. getAngle()), this measured pulse width is buffered, compared against a range of valid widths, and then finally rescaled relative to the minimum and maximum pulse times provided by the user.

The static interrupt service routine from ServoInput. DIRECT_PIN_READ is a preprocessor define to mask and shift the pin state from the register.

The underlying code takes advantage of a quirk of C++ templates, where each template instance (i.e. pin) is a wholly separate class type. This means that each static isr() function and pulse data has unique, static memory storage even as part of an object. This allows up to 255 (uint8_t) unique ServoInputPin objects to be linked to the pin interrupt vectors with just a single function definition.

Behind the scenes, the ServoInputPin<> class relies on the ServoInputSignal base class for handling the range rescaling and data retrieval functions. This base also includes a linked list for iterative access, which is managed by the ServoInputManager (available globally as ServoInput) that provides convenience functions across all instances such as checking if all or any servo signals are available.

Download

The Servo Input library is licensed under the GNU Lesser General Public License and can be downloaded either from GitHub or through the library manager in the Arduino IDE.

For more information on how to use the Servo Input library, see the tutorial on how to use an RC controller with an Arduino.

If you use this in your own project, please let me know! I’d love to see what you create.


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Would you like to know more?