ServoEasing
ServoEasing.hpp
Go to the documentation of this file.
1 /*
2  * ServoEasing.hpp
3  *
4  * Enables smooth movement from one servo position to another.
5  * Linear as well as other ease movements (e.g. cubic) for all servos attached to the Arduino Servo library are provided.
6  * Interface is in degree but internally only microseconds (if using Servo library) or units (if using PCA9685 expander) are used,
7  * since the resolution is better and we avoid the map function on every Servo.write().
8  * The blocking functions wait for 20 ms since this is the default refresh time of the used Servo library.
9  *
10  * The AVR Servo library supports only one timer, which means not more than 12 servos are supported using this library.
11  *
12  * Copyright (C) 2019-2022 Armin Joachimsmeyer
13  * armin.joachimsmeyer@gmail.com
14  *
15  * This file is part of ServoEasing https://github.com/ArminJo/ServoEasing.
16  *
17  * ServoEasing is free software: you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation, either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
25  * See the GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program. If not, see <http://www.gnu.org/licenses/gpl.html>.
29  */
30 
31 /*
32  * This library can be configured at compile time by the following options / macros:
33  * For more details see: https://github.com/ArminJo/ServoEasing#compile-options--macros-for-this-library
34  *
35  * - USE_PCA9685_SERVO_EXPANDER Enables the use of the PCA9685 I2C expander chip/board.
36  * - USE_SERVO_LIB Use of PCA9685 normally disables use of regular servo library. You can force additional using of regular servo library by defining USE_SERVO_LIB.
37  * - USE_LEIGHTWEIGHT_SERVO_LIB Makes the servo pulse generating immune to other libraries blocking interrupts for a longer time like SoftwareSerial, Adafruit_NeoPixel and DmxSimple.
38  * - PROVIDE_ONLY_LINEAR_MOVEMENT Disables all but LINEAR movement. Saves up to 1540 bytes program memory.
39  * - DISABLE_COMPLEX_FUNCTIONS Disables the SINE, CIRCULAR, BACK, ELASTIC, BOUNCE and PRECISION easings.
40  * - MAX_EASING_SERVOS Saves 4 byte RAM per servo.
41  * - DISABLE_MICROS_AS_DEGREE_PARAMETER Disables passing also microsecond values as (target angle) parameter. Saves 128 bytes program memory.
42  * - PRINT_FOR_SERIAL_PLOTTER Generate serial output for Arduino Plotter (Ctrl-Shift-L).
43  */
44 
45 #ifndef _SERVO_EASING_HPP
46 #define _SERVO_EASING_HPP
47 
48 #include <Arduino.h>
49 
50 #include "ServoEasing.h"
51 
52 #if defined(USE_LEIGHTWEIGHT_SERVO_LIB) && (defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__))
53 #include "LightweightServo.hpp" // include sources of LightweightServo library
54 #endif
55 
56 /*
57  * Enable this to see information on each call.
58  * Since there should be no library which uses Serial, it should only be enabled for development purposes.
59  */
60 #if defined(DEBUG)
61 #define LOCAL_DEBUG
62 #else
63 //#define LOCAL_DEBUG // This enables debug output only for this file
64 #endif
65 #if defined(TRACE)
66 #define LOCAL_TRACE
67 // Propagate debug level
68 #define LOCAL_DEBUG
69 #else
70 //#define LOCAL_TRACE // This enables trace output only for this file
71 #endif
72 
73 // Enable this if you want to measure timing by toggling pin12 on an arduino
74 //#define MEASURE_SERVO_EASING_INTERRUPT_TIMING
75 #if defined(MEASURE_SERVO_EASING_INTERRUPT_TIMING)
76 #include "digitalWriteFast.h"
77 #define TIMING_OUTPUT_PIN 12
78 #endif
79 
80 #if defined(ESP8266) || defined(ESP32)
81 //# if defined(ESP32)
82 //#include "esp_task_wdt.h" // for esp_task_wdt_reset();
83 //# endif
84 #include "Ticker.h" // for ServoEasingInterrupt functions
85 Ticker Timer20ms;
86 
87 // BluePill in 2 flavors
88 #elif defined(STM32F1xx) // for "Generic STM32F1 series / STMicroelectronics:stm32" from STM32 Boards from STM32 cores of Arduino Board manager
89 // https://github.com/stm32duino/BoardManagerFiles/raw/master/STM32/package_stm_index.json
90 #include <HardwareTimer.h> // 4 timers and 3. timer is used for tone(), 2. for Servo
91 /*
92  * Use timer 4 as IRMP timer.
93  * Timer 4 blocks PB6, PB7, PB8, PB9, so if you require one of them as Servo output, you must choose another timer.
94  */
95 HardwareTimer Timer20ms(TIM4);
96 
97 #elif defined(__STM32F1__) // or ARDUINO_ARCH_STM32F1 for "Generic STM32F103C series / stm32duino:STM32F1" from STM32F1 Boards (STM32duino.com) of Arduino Board manager
98 // http://dan.drown.org/stm32duino/package_STM32duino_index.json
99 #include <HardwareTimer.h>
100 # if defined(STM32_HIGH_DENSITY)
101 HardwareTimer Timer20ms(7); // 8 timers and 8. timer is used for tone()
102 # else
103 /*
104  * Use timer 3 for ServoEasingInterrupt functions.
105  * Timer 3 blocks PA6, PA7, PB0, PB1, so if you required one of them as Servo output, you must choose another timer.
106  */
107 HardwareTimer Timer20ms(3); // 4 timers and 4. timer is used for tone()
108 # endif
109 
110 #elif defined(__SAM3X8E__) // Arduino DUE
111 /*
112  * Timer 0 to 5 are used by Servo library (by defining handlers)
113  *
114  * Timer 6 is TC2 channel 0
115  * Timer 7 is TC2 channel 1
116  * Timer 8 is TC2 channel 2
117  *
118  * We use timer 8 here
119  */
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
125 
126 #elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE + Sparkfun Apollo3
127 mbed::Ticker Timer20ms;
128 
129 /*************************************************************************************************************************************
130  * RP2040 based boards for pico core
131  * https://github.com/earlephilhower/arduino-pico
132  * https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
133  * Can use any pin for PWM, no timer restrictions
134  *************************************************************************************************************************************/
135 #elif defined(ARDUINO_ARCH_RP2040) // Raspberry Pi Pico, Adafruit Feather RP2040, etc.
136 #include "pico/time.h"
137 repeating_timer_t Timer20ms;
139 // The timer callback has a parameter and a return value
140 bool handleServoTimerInterruptHelper(repeating_timer_t*) {
142  return true;
143 }
144 
145 #elif defined(TEENSYDUINO)
146 // common for all Teensy
147 IntervalTimer Timer20ms;
148 #endif
149 
150 volatile bool ServoEasing::sInterruptsAreActive = false; // true if interrupts are still active, i.e. at least one Servo is moving with interrupts.
151 
156 uint_fast8_t ServoEasing::sServoArrayMaxIndex = 0; // maximum index of an attached servo in ServoEasing::ServoEasingArray[]
164 
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";
180 # endif
181 #endif // !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
182 
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
188 # endif
189 #endif
190  };
191 
192 #if defined(USE_PCA9685_SERVO_EXPANDER)
193 //#define USE_SOFT_I2C_MASTER // Saves 2110 bytes program memory and 200 bytes RAM compared with Arduino Wire
194 # if defined(USE_SOFT_I2C_MASTER)
195 #include "SoftI2CMasterConfig.h"
196 #include "SoftI2CMaster.h"
197 # endif // defined(USE_SOFT_I2C_MASTER)
198 
199 # if !defined _BV
200 # define _BV(bit) (1 << (bit))
201 # endif
202 // Constructor with I2C address required
203 #if defined(USE_SOFT_I2C_MASTER)
204 ServoEasing::ServoEasing(uint8_t aPCA9685I2CAddress) // @suppress("Class members should be properly initialized")
205 #else
206 ServoEasing::ServoEasing(uint8_t aPCA9685I2CAddress, TwoWire *aI2CClass) // @suppress("Class members should be properly initialized")
207 #endif
208  {
209  mPCA9685I2CAddress = aPCA9685I2CAddress;
210 #if !defined(USE_SOFT_I2C_MASTER)
211  mI2CClass = aI2CClass;
212 #endif
213 
214  // On an ESP8266 it was NOT initialized to 0 :-(.
217  mServoMoves = false;
218  mOperateServoReverse = false;
219 
220 #if defined(USE_SERVO_LIB)
221  mServoIsConnectedToExpander = true;
222 #endif
223 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
225 # if defined(ENABLE_EASE_USER)
226  mUserEaseInFunction = NULL;
227 # endif
228 #endif
230 
231 #if !defined(DISABLE_MIN_AND_MAX_CONSTRAINTS)
233  mMaxMicrosecondsOrUnits = 2 * DEFAULT_MICROSECONDS_FOR_180_DEGREE; // any big value is sufficient
234 #endif
235 
236 #if defined(MEASURE_SERVO_EASING_INTERRUPT_TIMING)
237  pinMode(TIMING_OUTPUT_PIN, OUTPUT);
238 #endif
239 }
240 
241 void ServoEasing::I2CInit() {
242 // Initialize I2C
243 #if defined(USE_SOFT_I2C_MASTER)
244  i2c_init(); // Initialize everything and check for bus lockup
245 #else
246  mI2CClass->begin();
247  mI2CClass->setClock(I2C_CLOCK_FREQUENCY); // 1000000 does not work for me, maybe because of parasitic breadboard capacities
248 # if defined (ARDUINO_ARCH_AVR) // Other platforms do not have this new function
249  mI2CClass->setWireTimeout(); // Sets default timeout of 25 ms.
250 # endif
251 #endif
252 }
256 void ServoEasing::PCA9685Reset() {
257  // Send software reset to expander(s)
258 #if defined(USE_SOFT_I2C_MASTER)
259  i2c_start(PCA9685_GENERAL_CALL_ADDRESS << 1);
260  i2c_write(PCA9685_SOFTWARE_RESET);
261  i2c_stop();
262 #else
263  mI2CClass->beginTransmission(PCA9685_GENERAL_CALL_ADDRESS);
264  mI2CClass->write(PCA9685_SOFTWARE_RESET);
265  mI2CClass->endTransmission();
266 #endif
267 }
268 
273 void ServoEasing::PCA9685Init() {
274  // Set expander to 20 ms period
275  I2CWriteByte(PCA9685_MODE1_REGISTER, _BV(PCA9685_MODE_1_SLEEP)); // go to sleep
276  I2CWriteByte(PCA9685_PRESCALE_REGISTER, PCA9685_PRESCALER_FOR_20_MS); // set the prescaler
277  I2CWriteByte(PCA9685_MODE1_REGISTER, _BV(PCA9685_MODE_1_AUTOINCREMENT)); // reset sleep and enable auto increment
278  delay(2); // > 500 us according to datasheet
279 }
280 
281 void ServoEasing::I2CWriteByte(uint8_t aAddress, uint8_t aData) {
282 #if defined(USE_SOFT_I2C_MASTER)
283  i2c_start(mPCA9685I2CAddress << 1);
284  i2c_write(aAddress);
285  i2c_write(aData);
286  i2c_stop();
287 #else
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) {
294  // I have seen this at my ESP32 module :-( - but it is no buffer overflow.
295  Serial.print((char) (tWireReturnCode + '0')); // Error enum i2c_err_t: I2C_ERROR_ACK = 2, I2C_ERROR_TIMEOUT = 3
296  }
297 # else
298  mI2CClass->endTransmission();
299 # endif
300 #endif
301 }
302 
309 void ServoEasing::setPWM(uint16_t aPWMOffValueAsUnits) {
310 #if defined(USE_SOFT_I2C_MASTER)
311  i2c_start(mPCA9685I2CAddress << 1);
312  i2c_write((PCA9685_FIRST_PWM_REGISTER + 2) + 4 * mServoPin);
313  i2c_write(aPWMOffValueAsUnits);
314  i2c_write(aPWMOffValueAsUnits >> 8);
315  i2c_stop();
316 #else
317  mI2CClass->beginTransmission(mPCA9685I2CAddress);
318  // +2 since we we do set the OFF value and not the ON value, which is fixed at 0
319  mI2CClass->write((PCA9685_FIRST_PWM_REGISTER + 2) + 4 * mServoPin); // 4 * mServoPin is the register offset
320  mI2CClass->write(aPWMOffValueAsUnits);
321  mI2CClass->write(aPWMOffValueAsUnits >> 8);
322 # if defined(LOCAL_DEBUG) && not defined(ESP32)
323  // The ESP32 I2C interferes with the Ticker / Timer library used.
324  // Even with 100 kHz clock we have some dropouts / NAK's because of sending address again instead of first data.
325  uint8_t tWireReturnCode = mI2CClass->endTransmission();
326  if (tWireReturnCode != 0) {
327  // If you end up here, maybe the second module is not attached?
328  Serial.print((char) (tWireReturnCode + '0')); // Error enum i2c_err_t: I2C_ERROR_ACK = 2, I2C_ERROR_TIMEOUT = 3
329  }
330 # else
331  mI2CClass->endTransmission();
332 # endif
333 #endif
334 }
335 
342 void ServoEasing::setPWM(uint16_t aPWMOnStartValueAsUnits, uint16_t aPWMPulseDurationAsUnits) {
343 #if defined(USE_SOFT_I2C_MASTER)
344  i2c_start(mPCA9685I2CAddress << 1);
345  i2c_write((PCA9685_FIRST_PWM_REGISTER) + 4 * mServoPin);
346  i2c_write(aPWMOnStartValueAsUnits);
347  i2c_write(aPWMOnStartValueAsUnits >> 8);
348  i2c_write(aPWMOnStartValueAsUnits + aPWMPulseDurationAsUnits);
349  i2c_write((aPWMOnStartValueAsUnits + aPWMPulseDurationAsUnits) >> 8);
350  i2c_stop();
351 #else
352  mI2CClass->beginTransmission(mPCA9685I2CAddress);
353  mI2CClass->write((PCA9685_FIRST_PWM_REGISTER) + 4 * mServoPin);
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)
359  // The ESP32 I2C interferes with the Ticker / Timer library used.
360  // Even with 100 kHz clock we have some dropouts / NAK's because of sending address again instead of first data.
361  uint8_t tWireReturnCode = mI2CClass->endTransmission(); // blocking call
362  if (tWireReturnCode != 0) {
363  // If you end up here, maybe the second module is not attached?
364  Serial.print((char) (tWireReturnCode + '0')); // Error enum i2c_err_t: I2C_ERROR_ACK = 2, I2C_ERROR_TIMEOUT = 3
365  }
366 # else
367  mI2CClass->endTransmission();
368 # endif
369 #endif
370 }
371 
372 int ServoEasing::MicrosecondsToPCA9685Units(int aMicroseconds) {
373  /*
374  * 4096 units per 20 milliseconds => aMicroseconds / 4.8828
375  */
376 #if defined(USE_SERVO_LIB)
377  if (!mServoIsConnectedToExpander) {
378  return aMicroseconds; // we must return microseconds here
379  }
380 #endif
381  return ((4096L * aMicroseconds) / REFRESH_INTERVAL_MICROS);
382 }
383 
384 int ServoEasing::PCA9685UnitsToMicroseconds(int aPCA9685Units) {
385  /*
386  * 4096 units per 20 milliseconds => aPCA9685Units * 4.8828
387  * (aPCA9685Units * 625) / 128 use int32_t to avoid overflow
388  */
389  return ((int32_t) aPCA9685Units * (REFRESH_INTERVAL_MICROS / 32)) / (4096 / 32);
390 }
391 
392 #endif // defined(USE_PCA9685_SERVO_EXPANDER)
393 
394 // Constructor without I2C address
395 ServoEasing::ServoEasing() // @suppress("Class members should be properly initialized")
396 #if (!defined(USE_PCA9685_SERVO_EXPANDER) || defined(USE_SERVO_LIB)) && !defined(USE_LEIGHTWEIGHT_SERVO_LIB)
397 :
398  Servo()
399 #endif
400 {
401  // On an ESP8266 it was NOT initialized to 0 :-(.
404  mServoMoves = false;
405 #if !defined(DISABLE_PAUSE_RESUME)
406  mServoIsPaused = false;
407 #endif
408  mOperateServoReverse = false;
409 
410 #if defined(USE_PCA9685_SERVO_EXPANDER) && defined(USE_SERVO_LIB)
411  mServoIsConnectedToExpander = false;
412 #endif
413 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
415 # if defined(ENABLE_EASE_USER)
416  mUserEaseInFunction = NULL;
417 # endif
418 #endif
420 
421 #if !defined(DISABLE_MIN_AND_MAX_CONSTRAINTS)
423  mMaxMicrosecondsOrUnits = 2 * DEFAULT_MICROSECONDS_FOR_180_DEGREE; // any big value is sufficient
424 #endif
425 
426 #if defined(MEASURE_SERVO_EASING_INTERRUPT_TIMING)
427  pinMode(TIMING_OUTPUT_PIN, OUTPUT);
428 #endif
429 }
430 
443 uint8_t ServoEasing::attach(int aPin) {
445 }
446 
447 // Here no units accepted, only microseconds!
448 uint8_t ServoEasing::attach(int aPin, int aMicrosecondsForServo0Degree, int aMicrosecondsForServo180Degree) {
449  return attach(aPin, aMicrosecondsForServo0Degree, aMicrosecondsForServo180Degree, 0, 180);
450 }
451 
455 uint8_t ServoEasing::attach(int aPin, int aInitialDegreeOrMicrosecond) {
456  return attach(aPin, aInitialDegreeOrMicrosecond, DEFAULT_MICROSECONDS_FOR_0_DEGREE, DEFAULT_MICROSECONDS_FOR_180_DEGREE);
457 }
458 
462 uint8_t ServoEasing::attachWithTrim(int aPin, int aTrimDegreeOrMicrosecond, int aInitialDegreeOrMicrosecond) {
463  return attachWithTrim(aPin, aTrimDegreeOrMicrosecond, aInitialDegreeOrMicrosecond, DEFAULT_MICROSECONDS_FOR_0_DEGREE,
465 }
466 
473 uint8_t ServoEasing::attach(int aPin, int aInitialDegreeOrMicrosecond, int aMicrosecondsForServo0Degree,
474  int aMicrosecondsForServo180Degree) {
475  return attach(aPin, aInitialDegreeOrMicrosecond, aMicrosecondsForServo0Degree, aMicrosecondsForServo180Degree, 0, 180);
476 }
477 
478 uint8_t ServoEasing::attachWithTrim(int aPin, int aTrimDegreeOrMicrosecond, int aInitialDegreeOrMicrosecond,
479  int aMicrosecondsForServo0Degree, int aMicrosecondsForServo180Degree) {
480  return attachWithTrim(aPin, aTrimDegreeOrMicrosecond, aInitialDegreeOrMicrosecond, aMicrosecondsForServo0Degree,
481  aMicrosecondsForServo180Degree, 0, 180);
482 }
483 
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,
493  aServoHighDegree);
494  write(aInitialDegreeOrMicrosecond);
495  return tReturnValue;
496 }
497 
498 uint8_t ServoEasing::attachWithTrim(int aPin, int aTrimDegreeOrMicrosecond, int aInitialDegreeOrMicrosecond,
499  int aMicrosecondsForServoLowDegree, int aMicrosecondsForServoHighDegree, int aServoLowDegree, int aServoHighDegree) {
500  uint8_t tReturnValue = attach(aPin, aMicrosecondsForServoLowDegree, aMicrosecondsForServoHighDegree, aServoLowDegree,
501  aServoHighDegree);
502  setTrim(aTrimDegreeOrMicrosecond, false);
503  write(aInitialDegreeOrMicrosecond);
504  return tReturnValue;
505 }
517 uint8_t ServoEasing::attach(int aPin, int aMicrosecondsForServoLowDegree, int aMicrosecondsForServoHighDegree, int aServoLowDegree,
518  int aServoHighDegree) {
519  /*
520  * Get the 0 and 180 degree values.
521  */
522  int tMicrosecondsForServo0Degree = map(0, aServoLowDegree, aServoHighDegree, aMicrosecondsForServoLowDegree,
523  aMicrosecondsForServoHighDegree);
524  int tMicrosecondsForServo180Degree = map(180, aServoLowDegree, aServoHighDegree, aMicrosecondsForServoLowDegree,
525  aMicrosecondsForServoHighDegree);
526 
527  mServoPin = aPin;
528 #if defined(USE_PCA9685_SERVO_EXPANDER)
529 # if defined(USE_SERVO_LIB)
530  if (mServoIsConnectedToExpander) {
531  // set units
532  mServo0DegreeMicrosecondsOrUnits = MicrosecondsToPCA9685Units(tMicrosecondsForServo0Degree);
533  mServo180DegreeMicrosecondsOrUnits = MicrosecondsToPCA9685Units(tMicrosecondsForServo180Degree);
534  } else {
535  // set microseconds
536  mServo0DegreeMicrosecondsOrUnits = tMicrosecondsForServo0Degree;
537  mServo180DegreeMicrosecondsOrUnits = tMicrosecondsForServo180Degree;
538  }
539 # else
540  // set units
541  mServo0DegreeMicrosecondsOrUnits = MicrosecondsToPCA9685Units(tMicrosecondsForServo0Degree);
542  mServo180DegreeMicrosecondsOrUnits = MicrosecondsToPCA9685Units(tMicrosecondsForServo180Degree);
543 # endif
544 #else
545  // set microseconds
546  mServo0DegreeMicrosecondsOrUnits = tMicrosecondsForServo0Degree;
547  mServo180DegreeMicrosecondsOrUnits = tMicrosecondsForServo180Degree;
548 #endif
549 
550  /*
551  * Now put this servo instance into list of servos
552  */
553  uint8_t tReturnValue = INVALID_SERVO; // flag indicating an invalid servo index
554  for (uint_fast8_t tServoIndex = 0; tServoIndex < MAX_EASING_SERVOS; ++tServoIndex) {
555  if (ServoEasingArray[tServoIndex] == NULL) {
556  ServoEasingArray[tServoIndex] = this;
557  tReturnValue = tServoIndex;
558  if (tServoIndex > sServoArrayMaxIndex) {
559  sServoArrayMaxIndex = tServoIndex;
560  }
561  break;
562  }
563  }
564  mServoIndex = tReturnValue;
565 
566 #if defined(LOCAL_TRACE)
567  Serial.print("Index=");
568  Serial.print(tReturnValue);
569  Serial.print(" pin=");
570  Serial.print(mServoPin);
571  Serial.print(" low=");
572  Serial.print(aServoLowDegree);
573  Serial.print('|');
574  Serial.print(aMicrosecondsForServoLowDegree);
575  Serial.print(" high=");
576  Serial.print(aServoHighDegree);
577  Serial.print('|');
578  Serial.print(aMicrosecondsForServoHighDegree);
579  Serial.print(' ');
580  printStatic(&Serial);
581 #endif
582 
583 #if defined(USE_PCA9685_SERVO_EXPANDER)
584  mCurrentMicrosecondsOrUnits = DEFAULT_PCA9685_UNITS_FOR_90_DEGREE; // The start value if we forget the initial write()
585 # if defined(USE_SERVO_LIB)
586  if (mServoIsConnectedToExpander) {
587  if (tReturnValue == 0) {
588  I2CInit(); // init only once
589  PCA9685Reset(); // reset only once
590  }
591  PCA9685Init(); // initialize at every attach is simpler but initializing once for every board would be sufficient.
592  return tReturnValue;
593  }
594 # else
595  if (tReturnValue == 0) {
596  I2CInit(); // init only once
597  PCA9685Reset(); // reset only once
598  }
599  PCA9685Init(); // initialize at every attach is simpler but initializing once for every board would be sufficient.
600  return tReturnValue;
601 # endif
602 #endif // defined(USE_PCA9685_SERVO_EXPANDER)
603 
604 #if !defined(USE_PCA9685_SERVO_EXPANDER) || defined(USE_SERVO_LIB)
605  /*
606  * Here servo is NOT connected to expander
607  */
608  mCurrentMicrosecondsOrUnits = DEFAULT_PULSE_WIDTH; // The start value if we forget the initial write()
609 // This error value has priority over the regular return value from Servo::attach()
610  if (tReturnValue == INVALID_SERVO) {
611  return tReturnValue;
612  }
613 
614 # if defined(USE_LEIGHTWEIGHT_SERVO_LIB)
615  if(aPin != 9 && aPin != 10) {
616  return false;
617  }
618  return aPin;
619 # else
620  /*
621  * Use standard arduino servo library
622  * Call attach() of the underlying Servo library
623  */
624 # if defined(ARDUINO_ARCH_APOLLO3)
625  Servo::attach(aPin, tMicrosecondsForServo0Degree, tMicrosecondsForServo180Degree);
626  return aPin; // Sparkfun apollo3 Servo library has no return value for attach :-(
627 # else
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)
632 }
633 
639  if (mServoIndex != INVALID_SERVO) {
641  // If servo with highest index in array was detached, we want to find new sServoArrayMaxIndex
644  }
645 
646 #if defined(USE_PCA9685_SERVO_EXPANDER)
647 # if defined(USE_SERVO_LIB)
648  if (mServoIsConnectedToExpander) {
649  setPWM(0); // set signal fully off
650  } else {
651 # if defined(USE_LEIGHTWEIGHT_SERVO_LIB)
652  deinitLightweightServoPin9_10(mServoPin == 9, mServoPin == 10); // disable output and change to input
653 # else
654  Servo::detach();
655 # endif
656  }
657 # else
658  setPWM(0); // set signal fully off
659 # endif // defined(USE_SERVO_LIB)
660 
661 #else
662 # if defined(USE_LEIGHTWEIGHT_SERVO_LIB)
663  deinitLightweightServoPin9_10(mServoPin == 9, mServoPin == 10); // disable output and change to input
664 # else
665  Servo::detach();
666 # endif
667 #endif // defined(USE_PCA9685_SERVO_EXPANDER)
668  }
669  mServoMoves = false; // safety net to enable right update handling if accidentally called
671 }
672 
679 void ServoEasing::setReverseOperation(bool aOperateServoReverse) {
680  mOperateServoReverse = aOperateServoReverse;
681 }
682 
683 uint_fast16_t ServoEasing::getSpeed() {
684  return mSpeed;
685 }
686 
687 void ServoEasing::setSpeed(uint_fast16_t aDegreesPerSecond) {
688  mSpeed = aDegreesPerSecond;
689 }
690 
697 void ServoEasing::setTrim(int aTrimDegreeOrMicrosecond, bool aDoWrite) {
698  if (aTrimDegreeOrMicrosecond >= 0) {
700  DegreeOrMicrosecondToMicrosecondsOrUnits(aTrimDegreeOrMicrosecond) - mServo0DegreeMicrosecondsOrUnits, aDoWrite);
701  } else {
704  aDoWrite);
705  }
706 }
707 
715 void ServoEasing::_setTrimMicrosecondsOrUnits(int aTrimMicrosecondsOrUnits, bool aDoWrite) {
716  mTrimMicrosecondsOrUnits = aTrimMicrosecondsOrUnits;
717  if (aDoWrite) {
719  }
720 }
721 
722 #if !defined(DISABLE_MIN_AND_MAX_CONSTRAINTS)
723 void ServoEasing::setMaxConstraint(int aMaxDegreeOrMicrosecond) {
725 }
726 void ServoEasing::setMinConstraint(int aMinDegreeOrMicrosecond) {
728 }
729 void ServoEasing::setMinMaxConstraint(int aMinDegreeOrMicrosecond, int aMaxDegreeOrMicrosecond) {
732 }
733 #endif
734 
735 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
736 void ServoEasing::setEasingType(uint_fast8_t aEasingType) {
737  mEasingType = aEasingType;
738 }
739 
741  return (mEasingType);
742 }
743 
744 # if defined(ENABLE_EASE_USER)
745 void ServoEasing::registerUserEaseInFunction(float (*aUserEaseInFunction)(float aFactorOfTimeCompletion, void *aUserDataPointer),
746  void *aUserDataPointer) {
747  mUserEaseInFunction = aUserEaseInFunction;
748  UserDataPointer = aUserDataPointer;
749 }
750 void ServoEasing::setUserDataPointer(void *aUserDataPointer) {
751  UserDataPointer = aUserDataPointer;
752 }
753 # endif
754 #endif // !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
755 
759 void ServoEasing::write(int aTargetDegreeOrMicrosecond) {
760 #if defined(LOCAL_TRACE)
761  Serial.print(F("write "));
762  Serial.print(aTargetDegreeOrMicrosecond);
763  Serial.print(' ');
764 #endif
765  /*
766  * Check for valid initialization of servo.
767  */
768  if (mServoIndex == INVALID_SERVO) {
769 #if defined(LOCAL_TRACE)
770  Serial.print(F("Error: detached servo"));
771 #endif
772  return;
773  }
774  ServoEasingNextPositionArray[mServoIndex] = aTargetDegreeOrMicrosecond;
776 }
777 
778 void ServoEasing::write(float aTargetDegreeOrMicrosecond) {
779 #if defined(LOCAL_TRACE)
780  Serial.print(F("write "));
781  Serial.print(aTargetDegreeOrMicrosecond);
782  Serial.print(' ');
783 #endif
784  /*
785  * Check for valid initialization of servo.
786  */
787  if (mServoIndex == INVALID_SERVO) {
788 #if defined(LOCAL_TRACE)
789  Serial.print(F("Error: detached servo"));
790 #endif
791  return;
792  }
793  ServoEasingNextPositionArray[mServoIndex] = aTargetDegreeOrMicrosecond;
795 }
796 
801 void ServoEasing::_writeMicrosecondsOrUnits(int aTargetDegreeOrMicrosecond) {
802  /*
803  * Check for valid initialization of servo.
804  */
805  if (mServoIndex == INVALID_SERVO) {
806 #if defined(LOCAL_TRACE)
807  Serial.print(F("Error: detached servo"));
808 #endif
809  return;
810  }
811 #if !defined(DISABLE_MIN_AND_MAX_CONSTRAINTS)
812  if (aTargetDegreeOrMicrosecond > mMaxMicrosecondsOrUnits) {
813  aTargetDegreeOrMicrosecond = mMaxMicrosecondsOrUnits;
814  } else if (aTargetDegreeOrMicrosecond < mMinMicrosecondsOrUnits) {
815  aTargetDegreeOrMicrosecond = mMinMicrosecondsOrUnits;
816  }
817 #endif
818  mCurrentMicrosecondsOrUnits = aTargetDegreeOrMicrosecond;
819 
820 #if defined(LOCAL_TRACE)
821  Serial.print(mServoIndex);
822  Serial.print('/');
823  Serial.print(mServoPin);
824  Serial.print(F(" us/u="));
825  Serial.print(aTargetDegreeOrMicrosecond);
826  if (mTrimMicrosecondsOrUnits != 0) {
827  Serial.print(" t=");
828  Serial.print(aTargetDegreeOrMicrosecond + mTrimMicrosecondsOrUnits);
829  }
830 #endif // TRACE
831 
832 // Apply trim - this is the only place mTrimMicrosecondsOrUnits is evaluated
833  aTargetDegreeOrMicrosecond += mTrimMicrosecondsOrUnits;
834 // Apply reverse, values for 0 to 180 are swapped if reverse - this is the only place mOperateServoReverse is evaluated
835 // (except in the DegreeToMicrosecondsOrUnitsWithTrimAndReverse() function for external testing purposes)
836  if (mOperateServoReverse) {
837  aTargetDegreeOrMicrosecond = mServo180DegreeMicrosecondsOrUnits
838  - (aTargetDegreeOrMicrosecond - mServo0DegreeMicrosecondsOrUnits);
839 #if defined(LOCAL_TRACE)
840  Serial.print(F(" r="));
841  Serial.print(aTargetDegreeOrMicrosecond);
842 #endif
843  }
844 
845 #if defined(PRINT_FOR_SERIAL_PLOTTER) && !defined(LOCAL_TRACE)
846  Serial.print(' '); // leading separator to separate multiple servo values
847  Serial.print(aTargetDegreeOrMicrosecond);
848 #endif
849 
850 #if defined(USE_PCA9685_SERVO_EXPANDER)
851 # if defined(LOCAL_TRACE)
852  // For each pin show PWM on value used below
853  Serial.print(F(" s="));
854  Serial.print(mServoPin * (4096 - (DEFAULT_PCA9685_UNITS_FOR_180_DEGREE + 100)) / 15); // mServoPin * 233
855 # endif
856 # if defined(USE_SERVO_LIB)
857  if (mServoIsConnectedToExpander) {
858  setPWM(mServoPin * ((4096 - (DEFAULT_PCA9685_UNITS_FOR_180_DEGREE + 100)) / 15), aTargetDegreeOrMicrosecond); // mServoPin * 233
859  } else {
860 # if defined(USE_LEIGHTWEIGHT_SERVO_LIB)
861  writeMicrosecondsLightweightServo(aTargetDegreeOrMicrosecond, (mServoPin == 9));
862 # else
863  Servo::writeMicroseconds(aTargetDegreeOrMicrosecond); // requires 7 us
864 # endif
865  }
866 # else
867  /*
868  * Distribute the servo start time over the 20 ms period.
869  * Unexpectedly this even saves 20 bytes Flash for an ATmega328P
870  */
871  setPWM(mServoPin * ((4096 - (DEFAULT_PCA9685_UNITS_FOR_180_DEGREE + 100)) / 15), aTargetDegreeOrMicrosecond); // mServoPin * 233
872 # endif
873 
874 #else
875 # if defined(USE_LEIGHTWEIGHT_SERVO_LIB)
876  writeMicrosecondsLightweightServo(aTargetDegreeOrMicrosecond, (mServoPin == 9));
877 # else
878  Servo::writeMicroseconds(aTargetDegreeOrMicrosecond); // requires 7 us
879 # endif
880 #endif
881 
882 #if defined(LOCAL_TRACE) && !defined(PRINT_FOR_SERIAL_PLOTTER)
883  Serial.println(); // no newline here, if serial plotter output is requested
884 #endif
885 }
886 
892 int ServoEasing::MicrosecondsToDegree(int aMicroseconds) {
893 #if defined(USE_PCA9685_SERVO_EXPANDER)
894 # if defined(USE_SERVO_LIB)
895  if (!mServoIsConnectedToExpander) {
896  return MicrosecondsOrUnitsToDegree(aMicroseconds); // not connected to PCA9685 here
897  }
898 # endif
899  int32_t tResult = aMicroseconds - PCA9685UnitsToMicroseconds(mServo0DegreeMicrosecondsOrUnits);
900  tResult = (tResult * 180) + 928;
901  return (tResult / PCA9685UnitsToMicroseconds((mServo180DegreeMicrosecondsOrUnits - mServo0DegreeMicrosecondsOrUnits)));
902 #else
903  return MicrosecondsOrUnitsToDegree(aMicroseconds);
904 #endif
905 }
906 
912 int ServoEasing::MicrosecondsOrUnitsToDegree(int aMicrosecondsOrUnits) {
922  /*
923  * compute map with rounding
924  */
925 // remove zero degree offset
926  int32_t tResult = aMicrosecondsOrUnits - mServo0DegreeMicrosecondsOrUnits;
927 #if defined(USE_PCA9685_SERVO_EXPANDER)
928 # if defined(USE_SERVO_LIB)
929  if (mServoIsConnectedToExpander) {
930  // here we have PCA9685 units
931  tResult = (tResult * 180) + 190;
932  } else {
933  // here we deal with microseconds
934  tResult = (tResult * 180) + 928;
935  }
936 # else
937  // here we have PCA9685 units
938  tResult = (tResult * 180) + 190;
939 # endif
940 #else
941 // here we deal with microseconds
942  tResult = (tResult * 180) + 928; // 928 is the value for 1/2 degree before scaling; (1856 = 180 - 0 degree micros) / 2
943 #endif
944 // scale by 180 degree range (180 - 0 degree micros)
946 
947 }
948 
949 int ServoEasing::MicrosecondsOrUnitsToMicroseconds(int aMicrosecondsOrUnits) {
950 #if defined(USE_PCA9685_SERVO_EXPANDER)
951  return PCA9685UnitsToMicroseconds(aMicrosecondsOrUnits);
952 #else
953  return aMicrosecondsOrUnits; // we have microseconds here
954 #endif
955 
956 }
957 
964 #if defined(DISABLE_MICROS_AS_DEGREE_PARAMETER)
965  return ((int32_t) (aDegreeOrMicrosecond * (int32_t) (mServo180DegreeMicrosecondsOrUnits - mServo0DegreeMicrosecondsOrUnits))
967 #else // defined(DISABLE_MICROS_AS_DEGREE_PARAMETER)
968  if (aDegreeOrMicrosecond < THRESHOLD_VALUE_FOR_INTERPRETING_VALUE_AS_MICROSECONDS) {
969  /*
970  * Here aDegreeOrMicrosecond contains degree
971  */
972 // return map(aDegreeOrMicrosecond, 0, 180, mServo0DegreeMicrosecondsOrUnits, mServo180DegreeMicrosecondsOrUnits);
973  // This saves 20 bytes program space and is faster :-)
974  return ((int32_t) (aDegreeOrMicrosecond * (int32_t) (mServo180DegreeMicrosecondsOrUnits - mServo0DegreeMicrosecondsOrUnits))
976  } else {
977  /*
978  * Here aDegreeOrMicrosecond contains microseconds
979  */
980 # if defined(USE_PCA9685_SERVO_EXPANDER)
981  return MicrosecondsToPCA9685Units(aDegreeOrMicrosecond); // return units here
982 # else
983  return aDegreeOrMicrosecond; // return microseconds here
984 # endif
985  }
986 #endif // defined(DISABLE_MICROS_AS_DEGREE_PARAMETER)
987 }
988 
990 // For microseconds and PCA9685 units:
991 #if defined(DISABLE_MICROS_AS_DEGREE_PARAMETER)
992  return ((int32_t) (aDegreeOrMicrosecond * ((float) (mServo180DegreeMicrosecondsOrUnits - mServo0DegreeMicrosecondsOrUnits))))
993  / 180 + mServo0DegreeMicrosecondsOrUnits; // return microseconds here
994 #else
995  if (aDegreeOrMicrosecond < THRESHOLD_VALUE_FOR_INTERPRETING_VALUE_AS_MICROSECONDS) {
996  /*
997  * Here aDegreeOrMicrosecond contains degree
998  */
999  return ((int32_t) (aDegreeOrMicrosecond * ((float) (mServo180DegreeMicrosecondsOrUnits - mServo0DegreeMicrosecondsOrUnits))))
1000  / 180 + mServo0DegreeMicrosecondsOrUnits; // return microseconds here
1001 
1002  } else {
1003  /*
1004  * Here aDegreeOrMicrosecond contains microseconds
1005  */
1006 # if defined(USE_PCA9685_SERVO_EXPANDER)
1007  return MicrosecondsToPCA9685Units(aDegreeOrMicrosecond); // return units here
1008 # else
1009  return aDegreeOrMicrosecond; // return microseconds here
1010 # endif
1011  }
1012 #endif // defined(DISABLE_MICROS_AS_DEGREE_PARAMETER)
1013 }
1014 
1019 // For microseconds and PCA9685 units:
1020  int tResultValue = map(aDegree, 0, 180, mServo0DegreeMicrosecondsOrUnits, mServo180DegreeMicrosecondsOrUnits);
1021  tResultValue += mTrimMicrosecondsOrUnits;
1022  if (mOperateServoReverse) {
1023  tResultValue = mServo180DegreeMicrosecondsOrUnits - (tResultValue - mServo0DegreeMicrosecondsOrUnits);
1024  }
1025  return tResultValue;
1026 }
1027 
1028 void ServoEasing::easeTo(int aTargetDegreeOrMicrosecond) {
1029  easeTo(aTargetDegreeOrMicrosecond, mSpeed);
1030 }
1031 
1032 void ServoEasing::easeTo(float aTargetDegreeOrMicrosecond) {
1033  easeTo(aTargetDegreeOrMicrosecond, mSpeed);
1034 }
1035 
1041 void ServoEasing::easeTo(int aTargetDegreeOrMicrosecond, uint_fast16_t aDegreesPerSecond) {
1042  startEaseTo(aTargetDegreeOrMicrosecond, aDegreesPerSecond, DO_NOT_START_UPDATE_BY_INTERRUPT); // no interrupts
1043  do {
1044  // First do the delay, then check for update, since we are probably called directly after start and there is nothing to move yet
1045  delay(REFRESH_INTERVAL_MILLIS); // 20 ms
1046 #if defined(PRINT_FOR_SERIAL_PLOTTER)
1047  } while (!updateAllServos()); // Update all servos in order to always create a complete plotter data set
1048 #else
1049  } while (!update());
1050 #endif
1051 }
1052 
1053 void ServoEasing::easeTo(float aTargetDegreeOrMicrosecond, uint_fast16_t aDegreesPerSecond) {
1054  startEaseTo(aTargetDegreeOrMicrosecond, aDegreesPerSecond, DO_NOT_START_UPDATE_BY_INTERRUPT); // no interrupts
1055  do {
1056  // First do the delay, then check for update, since we are probably called directly after start and there is nothing to move yet
1057  delay(REFRESH_INTERVAL_MILLIS); // 20 ms
1058 #if defined(PRINT_FOR_SERIAL_PLOTTER)
1059  } while (!updateAllServos());
1060 #else
1061  } while (!update());
1062 #endif
1063 }
1064 
1065 void ServoEasing::easeToD(int aTargetDegreeOrMicrosecond, uint_fast16_t aMillisForMove) {
1066  startEaseToD(aTargetDegreeOrMicrosecond, aMillisForMove, DO_NOT_START_UPDATE_BY_INTERRUPT);
1067  do {
1068  delay(REFRESH_INTERVAL_MILLIS); // 20 ms
1069 #if defined(PRINT_FOR_SERIAL_PLOTTER)
1070  } while (!updateAllServos());
1071 #else
1072  } while (!update());
1073 #endif
1074 }
1075 
1076 void ServoEasing::easeToD(float aTargetDegreeOrMicrosecond, uint_fast16_t aMillisForMove) {
1077  startEaseToD(aTargetDegreeOrMicrosecond, aMillisForMove, DO_NOT_START_UPDATE_BY_INTERRUPT);
1078  do {
1079  delay(REFRESH_INTERVAL_MILLIS); // 20 ms
1080 #if defined(PRINT_FOR_SERIAL_PLOTTER)
1081  } while (!updateAllServos());
1082 #else
1083  } while (!update());
1084 #endif
1085 }
1086 
1087 bool ServoEasing::setEaseTo(int aTargetDegreeOrMicrosecond) {
1088  return startEaseTo(aTargetDegreeOrMicrosecond, mSpeed, DO_NOT_START_UPDATE_BY_INTERRUPT);
1089 }
1090 
1091 bool ServoEasing::setEaseTo(float aTargetDegreeOrMicrosecond) {
1092  return startEaseTo(aTargetDegreeOrMicrosecond, mSpeed, DO_NOT_START_UPDATE_BY_INTERRUPT);
1093 }
1094 
1099 bool ServoEasing::setEaseTo(int aTargetDegreeOrMicrosecond, uint_fast16_t aDegreesPerSecond) {
1100  return startEaseTo(aTargetDegreeOrMicrosecond, aDegreesPerSecond, DO_NOT_START_UPDATE_BY_INTERRUPT);
1101 }
1102 
1103 bool ServoEasing::setEaseTo(float aTargetDegreeOrMicrosecond, uint_fast16_t aDegreesPerSecond) {
1104  return startEaseTo(aTargetDegreeOrMicrosecond, aDegreesPerSecond, DO_NOT_START_UPDATE_BY_INTERRUPT);
1105 }
1106 
1110 bool ServoEasing::startEaseTo(int aTargetDegreeOrMicrosecond) {
1111  return startEaseTo(aTargetDegreeOrMicrosecond, mSpeed, START_UPDATE_BY_INTERRUPT);
1112 }
1113 
1114 bool ServoEasing::startEaseTo(float aTargetDegreeOrMicrosecond) {
1115  return startEaseTo(aTargetDegreeOrMicrosecond, mSpeed, START_UPDATE_BY_INTERRUPT);
1116 }
1117 
1123 bool ServoEasing::startEaseTo(int aTargetDegreeOrMicrosecond, uint_fast16_t aDegreesPerSecond, bool aStartUpdateByInterrupt) {
1124 // return startEaseTo((float) aTargetDegreeOrMicrosecond, aDegreesPerSecond, aStartUpdateByInterrupt); // saves 400 bytes
1125  /*
1126  * Avoid division by 0 below
1127  */
1128  if (aDegreesPerSecond == 0) {
1129 #if defined(LOCAL_DEBUG)
1130  Serial.println(F("Speed is 0 -> set to 1"));
1131 #endif
1132  aDegreesPerSecond = 1;
1133  }
1134 
1135  /*
1136  * Get / convert target degree for computation of duration
1137  */
1138  int tTargetDegree = aTargetDegreeOrMicrosecond;
1139 #if defined(DISABLE_MICROS_AS_DEGREE_PARAMETER)
1140  tTargetDegree = aTargetDegreeOrMicrosecond; // no conversion required here
1141 #else
1142 // Convert aDegreeOrMicrosecond to target degree
1143  if (aTargetDegreeOrMicrosecond >= THRESHOLD_VALUE_FOR_INTERPRETING_VALUE_AS_MICROSECONDS) {
1144  tTargetDegree = MicrosecondsToDegree(aTargetDegreeOrMicrosecond);
1145  }
1146 #endif
1147 
1149 
1150  /*
1151  * Compute the MillisForCompleteMove parameter for use of startEaseToD() function
1152  */
1153  uint_fast16_t tMillisForCompleteMove = abs(tTargetDegree - tCurrentDegree) * MILLIS_IN_ONE_SECOND / aDegreesPerSecond;
1154 
1155 // bouncing has double movement, so take double time
1156 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
1158  tMillisForCompleteMove *= 2;
1159  }
1160 #endif
1161 
1162  return startEaseToD(aTargetDegreeOrMicrosecond, tMillisForCompleteMove, aStartUpdateByInterrupt);
1163 }
1164 
1165 bool ServoEasing::startEaseTo(float aTargetDegreeOrMicrosecond, uint_fast16_t aDegreesPerSecond, bool aStartUpdateByInterrupt) {
1166  /*
1167  * Avoid division by 0 below
1168  */
1169  if (aDegreesPerSecond == 0) {
1170 #if defined(LOCAL_DEBUG)
1171  Serial.println(F("Speed is 0 -> set to 1"));
1172 #endif
1173  aDegreesPerSecond = 1;
1174  }
1175 
1176  /*
1177  * Get / convert target degree for computation of duration
1178  * Do this as integer computation, with "less" precision
1179  */
1180  int tTargetDegree = aTargetDegreeOrMicrosecond;
1181 #if defined(DISABLE_MICROS_AS_DEGREE_PARAMETER)
1182  tTargetDegree = aTargetDegreeOrMicrosecond; // no conversion required here
1183 #else
1184 // Convert aDegreeOrMicrosecond to target degree
1185  if (aTargetDegreeOrMicrosecond >= THRESHOLD_VALUE_FOR_INTERPRETING_VALUE_AS_MICROSECONDS) {
1186  tTargetDegree = MicrosecondsToDegree(aTargetDegreeOrMicrosecond);
1187  }
1188 #endif
1189 
1191 
1192  /*
1193  * Compute the MillisForCompleteMove parameter for use of startEaseToD() function
1194  */
1195  uint_fast16_t tMillisForCompleteMove = abs(tTargetDegree - tCurrentDegree) * MILLIS_IN_ONE_SECOND / aDegreesPerSecond;
1196 
1197 // bouncing has double movement, so take double time
1198 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
1200  tMillisForCompleteMove *= 2;
1201  }
1202 #endif
1203 
1204  return startEaseToD(aTargetDegreeOrMicrosecond, tMillisForCompleteMove, aStartUpdateByInterrupt);
1205 }
1206 
1211 bool ServoEasing::setEaseToD(int aTargetDegreeOrMicrosecond, uint_fast16_t aMillisForMove) {
1212  return startEaseToD(aTargetDegreeOrMicrosecond, aMillisForMove, DO_NOT_START_UPDATE_BY_INTERRUPT);
1213 }
1214 
1215 bool ServoEasing::setEaseToD(float aTargetDegreeOrMicrosecond, uint_fast16_t aMillisForMove) {
1216  return startEaseToD(aTargetDegreeOrMicrosecond, aMillisForMove, DO_NOT_START_UPDATE_BY_INTERRUPT);
1217 }
1218 
1223 bool ServoEasing::noMovement(uint_fast16_t aMillisToWait) {
1225 }
1226 
1232 bool ServoEasing::startEaseToD(int aDegreeOrMicrosecond, uint_fast16_t aMillisForMove, bool aStartUpdateByInterrupt) {
1233  /*
1234  * Check for valid initialization of servo.
1235  */
1236  if (mServoIndex == INVALID_SERVO) {
1237 #if defined(LOCAL_TRACE)
1238  Serial.print(F("Error: detached servo"));
1239 #endif
1240  return true;
1241  }
1242 
1243 #if defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
1244  if (true) {
1245 #else
1246  if (mEasingType != EASE_DUMMY_MOVE) {
1247  // No end position for dummy move. This forces mDeltaMicrosecondsOrUnits to zero, avoiding any movement
1248 #endif
1249  // write the position also to ServoEasingNextPositionArray
1250  ServoEasingNextPositionArray[mServoIndex] = aDegreeOrMicrosecond;
1252  }
1253  int tCurrentMicrosecondsOrUnits = mCurrentMicrosecondsOrUnits;
1254  mDeltaMicrosecondsOrUnits = mEndMicrosecondsOrUnits - tCurrentMicrosecondsOrUnits;
1255 
1256  mMillisForCompleteMove = aMillisForMove;
1257  mStartMicrosecondsOrUnits = tCurrentMicrosecondsOrUnits;
1258 
1259 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
1261  // bouncing has same end position as start position
1262  mEndMicrosecondsOrUnits = tCurrentMicrosecondsOrUnits;
1263  }
1264 #endif
1265 
1266  mMillisAtStartMove = millis();
1267 
1268 #if defined(LOCAL_TRACE)
1269  printDynamic(&Serial, true);
1270 #elif defined(LOCAL_DEBUG)
1271  printDynamic(&Serial);
1272 #endif
1273 
1274  bool tReturnValue = !mServoMoves;
1275 
1276  mServoMoves = true;
1277 #if !defined(DISABLE_PAUSE_RESUME)
1278  mServoIsPaused = false;
1279 #endif
1280  if (aStartUpdateByInterrupt && !sInterruptsAreActive) {
1282  }
1283 
1284  return tReturnValue;
1285 }
1286 
1287 bool ServoEasing::startEaseToD(float aDegreeOrMicrosecond, uint_fast16_t aMillisForMove, bool aStartUpdateByInterrupt) {
1288  /*
1289  * Check for valid initialization of servo.
1290  */
1291  if (mServoIndex == INVALID_SERVO) {
1292 #if defined(LOCAL_TRACE)
1293  Serial.println(F("Error: detached servo"));
1294 #endif
1295  return true;
1296  }
1297 
1298 #if defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
1299  if (true) {
1300 #else
1301  if (mEasingType != EASE_DUMMY_MOVE) {
1302 #endif
1303  // write the position also to ServoEasingNextPositionArray
1304  ServoEasingNextPositionArray[mServoIndex] = aDegreeOrMicrosecond;
1305  // No end position for dummy move. This forces mDeltaMicrosecondsOrUnits to zero, avoiding any movement
1307  }
1308  int tCurrentMicrosecondsOrUnits = mCurrentMicrosecondsOrUnits;
1309  mDeltaMicrosecondsOrUnits = mEndMicrosecondsOrUnits - tCurrentMicrosecondsOrUnits;
1310 
1311  mMillisForCompleteMove = aMillisForMove;
1312  mStartMicrosecondsOrUnits = tCurrentMicrosecondsOrUnits;
1313 
1314 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
1316  // bouncing has same end position as start position
1317  mEndMicrosecondsOrUnits = tCurrentMicrosecondsOrUnits;
1318  }
1319 #endif
1320 
1321  mMillisAtStartMove = millis();
1322 
1323 #if defined(LOCAL_TRACE)
1324  printDynamic(&Serial, true);
1325 #elif defined(LOCAL_DEBUG)
1326  printDynamic(&Serial);
1327 #endif
1328 
1329  bool tReturnValue = !mServoMoves;
1330 
1331  // Check after printDynamic() to see the values
1332  mServoMoves = true;
1333 #if !defined(DISABLE_PAUSE_RESUME)
1334  mServoIsPaused = false;
1335 #endif
1336  if (aStartUpdateByInterrupt && !sInterruptsAreActive) {
1338  }
1339 
1340  return tReturnValue;
1341 }
1342 
1347  mServoMoves = false;
1348 #if !defined(ENABLE_EXTERNAL_SERVO_TIMER_HANDLER)
1349  if (!isOneServoMoving()) {
1350  // disable interrupt only if all servos stopped. This enables independent movements of servos with one interrupt handler.
1351  disableServoEasingInterrupt(); // For external handler, this must also be able to be managed externally
1352  }
1353 #endif
1354 }
1355 
1357 #if !defined(DISABLE_PAUSE_RESUME)
1358  mMillisAtStopMove = millis();
1359  mServoIsPaused = true;
1360 #endif
1361 }
1362 
1364 #if !defined(DISABLE_PAUSE_RESUME)
1365  mMillisAtStartMove += millis() - mMillisAtStopMove; // adjust the start time in order to continue the position of the stop() command.
1366  mServoIsPaused = false;
1367 #endif
1369 }
1370 
1372 #if !defined(DISABLE_PAUSE_RESUME)
1373  mMillisAtStartMove += millis() - mMillisAtStopMove; // adjust the start time in order to continue the position of the stop() command.
1374  mServoIsPaused = false;
1375 #endif
1376 }
1377 
1378 void ServoEasing::setTargetPositionReachedHandler(void (*aTargetPositionReachedHandler)(ServoEasing*)) {
1379  TargetPositionReachedHandler = aTargetPositionReachedHandler;
1380 }
1381 
1385 #if defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
1386 bool ServoEasing::update() {
1387 
1388  if (!mServoMoves) {
1389  return true;
1390  }
1391 
1392  uint32_t tMillisSinceStart = millis() - mMillisAtStartMove;
1393  if (tMillisSinceStart >= mMillisForCompleteMove) {
1394  // end of time reached -> write end position and return true
1396  mServoMoves = false;
1397  if(TargetPositionReachedHandler != NULL){
1398  // Call end callback function
1400  }
1401  return !mServoMoves; // mServoMoves may be changed by callback handler
1402  }
1403  /*
1404  * Use faster non float arithmetic
1405  * Linear movement: new position is: start position + total delta * (millis_done / millis_total aka "percentage of completion")
1406  * 40 us to compute
1407  */
1408  int_fast16_t tNewMicrosecondsOrUnits = mStartMicrosecondsOrUnits
1409  + ((mDeltaMicrosecondsOrUnits * (int32_t) tMillisSinceStart) / mMillisForCompleteMove);
1410  /*
1411  * Write new position only if changed
1412  */
1413  if (tNewMicrosecondsOrUnits != mCurrentMicrosecondsOrUnits) {
1414  _writeMicrosecondsOrUnits(tNewMicrosecondsOrUnits);
1415  }
1416  return false;
1417 }
1418 
1419 #else // PROVIDE_ONLY_LINEAR_MOVEMENT
1421 
1422  if (!mServoMoves) {
1423 # if defined(PRINT_FOR_SERIAL_PLOTTER)
1424  // call it always for serial plotter to output one servo value
1426 # endif
1427  return true;
1428  }
1429 
1430 #if !defined(DISABLE_PAUSE_RESUME)
1431  if (mServoIsPaused) {
1432  return false; // do not really move but still request next update
1433  }
1434 #endif
1435 
1436  uint32_t tMillisSinceStart = millis() - mMillisAtStartMove;
1437  if (tMillisSinceStart >= mMillisForCompleteMove) {
1438  // end of time reached -> write end position and return true
1440  mServoMoves = false;
1441  if (TargetPositionReachedHandler != NULL) {
1442  // Call end callback function
1444  }
1445  return !mServoMoves; // mServoMoves may be changed by callback handler
1446  }
1447 
1448  int tNewMicrosecondsOrUnits;
1449  if (mEasingType == EASE_LINEAR) {
1450  /*
1451  * Use faster non float arithmetic
1452  * Linear movement: new position is: start position + total delta * (millis_done / millis_total aka "percentage of completion")
1453  * 40 us to compute
1454  * Cast to int32 required for mMillisForCompleteMove for 32 bit platforms, otherwise we divide signed by unsigned. Thanks to drifkind.
1455  */
1456  tNewMicrosecondsOrUnits = mStartMicrosecondsOrUnits
1457  + ((mDeltaMicrosecondsOrUnits * (int32_t) tMillisSinceStart) / (int32_t) mMillisForCompleteMove);
1458  } else {
1459  /*
1460  * Non linear movement -> use floats
1461  * Compute tPercentageOfCompletion - from 0.0 to 1.0
1462  * The expected result of easing function is from 0.0 to 1.0
1463  * or from EASE_FUNCTION_MICROSECONDS_INDICATOR_OFFSET for direct microseconds result
1464  */
1465  float tFactorOfTimeCompletion = (float) tMillisSinceStart / (float) mMillisForCompleteMove;
1466  float tFactorOfMovementCompletion = 0.0;
1467 
1468  uint_fast8_t tCallStyle = mEasingType & CALL_STYLE_MASK; // Values are CALL_STYLE_DIRECT, CALL_STYLE_OUT, CALL_STYLE_IN_OUT, CALL_STYLE_BOUNCING_OUT_IN
1469 
1470  if (tCallStyle == CALL_STYLE_DIRECT) { // CALL_STYLE_IN
1471  // Use IN function direct: Call with PercentageOfCompletion | 0.0 to 1.0. FactorOfMovementCompletion is returnValue (from 0.0 to 1.0)
1472  tFactorOfMovementCompletion = callEasingFunction(tFactorOfTimeCompletion);
1473 
1474  } else if (tCallStyle == CALL_STYLE_OUT) {
1475  // Use IN function to generate OUT function: Call with (1 - PercentageOfCompletion) | 1.0 to 0.0. FactorOfMovementCompletion = (1 - returnValue)
1476  tFactorOfMovementCompletion = 1.0 - (callEasingFunction(1.0 - tFactorOfTimeCompletion));
1477 
1478  } else {
1479  if (tFactorOfTimeCompletion <= 0.5) {
1480  if (tCallStyle == CALL_STYLE_IN_OUT) {
1481  // In the first half, call with (2 * PercentageOfCompletion) | 0.0 to 1.0. FactorOfMovementCompletion = (0.5 * returnValue)
1482  tFactorOfMovementCompletion = 0.5 * (callEasingFunction(2.0 * tFactorOfTimeCompletion));
1483  }
1484  if (tCallStyle == CALL_STYLE_BOUNCING_OUT_IN) {
1485  // In the first half, call with (1 - (2 * PercentageOfCompletion)) | 1.0 to 0.0. FactorOfMovementCompletion = (1 - returnValue) -> call OUT 2 times faster.
1486  tFactorOfMovementCompletion = 1.0 - (callEasingFunction(1.0 - (2.0 * tFactorOfTimeCompletion)));
1487  }
1488  } else {
1489  if (tCallStyle == CALL_STYLE_IN_OUT) {
1490  // In the second half, call with (2 - (2 * PercentageOfCompletion)) | 1.0 to 0.0. FactorOfMovementCompletion = ( 1- (0.5 * returnValue))
1491  tFactorOfMovementCompletion = 1.0 - (0.5 * (callEasingFunction(2.0 - (2.0 * tFactorOfTimeCompletion))));
1492  }
1493  if (tCallStyle == CALL_STYLE_BOUNCING_OUT_IN) {
1494  // In the second half, call with ((2 * PercentageOfCompletion) - 1) | 0.0 to 1.0. FactorOfMovementCompletion = (1- returnValue) -> call OUT 2 times faster and backwards.
1495  tFactorOfMovementCompletion = 1.0 - callEasingFunction((2.0 * tFactorOfTimeCompletion) - 1.0);
1496  }
1497  }
1498  }
1499 
1500 #if defined(LOCAL_TRACE)
1501  Serial.print(F("FactorOfTimeCompletion="));
1502  Serial.print(tFactorOfTimeCompletion);
1503  Serial.print(F(" FactorOfMovementCompletion="));
1504  Serial.println(tFactorOfMovementCompletion);
1505 #endif
1506 
1507 #if defined(ENABLE_EASE_USER) || defined(ENABLE_EASE_PRECISION) // Only these two types returns microseconds yet
1508  // Threshold of 400 corresponds to around -14 degree
1509  if (tFactorOfMovementCompletion >= EASE_FUNCTION_MICROSECONDS_INDICATOR_OFFSET) {
1510  // Here we have called an easing function, which returns microseconds or units instead of the factor of completion (0.0 to 1.0)
1511 #if defined(USE_PCA9685_SERVO_EXPANDER)
1512 # if defined(USE_SERVO_LIB)
1513  if (mServoIsConnectedToExpander) {
1514  tNewMicrosecondsOrUnits = MicrosecondsToPCA9685Units(tFactorOfMovementCompletion);
1515  } else {
1516  tNewMicrosecondsOrUnits = tFactorOfMovementCompletion; // not connected to PCA9685 here
1517  }
1518 # else
1519  tNewMicrosecondsOrUnits = MicrosecondsToPCA9685Units(tFactorOfMovementCompletion);
1520 # endif
1521 #else
1522  tNewMicrosecondsOrUnits = tFactorOfMovementCompletion; // get the microseconds delivered by the function
1523 #endif
1524  } else
1525 #endif
1526 #if defined(ENABLE_EASE_USER)
1527  // check for degree values from -180 to 180 (tFactorOfMovementCompletion from 20 to 380)
1528  if (tFactorOfMovementCompletion >= EASE_FUNCTION_DEGREE_THRESHOLD) {
1529  // Here we have called an easing function, which returns degree instead of the factor of completion (0.0 to 1.0)
1530  tNewMicrosecondsOrUnits = DegreeOrMicrosecondToMicrosecondsOrUnits(
1531  (float) (tFactorOfMovementCompletion - EASE_FUNCTION_DEGREE_INDICATOR_OFFSET + 0.5));
1532  } else
1533 #endif
1534  {
1535  int tDeltaMicroseconds = mDeltaMicrosecondsOrUnits * tFactorOfMovementCompletion; // having this as int value saves float operations
1536  tNewMicrosecondsOrUnits = mStartMicrosecondsOrUnits + tDeltaMicroseconds;
1537  }
1538  }
1539 
1540 # if defined(PRINT_FOR_SERIAL_PLOTTER)
1541  // call it always for serial plotter
1542  _writeMicrosecondsOrUnits(tNewMicrosecondsOrUnits);
1543 # else
1544  /*
1545  * Write new position only if changed
1546  */
1547  if (tNewMicrosecondsOrUnits != mCurrentMicrosecondsOrUnits) {
1548  _writeMicrosecondsOrUnits(tNewMicrosecondsOrUnits);
1549  }
1550 # endif
1551  return false;
1552 }
1553 
1554 float ServoEasing::callEasingFunction(float aFactorOfTimeCompletion) {
1555  uint_fast8_t tEasingType = mEasingType & EASE_TYPE_MASK;
1556 
1557  switch (tEasingType) {
1558 
1559 # if defined(ENABLE_EASE_USER)
1560  case EASE_USER_DIRECT:
1561  if (mUserEaseInFunction != NULL) {
1562  return mUserEaseInFunction(aFactorOfTimeCompletion, UserDataPointer);
1563  } else {
1564  return 0.0;
1565  }
1566 # endif
1567 
1568 # if defined(ENABLE_EASE_QUADRATIC)
1569  case EASE_QUADRATIC_IN:
1570  return QuadraticEaseIn(aFactorOfTimeCompletion);
1571 # endif
1572 # if defined(ENABLE_EASE_CUBIC)
1573  case EASE_CUBIC_IN:
1574  return CubicEaseIn(aFactorOfTimeCompletion);
1575 # endif
1576 # if defined(ENABLE_EASE_QUARTIC)
1577  case EASE_QUARTIC_IN:
1578  return QuarticEaseIn(aFactorOfTimeCompletion);
1579 # endif
1580 
1581 # if defined(ENABLE_EASE_SINE)
1582  case EASE_SINE_IN:
1583  return SineEaseIn(aFactorOfTimeCompletion);
1584 # endif
1585 # if defined(ENABLE_EASE_CIRCULAR)
1586  case EASE_CIRCULAR_IN:
1587  return CircularEaseIn(aFactorOfTimeCompletion);
1588 # endif
1589 # if defined(ENABLE_EASE_BACK)
1590  case EASE_BACK_IN:
1591  return BackEaseIn(aFactorOfTimeCompletion);
1592 # endif
1593 # if defined(ENABLE_EASE_ELASTIC)
1594  case EASE_ELASTIC_IN:
1595  return ElasticEaseIn(aFactorOfTimeCompletion);
1596 # endif
1597 # if defined(ENABLE_EASE_BOUNCE)
1598  case EASE_BOUNCE_OUT:
1599  return EaseOutBounce(aFactorOfTimeCompletion);
1600 # endif
1601 # if defined(ENABLE_EASE_PRECISION)
1602  case EASE_PRECISION_IN:
1603  return LinearWithQuadraticBounce(aFactorOfTimeCompletion);
1604 # endif
1605  default:
1606  return 0.0;
1607  }
1608 }
1609 
1610 #endif //PROVIDE_ONLY_LINEAR_MOVEMENT
1611 
1616 #if defined(ESP8266)
1617  yield();
1618 #elif defined(ESP32)
1619 // esp_task_wdt_reset();
1620 // yield(); // taskYIELD() and yield() sometimes helps, but this is not deterministic :-(. Allow context switch for the ticker task to run
1621 #endif
1622  return mServoMoves;
1623 }
1624 
1629 #if defined(ESP8266)
1630  yield();
1631 #elif defined(ESP32)
1632 // esp_task_wdt_reset();
1633 // yield(); // Allow context switch for the ticker task to run
1634 #endif
1635  return sInterruptsAreActive;
1636 }
1637 
1646  return isMoving();
1647 }
1648 
1651 }
1652 
1655 }
1656 
1658  return mEndMicrosecondsOrUnits;
1659 }
1660 
1666 }
1667 
1670 }
1671 
1673  return mMillisForCompleteMove;
1674 }
1675 
1681 void ServoEasing::print(Print *aSerial, bool doExtendedOutput) {
1682  printDynamic(aSerial, doExtendedOutput);
1683  printStatic(aSerial);
1684 }
1685 
1690 void ServoEasing::printEasingType(Print *aSerial, uint_fast8_t aEasingType) {
1691 # if defined(__AVR__)
1692  const char *tEaseTypeStringPtr = (char*) pgm_read_word(&easeTypeStrings[aEasingType & EASE_TYPE_MASK]);
1693  aSerial->print((__FlashStringHelper*) (tEaseTypeStringPtr));
1694 # else
1695  aSerial->print(easeTypeStrings[aEasingType]);
1696 # endif
1697  uint_fast8_t tEasingTypeCallStyle = aEasingType & CALL_STYLE_MASK;
1698  if (tEasingTypeCallStyle == CALL_STYLE_IN) {
1699  aSerial->print(F("_in"));
1700  } else if (tEasingTypeCallStyle == CALL_STYLE_OUT) {
1701  aSerial->print(F("_out"));
1702  } else if (tEasingTypeCallStyle == CALL_STYLE_IN_OUT) {
1703  aSerial->print(F("_in_out"));
1704  } else {
1705  aSerial->print(F("_bouncing_in_out"));
1706  }
1707 }
1708 
1714 void ServoEasing::printDynamic(Print *aSerial, bool doExtendedOutput) {
1715 // pin is static but it is required for identifying the servo
1716  aSerial->print(mServoIndex);
1717  aSerial->print('/');
1718  aSerial->print(mServoPin);
1719  aSerial->print(F(": "));
1720 
1722  if (doExtendedOutput) {
1723  aSerial->print('|');
1724  aSerial->print(mCurrentMicrosecondsOrUnits);
1725  }
1726 
1727  aSerial->print(F(" -> "));
1729  if (doExtendedOutput) {
1730  aSerial->print('|');
1731  aSerial->print(mEndMicrosecondsOrUnits);
1732  }
1733 
1734  aSerial->print(F(" = "));
1735  int tDelta;
1736  if (mDeltaMicrosecondsOrUnits >= 0) {
1738  } else {
1740  }
1741  aSerial->print(tDelta);
1742  if (doExtendedOutput) {
1743  aSerial->print('|');
1744  aSerial->print(mDeltaMicrosecondsOrUnits);
1745  }
1746 
1747  aSerial->print(F(" in "));
1748  aSerial->print(mMillisForCompleteMove);
1749  aSerial->print(F(" ms"));
1750 
1751  aSerial->print(F(" with speed="));
1752  aSerial->print(mSpeed);
1753 
1754 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
1755  aSerial->print(F(" and easingType=0x"));
1756  aSerial->print(mEasingType, HEX);
1757  aSerial->print('|');
1758  printEasingType(aSerial, mEasingType);
1759 #endif
1760 
1761  if (doExtendedOutput) {
1762  aSerial->print(F(" MillisAtStartMove="));
1763  aSerial->print(mMillisAtStartMove);
1764  }
1765 
1766  aSerial->println();
1767 }
1768 
1773 void ServoEasing::printStatic(Print *aSerial) {
1774 
1775  aSerial->print(F("0="));
1776  aSerial->print(mServo0DegreeMicrosecondsOrUnits);
1777  aSerial->print(F(" 180="));
1778  aSerial->print(mServo180DegreeMicrosecondsOrUnits);
1779 
1780  aSerial->print(F(" trim="));
1781  if (mTrimMicrosecondsOrUnits >= 0) {
1783  } else {
1785  }
1786  aSerial->print('|');
1787  aSerial->print(mTrimMicrosecondsOrUnits);
1788 
1789  aSerial->print(F(" reverse="));
1790  aSerial->print(mOperateServoReverse);
1791 
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"));
1797 // aSerial->print((uintptr_t) mI2CClass, HEX); // defined since C++11
1798  aSerial->print((uint_fast16_t) mI2CClass, HEX);
1799 #endif
1800 
1801 # if defined(USE_SERVO_LIB)
1802  aSerial->print(F(" at expander="));
1803  aSerial->print(mServoIsConnectedToExpander);
1804 # endif
1805 #endif
1806 
1807  aSerial->print(F(" callback=0x"));
1808  aSerial->print((__SIZE_TYPE__) TargetPositionReachedHandler, HEX);
1809 
1810  aSerial->print(F(" MAX_EASING_SERVOS="));
1811  aSerial->print(MAX_EASING_SERVOS);
1812 
1813  aSerial->print(F(" this=0x"));
1814  aSerial->println((uint_fast16_t) this, HEX);
1815 }
1816 
1821 int clipDegreeSpecial(uint_fast8_t aDegreeToClip) {
1822  if (aDegreeToClip) {
1823  return aDegreeToClip;
1824  }
1825  if (aDegreeToClip < 218) {
1826  return 180;
1827  }
1828  return 0;
1829 }
1830 
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
1839 void handleServoTimerInterrupt(HardwareTimer *aDummy __attribute__((unused))) // changed in stm32duino 1.9 - 5/2020
1840 #else
1842 # endif
1843 {
1844 # if defined(USE_PCA9685_SERVO_EXPANDER)
1845 // Otherwise it will hang forever in I2C transfer
1846 # if !defined(ARDUINO_ARCH_MBED)
1847  interrupts();
1848 # endif
1849 # endif
1850  if (updateAllServos()) {
1851  // disable interrupt only if all servos stopped. This enables independent movements of servos with this interrupt handler.
1853  }
1854 }
1855 #endif // !defined(ENABLE_EXTERNAL_SERVO_TIMER_HANDLER)
1856 
1857 // The eclipse formatter has problems with // comments in undefined code blocks
1858 // !!! Must be without comment and closed by @formatter:on
1859 // @formatter:off
1867 #if defined(__AVR__)
1868 # if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
1869 # if defined(USE_PCA9685_SERVO_EXPANDER) && !defined(USE_SERVO_LIB)
1870 // set timer 5 to 20 ms, since the servo library does not do this for us
1871  TCCR5A = _BV(WGM11);// FastPWM Mode mode TOP (20 ms) determined by ICR1 - non-inverting Compare Output mode OC1A+OC1B
1872  TCCR5B = _BV(WGM13) | _BV(WGM12) | _BV(CS11);// set prescaler to 8, FastPWM mode mode bits WGM13 + WGM12
1873  ICR5 = (F_CPU / 8) / REFRESH_FREQUENCY; // 40000 - set period to 50 Hz / 20 ms
1874 # endif
1875 
1876  TIFR5 |= _BV(OCF5B); // clear any pending interrupts;
1877  TIMSK5 |= _BV(OCIE5B);// enable the output compare B interrupt
1878  OCR5B = ((clockCyclesPerMicrosecond() * REFRESH_INTERVAL_MICROS) / 8) - 100;// update values 100 us before the new servo period starts
1879 
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
1881  // For MegaTinyCore:
1882  // TCB1 is used by Tone()
1883  // TCB2 is used by Servo, but we cannot hijack the ISR, so we must use a dedicated timer for the 20 ms interrupt
1884  // TCB3 is used by millis()
1885  // Must use TCA0, since TCBx have only prescaler %2. Use single (16bit) mode, because it seems to be easier :-)
1886  TCA0.SINGLE.CTRLD = 0; // Single mode - required at least for MegaTinyCore
1887  TCA0.SINGLE.CTRLB = TCA_SINGLE_WGMODE_NORMAL_gc; // Normal mode, top = PER
1888  TCA0.SINGLE.PER = (((F_CPU / 1000000) * REFRESH_INTERVAL_MICROS) / 8); // 40000 at 16 MHz
1889  TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV8_gc | TCA_SINGLE_ENABLE_bm; // set prescaler to 8 and enable timer
1890  TCA0.SINGLE.INTCTRL = TCA_SINGLE_OVF_bm; // Enable overflow interrupt
1891 
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.
1894 # endif
1895 # elif defined(TCCR1B) && defined(TIFR1) // Uno, Nano etc.
1896  /*
1897  * Standard AVR
1898  * Use timer 1, together with the servo library, which uses the output compare A interrupt.
1899  * Therefore we use the output compare B interrupt and generate an interrupt 100 microseconds,
1900  * before a new servo period starts. This leaves the first servo signals undisturbed.
1901  */
1902 # if defined(USE_PCA9685_SERVO_EXPANDER) && !defined(USE_SERVO_LIB)
1903 // // set timer 1 to 20 ms, since the servo library or lightweight_servo library does not do this for us
1904  TCCR1A = _BV(WGM11); // FastPWM Mode mode TOP (20 ms) determined by ICR1 - non-inverting Compare Output mode OC1A+OC1B
1905  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11); // set prescaler to 8, FastPWM mode mode bits WGM13 + WGM12
1906  ICR1 = (F_CPU / 8) / REFRESH_FREQUENCY; // 40000 - set period to 50 Hz / 20 ms
1907 # endif
1908 
1909  TIFR1 |= _BV(OCF1B); // clear any pending interrupts;
1910  TIMSK1 |= _BV(OCIE1B); // enable the output compare B interrupt used by ServoEasing
1911  /*
1912  * Misuse the "Input Capture Noise Canceler Bit" as a flag, that signals that interrupts for ServoEasing are enabled again.
1913  * It is required if disableServoEasingInterrupt() is suppressed e.g. by an overwritten handleServoTimerInterrupt() function
1914  * because the servo interrupt is used to synchronize e.g. NeoPixel updates.
1915  */
1916  TCCR1B |= _BV(ICNC1);
1917 # if !defined(USE_LEIGHTWEIGHT_SERVO_LIB)
1918  OCR1B = ((clockCyclesPerMicrosecond() * REFRESH_INTERVAL_MICROS) / 8) - 100; // Generate interrupt 100 us before a new servo period starts
1919 # endif
1920 
1921 # else
1922 #error "This AVR CPU is not supported by ServoEasing"
1923 # endif
1924 
1925 #elif defined(ESP8266) || defined(ESP32)
1927  Timer20ms.detach(); // otherwise the ESP32 kernel at least will crash and reboot
1928  }
1929  // The callback is called by a RTOS task not an ISR, which allows us to have the callback without the IRAM attribute
1930  Timer20ms.attach_ms(REFRESH_INTERVAL_MILLIS, handleServoTimerInterrupt);
1931 
1932 // BluePill in 2 flavors
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); // used for generating only interrupts, no pin specified
1935  Timer20ms.setOverflow(REFRESH_INTERVAL_MICROS, MICROSEC_FORMAT); // microsecond period
1936  Timer20ms.attachInterrupt(handleServoTimerInterrupt); // this sets update interrupt enable
1937  Timer20ms.resume(); // Start or resume HardwareTimer: all channels are resumed, interrupts are enabled if necessary
1938 
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);
1941  Timer20ms.setPeriod(REFRESH_INTERVAL_MICROS); // 20000 microsecond period
1942  Timer20ms.attachInterrupt(TIMER_CH1, handleServoTimerInterrupt);
1943  Timer20ms.refresh(); // Set the timer's count to 0 and update the prescaler and overflow values.
1944 
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);
1950 
1951  // TIMER_CLOCK3 is MCK/32. MCK is 84MHz Set up the Timer in waveform mode which creates a PWM in UP mode with automatic trigger on RC Compare
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); // =52500 -> 20ms
1954 
1955  TC_Start(TC_FOR_20_MS_TIMER, CHANNEL_FOR_20_MS_TIMER); // Enables the timer clock stopped by TC_Configure() and performs a software reset to start the counting
1956 
1957  // Enable the RC Compare Interrupt
1958  TC_FOR_20_MS_TIMER->TC_CHANNEL[CHANNEL_FOR_20_MS_TIMER].TC_IER = TC_IER_CPCS;
1959  // Disable all others
1960  TC_FOR_20_MS_TIMER->TC_CHANNEL[CHANNEL_FOR_20_MS_TIMER].TC_IDR = ~TC_IER_CPCS;
1961 
1962 #elif defined(ARDUINO_ARCH_SAMD)
1963  // Servo uses timer 4 and we use timer 5. therefore we cannot change clock source to 32 kHz.
1964 
1965  TcCount16 *TC = (TcCount16*) TC5;
1966 # if defined(__SAMD51__)
1967  // SAMD51 Code initially provided by Lutz
1976  // Enable the TC5 clock, use generic clock generator 0 (F_CPU) for TC5
1977  GCLK->PCHCTRL[TC5_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
1978  while (GCLK->SYNCBUSY.reg > 0); // Sync GCLK for TC5
1979 
1980  // The TC should be disabled before the TC is reset in order to avoid undefined behavior.
1981  TC->CTRLA.reg &= ~TC_CTRLA_ENABLE; // Disable the Timer
1982  while (TC->SYNCBUSY.bit.ENABLE); // Wait for disabled
1983  // Reset TCx
1984  TC->CTRLA.reg = TC_CTRLA_SWRST;
1985  // When writing a '1' to the CTRLA.SWRST bit it will immediately read as '1'.
1986  while (TC->SYNCBUSY.bit.SWRST); // CTRL.SWRST will be cleared by hardware when the peripheral has been reset.
1987 
1988  // SAMD51 has F_CPU = 120 MHz
1989  TC->CC[0].reg = ((F_CPU / (256 * REFRESH_FREQUENCY)) - 1); // (9375 - 1) at 120 MHz and 256 prescaler. With prescaler 64 we get 37500-1.
1990  /*
1991  * Set timer counter mode to 16 bits, set mode as match frequency, prescaler is DIV256, start counter
1992  */
1993  TC->CTRLA.reg |= TC_CTRLA_MODE_COUNT16 | TC_WAVE_WAVEGEN_MFRQ | TC_CTRLA_PRESCALER_DIV256 | TC_CTRLA_ENABLE;
1994 // while (TC->STATUS.bit.SYNCBUSY == 1); // The next commands do an implicit wait :-)
1995 
1996 # else
1997  // Enable GCLK and select GCLK0 (F_CPU) as clock for TC4 and TC5
1998  GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5)); // GCLK0 is F_CPU | 48 MHz
1999 // while (GCLK->STATUS.bit.SYNCBUSY) // not required to wait
2000 // ;
2001 
2002  // The TC should be disabled before the TC is reset in order to avoid undefined behavior.
2003  TC->CTRLA.reg &= ~TC_CTRLA_ENABLE;
2004  // 14.3.2.2 When write-synchronization is ongoing for a register, any subsequent write attempts to this register will be discarded, and an error will be reported.
2005  // 14.3.1.4 It is also possible to perform the next read/write operation and wait,
2006  // as this next operation will be started once the previous write/read operation is synchronized and/or complete. ???
2007  while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync to ensure that we can write again to COUNT16.CTRLA.reg
2008  // Reset TCx
2009  TC->CTRLA.reg = TC_CTRLA_SWRST;
2010  // When writing a '1' to the CTRLA.SWRST bit it will immediately read as '1'.
2011  while (TC->CTRLA.bit.SWRST); // CTRL.SWRST will be cleared by hardware when the peripheral has been reset.
2012 
2013  TC->CC[0].reg = ((F_CPU / (64 * REFRESH_FREQUENCY)) - 1); // (15000 - 1) at 48 Mhz and 64 prescaler.
2014  /*
2015  * Set timer counter mode to 16 bits, set mode as match frequency, prescaler is DIV64 => 750 kHz clock, start counter
2016  */
2017  TC->CTRLA.reg |= TC_CTRLA_MODE_COUNT16| TC_CTRLA_WAVEGEN_MFRQ | TC_CTRLA_PRESCALER_DIV64 | TC_CTRLA_ENABLE;
2018 // while (TC->STATUS.bit.SYNCBUSY == 1); // The next commands do an implicit wait :-)
2019 
2020 # endif // defined(__SAMD51__)
2021  // Common SAMD here
2022  // Configure interrupt request
2023  NVIC_DisableIRQ(TC5_IRQn);
2024  NVIC_ClearPendingIRQ(TC5_IRQn);
2025  NVIC_SetPriority(TC5_IRQn, 0);
2026  NVIC_EnableIRQ(TC5_IRQn);
2027 
2028  // Enable the TC5 interrupt request
2029  TC->INTENSET.bit.MC0 = 1;
2030 // while (TC->STATUS.reg & TC_STATUS_SYNCBUSY) // Not required to wait at end of function
2031 // ; // wait until TC5 is done syncing
2032 
2033 //#elif defined(ARDUINO_ARCH_APOLLO3)
2034 // // use timer 3 segment A
2035 // am_hal_ctimer_clear(3, AM_HAL_CTIMER_TIMERA); // reset timer
2036 // // only AM_HAL_CTIMER_FN_REPEAT resets counter after match (CTC mode)
2037 // am_hal_ctimer_config_single(3, AM_HAL_CTIMER_TIMERA,
2038 // (AM_HAL_CTIMER_INT_ENABLE | AM_HAL_CTIMER_HFRC_12KHZ | AM_HAL_CTIMER_FN_REPEAT));
2039 // am_hal_ctimer_compare_set(3, AM_HAL_CTIMER_TIMERA, 0, 12000 / REFRESH_FREQUENCY);
2040 // am_hal_ctimer_start(3, AM_HAL_CTIMER_TIMERA);
2041 //
2042 // am_hal_ctimer_int_register(AM_HAL_CTIMER_INT_TIMERA3, handleServoTimerInterrupt);
2043 // am_hal_ctimer_int_enable(AM_HAL_CTIMER_INT_TIMERA3);
2044 // NVIC_EnableIRQ(CTIMER_IRQn);
2045 
2046 #elif defined(ARDUINO_ARCH_MBED)
2047  Timer20ms.attach(handleServoTimerInterrupt, std::chrono::microseconds(REFRESH_INTERVAL_MICROS));
2048 
2049 #elif defined(ARDUINO_ARCH_RP2040)
2050  add_repeating_timer_us(REFRESH_INTERVAL_MICROS, handleServoTimerInterruptHelper, NULL, &Timer20ms);
2051 
2052 #elif defined(TEENSYDUINO)
2053  // common for all Teensy
2055 
2056 #else
2057 #warning Board / CPU is not covered by definitions using pre-processor symbols -> no timer available. Please extend ServoEasing.cpp.
2058 #endif
2060 }
2061 
2062 #if defined(__AVR_ATmega328P__)
2063 
2066 void setTimer1InterruptMarginMicros(uint16_t aInterruptMarginMicros){
2067  // Generate interrupt aInterruptMarginMicros us before a new servo period starts
2068  OCR1B = ((clockCyclesPerMicrosecond() * REFRESH_INTERVAL_MICROS) / 8) - aInterruptMarginMicros;
2069 }
2070 #endif
2071 
2073 #if defined(__AVR__)
2074 # if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
2075  TIMSK5 &= ~(_BV(OCIE5B)); // disable the output compare B interrupt
2076 
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); // disable the overflow interrupt
2079 
2080 # elif defined(TIMSK1)// defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
2081  TIMSK1 &= ~(_BV(OCIE1B)); // disable the output compare B interrupt
2082 
2083 # else
2084 #error "This AVR CPU is not supported by ServoEasing"
2085 # endif
2086 
2087 #elif defined(ESP8266) || defined(ESP32)
2088  Timer20ms.detach();
2089 
2090 #elif defined(STM32F1xx) // for "Generic STM32F1 series" from STM32 Boards from STM32 cores of Arduino Board manager
2091  // https://github.com/stm32duino/BoardManagerFiles/raw/master/STM32/package_stm_index.json
2092  Timer20ms.setMode(LL_TIM_CHANNEL_CH1, TIMER_DISABLED);
2093  Timer20ms.detachInterrupt();
2094 
2095 #elif defined(__STM32F1__) // for "Generic STM32F103C series" from STM32F1 Boards (STM32duino.com) of Arduino Board manager
2096  // http://dan.drown.org/stm32duino/package_STM32duino_index.json
2097  Timer20ms.setMode(TIMER_CH1, TIMER_DISABLED);
2098  Timer20ms.detachInterrupt(TIMER_CH1);
2099 
2100 #elif defined(__SAM3X8E__) // Arduino DUE
2101  NVIC_DisableIRQ(IRQn_FOR_20_MS_TIMER);
2102 
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); //wait until TC5 is done syncing
2107 # else
2108  while (TC5->COUNT16.STATUS.reg & TC_STATUS_SYNCBUSY); //wait until TC5 is done syncing
2109 # endif
2110 
2111 #elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE + Sparkfun Apollo3
2112  Timer20ms.detach();
2113 
2114 #elif defined(ARDUINO_ARCH_RP2040)
2115  cancel_repeating_timer(&Timer20ms);
2116 
2117 //#elif defined(ARDUINO_ARCH_APOLLO3)
2118 // am_hal_ctimer_int_disable(AM_HAL_CTIMER_INT_TIMERA3);
2119 
2120 #elif defined(TEENSYDUINO)
2121  Timer20ms.end();
2122 #endif
2124 }
2125 
2126 // @formatter:on
2132 #if defined(__AVR__)
2133 # if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
2134 ISR(TIMER5_COMPB_vect) {
2136 }
2137 
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; // Reset interrupt flags.
2142 }
2143 
2144 # else // defined(__AVR__)
2145 ISR(TIMER1_COMPB_vect) {
2146 # if defined(MEASURE_SERVO_EASING_INTERRUPT_TIMING)
2147  digitalWriteFast(TIMING_OUTPUT_PIN, HIGH);
2148 # endif
2150 # if defined(MEASURE_SERVO_EASING_INTERRUPT_TIMING)
2151  digitalWriteFast(TIMING_OUTPUT_PIN, LOW);
2152 # endif
2153 }
2154 # endif
2155 
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);
2160 # endif
2161  // Clear interrupt
2162  TC_GetStatus(TC_FOR_20_MS_TIMER, CHANNEL_FOR_20_MS_TIMER);//Clear channel status to fire again the interrupt.
2164 # if defined(MEASURE_SERVO_EASING_INTERRUPT_TIMING)
2165  digitalWrite(TIMING_OUTPUT_PIN, LOW);
2166 # endif
2167 }
2168 
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);
2173 # endif
2174  // Clear interrupt
2175  TC5->COUNT16.INTFLAG.bit.MC0 = 1;
2177 # if defined(MEASURE_SERVO_EASING_INTERRUPT_TIMING)
2178  digitalWrite(TIMING_OUTPUT_PIN, LOW);
2179 # endif
2180 }
2181 
2182 //#elif defined(ARDUINO_ARCH_APOLLO3)
2183 //extern "C" void am_ctimer_isr(void) {
2184 // // Check and clear any active CTIMER interrupts.
2185 // uint32_t ui32Status = am_hal_ctimer_int_status_get(true);
2186 // am_hal_ctimer_int_clear(ui32Status);
2187 //
2188 // // Run handlers for the various possible timer events.
2189 // am_hal_ctimer_int_service(ui32Status);
2190 //}
2191 #endif // defined(__AVR__)
2192 
2193 /************************************
2194  * ServoEasing list functions
2195  ***********************************/
2196 
2197 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
2198 void setEasingTypeForAllServos(uint_fast8_t aEasingType) {
2200 }
2201 
2205 void setEasingTypeForMultipleServos(uint_fast8_t aNumberOfServos, uint_fast8_t aEasingType) {
2206  for (uint_fast8_t tServoIndex = 0; tServoIndex <= aNumberOfServos; ++tServoIndex) {
2207  if (ServoEasing::ServoEasingArray[tServoIndex] != NULL) {
2208  ServoEasing::ServoEasingArray[tServoIndex]->mEasingType = aEasingType;
2209  }
2210  }
2211 }
2212 #endif
2213 
2217 }
2218 
2219 void setEaseToForAllServosSynchronizeAndStartInterrupt(uint_fast16_t aDegreesPerSecond) {
2220  setEaseToForAllServos(aDegreesPerSecond);
2222 }
2223 
2230 }
2231 
2232 void setEaseToForAllServosSynchronizeAndWaitForAllServosToStop(uint_fast16_t aDegreesPerSecond) {
2233  setEaseToForAllServos(aDegreesPerSecond);
2235 }
2236 
2240 }
2241 
2242 void synchronizeAndEaseToArrayPositions(uint_fast16_t aDegreesPerSecond) {
2243  setEaseToForAllServos(aDegreesPerSecond);
2245 }
2246 
2251 void printArrayPositions(Print *aSerial) {
2252 // uint_fast8_t tServoIndex = 0;
2253  aSerial->print(F("ServoNextPositionArray="));
2254 // AJ 22.05.2019 This does not work with GCC 7.3.0 atmel6.3.1 and -Os
2255 // It drops the tServoIndex < MAX_EASING_SERVOS condition, since MAX_EASING_SERVOS is equal to the size of sServoArray
2256 // This has only an effect if the whole sServoArray is filled up, i.e we have declared MAX_EASING_SERVOS ServoEasing objects.
2257 // while (ServoEasing::ServoEasingArray[tServoIndex] != NULL && tServoIndex < MAX_EASING_SERVOS) {
2258 // aSerial->print(ServoEasingNextPositionArray[tServoIndex]);
2259 // aSerial->print(F(" | "));
2260 // tServoIndex++;
2261 // }
2262 
2263 // switching conditions cures the bug
2264 // while (tServoIndex < MAX_EASING_SERVOS && ServoEasing::ServoEasingArray[tServoIndex] != NULL) {
2265 
2266 // this also does not work
2267 // for (uint_fast8_t tServoIndex = 0; ServoEasing::ServoEasingArray[tServoIndex] != NULL && tServoIndex < MAX_EASING_SERVOS ; ++tServoIndex) {
2268 // aSerial->print(ServoEasingNextPositionArray[tServoIndex]);
2269 // aSerial->print(F(" | "));
2270 // }
2271  for (uint_fast8_t tServoIndex = 0; tServoIndex <= ServoEasing::sServoArrayMaxIndex; ++tServoIndex) {
2272  aSerial->print(ServoEasing::ServoEasingNextPositionArray[tServoIndex]);
2273  aSerial->print(F(" | "));
2274  }
2275  aSerial->println();
2276 }
2277 
2278 void writeAllServos(int aDegreeOrMicrosecond) {
2279  for (uint_fast8_t tServoIndex = 0; tServoIndex <= ServoEasing::sServoArrayMaxIndex; ++tServoIndex) {
2280  if (ServoEasing::ServoEasingArray[tServoIndex] != NULL) {
2281  ServoEasing::ServoEasingArray[tServoIndex]->write(aDegreeOrMicrosecond);
2282  }
2283  }
2284 }
2285 
2286 void setSpeedForAllServos(uint_fast16_t aDegreesPerSecond) {
2287  for (uint_fast8_t tServoIndex = 0; tServoIndex <= ServoEasing::sServoArrayMaxIndex; ++tServoIndex) {
2288  if (ServoEasing::ServoEasingArray[tServoIndex] != NULL) {
2289  ServoEasing::ServoEasingArray[tServoIndex]->mSpeed = aDegreesPerSecond;
2290  }
2291  }
2292 }
2293 
2294 #if defined(va_arg)
2295 
2298 void setDegreeForAllServos(uint_fast8_t aNumberOfServos, va_list *aDegreeValues) {
2299  for (uint_fast8_t tServoIndex = 0; tServoIndex < aNumberOfServos; ++tServoIndex) {
2300  ServoEasing::ServoEasingNextPositionArray[tServoIndex] = va_arg(*aDegreeValues, int);
2301  }
2302 }
2303 #endif
2304 
2305 #if defined(va_start)
2306 
2309 void setDegreeForAllServos(uint_fast8_t aNumberOfServos, ...) {
2310  va_list aDegreeValues;
2311  va_start(aDegreeValues, aNumberOfServos);
2312  setDegreeForAllServos(aNumberOfServos, &aDegreeValues);
2313  va_end(aDegreeValues);
2314 }
2315 #endif
2316 
2323  bool tOneServoIsMoving = false;
2324  for (uint_fast8_t tServoIndex = 0; tServoIndex <= ServoEasing::sServoArrayMaxIndex; ++tServoIndex) {
2325  if (ServoEasing::ServoEasingArray[tServoIndex] != NULL) {
2326  tOneServoIsMoving = ServoEasing::ServoEasingArray[tServoIndex]->setEaseTo(
2328  || tOneServoIsMoving;
2329  }
2330  }
2331  return tOneServoIsMoving;
2332 }
2333 
2339 bool setEaseToForAllServos(uint_fast16_t aDegreesPerSecond) {
2340  bool tOneServoIsMoving = false;
2341  for (uint_fast8_t tServoIndex = 0; tServoIndex <= ServoEasing::sServoArrayMaxIndex; ++tServoIndex) {
2342  if (ServoEasing::ServoEasingArray[tServoIndex] != NULL) {
2343  tOneServoIsMoving = ServoEasing::ServoEasingArray[tServoIndex]->setEaseTo(
2344  ServoEasing::ServoEasingNextPositionArray[tServoIndex], aDegreesPerSecond) || tOneServoIsMoving;
2345  }
2346  }
2347  return tOneServoIsMoving;
2348 }
2349 
2355 bool setEaseToDForAllServos(uint_fast16_t aMillisForMove) {
2356  bool tOneServoIsMoving = false;
2357  for (uint_fast8_t tServoIndex = 0; tServoIndex <= ServoEasing::sServoArrayMaxIndex; ++tServoIndex) {
2358  if (ServoEasing::ServoEasingArray[tServoIndex] != NULL) {
2359  tOneServoIsMoving = ServoEasing::ServoEasingArray[tServoIndex]->setEaseToD(
2360  ServoEasing::ServoEasingNextPositionArray[tServoIndex], aMillisForMove) || tOneServoIsMoving;
2361  }
2362  }
2363  return tOneServoIsMoving;
2364 }
2365 
2367  for (uint_fast8_t tServoIndex = 0; tServoIndex <= ServoEasing::sServoArrayMaxIndex; ++tServoIndex) {
2368  if (ServoEasing::ServoEasingArray[tServoIndex] != NULL && ServoEasing::ServoEasingArray[tServoIndex]->mServoMoves) {
2369  return true;
2370  }
2371  }
2372  return false;
2373 }
2374 
2376  for (uint_fast8_t tServoIndex = 0; tServoIndex <= ServoEasing::sServoArrayMaxIndex; ++tServoIndex) {
2377  if (ServoEasing::ServoEasingArray[tServoIndex] != NULL) {
2378  ServoEasing::ServoEasingArray[tServoIndex]->mServoMoves = false;
2379  }
2380  }
2381 #if !defined(ENABLE_EXTERNAL_SERVO_TIMER_HANDLER)
2382  disableServoEasingInterrupt(); // For external handler, this must also be able to be managed externally
2383 #endif
2384 }
2385 
2387 #if !defined(DISABLE_PAUSE_RESUME)
2388  unsigned long tMillis = millis();
2389  for (uint_fast8_t tServoIndex = 0; tServoIndex <= ServoEasing::sServoArrayMaxIndex; ++tServoIndex) {
2390  if (ServoEasing::ServoEasingArray[tServoIndex] != NULL) {
2391  ServoEasing::ServoEasingArray[tServoIndex]->mServoIsPaused = true;
2392  ServoEasing::ServoEasingArray[tServoIndex]->mMillisAtStopMove = tMillis;
2393  }
2394  }
2395 #endif
2396 }
2397 
2399  for (uint_fast8_t tServoIndex = 0; tServoIndex <= ServoEasing::sServoArrayMaxIndex; ++tServoIndex) {
2400  if (ServoEasing::ServoEasingArray[tServoIndex] != NULL) {
2402  }
2403  }
2404 }
2405 
2407  for (uint_fast8_t tServoIndex = 0; tServoIndex <= ServoEasing::sServoArrayMaxIndex; ++tServoIndex) {
2408  if (ServoEasing::ServoEasingArray[tServoIndex] != NULL) {
2410  }
2411  }
2412 }
2413 
2418  bool tAllServosStopped = true;
2419  for (uint_fast8_t tServoIndex = 0; tServoIndex <= ServoEasing::sServoArrayMaxIndex; ++tServoIndex) {
2420  if (ServoEasing::ServoEasingArray[tServoIndex] != NULL) {
2421  tAllServosStopped = ServoEasing::ServoEasingArray[tServoIndex]->update() && tAllServosStopped;
2422 
2423  }
2424  }
2425 #if defined(PRINT_FOR_SERIAL_PLOTTER)
2426  Serial.println(); // End of one complete data set
2427 #endif
2428  return tAllServosStopped;
2429 }
2430 
2435  do {
2436  // First do the delay, then check for update, since we are probably called directly after start and there is nothing to move yet
2437  delay(REFRESH_INTERVAL_MILLIS); // 20 ms
2438  } while (!updateAllServos());
2439 }
2440 
2446 bool delayAndUpdateAndWaitForAllServosToStop(unsigned long aMillisDelay, bool aTerminateDelayIfAllServosStopped) {
2447  while (true) {
2448  // First do the delay, then check for update, since we are probably called directly after start and there is nothing to move yet
2449  if (aMillisDelay > REFRESH_INTERVAL_MILLIS) {
2450  aMillisDelay -= REFRESH_INTERVAL_MILLIS;
2451  delay(REFRESH_INTERVAL_MILLIS); // 20 ms
2452  if (updateAllServos() && aTerminateDelayIfAllServosStopped) {
2453  // terminate delay here and return
2454  return true;
2455  }
2456  } else {
2457  delay(aMillisDelay);
2458  return updateAllServos();
2459  }
2460  }
2461 }
2462 
2469 }
2470 
2474 void synchronizeAllServosAndStartInterrupt(bool aStartUpdateByInterrupt) {
2475  /*
2476  * Find maximum duration and one start time
2477  */
2478  uint_fast16_t tMaxMillisForCompleteMove = 0;
2479  uint32_t tMillisAtStartMove = 0;
2480 
2481  for (uint_fast8_t tServoIndex = 0; tServoIndex <= ServoEasing::sServoArrayMaxIndex; ++tServoIndex) {
2482  if (ServoEasing::ServoEasingArray[tServoIndex] != NULL && ServoEasing::ServoEasingArray[tServoIndex]->mServoMoves) {
2483  // process servos which really moves
2484  tMillisAtStartMove = ServoEasing::ServoEasingArray[tServoIndex]->mMillisAtStartMove;
2485  if (ServoEasing::ServoEasingArray[tServoIndex]->mMillisForCompleteMove > tMaxMillisForCompleteMove) {
2486  tMaxMillisForCompleteMove = ServoEasing::ServoEasingArray[tServoIndex]->mMillisForCompleteMove;
2487  }
2488  }
2489  }
2490 
2491 #if defined(LOCAL_TRACE)
2492  Serial.print(F("Number of servos="));
2493  Serial.print(ServoEasing::sServoArrayMaxIndex);
2494  Serial.print(F(" MillisAtStartMove="));
2495  Serial.print(tMillisAtStartMove);
2496  Serial.print(F(" MaxMillisForCompleteMove="));
2497  Serial.println(tMaxMillisForCompleteMove);
2498 #endif
2499 
2500  /*
2501  * Set maximum duration and start time to all servos
2502  * Synchronize start time to avoid race conditions at the end of movement
2503  */
2504  for (uint_fast8_t tServoIndex = 0; tServoIndex <= ServoEasing::sServoArrayMaxIndex; ++tServoIndex) {
2505  if (ServoEasing::ServoEasingArray[tServoIndex] != NULL && ServoEasing::ServoEasingArray[tServoIndex]->mServoMoves) {
2506  ServoEasing::ServoEasingArray[tServoIndex]->mMillisAtStartMove = tMillisAtStartMove;
2507  ServoEasing::ServoEasingArray[tServoIndex]->mMillisForCompleteMove = tMaxMillisForCompleteMove;
2508  }
2509  }
2510 
2511  if (aStartUpdateByInterrupt) {
2513  }
2514 }
2515 
2516 #if !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
2517 /*********************************************************
2518  * Included easing functions
2519  * Input is from 0.0 to 1.0 with 0.0 -> 0 % and 1.0 -> 100% completion of time between the two endpoints
2520  * Output is from 0.0 to 1.0 with: 0.0 -> 0 % and 1.0 -> 100% completion of movement (e.g. 1.1 is 10% overshot)
2521  ********************************************************/
2522 //float (*sEaseFunctionArray[])(
2523 // float aFactorOfTimeCompletion) = {&QuadraticEaseIn, &CubicEaseIn, &QuarticEaseIn, &SineEaseIn, &CircularEaseIn, &BackEaseIn, &ElasticEaseIn,
2524 // &EaseOutBounce};
2528 float ServoEasing::QuadraticEaseIn(float aFactorOfTimeCompletion) {
2529  return (aFactorOfTimeCompletion * aFactorOfTimeCompletion);
2530 }
2531 
2532 float ServoEasing::CubicEaseIn(float aFactorOfTimeCompletion) {
2533  return (aFactorOfTimeCompletion * QuadraticEaseIn(aFactorOfTimeCompletion));
2534 }
2535 
2536 float ServoEasing::QuarticEaseIn(float aFactorOfTimeCompletion) {
2537  return QuadraticEaseIn(QuadraticEaseIn(aFactorOfTimeCompletion));
2538 }
2539 
2544 float ServoEasing::SineEaseIn(float aFactorOfTimeCompletion) {
2545  return sin((aFactorOfTimeCompletion - 1) * M_PI_2) + 1;
2546 }
2547 
2553 float ServoEasing::CircularEaseIn(float aFactorOfTimeCompletion) {
2554  return 1 - sqrt(1 - (aFactorOfTimeCompletion * aFactorOfTimeCompletion));
2555 }
2556 
2561 float ServoEasing::BackEaseIn(float aFactorOfTimeCompletion) {
2562  return (aFactorOfTimeCompletion * aFactorOfTimeCompletion * aFactorOfTimeCompletion)
2563  - (aFactorOfTimeCompletion * sin(aFactorOfTimeCompletion * M_PI));
2564 }
2565 
2570 float ServoEasing::ElasticEaseIn(float aFactorOfTimeCompletion) {
2571  return sin(13 * M_PI_2 * aFactorOfTimeCompletion) * pow(2, 10 * (aFactorOfTimeCompletion - 1));
2572 }
2573 
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
2588 float ServoEasing::LinearWithQuadraticBounce(float aFactorOfTimeCompletion) {
2590  || ((mEasingType & CALL_STYLE_OUT) == 0 && mDeltaMicrosecondsOrUnits >= 0)) {
2591  // Use linear moving for this direction/type combination.
2592  return aFactorOfTimeCompletion;
2593  } else {
2594  if (mEasingType & CALL_STYLE_OUT) {
2595  aFactorOfTimeCompletion = 1 - aFactorOfTimeCompletion; // reverse the reverse calling :-) so we have from 0.0 to 1.0
2596  }
2597 
2598  /*
2599  * We are approaching from the direction, which requires a bounce.
2600  * Use scaled linear moving the first 80 % of the movement, and add a quadratic bounce for the remaining 20%.
2601  */
2602  if (aFactorOfTimeCompletion < PART_OF_LINEAR_MOVEMENT) {
2603  // The linear part, return scaled up float aFactorOfTimeCompletion
2604  aFactorOfTimeCompletion = aFactorOfTimeCompletion * (1.0 / PART_OF_LINEAR_MOVEMENT);
2605  if (mEasingType & CALL_STYLE_OUT) {
2606  return 1 - aFactorOfTimeCompletion; // must return reverse factor
2607  }
2608  return aFactorOfTimeCompletion; // for IN function, return plain factor
2609 
2610  } else {
2611  /*
2612  * The bounce for the IN function (aFactorOfTimeCompletion from 0.8 to 1.0)
2613  */
2614  float tRemainingFactor;
2615  if (aFactorOfTimeCompletion < (1.0 - PART_OF_BOUNCE_MOVEMENT_HALF)) {
2616  // Between 80 % and 90 % here. Starting part of the overshoot bounce
2617  tRemainingFactor = aFactorOfTimeCompletion - PART_OF_LINEAR_MOVEMENT; // tRemainingFactor - 0.8 -> 0.0 to 0.1
2618  tRemainingFactor = tRemainingFactor * (1 / PART_OF_BOUNCE_MOVEMENT_HALF); // tRemainingFactor is 0.0 to 1.0
2619  tRemainingFactor = 1.0 - tRemainingFactor; // tRemainingFactor is 1.0 to 0.0 -> quadratic out
2620  } else {
2621  // Between 90 % and 100 % here. Returning part of the overshoot bounce
2622  tRemainingFactor = aFactorOfTimeCompletion - (1.0 - PART_OF_BOUNCE_MOVEMENT_HALF); // tRemainingFactor - 0.9 -> 0.0 to 0.1
2623  tRemainingFactor = tRemainingFactor * (1 / PART_OF_BOUNCE_MOVEMENT_HALF); // tRemainingFactor is 0.0 to 1.0 -> quadratic in
2624  }
2625 
2626  uint_fast8_t tBumpMicrosecondsOrUnits = OVERSHOOT_AMOUNT_MILLIS;
2627 #if defined(USE_PCA9685_SERVO_EXPANDER)
2628 # if defined(USE_SERVO_LIB)
2629  if (mServoIsConnectedToExpander) {
2630  tBumpMicrosecondsOrUnits = OVERSHOOT_AMOUNT_UNITS;
2631  } else {
2632  tBumpMicrosecondsOrUnits = OVERSHOOT_AMOUNT_MILLIS;
2633  }
2634 # else
2635  tBumpMicrosecondsOrUnits = OVERSHOOT_AMOUNT_UNITS;
2636 # endif
2637 #else
2638  tBumpMicrosecondsOrUnits = OVERSHOOT_AMOUNT_MILLIS;
2639 #endif
2640  // return direct microseconds or units values for constant bump
2641  if (mEasingType & CALL_STYLE_OUT) {
2642  /*
2643  * Positive bounce for movings from below
2644  * must compensate for processing at update by: tFactorOfMovementCompletion = 1.0 - (callEasingFunction(1.0 - tFactorOfTimeCompletion));
2645  */
2646  return -(mEndMicrosecondsOrUnits + tBumpMicrosecondsOrUnits - 1
2647  - (tBumpMicrosecondsOrUnits * tRemainingFactor * tRemainingFactor));
2648  } else {
2649  /*
2650  * Negative bounce for movings from above
2651  */
2652  return mEndMicrosecondsOrUnits + (tBumpMicrosecondsOrUnits * tRemainingFactor * tRemainingFactor)
2653  - tBumpMicrosecondsOrUnits;
2654  }
2655  }
2656  }
2657 }
2658 
2664 float ServoEasing::EaseOutBounce(float aFactorOfTimeCompletion) {
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;
2674  } else {
2675  tFactorOfMovementCompletion = (54 / 5.0 * aFactorOfTimeCompletion * aFactorOfTimeCompletion)
2676  - (513 / 25.0 * aFactorOfTimeCompletion) + 268 / 25.0;
2677  }
2678  return tFactorOfMovementCompletion;
2679 }
2680 #endif // !defined(PROVIDE_ONLY_LINEAR_MOVEMENT)
2681 
2682 /************************************
2683  * Convenience I2C check function
2684  * One version as class methods and one version as static function
2685  ***********************************/
2686 #if defined(USE_PCA9685_SERVO_EXPANDER)
2687 
2693 #if defined(__AVR__)
2694 bool ServoEasing::InitializeAndCheckI2CConnection(Print *aSerial) // Print instead of Stream saves 95 bytes flash
2695 #else
2696 bool ServoEasing::InitializeAndCheckI2CConnection(Stream *aSerial) // Print has no flush()
2697 #endif
2698  {
2699 #if !defined(USE_SOFT_I2C_MASTER)
2700  // Initialize wire before checkI2CConnection()
2701  I2CInit();
2702 #endif
2703  return checkI2CConnection(mPCA9685I2CAddress, aSerial);
2704 }
2705 
2706 #if defined(__AVR__)
2707 bool checkI2CConnection(uint8_t aI2CAddress, Print *aSerial) // Print instead of Stream saves 95 bytes flash
2708 #else
2709 bool checkI2CConnection(uint8_t aI2CAddress, Stream *aSerial) // Print has no flush()
2710 #endif
2711  {
2712 
2713  bool tRetValue = false;
2714  aSerial->print(F("Try to communicate with I2C device at address=0x"));
2715  aSerial->println(aI2CAddress, HEX);
2716  aSerial->flush();
2717 
2718  // Initialize wire
2719 #if defined(USE_SOFT_I2C_MASTER)
2720  if(i2c_init()){
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"));
2724  tRetValue = true;
2725  } else {
2726  aSerial->print(F("Found"));
2727  }
2728  i2c_stop();
2729  aSerial->print(F(" I2C device attached at address: 0x"));
2730  aSerial->println(aI2CAddress, HEX);
2731  } else {
2732  aSerial->println(F("I2C init failed"));
2733  }
2734 #else // defined(USE_SOFT_I2C_MASTER)
2735 
2736 # if defined (ARDUINO_ARCH_AVR) // Other platforms do not have this new function
2737  do {
2738  Wire.beginTransmission(aI2CAddress);
2739  if (Wire.getWireTimeoutFlag()) {
2740  aSerial->println(F("Timeout accessing I2C bus. Wait for bus becoming available"));
2741  Wire.clearWireTimeoutFlag();
2742  delay(100);
2743  } else {
2744  break;
2745  }
2746  } while (true);
2747 # else
2748  Wire.beginTransmission(aI2CAddress);
2749 # endif
2750 
2751  uint8_t tWireReturnCode = Wire.endTransmission(true);
2752  if (tWireReturnCode == 0) {
2753  aSerial->print(F("Found"));
2754  } else {
2755  aSerial->print(F("Error code="));
2756  aSerial->print(tWireReturnCode);
2757  aSerial->print(F(". Communication with I2C was successful, but found no"));
2758  tRetValue = true;
2759  }
2760  aSerial->print(F(" I2C device attached at address: 0x"));
2761  aSerial->println(aI2CAddress, HEX);
2762 #endif // defined(USE_SOFT_I2C_MASTER)
2763 
2764  if (tRetValue) {
2765  aSerial->println(F("PCA9685 expander not connected"));
2766  }
2767  return tRetValue;
2768 }
2769 # endif // defined(USE_PCA9685_SERVO_EXPANDER)
2770 
2771 #if defined(LOCAL_DEBUG)
2772 #undef LOCAL_DEBUG
2773 #endif
2774 #if defined(LOCAL_TRACE)
2775 #undef LOCAL_TRACE
2776 #endif
2777 #endif // _SERVO_EASING_HPP
ServoEasing::getDeltaMicrosecondsOrUnits
int getDeltaMicrosecondsOrUnits()
Definition: ServoEasing.hpp:1668
setEaseToForAllServos
bool setEaseToForAllServos()
Sets target position using content of ServoEasingNextPositionArray.
Definition: ServoEasing.hpp:2322
PROGMEM
const char easeTypeLinear[] PROGMEM
Definition: ServoEasing.hpp:165
setEasingTypeForMultipleServos
void setEasingTypeForMultipleServos(uint_fast8_t aNumberOfServos, uint_fast8_t aEasingType)
Sets easing type aEasingType for the first aNumberOfServos in ServoEasingArray[].
Definition: ServoEasing.hpp:2205
ServoEasing::_setTrimMicrosecondsOrUnits
void _setTrimMicrosecondsOrUnits(int aTrimMicrosecondsOrUnits, bool aDoWrite=false)
Definition: ServoEasing.hpp:715
ServoEasing::MicrosecondsOrUnitsToDegree
int MicrosecondsOrUnitsToDegree(int aMicrosecondsOrUnits)
Used to convert e.g.
Definition: ServoEasing.hpp:912
ServoEasing::areInterruptsActive
static bool areInterruptsActive()
The recommended test if at least one servo is moving yet.
Definition: ServoEasing.hpp:1628
updateAllServos
bool updateAllServos()
Definition: ServoEasing.hpp:2417
ServoEasing::getSpeed
uint_fast16_t getSpeed()
Definition: ServoEasing.hpp:683
ServoEasing::mSpeed
uint_fast16_t mSpeed
max speed is 450 degree/sec for SG90 and 540 degree/second for MG90 servos -> see speedTest....
Definition: ServoEasing.h:595
ServoEasing::mServoMoves
volatile bool mServoMoves
Definition: ServoEasing.h:605
ServoEasing::registerUserEaseInFunction
void registerUserEaseInFunction(float(*aUserEaseInFunction)(float aPercentageOfCompletion, void *aUserDataPointer), void *aUserDataPointer=NULL)
Definition: ServoEasing.hpp:745
ServoEasing::update
bool update()
Definition: ServoEasing.hpp:1420
synchronizeAndEaseToArrayPositions
void synchronizeAndEaseToArrayPositions()
Definition: ServoEasing.hpp:2237
ServoEasing::attach
uint8_t attach(int aPin)
Specify the microseconds values for 0 and 180 degree for the servo.
Definition: ServoEasing.hpp:443
ServoEasing
Definition: ServoEasing.h:418
ServoEasing::setReverseOperation
void setReverseOperation(bool aOperateServoReverse)
Definition: ServoEasing.hpp:679
ServoEasing::getEasingType
uint_fast8_t getEasingType()
Definition: ServoEasing.hpp:740
CALL_STYLE_DIRECT
#define CALL_STYLE_DIRECT
Definition: ServoEasing.h:287
ServoEasing::TargetPositionReachedHandler
void(* TargetPositionReachedHandler)(ServoEasing *)
Is called any time when target servo position is reached.
Definition: ServoEasing.h:647
ServoEasing::mStartMicrosecondsOrUnits
int mStartMicrosecondsOrUnits
Only used with millisAtStartMove to compute currentMicrosecondsOrUnits in update()
Definition: ServoEasing.h:588
ServoEasing::getMillisForCompleteMove
int getMillisForCompleteMove()
Definition: ServoEasing.hpp:1672
PART_OF_LINEAR_MOVEMENT
#define PART_OF_LINEAR_MOVEMENT
Definition: ServoEasing.hpp:2574
ServoEasing::setTargetPositionReachedHandler
void setTargetPositionReachedHandler(void(*aTargetPositionReachedHandler)(ServoEasing *))
Definition: ServoEasing.hpp:1378
ServoEasing::mServo0DegreeMicrosecondsOrUnits
int mServo0DegreeMicrosecondsOrUnits
Values contain always microseconds except for servos connected to a PCA9685 expander,...
Definition: ServoEasing.h:644
CALL_STYLE_BOUNCING_OUT_IN
#define CALL_STYLE_BOUNCING_OUT_IN
Definition: ServoEasing.h:291
ServoEasing::mTrimMicrosecondsOrUnits
int mTrimMicrosecondsOrUnits
This value is always added by the function _writeMicrosecondsOrUnits() to the requested degree/units/...
Definition: ServoEasing.h:638
CALL_STYLE_MASK
#define CALL_STYLE_MASK
Definition: ServoEasing.h:293
CALL_STYLE_IN
#define CALL_STYLE_IN
Definition: ServoEasing.h:288
printArrayPositions
void printArrayPositions(Print *aSerial)
Prints content of ServoNextPositionArray for debugging purposes.
Definition: ServoEasing.hpp:2251
OVERSHOOT_AMOUNT_UNITS
#define OVERSHOOT_AMOUNT_UNITS
Definition: ServoEasing.hpp:2579
ServoEasing::mServoPin
uint8_t mServoPin
pin number / port number of PCA9685 [0-15] or NO_SERVO_ATTACHED_PIN_NUMBER - at least required for Li...
Definition: ServoEasing.h:617
ServoEasing::mMinMicrosecondsOrUnits
int mMinMicrosecondsOrUnits
Min value checked at _writeMicrosecondsOrUnits(), before trim and reverse is applied.
Definition: ServoEasing.h:636
writeAllServos
void writeAllServos(int aDegreeOrMicrosecond)
Definition: ServoEasing.hpp:2278
ServoEasing::setEaseTo
bool setEaseTo(int aTargetDegreeOrMicrosecond)
Definition: ServoEasing.hpp:1087
EASE_CUBIC_IN
#define EASE_CUBIC_IN
Definition: ServoEasing.h:306
EASE_FUNCTION_DEGREE_THRESHOLD
#define EASE_FUNCTION_DEGREE_THRESHOLD
Definition: ServoEasing.h:280
ServoEasing::getEndMicrosecondsOrUnits
int getEndMicrosecondsOrUnits()
Definition: ServoEasing.hpp:1657
PCA9685_FIRST_PWM_REGISTER
#define PCA9685_FIRST_PWM_REGISTER
Definition: ServoEasing.h:398
ServoEasing::resumeWithInterrupts
void resumeWithInterrupts()
Definition: ServoEasing.hpp:1363
DO_NOT_START_UPDATE_BY_INTERRUPT
#define DO_NOT_START_UPDATE_BY_INTERRUPT
Definition: ServoEasing.h:409
ServoEasing::stop
void stop()
This stops the servo at any position.
Definition: ServoEasing.hpp:1346
ServoEasing::attachWithTrim
uint8_t attachWithTrim(int aPin, int aTrimDegreeOrMicrosecond, int aInitialDegreeOrMicrosecond)
Combination of attach with initial setTrim() and write().
Definition: ServoEasing.hpp:462
ServoEasing::noMovement
bool noMovement(uint_fast16_t aMillisToWait)
stay at the position for aMillisToWait Used as delay for callback
Definition: ServoEasing.hpp:1223
checkI2CConnection
bool checkI2CConnection(uint8_t aI2CAddress, Stream *aSerial)
ServoEasing::isMoving
bool isMoving()
Test if servo is moving yet.
Definition: ServoEasing.hpp:1615
ServoEasing::mMaxMicrosecondsOrUnits
int mMaxMicrosecondsOrUnits
Max value checked at _writeMicrosecondsOrUnits(), before trim and reverse is applied.
Definition: ServoEasing.h:635
DEFAULT_PCA9685_UNITS_FOR_180_DEGREE
#define DEFAULT_PCA9685_UNITS_FOR_180_DEGREE
Definition: ServoEasing.h:219
setEaseToForAllServosSynchronizeAndWaitForAllServosToStop
void setEaseToForAllServosSynchronizeAndWaitForAllServosToStop()
Synchronize and blocking wait until all servos are stopped.
Definition: ServoEasing.hpp:2227
EASE_FUNCTION_DEGREE_INDICATOR_OFFSET
#define EASE_FUNCTION_DEGREE_INDICATOR_OFFSET
Definition: ServoEasing.h:279
ServoEasing::setUserDataPointer
void setUserDataPointer(void *aUserDataPointer)
Definition: ServoEasing.hpp:750
EASE_BOUNCE_OUT
#define EASE_BOUNCE_OUT
Definition: ServoEasing.h:360
pauseAllServos
void pauseAllServos()
Definition: ServoEasing.hpp:2386
ServoEasing::pause
void pause()
Definition: ServoEasing.hpp:1356
ServoEasing::mServoIsPaused
bool mServoIsPaused
Definition: ServoEasing.h:624
EASE_LINEAR
#define EASE_LINEAR
Definition: ServoEasing.h:296
ServoEasing::InitializeAndCheckI2CConnection
bool InitializeAndCheckI2CConnection(Stream *aSerial)
stopAllServos
void stopAllServos()
Definition: ServoEasing.hpp:2375
ServoEasing::BackEaseIn
static float BackEaseIn(float aPercentageOfCompletion)
see: https://easings.net/#easeInOutBack and https://github.com/warrenm/AHEasing/blob/master/AHEasing/...
Definition: ServoEasing.hpp:2561
ServoEasing::mEndMicrosecondsOrUnits
int mEndMicrosecondsOrUnits
Only used once as last value if movement was finished to provide exact end position.
Definition: ServoEasing.h:589
synchronizeAllServosStartAndWaitForAllServosToStop
void synchronizeAllServosStartAndWaitForAllServosToStop()
Synchronize and blocking wait until all servos are stopped.
Definition: ServoEasing.hpp:2466
ServoEasing::DegreeToMicrosecondsOrUnitsWithTrimAndReverse
int DegreeToMicrosecondsOrUnitsWithTrimAndReverse(int aDegree)
Mainly for testing, since trim and reverse are applied at each write.
Definition: ServoEasing.hpp:1018
clipDegreeSpecial
int clipDegreeSpecial(uint_fast8_t aDegreeToClip)
Clips the unsigned degree value and handles unsigned underflow.
Definition: ServoEasing.hpp:1821
PCA9685_MODE_1_AUTOINCREMENT
#define PCA9685_MODE_1_AUTOINCREMENT
Definition: ServoEasing.h:396
PCA9685_PRESCALER_FOR_20_MS
#define PCA9685_PRESCALER_FOR_20_MS
Definition: ServoEasing.h:405
ServoEasing::setMinConstraint
void setMinConstraint(int aMinDegreeOrMicrosecond)
Definition: ServoEasing.hpp:726
EASE_USER_DIRECT
#define EASE_USER_DIRECT
Definition: ServoEasing.h:320
CALL_STYLE_IN_OUT
#define CALL_STYLE_IN_OUT
Definition: ServoEasing.h:290
ServoEasing::LinearWithQuadraticBounce
float LinearWithQuadraticBounce(float aPercentageOfCompletion)
PRECISION (LinearWithQuadraticBounce) is like linear, but adds a 5 degree bounce in the last 20 % of ...
Definition: ServoEasing.hpp:2588
isOneServoMoving
bool isOneServoMoving()
Definition: ServoEasing.hpp:2366
CALL_STYLE_OUT
#define CALL_STYLE_OUT
Definition: ServoEasing.h:289
ServoEasing::setTrim
void setTrim(int aTrimDegreeOrMicrosecond, bool aDoWrite=false)
Definition: ServoEasing.hpp:697
ServoEasing::printDynamic
void printDynamic(Print *aSerial, bool doExtendedOutput=true)
Prints values which may change from move to move.
Definition: ServoEasing.hpp:1714
DEFAULT_PULSE_WIDTH
#define DEFAULT_PULSE_WIDTH
Definition: ServoEasing.h:162
ServoEasing::setMinMaxConstraint
void setMinMaxConstraint(int aMinDegreeOrMicrosecond, int aMaxDegreeOrMicrosecond)
Definition: ServoEasing.hpp:729
ServoEasing::mEasingType
uint8_t mEasingType
Definition: ServoEasing.h:598
ServoEasing::MicrosecondsOrUnitsToMicroseconds
int MicrosecondsOrUnitsToMicroseconds(int aMicrosecondsOrUnits)
Definition: ServoEasing.hpp:949
ServoEasing::mMillisForCompleteMove
uint_fast16_t mMillisForCompleteMove
Definition: ServoEasing.h:622
ServoEasing::EaseOutBounce
static float EaseOutBounce(float aPercentageOfCompletion)
!!! ATTENTION !!! we have only the out function implemented see: https://easings.net/de#easeOutBounce...
Definition: ServoEasing.hpp:2664
EASE_BACK_IN
#define EASE_BACK_IN
Definition: ServoEasing.h:344
delayAndUpdateAndWaitForAllServosToStop
bool delayAndUpdateAndWaitForAllServosToStop(unsigned long aMillisDelay, bool aTerminateDelayIfAllServosStopped)
Definition: ServoEasing.hpp:2446
ServoEasing::resumeWithoutInterrupts
void resumeWithoutInterrupts()
Definition: ServoEasing.hpp:1371
ServoEasing::print
void print(Print *aSerial, bool doExtendedOutput=true)
Do a printDynamic() and a printStatic()
Definition: ServoEasing.hpp:1681
ServoEasing::mMillisAtStartMove
uint32_t mMillisAtStartMove
Definition: ServoEasing.h:621
ServoEasing::easeToD
void easeToD(int aTargetDegreeOrMicrosecond, uint_fast16_t aMillisForMove)
Definition: ServoEasing.hpp:1065
ServoEasing::mUserEaseInFunction
float(* mUserEaseInFunction)(float aPercentageOfCompletion, void *aUserDataPointer)
Definition: ServoEasing.h:601
ServoEasing::getCurrentAngle
int getCurrentAngle()
Definition: ServoEasing.hpp:1649
ServoEasing::MicrosecondsToDegree
int MicrosecondsToDegree(int aMicroseconds)
Only used in startEaseTo to compute target degree For PCA9685, we have stored units in mServo0DegreeM...
Definition: ServoEasing.hpp:892
REFRESH_INTERVAL_MICROS
#define REFRESH_INTERVAL_MICROS
Definition: ServoEasing.h:170
DEFAULT_MICROSECONDS_FOR_0_DEGREE
#define DEFAULT_MICROSECONDS_FOR_0_DEGREE
Definition: ServoEasing.h:208
ServoEasing::printStatic
void printStatic(Print *aSerial)
Prints values which normally does NOT change from move to move.
Definition: ServoEasing.hpp:1773
EASE_QUADRATIC_IN
#define EASE_QUADRATIC_IN
Definition: ServoEasing.h:299
ServoEasing::setMaxConstraint
void setMaxConstraint(int aMaxDegreeOrMicrosecond)
Definition: ServoEasing.hpp:723
ServoEasing::callEasingFunction
float callEasingFunction(float aPercentageOfCompletion)
Definition: ServoEasing.hpp:1554
PCA9685_SOFTWARE_RESET
#define PCA9685_SOFTWARE_RESET
Definition: ServoEasing.h:391
resumeWithoutInterruptsAllServos
void resumeWithoutInterruptsAllServos()
Definition: ServoEasing.hpp:2406
ServoEasing::startEaseToD
bool startEaseToD(int aTargetDegreeOrMicrosecond, uint_fast16_t aMillisForMove, bool aStartUpdateByInterrupt=START_UPDATE_BY_INTERRUPT)
Sets up all the values required for a smooth move to new value Lower level function with time instead...
Definition: ServoEasing.hpp:1232
ServoEasing::CircularEaseIn
static float CircularEaseIn(float aPercentageOfCompletion)
It is very fast in the middle! see: https://easings.net/#easeInOutCirc and https://github....
Definition: ServoEasing.hpp:2553
handleServoTimerInterrupt
void handleServoTimerInterrupt()
Update all servos from list and check if all servos have stopped.
Definition: ServoEasing.hpp:1841
PCA9685_MODE1_REGISTER
#define PCA9685_MODE1_REGISTER
Definition: ServoEasing.h:394
ServoEasing::detach
void detach()
Mark a detached servo in the array by setting the object pointer to NULL The next attach() then uses ...
Definition: ServoEasing.hpp:638
ServoEasing::mMillisAtStopMove
uint32_t mMillisAtStopMove
Definition: ServoEasing.h:625
EASE_TYPE_MASK
#define EASE_TYPE_MASK
Definition: ServoEasing.h:294
PCA9685_GENERAL_CALL_ADDRESS
#define PCA9685_GENERAL_CALL_ADDRESS
Definition: ServoEasing.h:390
ServoEasing::startEaseTo
bool startEaseTo(int aTargetDegreeOrMicrosecond)
Starts interrupt for update()
Definition: ServoEasing.hpp:1110
EASE_ELASTIC_IN
#define EASE_ELASTIC_IN
Definition: ServoEasing.h:351
THRESHOLD_VALUE_FOR_INTERPRETING_VALUE_AS_MICROSECONDS
#define THRESHOLD_VALUE_FOR_INTERPRETING_VALUE_AS_MICROSECONDS
Definition: ServoEasing.h:198
setSpeedForAllServos
void setSpeedForAllServos(uint_fast16_t aDegreesPerSecond)
Definition: ServoEasing.hpp:2286
LightweightServo.hpp
ServoEasing::getEndMicrosecondsOrUnitsWithTrim
int getEndMicrosecondsOrUnitsWithTrim()
Not used internally.
Definition: ServoEasing.hpp:1664
ServoEasing::ServoEasing
ServoEasing()
Definition: ServoEasing.hpp:395
ServoEasing::_writeMicrosecondsOrUnits
void _writeMicrosecondsOrUnits(int aTargetDegreeOrMicrosecond)
Internal function Before sending the value to the underlying Servo library, trim and reverse is appli...
Definition: ServoEasing.hpp:801
ServoEasing::ServoEasingNextPositionArray
static float ServoEasingNextPositionArray[MAX_EASING_SERVOS]
Used exclusively for *ForAllServos() functions.
Definition: ServoEasing.h:664
ServoEasing::getCurrentMicroseconds
int getCurrentMicroseconds()
Definition: ServoEasing.hpp:1653
REFRESH_FREQUENCY
#define REFRESH_FREQUENCY
Definition: ServoEasing.h:172
ServoEasing::sInterruptsAreActive
static volatile bool sInterruptsAreActive
It is required for ESP32, where the timer interrupt routine does not block the loop.
Definition: ServoEasing.h:655
EASE_QUARTIC_IN
#define EASE_QUARTIC_IN
Definition: ServoEasing.h:313
ServoEasing::mServo180DegreeMicrosecondsOrUnits
int mServo180DegreeMicrosecondsOrUnits
Definition: ServoEasing.h:645
PART_OF_BOUNCE_MOVEMENT_HALF
#define PART_OF_BOUNCE_MOVEMENT_HALF
Definition: ServoEasing.hpp:2576
START_EASE_TO_SPEED
#define START_EASE_TO_SPEED
Definition: ServoEasing.h:45
ServoEasing::printEasingType
static void printEasingType(Print *aSerial, uint_fast8_t aEasingType)
Definition: ServoEasing.hpp:1690
DEFAULT_PCA9685_UNITS_FOR_90_DEGREE
#define DEFAULT_PCA9685_UNITS_FOR_90_DEGREE
Definition: ServoEasing.h:217
ServoEasing::ServoEasingArray
static ServoEasing * ServoEasingArray[MAX_EASING_SERVOS]
Definition: ServoEasing.h:663
EASE_DUMMY_MOVE
#define EASE_DUMMY_MOVE
Definition: ServoEasing.h:327
MAX_EASING_SERVOS
#define MAX_EASING_SERVOS
Definition: ServoEasing.h:157
ServoEasing::DegreeOrMicrosecondToMicrosecondsOrUnits
int DegreeOrMicrosecondToMicrosecondsOrUnits(int aDegreeOrMicrosecond)
We have around 10 us per degree Used to convert (external) provided degree values to internal microse...
Definition: ServoEasing.hpp:963
INVALID_SERVO
#define INVALID_SERVO
Definition: ServoEasing.h:168
ServoEasing::QuadraticEaseIn
static float QuadraticEaseIn(float aPercentageOfCompletion)
The simplest non linear easing function.
Definition: ServoEasing.hpp:2528
setEasingTypeForAllServos
void setEasingTypeForAllServos(uint_fast8_t aEasingType)
60 us for single servo + 160 us per servo if using I2C e.g.for PCA9685 expander at 400 kHz or + 100 a...
Definition: ServoEasing.hpp:2198
ServoEasing::UserDataPointer
void * UserDataPointer
Definition: ServoEasing.h:600
updateAndWaitForAllServosToStop
void updateAndWaitForAllServosToStop()
Blocking wait until all servos are stopped.
Definition: ServoEasing.hpp:2434
synchronizeAllServosAndStartInterrupt
void synchronizeAllServosAndStartInterrupt(bool aStartUpdateByInterrupt)
Take the longer duration in order to move all servos synchronously.
Definition: ServoEasing.hpp:2474
START_UPDATE_BY_INTERRUPT
#define START_UPDATE_BY_INTERRUPT
Definition: ServoEasing.h:408
ServoEasing::SineEaseIn
static float SineEaseIn(float aPercentageOfCompletion)
Take half of negative cosines of first quadrant Is behaves almost like QUADRATIC.
Definition: ServoEasing.hpp:2544
setEaseToForAllServosSynchronizeAndStartInterrupt
void setEaseToForAllServosSynchronizeAndStartInterrupt()
Definition: ServoEasing.hpp:2214
ServoEasing::mOperateServoReverse
bool mOperateServoReverse
Reverse means, that values for 180 and 0 degrees are swapped by: aValue = mServo180DegreeMicroseconds...
Definition: ServoEasing.h:633
EASE_CIRCULAR_IN
#define EASE_CIRCULAR_IN
Definition: ServoEasing.h:337
PCA9685_MODE_1_SLEEP
#define PCA9685_MODE_1_SLEEP
Definition: ServoEasing.h:397
disableServoEasingInterrupt
void disableServoEasingInterrupt()
Definition: ServoEasing.hpp:2072
ServoEasing::sServoArrayMaxIndex
static uint_fast8_t sServoArrayMaxIndex
Two arrays of all servos to enable synchronized movings Servos are inserted in the order,...
Definition: ServoEasing.h:662
ServoEasing.h
DEFAULT_MICROSECONDS_FOR_180_DEGREE
#define DEFAULT_MICROSECONDS_FOR_180_DEGREE
Definition: ServoEasing.h:212
OVERSHOOT_AMOUNT_MILLIS
#define OVERSHOOT_AMOUNT_MILLIS
Definition: ServoEasing.hpp:2578
ServoEasing::mDeltaMicrosecondsOrUnits
int mDeltaMicrosecondsOrUnits
end - start
Definition: ServoEasing.h:590
ServoEasing::setEaseToD
bool setEaseToD(int aTargetDegreeOrMicrosecond, uint_fast16_t aDegreesPerSecond)
Sets easing parameter, but does not start.
Definition: ServoEasing.hpp:1211
ServoEasing::mServoIndex
uint8_t mServoIndex
Index in sServoArray or INVALID_SERVO if error while attach() or if detached.
Definition: ServoEasing.h:619
ServoEasing::mCurrentMicrosecondsOrUnits
volatile int mCurrentMicrosecondsOrUnits
Internally only microseconds (or units (= 4.88 us) if using PCA9685 expander) and not degree are used...
Definition: ServoEasing.h:587
ServoEasing::setSpeed
void setSpeed(uint_fast16_t aDegreesPerSecond)
Definition: ServoEasing.hpp:687
ServoEasing::isMovingAndCallYield
bool isMovingAndCallYield() __attribute__((deprecated("Replaced by isMoving(). Often better to use areInterruptsActive() instead.")))
Call yield here (actually only for ESP8266), so the user do not need to care for it in long running l...
Definition: ServoEasing.hpp:1645
EASE_PRECISION_IN
#define EASE_PRECISION_IN
Definition: ServoEasing.h:364
ServoEasing::write
void write(int aTargetDegreeOrMicrosecond)
Definition: ServoEasing.hpp:759
setEaseToDForAllServos
bool setEaseToDForAllServos(uint_fast16_t aMillisForMove)
Sets target position using content of ServoEasingNextPositionArray and use aMillisForMove instead of ...
Definition: ServoEasing.hpp:2355
enableServoEasingInterrupt
void enableServoEasingInterrupt()
Timer1 is used for the Arduino Servo library.
Definition: ServoEasing.hpp:1866
resumeWithInterruptsAllServos
void resumeWithInterruptsAllServos()
Definition: ServoEasing.hpp:2398
EASE_SINE_IN
#define EASE_SINE_IN
Definition: ServoEasing.h:330
ServoEasing::CubicEaseIn
static float CubicEaseIn(float aPercentageOfCompletion)
Definition: ServoEasing.hpp:2532
ServoEasing::easeTo
void easeTo(int aTargetDegreeOrMicrosecond)
Definition: ServoEasing.hpp:1028
EASE_FUNCTION_MICROSECONDS_INDICATOR_OFFSET
#define EASE_FUNCTION_MICROSECONDS_INDICATOR_OFFSET
Definition: ServoEasing.h:281
ServoEasing::ElasticEaseIn
static float ElasticEaseIn(float aPercentageOfCompletion)
see: https://easings.net/#easeInOutElastic and https://github.com/warrenm/AHEasing/blob/master/AHEasi...
Definition: ServoEasing.hpp:2570
ServoEasing::QuarticEaseIn
static float QuarticEaseIn(float aPercentageOfCompletion)
Definition: ServoEasing.hpp:2536
REFRESH_INTERVAL_MILLIS
#define REFRESH_INTERVAL_MILLIS
Definition: ServoEasing.h:171
MILLIS_IN_ONE_SECOND
#define MILLIS_IN_ONE_SECOND
Definition: ServoEasing.h:40
PCA9685_PRESCALE_REGISTER
#define PCA9685_PRESCALE_REGISTER
Definition: ServoEasing.h:399
ServoEasing::setEasingType
void setEasingType(uint_fast8_t aEasingType)
Definition: ServoEasing.hpp:736