diff --git a/.github/workflows/LibraryBuild.yml b/.github/workflows/LibraryBuild.yml index 6c4f77e..e391226 100644 --- a/.github/workflows/LibraryBuild.yml +++ b/.github/workflows/LibraryBuild.yml @@ -112,7 +112,7 @@ jobs: - arduino-boards-fqbn: esp32:esp32:featheresp32:FlashFreq=80 platform-url: https://dl.espressif.com/dl/package_esp32_index.json - sketches-exclude: QuadrupedControl,RobotArmControl # Comma separated list of (unique substrings of) example names to exclude in build + sketches-exclude: QuadrupedControl,RobotArmControl # Comma separated list of example names to exclude in build build-properties: PCA9685_Expander: -DUSE_PCA9685_SERVO_EXPANDER PCA9685_ExpanderAndServo: -DUSE_PCA9685_SERVO_EXPANDER -DUSE_SERVO_LIB diff --git a/README.md b/README.md index cb28a94..bd6eeb6 100644 --- a/README.md +++ b/README.md @@ -18,15 +18,18 @@ This is a library for smooth servo movements. It uses the standard Arduino Servo As an alternative to the Arduino Servo library, ServoEasing can be used with a [PCA9685 servo expander](https://learn.adafruit.com/16-channel-pwm-servo-driver?view=all) using the Arduino Wire library or a compatible one (and their restrictions).
For **ESP32** you need to install the Arduino ESP32Servo library.

