Code Monkey home page Code Monkey logo

servoeasing's Introduction

A library for smooth servo movements.
It uses the standard Arduino Servo library and therefore has its restrictions regarding pins and platform support.

Badge License: GPLv3     Badge Version     Badge Commits since latest     Badge Build Status     Badge Hit Counter

Stand With Ukraine

Available as Arduino library "ServoEasing". Contains the QuadrupedControl example.

Button Install     Button API     Button Changelog

If you find this library useful, please give it a star.

🌎 Google Translate


YouTube video of ServoEasing in action

Demonstration of different servo easings

Table of content


Servo easing library for Arduino

Its purpose is to interpolate the movement between two servo positions set by software.
If your servo control data is e.g. generated by an joystick or other "slow" changing inputs and therefore does not change suddenly or does not jump, you most likely do not need this library!, you may consider to use a digital low pass or simple EMA filters to smooth your values used to control the servos.
ServoEasing works with the Arduino Servo library as well as with PCA9685 servo expanders. The expander in turn requires the Arduino Wire library or a compatible one and is bound to their restrictions.
For ESP32 you need to install the Arduino ESP32Servo library.

If you require only one or two servos, you may want to use the included LightweightServo library which is like the Adafruit TiCoServo library, but more lightweight and currently only for Uno, Nano, instead of the Arduino Servo library. The LightweightServo library uses the internal Timer1 with no software overhead and therefore has no problems with servo twitching or interrupt blocking libraries like SoftwareSerial, Adafruit_NeoPixel and DmxSimple.
For instructions how to enable these alternatives, see Compile options / macros.


Features

  • Linear and 9 other ease movements are provided.
  • All servos can move synchronized or independently.
  • Non blocking movements are implemented by the startEaseTo* functions by using a timer. This functions are not available for all platforms.
  • Degree values >= 400 is taken as microsecond values for the servo pulse to allow fine-grained control.
  • Float angels are supported to allow fine-grained servo control comparable to using microseconds.
  • User-specified callback function at "servo arrived" enables movement control independent of main loop.
  • Stop and resume of servo movement.
  • A trim value can be set for any servo. Its value is internally added to each requested position.
  • Reverse operation of servo is possible e.g. if it is mounted head down.
  • Constraints for minimum and maximum servo degree can be specified. Trim and reverse are applied after constraint processing.
  • 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.
  • Multiple servo handling by *ForAllServos() functions like setIntegerDegreeForAllServos(3, 135, 135, 135).
  • All ServoEasing objects are accessible by using the ServoEasing::ServoEasingArray[].
  • Easy implementation of a move list - see ConsecutiveEasingsWithCallback example.

List of easing functions

Linear     Quadratic     Cubic     Quartic

Sine     Circular     Back     Elastic     Bounce

Precision     Dummy     User defined

  • Precision is like linear, but if descending, add a 5 ° negative bounce in the last 20 % of the movement time. So the target position is always approached from below. This enables it to taken out the slack/backlash of any hardware moved by the servo.
  • Dummy is used for delays in callback handler.

All easing functions can be used in the following variants:

All ease functions are called internally with the value: PercentageOfCompletion / 100 giving a call value from 0 to 1.

  • In: Start the function with 0 and go to 1 linear. Except for PRECISION, where we do a bounce if approaching from above (go in to origin).
  • Out: Start the function with 1 and go to 0 linear. Except for PRECISION, where we do a bounce if approaching from below (go out from origin).
  • InOut: Start the function with 0 go to 1 and back to 0.
  • Bouncing: Start with OUT, then return with IN to start degree. E.g. Bouncing of the SINE function results in the upper (positive) half of the sine.

All easing types (starting in flavor IN_OUT, then IN, OUT and BOUNCE) in one plot.

Since the values are computed in a fixed 20 ms raster, the last degree increment or decrement in an easing may be much smaller than the increment/decrement before, resulting in some small discontinuities between adjacent movements.
Arduino Plotter Output for Linear->Quadratic->Cubic->Quartic->Sine-Circular->Back->Elastic

Constraints

To restrict servo movements to a fixed range, you can specify constraints with setMinMaxConstraint(int aMinDegreeOrMicrosecond, int aMaxDegreeOrMicrosecond).

Arduino Plotter Output with constraints at 5 ° and 175 ° activated.

Arduino Plotter Output with constraints at 5 degree and 175 degree activated

Disable easing temporarily

By setting speed -which is an uint16- to a real high value like e.g. 50000, you can effectively disable easing and get almost the same behavior as if using Servo.write() function directly.


API

For floating point constants, use the notation of 123.456f with trailing f (for a floating point constant) to avoid compiler errors. A Doxygen documentation of the sources is available here.

Usage

See also the examples here.

void setup() {
    Servo1.attach(SERVO1_PIN, 45);
}
void loop() {
    Servo1.setEasingType(EASE_CUBIC_IN_OUT); // EASE_LINEAR is default
    Servo1.easeTo(135, 40);                                 // Blocking call
    Servo1.startEaseTo(45, 40, START_UPDATE_BY_INTERRUPT);  // Non blocking call
    // Now the servo is moving to the end position independently of your program.
    delay(5000);
}

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().

  • Do not forget to initially set the start position for the Servo, since the library has no knowledge about your servos initial position and therefore starts at 0 ° at the first move, which may be undesirable.
    Setting the start position of the servo can be done as the second parameter to myServo.attach(int aPin, int aInitialDegree) or by calling myServo.write(int aDegree),
  • And do not forget to initially set the moving speed (as degrees per second) with myServo.setSpeed() or as second parameter to startEaseTo() or easeTo(). Otherwise the Servo will start with the speed of 5 ° per second, to indicate that speed was not set.

Multiple servo handling

You can handle multiple servos simultaneously by special functions like writeAllServos(), setSpeedForAllServos(), setIntegerDegreeForAllServos(), setEaseToDForAllServos(), updateAndWaitForAllServosToStop(), setEaseToForAllServosSynchronizeAndWaitForAllServosToStop(), setEaseToForAllServosSynchronizeAndStartInterrupt() and much more.
See below.


