ESP32Servo
ESP32Servo.cpp
Go to the documentation of this file.
1 /*
2 Copyright (c) 2017 John K. Bennett. All right reserved.
3 
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8 
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13 
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 
18 * Notes on the implementation:
19 * The ESP32 supports 16 hardware LED PWM channels that are intended
20 * to be used for LED brightness control. The low level ESP32 code
21 * (esp32-hal-ledc.*) allows us to set the PWM frequency and bit-depth,
22 * and then manipulate them by setting bits in the relevant control
23 * registers.
24 *
25 * Different servos require different pulse widths to vary servo angle, but the range is
26 * an approximately 500-2500 microsecond pulse every 20ms (50Hz). In general, hobbyist servos
27 * sweep 180 degrees, so the lowest number in the published range for a particular servo
28 * represents an angle of 0 degrees, the middle of the range represents 90 degrees, and the top
29 * of the range represents 180 degrees. So for example, if the range is 1000us to 2000us,
30 * 1000us would equal an angle of 0, 1500us would equal 90 degrees, and 2000us would equal 180
31 * degrees. We vary pulse width (recall that the pulse period is already set to 20ms) as follows:
32 *
33 * The ESP32 PWM timers allow us to set the timer width (max 20 bits). Thus
34 * the timer "tick" length is (pulse_period/2**timer_width), and the equation for pulse_high_width
35 * (the portion of the 20ms cycle that the signal is high) becomes:
36 *
37 * pulse_high_width = count * tick_length
38 * = count * (pulse_period/2**timer_width)
39 *
40 * and count = (pulse_high_width / (pulse_period/2**timer_width))
41 *
42 * So, for example, if I want a 1500us pulse_high_width, I set pulse_period to 20ms (20000us)
43 * (this value is set in the ledcSetup call), and count (used in the ledcWrite call) to
44 * 1500/(20000/65536), or 4924. This is the value we write to the timer in the ledcWrite call.
45 * If we increase the timer_width, the timer_count values need to be adjusted.
46 *
47 * The servo signal pins connect to any available GPIO pins on the ESP32, but not all pins are
48 * GPIO pins.
49 *
50 * The ESP32 is a 32 bit processor that includes FP support; this code reflects that fact.
51 */
52 
53 #include <ESP32Servo.h>
54 #include "Arduino.h"
55 
56 //
58 { // initialize this channel with plausible values, except pin # (we set pin # when attached)
59  REFRESH_CPS = 50;
62  this->pinNumber = -1; // make it clear that we haven't attached a pin to this channel
63  this->min = DEFAULT_uS_LOW;
64  this->max = DEFAULT_uS_HIGH;
65  this->timer_width_ticks = pow(2,this->timer_width);
66 
67 }
69 
70  return &pwm;
71 }
72 
74 {
75 
76  return (this->attach(pin, DEFAULT_uS_LOW, DEFAULT_uS_HIGH));
77 }
78 
79 int Servo::attach(int pin, int min, int max)
80 {
81 
82 #ifdef ENFORCE_PINS
83  // Recommend only the following pins 2,4,12-19,21-23,25-27,32-33
84  if (pwm.hasPwm(pin))
85  {
86 #endif
87 
88  // OK to proceed; first check for new/reuse
89  if (this->pinNumber < 0) // we are attaching to a new or previously detached pin; we need to initialize/reinitialize
90  {
93  this->timer_width_ticks = pow(2,this->timer_width);
94  }
95  this->pinNumber = pin;
96 #ifdef ENFORCE_PINS
97  }
98  else
99  {
100  Serial.println("This pin can not be a servo: "+String(pin)+"\r\nServo availible on: 2,4,5,12-19,21-23,25-27,32-33");
101  return 0;
102  }
103 #endif
104 
105 
106  // min/max checks
107  if (min < MIN_PULSE_WIDTH) // ensure pulse width is valid
108  min = MIN_PULSE_WIDTH;
109  if (max > MAX_PULSE_WIDTH)
110  max = MAX_PULSE_WIDTH;
111  this->min = min; //store this value in uS
112  this->max = max; //store this value in uS
113  // Set up this channel
114  // if you want anything other than default timer width, you must call setTimerWidth() before attach
115  pwm.attachPin(this->pinNumber,REFRESH_CPS, this->timer_width ); // GPIO pin assigned to channel
116  //Serial.println("Attaching servo : "+String(pin)+" on PWM "+String(pwm.getChannel()));
117  return 1;
118 }
119 
121 {
122  if (this->attached())
123  {
124  //keep track of detached servos channels so we can reuse them if needed
125  pwm.detachPin(this->pinNumber);
126 
127  this->pinNumber = -1;
128  }
129 }
130 
131 void Servo::write(int value)
132 {
133  // treat values less than MIN_PULSE_WIDTH (500) as angles in degrees (valid values in microseconds are handled as microseconds)
134  if (value < MIN_PULSE_WIDTH)
135  {
136  if (value < 0)
137  value = 0;
138  else if (value > 180)
139  value = 180;
140 
141  value = map(value, 0, 180, this->min, this->max);
142  }
143  this->writeMicroseconds(value);
144 }
145 
147 {
148  // calculate and store the values for the given channel
149  if (this->attached()) // ensure channel is valid
150  {
151  if (value < this->min) // ensure pulse width is valid
152  value = this->min;
153  else if (value > this->max)
154  value = this->max;
155 
156  value = usToTicks(value); // convert to ticks
157  this->ticks = value;
158  // do the actual write
159  pwm.write( this->ticks);
160  }
161 }
162 
163 int Servo::read() // return the value as degrees
164 {
165  return (map(readMicroseconds()+1, this->min, this->max, 0, 180));
166 }
167 
169 {
170  int pulsewidthUsec;
171  if (this->attached())
172  {
173  pulsewidthUsec = ticksToUs(this->ticks);
174  }
175  else
176  {
177  pulsewidthUsec = 0;
178  }
179 
180  return (pulsewidthUsec);
181 }
182 
184 {
185  return (pwm.attached());
186 }
187 
188 void Servo::setTimerWidth(int value)
189 {
190  // only allow values between 16 and 20
191  if (value < 16)
192  value = 16;
193  else if (value > 20)
194  value = 20;
195 
196  // Fix the current ticks value after timer width change
197  // The user can reset the tick value with a write() or writeUs()
198  int widthDifference = this->timer_width - value;
199  // if positive multiply by diff; if neg, divide
200  if (widthDifference > 0)
201  {
202  this->ticks = widthDifference * this->ticks;
203  }
204  else if (widthDifference < 0)
205  {
206  this->ticks = this->ticks/-widthDifference;
207  }
208 
209  this->timer_width = value;
210  this->timer_width_ticks = pow(2,this->timer_width);
211 
212  // If this is an attached servo, clean up
213  if (this->attached())
214  {
215  // detach, setup and attach again to reflect new timer width
216  pwm.detachPin(this->pinNumber);
218  }
219 }
220 
222 {
223  return (this->timer_width);
224 }
225 
226 int Servo::usToTicks(int usec)
227 {
228  return (int)((float)usec / ((float)REFRESH_USEC / (float)this->timer_width_ticks)*(((float)REFRESH_CPS)/50.0));
229 }
230 
232 {
233  return (int)((float)ticks * ((float)REFRESH_USEC / (float)this->timer_width_ticks)/(((float)REFRESH_CPS)/50.0));
234 }
235 
236 
#define DEFAULT_TIMER_WIDTH
Definition: ESP32Servo.h:84
#define DEFAULT_PULSE_WIDTH_TICKS
Definition: ESP32Servo.h:92
void write(int value)
Definition: ESP32Servo.cpp:131
#define REFRESH_USEC
Definition: ESP32Servo.h:94
int max
Definition: ESP32Servo.h:150
#define MIN_PULSE_WIDTH
Definition: ESP32Servo.h:89
void detachPin(int pin)
Definition: ESP32PWM.cpp:250
void attachPin(uint8_t pin)
Definition: ESP32PWM.cpp:231
#define MAX_PULSE_WIDTH
Definition: ESP32Servo.h:90
int ticks
Definition: ESP32Servo.h:153
ESP32PWM pwm
Definition: ESP32Servo.h:156
bool attached()
Definition: ESP32Servo.cpp:183
int read()
Definition: ESP32Servo.cpp:163
void write(uint32_t duty)
Definition: ESP32PWM.cpp:162
int readTimerWidth()
Definition: ESP32Servo.cpp:221
int REFRESH_CPS
Definition: ESP32Servo.h:157
#define DEFAULT_uS_LOW
Definition: ESP32Servo.h:73
int pinNumber
Definition: ESP32Servo.h:151
int timer_width_ticks
Definition: ESP32Servo.h:154
ESP32PWM * getPwm()
Definition: ESP32Servo.cpp:68
void detach()
Definition: ESP32Servo.cpp:120
bool attached()
Definition: ESP32PWM.h:56
int timer_width
Definition: ESP32Servo.h:152
static bool hasPwm(int pin)
Definition: ESP32PWM.h:99
int attach(int pin)
Definition: ESP32Servo.cpp:73
void writeMicroseconds(int value)
Definition: ESP32Servo.cpp:146
void setTimerWidth(int value)
Definition: ESP32Servo.cpp:188
int min
Definition: ESP32Servo.h:149
int pin
Definition: ToneExample.ino:7
#define DEFAULT_uS_HIGH
Definition: ESP32Servo.h:74
int ticksToUs(int ticks)
Definition: ESP32Servo.cpp:231
int readMicroseconds()
Definition: ESP32Servo.cpp:168
int usToTicks(int usec)
Definition: ESP32Servo.cpp:226