45 #ifndef _SERVO_EASING_HPP
46 #define _SERVO_EASING_HPP
52 #if defined(USE_LEIGHTWEIGHT_SERVO_LIB) && (defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__))
75 #if defined(MEASURE_SERVO_EASING_INTERRUPT_TIMING)
76 #include "digitalWriteFast.h"
77 #define TIMING_OUTPUT_PIN 12
80 #if defined(ESP8266) || defined(ESP32)
88 #elif defined(STM32F1xx) // for "Generic STM32F1 series / STMicroelectronics:stm32" from STM32 Boards from STM32 cores of Arduino Board manager
90 #include <HardwareTimer.h>
95 HardwareTimer Timer20ms(TIM4);
97 #elif defined(__STM32F1__) // or ARDUINO_ARCH_STM32F1 for "Generic STM32F103C series / stm32duino:STM32F1" from STM32F1 Boards (STM32duino.com) of Arduino Board manager
99 #include <HardwareTimer.h>
100 # if defined(STM32_HIGH_DENSITY)
101 HardwareTimer Timer20ms(7);
107 HardwareTimer Timer20ms(3);
110 #elif defined(__SAM3X8E__) // Arduino DUE
120 #define TC_FOR_20_MS_TIMER TC2
121 #define CHANNEL_FOR_20_MS_TIMER 2
122 #define ID_TC_FOR_20_MS_TIMER ID_TC8 // Timer 8 is TC2 channel 2
123 #define IRQn_FOR_20_MS_TIMER TC8_IRQn
124 #define HANDLER_FOR_20_MS_TIMER TC8_Handler
126 #elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE + Sparkfun Apollo3
127 mbed::Ticker Timer20ms;
135 #elif defined(ARDUINO_ARCH_RP2040) // Raspberry Pi Pico, Adafruit Feather RP2040, etc.
136 #include "pico/time.h"
137 repeating_timer_t Timer20ms;
140 bool handleServoTimerInterruptHelper(repeating_timer_t*) {
145 #elif defined(TEENSYDUINO)
147 IntervalTimer Timer20ms;
165 const char easeTypeLinear[]
PROGMEM =
"linear";
166 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
167 const char easeTypeQuadratic[]
PROGMEM =
"quadratic";
168 const char easeTypeCubic[]
PROGMEM =
"cubic";
169 const char easeTypeQuartic[]
PROGMEM =
"quartic";
170 const char easeTypePrecision[]
PROGMEM =
"precision";
171 const char easeTypeUser[]
PROGMEM =
"user";
172 const char easeTypeNotDefined[]
PROGMEM =
"";
173 const char easeTypeDummy[]
PROGMEM =
"dummy";
174 # if !defined(DISABLE_COMPLEX_FUNCTIONS)
175 const char easeTypeSine[]
PROGMEM =
"sine";
176 const char easeTypeCircular[]
PROGMEM =
"circular";
177 const char easeTypeBack[]
PROGMEM =
"back";
178 const char easeTypeElastic[]
PROGMEM =
"elastic";
179 const char easeTypeBounce[]
PROGMEM =
"bounce";
181 #endif // !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
183 const char *
const easeTypeStrings[]
PROGMEM = { easeTypeLinear
184 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
185 , easeTypeQuadratic, easeTypeCubic, easeTypeQuartic, easeTypeNotDefined, easeTypeNotDefined, easeTypeUser, easeTypeDummy,
186 # if !defined(DISABLE_COMPLEX_FUNCTIONS)
187 easeTypeSine, easeTypeCircular, easeTypeBack, easeTypeElastic, easeTypeBounce, easeTypePrecision
192 #if defined(USE_PCA9685_SERVO_EXPANDER)
194 # if defined(USE_SOFT_I2C_MASTER)
195 #include "SoftI2CMasterConfig.h"
196 #include "SoftI2CMaster.h"
197 # endif // defined(USE_SOFT_I2C_MASTER)
200 # define _BV(bit) (1 << (bit))
203 #if defined(USE_SOFT_I2C_MASTER)
209 mPCA9685I2CAddress = aPCA9685I2CAddress;
210 #if !defined(USE_SOFT_I2C_MASTER)
211 mI2CClass = aI2CClass;
220 #if defined(USE_SERVO_LIB)
221 mServoIsConnectedToExpander =
true;
223 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
225 # if defined(ENABLE_EASE_USER)
231 #if !defined(DISABLE_MIN_AND_MAX_CONSTRAINTS)
236 #if defined(MEASURE_SERVO_EASING_INTERRUPT_TIMING)
237 pinMode(TIMING_OUTPUT_PIN, OUTPUT);
241 void ServoEasing::I2CInit() {
243 #if defined(USE_SOFT_I2C_MASTER)
247 mI2CClass->setClock(I2C_CLOCK_FREQUENCY);
248 # if defined (ARDUINO_ARCH_AVR) // Other platforms do not have this new function
249 mI2CClass->setWireTimeout();
256 void ServoEasing::PCA9685Reset() {
258 #if defined(USE_SOFT_I2C_MASTER)
265 mI2CClass->endTransmission();
273 void ServoEasing::PCA9685Init() {
281 void ServoEasing::I2CWriteByte(uint8_t aAddress, uint8_t aData) {
282 #if defined(USE_SOFT_I2C_MASTER)
283 i2c_start(mPCA9685I2CAddress << 1);
288 mI2CClass->beginTransmission(mPCA9685I2CAddress);
289 mI2CClass->write(aAddress);
290 mI2CClass->write(aData);
291 # if defined(LOCAL_DEBUG)
292 uint8_t tWireReturnCode = mI2CClass->endTransmission();
293 if (tWireReturnCode != 0) {
295 Serial.print((
char) (tWireReturnCode +
'0'));
298 mI2CClass->endTransmission();
309 void ServoEasing::setPWM(uint16_t aPWMOffValueAsUnits) {
310 #if defined(USE_SOFT_I2C_MASTER)
311 i2c_start(mPCA9685I2CAddress << 1);
313 i2c_write(aPWMOffValueAsUnits);
314 i2c_write(aPWMOffValueAsUnits >> 8);
317 mI2CClass->beginTransmission(mPCA9685I2CAddress);
320 mI2CClass->write(aPWMOffValueAsUnits);
321 mI2CClass->write(aPWMOffValueAsUnits >> 8);
322 # if defined(LOCAL_DEBUG) && not defined(ESP32)
325 uint8_t tWireReturnCode = mI2CClass->endTransmission();
326 if (tWireReturnCode != 0) {
328 Serial.print((
char) (tWireReturnCode +
'0'));
331 mI2CClass->endTransmission();
342 void ServoEasing::setPWM(uint16_t aPWMOnStartValueAsUnits, uint16_t aPWMPulseDurationAsUnits) {
343 #if defined(USE_SOFT_I2C_MASTER)
344 i2c_start(mPCA9685I2CAddress << 1);
346 i2c_write(aPWMOnStartValueAsUnits);
347 i2c_write(aPWMOnStartValueAsUnits >> 8);
348 i2c_write(aPWMOnStartValueAsUnits + aPWMPulseDurationAsUnits);
349 i2c_write((aPWMOnStartValueAsUnits + aPWMPulseDurationAsUnits) >> 8);
352 mI2CClass->beginTransmission(mPCA9685I2CAddress);
354 mI2CClass->write(aPWMOnStartValueAsUnits);
355 mI2CClass->write(aPWMOnStartValueAsUnits >> 8);
356 mI2CClass->write(aPWMOnStartValueAsUnits + aPWMPulseDurationAsUnits);
357 mI2CClass->write((aPWMOnStartValueAsUnits + aPWMPulseDurationAsUnits) >> 8);
358 # if defined(LOCAL_DEBUG) && not defined(ESP32)
361 uint8_t tWireReturnCode = mI2CClass->endTransmission();
362 if (tWireReturnCode != 0) {
364 Serial.print((
char) (tWireReturnCode +
'0'));
367 mI2CClass->endTransmission();
372 int ServoEasing::MicrosecondsToPCA9685Units(
int aMicroseconds) {
376 #if defined(USE_SERVO_LIB)
377 if (!mServoIsConnectedToExpander) {
378 return aMicroseconds;
384 int ServoEasing::PCA9685UnitsToMicroseconds(
int aPCA9685Units) {
392 #endif // defined(USE_PCA9685_SERVO_EXPANDER)
396 #if (!defined(USE_PCA9685_SERVO_EXPANDER) || defined(USE_SERVO_LIB)) && !defined(USE_LEIGHTWEIGHT_SERVO_LIB)
405 #if !defined(DISABLE_PAUSE_RESUME)
410 #if defined(USE_PCA9685_SERVO_EXPANDER) && defined(USE_SERVO_LIB)
411 mServoIsConnectedToExpander =
false;
413 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
415 # if defined(ENABLE_EASE_USER)
421 #if !defined(DISABLE_MIN_AND_MAX_CONSTRAINTS)
426 #if defined(MEASURE_SERVO_EASING_INTERRUPT_TIMING)
427 pinMode(TIMING_OUTPUT_PIN, OUTPUT);
448 uint8_t
ServoEasing::attach(
int aPin,
int aMicrosecondsForServo0Degree,
int aMicrosecondsForServo180Degree) {
449 return attach(aPin, aMicrosecondsForServo0Degree, aMicrosecondsForServo180Degree, 0, 180);
474 int aMicrosecondsForServo180Degree) {
475 return attach(aPin, aInitialDegreeOrMicrosecond, aMicrosecondsForServo0Degree, aMicrosecondsForServo180Degree, 0, 180);
479 int aMicrosecondsForServo0Degree,
int aMicrosecondsForServo180Degree) {
480 return attachWithTrim(aPin, aTrimDegreeOrMicrosecond, aInitialDegreeOrMicrosecond, aMicrosecondsForServo0Degree,
481 aMicrosecondsForServo180Degree, 0, 180);
490 uint8_t
ServoEasing::attach(
int aPin,
int aInitialDegreeOrMicrosecond,
int aMicrosecondsForServoLowDegree,
491 int aMicrosecondsForServoHighDegree,
int aServoLowDegree,
int aServoHighDegree) {
492 uint8_t tReturnValue =
attach(aPin, aMicrosecondsForServoLowDegree, aMicrosecondsForServoHighDegree, aServoLowDegree,
494 write(aInitialDegreeOrMicrosecond);
499 int aMicrosecondsForServoLowDegree,
int aMicrosecondsForServoHighDegree,
int aServoLowDegree,
int aServoHighDegree) {
500 uint8_t tReturnValue =
attach(aPin, aMicrosecondsForServoLowDegree, aMicrosecondsForServoHighDegree, aServoLowDegree,
502 setTrim(aTrimDegreeOrMicrosecond,
false);
503 write(aInitialDegreeOrMicrosecond);
517 uint8_t
ServoEasing::attach(
int aPin,
int aMicrosecondsForServoLowDegree,
int aMicrosecondsForServoHighDegree,
int aServoLowDegree,
518 int aServoHighDegree) {
522 int tMicrosecondsForServo0Degree = map(0, aServoLowDegree, aServoHighDegree, aMicrosecondsForServoLowDegree,
523 aMicrosecondsForServoHighDegree);
524 int tMicrosecondsForServo180Degree = map(180, aServoLowDegree, aServoHighDegree, aMicrosecondsForServoLowDegree,
525 aMicrosecondsForServoHighDegree);
528 #if defined(USE_PCA9685_SERVO_EXPANDER)
529 # if defined(USE_SERVO_LIB)
530 if (mServoIsConnectedToExpander) {
554 for (uint_fast8_t tServoIndex = 0; tServoIndex <
MAX_EASING_SERVOS; ++tServoIndex) {
557 tReturnValue = tServoIndex;
566 #if defined(LOCAL_TRACE)
567 Serial.print(
"Index=");
568 Serial.print(tReturnValue);
569 Serial.print(
" pin=");
571 Serial.print(
" low=");
572 Serial.print(aServoLowDegree);
574 Serial.print(aMicrosecondsForServoLowDegree);
575 Serial.print(
" high=");
576 Serial.print(aServoHighDegree);
578 Serial.print(aMicrosecondsForServoHighDegree);
583 #if defined(USE_PCA9685_SERVO_EXPANDER)
585 # if defined(USE_SERVO_LIB)
586 if (mServoIsConnectedToExpander) {
587 if (tReturnValue == 0) {
595 if (tReturnValue == 0) {
602 #endif // defined(USE_PCA9685_SERVO_EXPANDER)
604 #if !defined(USE_PCA9685_SERVO_EXPANDER) || defined(USE_SERVO_LIB)
614 # if defined(USE_LEIGHTWEIGHT_SERVO_LIB)
615 if(aPin != 9 && aPin != 10) {
624 # if defined(ARDUINO_ARCH_APOLLO3)
625 Servo::attach(aPin, tMicrosecondsForServo0Degree, tMicrosecondsForServo180Degree);
628 return Servo::attach(aPin, tMicrosecondsForServo0Degree, tMicrosecondsForServo180Degree);
629 # endif // defined(ARDUINO_ARCH_APOLLO3)
630 # endif // defined(USE_LEIGHTWEIGHT_SERVO_LIB)
631 #endif // defined(USE_SERVO_LIB)
646 #if defined(USE_PCA9685_SERVO_EXPANDER)
647 # if defined(USE_SERVO_LIB)
648 if (mServoIsConnectedToExpander) {
651 # if defined(USE_LEIGHTWEIGHT_SERVO_LIB)
659 # endif // defined(USE_SERVO_LIB)
662 # if defined(USE_LEIGHTWEIGHT_SERVO_LIB)
667 #endif // defined(USE_PCA9685_SERVO_EXPANDER)
688 mSpeed = aDegreesPerSecond;
698 if (aTrimDegreeOrMicrosecond >= 0) {
722 #if !defined(DISABLE_MIN_AND_MAX_CONSTRAINTS)
735 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
744 # if defined(ENABLE_EASE_USER)
746 void *aUserDataPointer) {
754 #endif // !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
760 #if defined(LOCAL_TRACE)
761 Serial.print(F(
"write "));
762 Serial.print(aTargetDegreeOrMicrosecond);
769 #if defined(LOCAL_TRACE)
770 Serial.print(F(
"Error: detached servo"));
779 #if defined(LOCAL_TRACE)
780 Serial.print(F(
"write "));
781 Serial.print(aTargetDegreeOrMicrosecond);
788 #if defined(LOCAL_TRACE)
789 Serial.print(F(
"Error: detached servo"));
806 #if defined(LOCAL_TRACE)
807 Serial.print(F(
"Error: detached servo"));
811 #if !defined(DISABLE_MIN_AND_MAX_CONSTRAINTS)
820 #if defined(LOCAL_TRACE)
824 Serial.print(F(
" us/u="));
825 Serial.print(aTargetDegreeOrMicrosecond);
839 #if defined(LOCAL_TRACE)
840 Serial.print(F(
" r="));
841 Serial.print(aTargetDegreeOrMicrosecond);
845 #if defined(PRINT_FOR_SERIAL_PLOTTER) && !defined(LOCAL_TRACE)
847 Serial.print(aTargetDegreeOrMicrosecond);
850 #if defined(USE_PCA9685_SERVO_EXPANDER)
851 # if defined(LOCAL_TRACE)
853 Serial.print(F(
" s="));
856 # if defined(USE_SERVO_LIB)
857 if (mServoIsConnectedToExpander) {
860 # if defined(USE_LEIGHTWEIGHT_SERVO_LIB)
861 writeMicrosecondsLightweightServo(aTargetDegreeOrMicrosecond, (
mServoPin == 9));
863 Servo::writeMicroseconds(aTargetDegreeOrMicrosecond);
875 # if defined(USE_LEIGHTWEIGHT_SERVO_LIB)
876 writeMicrosecondsLightweightServo(aTargetDegreeOrMicrosecond, (
mServoPin == 9));
878 Servo::writeMicroseconds(aTargetDegreeOrMicrosecond);
882 #if defined(LOCAL_TRACE) && !defined(PRINT_FOR_SERIAL_PLOTTER)
893 #if defined(USE_PCA9685_SERVO_EXPANDER)
894 # if defined(USE_SERVO_LIB)
895 if (!mServoIsConnectedToExpander) {
900 tResult = (tResult * 180) + 928;
927 #if defined(USE_PCA9685_SERVO_EXPANDER)
928 # if defined(USE_SERVO_LIB)
929 if (mServoIsConnectedToExpander) {
931 tResult = (tResult * 180) + 190;
934 tResult = (tResult * 180) + 928;
938 tResult = (tResult * 180) + 190;
942 tResult = (tResult * 180) + 928;
950 #if defined(USE_PCA9685_SERVO_EXPANDER)
951 return PCA9685UnitsToMicroseconds(aMicrosecondsOrUnits);
953 return aMicrosecondsOrUnits;
964 #if defined(DISABLE_MICROS_AS_DEGREE_PARAMETER)
967 #else // defined(DISABLE_MICROS_AS_DEGREE_PARAMETER)
980 # if defined(USE_PCA9685_SERVO_EXPANDER)
981 return MicrosecondsToPCA9685Units(aDegreeOrMicrosecond);
983 return aDegreeOrMicrosecond;
986 #endif // defined(DISABLE_MICROS_AS_DEGREE_PARAMETER)
991 #if defined(DISABLE_MICROS_AS_DEGREE_PARAMETER)
1006 # if defined(USE_PCA9685_SERVO_EXPANDER)
1007 return MicrosecondsToPCA9685Units(aDegreeOrMicrosecond);
1009 return aDegreeOrMicrosecond;
1012 #endif // defined(DISABLE_MICROS_AS_DEGREE_PARAMETER)
1025 return tResultValue;
1046 #if defined(PRINT_FOR_SERIAL_PLOTTER)
1058 #if defined(PRINT_FOR_SERIAL_PLOTTER)
1069 #if defined(PRINT_FOR_SERIAL_PLOTTER)
1080 #if defined(PRINT_FOR_SERIAL_PLOTTER)
1128 if (aDegreesPerSecond == 0) {
1129 #if defined(LOCAL_DEBUG)
1130 Serial.println(F(
"Speed is 0 -> set to 1"));
1132 aDegreesPerSecond = 1;
1138 int tTargetDegree = aTargetDegreeOrMicrosecond;
1139 #if defined(DISABLE_MICROS_AS_DEGREE_PARAMETER)
1140 tTargetDegree = aTargetDegreeOrMicrosecond;
1153 uint_fast16_t tMillisForCompleteMove = abs(tTargetDegree - tCurrentDegree) *
MILLIS_IN_ONE_SECOND / aDegreesPerSecond;
1156 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
1158 tMillisForCompleteMove *= 2;
1162 return startEaseToD(aTargetDegreeOrMicrosecond, tMillisForCompleteMove, aStartUpdateByInterrupt);
1169 if (aDegreesPerSecond == 0) {
1170 #if defined(LOCAL_DEBUG)
1171 Serial.println(F(
"Speed is 0 -> set to 1"));
1173 aDegreesPerSecond = 1;
1180 int tTargetDegree = aTargetDegreeOrMicrosecond;
1181 #if defined(DISABLE_MICROS_AS_DEGREE_PARAMETER)
1182 tTargetDegree = aTargetDegreeOrMicrosecond;
1195 uint_fast16_t tMillisForCompleteMove = abs(tTargetDegree - tCurrentDegree) *
MILLIS_IN_ONE_SECOND / aDegreesPerSecond;
1198 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
1200 tMillisForCompleteMove *= 2;
1204 return startEaseToD(aTargetDegreeOrMicrosecond, tMillisForCompleteMove, aStartUpdateByInterrupt);
1237 #if defined(LOCAL_TRACE)
1238 Serial.print(F(
"Error: detached servo"));
1243 #if defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
1259 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
1268 #if defined(LOCAL_TRACE)
1270 #elif defined(LOCAL_DEBUG)
1277 #if !defined(DISABLE_PAUSE_RESUME)
1284 return tReturnValue;
1292 #if defined(LOCAL_TRACE)
1293 Serial.println(F(
"Error: detached servo"));
1298 #if defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
1314 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
1323 #if defined(LOCAL_TRACE)
1325 #elif defined(LOCAL_DEBUG)
1333 #if !defined(DISABLE_PAUSE_RESUME)
1340 return tReturnValue;
1348 #if !defined(ENABLE_EXTERNAL_SERVO_TIMER_HANDLER)
1357 #if !defined(DISABLE_PAUSE_RESUME)
1364 #if !defined(DISABLE_PAUSE_RESUME)
1372 #if !defined(DISABLE_PAUSE_RESUME)
1385 #if defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
1419 #else // PROVIDE_ONLY_LINEAR_MOVEMENT
1423 # if defined(PRINT_FOR_SERIAL_PLOTTER)
1430 #if !defined(DISABLE_PAUSE_RESUME)
1448 int tNewMicrosecondsOrUnits;
1466 float tFactorOfMovementCompletion = 0.0;
1476 tFactorOfMovementCompletion = 1.0 - (
callEasingFunction(1.0 - tFactorOfTimeCompletion));
1479 if (tFactorOfTimeCompletion <= 0.5) {
1482 tFactorOfMovementCompletion = 0.5 * (
callEasingFunction(2.0 * tFactorOfTimeCompletion));
1486 tFactorOfMovementCompletion = 1.0 - (
callEasingFunction(1.0 - (2.0 * tFactorOfTimeCompletion)));
1491 tFactorOfMovementCompletion = 1.0 - (0.5 * (
callEasingFunction(2.0 - (2.0 * tFactorOfTimeCompletion))));
1495 tFactorOfMovementCompletion = 1.0 -
callEasingFunction((2.0 * tFactorOfTimeCompletion) - 1.0);
1500 #if defined(LOCAL_TRACE)
1501 Serial.print(F(
"FactorOfTimeCompletion="));
1502 Serial.print(tFactorOfTimeCompletion);
1503 Serial.print(F(
" FactorOfMovementCompletion="));
1504 Serial.println(tFactorOfMovementCompletion);
1507 #if defined(ENABLE_EASE_USER) || defined(ENABLE_EASE_PRECISION) // Only these two types returns microseconds yet
1511 #if defined(USE_PCA9685_SERVO_EXPANDER)
1512 # if defined(USE_SERVO_LIB)
1513 if (mServoIsConnectedToExpander) {
1514 tNewMicrosecondsOrUnits = MicrosecondsToPCA9685Units(tFactorOfMovementCompletion);
1516 tNewMicrosecondsOrUnits = tFactorOfMovementCompletion;
1519 tNewMicrosecondsOrUnits = MicrosecondsToPCA9685Units(tFactorOfMovementCompletion);
1522 tNewMicrosecondsOrUnits = tFactorOfMovementCompletion;
1526 #if defined(ENABLE_EASE_USER)
1540 # if defined(PRINT_FOR_SERIAL_PLOTTER)
1557 switch (tEasingType) {
1559 # if defined(ENABLE_EASE_USER)
1568 # if defined(ENABLE_EASE_QUADRATIC)
1572 # if defined(ENABLE_EASE_CUBIC)
1576 # if defined(ENABLE_EASE_QUARTIC)
1581 # if defined(ENABLE_EASE_SINE)
1585 # if defined(ENABLE_EASE_CIRCULAR)
1589 # if defined(ENABLE_EASE_BACK)
1593 # if defined(ENABLE_EASE_ELASTIC)
1597 # if defined(ENABLE_EASE_BOUNCE)
1601 # if defined(ENABLE_EASE_PRECISION)
1610 #endif //PROVIDE_ONLY_LINEAR_MOVEMENT
1616 #if defined(ESP8266)
1618 #elif defined(ESP32)
1629 #if defined(ESP8266)
1631 #elif defined(ESP32)
1691 # if defined(__AVR__)
1692 const char *tEaseTypeStringPtr = (
char*) pgm_read_word(&easeTypeStrings[aEasingType &
EASE_TYPE_MASK]);
1693 aSerial->print((__FlashStringHelper*) (tEaseTypeStringPtr));
1695 aSerial->print(easeTypeStrings[aEasingType]);
1699 aSerial->print(F(
"_in"));
1701 aSerial->print(F(
"_out"));
1703 aSerial->print(F(
"_in_out"));
1705 aSerial->print(F(
"_bouncing_in_out"));
1717 aSerial->print(
'/');
1719 aSerial->print(F(
": "));
1722 if (doExtendedOutput) {
1723 aSerial->print(
'|');
1727 aSerial->print(F(
" -> "));
1729 if (doExtendedOutput) {
1730 aSerial->print(
'|');
1734 aSerial->print(F(
" = "));
1741 aSerial->print(tDelta);
1742 if (doExtendedOutput) {
1743 aSerial->print(
'|');
1747 aSerial->print(F(
" in "));
1749 aSerial->print(F(
" ms"));
1751 aSerial->print(F(
" with speed="));
1754 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
1755 aSerial->print(F(
" and easingType=0x"));
1757 aSerial->print(
'|');
1761 if (doExtendedOutput) {
1762 aSerial->print(F(
" MillisAtStartMove="));
1775 aSerial->print(F(
"0="));
1777 aSerial->print(F(
" 180="));
1780 aSerial->print(F(
" trim="));
1786 aSerial->print(
'|');
1789 aSerial->print(F(
" reverse="));
1792 #if defined(USE_PCA9685_SERVO_EXPANDER)
1793 aSerial->print(F(
" PCA9685I2CAddress=0x"));
1794 aSerial->print(mPCA9685I2CAddress, HEX);
1795 #if !defined(USE_SOFT_I2C_MASTER)
1796 aSerial->print(F(
" &Wire=0x"));
1798 aSerial->print((uint_fast16_t) mI2CClass, HEX);
1801 # if defined(USE_SERVO_LIB)
1802 aSerial->print(F(
" at expander="));
1803 aSerial->print(mServoIsConnectedToExpander);
1807 aSerial->print(F(
" callback=0x"));
1810 aSerial->print(F(
" MAX_EASING_SERVOS="));
1813 aSerial->print(F(
" this=0x"));
1814 aSerial->println((uint_fast16_t)
this, HEX);
1822 if (aDegreeToClip) {
1823 return aDegreeToClip;
1825 if (aDegreeToClip < 218) {
1837 #if !defined(ENABLE_EXTERNAL_SERVO_TIMER_HANDLER)
1838 # if defined(STM32F1xx) && STM32_CORE_VERSION_MAJOR == 1 && STM32_CORE_VERSION_MINOR <= 8 // for "Generic STM32F1 series" from STM32 Boards from STM32 cores of Arduino Board manager
1844 # if defined(USE_PCA9685_SERVO_EXPANDER)
1846 # if !defined(ARDUINO_ARCH_MBED)
1855 #endif // !defined(ENABLE_EXTERNAL_SERVO_TIMER_HANDLER)
1867 #if defined(__AVR__)
1868 # if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
1869 # if defined(USE_PCA9685_SERVO_EXPANDER) && !defined(USE_SERVO_LIB)
1871 TCCR5A = _BV(WGM11);
1872 TCCR5B = _BV(WGM13) | _BV(WGM12) | _BV(CS11);
1876 TIFR5 |= _BV(OCF5B);
1877 TIMSK5 |= _BV(OCIE5B);
1880 # elif defined(__AVR_ATmega4808__) || defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3217__) // Thinary Nano Every with MegaCoreX, Uno WiFi Rev 2, Nano Every, Tiny Core 32 Dev Board
1886 TCA0.SINGLE.CTRLD = 0;
1887 TCA0.SINGLE.CTRLB = TCA_SINGLE_WGMODE_NORMAL_gc;
1889 TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV8_gc | TCA_SINGLE_ENABLE_bm;
1890 TCA0.SINGLE.INTCTRL = TCA_SINGLE_OVF_bm;
1892 # if defined(MEGACOREX) && defined(USE_TIMERB2)
1893 #error It seems, that not the Megacore Servo library, but an Arduino Servo library is taken for compile, which will lead to errors. You must update MEGACOREX to >= 1.1.1 or remove the Arduino Servo library to fix this.
1895 # elif defined(TCCR1B) && defined(TIFR1) // Uno, Nano etc.
1902 # if defined(USE_PCA9685_SERVO_EXPANDER) && !defined(USE_SERVO_LIB)
1904 TCCR1A = _BV(WGM11);
1905 TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11);
1909 TIFR1 |= _BV(OCF1B);
1910 TIMSK1 |= _BV(OCIE1B);
1916 TCCR1B |= _BV(ICNC1);
1917 # if !defined(USE_LEIGHTWEIGHT_SERVO_LIB)
1922 #error "This AVR CPU is not supported by ServoEasing"
1925 #elif defined(ESP8266) || defined(ESP32)
1933 #elif defined(STM32F1xx) // for "STMicroelectronics:stm32" from STM32 Boards from STM32 cores of Arduino Board manager
1934 Timer20ms.setMode(LL_TIM_CHANNEL_CH1, TIMER_OUTPUT_COMPARE, NC);
1939 #elif defined(__STM32F1__) // for "stm32duino:STM32F1 Generic STM32F103C series" from STM32F1 Boards (Roger Clark STM32duino.com)
1940 Timer20ms.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE);
1943 Timer20ms.refresh();
1945 #elif defined(__SAM3X8E__) // Arduino DUE
1946 pmc_set_writeprotect(
false);
1947 pmc_enable_periph_clk(ID_TC_FOR_20_MS_TIMER);
1948 NVIC_ClearPendingIRQ(IRQn_FOR_20_MS_TIMER);
1949 NVIC_EnableIRQ(IRQn_FOR_20_MS_TIMER);
1952 TC_Configure(TC_FOR_20_MS_TIMER, CHANNEL_FOR_20_MS_TIMER, TC_CMR_TCCLKS_TIMER_CLOCK3 | TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC);
1953 TC_SetRC(TC_FOR_20_MS_TIMER, CHANNEL_FOR_20_MS_TIMER, (F_CPU / 32) /
REFRESH_FREQUENCY);
1955 TC_Start(TC_FOR_20_MS_TIMER, CHANNEL_FOR_20_MS_TIMER);
1958 TC_FOR_20_MS_TIMER->TC_CHANNEL[CHANNEL_FOR_20_MS_TIMER].TC_IER = TC_IER_CPCS;
1960 TC_FOR_20_MS_TIMER->TC_CHANNEL[CHANNEL_FOR_20_MS_TIMER].TC_IDR = ~TC_IER_CPCS;
1962 #elif defined(ARDUINO_ARCH_SAMD)
1965 TcCount16 *TC = (TcCount16*) TC5;
1966 # if defined(__SAMD51__)
1977 GCLK->PCHCTRL[TC5_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
1978 while (GCLK->SYNCBUSY.reg > 0);
1981 TC->CTRLA.reg &= ~TC_CTRLA_ENABLE;
1982 while (TC->SYNCBUSY.bit.ENABLE);
1984 TC->CTRLA.reg = TC_CTRLA_SWRST;
1986 while (TC->SYNCBUSY.bit.SWRST);
1993 TC->CTRLA.reg |= TC_CTRLA_MODE_COUNT16 | TC_WAVE_WAVEGEN_MFRQ | TC_CTRLA_PRESCALER_DIV256 | TC_CTRLA_ENABLE;
1998 GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5));
2003 TC->CTRLA.reg &= ~TC_CTRLA_ENABLE;
2007 while (TC->STATUS.bit.SYNCBUSY == 1);
2009 TC->CTRLA.reg = TC_CTRLA_SWRST;
2011 while (TC->CTRLA.bit.SWRST);
2017 TC->CTRLA.reg |= TC_CTRLA_MODE_COUNT16| TC_CTRLA_WAVEGEN_MFRQ | TC_CTRLA_PRESCALER_DIV64 | TC_CTRLA_ENABLE;
2020 # endif // defined(__SAMD51__)
2023 NVIC_DisableIRQ(TC5_IRQn);
2024 NVIC_ClearPendingIRQ(TC5_IRQn);
2025 NVIC_SetPriority(TC5_IRQn, 0);
2026 NVIC_EnableIRQ(TC5_IRQn);
2029 TC->INTENSET.bit.MC0 = 1;
2046 #elif defined(ARDUINO_ARCH_MBED)
2049 #elif defined(ARDUINO_ARCH_RP2040)
2052 #elif defined(TEENSYDUINO)
2057 #warning Board / CPU is not covered by definitions using pre-processor symbols -> no timer available. Please extend ServoEasing.cpp.
2062 #if defined(__AVR_ATmega328P__)
2066 void setTimer1InterruptMarginMicros(uint16_t aInterruptMarginMicros){
2073 #if defined(__AVR__)
2074 # if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
2075 TIMSK5 &= ~(_BV(OCIE5B));
2077 # elif defined(__AVR_ATmega4808__) || defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3217__) // Thinary Nano Every with MegaCoreX, Uno WiFi Rev 2, Nano Every, Tiny Core 32 Dev Board
2078 TCA0.SINGLE.INTCTRL &= ~(TCA_SINGLE_OVF_bm);
2080 # elif defined(TIMSK1)// defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
2081 TIMSK1 &= ~(_BV(OCIE1B));
2084 #error "This AVR CPU is not supported by ServoEasing"
2087 #elif defined(ESP8266) || defined(ESP32)
2090 #elif defined(STM32F1xx) // for "Generic STM32F1 series" from STM32 Boards from STM32 cores of Arduino Board manager
2092 Timer20ms.setMode(LL_TIM_CHANNEL_CH1, TIMER_DISABLED);
2093 Timer20ms.detachInterrupt();
2095 #elif defined(__STM32F1__) // for "Generic STM32F103C series" from STM32F1 Boards (STM32duino.com) of Arduino Board manager
2097 Timer20ms.setMode(TIMER_CH1, TIMER_DISABLED);
2098 Timer20ms.detachInterrupt(TIMER_CH1);
2100 #elif defined(__SAM3X8E__) // Arduino DUE
2101 NVIC_DisableIRQ(IRQn_FOR_20_MS_TIMER);
2103 #elif defined(ARDUINO_ARCH_SAMD)
2104 TC5->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE;
2105 # if defined(__SAMD51__)
2106 while (TC5->COUNT16.STATUS.reg & TC_SYNCBUSY_STATUS);
2108 while (TC5->COUNT16.STATUS.reg & TC_STATUS_SYNCBUSY);
2111 #elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE + Sparkfun Apollo3
2114 #elif defined(ARDUINO_ARCH_RP2040)
2115 cancel_repeating_timer(&Timer20ms);
2120 #elif defined(TEENSYDUINO)
2132 #if defined(__AVR__)
2133 # if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
2134 ISR(TIMER5_COMPB_vect) {
2138 # elif defined(__AVR_ATmega4808__) || defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny3217__) // Thinary Nano Every with MegaCoreX, Uno WiFi Rev 2, Nano Every, Tiny Core 32 Dev Board
2139 ISR(TCA0_OVF_vect) {
2140 TCA0.SINGLE.INTFLAGS = TCA_SINGLE_OVF_bm;
2144 # else // defined(__AVR__)
2145 ISR(TIMER1_COMPB_vect) {
2146 # if defined(MEASURE_SERVO_EASING_INTERRUPT_TIMING)
2147 digitalWriteFast(TIMING_OUTPUT_PIN, HIGH);
2150 # if defined(MEASURE_SERVO_EASING_INTERRUPT_TIMING)
2151 digitalWriteFast(TIMING_OUTPUT_PIN, LOW);
2156 #elif defined(__SAM3X8E__) // Arduino DUE
2157 void HANDLER_FOR_20_MS_TIMER(
void) {
2158 # if defined(MEASURE_SERVO_EASING_INTERRUPT_TIMING)
2159 digitalWrite(TIMING_OUTPUT_PIN, HIGH);
2162 TC_GetStatus(TC_FOR_20_MS_TIMER, CHANNEL_FOR_20_MS_TIMER);
2164 # if defined(MEASURE_SERVO_EASING_INTERRUPT_TIMING)
2165 digitalWrite(TIMING_OUTPUT_PIN, LOW);
2169 #elif defined(ARDUINO_ARCH_SAMD)
2170 void TC5_Handler(
void) {
2171 # if defined(MEASURE_SERVO_EASING_INTERRUPT_TIMING)
2172 digitalWrite(TIMING_OUTPUT_PIN, HIGH);
2175 TC5->COUNT16.INTFLAG.bit.MC0 = 1;
2177 # if defined(MEASURE_SERVO_EASING_INTERRUPT_TIMING)
2178 digitalWrite(TIMING_OUTPUT_PIN, LOW);
2191 #endif // defined(__AVR__)
2197 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
2206 for (uint_fast8_t tServoIndex = 0; tServoIndex <= aNumberOfServos; ++tServoIndex) {
2253 aSerial->print(F(
"ServoNextPositionArray="));
2273 aSerial->print(F(
" | "));
2298 void setDegreeForAllServos(uint_fast8_t aNumberOfServos, va_list *aDegreeValues) {
2299 for (uint_fast8_t tServoIndex = 0; tServoIndex < aNumberOfServos; ++tServoIndex) {
2305 #if defined(va_start)
2309 void setDegreeForAllServos(uint_fast8_t aNumberOfServos, ...) {
2310 va_list aDegreeValues;
2311 va_start(aDegreeValues, aNumberOfServos);
2312 setDegreeForAllServos(aNumberOfServos, &aDegreeValues);
2313 va_end(aDegreeValues);
2323 bool tOneServoIsMoving =
false;
2328 || tOneServoIsMoving;
2331 return tOneServoIsMoving;
2340 bool tOneServoIsMoving =
false;
2347 return tOneServoIsMoving;
2356 bool tOneServoIsMoving =
false;
2363 return tOneServoIsMoving;
2381 #if !defined(ENABLE_EXTERNAL_SERVO_TIMER_HANDLER)
2387 #if !defined(DISABLE_PAUSE_RESUME)
2388 unsigned long tMillis = millis();
2418 bool tAllServosStopped =
true;
2425 #if defined(PRINT_FOR_SERIAL_PLOTTER)
2428 return tAllServosStopped;
2457 delay(aMillisDelay);
2478 uint_fast16_t tMaxMillisForCompleteMove = 0;
2479 uint32_t tMillisAtStartMove = 0;
2491 #if defined(LOCAL_TRACE)
2492 Serial.print(F(
"Number of servos="));
2494 Serial.print(F(
" MillisAtStartMove="));
2495 Serial.print(tMillisAtStartMove);
2496 Serial.print(F(
" MaxMillisForCompleteMove="));
2497 Serial.println(tMaxMillisForCompleteMove);
2511 if (aStartUpdateByInterrupt) {
2516 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
2529 return (aFactorOfTimeCompletion * aFactorOfTimeCompletion);
2533 return (aFactorOfTimeCompletion *
QuadraticEaseIn(aFactorOfTimeCompletion));
2545 return sin((aFactorOfTimeCompletion - 1) * M_PI_2) + 1;
2554 return 1 - sqrt(1 - (aFactorOfTimeCompletion * aFactorOfTimeCompletion));
2562 return (aFactorOfTimeCompletion * aFactorOfTimeCompletion * aFactorOfTimeCompletion)
2563 - (aFactorOfTimeCompletion * sin(aFactorOfTimeCompletion * M_PI));
2571 return sin(13 * M_PI_2 * aFactorOfTimeCompletion) * pow(2, 10 * (aFactorOfTimeCompletion - 1));
2574 #define PART_OF_LINEAR_MOVEMENT 0.8
2575 #define PART_OF_BOUNCE_MOVEMENT (1.0 - PART_OF_LINEAR_MOVEMENT)
2576 #define PART_OF_BOUNCE_MOVEMENT_HALF ((1.0 - PART_OF_LINEAR_MOVEMENT) / 2) // 0.1
2578 #define OVERSHOOT_AMOUNT_MILLIS 50 // around 5 degree
2579 #define OVERSHOOT_AMOUNT_UNITS 10 // around 5 degree
2592 return aFactorOfTimeCompletion;
2595 aFactorOfTimeCompletion = 1 - aFactorOfTimeCompletion;
2606 return 1 - aFactorOfTimeCompletion;
2608 return aFactorOfTimeCompletion;
2614 float tRemainingFactor;
2619 tRemainingFactor = 1.0 - tRemainingFactor;
2627 #if defined(USE_PCA9685_SERVO_EXPANDER)
2628 # if defined(USE_SERVO_LIB)
2629 if (mServoIsConnectedToExpander) {
2647 - (tBumpMicrosecondsOrUnits * tRemainingFactor * tRemainingFactor));
2653 - tBumpMicrosecondsOrUnits;
2665 float tFactorOfMovementCompletion;
2666 if (aFactorOfTimeCompletion < 4 / 11.0) {
2667 tFactorOfMovementCompletion = (121 * aFactorOfTimeCompletion * aFactorOfTimeCompletion) / 16.0;
2668 }
else if (aFactorOfTimeCompletion < 8 / 11.0) {
2669 tFactorOfMovementCompletion = (363 / 40.0 * aFactorOfTimeCompletion * aFactorOfTimeCompletion)
2670 - (99 / 10.0 * aFactorOfTimeCompletion) + 17 / 5.0;
2671 }
else if (aFactorOfTimeCompletion < 9 / 10.0) {
2672 tFactorOfMovementCompletion = (4356 / 361.0 * aFactorOfTimeCompletion * aFactorOfTimeCompletion)
2673 - (35442 / 1805.0 * aFactorOfTimeCompletion) + 16061 / 1805.0;
2675 tFactorOfMovementCompletion = (54 / 5.0 * aFactorOfTimeCompletion * aFactorOfTimeCompletion)
2676 - (513 / 25.0 * aFactorOfTimeCompletion) + 268 / 25.0;
2678 return tFactorOfMovementCompletion;
2680 #endif // !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
2686 #if defined(USE_PCA9685_SERVO_EXPANDER)
2693 #if defined(__AVR__)
2699 #if !defined(USE_SOFT_I2C_MASTER)
2706 #if defined(__AVR__)
2713 bool tRetValue =
false;
2714 aSerial->print(F(
"Try to communicate with I2C device at address=0x"));
2715 aSerial->println(aI2CAddress, HEX);
2719 #if defined(USE_SOFT_I2C_MASTER)
2721 if(!i2c_start(aI2CAddress << 1)){
2722 aSerial->println(F(
"No acknowledge received from the slave"));
2723 aSerial->print(F(
"Communication with I2C was successful, but found no"));
2726 aSerial->print(F(
"Found"));
2729 aSerial->print(F(
" I2C device attached at address: 0x"));
2730 aSerial->println(aI2CAddress, HEX);
2732 aSerial->println(F(
"I2C init failed"));
2734 #else // defined(USE_SOFT_I2C_MASTER)
2736 # if defined (ARDUINO_ARCH_AVR) // Other platforms do not have this new function
2738 Wire.beginTransmission(aI2CAddress);
2739 if (Wire.getWireTimeoutFlag()) {
2740 aSerial->println(F(
"Timeout accessing I2C bus. Wait for bus becoming available"));
2741 Wire.clearWireTimeoutFlag();
2748 Wire.beginTransmission(aI2CAddress);
2751 uint8_t tWireReturnCode = Wire.endTransmission(
true);
2752 if (tWireReturnCode == 0) {
2753 aSerial->print(F(
"Found"));
2755 aSerial->print(F(
"Error code="));
2756 aSerial->print(tWireReturnCode);
2757 aSerial->print(F(
". Communication with I2C was successful, but found no"));
2760 aSerial->print(F(
" I2C device attached at address: 0x"));
2761 aSerial->println(aI2CAddress, HEX);
2762 #endif // defined(USE_SOFT_I2C_MASTER)
2765 aSerial->println(F(
"PCA9685 expander not connected"));
2769 # endif // defined(USE_PCA9685_SERVO_EXPANDER)
2771 #if defined(LOCAL_DEBUG)
2774 #if defined(LOCAL_TRACE)
2777 #endif // _SERVO_EASING_HPP