Comparison between Quadratic, Cubic and Sine easings.

Arduino Serial Plotter result of the SymmetricEasing example. Arduino plot


Useful resources


Resolution of servo positioning

  • The standard range of 544 to 2400 µs per 180 ° results in an timing of around 10 µs per degree.
  • The Arduino Servo library on AVR uses an prescaler of 8 at 16 MHz clock resulting in a resolution of 0.5 µs.
  • The PCA9685 expander has a resolution of 4.88 µs per step (@ 20 ms interval) resulting in a resolution of 0.5 °. Digital Servos have a deadband of approximately 5 µs / 0.5 ° 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.

Mapping of servo positioning

If you want to operate your servo e.g. from -90 ° to +90 °, you have two possibilities to specify this during attach:

  1. Use Servo1.attachWithTrim(SERVO1_PIN, 90, START_DEGREE_VALUE, DEFAULT_MICROSECONDS_FOR_0_DEGREE, DEFAULT_MICROSECONDS_FOR_180_DEGREE) like it is done in the TwoServos example.
  2. Use Servo1.attach(SERVO1_PIN, DEFAULT_MICROSECONDS_FOR_0_DEGREE, DEFAULT_MICROSECONDS_FOR_180_DEGREE, -90, 90).

If your servo has other timing characteristics than the default one -544 µs for 0 and 2400 µs for 180 ° - you have to use Servo1.attach(SERVO1_PIN, <MY_SERVO_MICROSECONDS_FOR_0_DEGREE>, <MY_SERVO_MICROSECONDS_FOR_180_DEGREE>).
You can combine this with variant 2 from above to transparently specify your servo characteristics e.g. like it is done in the RobotArmControl example:

#define PIVOT_MICROS_AT_PLUS_70_DEGREE        2400 // Left - the MG90 servos are not capable of full 180°
#define PIVOT_MICROS_AT_MINUS_70_DEGREE        700 // Right
BasePivotServo.attach(PIVOT_SERVO_PIN, 0, PIVOT_MICROS_AT_MINUS_70_DEGREE, PIVOT_MICROS_AT_PLUS_70_DEGREE, -70, 70);

Speed of servo positioning

These values are measured with the SpeedTest example.

These are the fastest values for my SG90 servos at 5 volt (4.2 volt with servo active).

Degree Duration Speed
180 400 ms 450 degree per second
90 300 ms 300 degree per second
45 180 ms 250 degree per second
30 150 ms 200 degree per second
20 130 ms 150 degree per second
10 80 ms 125 degree per second

Values for the MG90Sservos servos at 5 volt (4.2 volt with servo active).

Degree Duration Speed
180 330 ms 540 degree per second
90 220 ms 410 degree per second
45 115 ms 390 degree per second

Minimum number of pulses for reliable servo positioning

After disconnected, my SG90 servo requires 4 pulses for a 180 degree turn. It may be less, if the turn is smaller.
After disconnected, myMG90 servo requires 1 pulse for a 110 degree turn. the second pulse (after 20 ms) adds around 10 degree to it, so it takes around 6 to 7 pulses (120 ms to 140 ms) for a complete 180 degree turn.
These values seems to be independent of the turn direction.

These values are measured with the UnitTest example with TEST_FIXED_PULSE_NUMBERS defined.

Why *.hpp instead of *.cpp?

Every *.cpp file is compiled separately by a call of the compiler exclusively for this cpp file. These calls are managed by the IDE / make system. In the Arduino IDE the calls are executed when you click on Verify or Upload.
And now our problem with Arduino is: How to set compile options for all *.cpp files, especially for libraries used?
IDE's like Sloeber or PlatformIO support this by allowing to specify a set of options per project. They add these options at each compiler call e.g. -DTRACE.
But Arduino lacks this feature. So the workaround is not to compile all sources separately, but to concatenate them to one huge source file by including them in your source. This is done by e.g. #include "ServoEasing.hpp".
But why not #include "ServoEasing.cpp"?
Try it and you will see tons of errors, because each function of the *.cpp file is now compiled twice, first by compiling the huge file and second by compiling the *.cpp file separately, like described above. So using the extension cpp is not longer possible, and one solution is to use hpp as extension, to show that it is an included *.cpp file. Every other extension e.g. cinclude would do, but hpp seems to be common sense.

Using the new *.hpp files

In order to support compile options more easily, the line #include <ServoEasing.h> must be changed to #include <ServoEasing.hpp> in your main program (aka *.ino file with setup() and loop()).

In all other files you must use #include <ServoEasing.h>, to prevent multiple definitions linker errors:

If you forget to include ServoEasing.hpp, you will see errors like Simple.ino:57: undefined reference to ServoEasing::attach(int, int).

Ensure that all macros in your main program are defined before any #include <ServoEasing.hpp>.
The following macros will definitely be overridden with default values otherwise:

  • MAX_EASING_SERVOS
  • REFRESH_INTERVAL
  • USE_PCA9685_SERVO_EXPANDER

Compile options / macros for this library

To customize the library to different requirements, there are some compile options / macros available.
These macros must be defined in your program before the line #include <ServoEasing.hpp> to take effect.
Modify them by enabling / disabling them, or change the values if applicable.