For **AVR**, if you need only one or two servos, you may want to use the included [Lightweight Servo library](https://github.com/ArminJo/LightweightServo) instead of the Arduino Servo library because it uses only the internal Timer1 with no software overhead and has no problems with interrupt blocking libraries like SoftwareSerial, Adafruit_NeoPixel and DmxSimple.
-For instructions how to enable these alternatives see [Modifying library properties](#modifying-library-properties) +For instructions how to enable these alternatives see [Compile options / macros](#compile-options--macros) ### Features - **Linear** and 9 other ease movements are provided. - All servos can move **synchronized** or **independently**. - **Non blocking** movements are enabled by using **startEaseTo\* functions** by reusing the interrupts of the servo timer Timer1 or using a dedicated timer on other platforms. This function is not available for all platforms. -- Trim value for each servo may be set. -- Reverse operation of servo is possible eg. if it is mounted head down. -- Allow to specify an arbitrary mapping between degrees and microseconds by `attach(int aPin, int aMicrosecondsForServoLowDegree, int aMicrosecondsForServoHighDegree, int aServoLowDegree, int aServoHighDegree)` +- **Trim** value for each servo may be set. +- **Reverse operation** of servo is possible eg. if it is mounted head down. +- Allow to specify an arbitrary mapping between degrees and microseconds by `attach(int aPin, int aMicrosecondsForServoLowDegree, int aMicrosecondsForServoHighDegree, int aServoLowDegree, int aServoHighDegree)`. +- **Servo speed** can be specified in **degree per second** or **milliseconds** for the complete move. + +## [API](https://github.com/ArminJo/ServoEasing/blob/master/src/ServoEasing.h#L428) ## Usage Just call **myServo.startEaseTo()** instead of **myServo.write()** and you are done. Or if you want to wait (blocking) until servo has arrived, use **myServo.easeTo()**.
@@ -68,7 +71,7 @@ Just call **myServo.startEaseTo()** instead of **myServo.write()** and you are d Digital Servos have a **deadband of approximately 5 µs / 0.5 degree** which means, that you will see a **stuttering movement** if the moving speed is slow. If you control them with a PCA9685 expander it may get worse, since one step of 4.88 µs can be within the deadband, so it takes 2 steps to move the servo from its current position. -# Compile options / macros +# Compile options / macros for this library To customize the software to different car extensions, there are some compile options / macros available.
Modify it by commenting them out or in, or change the values if applicable. Or define the macro with the -D compiler option for gobal compile (the latter is not possible with the Arduino IDE, so consider to use [Sloeber](https://eclipse.baeyens.it).
| Option | Default | File | Description | @@ -80,14 +83,14 @@ Modify it by commenting them out or in, or change the values if applicable. Or d | `PRINT_FOR_SERIAL_PLOTTER` | disabled | ServoEasing.h | Generate serial output for Arduino Plotter. | | `USE_LEIGHTWEIGHT_SERVO_LIB` | disabled | ServoEasing.h | Makes the servo pulse generating immune to other libraries blocking interrupts for a longer time like SoftwareSerial, Adafruit_NeoPixel and DmxSimple. See below. Saves up to 742 bytes FLASH and 42 bytes RAM. | -# Modifying library properties -### Modifying library properties with Arduino IDE +# Modifying compile options +### Modifying compile options with Arduino IDE First use *Sketch/Show Sketch Folder (Ctrl+K)*.
If you did not yet stored the example as your own sketch, then you are instantly in the right library folder.
Otherwise you have to navigate to the parallel `libraries` folder and select the library you want to access.
In both cases the library files itself are located in the `src` directory.
-### Modifying library properties with Sloeber IDE +### Modifying compile options with Sloeber IDE If you are using Sloeber as your IDE, you can easily define global symbols with *Properties/Arduino/CompileOptions*.
![Sloeber settings](https://github.com/ArminJo/ServoEasing/blob/master/pictures/SloeberDefineSymbols.png) diff --git a/src/LightweightServo.cpp b/src/LightweightServo.cpp index 3c153a4..5b7e79e 100644 --- a/src/LightweightServo.cpp +++ b/src/LightweightServo.cpp @@ -31,10 +31,6 @@ #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__) #include "LightweightServo.h" -/* - * Commenting out this saves 70 bytes flash memory. You must then use the init function initLightweightServoPin9And10() manually. - */ -//#define DISABLE_SERVO_TIMER_AUTO_INITIALIZE /* * Variables to enable adjustment for different servo types * 544 and 2400 are values compatible with standard arduino values @@ -103,12 +99,12 @@ void deinitLightweightServoPin9_10(bool aUsePin9) { * If aUsePin9 is false, then Pin10 is used * 236 / 186(without auto init) bytes code size */ -int writeLightweightServo(int aValue, bool aUsePin9, bool aUpdateFast) { - if (aValue <= 180) { - aValue = DegreeToMicrosecondsLightweightServo(aValue); +int writeLightweightServo(int aDegree, bool aUsePin9, bool aUpdateFast) { + if (aDegree <= 180) { + aDegree = DegreeToMicrosecondsLightweightServo(aDegree); } - writeMicrosecondsLightweightServo(aValue, aUsePin9, aUpdateFast); - return aValue; + writeMicrosecondsLightweightServo(aDegree, aUsePin9, aUpdateFast); + return aDegree; } void writeMicrosecondsLightweightServo(int aMicroseconds, bool aUsePin9, bool aUpdateFast) { @@ -153,8 +149,8 @@ void setLightweightServoPulseMicrosFor0And180Degree(int aMicrosecondsForServo0De /* * Pin 9 / Channel A. If value is below 180 then assume degree, otherwise assume microseconds */ -void write9(int aValue, bool aUpdateFast) { - writeLightweightServo(aValue, true, aUpdateFast); +void write9(int aDegree, bool aUpdateFast) { + writeLightweightServo(aDegree, true, aUpdateFast); } void writeMicroseconds9(int aMicroseconds, bool aUpdateFast) { @@ -171,8 +167,8 @@ void writeMicroseconds9Direct(int aMicroseconds) { /* * Pin 10 / Channel B */ -void write10(int aValue, bool aUpdateFast) { - writeLightweightServo(aValue, false, aUpdateFast); +void write10(int aDegree, bool aUpdateFast) { + writeLightweightServo(aDegree, false, aUpdateFast); } void writeMicroseconds10(int aMicroseconds, bool aUpdateFast) { @@ -189,12 +185,12 @@ void writeMicroseconds10Direct(int aMicroseconds) { /* * Conversion functions */ -int DegreeToMicrosecondsLightweightServo(int aValueDegree) { - return (map(aValueDegree, 0, 180, sMicrosecondsForServo0Degree, sMicrosecondsForServo180Degree)); +int DegreeToMicrosecondsLightweightServo(int aDegree) { + return (map(aDegree, 0, 180, sMicrosecondsForServo0Degree, sMicrosecondsForServo180Degree)); } -int MicrosecondsToDegreeLightweightServo(int aValueMicros) { - return map(aValueMicros, sMicrosecondsForServo0Degree, sMicrosecondsForServo180Degree, 0, 180); +int MicrosecondsToDegreeLightweightServo(int aMicroseconds) { + return map(aMicroseconds, sMicrosecondsForServo0Degree, sMicrosecondsForServo180Degree, 0, 180); } #endif diff --git a/src/LightweightServo.h b/src/LightweightServo.h index fe94c42..e84b39b 100644 --- a/src/LightweightServo.h +++ b/src/LightweightServo.h @@ -32,6 +32,11 @@ #include +/* + * Commenting out this saves 70 bytes flash memory. You must then use the init function initLightweightServoPin9And10() manually. + */ +//#define DISABLE_SERVO_TIMER_AUTO_INITIALIZE + #define ISR1_COUNT_FOR_20_MILLIS 40000 // you can modify this if you have servos which accept a higher rate /* @@ -45,20 +50,20 @@ void deinitLightweightServoPin9_10(bool aUsePin9); void setLightweightServoPulseMicrosFor0And180Degree(int aMicrosecondsForServo0Degree, int a180DegreeValue); void setLightweightServoRefreshRate(unsigned int aRefreshPeriodMicroseconds); -int writeLightweightServo(int aValue, bool aUsePin9, bool aUpdateFast = false); +int writeLightweightServo(int aDegree, bool aUsePin9, bool aUpdateFast = false); void writeMicrosecondsLightweightServo(int aMicroseconds, bool aUsePin9, bool aUpdateFast = false); -void write9(int aValue, bool aUpdateFast = false); // setLightweightServoPulsePin9 Channel A +void write9(int aDegree, bool aUpdateFast = false); // setLightweightServoPulsePin9 Channel A void writeMicroseconds9(int aMicroseconds, bool aUpdateFast = false); void writeMicroseconds9Direct(int aMicroseconds); -void write10(int aValue, bool aUpdateFast = false); // setLightweightServoPulsePin10 Channel B +void write10(int aDegree, bool aUpdateFast = false); // setLightweightServoPulsePin10 Channel B void writeMicroseconds10(int aMicroseconds, bool aUpdateFast = false); void writeMicroseconds10Direct(int aMicroseconds); // convenience functions -int DegreeToMicrosecondsLightweightServo(int aValueDegree); -int MicrosecondsToDegreeLightweightServo(int aValueMicros); +int DegreeToMicrosecondsLightweightServo(int aDegree); +int MicrosecondsToDegreeLightweightServo(int aMicroseconds); #endif // AVR_ATmega328 #endif // LIGHTWEIGHT_SERVO_H_ diff --git a/src/ServoEasing.h b/src/ServoEasing.h index d7d71cd..192f989 100644 --- a/src/ServoEasing.h +++ b/src/ServoEasing.h @@ -329,12 +329,12 @@ * Values for provided EaseTypes * The call style is coded in the upper 3 bits */ -#define CALL_STYLE_DIRECT 0x00 // == IN -#define CALL_STYLE_OUT 0x20 -#define CALL_STYLE_IN_OUT 0x40 +#define CALL_STYLE_DIRECT 0x00 // == IN +#define CALL_STYLE_OUT 0x20 +#define CALL_STYLE_IN_OUT 0x40 #define CALL_STYLE_BOUNCING_OUT_IN 0x60 // Bouncing has double movement, so double time (half speed) is taken for this modes -#define CALL_STYLE_MASK 0xE0 // for future extensions +#define CALL_STYLE_MASK 0xE0 // for future extensions #define EASE_TYPE_MASK 0x0F #define EASE_LINEAR 0x00 // No bouncing available @@ -389,15 +389,15 @@ // some PCA9685 specific constants #define PCA9685_GENERAL_CALL_ADDRESS 0x00 -#define PCA9685_SOFTWARE_RESET 6 -#define PCA9685_DEFAULT_ADDRESS 0x40 -#define PCA9685_MAX_CHANNELS 16 // 16 PWM channels on each PCA9685 expansion module -#define PCA9685_MODE1_REGISTER 0x0 -#define PCA9685_MODE_1_RESTART 7 -#define PCA9685_MODE_1_AUTOINCREMENT 5 -#define PCA9685_MODE_1_SLEEP 4 -#define PCA9685_FIRST_PWM_REGISTER 0x06 -#define PCA9685_PRESCALE_REGISTER 0xFE +#define PCA9685_SOFTWARE_RESET 6 +#define PCA9685_DEFAULT_ADDRESS 0x40 +#define PCA9685_MAX_CHANNELS 16 // 16 PWM channels on each PCA9685 expansion module +#define PCA9685_MODE1_REGISTER 0x0 +#define PCA9685_MODE_1_RESTART 7 +#define PCA9685_MODE_1_AUTOINCREMENT 5 +#define PCA9685_MODE_1_SLEEP 4 +#define PCA9685_FIRST_PWM_REGISTER 0x06 +#define PCA9685_PRESCALE_REGISTER 0xFE #define PCA9685_PRESCALER_FOR_20_MS ((25000000L /(4096L * 50))-1) // = 121 / 0x79 at 50 Hz