|
ServoEasing
|
🌎 Google Translate
Its purpose is to interpolate the movement between two servo positions set by software.
If your servo control data is e.g. generated by an joystick or other *"slow"* changing inputs and therefore does not change suddenly or does not jump, you most likely do not need this library!, you may consider to use a digital low pass or simple EMA filters to smooth your values used to control the servos.
ServoEasing works with the Arduino Servo library as well as with PCA9685 servo expanders. The expander in turn requires the Arduino Wire library or a compatible one and is bound to their restrictions.
For ESP32 you need to install the Arduino ESP32Servo library.
If you require only one or two servos, you may want to use the included LightweightServo library which is like the Adafruit TiCoServo library, but more lightweight and currently only for Uno, Nano, instead of the Arduino Servo library. The LightweightServo library uses the internal Timer1 with no software overhead and therefore has no problems with servo twitching or interrupt blocking libraries like SoftwareSerial, Adafruit_NeoPixel and DmxSimple.
For instructions how to enable these alternatives, see Compile options / macros.
attach(int aPin, int aInitialDegreeOrMicrosecond, int aMicrosecondsForServoLowDegree, int aMicrosecondsForServoHighDegree, int aServoLowDegree, int aServoHighDegree).setIntegerDegreeForAllServos(3, 135, 135, 135).ServoEasing::ServoEasingArray[].Linear Quadratic Cubic Quartic
Sine Circular Back Elastic Bounce
Precision Dummy User defined
All ease functions are called internally with the value: PercentageOfCompletion / 100 giving a call value from 0 to 1.
Since the values are computed in a fixed 20 ms raster, the last degree increment or decrement in an easing may be much smaller than the increment/decrement before, resulting in some small discontinuities between adjacent movements.

To restrict servo movements to a fixed range, you can specify constraints with setMinMaxConstraint(int aMinDegreeOrMicrosecond, int aMaxDegreeOrMicrosecond) like it is done in the TwoServos example.

By setting speed -which is an uint16- to a real high value like e.g. 50000, you can effectively disable easing and get almost the same behavior as if using Servo.write() function directly.
For floating point constants, use the notation of 123.456f with trailing f (for a floating point constant) to avoid compiler errors. A Doxygen documentation of the sources is available here.
See also the examples here.
Just call myServo.startEaseTo() instead of myServo.write() and you are done. Or if you want to wait (blocking) until servo has arrived, use myServo.easeTo().
myServo.attach(int aPin, int aInitialDegree) or by calling myServo.write(int aDegree),myServo.setSpeed() or as second parameter to startEaseTo() or easeTo(). Otherwise the Servo will start with the speed of 5 ° per second, to indicate that speed was not set.
You can handle multiple servos simultaneously by special functions like writeAllServos(), setSpeedForAllServos(), setIntegerDegreeForAllServos(), setEaseToDForAllServos(), updateAndWaitForAllServosToStop(), setEaseToForAllServosSynchronizeAndWaitForAllServosToStop(), setEaseToForAllServosSynchronizeAndStartInterrupt() and much more.
See below.
Arduino Serial Plotter result of the SymmetricEasing example. 