Name Default value Description
USE_PCA9685_SERVO_EXPANDER disabled Enables the use of the PCA9685 I2C expander chip/board.
PCA9685_ACTUAL_CLOCK_FREQUENCY 25000000L Change it, if your PCA9685 has another than the default 25 MHz internal clock. See chapter 2 and 5 of the PCA9685 Datasheet "25 MHz typical internal oscillator requires no external components". This value is taken for all attached PCA9685 expanders!
USE_SOFT_I2C_MASTER disabled Saves up to 1756 bytes program memory and 218 bytes RAM for PCA9685 I2C communication compared with Arduino Wire.
USE_SERVO_LIB disabled Use of PCA9685 normally disables use of regular servo library. You can force additional using of regular servo library by defining USE_SERVO_LIB. See below.
PROVIDE_ONLY_LINEAR_MOVEMENT disabled Disables all but LINEAR movement. Saves up to 1540 bytes program memory.
DISABLE_COMPLEX_FUNCTIONS disabled Disables the SINE, CIRCULAR, BACK, ELASTIC, BOUNCE and PRECISION easings. Saves up to 1850 bytes program memory.
MAX_EASING_SERVOS 12, 16(for PCA9685) Saves 4 byte RAM per servo. If this value is smaller than the amount of servos declared, attach() will return error and other library functions will not work as expected.
Of course all AllServos() functions and isOneServoMoving() can't work correctly!
DISABLE_MICROS_AS_DEGREE_PARAMETER disabled Disables passing also microsecond values as (target angle) parameter. Saves up to 128 bytes program memory.
DISABLE_MIN_AND_MAX_CONSTRAINTS disabled Disables servo movement constraints. Saves 4 bytes RAM per servo but strangely enough no program memory.
DISABLE_PAUSE_RESUME disabled Disables pause and resume functionality. Saves 5 bytes RAM per servo.
PRINT_FOR_SERIAL_PLOTTER disabled Generate serial output for Arduino Plotter (Ctrl-Shift-L).
DEBUG disabled Generates lots of lovely debug output for this library.
USE_LEIGHTWEIGHT_SERVO_LIB disabled Available only for ATmega328. Supports only servos at pin 9 and 10. Makes the servo pulse generating immune to other libraries blocking interrupts for a longer time like SoftwareSerial, Adafruit_NeoPixel and DmxSimple. See below. Saves up to 742 bytes program memory and 42 bytes RAM.
MINIMUM_PULSE_WIDTH 400 The shortest pulse which can be sent to a servo by this library. This value is smaller than the value used by the Arduino Servo library, which is 544 us (MIN_PULSE_WIDTH), to be more versatile.
MAXIMUM_PULSE_WIDTH 3500 The shortest pulse which can be sent to a servo by this library. This value is greater than the value used by the Arduino Servo library, which is 2400 us (MAX_PULSE_WIDTH), to be more versatile.

Using PCA9685 16-Channel Servo Expander

Using the PCA9685 expander makes the servo pulse generating immune to other libraries blocking interrupts for a longer time like SoftwareSerial, Adafruit_NeoPixel and DmxSimple.
To enable the use of the expander, activate the line #define USE_PCA9685_SERVO_EXPANDER before #include <ServoEasing.hpp>.
In expander mode, timer1 is only required for the startEaseTo* functions and not for the blocking easeTo* functions, since no servo signal must be generated by it.

The pin number parameter of the attach function determines the port number of the PCA9685 and can be in the range from 0 to 15.

Be aware that the PCA9685 expander is reset at the first attach() and initialized at every further attach().
To control simultaneously servos with the Arduino Servo library i.e. servos which are directly connected to the Arduino board, activate the line #define USE_SERVO_LIB.
In this case you should attach the expander servos first in order to initialize the expander board correctly. And as long as no servo using the Arduino Servo library is attached, the expander servos will not move, which should not be a problem since you normally attach all servos in setup().
Resolution of the is PCA9685 signal is approximately 0.5 °.

On the ESP32 the I2C library is only capable to run at 100 kHz, because it interferes with the Ticker / Timer library used. Even with 100 kHz clock we have some dropouts / NAK's because of sending address again instead of first data.
Since the raw transmission time of 32 Servo positions is 17.4 µs @ 100 kHz, not more than 2 expander boards can be connected to one I2C bus on an ESP32 board, if all servos should move simultaneously.
If you do not use any timer in your program you can increase speed up to 800 kHz. Maybe you have to attach 2 x 2.2 kΩ pullup resistors to the I2C lines to have it working reliably.


Using the included Lightweight Servo library for ATmega328

This library is like the Adafruit TiCoServo library, but more lightweight and currently only for Uno, Nano.

Using the Lightweight Servo library reduces sketch size and makes the servo pulse generating immune to other libraries blocking interrupts for a longer time like SoftwareSerial, Adafruit_NeoPixel and DmxSimple.
Up to 2 servos are supported by this library and they must be physically attached to pin 9 and/or 10 of the Arduino board.
To enable it, activate the line #define USE_LEIGHTWEIGHT_SERVO_LIB before the line #include "LightweightServo.hpp" like it is done in the TwoServos example.
If you do not use the Arduino IDE, take care that Arduino Servo library sources are not compiled / included in the project.


Handling multiple servos with the internal ServoEasingArray

The ServoEasing library provides two arrays to ease the handling of multiple servos.

  • ServoEasing *ServoEasing::ServoEasingArray[MAX_EASING_SERVOS]
  • float ServoEasing::ServoEasingNextPositionArray[MAX_EASING_SERVOS]

Every ServoEasing object is appended to the ServoEasingArray by the attach() function. Only the order of the attach() statements determines the position in the array. So you can access your servo, which you attached secondly, also by ServoEasing::ServoEasingArray[1]->setEaseTo(135) as it is done here.
There are also many other *AllServos* functions like stopAllServos().

To move multiple servo, you can fill up the ServoEasing::ServoEasingNextPositionArray with the desired positions and then use e.g. the function setEaseToForAllServos(). Then you must enable interrupt with enableServoEasingInterrupt() or call updateAllServos() in your main loop until it returns true.
If you want to move all your servos synchronized, i.e. they all stop at the same time, you can use the setEaseToForAllServosSynchronizeAndWaitForAllServosToStop() or setEaseToForAllServosSynchronizeAndStartInterrupt function.
An example can be found here.
The Quadruped example makes heavy use of the *AllServos* functions.

If you detach a servo and then attach another one, the latter will get the index of the former detached one.


Examples for this library

The examples are available at File > Examples > Examples from Custom Libraries / ServoEasing.


WOKWI online examples


Servo utilities


Building breadboard servo adapter

