diff --git a/.github/workflows/LibraryBuild.yml b/.github/workflows/LibraryBuild.yml index 6d3b224..6b2e678 100644 --- a/.github/workflows/LibraryBuild.yml +++ b/.github/workflows/LibraryBuild.yml @@ -70,6 +70,7 @@ jobs: ############################################################################################################# include: - arduino-boards-fqbn: arduino:avr:uno + required-libraries: NeoPatterns,PlayRtttl # for QuadrupedControl: -DQUADRUPED_1_WITH_DVD_REMOTE build-properties: # the flags were put in compiler.cpp.extra_flags OneServo: -DUSE_PCA9685_SERVO_EXPANDER -DDISABLE_PAUSE_RESUME SymmetricEasing: -DDISABLE_COMPLEX_FUNCTIONS -DDEBUG @@ -77,7 +78,7 @@ jobs: Simple: -DPRINT_FOR_SERIAL_PLOTTER PCA9685_ExpanderFor32Servos: -DTRACE -DDISABLE_MICROS_AS_DEGREE_PARAMETER PCA9685_ExpanderAndServo: -DUSE_SOFT_I2C_MASTER - QuadrupedControl: -DQUADRUPED_1_WITH_DVD_REMOTE + QuadrupedControl: -DQUADRUPED_1_WITH_DVD_REMOTE -mrelax - arduino-boards-fqbn: arduino:avr:leonardo sketches-exclude: TwoServos,RobotArmControl # No LightweightServoLib and TinyReceiver available @@ -146,7 +147,7 @@ jobs: # First of all, we clone the repo using the `checkout` action. - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@master - name: Arduino Lint uses: arduino/arduino-lint-action@v1 diff --git a/README.md b/README.md index f26d05e..0476fb1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # [ServoEasing](https://github.com/ArminJo/ServoEasing) - move your servo more natural A library for smooth servo movements. It uses the standard Arduino Servo library and therefore has its restrictions regarding pins and platform support. -### [Version 3.0.1](https://github.com/ArminJo/ServoEasing/archive/master.zip) - work in progress +### [Version 3.1.1](https://github.com/ArminJo/ServoEasing/archive/master.zip) - work in progress [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![Installation instructions](https://www.ardu-badge.com/badge/ServoEasing.svg?)](https://www.ardu-badge.com/ServoEasing) @@ -325,11 +325,12 @@ This will print internal information visible in the Arduino *Serial Monitor* whi If you see errors like this `Simple.ino:57: undefined reference to ServoEasing::attach(int, int)`, you included *ServoEasing.h* instead of *ServoEasing.hpp*. # Revision History -### Version 3.0.1 +### Version 3.1.0 - SAMD51 support by Lutz Aumüller. - Added support to pause and resume and `DISABLE_PAUSE_RESUME`. - Fixed some bugs for PCA9685 expander introduced in 3.0.0. - Feather Huzzah support with the help of Danner Claflin. +- Added `ENABLE_EXTERNAL_SERVO_TIMER_HANDLER` macro. ### Version 3.0.0 - Added target reached callback functionality, to enable multiple movements without loop control. diff --git a/examples/QuadrupedControl/QuadrupedConfiguration.h b/examples/QuadrupedControl/QuadrupedConfiguration.h index 8a97f17..d9b312d 100644 --- a/examples/QuadrupedControl/QuadrupedConfiguration.h +++ b/examples/QuadrupedControl/QuadrupedConfiguration.h @@ -90,6 +90,8 @@ extern ServoEasing USServo; #endif #if defined(QUADRUPED_HAS_NEOPIXEL) +// Must be defined here, since we still have *.cpp sources +#define DO_NOT_SUPPORT_BRIGHTNESS #define DO_NOT_SUPPORT_RGBW // saves up to 428 bytes additional program memory for the AllPatternsOnMultiDevices() example. // patterns always used if Neopixel are enabled diff --git a/examples/QuadrupedControl/QuadrupedControl.ino b/examples/QuadrupedControl/QuadrupedControl.ino index 955405d..de52903 100644 --- a/examples/QuadrupedControl/QuadrupedControl.ino +++ b/examples/QuadrupedControl/QuadrupedControl.ino @@ -63,11 +63,15 @@ #define QUADRUPED_MOVEMENT_BREAK_FLAG (doShutDown) #endif +#define USE_NO_RTX_EXTENSIONS // Disables RTX format definitions `'s'` (style) and `'l'` (loop). Saves up to 332 bytes program memory #define ENABLE_EXTERNAL_SERVO_TIMER_HANDLER // Evaluated by ServoEasing.hpp #include "QuadrupedControlCommands.hpp" // In turn includes ServoEasing. Commands can also be used e.g. in loop(). #if defined(QUADRUPED_HAS_NEOPIXEL) #include "QuadrupedNeoPixel.hpp" #endif +#if defined(QUADRUPED_ENABLE_RTTTL) +#include +#endif #include "ADCUtils.hpp" // for getVCCVoltageMillivoltSimple() and printVCCVoltageMillivolt() @@ -75,9 +79,6 @@ ServoEasing USServo; #endif -#if defined(QUADRUPED_ENABLE_RTTTL) -#include -#endif //#define INFO // activate this to see serial info output diff --git a/examples/QuadrupedControl/QuadrupedNeoPixel.h b/examples/QuadrupedControl/QuadrupedNeoPixel.h new file mode 100644 index 0000000..b9d3b6a --- /dev/null +++ b/examples/QuadrupedControl/QuadrupedNeoPixel.h @@ -0,0 +1,57 @@ +/* + * QuadrupedNeoPixel.h + * + * Created on: 18.09.2019 + * Author: Armin + */ + +#ifndef _QUADRUPED_NEOPIXEL_H +#define _QUADRUPED_NEOPIXEL_H + +#if defined(QUADRUPED_HAS_NEOPIXEL) +#include + +#define PIN_NEOPIXEL 4 +// How many NeoPixels are mounted? +#define NUM_PIXELS 24 + +#define PIXELS_ON_ONE_BAR 8 +#define PIXEL_OFFSET_RIGHT_BAR 0 +#define PIXEL_OFFSET_FRONT_BAR PIXELS_ON_ONE_BAR +#define PIXEL_OFFSET_LEFT_BAR (2*PIXELS_ON_ONE_BAR) + +void initNeoPatterns(); + +#if defined(HAS_ADDITIONAL_REMOTE_COMMANDS) +void doPattern1(); +void doPattern2(); +void doPatternStripes(); +void doPatternHeartbeat(); +void doPatternFire(); +#endif + +void doWipeOutPatterns(); + +bool isAtLeastOnePatternActive(); + +void showPatternSynchronizedWithServos(); + +void handleAutomaticMovementPattern(); +void handleQuadrupedNeoPixelUpdate(); +void handleServoTimerInterrupt(); + +uint16_t getDelayFromSpeed(); + +extern NeoPatterns QuadrupedNeoPixelBar; // The main 24 pixel bar containing all the other 3 logical NeoPatterns objects. +extern NeoPatterns RightNeoPixelBar; // 8 Pixel bar at the right +extern NeoPatterns FrontNeoPixelBar; +extern NeoPatterns LeftNeoPixelBar; +extern color32_t sBarBackgroundColorArrayForDistance[]; // // The color background for front distance bar + +extern bool sAtLeastOnePatternsIsActive; // True if at least one pattern is active => call update() +extern bool sCleanPatternAfterEnd; // Do a wipe out after pattern ended +extern bool sShowPatternSynchronizedWithServos; // Flag set e.g. by main loop to force to show the pattern (e.g.distance value) synchronized with servo interrupts. + +#endif // #if defined(QUADRUPED_HAS_NEOPIXEL) + +#endif // _QUADRUPED_NEOPIXEL_H diff --git a/examples/QuadrupedControl/QuadrupedNeoPixel.hpp b/examples/QuadrupedControl/QuadrupedNeoPixel.hpp new file mode 100644 index 0000000..c988c51 --- /dev/null +++ b/examples/QuadrupedControl/QuadrupedNeoPixel.hpp @@ -0,0 +1,329 @@ +/* + * QuadrupedNeoPixel.hpp + * + * This file mainly contains the control of the attached 3 NeoPixel 8 pixel bars. + * These 3 bars are chained, in order to use only one pin, and are electrically one 24 pixel bar. + * + * The NeopPixel updates are synchronized with the ServoEasing updates by overwriting the ServoEasing + * function handleServoTimerInterrupt() with a function, which calls handleQuadrupedNeoPixelUpdate() + * in order not to disturb the servo pulse generation. + * + * New automatic movement patterns are triggered by (sLastNPEvaluatedAction != sCurrentlyRunningAction) + * which is evaluated in handleQuadrupedNeoPixelUpdate() in ISR context. + * + * Copyright (C) 2019-2022 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of QuadrupedControl https://github.com/ArminJo/QuadrupedControl. + * + * QuadrupedControl is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _QUADRUPED_NEOPIXEL_HPP +#define _QUADRUPED_NEOPIXEL_HPP + +#include + +#if defined(QUADRUPED_HAS_NEOPIXEL) + +#include "QuadrupedNeoPixel.h" +#include "QuadrupedControlCommands.h" +#include + +#if defined(QUADRUPED_HAS_IR_CONTROL) +#include "IRCommandDispatcher.h" +#include "TinyIRReceiver.h" // for isTinyReceiverIdle() +#endif + +#if defined(QUADRUPED_ENABLE_RTTTL) +#define SUPPRESS_HPP_WARNING +#include +#endif + +#include "QuadrupedServoControl.h" +#include "QuadrupedBasicMovements.h" // for sMovingDirection + +//#define INFO // activate this to see serial info output + +void QuadrupedOnPatternCompleteHandler(NeoPatterns *aLedsPtr); +bool sAtLeastOnePatternsIsActive; // True if at least one pattern is active => call update() +bool sCleanPatternAfterEnd; // Do a wipe out after pattern ended +bool sShowPatternSynchronizedWithServos; // Flag set e.g. by main loop to force to show the pattern (e.g.distance value) synchronized with servo interrupts. +uint8_t sLastNPEvaluatedAction; // do determine changes of sCurrentlyRunningAction + +NeoPatterns QuadrupedNeoPixelBar = NeoPatterns(NUM_PIXELS, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800, &QuadrupedOnPatternCompleteHandler, + true); +// false -> do not allow show on partial NeoPixel bar +NeoPatterns RightNeoPixelBar = NeoPatterns(&QuadrupedNeoPixelBar, PIXEL_OFFSET_RIGHT_BAR, PIXELS_ON_ONE_BAR, false, + &QuadrupedOnPatternCompleteHandler, true); +NeoPatterns FrontNeoPixelBar = NeoPatterns(&QuadrupedNeoPixelBar, PIXEL_OFFSET_FRONT_BAR, PIXELS_ON_ONE_BAR, false, + &QuadrupedOnPatternCompleteHandler, true); +NeoPatterns LeftNeoPixelBar = NeoPatterns(&QuadrupedNeoPixelBar, PIXEL_OFFSET_LEFT_BAR, PIXELS_ON_ONE_BAR, false, + &QuadrupedOnPatternCompleteHandler, true); + +// The color background for front distance bar +color32_t sBarBackgroundColorArrayForDistance[PIXELS_ON_ONE_BAR] = { COLOR32_RED_QUARTER, COLOR32_RED_QUARTER, COLOR32_RED_QUARTER, +COLOR32_YELLOW, COLOR32_YELLOW, COLOR32_GREEN_QUARTER, COLOR32_GREEN_QUARTER, COLOR32_GREEN_QUARTER }; + + +/* + * @brief This function checks all patterns for update and calls show() of the underlying 24 pixel bar if required. + * It is called in ISR context by handleServoTimerInterrupt() since the show() function blocks interrupts + * and must therefore be synchronized with the servo pulse generation. + * @return - true if at least one pattern is active. + */ +void handleQuadrupedNeoPixelUpdate() { + +#if defined(QUADRUPED_HAS_IR_CONTROL) + if (isTinyReceiverIdle()) { +#endif + + /* + * Update patterns if active + */ + if (sAtLeastOnePatternsIsActive) { + sAtLeastOnePatternsIsActive = QuadrupedNeoPixelBar.updateAndShowAlsoAllPartialPatterns(); + } + + /* + * Check for patterns start or update. + */ + if (sLastNPEvaluatedAction != sCurrentlyRunningAction) { +#if defined(DEBUG) + Serial.print(F("NP last action=")); + Serial.print(sLastNPEvaluatedAction); + Serial.print(' '); +#endif + sLastNPEvaluatedAction = sCurrentlyRunningAction; + handleAutomaticMovementPattern(); // To trigger NeoPatterns generation + } + + /* + * Check if main loop requires pattern display + */ + if (sShowPatternSynchronizedWithServos) { + sShowPatternSynchronizedWithServos = false; + QuadrupedNeoPixelBar.show(); + } +#if defined(QUADRUPED_HAS_IR_CONTROL) + } +#endif +} + + +/* + * Must be called if one pattern has ended or movement has changed + */ +void handleAutomaticMovementPattern() { +#if defined(INFO) + Serial.print(F("NP current action=")); + Serial.print(sCurrentlyRunningAction); + Serial.print('|'); +#endif + if (sCurrentlyRunningAction == ACTION_TYPE_STOP) { +#if defined(INFO) + Serial.println(F("Stop")); + QuadrupedNeoPixelBar.stop(); // stop background pattern +#endif + } else { + /* + * Action ongoing. Start or restart pattern according to sCurrentlyRunningAction and other parameter. + */ + sAtLeastOnePatternsIsActive = true; + switch (sCurrentlyRunningAction) { + case ACTION_TYPE_CREEP: +#if defined(INFO) + Serial.print(F("Creep")); +#endif + RightNeoPixelBar.ColorWipe(Adafruit_NeoPixel::Color(0, NeoPixel::gamma5(sBodyHeight), 0), getDelayFromSpeed(), 0, + sMovingDirection); + LeftNeoPixelBar.ColorWipe(Adafruit_NeoPixel::Color(NeoPixel::gamma5(sBodyHeight), 0, 0), getDelayFromSpeed(), 0, + (sMovingDirection + MOVE_DIRECTION_BACKWARD) & MOVE_DIRECTION_MASK); + break; + + case ACTION_TYPE_TURN: +#if defined(INFO) + Serial.print(F("Turn")); +#endif + QuadrupedNeoPixelBar.Stripes(COLOR32_RED_HALF, 2, COLOR32_GREEN_HALF, 2, 100, getDelayFromSpeed(), sMovingDirection); + break; + case ACTION_TYPE_TWIST: +#if defined(INFO) + Serial.print(F("Twist")); +#endif + QuadrupedNeoPixelBar.Stripes(COLOR32_RED_HALF, 2, COLOR32_GREEN_HALF, 2, 30, getDelayFromSpeed(), sMovingDirection); + break; + + case ACTION_TYPE_TROT: +#if defined(INFO) + Serial.print(F("Trot")); +#endif + RightNeoPixelBar.ScannerExtended(Adafruit_NeoPixel::Color(0, NeoPixel::gamma5(sBodyHeight), 0), 3, getDelayFromSpeed(), + 0, FLAG_SCANNER_EXT_ROCKET, sMovingDirection); + LeftNeoPixelBar.ScannerExtended(Adafruit_NeoPixel::Color(NeoPixel::gamma5(sBodyHeight), 0, 0), 3, getDelayFromSpeed(), + 0, FLAG_SCANNER_EXT_ROCKET, (sMovingDirection + MOVE_DIRECTION_BACKWARD) & MOVE_DIRECTION_MASK); + break; + + case ACTION_TYPE_ATTENTION: +#if defined(INFO) + Serial.print(F("Attention")); +#endif + RightNeoPixelBar.Flash(COLOR32_GREEN_HALF, 200, COLOR32_BLACK,200, 5); + FrontNeoPixelBar.Flash(COLOR32_BLUE_HALF, 200, COLOR32_BLACK,200, 5); + LeftNeoPixelBar.Flash(COLOR32_RED_HALF, 200, COLOR32_BLACK,200, 5); + break; + + case ACTION_TYPE_PAUSE: +#if defined(INFO) + Serial.print(F("Pause")); +#endif + RightNeoPixelBar.stop(); + FrontNeoPixelBar.stop(); + LeftNeoPixelBar.stop(); + QuadrupedNeoPixelBar.Heartbeat(COLOR32_BLUE_QUARTER, getDelayFromSpeed() / 2, 2, FLAG_DO_NOT_CLEAR); + break; + + default: + sAtLeastOnePatternsIsActive = false; +#if defined(INFO) + Serial.print(F("Not yet implemented")); +#endif + break; + } +#if defined(INFO) + Serial.print(F(" -> ")); +#endif + if(RightNeoPixelBar.ActivePattern != PATTERN_NONE) { + QuadrupedNeoPixelBar.stop(); // stop background pattern, since we have detail patterns + LeftNeoPixelBar.printlnPattern(); + } else { + QuadrupedNeoPixelBar.printlnPattern(); + } + } +} + +/* + * The completion callback for each pattern + */ +void QuadrupedOnPatternCompleteHandler(NeoPatterns *aLedsPtr) { +#if defined(DEBUG) + Serial.print(F("Offset=")); + Serial.print(aLedsPtr->PixelOffset); + Serial.print(F(" Pattern \"")); + aLedsPtr->printPatternName(aLedsPtr->ActivePattern, &Serial); + Serial.println(F("\" finished")); +#endif + // Reset ActivePattern if no new one will be started. + aLedsPtr->ActivePattern = PATTERN_NONE; + + handleAutomaticMovementPattern(); + /* + * Check for cleanup finished pattern + */ + if (sCleanPatternAfterEnd && aLedsPtr->ActivePattern == PATTERN_NONE) { + sCleanPatternAfterEnd = false; + Serial.println(F("Do wipe out")); + doWipeOutPatterns(); + } +} + +/* + * @ return 133 for 90 degree per second, which is start speed + */ +uint16_t getDelayFromSpeed() { + uint16_t tDelay = 12000 / sQuadrupedServoSpeed; +#if defined(DEBUG) + Serial.print(F("Speed=")); + Serial.print(sQuadrupedServoSpeed); + Serial.print(F(" Delay=")); + Serial.println(tDelay); +#endif + return tDelay; +} + +#if defined(HAS_ADDITIONAL_REMOTE_COMMANDS) +void doPattern1() { + RightNeoPixelBar.RainbowCycle(getDelayFromSpeed() / 8); + LeftNeoPixelBar.RainbowCycle(getDelayFromSpeed() / 8); + sCleanPatternAfterEnd = true; + sAtLeastOnePatternsIsActive = true; + + LeftNeoPixelBar.printlnPattern(); +} + +void doPattern2() { + uint16_t tDelay = getDelayFromSpeed(); + RightNeoPixelBar.Fade(COLOR32_GREEN_QUARTER, COLOR32_RED_QUARTER, 32, tDelay); + LeftNeoPixelBar.Fade(COLOR32_RED_QUARTER, COLOR32_GREEN_QUARTER, 32, tDelay); + sAtLeastOnePatternsIsActive = true; + + LeftNeoPixelBar.printlnPattern(); +} + +void doPatternStripes() { + RightNeoPixelBar.Stripes(COLOR32_GREEN_QUARTER, 2, COLOR32_RED_QUARTER, 2, 128, getDelayFromSpeed()); + LeftNeoPixelBar.Stripes(COLOR32_GREEN_QUARTER, 2, COLOR32_RED_QUARTER, 2, 128, getDelayFromSpeed()); + sAtLeastOnePatternsIsActive = true; + + LeftNeoPixelBar.printlnPattern(); +} + +void doPatternHeartbeat() { + uint16_t tDelay = getDelayFromSpeed(); + RightNeoPixelBar.Heartbeat(COLOR32_GREEN_HALF, tDelay, 2); + FrontNeoPixelBar.Heartbeat(COLOR32_BLUE_HALF, tDelay, 2); + LeftNeoPixelBar.Heartbeat(COLOR32_RED_HALF, tDelay, 2); + sAtLeastOnePatternsIsActive = true; + + LeftNeoPixelBar.printlnPattern(); +} + +void doPatternFire() { + uint16_t tDelay = getDelayFromSpeed(); + RightNeoPixelBar.Fire(tDelay, 80); + LeftNeoPixelBar.Fire(tDelay, 80, DIRECTION_DOWN); + sCleanPatternAfterEnd = true; + sAtLeastOnePatternsIsActive = true; + LeftNeoPixelBar.printlnPattern(); +} +#endif // HAS_ADDITIONAL_REMOTE_COMMANDS + +void initNeoPatterns() { + QuadrupedNeoPixelBar.begin(); // This sets the output pin. + RightNeoPixelBar.ColorWipe(COLOR32_GREEN_QUARTER, 120); + FrontNeoPixelBar.ScannerExtended(COLOR32_BLUE_HALF, 2, 120, 2, FLAG_SCANNER_EXT_ROCKET | FLAG_SCANNER_EXT_START_AT_BOTH_ENDS); + LeftNeoPixelBar.ColorWipe(COLOR32_RED_QUARTER, 120, 0, DIRECTION_DOWN); + setTimer1InterruptMarginMicros(4000); // To have the last 4 ms (of available 8) of the 20 ms time slot for servo for processing NeoPatterns and PlayRtttl + sAtLeastOnePatternsIsActive = true; // enable updates for pattern +} + +void doWipeOutPatterns() { + RightNeoPixelBar.ColorWipe(COLOR32_BLACK, getDelayFromSpeed(), FLAG_DO_NOT_CLEAR, DIRECTION_DOWN); + FrontNeoPixelBar.clear(); + LeftNeoPixelBar.ColorWipe(COLOR32_BLACK, getDelayFromSpeed(), FLAG_DO_NOT_CLEAR); + sAtLeastOnePatternsIsActive = true; // enable updates for pattern +} + +void showPatternSynchronizedWithServos() { + sShowPatternSynchronizedWithServos = true; // To trigger show() in handleQuadrupedNeoPixelUpdate() +} + +bool isAtLeastOnePatternActive() { + return (RightNeoPixelBar.ActivePattern != PATTERN_NONE || FrontNeoPixelBar.ActivePattern != PATTERN_NONE + || LeftNeoPixelBar.ActivePattern != PATTERN_NONE); +} + +#endif // #if defined(QUADRUPED_HAS_NEOPIXEL) + +#endif // _QUADRUPED_NEOPIXEL_HPP diff --git a/examples/QuadrupedControl/QuadrupedNeoPixelHelper.cpp b/examples/QuadrupedControl/QuadrupedNeoPixelHelper.cpp new file mode 100644 index 0000000..1139aff --- /dev/null +++ b/examples/QuadrupedControl/QuadrupedNeoPixelHelper.cpp @@ -0,0 +1,73 @@ +/* + * QuadrupedNeoPixelHelper.cpp + * + * The NeopPixel updates must be synchronized with the ServoEasing updates in order not to interfere with the servo pulse generation. + * Therefore we have a function handleServoTimerInterrupt() which replaces the disabled function of ServoEasing. + * + * Copyright (C) 2022 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of QuadrupedControl https://github.com/ArminJo/QuadrupedControl. + * + * QuadrupedControl is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "QuadrupedConfiguration.h" +//#define QUADRUPED_HAS_NEOPIXEL // Requires additionally 6300 to 6600 bytes +#if defined(QUADRUPED_HAS_NEOPIXEL) + +#define SUPPRESS_HPP_WARNING +#include "ServoEasing.h" + +#if defined(QUADRUPED_ENABLE_RTTTL) +#include +#endif + +/* + * Called every 20 ms. + * The modified overwritten ServoEasing ISR handling function extended for NeoPixel handling. + * We have 4 ms for our processing until servo interrupt starts again by call of setTimer1InterruptMarginMicros(4000) above. + * + * NeoPixels are handled here, since their show() function blocks interrupts + * and must therefore be synchronized with the servo pulse generation. + * The interrupt is not disabled after all servos are stopped like in the original function. + * This enables to call the NeoPixel update function continuously here. + * Calling of updateAllServos() is controlled by the misused ICNC1 / Input Capture Noise Canceler flag, which is set by ServoEasing :-). + * + * Update all servos from list and check if all servos have stopped. + */ +void handleServoTimerInterrupt() { +#if defined(USE_PCA9685_SERVO_EXPANDER) + // Otherwise it will hang forever in I2C transfer + interrupts(); // Enable interrupts +#endif +// Check the (misused) ICNC1 flag, which signals that ServoEasing interrupts were enabled again. + if (TCCR1B & _BV(ICNC1)) { + // Flag was set -> call update + if (updateAllServos()) { + // All servos have stopped here + // Do not disable interrupt (we need it for NeoPixels), only reset the flag + TCCR1B &= ~_BV(ICNC1); // Reset flag +#if defined(INFO) + Serial.println(F("All servos stopped, using interrupts now only for NeoPixels update")); +#endif + } + } + handleQuadrupedNeoPixelUpdate(); +} + +#endif // #if defined(QUADRUPED_HAS_NEOPIXEL) + diff --git a/library.json b/library.json index 372c1b2..9da5543 100644 --- a/library.json +++ b/library.json @@ -6,7 +6,7 @@ "type": "git", "url": "https://github.com/ArminJo/ServoEasing" }, - "version": "3.0.1", + "version": "3.1.0", "exclude": "pictures", "authors": { "name": "Armin Joachimsmeyer", diff --git a/library.properties b/library.properties index c1b3bcf..9e0024f 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=ServoEasing -version=3.0.1 +version=3.1.0 author=Armin Joachimsmeyer maintainer=Armin Joachimsmeyer sentence=Enables smooth servo movement.
Linear as well as other (Cubic, Circular, Bounce, etc.) ease movements for servos are provided. The Arduino Servo library or PCA9685 servo expanders are supported.
-paragraph=Just use myServo.easeTo() instead of myServo.write() and you have smooth servo movement.
Non blocking movement for all servos attached to the Arduino Servo library is implemented by reusing the interrupts of the Arduino servo timer.
All servos can move synchronized.