Servos are build to reach the target position as fast as possible. To achieve slow movement, this library gradually adjusts the target position, and the servo attempts to respond to these changes as quickly as possible. This creates the appearance of slow movement even when the servo reacts very quickly.
Initially, after powering on, the software has no information about the actual servo position!
This leads to the servo moving quickly to the start position that the software sends it. This cannot be avoided unless we can magically guess the actual position!
If you want to operate your servo e.g. from -90 ° to +90 °, you have two possibilities to specify this during attach:
Servo1.attachWithTrim(SERVO1_PIN, 90, START_DEGREE_VALUE, DEFAULT_MICROSECONDS_FOR_0_DEGREE, DEFAULT_MICROSECONDS_FOR_180_DEGREE) like it is done in the TwoServos example.Servo1.attach(SERVO1_PIN, START_DEGREE_VALUE, DEFAULT_MICROSECONDS_FOR_0_DEGREE, DEFAULT_MICROSECONDS_FOR_180_DEGREE, -90, 90).If your servo has other timing characteristics than the default one -544 µs for 0 and 2400 µs for 180 ° - you have to use Servo1.attach(SERVO1_PIN, <MY_SERVO_MICROSECONDS_FOR_0_DEGREE>, <MY_SERVO_MICROSECONDS_FOR_180_DEGREE>).
You can combine this with variant 2 from above to transparently specify your servo characteristics e.g. like it is done in the RobotArmControl example:
These values are measured with the SpeedTest example.
These are the fastest values for my SG90 servos at 5 volt (4.2 volt with servo active).
| Degree | Duration | Speed |
|---|---|---|
| 180 | 400 ms | 450 degree per second |
| 90 | 300 ms | 300 degree per second |
| 45 | 180 ms | 250 degree per second |
| 30 | 150 ms | 200 degree per second |
| 20 | 130 ms | 150 degree per second |
| 10 | 80 ms | 125 degree per second |
Values for the MG90Sservos servos at 5 volt (4.2 volt with servo active).
| Degree | Duration | Speed |
|---|---|---|
| 180 | 330 ms | 540 degree per second |
| 90 | 220 ms | 410 degree per second |
| 45 | 115 ms | 390 degree per second |
After disconnected, my SG90 servo requires 4 pulses for a 180 degree turn. It may be less, if the turn is smaller.
After disconnected, my**MG90** servo requires 1 pulse for a 110 degree turn. The second pulse (after 20 ms) adds around 10 degree to it, so it takes around 6 to 7 pulses (120 ms to 140 ms) for a complete 180 degree turn.
These values seems to be independent of the turn direction.
These values are measured with the UnitTest example with TEST_FIXED_PULSE_NUMBERS defined.
Every *.cpp file is compiled separately by a call of the compiler exclusively for this cpp file. These calls are managed by the IDE / make system. In the Arduino IDE the calls are executed when you click on Verify or Upload.
And now our problem with Arduino is:
How to set compile options for all *.cpp files, especially for libraries used?
IDE's like Sloeber or PlatformIO support this by allowing to specify a set of options per project. They add these options at each compiler call e.g. -DTRACE.
But Arduino lacks this feature. So the workaround is not to compile all sources separately, but to concatenate them to one huge source file by including them in your source. This is done by e.g. #include "ServoEasing.hpp".
But why not #include "ServoEasing.cpp"?
Try it and you will see tons of errors, because each function of the *.cpp file is now compiled twice, first by compiling the huge file and second by compiling the *.cpp file separately, like described above. So using the extension cpp is not longer possible, and one solution is to use hpp as extension, to show that it is an included *.cpp file. Every other extension e.g. cinclude would do, but hpp seems to be common sense.
In order to support compile options more easily, the line #include <ServoEasing.h> must be changed to #include <ServoEasing.hpp> in your main program (aka *.ino file with setup() and loop()).
In all other files you must use #include <ServoEasing.h>, to prevent multiple definitions linker errors:
If you forget to include ServoEasing.hpp, you will see errors like Simple.ino:57: undefined reference to ServoEasing::attach(int, int).
Ensure that all macros in your main program are defined before any #include <ServoEasing.hpp>.
The following macros will definitely be overridden with default values otherwise:
MAX_EASING_SERVOSREFRESH_INTERVALUSE_PCA9685_SERVO_EXPANDER
To customize the library to different requirements, there are some compile options / macros available.
These macros must be defined in your program before the line #include <ServoEasing.hpp> to take effect.
Modify them by enabling / disabling them, or change the values if applicable.
| Name | Default value | Description |
|---|---|---|
USE_PCA9685_SERVO_EXPANDER | disabled | Enables the use of the PCA9685 I2C expander chip/board. |
PCA9685_ACTUAL_CLOCK_FREQUENCY | 25000000L | Change and activate it, if your PCA9685 has another than the default 25 MHz internal clock. See chapter 2 and 5 of the PCA9685 Datasheet "25 MHz typical internal oscillator requires no external components". This value is taken for all attached PCA9685 expanders! To specify it for each PCA9685 expander individually, use PCA9685Init(uint32_t aActualPCA9685ClockFrequency) after the last attach(). Adafruit provides a library example to get the PCA9685 actual internal frequency. |
USE_SOFT_I2C_MASTER | disabled | Saves up to 1756 bytes program memory and 218 bytes RAM for PCA9685 I2C communication compared with Arduino Wire. |
USE_SERVO_LIB | disabled | Use of PCA9685 normally disables use of regular servo library. You can force additional using of regular servo library by defining USE_SERVO_LIB. See below. |
USE_USER_PROVIDED_SERVO_LIB | disabled | If you have a different servo implementation, e.g. this M5Stack Servo expander you can provide your own servo library by activating this macro. You must also include the .h file of your library e.g. #include "DummyServo.h". |
PROVIDE_ONLY_LINEAR_MOVEMENT | disabled | Disables all but LINEAR movement. Saves up to 1540 bytes program memory. |
DISABLE_COMPLEX_FUNCTIONS | disabled | Disables the SINE, CIRCULAR, BACK, ELASTIC, BOUNCE and PRECISION easings. Saves up to 1850 bytes program memory. |
MAX_EASING_SERVOS | 12, 16(for PCA9685) | Saves 4 byte RAM per servo. If this value is smaller than the amount of servos declared, attach() will return error and other library functions will not work as expected. Then of course all AllServos() functions and isOneServoMoving() also can't work correctly! |
SERVO_REFRESH_INTERVAL_MICROS | 20000 | This value determines the update rate for writing new servo values. If not defined, its value is taken from REFRESH_INTERVAL of Servo.h or REFRESH_USEC from ESP32Servo.h. This enables the setting of ServoEasing refresh period by just changing one of these values. |
DISABLE_MICROS_AS_DEGREE_PARAMETER | disabled | Disables passing also microsecond values as (target angle) parameter. Saves up to 128 bytes program memory. |
ENABLE_MIN_AND_MAX_CONSTRAINTS | disabled | Enables servo movement constraints. Requires 4 bytes RAM per servo and 36 bytes program memory. |
DISABLE_PAUSE_RESUME | disabled | Disables pause and resume functionality. Saves 5 bytes RAM per servo. |
PRINT_FOR_SERIAL_PLOTTER | disabled | Generate serial output for Arduino Plotter (Ctrl-Shift-L). |
DEBUG | disabled | Generates lots of lovely debug output for this library. |
USE_LIGHTWEIGHT_SERVO_LIBRARY | disabled | Available only for ATmega328 and ATmega2560. Supports only servos at pin 9 and 10 (plus pin 44, 45, 46 on ATmega2560). Makes the servo pulse generating immune to other libraries blocking interrupts for a longer time like SoftwareSerial, Adafruit_NeoPixel and DmxSimple. See below. Saves up to 742 bytes program memory and 42 bytes RAM. |
MINIMUM_PULSE_WIDTH | 400 | The shortest pulse which can be sent to a servo by this library. This value is smaller than the value used by the Arduino Servo library, which is 544 us (MIN_PULSE_WIDTH), to be more versatile. |
MAXIMUM_PULSE_WIDTH | 3500 | The longest pulse which can be sent to a servo by this library. This value is greater than the value used by the Arduino Servo library, which is 2400 us (MAX_PULSE_WIDTH), to be more versatile. |
THRESHOLD_VALUE_FOR_INTERPRETING_VALUE_AS_MICROSECONDS | 360 | Values above 360 are interpreted as microseconds in all functions accepting degree as parameter. |
Using the PCA9685 expander makes the servo pulse generating immune to other libraries blocking interrupts for a longer time like SoftwareSerial, Adafruit_NeoPixel and DmxSimple.
To enable the use of the expander, activate the line #define USE_PCA9685_SERVO_EXPANDER before #include <ServoEasing.hpp>.
In expander mode, timer1 is only required for the startEaseTo* functions and not for the blocking easeTo* functions, since no servo signal must be generated by it.
The pin number parameter of the attach function determines the port number of the PCA9685 and can be in the range from 0 to 15.
Be aware that the PCA9685 expander is reset at the first attach() and initialized at every further attach().
If you have a PCA9685 expander using external clock input, you must call PCA9685InitWithExternalClock(uint32_t aExternalClockFrequencyHertz) once after the last attach() for this PCA9685 expander. It does not matter which servo of the PCA9685 expander is used as object for this call.
To control simultaneously servos with the Arduino Servo library i.e. servos which are directly connected to the Arduino board, activate the line #define USE_SERVO_LIB.
In this case you should attach the expander servos first in order to initialize the expander board correctly. And as long as no servo using the Arduino Servo library is attached, the expander servos will not move, which should not be a problem since you normally attach all servos in setup().
Resolution of the is PCA9685 signal is approximately 0.5 °.
On the ESP32 the I2C library is only capable to run at 100 kHz, because it interferes with the Ticker / Timer library used. Even with 100 kHz clock we have some dropouts / NAK's because of sending address again instead of first data.
Since the raw transmission time of 32 Servo positions is 17.4 µs @ 100 kHz, not more than 2 expander boards can be connected to one I2C bus on an ESP32 board, if all servos should move simultaneously.
If you do not use any timer in your program you can increase speed up to 800 kHz. Maybe you have to attach 2 x 2.2 k pullup resistors to the I2C lines to have it working reliably.
This library is like the Adafruit TiCoServo library, but more lightweight and currently only for Uno, Nano.
Using the Lightweight Servo library reduces sketch size and makes the servo pulse generating immune to other libraries blocking interrupts for a longer time like SoftwareSerial, Adafruit_NeoPixel and DmxSimple.
Up to 2 servos are supported by this library and they must be physically attached to pin 9 and/or 10 of the Arduino board.
To enable it, activate the line #define before the line #include "LightweightServo.hpp" like it is done in the TwoServos example.
If you do not use the Arduino IDE, take care that Arduino Servo library sources are not compiled / included in the project.
The ServoEasing library provides two arrays to ease the handling of multiple servos.
ServoEasing *ServoEasing::ServoEasingArray[MAX_EASING_SERVOS]float ServoEasing::ServoEasingNextPositionArray[MAX_EASING_SERVOS]Every ServoEasing object is appended to the ServoEasingArray by the attach() function. Only the order of the attach() statements determines the position in the array. So you can access your servo, which you attached secondly, also by ServoEasing::ServoEasingArray[1]->setEaseTo(135) as it is done here.
There are also many other *AllServos* functions like stopAllServos().
To move multiple servo, you can fill up the ServoEasing::ServoEasingNextPositionArray with the desired positions and then use e.g. the function setEaseToForAllServos(). Then you must enable interrupt with enableServoEasingInterrupt() or call updateAllServos() in your main loop until it returns true.
If you want to move all your servos synchronized, i.e. they all stop at the same time, you can use the setEaseToForAllServosSynchronizeAndWaitForAllServosToStop() or setEaseToForAllServosSynchronizeAndStartInterrupt function.
An example can be found here.
The Quadruped example makes heavy use of the *AllServos* functions.
If you detach a servo and then attach another one, the latter will get the index of the former detached one.
This example controls 8 servos to move a Quadruped robot. It is documented here.
| MeArm robot | Control board |
|---|---|
| |
This example controls a MeArm robot arm with 4 servos using 4 potentiometers and/or an IR Remote.
It is documented here.
| MeArm robot | Control board |
|---|---|
| |
are documented here and are available at the Arduino IDE at File > Examples > Examples from Custom Libraries / ServoEasing.
Converting a 10 pin double row pin header with 21 mm pin length to a breadboard servo adapter.