Converting a 10 pin double row pin header with 21 mm pin length to a breadboard servo adapter. Side view Top view


Internals

The API accepts degrees or microseconds as float or integer values, but internally only microseconds (or units (= 4.88 µs) if using PCA9685 expander) and not degrees are used to speed up things.

If you do not specify an initial position with attach, the first attach moves servo to DEFAULT_PULSE_WIDTH (90 degree | 1500 us). This behavior is implemented by the underlying Servo library. Following attaches just use the last position of this Servo.


Supported Arduino architectures

Every Arduino architecture with a Servo library will work without any modifications in blocking mode.
Non blocking behavior can always be achieved manually by calling update() or updateAllServos() in a loop - see ThreeServos example.
Interrupt based movement (movement without calling update() manually in a loop) is supported for the following Arduino architectures:
avr, megaavr, sam, samd, esp8266, esp32, stm32, STM32F1 and apollo3.
It is not planned to support the ATtiny architecture, but you are invited to do it by yourself and send a pull request.


Timer usage for interrupt based movement

On AVR Timer1 is used for the Arduino Servo library. To have non blocking easing functions its unused Channel B is used to generate an interrupt 100 µs before the end of the 20 ms Arduino Servo refresh period. This interrupt then updates all servo values for the next servo signal refresh period.

Platform Timer Library providing the timer
avr Timer1 Servo.h
ATmega Timer5 Servo.h
megaavr TCA0
sam ID_TC8 (TC2 channel 2)
samd TC5
esp8266 + esp32 Ticker Ticker.h
stm32 TIM3 HardwareTimer.h
STM32F1 3 or 7 HardwareTimer.h
Teensy IntervalTimer
apollo3 timer 3 segment A
Mbed mbed::Ticker Ticker.h
RP2040 / Pi Pico default alarm pool time.h

Adding a new platform / board

If timer support is available for a platform the library can be ported by adding code for the Timer20ms like is was done for ESP and STM.
To add a new platform, the following steps have to be performed:

  1. If the new platform has an Arduino compatible Servo library, fine, otherwise include the one required for this platform like it is done for ESP32 here.
  2. You need a 20ms interrupt source providing the functions enableServoEasingInterrupt() and (optional) disableServoEasingInterrupt(). Extend these functions with code for the new platform. Place includes and timer definitions at top of ServoEasing.hpp.
  3. If your interrupt source requires an ISR (Interrupt Service Routine) place it after disableServoEasingInterrupt() where all the other ISR are located.
  4. To test the new platform, you may want to enable TRACE output by activating the line #define TRACE in ServoEasing.hpp and enabling interrupt timing feedback by activating the line #define MEASURE_SERVO_EASING_INTERRUPT_TIMING in ServoEasing.hpp.
  5. If it works for you, please issue a Pull Request, to share your efforts with the community.

Good luck!


Troubleshooting

If you see strange behavior, you can open the library file ServoEasing.hpp and activate the line #define TRACE or #define DEBUG. This will print internal information visible in the Arduino Serial Monitor which may help finding the reason for it.


Revision History

Version 3.3.0

  • Added functions setEaseTo(), setEaseToD(), startEaseTo() and startEaseToD() with first parameter as unsigned int to avoid compiler errors call of overloaded 'startEaseTo(unsigned int....
  • Added functions read() and readMicroseconds() to be compatible to Servo library.
  • Added function reattach() without parameters to be used after detach().

Version 3.2.1

  • Renamed function setDegreeForAllServos() to setIntegerDegreeForAllServos() and added function setFloatDegreeForAllServos().

Version 3.2.0

  • ATmega4808 support added.
  • Added function getCurrentMicroseconds().
  • Improved many and added workaround for ESP32 bug in while loops in examples.
  • Added PCA9685_ACTUAL_CLOCK_FREQUENCY macro.
  • Renamed function synchronizeAndEaseToArrayPositions() to setEaseToForAllServosSynchronizeAndWaitForAllServosToStop().

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.
  • Changed ENABLE_MICROS_AS_DEGREE_PARAMETER to DISABLE_MICROS_AS_DEGREE_PARAMETER thus enabling micros as parameter by default.
  • Fixed some bugs for micros as parameter.
  • Changed constants for easing types.
  • Additional parameter aUserDataPointer for user easing function.
  • New easing type PRECISION.
  • New function printEasingType().
  • Easing functions are converted to static member functions now.
  • Easing types can be disabled individually.
  • Improved PCA9685 handling / support for SoftI2CMaster.
  • Changed default for parameter doWrite for setTrim() from false to true.
  • Added min and max constraints for servo write() and DISABLE_MIN_AND_MAX_CONSTRAINTS.

Version 2.4.1

  • RP2040 support.

Version 2.4.0

  • New attach() functions with initial degree parameter to be written immediately. This replaces the attach() and write() combination at setup.
  • Renamed ServoEasing.cpp to ServoEasing.hpp and LightweightServo.cpp to LightweightServo.hpp.

Version 2.3.4

  • ENABLE_MICROS_AS_DEGREE_PARAMETER also available for PCA9685 expander.
  • Moved sServoArrayMaxIndex, sServoNextPositionArray and sServoArray to ServoEasing::sServoArrayMaxIndex, ServoEasing::ServoEasingNextPositionArray and ServoEasing::ServoEasingArray.
  • Support for Apollo3 2.x core.
  • Fixed ESP8266 pin definitions.

Version 2.3.3

  • Added compile option ENABLE_MICROS_AS_DEGREE_PARAMETER to allow usage of microseconds instead of degree as function arguments for all functions using degrees as argument.
  • Improved LightweightServo API.

Version 2.3.2

  • Removed blocking wait for ATmega32U4 Serial in examples.
  • Improved output for Arduino Serial Plotter.

Version 2.3.1

  • Fixed wrong timer selection for STM32F1xx / ARDUINO_ARCH_STM32.
  • Documentation.

Version 2.3.0

  • Fixed EASE_LINEAR formula bug introduced with 2.0.0 for 32 bit CPU's. Thanks to drifkind.
  • Added stop(), continueWithInterrupts() and continueWithoutInterrupts() functions.

