├── .clang-format ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── examples ├── 01-SimpleServo │ └── 01-SimpleServo.ino ├── 02-ServoPotentiometer │ └── 02-ServoPotentiometer.ino ├── 03-MultipleServos │ └── 03-MultipleServos.ino ├── 04-SimpleServoAngles │ └── 04-SimpleServoAngles.ino └── 05-SimpleServoRadians │ └── 05-SimpleServoRadians.ino ├── library.json ├── library.properties ├── platformio.ini └── src ├── Servo.cpp └── Servo.h /.clang-format: -------------------------------------------------------------------------------- 1 | # Google C/C++ Code Style settings 2 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html 3 | # Author: Kehan Xue, kehan.xue (at) gmail.com 4 | 5 | Language: Cpp 6 | BasedOnStyle: Google 7 | AccessModifierOffset: -1 8 | AlignAfterOpenBracket: Align 9 | AlignConsecutiveAssignments: None 10 | AlignOperands: Align 11 | AllowAllArgumentsOnNextLine: true 12 | AllowAllConstructorInitializersOnNextLine: true 13 | AllowAllParametersOfDeclarationOnNextLine: false 14 | AllowShortBlocksOnASingleLine: Empty 15 | AllowShortCaseLabelsOnASingleLine: false 16 | AllowShortFunctionsOnASingleLine: Inline 17 | AllowShortIfStatementsOnASingleLine: Never # To avoid conflict, set this "Never" and each "if statement" should include brace when coding 18 | AllowShortLambdasOnASingleLine: Inline 19 | AllowShortLoopsOnASingleLine: false 20 | AlwaysBreakAfterReturnType: None 21 | AlwaysBreakTemplateDeclarations: Yes 22 | BinPackArguments: true 23 | BreakBeforeBraces: Custom 24 | BraceWrapping: 25 | AfterCaseLabel: false 26 | AfterClass: false 27 | AfterStruct: false 28 | AfterControlStatement: Never 29 | AfterEnum: false 30 | AfterFunction: false 31 | AfterNamespace: false 32 | AfterUnion: false 33 | AfterExternBlock: false 34 | BeforeCatch: false 35 | BeforeElse: false 36 | BeforeLambdaBody: false 37 | IndentBraces: false 38 | SplitEmptyFunction: false 39 | SplitEmptyRecord: false 40 | SplitEmptyNamespace: false 41 | BreakBeforeBinaryOperators: None 42 | BreakBeforeTernaryOperators: true 43 | BreakConstructorInitializers: BeforeColon 44 | BreakInheritanceList: BeforeColon 45 | ColumnLimit: 120 46 | CompactNamespaces: false 47 | ContinuationIndentWidth: 4 48 | Cpp11BracedListStyle: true 49 | DerivePointerAlignment: false # Make sure the * or & align on the left 50 | EmptyLineBeforeAccessModifier: LogicalBlock 51 | FixNamespaceComments: true 52 | IncludeBlocks: Preserve 53 | IndentCaseLabels: true 54 | IndentPPDirectives: None 55 | IndentWidth: 4 56 | KeepEmptyLinesAtTheStartOfBlocks: true 57 | MaxEmptyLinesToKeep: 1 58 | NamespaceIndentation: None 59 | ObjCSpaceAfterProperty: false 60 | ObjCSpaceBeforeProtocolList: true 61 | PointerAlignment: Left 62 | ReflowComments: false 63 | # SeparateDefinitionBlocks: Always # Only support since clang-format 14 64 | SpaceAfterCStyleCast: false 65 | SpaceAfterLogicalNot: false 66 | SpaceAfterTemplateKeyword: true 67 | SpaceBeforeAssignmentOperators: true 68 | SpaceBeforeCpp11BracedList: false 69 | SpaceBeforeCtorInitializerColon: true 70 | SpaceBeforeInheritanceColon: true 71 | SpaceBeforeParens: ControlStatements 72 | SpaceBeforeRangeBasedForLoopColon: true 73 | SpaceBeforeSquareBrackets: false 74 | SpaceInEmptyParentheses: false 75 | SpacesBeforeTrailingComments: 2 76 | SpacesInAngles: false 77 | SpacesInCStyleCastParentheses: false 78 | SpacesInContainerLiterals: false 79 | SpacesInParentheses: false 80 | SpacesInSquareBrackets: false 81 | Standard: c++11 82 | TabWidth: 4 83 | UseTab: Never -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Continuous Integration (CI) is the practice, in software 2 | # engineering, of merging all developer working copies with a shared mainline 3 | # several times a day < http://docs.platformio.org/page/ci/index.html > 4 | # 5 | # Documentation: 6 | # 7 | # * Travis CI Embedded Builds with PlatformIO 8 | # < https://docs.travis-ci.com/user/integration/platformio/ > 9 | # 10 | # * PlatformIO integration with Travis CI 11 | # < http://docs.platformio.org/page/ci/travis.html > 12 | # 13 | # * User Guide for `platformio ci` command 14 | # < http://docs.platformio.org/page/userguide/cmd_ci.html > 15 | # 16 | # 17 | # Please choice one of the following templates (proposed below) and uncomment 18 | # it (remove "# " before each line) or use own configuration according to the 19 | # Travis CI documentation (see above). 20 | # 21 | 22 | 23 | # 24 | # Template #1: General project. Test it using existing `platformio.ini`. 25 | # 26 | 27 | # language: python 28 | # python: 29 | # - "2.7" 30 | # 31 | # sudo: false 32 | # cache: 33 | # directories: 34 | # - "~/.platformio" 35 | # 36 | # install: 37 | # - pip install -U platformio 38 | # - platformio update 39 | # 40 | # script: 41 | # - platformio run 42 | 43 | 44 | # 45 | # Template #2: The project is intended to by used as a library with examples 46 | # 47 | 48 | language: python 49 | python: 50 | - "2.7" 51 | 52 | sudo: false 53 | cache: 54 | directories: 55 | - "~/.platformio" 56 | 57 | env: 58 | - PLATFORMIO_CI_SRC=examples/01-SimpleServo 59 | - PLATFORMIO_CI_SRC=examples/02-ServoPotentiometer 60 | - PLATFORMIO_CI_SRC=examples/03-MultipleServos 61 | - PLATFORMIO_CI_SRC=examples/04-SimpleServoAngles 62 | 63 | install: 64 | - pip install -U platformio 65 | - platformio update 66 | 67 | script: 68 | - platformio ci --lib="." --project-conf="./platformio.ini" 69 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 RoboticsBrno (RobotikaBrno) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ServoESP32 [![Build Status](https://travis-ci.com/RoboticsBrno/ServoESP32.svg?branch=master)](https://travis-ci.com/RoboticsBrno/ServoESP32) 2 | 3 | Generate RC servo signal on a selected pins with ESP32 device and Arduino framework. 4 | 5 | Base on [servo library for stm32f4 (d2a4a47)](https://github.com/arduino-libraries/Servo/blob/master/src/stm32f4/ServoTimers.h). 6 | 7 | ## Interface 8 | 9 | The interface is similar to Arduino/Servo: https://www.arduino.cc/en/Reference/Servo 10 | 11 | But the function `attach()` is different: 12 | 13 | ```c 14 | bool attach( 15 | int pin, 16 | int channel = CHANNEL_NOT_ATTACHED, 17 | int minAngle = DEFAULT_MIN_ANGLE, 18 | int maxAngle = DEFAULT_MAX_ANGLE, 19 | int minPulseWidthUs = DEFAULT_MIN_PULSE_WIDTH_US, 20 | int maxPulseWidthUs = DEFAULT_MAX_PULSE_WIDTH_US, 21 | int frequency = DEFAULT_FREQUENCY 22 | ); 23 | ``` 24 | 25 | More information in [source code documentation](src/Servo.h). 26 | 27 | Example: [04-SimpleServoAngles](examples/04-SimpleServoAngles/04-SimpleServoAngles.ino) 28 | 29 | There are also a ServoFloat and ServoDouble variant available. Use one of these when working in radians. 30 | 31 | Example: : [05-SimpleServoRadians](examples/05-SimpleServoRadians/05-SimpleServoRadians.ino) 32 | 33 | ### IMPORTANT INFO 34 | According testings, the frequency for ESP32 S2/S3/C3 has to be set at least to 200 Hz. Here is an example, how to set just frequency: 35 | 36 | ```cpp 37 | Servo servo1; 38 | const int servoPin = 4; 39 | const int frequency = 200; // Hz 40 | 41 | servo1.attach( 42 | servoPin, 43 | Servo::CHANNEL_NOT_ATTACHED, 44 | Servo::DEFAULT_MIN_ANGLE, 45 | Servo::DEFAULT_MAX_ANGLE, 46 | Servo::DEFAULT_MIN_PULSE_WIDTH_US, 47 | Servo::DEFAULT_MAX_PULSE_WIDTH_US, 48 | frequency 49 | ); 50 | ``` 51 | 52 | For more information look at the [PR25](https://github.com/RoboticsBrno/ServoESP32/pull/25) 53 | 54 | ## PlatformIO 55 | 56 | This library is also available at the [PlatformIO](https://platformio.org) as [ServoESP32](https://platformio.org/lib/show/1739/ServoESP32). 57 | 58 | ## Arduino IDE 59 | 60 | This library is available in Arduino IDE Library Manager as `ServoESP32`. 61 | 62 | ## Known issues 63 | 64 | ### Problem with build in Arduino IDE 1.8.10 65 | 66 | There was an [issue](https://github.com/arduino/arduino-cli/pull/565) with building this library in Arduino IDE 1.8.10. But this issue should be fixed in Arduino IDE 1.8.11. 67 | -------------------------------------------------------------------------------- /examples/01-SimpleServo/01-SimpleServo.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static const int servoPin = 4; 4 | 5 | Servo servo1; 6 | 7 | void setup() { 8 | Serial.begin(115200); 9 | servo1.attach(servoPin); 10 | } 11 | 12 | void loop() { 13 | for(int posDegrees = 0; posDegrees <= 180; posDegrees++) { 14 | servo1.write(posDegrees); 15 | Serial.println(posDegrees); 16 | delay(20); 17 | } 18 | 19 | for(int posDegrees = 180; posDegrees >= 0; posDegrees--) { 20 | servo1.write(posDegrees); 21 | Serial.println(posDegrees); 22 | delay(20); 23 | } 24 | } -------------------------------------------------------------------------------- /examples/02-ServoPotentiometer/02-ServoPotentiometer.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static const int servoPin = 4; 4 | static const int potentiometerPin = 32; 5 | 6 | Servo servo1; 7 | 8 | void setup() { 9 | Serial.begin(115200); 10 | servo1.attach(servoPin); 11 | } 12 | 13 | void loop() { 14 | int servoPosition = map(analogRead(potentiometerPin), 0, 4096, 0, 180); 15 | servo1.write(servoPosition); 16 | Serial.println(servoPosition); 17 | delay(20); 18 | } 19 | -------------------------------------------------------------------------------- /examples/03-MultipleServos/03-MultipleServos.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static const int servosPins[5] = {4, 16, 18, 19, 21}; 4 | 5 | Servo servos[5]; 6 | 7 | void setServos(int degrees) { 8 | for(int i = 0; i < 5; ++i) { 9 | servos[i].write((degrees + (35 * i)) % 180); 10 | } 11 | } 12 | 13 | void setup() { 14 | Serial.begin(115200); 15 | 16 | for(int i = 0; i < 5; ++i) { 17 | if(!servos[i].attach(servosPins[i])) { 18 | Serial.print("Servo "); 19 | Serial.print(i); 20 | Serial.println("attach error"); 21 | } 22 | } 23 | } 24 | 25 | void loop() { 26 | for(int posDegrees = 0; posDegrees <= 180; posDegrees++) { 27 | setServos(posDegrees); 28 | Serial.println(posDegrees); 29 | delay(20); 30 | } 31 | 32 | for(int posDegrees = 180; posDegrees >= 0; posDegrees--) { 33 | setServos(posDegrees); 34 | Serial.println(posDegrees); 35 | delay(20); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/04-SimpleServoAngles/04-SimpleServoAngles.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | * Description: 5 | * Example for setting the minimal and maximal angle. 6 | */ 7 | 8 | static const int servoPin = 4; 9 | 10 | Servo servo1; 11 | 12 | void setup() { 13 | Serial.begin(115200); 14 | servo1.attach( 15 | servoPin, 16 | Servo::CHANNEL_NOT_ATTACHED, 17 | 45, 18 | 120 19 | ); 20 | } 21 | 22 | void loop() { 23 | for(int posDegrees = 0; posDegrees <= 180; posDegrees++) { 24 | servo1.write(posDegrees); 25 | Serial.println(posDegrees); 26 | delay(20); 27 | } 28 | 29 | for(int posDegrees = 180; posDegrees >= 0; posDegrees--) { 30 | servo1.write(posDegrees); 31 | Serial.println(posDegrees); 32 | delay(20); 33 | } 34 | } -------------------------------------------------------------------------------- /examples/05-SimpleServoRadians/05-SimpleServoRadians.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | * Description: 5 | * Example for using floating point angle and radians. 6 | */ 7 | 8 | static const int servoPin = 4; 9 | 10 | ServoFloat servo1; 11 | 12 | float deg2rad(float in) { 13 | return in * M_PI / 180.0; 14 | } 15 | 16 | const float minAngle = deg2rad(45.0); 17 | const float maxAngle = deg2rad(120.0); 18 | const float stepAngle = deg2rad(1.0); 19 | 20 | void setup() { 21 | Serial.begin(115200); 22 | servo1.attach(servoPin, Servo::CHANNEL_NOT_ATTACHED, minAngle, maxAngle); 23 | } 24 | 25 | void loop() { 26 | for (float angleRadians = minAngle; angleRadians <= maxAngle; angleRadians += stepAngle) { 27 | servo1.write(angleRadians); 28 | Serial.println(angleRadians); 29 | delay(20); 30 | } 31 | 32 | for (float angleRadians = maxAngle; angleRadians >= minAngle; angleRadians -= stepAngle) { 33 | servo1.write(angleRadians); 34 | Serial.println(angleRadians); 35 | delay(20); 36 | } 37 | } -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ServoESP32", 3 | "keywords": "servo, esp32", 4 | "description": "Generate RC servo signal on a selected pins with ESP32 device and Arduino framework.", 5 | "homepage": "https://github.com/RoboticsBrno/ServoESP32/", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/RoboticsBrno/ServoESP32.git" 10 | }, 11 | "authors": { 12 | "name": "Jaroslav Paral", 13 | "email": "paral@robotikabrno.cz", 14 | "url": "http://www.robotikabrno.cz", 15 | "maintainer": true 16 | }, 17 | "version": "1.1.1", 18 | "frameworks": "arduino", 19 | "platforms": "espressif32" 20 | } -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ServoESP32 2 | version=1.1.1 3 | author=Jaroslav Paral 4 | maintainer=Jaroslav Paral 5 | sentence=Generate RC servo signal on a selected pins with ESP32 device and Arduino framework. 6 | paragraph= 7 | category=Device Control 8 | url=https://github.com/RoboticsBrno/ServoESP32/ 9 | architectures=esp32 10 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | [env:esp32dev] 12 | platform = espressif32 13 | board = esp32dev 14 | framework = arduino 15 | 16 | upload_speed = 921600 17 | 18 | monitor_speed = 115200 19 | 20 | build_unflags = -std=gnu++11 21 | build_flags = 22 | -std=c++14 23 | -fmax-errors=5 24 | -------------------------------------------------------------------------------- /src/Servo.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * The MIT License 3 | * 4 | * Copyright (c) 2010, LeafLabs, LLC. 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | *****************************************************************************/ 26 | 27 | /** 28 | * Arduino srl - www.arduino.org 29 | * Base on lib for stm32f4 (d2a4a47): https://github.com/arduino-libraries/Servo/blob/master/src/stm32f4/ServoTimers.h 30 | * 2017 Jul 5: Edited by Jaroslav Páral (jarekparal) - paral@robotikabrno.cz 31 | */ 32 | 33 | // Implementation is in Servo.h 34 | #include 35 | 36 | int ServoBase::channel_next_free = 0; -------------------------------------------------------------------------------- /src/Servo.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * The MIT License 3 | * 4 | * Copyright (c) 2010, LeafLabs, LLC. 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, copy, 10 | * modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | *****************************************************************************/ 26 | 27 | /** 28 | * Arduino srl - www.arduino.org 29 | * Base on lib for stm32f4 (d2a4a47): 30 | * https://github.com/arduino-libraries/Servo/blob/master/src/stm32f4/ServoTimers.h 31 | * 2017 Jul 5: Edited by Jaroslav Páral (jarekparal) - paral@robotikabrno.cz 32 | */ 33 | 34 | // clang-format off 35 | #pragma once 36 | 37 | #include "Arduino.h" 38 | 39 | class ServoBase { 40 | protected: 41 | // The main purpose of ServoBase is to make sure that multiple instances of 42 | // ServoTemplate class with different types share channel_next_free. 43 | static int channel_next_free; 44 | }; 45 | 46 | template 47 | class ServoTemplate : public ServoBase { 48 | // From esp32-hal-ledc.c 49 | #ifdef SOC_LEDC_SUPPORT_HS_MODE 50 | #define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM << 1) 51 | #else 52 | #define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM) 53 | #endif 54 | public: 55 | /** 56 | * Default min/max pulse widths (in microseconds) and angles 57 | * (in degrees). Values chosen for Arduino compatibility. 58 | * These values are part of the public API; DO NOT CHANGE THEM. 59 | */ 60 | static constexpr int DEFAULT_MIN_ANGLE = 0; 61 | static constexpr int DEFAULT_MAX_ANGLE = 180; 62 | 63 | static const int DEFAULT_MIN_PULSE_WIDTH_US = 544; // the shortest pulse sent to a servo 64 | static const int DEFAULT_MAX_PULSE_WIDTH_US = 2400; // the longest pulse sent to a servo 65 | 66 | static const int DEFAULT_FREQUENCY = 50; 67 | 68 | static const int TIMER_RESOLUTION = std::min(16, SOC_LEDC_TIMER_BIT_WIDE_NUM); 69 | static const int PERIOD_TICKS = (1 << TIMER_RESOLUTION) - 1; 70 | 71 | static const int CHANNEL_NOT_ATTACHED = -1; 72 | 73 | // Pin number of unattached pins 74 | static const int PIN_NOT_ATTACHED = -1; 75 | 76 | /** 77 | * @brief Construct a new ServoTemplate instance. 78 | * 79 | * The new instance will not be attached to any pin. 80 | */ 81 | ServoTemplate() { _resetFields(); } 82 | 83 | /** 84 | * @brief Destruct a ServoTemplate instance. 85 | * 86 | * Call _() and detach(). 87 | */ 88 | ~ServoTemplate() { detach(); } 89 | 90 | /** 91 | * @brief Associate this instance with a servomotor whose input is 92 | * connected to pin. 93 | * @param pin Pin connected to the servo pulse width input. This 94 | * pin must be capable of PWM output (all ESP32 pins). 95 | * 96 | * @param channel Channel which is set to ESP32 Arduino function ledcSetup(). 97 | * Channel must be number between 0 - 15. 98 | * It is possible to use automatic channel setup with constant 99 | * Servo::CHANNEL_NOT_ATTACHED. 100 | * 101 | * @param minAngle Target angle (in degrees or radians) associated with 102 | * minPulseWidthUs. Defaults to DEFAULT_MIN_ANGLE = 0. 103 | * 104 | * @param maxAngle Target angle (in degrees or radians) associated with 105 | * maxPulseWidthUs. Defaults to DEFAULT_MAX_ANGLE = 180. 106 | * 107 | * @param minPulseWidthUs Minimum pulse width to write to pin, in 108 | * microseconds. This will be associated 109 | * with a minAngle angle. Defaults to 110 | * DEFAULT_MIN_PULSE_WIDTH_US = 544. 111 | * 112 | * @param maxPulseWidthUs Maximum pulse width to write to pin, in 113 | * microseconds. This will be associated 114 | * with a maxAngle angle. Defaults to 115 | * DEFAULT_MAX_PULSE_WIDTH_US = 2400. 116 | * 117 | * @param frequency Frequency in hz to send PWM at. 118 | * Defaults to DEFAULT_FREQUENCY. 119 | * 120 | * @sideeffect May set pinMode(pin, PWM). 121 | * 122 | * @return true if successful, false when pin doesn't support PWM. 123 | */ 124 | bool attach(int pin, int channel = CHANNEL_NOT_ATTACHED, T minAngle = DEFAULT_MIN_ANGLE, 125 | T maxAngle = DEFAULT_MAX_ANGLE, int minPulseWidthUs = DEFAULT_MIN_PULSE_WIDTH_US, 126 | int maxPulseWidthUs = DEFAULT_MAX_PULSE_WIDTH_US, int frequency = DEFAULT_FREQUENCY) { 127 | int tempPeriodUs = std::round(1000000.0 / frequency); 128 | if (tempPeriodUs <= maxPulseWidthUs) { 129 | return false; 130 | } 131 | if (channel == CHANNEL_NOT_ATTACHED) { 132 | if (channel_next_free == LEDC_CHANNELS) { 133 | return false; 134 | } 135 | _channel = channel_next_free; 136 | channel_next_free++; 137 | } else { 138 | _channel = channel; 139 | } 140 | 141 | _pin = pin; 142 | _minAngle = minAngle; 143 | _maxAngle = maxAngle; 144 | _minPulseWidthUs = minPulseWidthUs; 145 | _maxPulseWidthUs = maxPulseWidthUs; 146 | _periodUs = tempPeriodUs; 147 | 148 | ledcSetup(_channel, frequency, TIMER_RESOLUTION); 149 | ledcAttachPin(_pin, _channel); 150 | return true; 151 | } 152 | 153 | /** 154 | * @brief Stop driving the servo pulse train. 155 | * 156 | * If not currently attached to a motor, this function has no effect. 157 | * 158 | * @return true if this call did anything, false otherwise. 159 | */ 160 | bool detach() { 161 | if (!this->attached()) { 162 | return false; 163 | } 164 | 165 | if (_channel == (channel_next_free - 1)) 166 | channel_next_free--; 167 | 168 | ledcDetachPin(_pin); 169 | _pin = PIN_NOT_ATTACHED; 170 | return true; 171 | } 172 | 173 | /** 174 | * @brief Set the servomotor target angle. 175 | * 176 | * @param angle Target angle, in degrees or radians. If the target 177 | * angle is outside the range specified at attach() time, it 178 | * will be clamped to lie in that range. 179 | * 180 | * @see ServoTemplate::attach() 181 | */ 182 | void write(T angle) { 183 | angle = constrain(angle, _minAngle, _maxAngle); 184 | writeMicroseconds(_angleToUs(angle)); 185 | } 186 | 187 | /** 188 | * @brief Set the pulse width, in microseconds. 189 | * 190 | * @param pulseWidthUs Pulse width to send to the servomotor, in 191 | * microseconds. If outside of the range 192 | * specified at attach() time, it is clamped to 193 | * lie in that range. 194 | * 195 | * @see ServoTemplate::attach() 196 | */ 197 | void writeMicroseconds(int pulseWidthUs) { 198 | if (!attached()) { 199 | return; 200 | } 201 | pulseWidthUs = constrain(pulseWidthUs, _minPulseWidthUs, _maxPulseWidthUs); 202 | _pulseWidthTicks = _usToTicks(pulseWidthUs); 203 | ledcWrite(_channel, _pulseWidthTicks); 204 | } 205 | 206 | /** 207 | * Get the servomotor's target angle, in degrees or radians. This will 208 | * lie inside the range specified at attach() time. 209 | * 210 | * @see ServoTemplate::attach() 211 | */ 212 | T read() const { return _usToAngle(readMicroseconds()); } 213 | 214 | /** 215 | * Get the current pulse width, in microseconds. This will 216 | * lie within the range specified at attach() time. 217 | * 218 | * @see ServoTemplate::attach() 219 | */ 220 | int readMicroseconds() const { 221 | if (!this->attached()) { 222 | return 0; 223 | } 224 | int duty = ledcRead(_channel); 225 | return _ticksToUs(duty); 226 | } 227 | 228 | /** 229 | * @brief Check if this instance is attached to a servo. 230 | * @return true if this instance is attached to a servo, false otherwise. 231 | * @see ServoTemplate::attachedPin() 232 | */ 233 | bool attached() const { return _pin != PIN_NOT_ATTACHED; } 234 | 235 | /** 236 | * @brief Get the pin this instance is attached to. 237 | * @return Pin number if currently attached to a pin, PIN_NOT_ATTACHED 238 | * otherwise. 239 | * @see ServoTemplate::attach() 240 | */ 241 | int attachedPin() const { return _pin; } 242 | 243 | private: 244 | void _resetFields(void) { 245 | _pin = PIN_NOT_ATTACHED; 246 | _pulseWidthTicks = 0; 247 | _channel = CHANNEL_NOT_ATTACHED; 248 | _minAngle = DEFAULT_MIN_ANGLE; 249 | _maxAngle = DEFAULT_MAX_ANGLE; 250 | _minPulseWidthUs = DEFAULT_MIN_PULSE_WIDTH_US; 251 | _maxPulseWidthUs = DEFAULT_MAX_PULSE_WIDTH_US; 252 | _periodUs = 1000000 / DEFAULT_FREQUENCY; 253 | } 254 | 255 | T mapTemplate(T x, T in_min, T in_max, T out_min, T out_max) const { 256 | if constexpr (std::is_floating_point_v) { 257 | return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; 258 | } else { 259 | // Use normal map with integers, because extra care is needed. 260 | return map(x, in_min, in_max, out_min, out_max); 261 | } 262 | } 263 | 264 | int _usToTicks(int us) const { return std::round((PERIOD_TICKS * us) / _periodUs); } 265 | int _ticksToUs(int duty) const { return std::round((_periodUs * duty) / PERIOD_TICKS); } 266 | T _usToAngle(int us) const { return mapTemplate((T)us, (T)_minPulseWidthUs, (T)_maxPulseWidthUs, _minAngle, _maxAngle); } 267 | int _angleToUs(T angle) const { 268 | return (int)mapTemplate(angle, _minAngle, _maxAngle, _minPulseWidthUs, _maxPulseWidthUs); 269 | } 270 | 271 | int _pin; 272 | int _pulseWidthTicks; 273 | int _channel; 274 | int _minPulseWidthUs, _maxPulseWidthUs; 275 | T _minAngle, _maxAngle; 276 | int _periodUs; 277 | }; 278 | 279 | // For backwards compatability, naming the int version simply "Servo" allow 280 | // users to upgrade library without complications. 281 | using Servo = ServoTemplate; 282 | 283 | // Use ServoFloat for float precision 284 | using ServoFloat = ServoTemplate; 285 | 286 | // Use ServoDouble for double precision 287 | using ServoDouble = ServoTemplate; 288 | --------------------------------------------------------------------------------