The API accepts degrees or microseconds as float or integer values, but internally only microseconds (or units (= 4.88 µs) if using PCA9685 expander) and not degrees are used to speed up things.
If you do not specify an initial position with attach, the first attach moves servo to DEFAULT_PULSE_WIDTH (90 degree | 1500 us). This behavior is implemented by the underlying Servo library. Following attaches just use the last position of this Servo.
Every Arduino architecture with a Servo library will work without any modifications in blocking mode.
Non blocking behavior can always be achieved manually by calling update() or updateAllServos() in a loop - see ThreeServos example.
Interrupt based movement (movement without calling update() manually in a loop) is supported for the following Arduino architectures:
avr, megaavr, sam, samd, esp8266, esp32, stm32, STM32F1 and apollo3.
It is not planned to support the ATtiny architecture, but you are invited to do it by yourself and send a pull request.
On AVR Timer1 is used for the Arduino Servo library. To have non blocking easing functions its unused Channel B is used to generate an interrupt 100 µs before the end of the 20 ms Arduino Servo refresh period. This interrupt then updates all servo values for the next servo signal refresh period.
| Platform | Timer | Library providing the timer |
|---|---|---|
| avr | Timer1 | Servo.h |
| ATmega | Timer5 | Servo.h |
| megaavr | TCA0 | |
| sam | ID_TC8 (TC2 channel 2) | |
| samd | TC5 | |
| esp8266 + esp32 | Ticker | Ticker.h |
| stm32 | TIM3 | HardwareTimer.h |
| STM32F1 | 3 or 7 | HardwareTimer.h |
| Teensy | IntervalTimer | |
| apollo3 | timer 3 segment A | |
| Mbed | mbed::Ticker | Ticker.h |
| RP2040 / Pi Pico | default alarm pool | time.h |
If timer support is available for a platform the library can be ported by adding code for the Timer20ms like is was done for ESP and STM.
To add a new platform, the following steps have to be performed:
#define TRACE in ServoEasing.hpp and enabling interrupt timing feedback by activating the line #define MEASURE_SERVO_EASING_INTERRUPT_TIMING in ServoEasing.hpp.Good luck!
If you see strange behavior, you can open the library file ServoEasing.hpp and activate the line #define TRACE or #define DEBUG. This will print internal information visible in the Arduino Serial Monitor which may help finding the reason for it.
synchronizeAllServosAndStartInterrupt().REFRESH_INTERVAL_MILLIS to SERVO_REFRESH_INTERVAL_MICROS.PCA9685Init(uint32_t aActualPCA9685ClockFrequencyHertz) and PCA9685InitWithExternalClock(uint32_t aExternalClockFrequencyHertz).reattach();InitializeAndCheckI2CConnection() to initializeAndCheckI2CConnection().applyTrimAndreverseToTargetMicrosecondsOrUnits() to applyTrimAndReverseToTargetMicrosecondsOrUnits().mCurrentMicrosecondsOrUnits to mLastTargetMicrosecondsOrUnits to make clear, that trim and reverse is NOT applied to this value.DISABLE_MIN_AND_MAX_CONSTRAINTS to ENABLE_MIN_AND_MAX_CONSTRAINTS. Constraint checking is now disabled by default.printEasingType() for non AVR platforms.setEaseTo(), setEaseToD(), startEaseTo() and startEaseToD() with first parameter as unsigned int to avoid compiler errors call of overloaded 'startEaseTo(unsigned int....USE_USER_PROVIDED_SERVO_LIB macro.setDegreeForAllServos() to setIntegerDegreeForAllServos() and added function setFloatDegreeForAllServos().getCurrentMicroseconds().PCA9685_ACTUAL_CLOCK_FREQUENCY macro.synchronizeAndEaseToArrayPositions() to setEaseToForAllServosSynchronizeAndWaitForAllServosToStop().DISABLE_PAUSE_RESUME.ENABLE_EXTERNAL_SERVO_TIMER_HANDLER macro.ENABLE_MICROS_AS_DEGREE_PARAMETER to DISABLE_MICROS_AS_DEGREE_PARAMETER thus enabling micros as parameter by default.PRECISION.printEasingType().doWrite for setTrim() from false to true.DISABLE_MIN_AND_MAX_CONSTRAINTS.attach() functions with initial degree parameter to be written immediately. This replaces the attach() and write() combination at setup.ServoEasing.cpp to ServoEasing.hpp and LightweightServo.cpp to LightweightServo.hpp.ENABLE_MICROS_AS_DEGREE_PARAMETER also available for PCA9685 expander.sServoArrayMaxIndex, sServoNextPositionArray and sServoArray to ServoEasing::sServoArrayMaxIndex, ServoEasing::ServoEasingNextPositionArray and ServoEasing::ServoEasingArray.ENABLE_MICROS_AS_DEGREE_PARAMETER to allow usage of microseconds instead of degree as function arguments for all functions using degrees as argument.STM32F1xx / ARDUINO_ARCH_STM32.stop(), continueWithInterrupts() and continueWithoutInterrupts() functions.PCA9685_Expander and standard Servos can be controlled simultaneously by defining USE_SERVO_LIB.areInterruptsActive().Wire.begin() in setup of PCA9685_Expander example.isMovingAndCallYield() yield() only called/required for an ESP8266.areInterruptsActive(), especially for ESP32.Print * instead of Stream *.delayAndUpdateAndWaitForAllServosToStop().doWrite which is default false in contrast to older versions, where a write was always performed.attach( aPin, aMicrosecondsForServoLowDegree, aMicrosecondsForServoHighDegree, aServoLowDegree, aServoHighDegree) function for arbitrary mapping of servo degree to servo pulse width.sServoArray[] now depends from order of calling attach() and not from order of declaration.detach() function.RobotArmControl + QuadrupedControl examples refactored.SpeedTest example. Now also able to change the width of the refresh period.AsymetricEasing example overhauled.easeTo() and write() store their degree parameter now also in sServoNextPositionArray.setSpeed(), getSpeed(), setSpeedForAllServos() and added ease* functions without speed parameter.getEndMicrosecondsOrUnits(), getDeltaMicrosecondsOrUnits().PROVIDE_ONLY_LINEAR_MOVEMENT to save additional 1500 bytes program memory if enabled.clipDegreeSpecial().Initial Arduino library version.
A commercial license is granted to "Video Ispezioni Srl" for use in a pipeline inspection and rehabilitation robot as from 10.10.2024.