Version 2.2.0

  • ATmega4809 (Uno WiFi Rev 2, Nano Every) support.
  • Corrected position of macro for MAX_EASING_SERVOS.

Version 2.1.1

  • Fixed bug in detach of first servo.

Version 2.1.0

  • Added support of Teensy boards.

Version 2.0.0

  • PCA9685_Expander and standard Servos can be controlled simultaneously by defining USE_SERVO_LIB.
  • Changed some types to _fast types
  • Standardize pins for all examples

Version 1.6.1

  • Fix bug for Arduino SAMD boards.

Version 1.6.0

  • Added support of Apollo3 boards.
  • Print library version in examples.

Version 1.5.2

  • More examples using areInterruptsActive().
  • Added support of Arduino SAMD boards.

Version 1.5.1

  • Added support for STM32 cores of Arduino Board manager. Seen in the Arduino IDE as "Generic STM32F1 series" from STM32 Boards.
  • Inserted missing Wire.begin() in setup of PCA9685_Expander example.
  • In isMovingAndCallYield() yield() only called/required for an ESP8266.
  • New function areInterruptsActive(), especially for ESP32.

Version 1.5.0

  • Use type Print * instead of Stream *.
  • New LightweightServoExample.
  • Added function delayAndUpdateAndWaitForAllServosToStop().
  • Added Arduino Due support by using timer 8.
  • New PCA9685_ExpanderFor32Servos example.

Version 1.4.3

  • Improved detach() handling.
  • Initialize variables explicitly to 0 in constructor. On an ESP8266 they were NOT initialized to 0 😞.

Version 1.4.2

  • Improved INVALID_SERVO handling.
  • Speed 0 (not initialized) handling.
  • Fixed bug in ThreeServos example.

Version 1.4.1

  • Improved documentation and definitions for continuous rotating servo. Thanks to Eebel!
  • Improved support and documentation for generating Arduino Serial Plotter output.
  • Support of STM32F1 / BluePill boards.

Version 1.4.0

  • setTrim has additional parameter doWrite which is default false in contrast to older versions, where a write was always performed.
  • New attach( aPin, aMicrosecondsForServoLowDegree, aMicrosecondsForServoHighDegree, aServoLowDegree, aServoHighDegree) function for arbitrary mapping of servo degree to servo pulse width.
  • Order of Servos in sServoArray[] now depends from order of calling attach() and not from order of declaration.
  • New example for continuous rotating servo.

Version 1.3.1

  • Added detach() function.

Version 1.3.0

  • Added ESP32 support by using ESP32Servo.h and Ticker.h instead of Servo.h timer interrupts.
  • Changed degree parameter and values from uint8_t to integer to support operating a servo from -90 ° to + 90 ° with 90 ° trim.
  • RobotArmControl + QuadrupedControl examples refactored.
  • Changed "while" to "for" loops to avoid a gcc 7.3.0 atmel6.3.1 bug.
  • Extended SpeedTest example. Now also able to change the width of the refresh period.

Version 1.2

  • Added ESP8266 support by using Ticker instead of timer interrupts for ESP.
  • AsymetricEasing example overhauled.

Version 1.1.0

  • Corrected sine, circular, back and elastic IN functions.
  • easeTo() and write() store their degree parameter now also in sServoNextPositionArray.
  • added setSpeed(), getSpeed(), setSpeedForAllServos() and added ease* functions without speed parameter.
  • added getEndMicrosecondsOrUnits(), getDeltaMicrosecondsOrUnits().
  • added setDegreeForAllServos(uint8_t aNumberOfValues, va_list * aDegreeValues),setDegreeForAllServos(uint8_t aNumberOfValues, ...).
  • added compile switch PROVIDE_ONLY_LINEAR_MOVEMENT to save additional 1500 bytes program memory if enabled.
  • added convenience function clipDegreeSpecial().

Version 1.0.0

Initial Arduino library version.

CI

Since Travis CI is slow (5 times slower 17:43 vs. 3:15 minutes), the library examples are now tested with GitHub Actions for this boards.

Requests for modifications / extensions

Please write me a PM including your motivation/problem if you need a modification or an extension.

servoeasing's People

Contributors

arminjo avatar clowrey avatar jnth avatar per1234 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

servoeasing's Issues

Interrupt startEaseTo functions not working on Arduino Nano Every

Bug Report

Board

  • Arduino megaAVR board (NanoEvery)

IDE

  • Arduino IDE

Example to reproduce the issue

  • OneServo

Version

Current behavior

With Arduino Nano Every all of the startEaseTo functions that use interrupts are non functional. Using the examples and code within other applications it doesn't move the servo. Using the easeTo function (blocking) works well.

I believe there might be something to do with using the Millis() TCA0 timer, it is referenced that the Every uses the TCB3 but I noticed that once the code had gone to use the startEaseTo function and changed the mode of the TCA0 timer the delay speed changed indicating it had affected the millis() counter.

Is it possible to use a PCA9685 AND Servos connected to Arduino?

Bug Report

Is it possible to use PCA 9685 expander and also run another servo from the Mega 2560.

I'm building another droid. I communicate with three servos in the head of the droid. I have another servo in the body of the droid. Space is extremely tight in this build.

Arduino Platform

  • Arduino ATmega328* board (UNO, Nano)
  • [X ] Arduino ATmega2560 board (Mega)
  • Arduino ATmega32U4 board (Leonardo)
  • Arduino SAM board (Due)
  • Arduino SAMD board (Zero, MKR*)
  • ESP8266 board
  • ESP32 board
  • STM32 board
  • Other - please specify

IDE

  • [X ] Arduino IDE
  • Arduino Pro IDE
  • Sloeber IDE
  • PlatformIO IDE
  • Other

Example expressing the bug

  • Simple
  • OneServo
  • TwoServos
  • AsymmetricEasing
  • [X ] PCA9685_Expander
  • PCA9685_ExpanderFor32Servos
  • ContinuousRotatingServo
  • Other