Includes the following easing functions:
  • Linear, Quadratic, Cubic and Quartic
  • Sine, Circular, Back, Elastic and Bounce
  • User defined
Each function supports the easing types In, Out, InOut and BouncingOutIn
Trim and reverse operations are supported as well as continuous rotating servos.

New: New easing type PRECISION. Added min and max constraints for servo write(). Changed constants for easing types. Fixed some bugs for micros as parameter. Bugfixes for PCA9685 expander.
Revision History
+paragraph=Just use myServo.easeTo() instead of myServo.write() and you have smooth servo movement.
Non blocking movement for all servos attached to the Arduino Servo library is implemented by reusing the interrupts of the Arduino servo timer.
All servos can move synchronized.

Includes the following easing functions:
  • Linear, Quadratic, Cubic and Quartic
  • Sine, Circular, Back, Elastic and Bounce
  • User defined
Each function supports the easing types In, Out, InOut and BouncingOutIn
Trim and reverse operations are supported as well as continuous rotating servos.

New: New easing type PRECISION. Added min and max constraints for servo write(). Changed constants for easing types. Fixed some bugs for micros as parameter. Bugfixes for PCA9685 expander. Added support to pause and resume.
Revision History
category=Device Control url=https://github.com/ArminJo/ServoEasing architectures=avr,megaavr,sam,samd,esp8266,esp32,stm32,STM32F1,apollo3,mbed,mbed_nano,rp2040 diff --git a/src/ServoEasing.h b/src/ServoEasing.h index 463e645..c58e456 100644 --- a/src/ServoEasing.h +++ b/src/ServoEasing.h @@ -24,10 +24,10 @@ #ifndef _SERVO_EASING_H #define _SERVO_EASING_H -#define VERSION_SERVO_EASING "3.0.1" +#define VERSION_SERVO_EASING "3.1.0" #define VERSION_SERVO_EASING_MAJOR 3 -#define VERSION_SERVO_EASING_MINOR 0 -#define VERSION_SERVO_EASING_PATCH 1 +#define VERSION_SERVO_EASING_MINOR 1 +#define VERSION_SERVO_EASING_PATCH 0 // The change log is at the bottom of the file /* @@ -718,7 +718,7 @@ bool checkI2CConnection(uint8_t aI2CAddress, Stream *aSerial); // Print class ha #endif /* - * Version 3.0.1 - 07/2022 + * Version 3.1.0 - 08/2022 * - SAMD51 support by Lutz Aumüller. * - Added support to pause and resume and `DISABLE_PAUSE_RESUME`. * - Fixed some bugs for PCA9685 expander introduced in 3.0.0.