feat: 更新ESP32Servo库
This commit is contained in:
1
arduino-cli/libraries/ESP32Servo/.gitignore
vendored
Normal file
1
arduino-cli/libraries/ESP32Servo/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/.project
|
||||||
32
arduino-cli/libraries/ESP32Servo/AddDoxygenToGHPages.sh
Normal file
32
arduino-cli/libraries/ESP32Servo/AddDoxygenToGHPages.sh
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
GITURL=$(git config --get remote.origin.url)
|
||||||
|
|
||||||
|
echo $GITURL
|
||||||
|
|
||||||
|
rm -rf html
|
||||||
|
git clone $GITURL html
|
||||||
|
cd html
|
||||||
|
if ( git checkout origin/gh-pages -b gh-pages) then
|
||||||
|
echo "Checked out $GITURL gh-pages"
|
||||||
|
else
|
||||||
|
echo "Creating out $GITURL gh-pages"
|
||||||
|
git checkout origin/master -b gh-pages
|
||||||
|
rm -r *
|
||||||
|
echo "# A simple README file for the gh-pages branch" > README.md
|
||||||
|
git add README.md
|
||||||
|
git commit -m"Replaced gh-pages html with simple readme"
|
||||||
|
git push -u origin gh-pages
|
||||||
|
fi
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
doxygen doxy.doxyfile
|
||||||
|
|
||||||
|
cd html
|
||||||
|
git add *
|
||||||
|
git add search/*
|
||||||
|
git commit -a -m"updating the doxygen"
|
||||||
|
git push
|
||||||
|
cd ..
|
||||||
|
rm -rf html
|
||||||
|
git checkout master
|
||||||
@@ -1,11 +1,18 @@
|
|||||||
= Servo Library for ESP32 =
|
# Servo Library for ESP32
|
||||||
|
|
||||||
|
Specifically for the V3.0.0 of Arduino ESP32. All ADC's have been updated to work correctly with the new release
|
||||||
|
|
||||||
|
https://github.com/espressif/arduino-esp32/releases
|
||||||
|
|
||||||
This library attempts to faithfully replicate the semantics of the
|
This library attempts to faithfully replicate the semantics of the
|
||||||
Arduino Servo library (see http://www.arduino.cc/en/Reference/Servo)
|
Arduino Servo library (see http://www.arduino.cc/en/Reference/Servo)
|
||||||
for the ESP32, with two (optional) additions. The two new functions
|
for the ESP32, with two (optional) additions. The two new functions
|
||||||
expose the ability of the ESP32 PWM timers to vary timer width.
|
expose the ability of the ESP32 PWM timers to vary timer width.
|
||||||
|
# Documentation by Doxygen
|
||||||
|
|
||||||
== License ==
|
[ESP32Servo Doxygen](https://madhephaestus.github.io/ESP32Servo/annotated.html)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
Copyright (c) 2017 John K. Bennett. All right reserved.
|
Copyright (c) 2017 John K. Bennett. All right reserved.
|
||||||
|
|
||||||
@@ -23,47 +30,65 @@ You should have received a copy of the GNU Lesser General Public
|
|||||||
License along with this library; if not, write to the Free Software
|
License along with this library; if not, write to the Free Software
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
Library Description:
|
## Library Description:
|
||||||
--------------------
|
```
|
||||||
Servo - Class for manipulating servo motors connected to ESP32 pins.
|
Servo - Class for manipulating servo motors connected to ESP32 pins.
|
||||||
|
|
||||||
int attach(pin ) - Attaches the given GPIO pin to the next free channel
|
int attach(pin ) - Attaches the given GPIO pin to the next free channel
|
||||||
(channels that have previously been detached are used first),
|
(channels that have previously been detached are used first),
|
||||||
returns channel number or 0 if failure. All pin numbers are allowed,
|
returns channel number or 0 if failure. All pin numbers are allowed,
|
||||||
but only pins 2,4,12-19,21-23,25-27,32-33 are recommended.
|
but only pins 2,4,12-19,21-23,25-27,32-33 are recommended.
|
||||||
|
|
||||||
int attach(pin, min, max ) - Attaches to a pin setting min and max
|
int attach(pin, min, max ) - Attaches to a pin setting min and max
|
||||||
values in microseconds; enforced minimum min is 500, enforced max
|
values in microseconds; enforced minimum min is 500, enforced max
|
||||||
is 2500. Other semantics are the same as attach().
|
is 2500. Other semantics are the same as attach().
|
||||||
|
|
||||||
void write () - Sets the servo angle in degrees; a value below 500 is
|
void write () - Sets the servo angle in degrees; a value below 500 is
|
||||||
treated as a value in degrees (0 to 180). These limit are enforced,
|
treated as a value in degrees (0 to 180). These limit are enforced,
|
||||||
i.e., values are constrained as follows:
|
i.e., values are constrained as follows:
|
||||||
Value Becomes
|
Value Becomes
|
||||||
----- -------
|
----- -------
|
||||||
< 0 0
|
< 0 0
|
||||||
0 - 180 value (treated as degrees)
|
0 - 180 value (treated as degrees)
|
||||||
181 - 499 180
|
181 - 499 180
|
||||||
500 - (min-1) min
|
500 - (min-1) min
|
||||||
min-max (from attach or default) value (treated as microseconds)
|
min-max (from attach or default) value (treated as microseconds)
|
||||||
(max+1) - 2500 max
|
(max+1) - 2500 max
|
||||||
|
|
||||||
void writeMicroseconds() - Sets the servo pulse width in microseconds.
|
void writeMicroseconds() - Sets the servo pulse width in microseconds.
|
||||||
min and max are enforced (see above).
|
min and max are enforced (see above).
|
||||||
|
|
||||||
int read() - Gets the last written servo pulse width as an angle between 0 and 180.
|
int read() - Gets the last written servo pulse width as an angle between 0 and 180.
|
||||||
int readMicroseconds() - Gets the last written servo pulse width in microseconds.
|
|
||||||
bool attached() - Returns true if this servo instance is attached to a pin.
|
|
||||||
void detach() - Stops an the attached servo, frees the attached pin, and frees
|
|
||||||
its channel for reuse.
|
|
||||||
|
|
||||||
*** New ESP32-specific functions **
|
int readMicroseconds() - Gets the last written servo pulse width in microseconds.
|
||||||
setTimerWidth(value) - Sets the PWM timer width (must be 16-20) (ESP32 ONLY);
|
|
||||||
as a side effect, the pulse width is recomputed.
|
bool attached() - Returns true if this servo instance is attached to a pin.
|
||||||
int readTimerWidth() - Gets the PWM timer width (ESP32 ONLY)
|
|
||||||
|
void detach() - Stops an the attached servo, frees the attached pin, and frees
|
||||||
|
its channel for reuse.
|
||||||
|
```
|
||||||
|
|
||||||
|
### **New ESP32-specific functions**
|
||||||
|
|
||||||
Useful Defaults:
|
```
|
||||||
----------------
|
setTimerWidth(value) - Sets the PWM timer width (must be 16-20) (ESP32 ONLY);
|
||||||
default min pulse width for attach(): 1000us
|
as a side effect, the pulse width is recomputed.
|
||||||
default max pulse width for attach(): 2000us
|
|
||||||
|
int readTimerWidth() - Gets the PWM timer width (ESP32 ONLY)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Useful Defaults:
|
||||||
|
|
||||||
|
default min pulse width for attach(): 544us
|
||||||
|
|
||||||
|
default max pulse width for attach(): 2400us
|
||||||
|
|
||||||
default timer width 16 (if timer width is not set)
|
default timer width 16 (if timer width is not set)
|
||||||
|
|
||||||
default pulse width 1500us (servos are initialized with this value)
|
default pulse width 1500us (servos are initialized with this value)
|
||||||
|
|
||||||
MINIMUM pulse with: 500us
|
MINIMUM pulse with: 500us
|
||||||
|
|
||||||
MAXIMUM pulse with: 2500us
|
MAXIMUM pulse with: 2500us
|
||||||
|
|
||||||
MAXIMUM number of servos: 16 (this is the number of PWM channels in the ESP32)
|
MAXIMUM number of servos: 16 (this is the number of PWM channels in the ESP32)
|
||||||
2428
arduino-cli/libraries/ESP32Servo/doxy.doxyfile
Normal file
2428
arduino-cli/libraries/ESP32Servo/doxy.doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
@@ -37,14 +37,24 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Include the ESP32 Arduino Servo Library instead of the original Arduino Servo Library
|
// Include the ESP32 Arduino Servo Library instead of the original Arduino Servo Library
|
||||||
#include <ESP32_Servo.h>
|
#include <ESP32Servo.h>
|
||||||
|
|
||||||
Servo myservo; // create servo object to control a servo
|
Servo myservo; // create servo object to control a servo
|
||||||
|
|
||||||
// Possible PWM GPIO pins on the ESP32: 0(used by on-board button),2,4,5(used by on-board LED),12-19,21-23,25-27,32-33
|
// Possible PWM GPIO pins on the ESP32: 0(used by on-board button),2,4,5(used by on-board LED),12-19,21-23,25-27,32-33
|
||||||
|
// Possible PWM GPIO pins on the ESP32-S2: 0(used by on-board button),1-17,18(used by on-board LED),19-21,26,33-42
|
||||||
|
// Possible PWM GPIO pins on the ESP32-S3: 0(used by on-board button),1-21,35-45,47,48(used by on-board LED)
|
||||||
|
// Possible PWM GPIO pins on the ESP32-C3: 0(used by on-board button),1-7,8(used by on-board LED),9-10,18-21
|
||||||
int servoPin = 18; // GPIO pin used to connect the servo control (digital out)
|
int servoPin = 18; // GPIO pin used to connect the servo control (digital out)
|
||||||
// Possible ADC pins on the ESP32: 0,2,4,12-15,32-39; 34-39 are recommended for analog input
|
// Possible ADC pins on the ESP32: 0,2,4,12-15,32-39; 34-39 are recommended for analog input
|
||||||
|
// Possible ADC pins on the ESP32-S2: 1-20 are recommended for analog input
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
int potPin = 10; // GPIO pin used to connect the potentiometer (analog in)
|
||||||
|
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||||
|
int potPin = 4; // GPIO pin used to connect the potentiometer (analog in)
|
||||||
|
#else
|
||||||
int potPin = 34; // GPIO pin used to connect the potentiometer (analog in)
|
int potPin = 34; // GPIO pin used to connect the potentiometer (analog in)
|
||||||
|
#endif
|
||||||
int ADC_Max = 4096; // This is the default ADC max value on the ESP32 (12 bit ADC width);
|
int ADC_Max = 4096; // This is the default ADC max value on the ESP32 (12 bit ADC width);
|
||||||
// this width can be set (in low-level oode) from 9-12 bits, for a
|
// this width can be set (in low-level oode) from 9-12 bits, for a
|
||||||
// a range of max values of 512-4096
|
// a range of max values of 512-4096
|
||||||
@@ -53,6 +63,12 @@ int val; // variable to read the value from the analog pin
|
|||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
|
// Allow allocation of all timers
|
||||||
|
ESP32PWM::allocateTimer(0);
|
||||||
|
ESP32PWM::allocateTimer(1);
|
||||||
|
ESP32PWM::allocateTimer(2);
|
||||||
|
ESP32PWM::allocateTimer(3);
|
||||||
|
myservo.setPeriodHertz(50);// Standard 50hz servo
|
||||||
myservo.attach(servoPin, 500, 2400); // attaches the servo on pin 18 to the servo object
|
myservo.attach(servoPin, 500, 2400); // attaches the servo on pin 18 to the servo object
|
||||||
// using SG90 servo min/max of 500us and 2400us
|
// using SG90 servo min/max of 500us and 2400us
|
||||||
// for MG995 large servo, use 1000us and 2000us,
|
// for MG995 large servo, use 1000us and 2000us,
|
||||||
|
|||||||
@@ -33,74 +33,130 @@
|
|||||||
* Experimentally, 550 and 2350 are pretty close to 0 and 180.
|
* Experimentally, 550 and 2350 are pretty close to 0 and 180.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <ESP32_Servo.h>
|
#include <ESP32Servo.h>
|
||||||
|
|
||||||
// create four servo objects
|
// create four servo objects
|
||||||
Servo servo1;
|
Servo servo1;
|
||||||
Servo servo2;
|
Servo servo2;
|
||||||
Servo servo3;
|
Servo servo3;
|
||||||
Servo servo4;
|
Servo servo4;
|
||||||
|
Servo servo5;
|
||||||
// Published values for SG90 servos; adjust if needed
|
// Published values for SG90 servos; adjust if needed
|
||||||
int minUs = 500;
|
int minUs = 1000;
|
||||||
int maxUs = 2400;
|
int maxUs = 2000;
|
||||||
|
|
||||||
// These are all GPIO pins on the ESP32
|
// These are all GPIO pins on the ESP32
|
||||||
// Recommended pins include 2,4,12-19,21-23,25-27,32-33
|
// Recommended pins include 2,4,12-19,21-23,25-27,32-33
|
||||||
int servo1Pin = 18;
|
// for the ESP32-S2 the GPIO pins are 1-21,26,33-42
|
||||||
int servo2Pin = 19;
|
// for the ESP32-S3 the GPIO pins are 1-21,35-45,47-48
|
||||||
int servo3Pin = 22;
|
// for the ESP32-C3 the GPIO pins are 1-10,18-21
|
||||||
int servo4Pin = 23;
|
#if defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||||
|
int servo1Pin = 7;
|
||||||
|
int servo2Pin = 6;
|
||||||
|
int servo3Pin = 5;
|
||||||
|
int servo4Pin = 4;
|
||||||
|
int servo5Pin = 3;
|
||||||
|
#else
|
||||||
|
int servo1Pin = 15;
|
||||||
|
int servo2Pin = 16;
|
||||||
|
int servo3Pin = 14;
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
int servo4Pin = 13;
|
||||||
|
#else
|
||||||
|
int servo4Pin = 32;
|
||||||
|
#endif
|
||||||
|
int servo5Pin = 4;
|
||||||
|
#endif
|
||||||
|
|
||||||
int pos = 0; // position in degrees
|
int pos = 0; // position in degrees
|
||||||
|
ESP32PWM pwm;
|
||||||
|
void setup() {
|
||||||
|
// Allow allocation of all timers
|
||||||
|
ESP32PWM::allocateTimer(0);
|
||||||
|
ESP32PWM::allocateTimer(1);
|
||||||
|
ESP32PWM::allocateTimer(2);
|
||||||
|
ESP32PWM::allocateTimer(3);
|
||||||
|
Serial.begin(115200);
|
||||||
|
servo1.setPeriodHertz(50); // Standard 50hz servo
|
||||||
|
servo2.setPeriodHertz(50); // Standard 50hz servo
|
||||||
|
servo3.setPeriodHertz(330); // Standard 50hz servo
|
||||||
|
servo4.setPeriodHertz(200); // Standard 50hz servo
|
||||||
|
//servo5.setPeriodHertz(50); // Standard 50hz servo
|
||||||
|
|
||||||
|
|
||||||
void setup()
|
|
||||||
{
|
|
||||||
servo1.attach(servo1Pin, minUs, maxUs);
|
|
||||||
servo2.attach(servo2Pin, minUs, maxUs);
|
|
||||||
servo3.attach(servo3Pin, minUs, maxUs);
|
|
||||||
servo4.attach(servo4Pin, minUs, maxUs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
for (pos = 0; pos <= 180; pos += 1) { // sweep from 0 degrees to 180 degrees
|
servo1.attach(servo1Pin, minUs, maxUs);
|
||||||
// in steps of 1 degree
|
servo2.attach(servo2Pin, minUs, maxUs);
|
||||||
servo1.write(pos);
|
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||||
delay(20); // waits 20ms for the servo to reach the position
|
pwm.attachPin(37, 10000);//10khz
|
||||||
}
|
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||||
for (pos = 180; pos >= 0; pos -= 1) { // sweep from 180 degrees to 0 degrees
|
pwm.attachPin(7, 10000);//10khz
|
||||||
servo1.write(pos);
|
#else
|
||||||
delay(20);
|
pwm.attachPin(27, 10000);//10khz
|
||||||
}
|
#endif
|
||||||
|
servo3.attach(servo3Pin, minUs, maxUs);
|
||||||
|
servo4.attach(servo4Pin, minUs, maxUs);
|
||||||
|
|
||||||
for (pos = 0; pos <= 180; pos += 1) { // sweep from 0 degrees to 180 degrees
|
//servo5.attach(servo5Pin, minUs, maxUs);
|
||||||
// in steps of 1 degree
|
|
||||||
servo2.write(pos);
|
|
||||||
delay(20); // waits 20ms for the servo to reach the position
|
|
||||||
}
|
|
||||||
for (pos = 180; pos >= 0; pos -= 1) { // sweep from 180 degrees to 0 degrees
|
|
||||||
servo2.write(pos);
|
|
||||||
delay(20);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (pos = 0; pos <= 180; pos += 1) { // sweep from 0 degrees to 180 degrees
|
|
||||||
// in steps of 1 degree
|
|
||||||
servo3.write(pos);
|
|
||||||
delay(20); // waits 20ms for the servo to reach the position
|
|
||||||
}
|
|
||||||
for (pos = 180; pos >= 0; pos -= 1) { // sweep from 180 degrees to 0 degrees
|
|
||||||
servo3.write(pos);
|
|
||||||
delay(20);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (pos = 0; pos <= 180; pos += 1) { // sweep from 0 degrees to 180 degrees
|
for (pos = 0; pos <= 180; pos += 1) { // sweep from 0 degrees to 180 degrees
|
||||||
// in steps of 1 degree
|
// in steps of 1 degree
|
||||||
servo4.write(pos);
|
servo1.write(pos);
|
||||||
delay(20); // waits 20ms for the servo to reach the position
|
delay(1); // waits 20ms for the servo to reach the position
|
||||||
}
|
}
|
||||||
for (pos = 180; pos >= 0; pos -= 1) { // sweep from 180 degrees to 0 degrees
|
for (pos = 180; pos >= 0; pos -= 1) { // sweep from 180 degrees to 0 degrees
|
||||||
servo4.write(pos);
|
servo1.write(pos);
|
||||||
delay(20);
|
delay(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (pos = 0; pos <= 180; pos += 1) { // sweep from 0 degrees to 180 degrees
|
||||||
|
// in steps of 1 degree
|
||||||
|
servo2.write(pos);
|
||||||
|
delay(1); // waits 20ms for the servo to reach the position
|
||||||
|
}
|
||||||
|
for (pos = 180; pos >= 0; pos -= 1) { // sweep from 180 degrees to 0 degrees
|
||||||
|
servo2.write(pos);
|
||||||
|
delay(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (pos = 0; pos <= 180; pos += 1) { // sweep from 0 degrees to 180 degrees
|
||||||
|
// in steps of 1 degree
|
||||||
|
servo3.write(pos);
|
||||||
|
delay(1); // waits 20ms for the servo to reach the position
|
||||||
|
}
|
||||||
|
for (pos = 180; pos >= 0; pos -= 1) { // sweep from 180 degrees to 0 degrees
|
||||||
|
servo3.write(pos);
|
||||||
|
delay(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (pos = 0; pos <= 180; pos += 1) { // sweep from 0 degrees to 180 degrees
|
||||||
|
// in steps of 1 degree
|
||||||
|
servo4.write(pos);
|
||||||
|
delay(1); // waits 20ms for the servo to reach the position
|
||||||
|
}
|
||||||
|
for (pos = 180; pos >= 0; pos -= 1) { // sweep from 180 degrees to 0 degrees
|
||||||
|
servo4.write(pos);
|
||||||
|
delay(1);
|
||||||
|
}
|
||||||
|
for (pos = 0; pos <= 180; pos += 1) { // sweep from 0 degrees to 180 degrees
|
||||||
|
// in steps of 1 degree
|
||||||
|
servo5.write(pos);
|
||||||
|
delay(1); // waits 20ms for the servo to reach the position
|
||||||
|
}
|
||||||
|
for (pos = 180; pos >= 0; pos -= 1) { // sweep from 180 degrees to 0 degrees
|
||||||
|
servo5.write(pos);
|
||||||
|
delay(1);
|
||||||
|
}
|
||||||
|
servo1.detach();
|
||||||
|
servo2.detach();;
|
||||||
|
servo3.detach();
|
||||||
|
servo4.detach();
|
||||||
|
pwm.detachPin(27);
|
||||||
|
|
||||||
|
delay(5000);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,109 +0,0 @@
|
|||||||
/*
|
|
||||||
* ESP32 Servo Example
|
|
||||||
* John K. Bennett
|
|
||||||
* March, 2017
|
|
||||||
*
|
|
||||||
* This sketch uses low-level ESP32 PWM functionality to sweep 4 servos in sequence.
|
|
||||||
* It does NOT use the ESP32_Servo library for Arduino.
|
|
||||||
*
|
|
||||||
* The ESP32 supports 16 hardware LED PWM channels that are intended
|
|
||||||
* to be used for LED brightness control. The low level ESP32 code allows us to set the
|
|
||||||
* PWM frequency and bit-depth, and then control them by setting bits in the relevant control
|
|
||||||
* register. The core files esp32-hal-ledc.* provides helper functions to make this set up
|
|
||||||
* straightforward.
|
|
||||||
*
|
|
||||||
* Different servos require different pulse widths to vary servo angle, but the range is
|
|
||||||
* an approximately 500-2500 microsecond pulse every 20ms (50Hz). In general, hobbyist servos
|
|
||||||
* sweep 180 degrees, so the lowest number in the published range for a particular servo
|
|
||||||
* represents an angle of 0 degrees, the middle of the range represents 90 degrees, and the top
|
|
||||||
* of the range represents 180 degrees. So for example, if the range is 1000us to 2000us,
|
|
||||||
* 1000us would equal an angle of 0, 1500us would equal 90 degrees, and 2000us would equal 1800
|
|
||||||
* degrees.
|
|
||||||
*
|
|
||||||
* The ESP32 PWM timers allow us to set the timer width (max 20 bits). Thus
|
|
||||||
* the timer "tick" length is (pulse_period/2**timer_width), and the equation for pulse_high_width
|
|
||||||
* (the portion of cycle (20ms in our case) that the signal is high) becomes:
|
|
||||||
*
|
|
||||||
* pulse_high_width = count * tick_length
|
|
||||||
* = count * (pulse_period/2**timer_width)
|
|
||||||
*
|
|
||||||
* and count = (pulse_high_width / (pulse_period/2**timer_width))
|
|
||||||
*
|
|
||||||
* For example, if we want a 1500us pulse_high_width, we set pulse_period to 20ms (20000us)
|
|
||||||
* (this value is set in the ledcSetup call), and count (used in the ledcWrite call) to
|
|
||||||
* 1500/(20000/65655), or 4924. This is the value we write to the timer in the ledcWrite call.
|
|
||||||
*
|
|
||||||
* As a concrete example, suppose we want to repeatedly sweep four Tower Pro SG90 servos
|
|
||||||
* from 0 to 180 degrees. The published pulse width range for the SG90 is 500-2400us. Thus,
|
|
||||||
* we should vary the count used in ledcWrite from 1638 to 7864.
|
|
||||||
*
|
|
||||||
* Circuit:
|
|
||||||
* Servo motors have three wires: power, ground, and signal. The power wire is typically red,
|
|
||||||
* the ground wire is typically black or brown, and the signal wire is typically yellow,
|
|
||||||
* orange or white. Since the ESP32 can supply limited current at only 3.3V, and servos draw
|
|
||||||
* considerable power, we will connect servo power to the VBat pin of the ESP32 (located
|
|
||||||
* near the USB connector). THIS IS ONLY APPROPRIATE FOR SMALL SERVOS.
|
|
||||||
*
|
|
||||||
* We could also connect servo power to a separate external
|
|
||||||
* power source (as long as we connect all of the grounds (ESP32, servo, and external power).
|
|
||||||
* In this example, we just connect ESP32 ground to servo ground. The servo signal pins
|
|
||||||
* connect to any available GPIO pins on the ESP32 (in this example, we use pins
|
|
||||||
* 22, 19, 23, & 18).
|
|
||||||
*
|
|
||||||
* In this example, we assume four Tower Pro SG90 small servos.
|
|
||||||
* The published min and max for this servo are 500 and 2400, respectively.
|
|
||||||
* These values actually drive the servos a little past 0 and 180, so
|
|
||||||
* if you are particular, adjust the min and max values to match your needs.
|
|
||||||
* Experimentally, 550us and 2350us are pretty close to 0 and 180.
|
|
||||||
*
|
|
||||||
* This code was inspired by a post on Hackaday by Elliot Williams.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Values for TowerPro SG90 small servos; adjust if needed
|
|
||||||
#define COUNT_LOW 1638
|
|
||||||
#define COUNT_HIGH 7864
|
|
||||||
|
|
||||||
#define TIMER_WIDTH 16
|
|
||||||
|
|
||||||
#include "esp32-hal-ledc.h"
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
ledcSetup(1, 50, TIMER_WIDTH); // channel 1, 50 Hz, 16-bit width
|
|
||||||
ledcAttachPin(22, 1); // GPIO 22 assigned to channel 1
|
|
||||||
|
|
||||||
ledcSetup(2, 50, TIMER_WIDTH); // channel 2, 50 Hz, 16-bit width
|
|
||||||
ledcAttachPin(19, 2); // GPIO 19 assigned to channel 2
|
|
||||||
|
|
||||||
ledcSetup(3, 50, TIMER_WIDTH); // channel 3, 50 Hz, 16-bit width
|
|
||||||
ledcAttachPin(23, 3); // GPIO 23 assigned to channel 3
|
|
||||||
|
|
||||||
ledcSetup(4, 50, TIMER_WIDTH); // channel 4, 50 Hz, 16-bit width
|
|
||||||
ledcAttachPin(18, 4); // GPIO 18 assigned to channel 4
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
for (int i=COUNT_LOW ; i < COUNT_HIGH ; i=i+100)
|
|
||||||
{
|
|
||||||
ledcWrite(1, i); // sweep servo 1
|
|
||||||
delay(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=COUNT_LOW ; i < COUNT_HIGH ; i=i+100)
|
|
||||||
{
|
|
||||||
ledcWrite(2, i); // sweep servo 2
|
|
||||||
delay(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=COUNT_LOW ; i < COUNT_HIGH ; i=i+100)
|
|
||||||
{
|
|
||||||
ledcWrite(3, i); // sweep the servo
|
|
||||||
delay(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=COUNT_LOW ; i < COUNT_HIGH ; i=i+100)
|
|
||||||
{
|
|
||||||
ledcWrite(4, i); // sweep the servo
|
|
||||||
delay(200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#include <ESP32Servo.h>
|
||||||
|
int APin = 13;
|
||||||
|
ESP32PWM pwm;
|
||||||
|
int freq = 1000;
|
||||||
|
void setup() {
|
||||||
|
// Allow allocation of all timers
|
||||||
|
ESP32PWM::allocateTimer(0);
|
||||||
|
ESP32PWM::allocateTimer(1);
|
||||||
|
ESP32PWM::allocateTimer(2);
|
||||||
|
ESP32PWM::allocateTimer(3);
|
||||||
|
Serial.begin(115200);
|
||||||
|
pwm.attachPin(APin, freq, 10); // 1KHz 10 bits
|
||||||
|
|
||||||
|
}
|
||||||
|
void loop() {
|
||||||
|
|
||||||
|
// fade the LED on thisPin from off to brightest:
|
||||||
|
for (float brightness = 0; brightness <= 0.5; brightness += 0.001) {
|
||||||
|
// Write a unit vector value from 0.0 to 1.0
|
||||||
|
pwm.writeScaled(brightness);
|
||||||
|
delay(2);
|
||||||
|
}
|
||||||
|
//delay(1000);
|
||||||
|
// fade the LED on thisPin from brithstest to off:
|
||||||
|
for (float brightness = 0.5; brightness >= 0; brightness -= 0.001) {
|
||||||
|
freq += 10;
|
||||||
|
// Adjust the frequency on the fly with a specific brightness
|
||||||
|
// Frequency is in herts and duty cycle is a unit vector 0.0 to 1.0
|
||||||
|
pwm.adjustFrequency(freq, brightness); // update the time base of the PWM
|
||||||
|
delay(2);
|
||||||
|
}
|
||||||
|
// pause between LEDs:
|
||||||
|
delay(1000);
|
||||||
|
freq = 1000;
|
||||||
|
pwm.adjustFrequency(freq, 0.0); // reset the time base
|
||||||
|
}
|
||||||
@@ -36,31 +36,47 @@
|
|||||||
* if you are particular, adjust the min and max values to match your needs.
|
* if you are particular, adjust the min and max values to match your needs.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <ESP32_Servo.h>
|
#include <ESP32Servo.h>
|
||||||
|
|
||||||
Servo myservo; // create servo object to control a servo
|
Servo myservo; // create servo object to control a servo
|
||||||
// 16 servo objects can be created on the ESP32
|
// 16 servo objects can be created on the ESP32
|
||||||
|
|
||||||
int pos = 0; // variable to store the servo position
|
int pos = 0; // variable to store the servo position
|
||||||
// Recommended PWM GPIO pins on the ESP32 include 2,4,12-19,21-23,25-27,32-33
|
// Recommended PWM GPIO pins on the ESP32 include 2,4,12-19,21-23,25-27,32-33
|
||||||
|
// Possible PWM GPIO pins on the ESP32-S2: 0(used by on-board button),1-17,18(used by on-board LED),19-21,26,33-42
|
||||||
|
// Possible PWM GPIO pins on the ESP32-S3: 0(used by on-board button),1-21,35-45,47,48(used by on-board LED)
|
||||||
|
// Possible PWM GPIO pins on the ESP32-C3: 0(used by on-board button),1-7,8(used by on-board LED),9-10,18-21
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
int servoPin = 17;
|
||||||
|
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||||
|
int servoPin = 7;
|
||||||
|
#else
|
||||||
int servoPin = 18;
|
int servoPin = 18;
|
||||||
|
#endif
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
myservo.attach(servoPin); // attaches the servo on pin 18 to the servo object
|
// Allow allocation of all timers
|
||||||
// using default min/max of 1000us and 2000us
|
ESP32PWM::allocateTimer(0);
|
||||||
// different servos may require different min/max settings
|
ESP32PWM::allocateTimer(1);
|
||||||
// for an accurate 0 to 180 sweep
|
ESP32PWM::allocateTimer(2);
|
||||||
|
ESP32PWM::allocateTimer(3);
|
||||||
|
myservo.setPeriodHertz(50); // standard 50 hz servo
|
||||||
|
myservo.attach(servoPin, 1000, 2000); // attaches the servo on pin 18 to the servo object
|
||||||
|
// using default min/max of 1000us and 2000us
|
||||||
|
// different servos may require different min/max settings
|
||||||
|
// for an accurate 0 to 180 sweep
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees
|
|
||||||
// in steps of 1 degree
|
for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees
|
||||||
myservo.write(pos); // tell servo to go to position in variable 'pos'
|
// in steps of 1 degree
|
||||||
delay(15); // waits 15ms for the servo to reach the position
|
myservo.write(pos); // tell servo to go to position in variable 'pos'
|
||||||
}
|
delay(15); // waits 15ms for the servo to reach the position
|
||||||
for (pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees
|
}
|
||||||
myservo.write(pos); // tell servo to go to position in variable 'pos'
|
for (pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees
|
||||||
delay(15); // waits 15ms for the servo to reach the position
|
myservo.write(pos); // tell servo to go to position in variable 'pos'
|
||||||
}
|
delay(15); // waits 15ms for the servo to reach the position
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ESP32Servo.h>
|
||||||
|
int pin = 2;
|
||||||
|
void setup() {
|
||||||
|
// Allow allocation of all timers
|
||||||
|
ESP32PWM::allocateTimer(0);
|
||||||
|
ESP32PWM::allocateTimer(1);
|
||||||
|
ESP32PWM::allocateTimer(2);
|
||||||
|
ESP32PWM::allocateTimer(3);
|
||||||
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
tone(pin, 4186, // C
|
||||||
|
500); // half a second
|
||||||
|
tone(pin, 5274, // E
|
||||||
|
500); // half a second
|
||||||
|
delay(500);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
Mega analogWrite() test
|
||||||
|
|
||||||
|
This sketch fades LEDs up and down one at a time on digital pins 2 through 13.
|
||||||
|
This sketch was written for the Arduino Mega, and will not work on previous boards.
|
||||||
|
|
||||||
|
The circuit:
|
||||||
|
* LEDs attached from pins 2 through 13 to ground. or for ESP32-S2 pins 1-17,19-21,26,33-42
|
||||||
|
|
||||||
|
created 8 Feb 2009
|
||||||
|
by Tom Igoe
|
||||||
|
|
||||||
|
This example code is in the public domain.
|
||||||
|
|
||||||
|
*/
|
||||||
|
// These constants won't change. They're used to give names
|
||||||
|
// to the pins used:
|
||||||
|
#if defined(ARDUINO_ESP32S2_DEV) || defined(ARDUINO_ESP32S3_DEV)
|
||||||
|
const int lowestPin = 1;
|
||||||
|
const int highestPin = 42;
|
||||||
|
#elif defined(ARDUINO_ESP32C3_DEV)
|
||||||
|
const int lowestPin = 1;
|
||||||
|
const int highestPin = 19;
|
||||||
|
#else
|
||||||
|
const int lowestPin = 2;
|
||||||
|
const int highestPin = 33;
|
||||||
|
#endif
|
||||||
|
#include <ESP32Servo.h>
|
||||||
|
Servo myservo;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
// Allow allocation of all timers
|
||||||
|
ESP32PWM::allocateTimer(0);
|
||||||
|
ESP32PWM::allocateTimer(1);
|
||||||
|
ESP32PWM::allocateTimer(2);
|
||||||
|
ESP32PWM::allocateTimer(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (!myservo.attached()) {
|
||||||
|
myservo.setPeriodHertz(50); // standard 50 hz servo
|
||||||
|
myservo.attach(33, 1000, 2000); // Attach the servo after it has been detatched
|
||||||
|
}
|
||||||
|
myservo.write(0);
|
||||||
|
// iterate over the pins:
|
||||||
|
for (int thisPin = lowestPin; thisPin <= highestPin; thisPin++) {
|
||||||
|
if (ESP32PWM::hasPwm(thisPin) && // Is it possible for this pin to PWM
|
||||||
|
(ESP32PWM::channelsRemaining() > 0 || // New channels availible to allocate
|
||||||
|
pwmFactory(thisPin) != NULL || // already allocated this pin in the factory
|
||||||
|
thisPin == 25 || // one of the 2 DAC outputs, no timer needed
|
||||||
|
thisPin == 26)) { // one of the 2 DAC outputs, no timer needed
|
||||||
|
if (pwmFactory(thisPin) == NULL) { // check if its the first time for the pin or its a DAC
|
||||||
|
#if defined(ARDUINO_ESP32S2_DEV)
|
||||||
|
if (thisPin == 17 || // one of the 2 DAC outputs, no timer needed
|
||||||
|
thisPin == 18)
|
||||||
|
#elif defined(ARDUINO_ESP32C3_DEV) || defined(ARDUINO_ESP32S3_DEV)
|
||||||
|
if (1 == 0) // no DAC outputs for these chips
|
||||||
|
#else
|
||||||
|
if (thisPin == 25 || // one of the 2 DAC outputs, no timer needed
|
||||||
|
thisPin == 26)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
Serial.println("DAC to pin " + String(thisPin));
|
||||||
|
} else
|
||||||
|
Serial.println("Writing to pin " + String(thisPin));
|
||||||
|
pinMode(thisPin, OUTPUT);
|
||||||
|
}
|
||||||
|
// fade the LED on thisPin from off to brightest:
|
||||||
|
for (int brightness = 0; brightness < 255; brightness++) {
|
||||||
|
analogWrite(thisPin, brightness);
|
||||||
|
delay(1);
|
||||||
|
myservo.write(brightness);
|
||||||
|
}
|
||||||
|
// fade the LED on thisPin from brithstest to off:
|
||||||
|
for (int brightness = 255; brightness >= 0; brightness--) {
|
||||||
|
analogWrite(thisPin, brightness);
|
||||||
|
myservo.write(brightness);
|
||||||
|
delay(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
myservo.detach(); // Turn the servo off for a while
|
||||||
|
delay(2000);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
name=ESP32_Servo
|
name=ESP32Servo
|
||||||
version=1.0
|
version=3.0.6
|
||||||
author=John K. Bennett
|
author=Kevin Harrington,John K. Bennett
|
||||||
maintainer=John K. Bennett <jkb@colorado.edu>
|
maintainer=Kevin Harrington <mad.hephaestus@gmail.com>
|
||||||
sentence=Allows ESP32 boards to control servo motors using Arduino semantics.
|
sentence=Allows ESP32 boards to control servo, tone and analogWrite motors using Arduino semantics.
|
||||||
paragraph=This library can control a many types of servos.<br />It makes use of the ESP32 PWM timers: the library can control up to 16 servos on individual channels<br />No attempt has been made to support multiple servos per channel.<br />
|
paragraph=This library can control a many types of servos.<br />It makes use of the ESP32 PWM timers: the library can control up to 16 servos on individual channels<br />No attempt has been made to support multiple servos per channel.<br />
|
||||||
category=Device Control
|
category=Device Control
|
||||||
url=http://www.arduino.cc/en/Reference/Servo
|
url=https://madhephaestus.github.io/ESP32Servo/annotated.html
|
||||||
architectures=esp32
|
architectures=esp32
|
||||||
|
includes=ESP32Servo.h,analogWrite.h,tone.h,ESP32Tone.h,ESP32PWM.h
|
||||||
|
|
||||||
|
|||||||
411
arduino-cli/libraries/ESP32Servo/src/ESP32PWM.cpp
Normal file
411
arduino-cli/libraries/ESP32Servo/src/ESP32PWM.cpp
Normal file
@@ -0,0 +1,411 @@
|
|||||||
|
/*
|
||||||
|
* ESP32PWM.cpp
|
||||||
|
*
|
||||||
|
* Created on: Sep 22, 2018
|
||||||
|
* Author: hephaestus
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ESP32PWM.h>
|
||||||
|
#include "esp32-hal-ledc.h"
|
||||||
|
|
||||||
|
// initialize the class variable ServoCount
|
||||||
|
int ESP32PWM::PWMCount = -1; // the total number of attached servos
|
||||||
|
bool ESP32PWM::explicateAllocationMode=false;
|
||||||
|
ESP32PWM * ESP32PWM::ChannelUsed[NUM_PWM]; // used to track whether a channel is in service
|
||||||
|
long ESP32PWM::timerFreqSet[4] = { -1, -1, -1, -1 };
|
||||||
|
int ESP32PWM::timerCount[4] = { 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
static const char* TAG = "ESP32PWM";
|
||||||
|
|
||||||
|
// The ChannelUsed array elements are 0 if never used, 1 if in use, and -1 if used and disposed
|
||||||
|
// (i.e., available for reuse)
|
||||||
|
/**
|
||||||
|
* allocateTimer
|
||||||
|
* @param a timer number 0-3 indicating which timer to allocate in this library
|
||||||
|
* Switch to explicate allocation mode
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void ESP32PWM::allocateTimer(int timerNumber){
|
||||||
|
if(timerNumber<0 || timerNumber>3)
|
||||||
|
return;
|
||||||
|
if(ESP32PWM::explicateAllocationMode==false){
|
||||||
|
ESP32PWM::explicateAllocationMode=true;
|
||||||
|
for(int i=0;i<4;i++)
|
||||||
|
ESP32PWM::timerCount[i]=4;// deallocate all timers to start mode
|
||||||
|
}
|
||||||
|
ESP32PWM::timerCount[timerNumber]=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP32PWM::ESP32PWM() {
|
||||||
|
resolutionBits = 8;
|
||||||
|
pwmChannel = -1;
|
||||||
|
pin = -1;
|
||||||
|
myFreq = -1;
|
||||||
|
if (PWMCount == -1) {
|
||||||
|
for (int i = 0; i < NUM_PWM; i++)
|
||||||
|
ChannelUsed[i] = NULL; // load invalid data into the storage array of pin mapping
|
||||||
|
PWMCount = PWM_BASE_INDEX; // 0th channel does not work with the PWM system
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP32PWM::~ESP32PWM() {
|
||||||
|
if (attached()) {
|
||||||
|
#ifdef ESP_ARDUINO_VERSION_MAJOR
|
||||||
|
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||||
|
ledcDetach(pin);
|
||||||
|
#else
|
||||||
|
ledcDetachPin(pin);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
ledcDetachPin(pin);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
deallocate();
|
||||||
|
}
|
||||||
|
|
||||||
|
double ESP32PWM::_ledcSetupTimerFreq(uint8_t pin, double freq,
|
||||||
|
uint8_t bit_num) {
|
||||||
|
|
||||||
|
#ifdef ESP_ARDUINO_VERSION_MAJOR
|
||||||
|
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||||
|
return ledcAttach(pin, freq, bit_num);
|
||||||
|
#else
|
||||||
|
return ledcSetup(pin, freq, bit_num);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
return ledcSetup(pin, freq, bit_num);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int ESP32PWM::timerAndIndexToChannel(int timerNum, int index) {
|
||||||
|
int localIndex = 0;
|
||||||
|
for (int j = 0; j < NUM_PWM; j++) {
|
||||||
|
if (((j / 2) % 4) == timerNum) {
|
||||||
|
if (localIndex == index) {
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
localIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int ESP32PWM::allocatenext(double freq) {
|
||||||
|
long freqlocal = (long) freq;
|
||||||
|
if (pwmChannel < 0) {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
bool freqAllocated = ((timerFreqSet[i] == freqlocal)
|
||||||
|
|| (timerFreqSet[i] == -1));
|
||||||
|
if (freqAllocated && timerCount[i] < 4) {
|
||||||
|
if (timerFreqSet[i] == -1) {
|
||||||
|
//Serial.println("Starting timer "+String(i)+" at freq "+String(freq));
|
||||||
|
timerFreqSet[i] = freqlocal;
|
||||||
|
}
|
||||||
|
//Serial.println("Free channel timer "+String(i)+" at freq "+String(freq)+" remaining "+String(4-timerCount[i]));
|
||||||
|
|
||||||
|
timerNum = i;
|
||||||
|
for (int index=0; index<4; ++index)
|
||||||
|
{
|
||||||
|
int myTimerNumber = timerAndIndexToChannel(timerNum,index);
|
||||||
|
if ((myTimerNumber >= 0) && (!ChannelUsed[myTimerNumber]))
|
||||||
|
{
|
||||||
|
pwmChannel = myTimerNumber;
|
||||||
|
// Serial.println(
|
||||||
|
// "PWM on ledc channel #" + String(pwmChannel)
|
||||||
|
// + " using 'timer " + String(timerNum)
|
||||||
|
// + "' to freq " + String(freq) + "Hz");
|
||||||
|
ChannelUsed[pwmChannel] = this;
|
||||||
|
timerCount[timerNum]++;
|
||||||
|
PWMCount++;
|
||||||
|
myFreq = freq;
|
||||||
|
return pwmChannel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if(timerFreqSet[i]>0)
|
||||||
|
// Serial.println("Timer freq mismatch target="+String(freq)+" on timer "+String(i)+" was "+String(timerFreqSet[i]));
|
||||||
|
// else
|
||||||
|
// Serial.println("Timer out of channels target="+String(freq)+" on timer "+String(i)+" was "+String(timerCount[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return pwmChannel;
|
||||||
|
}
|
||||||
|
ESP_LOGE(TAG,
|
||||||
|
"ERROR All PWM timers allocated! Can't accomodate %d Hz\r\nHalting...", freq);
|
||||||
|
while (1)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
void ESP32PWM::deallocate() {
|
||||||
|
if (pwmChannel < 0)
|
||||||
|
return;
|
||||||
|
ESP_LOGE(TAG, "PWM deallocating LEDc #%d",pwmChannel);
|
||||||
|
timerCount[getTimer()]--;
|
||||||
|
if (timerCount[getTimer()] == 0) {
|
||||||
|
timerFreqSet[getTimer()] = -1; // last pwn closed out
|
||||||
|
}
|
||||||
|
timerNum = -1;
|
||||||
|
attachedState = false;
|
||||||
|
ChannelUsed[pwmChannel] = NULL;
|
||||||
|
pwmChannel = -1;
|
||||||
|
PWMCount--;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int ESP32PWM::getChannel() {
|
||||||
|
if (pwmChannel < 0) {
|
||||||
|
ESP_LOGE(TAG, "FAIL! must setup() before using get channel!");
|
||||||
|
}
|
||||||
|
return pwmChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
double ESP32PWM::setup(double freq, uint8_t resolution_bits) {
|
||||||
|
checkFrequencyForSideEffects(freq);
|
||||||
|
|
||||||
|
resolutionBits = resolution_bits;
|
||||||
|
if (attached()) {
|
||||||
|
#ifdef ESP_ARDUINO_VERSION_MAJOR
|
||||||
|
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||||
|
ledcDetach(pin);
|
||||||
|
double val = ledcAttach(getPin(), freq, resolution_bits);
|
||||||
|
#else
|
||||||
|
ledcDetachPin(pin);
|
||||||
|
double val = ledcSetup(getChannel(), freq, resolution_bits);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
ledcDetachPin(pin);
|
||||||
|
double val = ledcSetup(getChannel(), freq, resolution_bits);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
attachPin(pin);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
#ifdef ESP_ARDUINO_VERSION_MAJOR
|
||||||
|
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||||
|
return ledcAttach(getPin(), freq, resolution_bits);
|
||||||
|
#else
|
||||||
|
return ledcSetup(getChannel(), freq, resolution_bits);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
return ledcSetup(getChannel(), freq, resolution_bits);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
double ESP32PWM::getDutyScaled() {
|
||||||
|
return mapf((double) myDuty, 0, (double) ((1 << resolutionBits) - 1), 0.0,
|
||||||
|
1.0);
|
||||||
|
}
|
||||||
|
void ESP32PWM::writeScaled(double duty) {
|
||||||
|
write(mapf(duty, 0.0, 1.0, 0, (double) ((1 << resolutionBits) - 1)));
|
||||||
|
}
|
||||||
|
void ESP32PWM::write(uint32_t duty) {
|
||||||
|
myDuty = duty;
|
||||||
|
#ifdef ESP_ARDUINO_VERSION_MAJOR
|
||||||
|
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||||
|
ledcWrite(getPin(), duty);
|
||||||
|
#else
|
||||||
|
ledcWrite(getChannel(), duty);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
ledcWrite(getChannel(), duty);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
void ESP32PWM::adjustFrequencyLocal(double freq, double dutyScaled) {
|
||||||
|
timerFreqSet[getTimer()] = (long) freq;
|
||||||
|
myFreq = freq;
|
||||||
|
if (attached()) {
|
||||||
|
#ifdef ESP_ARDUINO_VERSION_MAJOR
|
||||||
|
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||||
|
ledcDetach(pin);
|
||||||
|
// Remove the PWM during frequency adjust
|
||||||
|
_ledcSetupTimerFreq(getPin(), freq, resolutionBits);
|
||||||
|
writeScaled(dutyScaled);
|
||||||
|
ledcAttach(getPin(), freq, resolutionBits); // re-attach the pin after frequency adjust
|
||||||
|
#else
|
||||||
|
ledcDetachPin(pin);
|
||||||
|
// Remove the PWM during frequency adjust
|
||||||
|
_ledcSetupTimerFreq(getChannel(), freq, resolutionBits);
|
||||||
|
writeScaled(dutyScaled);
|
||||||
|
ledcAttachPin(pin, getChannel()); // re-attach the pin after frequency adjust
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
ledcDetachPin(pin);
|
||||||
|
// Remove the PWM during frequency adjust
|
||||||
|
_ledcSetupTimerFreq(getChannel(), freq, resolutionBits);
|
||||||
|
writeScaled(dutyScaled);
|
||||||
|
ledcAttachPin(pin, getChannel()); // re-attach the pin after frequency adjust
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} else {
|
||||||
|
_ledcSetupTimerFreq(getPin(), freq, resolutionBits);
|
||||||
|
writeScaled(dutyScaled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void ESP32PWM::adjustFrequency(double freq, double dutyScaled) {
|
||||||
|
if(dutyScaled<0)
|
||||||
|
dutyScaled=getDutyScaled();
|
||||||
|
writeScaled(dutyScaled);
|
||||||
|
for (int i = 0; i < timerCount[getTimer()]; i++) {
|
||||||
|
int pwm = timerAndIndexToChannel(getTimer(), i);
|
||||||
|
if (ChannelUsed[pwm] != NULL) {
|
||||||
|
if (ChannelUsed[pwm]->myFreq != freq) {
|
||||||
|
ChannelUsed[pwm]->adjustFrequencyLocal(freq,
|
||||||
|
ChannelUsed[pwm]->getDutyScaled());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
double ESP32PWM::writeTone(double freq) {
|
||||||
|
for (int i = 0; i < timerCount[getTimer()]; i++) {
|
||||||
|
int pwm = timerAndIndexToChannel(getTimer(), i);
|
||||||
|
if (ChannelUsed[pwm] != NULL) {
|
||||||
|
if (ChannelUsed[pwm]->myFreq != freq) {
|
||||||
|
ChannelUsed[pwm]->adjustFrequencyLocal(freq,
|
||||||
|
ChannelUsed[pwm]->getDutyScaled());
|
||||||
|
}
|
||||||
|
write(1 << (resolutionBits-1)); // writeScaled(0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
double ESP32PWM::writeNote(note_t note, uint8_t octave) {
|
||||||
|
const uint16_t noteFrequencyBase[12] = {
|
||||||
|
// C C# D Eb E F F# G G# A Bb B
|
||||||
|
4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459,
|
||||||
|
7902 };
|
||||||
|
|
||||||
|
if (octave > 8 || note >= NOTE_MAX) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
double noteFreq = (double) noteFrequencyBase[note]
|
||||||
|
/ (double) (1 << (8 - octave));
|
||||||
|
return writeTone(noteFreq);
|
||||||
|
}
|
||||||
|
uint32_t ESP32PWM::read() {
|
||||||
|
#ifdef ESP_ARDUINO_VERSION_MAJOR
|
||||||
|
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||||
|
return ledcRead(getPin());
|
||||||
|
#else
|
||||||
|
return ledcRead(getChannel());
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
return ledcRead(getChannel());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
double ESP32PWM::readFreq() {
|
||||||
|
return myFreq;
|
||||||
|
}
|
||||||
|
void ESP32PWM::attach(int p) {
|
||||||
|
pin = p;
|
||||||
|
attachedState = true;
|
||||||
|
}
|
||||||
|
void ESP32PWM::attachPin(uint8_t pin) {
|
||||||
|
|
||||||
|
if (hasPwm(pin)) {
|
||||||
|
attach(pin);
|
||||||
|
bool success=true;
|
||||||
|
#ifdef ESP_ARDUINO_VERSION_MAJOR
|
||||||
|
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||||
|
success=ledcAttach(pin, readFreq(), resolutionBits);
|
||||||
|
#else
|
||||||
|
ledcAttachPin(pin, getChannel());
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
ledcAttachPin(pin, getChannel());
|
||||||
|
#endif
|
||||||
|
if(success)
|
||||||
|
return;
|
||||||
|
ESP_LOGE(TAG, "ERROR PWM channel failed to configure on!",pin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
|
ESP_LOGE(TAG, "ERROR PWM channel unavailable on pin requested! %d PWM available on: 1-21,26,33-42",pin);
|
||||||
|
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
ESP_LOGE(TAG, "ERROR PWM channel unavailable on pin requested! %d PWM available on: 1-21,35-45,47-48",pin);
|
||||||
|
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||||
|
ESP_LOGE(TAG, "ERROR PWM channel unavailable on pin requested! %d PWM available on: 1-10,18-21",pin);
|
||||||
|
#else
|
||||||
|
ESP_LOGE(TAG, "ERROR PWM channel unavailable on pin requested! %d PWM available on: 2,4,5,12-19,21-23,25-27,32-33",pin);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
void ESP32PWM::attachPin(uint8_t pin, double freq, uint8_t resolution_bits) {
|
||||||
|
|
||||||
|
if (hasPwm(pin)){
|
||||||
|
int ret=setup(freq, resolution_bits);
|
||||||
|
ESP_LOGW(TAG, "Pin Setup %d with code %d",pin,ret);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ESP_LOGE(TAG, "ERROR Pin Failed %d ",pin);
|
||||||
|
attachPin(pin);
|
||||||
|
}
|
||||||
|
void ESP32PWM::detachPin(int pin) {
|
||||||
|
#ifdef ESP_ARDUINO_VERSION_MAJOR
|
||||||
|
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||||
|
|
||||||
|
ledcDetach(pin);
|
||||||
|
#else
|
||||||
|
ledcDetachPin(pin);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
ledcDetachPin(pin);
|
||||||
|
#endif
|
||||||
|
deallocate();
|
||||||
|
}
|
||||||
|
/* Side effects of frequency changes happen because of shared timers
|
||||||
|
*
|
||||||
|
* LEDC Chan to Group/Channel/Timer Mapping
|
||||||
|
** ledc: 0 => Group: 0, Channel: 0, Timer: 0
|
||||||
|
** ledc: 1 => Group: 0, Channel: 1, Timer: 0
|
||||||
|
** ledc: 2 => Group: 0, Channel: 2, Timer: 1
|
||||||
|
** ledc: 3 => Group: 0, Channel: 3, Timer: 1
|
||||||
|
** ledc: 4 => Group: 0, Channel: 4, Timer: 2
|
||||||
|
** ledc: 5 => Group: 0, Channel: 5, Timer: 2
|
||||||
|
** ledc: 6 => Group: 0, Channel: 6, Timer: 3
|
||||||
|
** ledc: 7 => Group: 0, Channel: 7, Timer: 3
|
||||||
|
** ledc: 8 => Group: 1, Channel: 0, Timer: 0
|
||||||
|
** ledc: 9 => Group: 1, Channel: 1, Timer: 0
|
||||||
|
** ledc: 10 => Group: 1, Channel: 2, Timer: 1
|
||||||
|
** ledc: 11 => Group: 1, Channel: 3, Timer: 1
|
||||||
|
** ledc: 12 => Group: 1, Channel: 4, Timer: 2
|
||||||
|
** ledc: 13 => Group: 1, Channel: 5, Timer: 2
|
||||||
|
** ledc: 14 => Group: 1, Channel: 6, Timer: 3
|
||||||
|
** ledc: 15 => Group: 1, Channel: 7, Timer: 3
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool ESP32PWM::checkFrequencyForSideEffects(double freq) {
|
||||||
|
|
||||||
|
allocatenext(freq);
|
||||||
|
for (int i = 0; i < timerCount[getTimer()]; i++) {
|
||||||
|
int pwm = timerAndIndexToChannel(getTimer(), i);
|
||||||
|
|
||||||
|
if (pwm == pwmChannel)
|
||||||
|
continue;
|
||||||
|
if (ChannelUsed[pwm] != NULL)
|
||||||
|
if (ChannelUsed[pwm]->getTimer() == getTimer()) {
|
||||||
|
double diff = abs(ChannelUsed[pwm]->myFreq - freq);
|
||||||
|
if (abs(diff) > 0.1) {
|
||||||
|
ESP_LOGW(TAG,
|
||||||
|
"\tWARNING PWM channel %d \
|
||||||
|
shares a timer with channel %d\n \
|
||||||
|
\tchanging the frequency to %d \
|
||||||
|
Hz will ALSO change channel %d \
|
||||||
|
\n\tfrom its previous frequency of %d Hz\n "
|
||||||
|
,pwmChannel, pwm, freq,pwm, ChannelUsed[pwm]->myFreq);
|
||||||
|
ChannelUsed[pwm]->myFreq = freq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP32PWM* pwmFactory(int pin) {
|
||||||
|
for (int i = 0; i < NUM_PWM; i++)
|
||||||
|
if (ESP32PWM::ChannelUsed[i] != NULL) {
|
||||||
|
if (ESP32PWM::ChannelUsed[i]->getPin() == pin)
|
||||||
|
return ESP32PWM::ChannelUsed[i];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
150
arduino-cli/libraries/ESP32Servo/src/ESP32PWM.h
Normal file
150
arduino-cli/libraries/ESP32Servo/src/ESP32PWM.h
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* ESP32PWM.h
|
||||||
|
*
|
||||||
|
* Created on: Sep 22, 2018
|
||||||
|
* Author: hephaestus
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIBRARIES_ESP32SERVO_SRC_ESP32PWM_H_
|
||||||
|
#define LIBRARIES_ESP32SERVO_SRC_ESP32PWM_H_
|
||||||
|
#include "esp32-hal-ledc.h"
|
||||||
|
#if defined(ARDUINO)
|
||||||
|
#include "Arduino.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||||
|
#define NUM_PWM 6
|
||||||
|
#elif defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
#define NUM_PWM 8
|
||||||
|
#else
|
||||||
|
#define NUM_PWM 16
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PWM_BASE_INDEX 0
|
||||||
|
#define USABLE_ESP32_PWM (NUM_PWM-PWM_BASE_INDEX)
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
class ESP32PWM {
|
||||||
|
private:
|
||||||
|
|
||||||
|
void attach(int pin);
|
||||||
|
int pwmChannel = 0; // channel number for this servo
|
||||||
|
bool attachedState = false;
|
||||||
|
int pin;
|
||||||
|
uint8_t resolutionBits;
|
||||||
|
double myFreq;
|
||||||
|
int allocatenext(double freq);
|
||||||
|
|
||||||
|
static double _ledcSetupTimerFreq(uint8_t pin, double freq,
|
||||||
|
uint8_t bit_num);
|
||||||
|
|
||||||
|
bool checkFrequencyForSideEffects(double freq);
|
||||||
|
|
||||||
|
void adjustFrequencyLocal(double freq, double dutyScaled);
|
||||||
|
static double mapf(double x, double in_min, double in_max, double out_min,
|
||||||
|
double out_max) {
|
||||||
|
if(x>in_max)
|
||||||
|
return out_max;
|
||||||
|
if(x<in_min)
|
||||||
|
return out_min;
|
||||||
|
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||||
|
}
|
||||||
|
|
||||||
|
double setup(double freq, uint8_t resolution_bits=10);
|
||||||
|
//channel 0-15 resolution 1-16bits freq limits depend on resolution9
|
||||||
|
void attachPin(uint8_t pin);
|
||||||
|
// pin allocation
|
||||||
|
void deallocate();
|
||||||
|
public:
|
||||||
|
// setup
|
||||||
|
ESP32PWM();
|
||||||
|
virtual ~ESP32PWM();
|
||||||
|
|
||||||
|
|
||||||
|
void detachPin(int pin);
|
||||||
|
void attachPin(uint8_t pin, double freq, uint8_t resolution_bits=10);
|
||||||
|
bool attached() {
|
||||||
|
return attachedState;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write raw duty cycle
|
||||||
|
void write(uint32_t duty);
|
||||||
|
// Write a duty cycle to the PWM using a unit vector from 0.0-1.0
|
||||||
|
void writeScaled(double duty);
|
||||||
|
//Adjust frequency
|
||||||
|
double writeTone(double freq);
|
||||||
|
double writeNote(note_t note, uint8_t octave);
|
||||||
|
void adjustFrequency(double freq, double dutyScaled=-1);
|
||||||
|
|
||||||
|
// Read pwm data
|
||||||
|
uint32_t read();
|
||||||
|
double readFreq();
|
||||||
|
double getDutyScaled();
|
||||||
|
|
||||||
|
//Timer data
|
||||||
|
static int timerAndIndexToChannel(int timer, int index);
|
||||||
|
/**
|
||||||
|
* allocateTimer
|
||||||
|
* @param a timer number 0-3 indicating which timer to allocate in this library
|
||||||
|
* Switch to explicate allocation mode
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static void allocateTimer(int timerNumber);
|
||||||
|
static bool explicateAllocationMode;
|
||||||
|
int getTimer() {
|
||||||
|
return timerNum;
|
||||||
|
}
|
||||||
|
int timerNum = -1;
|
||||||
|
uint32_t myDuty = 0;
|
||||||
|
int getChannel();
|
||||||
|
static int PWMCount; // the total number of attached pwm
|
||||||
|
static int timerCount[4];
|
||||||
|
static ESP32PWM * ChannelUsed[NUM_PWM]; // used to track whether a channel is in service
|
||||||
|
static long timerFreqSet[4];
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
int getPin() {
|
||||||
|
return pin;
|
||||||
|
}
|
||||||
|
static bool hasPwm(int pin) {
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
|
if ((pin >=1 && pin <= 21) || //21
|
||||||
|
(pin == 26) || //1
|
||||||
|
(pin >= 33 && pin <= 42)) //10
|
||||||
|
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
if ((pin >=1 && pin <= 21) || //20
|
||||||
|
(pin >= 35 && pin <= 45) || //11
|
||||||
|
(pin == 47) || (pin == 48)) //2
|
||||||
|
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||||
|
if ((pin >=0 && pin <= 10) || //11
|
||||||
|
(pin >= 18 && pin <= 21)) //4
|
||||||
|
#elif defined(CONFIG_IDF_TARGET_ESP32C6)
|
||||||
|
if ((pin >=0 && pin <= 9) || //10
|
||||||
|
(pin >= 12 && pin <= 23)) //12
|
||||||
|
#elif defined(CONFIG_IDF_TARGET_ESP32H2)
|
||||||
|
if ((pin >=0 && pin <= 5) || //6
|
||||||
|
(pin >= 8 && pin <= 14) || //7
|
||||||
|
(pin >= 22 && pin <= 27)) //6
|
||||||
|
#else
|
||||||
|
if ((pin == 2) || //1
|
||||||
|
(pin == 4) || //1
|
||||||
|
(pin == 5) || //1
|
||||||
|
((pin >= 12) && (pin <= 19)) || //8
|
||||||
|
((pin >= 21) && (pin <= 23)) || //3
|
||||||
|
((pin >= 25) && (pin <= 27)) || //3
|
||||||
|
(pin == 32) || (pin == 33)) //2
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
static int channelsRemaining() {
|
||||||
|
return NUM_PWM - PWMCount;
|
||||||
|
}
|
||||||
|
static boolean DISABLE_DAC;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
ESP32PWM* pwmFactory(int pin);
|
||||||
|
|
||||||
|
#endif /* LIBRARIES_ESP32SERVO_SRC_ESP32PWM_H_ */
|
||||||
@@ -50,87 +50,75 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|||||||
* The ESP32 is a 32 bit processor that includes FP support; this code reflects that fact.
|
* The ESP32 is a 32 bit processor that includes FP support; this code reflects that fact.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ESP32_Servo.h"
|
#include <ESP32Servo.h>
|
||||||
#include "esp32-hal-ledc.h"
|
#if defined(ARDUINO)
|
||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
// initialize the class variable ServoCount
|
static const char* TAG = "ESP32Servo";
|
||||||
int Servo::ServoCount = 0;
|
|
||||||
|
|
||||||
// The ChannelUsed array elements are 0 if never used, 1 if in use, and -1 if used and disposed
|
|
||||||
// (i.e., available for reuse)
|
|
||||||
int Servo::ChannelUsed[MAX_SERVOS+1] = {0}; // we ignore the zeroth element
|
|
||||||
|
|
||||||
Servo::Servo()
|
Servo::Servo()
|
||||||
{
|
{ // initialize this channel with plausible values, except pin # (we set pin # when attached)
|
||||||
this->servoChannel = 0;
|
REFRESH_CPS = 50;
|
||||||
// see if there is a servo channel available for reuse
|
this->ticks = DEFAULT_PULSE_WIDTH_TICKS;
|
||||||
bool foundChannelForReuse = false;
|
this->timer_width = DEFAULT_TIMER_WIDTH;
|
||||||
for (int i = 1; i < MAX_SERVOS+1; i++)
|
this->pinNumber = -1; // make it clear that we haven't attached a pin to this channel
|
||||||
{
|
this->min = DEFAULT_uS_LOW;
|
||||||
if (ChannelUsed[i] == -1)
|
this->max = DEFAULT_uS_HIGH;
|
||||||
{
|
this->timer_width_ticks = pow(2,this->timer_width);
|
||||||
// reclaim this channel
|
|
||||||
ChannelUsed[i] = 1;
|
}
|
||||||
this->servoChannel = i;
|
ESP32PWM * Servo::getPwm(){
|
||||||
foundChannelForReuse = true;
|
|
||||||
break;
|
return &pwm;
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!foundChannelForReuse)
|
|
||||||
{
|
|
||||||
// no channels available for reuse; get a new one if we can
|
|
||||||
if (ServoCount < MAX_SERVOS)
|
|
||||||
{
|
|
||||||
this->servoChannel = ++ServoCount; // assign a servo channel number to this instance
|
|
||||||
ChannelUsed[this->servoChannel] = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this->servoChannel = 0; // too many servos in use
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if we got a channel either way, finish initializing it
|
|
||||||
if (this->servoChannel > 0)
|
|
||||||
{
|
|
||||||
// initialize this channel with plausible values, except pin # (we set pin # when attached)
|
|
||||||
this->ticks = DEFAULT_PULSE_WIDTH_TICKS;
|
|
||||||
this->timer_width = DEFAULT_TIMER_WIDTH;
|
|
||||||
this->pinNumber = -1; // make it clear that we haven't attached a pin to this channel
|
|
||||||
this->min = DEFAULT_uS_LOW;
|
|
||||||
this->max = DEFAULT_uS_HIGH;
|
|
||||||
this->timer_width_ticks = pow(2,this->timer_width);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Servo::attach(int pin)
|
int Servo::attach(int pin)
|
||||||
{
|
{
|
||||||
|
|
||||||
return (this->attach(pin, DEFAULT_uS_LOW, DEFAULT_uS_HIGH));
|
return (this->attach(pin, DEFAULT_uS_LOW, DEFAULT_uS_HIGH));
|
||||||
}
|
}
|
||||||
|
|
||||||
int Servo::attach(int pin, int min, int max)
|
int Servo::attach(int pin, int min, int max)
|
||||||
{
|
{
|
||||||
if ((this->servoChannel <= MAX_SERVOS) && (this->servoChannel > 0))
|
ESP_LOGW(TAG, "Attempting to Attach servo on pin=%d min=%d max=%d",pin,min,max);
|
||||||
{
|
|
||||||
// Recommend only the following pins 2,4,12-19,21-23,25-27,32-33 (enforcement commented out)
|
#ifdef ENFORCE_PINS
|
||||||
//if ((pin == 2) || (pin ==4) || ((pin >= 12) && (pin <= 19)) || ((pin >= 21) && (pin <= 23)) ||
|
// ESP32 Recommend only the following pins 2,4,12-19,21-23,25-27,32-33
|
||||||
// ((pin >= 25) && (pin <= 27)) || (pin == 32) || (pin == 33))
|
// ESP32-S2 only the following pins 1-21,26,33-42
|
||||||
//{
|
if (pwm.hasPwm(pin))
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
// OK to proceed; first check for new/reuse
|
// OK to proceed; first check for new/reuse
|
||||||
if (this->pinNumber < 0) // we are attaching to a new or previously detached pin; we need to initialize/reinitialize
|
if (this->pinNumber < 0) // we are attaching to a new or previously detached pin; we need to initialize/reinitialize
|
||||||
{
|
{
|
||||||
// claim/reclaim this channel
|
|
||||||
ChannelUsed[this->servoChannel] = 1;
|
|
||||||
this->ticks = DEFAULT_PULSE_WIDTH_TICKS;
|
this->ticks = DEFAULT_PULSE_WIDTH_TICKS;
|
||||||
this->timer_width = DEFAULT_TIMER_WIDTH;
|
this->timer_width = DEFAULT_TIMER_WIDTH;
|
||||||
this->timer_width_ticks = pow(2,this->timer_width);
|
this->timer_width_ticks = pow(2,this->timer_width);
|
||||||
}
|
}
|
||||||
this->pinNumber = pin;
|
this->pinNumber = pin;
|
||||||
//}
|
#ifdef ENFORCE_PINS
|
||||||
//else
|
}
|
||||||
//{
|
else
|
||||||
// return 0;
|
{
|
||||||
//}
|
#ifdef __XTENSA_esp32s3__
|
||||||
|
if(
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
|
ESP_LOGE(TAG, "This pin can not be a servo: %d Servo available on: 1-21,26,33-42", pin);
|
||||||
|
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
ESP_LOGE(TAG, "This pin can not be a servo: %d Servo available on: 1-21,35-45,47-48", pin);
|
||||||
|
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||||
|
ESP_LOGE(TAG, "This pin can not be a servo: %d Servo available on: 1-10,18-21", pin);
|
||||||
|
#else
|
||||||
|
ESP_LOGE(TAG, "This pin can not be a servo: %d Servo available on: 2,4,5,12-19,21-23,25-27,32-33",pin);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// min/max checks
|
// min/max checks
|
||||||
if (min < MIN_PULSE_WIDTH) // ensure pulse width is valid
|
if (min < MIN_PULSE_WIDTH) // ensure pulse width is valid
|
||||||
@@ -141,19 +129,20 @@ int Servo::attach(int pin, int min, int max)
|
|||||||
this->max = max; //store this value in uS
|
this->max = max; //store this value in uS
|
||||||
// Set up this channel
|
// Set up this channel
|
||||||
// if you want anything other than default timer width, you must call setTimerWidth() before attach
|
// if you want anything other than default timer width, you must call setTimerWidth() before attach
|
||||||
ledcSetup(this->servoChannel, REFRESH_CPS, this->timer_width); // channel #, 50 Hz, timer width
|
|
||||||
ledcAttachPin(this->pinNumber, this->servoChannel); // GPIO pin assigned to channel
|
pwm.attachPin(this->pinNumber,REFRESH_CPS, this->timer_width ); // GPIO pin assigned to channel
|
||||||
}
|
ESP_LOGW(TAG, "Success to Attach servo : %d on PWM %d",pin,pwm.getChannel());
|
||||||
else return 0;
|
|
||||||
|
return pwm.getChannel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Servo::detach()
|
void Servo::detach()
|
||||||
{
|
{
|
||||||
if (this->attached())
|
if (this->attached())
|
||||||
{
|
{
|
||||||
ledcDetachPin(this->pinNumber);
|
|
||||||
//keep track of detached servos channels so we can reuse them if needed
|
//keep track of detached servos channels so we can reuse them if needed
|
||||||
ChannelUsed[this->servoChannel] = -1;
|
pwm.detachPin(this->pinNumber);
|
||||||
|
|
||||||
this->pinNumber = -1;
|
this->pinNumber = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,30 +164,39 @@ void Servo::write(int value)
|
|||||||
|
|
||||||
void Servo::writeMicroseconds(int value)
|
void Servo::writeMicroseconds(int value)
|
||||||
{
|
{
|
||||||
// calculate and store the values for the given channel
|
writeTicks(usToTicks(value)); // convert to ticks
|
||||||
if ((this->servoChannel <= MAX_SERVOS) && (this->attached())) // ensure channel is valid
|
}
|
||||||
{
|
|
||||||
if (value < this->min) // ensure pulse width is valid
|
|
||||||
value = this->min;
|
|
||||||
else if (value > this->max)
|
|
||||||
value = this->max;
|
|
||||||
|
|
||||||
value = usToTicks(value); // convert to ticks
|
void Servo::writeTicks(int value)
|
||||||
|
{
|
||||||
|
// calculate and store the values for the given channel
|
||||||
|
if (this->attached()) // ensure channel is valid
|
||||||
|
{
|
||||||
|
if (value < usToTicks(this->min)) // ensure ticks are in range
|
||||||
|
value = usToTicks(this->min);
|
||||||
|
else if (value > usToTicks(this->max))
|
||||||
|
value = usToTicks(this->max);
|
||||||
this->ticks = value;
|
this->ticks = value;
|
||||||
// do the actual write
|
// do the actual write
|
||||||
ledcWrite(this->servoChannel, this->ticks);
|
pwm.write( this->ticks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Servo::release()
|
||||||
|
{
|
||||||
|
if (this->attached()) // ensure channel is valid
|
||||||
|
pwm.write(0);
|
||||||
|
}
|
||||||
|
|
||||||
int Servo::read() // return the value as degrees
|
int Servo::read() // return the value as degrees
|
||||||
{
|
{
|
||||||
return (map(readMicroseconds()+1, this->min, this->max, 0, 180));
|
return (map(readMicroseconds(), this->min, this->max, 0, 180));
|
||||||
}
|
}
|
||||||
|
|
||||||
int Servo::readMicroseconds()
|
int Servo::readMicroseconds()
|
||||||
{
|
{
|
||||||
int pulsewidthUsec;
|
int pulsewidthUsec;
|
||||||
if ((this->servoChannel <= MAX_SERVOS) && (this->attached()))
|
if (this->attached())
|
||||||
{
|
{
|
||||||
pulsewidthUsec = ticksToUs(this->ticks);
|
pulsewidthUsec = ticksToUs(this->ticks);
|
||||||
}
|
}
|
||||||
@@ -210,18 +208,24 @@ int Servo::readMicroseconds()
|
|||||||
return (pulsewidthUsec);
|
return (pulsewidthUsec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Servo::readTicks()
|
||||||
|
{
|
||||||
|
return this->ticks;
|
||||||
|
}
|
||||||
|
|
||||||
bool Servo::attached()
|
bool Servo::attached()
|
||||||
{
|
{
|
||||||
return (ChannelUsed[this->servoChannel]);
|
return (pwm.attached());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Servo::setTimerWidth(int value)
|
void Servo::setTimerWidth(int value)
|
||||||
{
|
{
|
||||||
// only allow values between 16 and 20
|
// only allow values between 10 and 14 for ESP32-C3
|
||||||
if (value < 16)
|
// only allow values between 16 and 20 for other ESP32
|
||||||
value = 16;
|
if (value < MINIMUM_TIMER_WIDTH )
|
||||||
else if (value > 20)
|
value = MINIMUM_TIMER_WIDTH;
|
||||||
value = 20;
|
else if (value > MAXIMUM_TIMER_WIDTH)
|
||||||
|
value = MAXIMUM_TIMER_WIDTH;
|
||||||
|
|
||||||
// Fix the current ticks value after timer width change
|
// Fix the current ticks value after timer width change
|
||||||
// The user can reset the tick value with a write() or writeUs()
|
// The user can reset the tick value with a write() or writeUs()
|
||||||
@@ -229,23 +233,22 @@ void Servo::setTimerWidth(int value)
|
|||||||
// if positive multiply by diff; if neg, divide
|
// if positive multiply by diff; if neg, divide
|
||||||
if (widthDifference > 0)
|
if (widthDifference > 0)
|
||||||
{
|
{
|
||||||
this->ticks << widthDifference;
|
this->ticks = widthDifference * this->ticks;
|
||||||
}
|
}
|
||||||
else
|
else if (widthDifference < 0)
|
||||||
{
|
{
|
||||||
this->ticks >> widthDifference;
|
this->ticks = this->ticks/-widthDifference;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->timer_width = value;
|
this->timer_width = value;
|
||||||
this->timer_width_ticks = pow(2,this->timer_width);
|
this->timer_width_ticks = pow(2,this->timer_width);
|
||||||
|
|
||||||
// If this is an attached servo, clean up
|
// If this is an attached servo, clean up
|
||||||
if ((this->servoChannel <= MAX_SERVOS) && (this->attached()))
|
if (this->attached())
|
||||||
{
|
{
|
||||||
// detach, setup and attach again to reflect new timer width
|
// detach, setup and attach again to reflect new timer width
|
||||||
ledcDetachPin(this->pinNumber);
|
pwm.detachPin(this->pinNumber);
|
||||||
ledcSetup(this->servoChannel, REFRESH_CPS, this->timer_width);
|
pwm.attachPin(this->pinNumber, REFRESH_CPS, this->timer_width);
|
||||||
ledcAttachPin(this->pinNumber, this->servoChannel);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,12 +259,12 @@ int Servo::readTimerWidth()
|
|||||||
|
|
||||||
int Servo::usToTicks(int usec)
|
int Servo::usToTicks(int usec)
|
||||||
{
|
{
|
||||||
return (int)((float)usec / ((float)REFRESH_USEC / (float)this->timer_width_ticks));
|
return (int)((double)usec / ((double)REFRESH_USEC / (double)this->timer_width_ticks)*(((double)REFRESH_CPS)/50.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
int Servo::ticksToUs(int ticks)
|
int Servo::ticksToUs(int ticks)
|
||||||
{
|
{
|
||||||
return (int)((float)ticks * ((float)REFRESH_USEC / (float)this->timer_width_ticks));
|
return (int)((double)ticks * ((double)REFRESH_USEC / (double)this->timer_width_ticks)/(((double)REFRESH_CPS)/50.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
171
arduino-cli/libraries/ESP32Servo/src/ESP32Servo.h
Normal file
171
arduino-cli/libraries/ESP32Servo/src/ESP32Servo.h
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2017 John K. Bennett. All right reserved.
|
||||||
|
|
||||||
|
ESP32_Servo.h - Servo library for ESP32 - Version 1
|
||||||
|
|
||||||
|
Original Servo.h written by Michael Margolis in 2009
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
A servo is activated by creating an instance of the Servo class, and passing
|
||||||
|
the desired GPIO pin to the attach() method.
|
||||||
|
The servos are pulsed in the background using the value most recently
|
||||||
|
written using the write() method.
|
||||||
|
|
||||||
|
The class methods are:
|
||||||
|
|
||||||
|
Servo - Class for manipulating servo motors connected to ESP32 pins.
|
||||||
|
int attach(pin ) - Attaches the given GPIO pin to the next free channel
|
||||||
|
(channels that have previously been detached are used first),
|
||||||
|
returns channel number or 0 if failure. All pin numbers are allowed,
|
||||||
|
but only pins 2,4,12-19,21-23,25-27,32-33 are recommended.
|
||||||
|
int attach(pin, min, max ) - Attaches to a pin setting min and max
|
||||||
|
values in microseconds; enforced minimum min is 500, enforced max
|
||||||
|
is 2500. Other semantics same as attach().
|
||||||
|
void write () - Sets the servo angle in degrees; a value below 500 is
|
||||||
|
treated as a value in degrees (0 to 180). These limit are enforced,
|
||||||
|
i.e., values are treated as follows:
|
||||||
|
Value Becomes
|
||||||
|
----- -------
|
||||||
|
< 0 0
|
||||||
|
0 - 180 value (treated as degrees)
|
||||||
|
181 - 499 180
|
||||||
|
500 - (min-1) min
|
||||||
|
min-max (from attach or default) value (treated as microseconds)
|
||||||
|
(max+1) - 2500 max
|
||||||
|
|
||||||
|
void writeMicroseconds() - Sets the servo pulse width in microseconds.
|
||||||
|
min and max are enforced (see above).
|
||||||
|
int read() - Gets the last written servo pulse width as an angle between 0 and 180.
|
||||||
|
int readMicroseconds() - Gets the last written servo pulse width in microseconds.
|
||||||
|
bool attached() - Returns true if this servo instance is attached.
|
||||||
|
void detach() - Stops an the attached servo, frees its attached pin, and frees
|
||||||
|
its channel for reuse).
|
||||||
|
|
||||||
|
*** ESP32-specific functions **
|
||||||
|
setTimerWidth(value) - Sets the PWM timer width (must be 16-20) (ESP32 ONLY);
|
||||||
|
as a side effect, the pulse width is recomputed.
|
||||||
|
int readTimerWidth() - Gets the PWM timer width (ESP32 ONLY)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ESP32_Servo_h
|
||||||
|
#define ESP32_Servo_h
|
||||||
|
//#include "analogWrite.h"
|
||||||
|
#include "ESP32PWM.h"
|
||||||
|
//#include "ESP32Tone.h"
|
||||||
|
//Enforce only using PWM pins on the ESP32
|
||||||
|
#define ENFORCE_PINS
|
||||||
|
// Default Arduino Servo.h
|
||||||
|
#define DEFAULT_uS_LOW 544
|
||||||
|
#define DEFAULT_uS_HIGH 2400
|
||||||
|
|
||||||
|
// Values for TowerPro MG995 large servos (and many other hobbyist servos)
|
||||||
|
//#define DEFAULT_uS_LOW 1000 // 1000us
|
||||||
|
//#define DEFAULT_uS_HIGH 2000 // 2000us
|
||||||
|
|
||||||
|
// Values for TowerPro SG90 small servos
|
||||||
|
//#define DEFAULT_uS_LOW 400
|
||||||
|
//#define DEFAULT_uS_HIGH 2400
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ESP32C3_DEV
|
||||||
|
#define MINIMUM_TIMER_WIDTH 10
|
||||||
|
#define MAXIMUM_TIMER_WIDTH 14
|
||||||
|
#define DEFAULT_TIMER_WIDTH 10
|
||||||
|
#else
|
||||||
|
#define MINIMUM_TIMER_WIDTH 10
|
||||||
|
#define MAXIMUM_TIMER_WIDTH 20
|
||||||
|
#define DEFAULT_TIMER_WIDTH 10
|
||||||
|
#endif
|
||||||
|
#define DEFAULT_TIMER_WIDTH_TICKS 1024
|
||||||
|
|
||||||
|
#define ESP32_Servo_VERSION 1 // software version of this library
|
||||||
|
|
||||||
|
#define MIN_PULSE_WIDTH 500 // the shortest pulse sent to a servo
|
||||||
|
#define MAX_PULSE_WIDTH 2500 // the longest pulse sent to a servo
|
||||||
|
#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached
|
||||||
|
#define DEFAULT_PULSE_WIDTH_TICKS 4825
|
||||||
|
//#define REFRESH_CPS 50
|
||||||
|
#define REFRESH_USEC 20000
|
||||||
|
|
||||||
|
#define MAX_SERVOS 16 // no. of PWM channels in ESP32
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This group/channel/timmer mapping is for information only;
|
||||||
|
* the details are handled by lower-level code
|
||||||
|
*
|
||||||
|
* LEDC Chan to Group/Channel/Timer Mapping
|
||||||
|
** ledc: 0 => Group: 0, Channel: 0, Timer: 0
|
||||||
|
** ledc: 1 => Group: 0, Channel: 1, Timer: 0
|
||||||
|
** ledc: 2 => Group: 0, Channel: 2, Timer: 1
|
||||||
|
** ledc: 3 => Group: 0, Channel: 3, Timer: 1
|
||||||
|
** ledc: 4 => Group: 0, Channel: 4, Timer: 2
|
||||||
|
** ledc: 5 => Group: 0, Channel: 5, Timer: 2
|
||||||
|
** ledc: 6 => Group: 0, Channel: 6, Timer: 3
|
||||||
|
** ledc: 7 => Group: 0, Channel: 7, Timer: 3
|
||||||
|
** ledc: 8 => Group: 1, Channel: 0, Timer: 0
|
||||||
|
** ledc: 9 => Group: 1, Channel: 1, Timer: 0
|
||||||
|
** ledc: 10 => Group: 1, Channel: 2, Timer: 1
|
||||||
|
** ledc: 11 => Group: 1, Channel: 3, Timer: 1
|
||||||
|
** ledc: 12 => Group: 1, Channel: 4, Timer: 2
|
||||||
|
** ledc: 13 => Group: 1, Channel: 5, Timer: 2
|
||||||
|
** ledc: 14 => Group: 1, Channel: 6, Timer: 3
|
||||||
|
** ledc: 15 => Group: 1, Channel: 7, Timer: 3
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Servo {
|
||||||
|
|
||||||
|
public:
|
||||||
|
Servo();
|
||||||
|
// Arduino Servo Library calls
|
||||||
|
int attach(int pin); // attach the given pin to the next free channel, returns channel number or 0 if failure
|
||||||
|
int attach(int pin, int min, int max); // as above but also sets min and max values for writes.
|
||||||
|
void detach();
|
||||||
|
void write(int value); // if value is < MIN_PULSE_WIDTH its treated as an angle, otherwise as pulse width in microseconds
|
||||||
|
void writeMicroseconds(int value); // Write pulse width in microseconds
|
||||||
|
void writeTicks(int value); // Write ticks, the smallest increment the servo can handle
|
||||||
|
void release();
|
||||||
|
int read(); // returns current pulse width as an angle between 0 and 180 degrees
|
||||||
|
int readMicroseconds(); // returns current pulse width in microseconds for this servo
|
||||||
|
int readTicks(); // returns current ticks, the smallest increment the servo can handle
|
||||||
|
bool attached(); // return true if this servo is attached, otherwise false
|
||||||
|
|
||||||
|
// ESP32 only functions
|
||||||
|
void setTimerWidth(int value); // set the PWM timer width (ESP32 ONLY)
|
||||||
|
int readTimerWidth(); // get the PWM timer width (ESP32 ONLY)
|
||||||
|
void setPeriodHertz(int hertz){
|
||||||
|
REFRESH_CPS=hertz;
|
||||||
|
setTimerWidth(this->timer_width);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
int usToTicks(int usec);
|
||||||
|
int ticksToUs(int ticks);
|
||||||
|
// static int ServoCount; // the total number of attached servos
|
||||||
|
// static int ChannelUsed[]; // used to track whether a channel is in service
|
||||||
|
// int servoChannel = 0; // channel number for this servo
|
||||||
|
|
||||||
|
int min = DEFAULT_uS_LOW; // minimum pulse width for this servo
|
||||||
|
int max = DEFAULT_uS_HIGH; // maximum pulse width for this servo
|
||||||
|
int pinNumber = 0; // GPIO pin assigned to this channel
|
||||||
|
int timer_width = DEFAULT_TIMER_WIDTH; // ESP32 allows variable width PWM timers
|
||||||
|
int ticks = DEFAULT_PULSE_WIDTH_TICKS; // current pulse width on this channel
|
||||||
|
int timer_width_ticks = DEFAULT_TIMER_WIDTH_TICKS; // no. of ticks at rollover; varies with width
|
||||||
|
ESP32PWM * getPwm(); // get the PWM object
|
||||||
|
ESP32PWM pwm;
|
||||||
|
int REFRESH_CPS = 50;
|
||||||
|
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -1,267 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2017 John K. Bennett. All right reserved.
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
* Notes on the implementation:
|
|
||||||
* The ESP32 supports 16 hardware LED PWM channels that are intended
|
|
||||||
* to be used for LED brightness control. The low level ESP32 code
|
|
||||||
* (esp32-hal-ledc.*) allows us to set the PWM frequency and bit-depth,
|
|
||||||
* and then manipulate them by setting bits in the relevant control
|
|
||||||
* registers.
|
|
||||||
*
|
|
||||||
* Different servos require different pulse widths to vary servo angle, but the range is
|
|
||||||
* an approximately 500-2500 microsecond pulse every 20ms (50Hz). In general, hobbyist servos
|
|
||||||
* sweep 180 degrees, so the lowest number in the published range for a particular servo
|
|
||||||
* represents an angle of 0 degrees, the middle of the range represents 90 degrees, and the top
|
|
||||||
* of the range represents 180 degrees. So for example, if the range is 1000us to 2000us,
|
|
||||||
* 1000us would equal an angle of 0, 1500us would equal 90 degrees, and 2000us would equal 180
|
|
||||||
* degrees. We vary pulse width (recall that the pulse period is already set to 20ms) as follows:
|
|
||||||
*
|
|
||||||
* The ESP32 PWM timers allow us to set the timer width (max 20 bits). Thus
|
|
||||||
* the timer "tick" length is (pulse_period/2**timer_width), and the equation for pulse_high_width
|
|
||||||
* (the portion of the 20ms cycle that the signal is high) becomes:
|
|
||||||
*
|
|
||||||
* pulse_high_width = count * tick_length
|
|
||||||
* = count * (pulse_period/2**timer_width)
|
|
||||||
*
|
|
||||||
* and count = (pulse_high_width / (pulse_period/2**timer_width))
|
|
||||||
*
|
|
||||||
* So, for example, if I want a 1500us pulse_high_width, I set pulse_period to 20ms (20000us)
|
|
||||||
* (this value is set in the ledcSetup call), and count (used in the ledcWrite call) to
|
|
||||||
* 1500/(20000/65536), or 4924. This is the value we write to the timer in the ledcWrite call.
|
|
||||||
* If we increase the timer_width, the timer_count values need to be adjusted.
|
|
||||||
*
|
|
||||||
* The servo signal pins connect to any available GPIO pins on the ESP32, but not all pins are
|
|
||||||
* GPIO pins.
|
|
||||||
*
|
|
||||||
* The ESP32 is a 32 bit processor that includes FP support; this code reflects that fact.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ESP32_Servo.h"
|
|
||||||
#include "esp32-hal-ledc.h"
|
|
||||||
#include "Arduino.h"
|
|
||||||
|
|
||||||
// initialize the class variable ServoCount
|
|
||||||
int Servo::ServoCount = 0;
|
|
||||||
|
|
||||||
// The ChannelUsed array elements are 0 if never used, 1 if in use, and -1 if used and disposed
|
|
||||||
// (i.e., available for reuse)
|
|
||||||
int Servo::ChannelUsed[MAX_SERVOS+1] = {0}; // we ignore the zeroth element
|
|
||||||
|
|
||||||
Servo::Servo()
|
|
||||||
{
|
|
||||||
this->servoChannel = 0;
|
|
||||||
// see if there is a servo channel available for reuse
|
|
||||||
bool foundChannelForReuse = false;
|
|
||||||
for (int i = 1; i < MAX_SERVOS+1; i++)
|
|
||||||
{
|
|
||||||
if (ChannelUsed[i] == -1)
|
|
||||||
{
|
|
||||||
// reclaim this channel
|
|
||||||
ChannelUsed[i] = 1;
|
|
||||||
this->servoChannel = i;
|
|
||||||
foundChannelForReuse = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!foundChannelForReuse)
|
|
||||||
{
|
|
||||||
// no channels available for reuse; get a new one if we can
|
|
||||||
if (ServoCount < MAX_SERVOS)
|
|
||||||
{
|
|
||||||
this->servoChannel = ++ServoCount; // assign a servo channel number to this instance
|
|
||||||
ChannelUsed[this->servoChannel] = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this->servoChannel = 0; // too many servos in use
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if we got a channel either way, finish initializing it
|
|
||||||
if (this->servoChannel > 0)
|
|
||||||
{
|
|
||||||
// initialize this channel with plausible values, except pin # (we set pin # when attached)
|
|
||||||
this->ticks = DEFAULT_PULSE_WIDTH_TICKS;
|
|
||||||
this->timer_width = DEFAULT_TIMER_WIDTH;
|
|
||||||
this->pinNumber = -1; // make it clear that we haven't attached a pin to this channel
|
|
||||||
this->min = DEFAULT_uS_LOW;
|
|
||||||
this->max = DEFAULT_uS_HIGH;
|
|
||||||
this->timer_width_ticks = pow(2,this->timer_width);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int Servo::attach(int pin)
|
|
||||||
{
|
|
||||||
return (this->attach(pin, DEFAULT_uS_LOW, DEFAULT_uS_HIGH));
|
|
||||||
}
|
|
||||||
|
|
||||||
int Servo::attach(int pin, int min, int max)
|
|
||||||
{
|
|
||||||
if ((this->servoChannel <= MAX_SERVOS) && (this->servoChannel > 0))
|
|
||||||
{
|
|
||||||
// Recommend only the following pins 2,4,12-19,21-23,25-27,32-33 (enforcement commented out)
|
|
||||||
//if ((pin == 2) || (pin ==4) || ((pin >= 12) && (pin <= 19)) || ((pin >= 21) && (pin <= 23)) ||
|
|
||||||
// ((pin >= 25) && (pin <= 27)) || (pin == 32) || (pin == 33))
|
|
||||||
//{
|
|
||||||
// OK to proceed; first check for new/reuse
|
|
||||||
if (this->pinNumber < 0) // we are attaching to a new or previously detached pin; we need to initialize/reinitialize
|
|
||||||
{
|
|
||||||
// claim/reclaim this channel
|
|
||||||
ChannelUsed[this->servoChannel] = 1;
|
|
||||||
this->ticks = DEFAULT_PULSE_WIDTH_TICKS;
|
|
||||||
this->timer_width = DEFAULT_TIMER_WIDTH;
|
|
||||||
this->timer_width_ticks = pow(2,this->timer_width);
|
|
||||||
}
|
|
||||||
this->pinNumber = pin;
|
|
||||||
//}
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
// return 0;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// min/max checks
|
|
||||||
if (min < MIN_PULSE_WIDTH) // ensure pulse width is valid
|
|
||||||
min = MIN_PULSE_WIDTH;
|
|
||||||
if (max > MAX_PULSE_WIDTH)
|
|
||||||
max = MAX_PULSE_WIDTH;
|
|
||||||
this->min = min; //store this value in uS
|
|
||||||
this->max = max; //store this value in uS
|
|
||||||
// Set up this channel
|
|
||||||
// if you want anything other than default timer width, you must call setTimerWidth() before attach
|
|
||||||
ledcSetup(this->servoChannel, REFRESH_CPS, this->timer_width); // channel #, 50 Hz, timer width
|
|
||||||
ledcAttachPin(this->pinNumber, this->servoChannel); // GPIO pin assigned to channel
|
|
||||||
}
|
|
||||||
else return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Servo::detach()
|
|
||||||
{
|
|
||||||
if (this->attached())
|
|
||||||
{
|
|
||||||
ledcDetachPin(this->pinNumber);
|
|
||||||
//keep track of detached servos channels so we can reuse them if needed
|
|
||||||
ChannelUsed[this->servoChannel] = -1;
|
|
||||||
this->pinNumber = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Servo::write(int value)
|
|
||||||
{
|
|
||||||
// treat values less than MIN_PULSE_WIDTH (500) as angles in degrees (valid values in microseconds are handled as microseconds)
|
|
||||||
if (value < MIN_PULSE_WIDTH)
|
|
||||||
{
|
|
||||||
if (value < 0)
|
|
||||||
value = 0;
|
|
||||||
else if (value > 180)
|
|
||||||
value = 180;
|
|
||||||
|
|
||||||
value = map(value, 0, 180, this->min, this->max);
|
|
||||||
}
|
|
||||||
this->writeMicroseconds(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Servo::writeMicroseconds(int value)
|
|
||||||
{
|
|
||||||
// calculate and store the values for the given channel
|
|
||||||
if ((this->servoChannel <= MAX_SERVOS) && (this->attached())) // ensure channel is valid
|
|
||||||
{
|
|
||||||
if (value < this->min) // ensure pulse width is valid
|
|
||||||
value = this->min;
|
|
||||||
else if (value > this->max)
|
|
||||||
value = this->max;
|
|
||||||
|
|
||||||
value = usToTicks(value); // convert to ticks
|
|
||||||
this->ticks = value;
|
|
||||||
// do the actual write
|
|
||||||
ledcWrite(this->servoChannel, this->ticks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int Servo::read() // return the value as degrees
|
|
||||||
{
|
|
||||||
return (map(readMicroseconds()+1, this->min, this->max, 0, 180));
|
|
||||||
}
|
|
||||||
|
|
||||||
int Servo::readMicroseconds()
|
|
||||||
{
|
|
||||||
int pulsewidthUsec;
|
|
||||||
if ((this->servoChannel <= MAX_SERVOS) && (this->attached()))
|
|
||||||
{
|
|
||||||
pulsewidthUsec = ticksToUs(this->ticks);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pulsewidthUsec = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (pulsewidthUsec);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Servo::attached()
|
|
||||||
{
|
|
||||||
return (ChannelUsed[this->servoChannel]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Servo::setTimerWidth(int value)
|
|
||||||
{
|
|
||||||
// only allow values between 16 and 20
|
|
||||||
if (value < 16)
|
|
||||||
value = 16;
|
|
||||||
else if (value > 20)
|
|
||||||
value = 20;
|
|
||||||
|
|
||||||
// Fix the current ticks value after timer width change
|
|
||||||
// The user can reset the tick value with a write() or writeUs()
|
|
||||||
int widthDifference = this->timer_width - value;
|
|
||||||
// if positive multiply by diff; if neg, divide
|
|
||||||
if (widthDifference > 0)
|
|
||||||
{
|
|
||||||
this->ticks << widthDifference;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this->ticks >> widthDifference;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->timer_width = value;
|
|
||||||
this->timer_width_ticks = pow(2,this->timer_width);
|
|
||||||
|
|
||||||
// If this is an attached servo, clean up
|
|
||||||
if ((this->servoChannel <= MAX_SERVOS) && (this->attached()))
|
|
||||||
{
|
|
||||||
// detach, setup and attach again to reflect new timer width
|
|
||||||
ledcDetachPin(this->pinNumber);
|
|
||||||
ledcSetup(this->servoChannel, REFRESH_CPS, this->timer_width);
|
|
||||||
ledcAttachPin(this->pinNumber, this->servoChannel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int Servo::readTimerWidth()
|
|
||||||
{
|
|
||||||
return (this->timer_width);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Servo::usToTicks(int usec)
|
|
||||||
{
|
|
||||||
return (int)((float)usec / ((float)REFRESH_USEC / (float)this->timer_width_ticks));
|
|
||||||
}
|
|
||||||
|
|
||||||
int Servo::ticksToUs(int ticks)
|
|
||||||
{
|
|
||||||
return (int)((float)ticks * ((float)REFRESH_USEC / (float)this->timer_width_ticks));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2017 John K. Bennett. All right reserved.
|
|
||||||
|
|
||||||
ESP32_Servo.h - Servo library for ESP32 - Version 1
|
|
||||||
|
|
||||||
Original Servo.h written by Michael Margolis in 2009
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
A servo is activated by creating an instance of the Servo class, and passing
|
|
||||||
the desired GPIO pin to the attach() method.
|
|
||||||
The servos are pulsed in the background using the value most recently
|
|
||||||
written using the write() method.
|
|
||||||
|
|
||||||
The class methods are:
|
|
||||||
|
|
||||||
Servo - Class for manipulating servo motors connected to ESP32 pins.
|
|
||||||
int attach(pin ) - Attaches the given GPIO pin to the next free channel
|
|
||||||
(channels that have previously been detached are used first),
|
|
||||||
returns channel number or 0 if failure. All pin numbers are allowed,
|
|
||||||
but only pins 2,4,12-19,21-23,25-27,32-33 are recommended.
|
|
||||||
int attach(pin, min, max ) - Attaches to a pin setting min and max
|
|
||||||
values in microseconds; enforced minimum min is 500, enforced max
|
|
||||||
is 2500. Other semantics same as attach().
|
|
||||||
void write () - Sets the servo angle in degrees; a value below 500 is
|
|
||||||
treated as a value in degrees (0 to 180). These limit are enforced,
|
|
||||||
i.e., values are treated as follows:
|
|
||||||
Value Becomes
|
|
||||||
----- -------
|
|
||||||
< 0 0
|
|
||||||
0 - 180 value (treated as degrees)
|
|
||||||
181 - 499 180
|
|
||||||
500 - (min-1) min
|
|
||||||
min-max (from attach or default) value (treated as microseconds)
|
|
||||||
(max+1) - 2500 max
|
|
||||||
|
|
||||||
void writeMicroseconds() - Sets the servo pulse width in microseconds.
|
|
||||||
min and max are enforced (see above).
|
|
||||||
int read() - Gets the last written servo pulse width as an angle between 0 and 180.
|
|
||||||
int readMicroseconds() - Gets the last written servo pulse width in microseconds.
|
|
||||||
bool attached() - Returns true if this servo instance is attached.
|
|
||||||
void detach() - Stops an the attached servo, frees its attached pin, and frees
|
|
||||||
its channel for reuse).
|
|
||||||
|
|
||||||
*** ESP32-specific functions **
|
|
||||||
setTimerWidth(value) - Sets the PWM timer width (must be 16-20) (ESP32 ONLY);
|
|
||||||
as a side effect, the pulse width is recomputed.
|
|
||||||
int readTimerWidth() - Gets the PWM timer width (ESP32 ONLY)
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ESP32_Servo_h
|
|
||||||
#define ESP32_Servo_h
|
|
||||||
|
|
||||||
// Values for TowerPro MG995 large servos (and many other hobbyist servos)
|
|
||||||
#define DEFAULT_uS_LOW 1000 // 1000us
|
|
||||||
#define DEFAULT_uS_HIGH 2000 // 2000us
|
|
||||||
|
|
||||||
// Values for TowerPro SG90 small servos
|
|
||||||
//#define DEFAULT_uS_LOW 400
|
|
||||||
//#define DEFAULT_uS_HIGH 2400
|
|
||||||
|
|
||||||
#define DEFAULT_TIMER_WIDTH 16
|
|
||||||
#define DEFAULT_TIMER_WIDTH_TICKS 65536
|
|
||||||
|
|
||||||
#define ESP32_Servo_VERSION 1 // software version of this library
|
|
||||||
|
|
||||||
#define MIN_PULSE_WIDTH 500 // the shortest pulse sent to a servo
|
|
||||||
#define MAX_PULSE_WIDTH 2500 // the longest pulse sent to a servo
|
|
||||||
#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached
|
|
||||||
#define DEFAULT_PULSE_WIDTH_TICKS 4825
|
|
||||||
#define REFRESH_CPS 50
|
|
||||||
#define REFRESH_USEC 20000
|
|
||||||
|
|
||||||
#define MAX_SERVOS 16 // no. of PWM channels in ESP32
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This group/channel/timmer mapping is for information only;
|
|
||||||
* the details are handled by lower-level code
|
|
||||||
*
|
|
||||||
* LEDC Chan to Group/Channel/Timer Mapping
|
|
||||||
** ledc: 0 => Group: 0, Channel: 0, Timer: 0
|
|
||||||
** ledc: 1 => Group: 0, Channel: 1, Timer: 0
|
|
||||||
** ledc: 2 => Group: 0, Channel: 2, Timer: 1
|
|
||||||
** ledc: 3 => Group: 0, Channel: 3, Timer: 1
|
|
||||||
** ledc: 4 => Group: 0, Channel: 4, Timer: 2
|
|
||||||
** ledc: 5 => Group: 0, Channel: 5, Timer: 2
|
|
||||||
** ledc: 6 => Group: 0, Channel: 6, Timer: 3
|
|
||||||
** ledc: 7 => Group: 0, Channel: 7, Timer: 3
|
|
||||||
** ledc: 8 => Group: 1, Channel: 0, Timer: 0
|
|
||||||
** ledc: 9 => Group: 1, Channel: 1, Timer: 0
|
|
||||||
** ledc: 10 => Group: 1, Channel: 2, Timer: 1
|
|
||||||
** ledc: 11 => Group: 1, Channel: 3, Timer: 1
|
|
||||||
** ledc: 12 => Group: 1, Channel: 4, Timer: 2
|
|
||||||
** ledc: 13 => Group: 1, Channel: 5, Timer: 2
|
|
||||||
** ledc: 14 => Group: 1, Channel: 6, Timer: 3
|
|
||||||
** ledc: 15 => Group: 1, Channel: 7, Timer: 3
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Servo
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Servo();
|
|
||||||
// Arduino Servo Library calls
|
|
||||||
int attach(int pin); // attach the given pin to the next free channel, returns channel number or 0 if failure
|
|
||||||
int attach(int pin, int min, int max); // as above but also sets min and max values for writes.
|
|
||||||
void detach();
|
|
||||||
void write(int value); // if value is < MIN_PULSE_WIDTH its treated as an angle, otherwise as pulse width in microseconds
|
|
||||||
void writeMicroseconds(int value); // Write pulse width in microseconds
|
|
||||||
int read(); // returns current pulse width as an angle between 0 and 180 degrees
|
|
||||||
int readMicroseconds(); // returns current pulse width in microseconds for this servo
|
|
||||||
bool attached(); // return true if this servo is attached, otherwise false
|
|
||||||
|
|
||||||
// ESP32 only functions
|
|
||||||
void setTimerWidth(int value); // set the PWM timer width (ESP32 ONLY)
|
|
||||||
int readTimerWidth(); // get the PWM timer width (ESP32 ONLY)
|
|
||||||
|
|
||||||
private:
|
|
||||||
int usToTicks(int usec);
|
|
||||||
int ticksToUs(int ticks);
|
|
||||||
static int ServoCount; // the total number of attached servos
|
|
||||||
static int ChannelUsed[]; // used to track whether a channel is in service
|
|
||||||
int servoChannel = 0; // channel number for this servo
|
|
||||||
int min = DEFAULT_uS_LOW; // minimum pulse width for this servo
|
|
||||||
int max = DEFAULT_uS_HIGH; // maximum pulse width for this servo
|
|
||||||
int pinNumber = 0; // GPIO pin assigned to this channel
|
|
||||||
int timer_width = DEFAULT_TIMER_WIDTH; // ESP32 allows variable width PWM timers
|
|
||||||
int ticks = DEFAULT_PULSE_WIDTH_TICKS; // current pulse width on this channel
|
|
||||||
int timer_width_ticks = DEFAULT_TIMER_WIDTH_TICKS; // no. of ticks at rollover; varies with width
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2017 John K. Bennett. All right reserved.
|
|
||||||
|
|
||||||
ESP32_Servo.h - Servo library for ESP32 - Version 1
|
|
||||||
|
|
||||||
Original Servo.h written by Michael Margolis in 2009
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
A servo is activated by creating an instance of the Servo class, and passing
|
|
||||||
the desired GPIO pin to the attach() method.
|
|
||||||
The servos are pulsed in the background using the value most recently
|
|
||||||
written using the write() method.
|
|
||||||
|
|
||||||
The class methods are:
|
|
||||||
|
|
||||||
Servo - Class for manipulating servo motors connected to ESP32 pins.
|
|
||||||
int attach(pin ) - Attaches the given GPIO pin to the next free channel
|
|
||||||
(channels that have previously been detached are used first),
|
|
||||||
returns channel number or 0 if failure. All pin numbers are allowed,
|
|
||||||
but only pins 2,4,12-19,21-23,25-27,32-33 are recommended.
|
|
||||||
int attach(pin, min, max ) - Attaches to a pin setting min and max
|
|
||||||
values in microseconds; enforced minimum min is 500, enforced max
|
|
||||||
is 2500. Other semantics same as attach().
|
|
||||||
void write () - Sets the servo angle in degrees; a value below 500 is
|
|
||||||
treated as a value in degrees (0 to 180). These limit are enforced,
|
|
||||||
i.e., values are treated as follows:
|
|
||||||
Value Becomes
|
|
||||||
----- -------
|
|
||||||
< 0 0
|
|
||||||
0 - 180 value (treated as degrees)
|
|
||||||
181 - 499 180
|
|
||||||
500 - (min-1) min
|
|
||||||
min-max (from attach or default) value (treated as microseconds)
|
|
||||||
(max+1) - 2500 max
|
|
||||||
|
|
||||||
void writeMicroseconds() - Sets the servo pulse width in microseconds.
|
|
||||||
min and max are enforced (see above).
|
|
||||||
int read() - Gets the last written servo pulse width as an angle between 0 and 180.
|
|
||||||
int readMicroseconds() - Gets the last written servo pulse width in microseconds.
|
|
||||||
bool attached() - Returns true if this servo instance is attached.
|
|
||||||
void detach() - Stops an the attached servo, frees its attached pin, and frees
|
|
||||||
its channel for reuse).
|
|
||||||
|
|
||||||
*** ESP32-specific functions **
|
|
||||||
setTimerWidth(value) - Sets the PWM timer width (must be 16-20) (ESP32 ONLY);
|
|
||||||
as a side effect, the pulse width is recomputed.
|
|
||||||
int readTimerWidth() - Gets the PWM timer width (ESP32 ONLY)
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ESP32_Servo_h
|
|
||||||
#define ESP32_Servo_h
|
|
||||||
|
|
||||||
// Values for TowerPro MG995 large servos (and many other hobbyist servos)
|
|
||||||
#define DEFAULT_uS_LOW 1000 // 1000us
|
|
||||||
#define DEFAULT_uS_HIGH 2000 // 2000us
|
|
||||||
|
|
||||||
// Values for TowerPro SG90 small servos
|
|
||||||
//#define DEFAULT_uS_LOW 400
|
|
||||||
//#define DEFAULT_uS_HIGH 2400
|
|
||||||
|
|
||||||
#define DEFAULT_TIMER_WIDTH 16
|
|
||||||
#define DEFAULT_TIMER_WIDTH_TICKS 65536
|
|
||||||
|
|
||||||
#define ESP32_Servo_VERSION 1 // software version of this library
|
|
||||||
|
|
||||||
#define MIN_PULSE_WIDTH 500 // the shortest pulse sent to a servo
|
|
||||||
#define MAX_PULSE_WIDTH 2500 // the longest pulse sent to a servo
|
|
||||||
#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached
|
|
||||||
#define DEFAULT_PULSE_WIDTH_TICKS 4825
|
|
||||||
#define REFRESH_CPS 50
|
|
||||||
#define REFRESH_USEC 20000
|
|
||||||
|
|
||||||
#define MAX_SERVOS 16 // no. of PWM channels in ESP32
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This group/channel/timmer mapping is for information only;
|
|
||||||
* the details are handled by lower-level code
|
|
||||||
*
|
|
||||||
* LEDC Chan to Group/Channel/Timer Mapping
|
|
||||||
** ledc: 0 => Group: 0, Channel: 0, Timer: 0
|
|
||||||
** ledc: 1 => Group: 0, Channel: 1, Timer: 0
|
|
||||||
** ledc: 2 => Group: 0, Channel: 2, Timer: 1
|
|
||||||
** ledc: 3 => Group: 0, Channel: 3, Timer: 1
|
|
||||||
** ledc: 4 => Group: 0, Channel: 4, Timer: 2
|
|
||||||
** ledc: 5 => Group: 0, Channel: 5, Timer: 2
|
|
||||||
** ledc: 6 => Group: 0, Channel: 6, Timer: 3
|
|
||||||
** ledc: 7 => Group: 0, Channel: 7, Timer: 3
|
|
||||||
** ledc: 8 => Group: 1, Channel: 0, Timer: 0
|
|
||||||
** ledc: 9 => Group: 1, Channel: 1, Timer: 0
|
|
||||||
** ledc: 10 => Group: 1, Channel: 2, Timer: 1
|
|
||||||
** ledc: 11 => Group: 1, Channel: 3, Timer: 1
|
|
||||||
** ledc: 12 => Group: 1, Channel: 4, Timer: 2
|
|
||||||
** ledc: 13 => Group: 1, Channel: 5, Timer: 2
|
|
||||||
** ledc: 14 => Group: 1, Channel: 6, Timer: 3
|
|
||||||
** ledc: 15 => Group: 1, Channel: 7, Timer: 3
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Servo
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Servo();
|
|
||||||
// Arduino Servo Library calls
|
|
||||||
int attach(int pin); // attach the given pin to the next free channel, returns channel number or 0 if failure
|
|
||||||
int attach(int pin, int min, int max); // as above but also sets min and max values for writes.
|
|
||||||
void detach();
|
|
||||||
void write(int value); // if value is < MIN_PULSE_WIDTH its treated as an angle, otherwise as pulse width in microseconds
|
|
||||||
void writeMicroseconds(int value); // Write pulse width in microseconds
|
|
||||||
int read(); // returns current pulse width as an angle between 0 and 180 degrees
|
|
||||||
int readMicroseconds(); // returns current pulse width in microseconds for this servo
|
|
||||||
bool attached(); // return true if this servo is attached, otherwise false
|
|
||||||
|
|
||||||
// ESP32 only functions
|
|
||||||
void setTimerWidth(int value); // set the PWM timer width (ESP32 ONLY)
|
|
||||||
int readTimerWidth(); // get the PWM timer width (ESP32 ONLY)
|
|
||||||
|
|
||||||
private:
|
|
||||||
int usToTicks(int usec);
|
|
||||||
int ticksToUs(int ticks);
|
|
||||||
static int ServoCount; // the total number of attached servos
|
|
||||||
static int ChannelUsed[]; // used to track whether a channel is in service
|
|
||||||
int servoChannel = 0; // channel number for this servo
|
|
||||||
int min = DEFAULT_uS_LOW; // minimum pulse width for this servo
|
|
||||||
int max = DEFAULT_uS_HIGH; // maximum pulse width for this servo
|
|
||||||
int pinNumber = 0; // GPIO pin assigned to this channel
|
|
||||||
int timer_width = DEFAULT_TIMER_WIDTH; // ESP32 allows variable width PWM timers
|
|
||||||
int ticks = DEFAULT_PULSE_WIDTH_TICKS; // current pulse width on this channel
|
|
||||||
int timer_width_ticks = DEFAULT_TIMER_WIDTH_TICKS; // no. of ticks at rollover; varies with width
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
Reference in New Issue
Block a user