Pin(s) used for servo, if not default

46 from Mega 2560 and pins 5,6,7 on PCA Expander

Version

  • Yes I use the latest version
  • [ X] Other - please specify
    1.4.1.

Current behavior

// init code goes here.. includes and global variables and the like

const int pinArmServo = 46; //Arm Angle is driven from 2560 due to location in the body
//------Rest of the servos are in the head and on the PCA 9685 Expander------
const int pinTiltServo = 5; //Tilt Side-To-Side
const int pinNeckTurn = 6; //Turn Neck
const int pinHeadNod = 7; //Nod Up-And-Down

ServoEasing ArmServo; //Not going to the PCA9685
ServoEasing HeadTiltServo(PCA9685_DEFAULT_ADDRESS, &Wire);
ServoEasing NeckTurnServo(PCA9685_DEFAULT_ADDRESS, &Wire);
ServoEasing HeadNodServo(PCA9685_DEFAULT_ADDRESS, &Wire);
}

struct ServoControlStruct {
uint16_t minDegree;
uint16_t maxDegree;
uint16_t center;
};

//-=-=-=-Structure for easier to read code-=-=-
ServoControlStruct ArmServoControl;
ServoControlStruct NeckTurnServoControl;
ServoControlStruct HeadNodServoControl;
ServoControlStruct HeadTiltServoControl;

//-=-=- Head servo targets-=-=- main loop will set these according to IMU and user inputs
uint8_t tNewArmPosition = 90;
uint8_t tNewHorizontal = 90;
uint8_t tNewVertical = 90;
uint8_t tNewTilt = 90;
uint16_t armCenter = 100;
uint16_t neckCenter = 90;
uint16_t nodCenter = 90;
uint16_t tiltCenter = 90;

void setup(){

Serial.begin(115200);
while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy etc
if (Usb.Init() == -1) {
Serial.print(F("\r\nOSC did not start"));
while (1); //halt
}
Serial.print(F("\r\nPS3 Bluetooth Library Started"));
setupServos();
}

void loop(){
////code to read PS3 inputs and grab Information to send to servos
// it sets values for tNewHorizontal, tNewArmPosition, tNewVertical, tNewTilt
//...

//The ArmServo doesn't move. All the servos connected to the PCA expander work very smoothly with the PS3 control inputs.

if (!ArmServo.isMoving()) {
ArmServo.setEaseTo(tNewArmPosition);
synchronizeAllServosAndStartInterrupt();
}
if (!NeckTurnServo.isMoving()) {
NeckTurnServo.setEaseTo(tNewHorizontal);
synchronizeAllServosAndStartInterrupt();
}
if (!HeadNodServo.isMoving()) {
HeadNodServo.setEaseTo(tNewVertical);
synchronizeAllServosAndStartInterrupt();
}
if (!HeadTiltServo.isMoving()) {
HeadTiltServo.setEaseTo(tNewTilt);
synchronizeAllServosAndStartInterrupt();
}

}

void setupServos(){
pinMode(pinArmServo, OUTPUT);
ArmServo.attach(pinArmServo);
HeadTiltServo.attach(pinTiltServo);
NeckTurnServo.attach(pinNeckTurn);
HeadNodServo.attach(pinHeadNod);

ArmServoControl.minDegree = 70;
ArmServoControl.maxDegree = 130;
ArmServoControl.center = armCenter;
NeckTurnServoControl.minDegree = 10;   
NeckTurnServoControl.maxDegree = 170;  
NeckTurnServoControl.center = neckCenter;
HeadNodServoControl.minDegree = 50;     
HeadNodServoControl.maxDegree = 140;   
HeadNodServoControl.center = nodCenter;
HeadTiltServoControl.minDegree = 35;  
HeadTiltServoControl.maxDegree = 155; 
HeadTiltServoControl.center = tiltCenter;

//ArmServo.write(0);
NeckTurnServo.write(NeckTurnServoControl.center);
HeadNodServo.write(HeadNodServoControl.center);
HeadTiltServo.write(HeadTiltServoControl.center);


// Wait for servos to reach start position.
delay(500);

}

Expected behavior

Have all servos respond to PS3 control inputs

Error output

None. No errors displayed.

Additional context

setDegreeForAllServos parses parameters (degrees) as int, not float

Hi and thanks for the library!

Something weird happens wen I try to use setDegreesForAllServos and this causes my servos to fly all over the place.
If I assign the ServoEasingNextPositionArray-elements one at a time directly, everything works as expected.
ServoEasing::ServoEasingNextPositionArray[i] = abs(theta[i + 1])
etc.

But if I assign them using setDegreeForAllServos, and then query the values, I get unexpected results.

Serial.printf("DE %f %f %f %f %f %f\n", abs(theta[1]), abs(180 - theta[2]), abs(theta[3]), abs(theta[4] + 90), abs(theta[5] + 90), abs(theta[6]));
setDegreeForAllServos(6, abs(theta[1]), abs(180 - theta[2]), abs(theta[3]), abs(theta[4] + 90), abs(theta[5] + 90), abs(theta[6]));
Serial.printf("SE %f %f %f %f %f %f\n", ServoEasing::ServoEasingNextPositionArray[0], ServoEasing::ServoEasingNextPositionArray[1].. )

Output:
DE 92.000000 115.000000 3.000000 90.000000 10.000000 16.000000
SE 1074266112.000000 0.000000 1079443456.000000 0.000000 1079820288.000000 0.000000

For even more interesting results:

std::array<float, THETAS> theta = {0, 92, 115, 3, 90,10, 16}; // [0] not in use
Serial.printf("DE %f %f %f %f %f %f\n", abs(theta[1]), abs(180 - theta[2]), abs(theta[3]), abs(theta[4] + 90), abs(theta[5] + 90), abs(theta[6]));
setDegreeForAllServos(6, abs(theta[1]), 180, (float)abs(theta[3]), theta[4], 90.0f, abs(theta[6]));
ServoEasing::ServoEasingNextPositionArray[0] = abs(theta[1]);
Serial.printf("SE %f %f %f %f %f %f\n", ServoEasing::ServoEasingNextPositionArray[0], ServoEasing::ServoEasingNextPositionArray[1], ServoEasing::ServoEasingNextPositionArray[2], ServoEasing::ServoEasingNextPositionArray[3], ServoEasing::ServoEasingNextPositionArray[4], ServoEasing::ServoEasingNextPositionArray[5]);

Output:

DE 92.000000 115.000000 3.000000 90.000000 10.000000 16.000000
SE 92.000000 0.000000 1079443456.000000 180.000000 1073421888.000000 0.000000

setDegreeForAllServos(6, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f);

Does not work either. So an issue with va_arg.. ?

Board

  • ESP32 board

IDE

  • PlatformIO IDE

Remove power from servo

Issues are expected to lead to changes in the repository, like code or documentation improvements or bug fixes.

If you have handling problems or questions, consider to open a discussion instead of an issue.
I am trying to remove all signals to a servo after I move it. In Servo Easing 1.4.1, I used the .detach() function and it seemed to depower the servos until I moved them again. In current version the servos always buzz when I am not trying to move them. I am using three PCA 9685 servo expanders with an Arduino Mega 2560.

Bug Report

Board

  • [X ] Arduino ATmega2560 board (Mega)

IDE

  • [ X] Arduino IDE

Example to reproduce the issue

Version

Please delete all unchecked lines above :-)

Pin(s) used for servo, if not default

Current behavior

Expected behavior

Additional context

SAMD- Platform enhancement. Trying what I think is basic example and it does not seem to move the servo

Hey!

So I am trying to use the library to move the servo in a non-blocking manner. I was able to get the blocking code of easeTo to work but when I changed to startEaseTo it did not seem to work. Here is is the code I am testing.

#include <Arduino.h>

#include "ServoEasing.h"

#define VERSION_EXAMPLE "1.4"

#define INFO // to see serial output of loop
const int SERVO1_PIN = 9;
const int SERVO1_MAX = 170;
const int SERVO1_MIN = 110;
#if !defined(LED_BUILTIN) && !defined(ESP32)
#define LED_BUILTIN PB1
#endif
ServoEasing Servo1;

void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
#if defined(AVR_ATmega32U4)
while (!Serial); //delay for Leonardo, but this loops forever for Maple Serial
#endif
#if defined(SERIAL_USB)
delay(2000); // To be able to connect Serial monitor after reset and before first printout
#endif
// Just to know which program is running on my Arduino
Serial.println(F("START " FILE "\r\nVersion " VERSION_EXAMPLE " from " DATE));

// Attach servo to pin
Serial.print(F("Attach servo at pin "));
Serial.println(SERVO1_PIN);
if (Servo1.attach(SERVO1_PIN) == INVALID_SERVO) {
    Serial.println(F("Error attaching servo"));
}

/**************************************************
 * Set servos to start position.
 * This is the position where the movement starts.
 *************************************************/
Serial.print(F("before first position "));
Servo1.write(SERVO1_MIN);
Serial.print(F("after first position "));
// Wait for servo to reach start position.
delay(500);

}

void blinkLED() {
digitalWrite(LED_BUILTIN, HIGH);
delay(100);
digitalWrite(LED_BUILTIN, LOW);
delay(100);
}

void loop() {
// put your main code here, to run repeatedly:
//Servo1.setSpeed(10); // This speed is taken if no further speed argument is given.
Servo1.setEasingType(EASE_CUBIC_IN_OUT);
Serial.print(F("before first ease "));
Servo1.startEaseTo(SERVO1_MAX, 50);
while (areInterruptsActive()) {
Serial.print(F("in easing "));
; // wait for servo to stop
}

Serial.print(F("after first ease "));
delay(7000);
// Servo1.startEaseTo(SERVO1_MIN, 50);
// delay(7000);

}`

I updated to the latest 1.5.2 and do not get any errors on compiling

Linking error when library is used outside of main sketch (2.4.0)

Bug Report

Version

  • 2.4.0

Current behavior

The switch to header-only library in release 2.4.0 completely broke compilation for me.

Context: I use the library in my own class "MyClass". "MyClass" is defined in file MyClass.h, which includes ServoEasing.hpp. Member function definition is in MyClass.cpp. MyClass.h is included in the main sketch, where MyClass is used.

The problem: After updating to 2.4.0 and changing #include "ServoEasing.h" to #include "ServoEasing.hpp" in MyClass.h my project compiles just fine, but then I get a ton of the following linker errors (only one error pasted here as an example, the same is repeated for all functions in ServoEasing.hpp, both free functions and ServoEasing class member functions):

.pio\build\uno\src\main.cpp.o (symbol from plugin): In function `QuadraticEaseIn(float)':
(.text+0x0): multiple definition of `QuadraticEaseIn(float)'
.pio\build\uno\src\MyClass.cpp.o (symbol from plugin):(.text+0x0): first defined here

This is due to the One Definiton Rule, which is a common issue for header-only libraries. The errors happen because the library code is included separately in two different translation units (directly in MyClass.cpp, and through MyClass.h in main.cpp), which results in the multiple definition error during linking.

Expected behavior

Library should compile and link correctly when used in custom files. For the moment I will just use 2.3.4, but I only see two possible long term solutions here:

  • Revert back from *.hpp to to *.cpp files (I'm actually curious here, what was the reason for the switch to a header-only library?)
  • Make all ServoEasing.hpp functions inline, as suggested here

I2C-PCA9685 detection failed / isMovingAndCallYield() crashes

Your library is great and I use it on the ESP32 together with the PCA9685 (1x)
and up to 6 servos attached. First of all: The servos already working as expected.
(Arduino 1.8.10, lastest ESP-Pack, lastest ServoEasing library)

But I've found two minor problems in "PCA9685_Expander.ino" ... I hope that's not my fault:
(1)
"Error: Communication with I2C was successful, but found no I2C device attached at address: 0x40"
Cause: Wire.endTransmission() returns always 1: data too long to fit in transmit buffer
But as I said above: The servos are correctly moving afterwards ...
(2)
OK: while (Servo2.isMoving()) { blinkLED(); }
Crash: while (Servo1.isMovingAndCallYield()) { } (Immediatly, if called)

Any ideas ?

No timer for Teensy 4.1

Bug Report

Arduino Platform

  • Arduino ATmega328* board (UNO, Nano)
  • Arduino ATmega2560 board (Mega)
  • Arduino ATmega32U4 board (Leonardo)
  • Arduino SAM board (Due)
  • Arduino SAMD board (Zero, MKR*)
  • ESP8266 board
  • ESP32 board
  • STM32 board
  • [x ] Other - please specify

IDE

  • Arduino IDE
  • Arduino Pro IDE
  • Sloeber IDE
  • PlatformIO IDE
  • [ x] Other

Example expressing the bug

  • Simple
  • OneServo
  • TwoServos
  • AsymmetricEasing
  • [x ] PCA9685_Expander
  • PCA9685_ExpanderFor32Servos
  • ContinuousRotatingServo
  • Other

Pin(s) used for servo, if not default

Version

  • [ x] Yes I use the latest version
  • Other - please specify

Current behavior

"No periodic timer support existent (or known) for this platform. Only blocking functions and simple example will run!"

Expected behavior

Error output

Additional context

ESP8266 pin definitions

Bug Report

Arduino Platform

  • ESP8266 board

  • Platform Servo1 Servo2 Servo3 Analog


  • ESP8266 14 // D5 12 // D6 13 // D7 0
    */

#if defined(ESP8266)
#define SERVO1_PIN 12 // D6
#define SERVO2_PIN 13 // D7
#define SERVO3_PIN 14 // D5

Shouldn't it be:
SERVO1_PIN 14 // D5
SERVO2_PIN 12 // D6
SERVO3_PIN 13 // D7
to agree with the table?

Detaching of the only servo

Bug Report

Arduino Platform

  • All

IDE

  • Arduino IDE

Version

  • Yes I use the latest version

Current behavior

#include "ServoEasing.h"
ServoEasing Servo1;
void setup () {
  Serial.begin(115200);
  delay(1000);
  
  Servo1.attach(D5);
  Servo1.detach();
  Servo1.attach(D5);
  Servo1.write(0);
  Servo1.startEaseTo(180);
}
void loop () {
}

This code causes an 28 exception ( LoadProhibitedCause )

Expected behavior

Move servo to 180

Error output

Decoding stack results
0x40201951: ServoEasing::update() at d:\��� ���㬥���\Arduino\libraries\ServoEasing-master\src\ServoEasing.cpp line 856
0x402019b4: updateAllServos() at d:\��� ���㬥���\Arduino\libraries\ServoEasing-master\src\ServoEasing.cpp line 1482
0x402019d8: handleServoTimerInterrupt() at d:\��� ���㬥���\Arduino\libraries\ServoEasing-master\src\ServoEasing.cpp line 1089
0x402042ba: std::_Function_handler ::_M_invoke(std::_Any_data const&) at c:\users\sergs\appdata\local\arduino15\packages\esp8266\tools\xtensa-lx106-elf-gcc\2.5.0-4-b40a506\xtensa-lx106-elf\include\c++\4.8.2/functional line 2073
0x40204332: Ticker::_static_callback(void*) at C:\Users\sergS\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.7.1\libraries\Ticker\src\Ticker.cpp line 71
0x4020250c: loop_task(ETSEvent*) at C:\Users\sergS\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.7.1\cores\esp8266\core_esp8266_main.cpp line 205
0x40202bbd: startWaveformClockCycles(uint8_t, uint32_t, uint32_t, uint32_t) at C:\Users\sergS\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.7.1\cores\esp8266\core_esp8266_waveform.cpp line 146
0x40201dee: Ticker::_attach_ms(unsigned int, bool, void (*)(void*), void*) at C:\Users\sergS\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.7.1\libraries\Ticker\src\Ticker.cpp line 49
0x4020359d: uart_flush(uart_t*) at C:\Users\sergS\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.7.1\cores\esp8266\uart.cpp line 544
0x401004e8: millis() at C:\Users\sergS\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.7.1\cores\esp8266\core_esp8266_wiring.cpp line 188
0x40201a5a: enableServoEasingInterrupt() at d:\��� ���㬥���\Arduino\libraries\ServoEasing-master\src\ServoEasing.cpp line 1224
0x40201ae0: ServoEasing::startEaseToD(int, unsigned int, bool) at d:\��� ���㬥���\Arduino\libraries\ServoEasing-master\src\ServoEasing.cpp line 707
0x40201b60: ServoEasing::startEaseTo(int, unsigned int, bool) at d:\��� ���㬥���\Arduino\libraries\ServoEasing-master\src\ServoEasing.cpp line 687
0x40100154: ets_post(uint8, ETSSignal, ETSParam) at C:\Users\sergS\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.7.1\cores\esp8266\core_esp8266_main.cpp line 177
0x40100175: esp_schedule() at C:\Users\sergS\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.7.1\cores\esp8266\core_esp8266_main.cpp line 125
0x402026bd: loop_wrapper() at C:\Users\sergS\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.7.1\cores\esp8266\core_esp8266_main.cpp line 199

Possible solving

Replace ServoEasing.cpp:393

            do {
                sServoArrayMaxIndex--;

                if (sServoArrayMaxIndex == 0) {
                    break;
                }
            } while (sServoArray[sServoArrayMaxIndex] == NULL);

with the

            while (sServoArrayMaxIndex > 0 && sServoArray[sServoArrayMaxIndex] == NULL) {
                sServoArrayMaxIndex--;
            }

or

            do {
                if (sServoArrayMaxIndex == 0) {
                    break;
                }

                sServoArrayMaxIndex--;
            } while (sServoArray[sServoArrayMaxIndex] == NULL);

if you wish

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.