Skip to content

Commit 9b437d7

Browse files
authored
Change servo defaults to safer defaults (#7023)
* The default timings were chosen vastly outside specification. On COTS servos, this causes much confusion for the user, because a wide range of angle starting from 0 degress, and a just a large range of angle leading up to 180 degrees, is dead - in fact, the servo doesn't move at all, from any position. Not investigated further for obvious reasons, as this may have also destroyed servos that ran hot trying to abide by the PWM signal but could not mechanically! * Change timing limits to safe values. With previous default timings and safety limits, popular servos could force against internal physical endstops, which could overload and destroy them. * Review action: revert changes to hard boundary for min/max duty cycle timings. Internal review, fix/drop legacy comments. * A Servo on each available ESP8266 GPIO (D0-D8), no observed interference. * Remove possible jerk due to force-cancelling duty cycle. * Overload attach: can specify initial angle for servo, too. * Stricter checks for configured, and default, bounded timings. * Missed the min/max in a comment. * Default microsecond lower bound of 1000 causes confusing behavior - 200 is minimum enforced elsewhere, so use it here too as nearest multiple of 100 from 180. * Comment rationale for changed defaults of min/max pulse widths. Comment rationale for changed defaults of min/max pulse widths.
1 parent c5c9f84 commit 9b437d7

File tree

2 files changed

+44
-27
lines changed

2 files changed

+44
-27
lines changed

libraries/Servo/src/Servo.cpp

+20-15
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2727
uint32_t Servo::_servoMap = 0;
2828

2929
// similiar to map but will have increased accuracy that provides a more
30-
// symetric api (call it and use result to reverse will provide the original value)
30+
// symmetrical api (call it and use result to reverse will provide the original value)
3131
int improved_map(int value, int minIn, int maxIn, int minOut, int maxOut)
3232
{
3333
const int rangeIn = maxIn - minIn;
3434
const int rangeOut = maxOut - minOut;
3535
const int deltaIn = value - minIn;
3636
// fixed point math constants to improve accurancy of divide and rounding
37-
const int fixedHalfDecimal = 1;
38-
const int fixedDecimal = fixedHalfDecimal * 2;
37+
constexpr int fixedHalfDecimal = 1;
38+
constexpr int fixedDecimal = fixedHalfDecimal * 2;
3939

4040
return ((deltaIn * rangeOut * fixedDecimal) / (rangeIn) + fixedHalfDecimal) / fixedDecimal + minOut;
4141
}
@@ -46,9 +46,9 @@ int improved_map(int value, int minIn, int maxIn, int minOut, int maxOut)
4646
Servo::Servo()
4747
{
4848
_attached = false;
49-
_valueUs = DEFAULT_PULSE_WIDTH;
50-
_minUs = MIN_PULSE_WIDTH;
51-
_maxUs = MAX_PULSE_WIDTH;
49+
_valueUs = DEFAULT_NEUTRAL_PULSE_WIDTH;
50+
_minUs = DEFAULT_MIN_PULSE_WIDTH;
51+
_maxUs = DEFAULT_MAX_PULSE_WIDTH;
5252
}
5353

5454
Servo::~Servo() {
@@ -58,10 +58,15 @@ Servo::~Servo() {
5858

5959
uint8_t Servo::attach(int pin)
6060
{
61-
return attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
61+
return attach(pin, DEFAULT_MIN_PULSE_WIDTH, DEFAULT_MAX_PULSE_WIDTH);
6262
}
6363

6464
uint8_t Servo::attach(int pin, uint16_t minUs, uint16_t maxUs)
65+
{
66+
return attach(pin, minUs, maxUs, _valueUs);
67+
}
68+
69+
uint8_t Servo::attach(int pin, uint16_t minUs, uint16_t maxUs, int value)
6570
{
6671
if (!_attached) {
6772
digitalWrite(pin, LOW);
@@ -76,7 +81,7 @@ uint8_t Servo::attach(int pin, uint16_t minUs, uint16_t maxUs)
7681
_maxUs = max((uint16_t)250, min((uint16_t)3000, maxUs));
7782
_minUs = max((uint16_t)200, min(_maxUs, minUs));
7883

79-
write(_valueUs);
84+
write(value);
8085

8186
return pin;
8287
}
@@ -85,27 +90,28 @@ void Servo::detach()
8590
{
8691
if (_attached) {
8792
_servoMap &= ~(1 << _pin);
93+
startWaveform(_pin, 0, REFRESH_INTERVAL, 1);
94+
delay(REFRESH_INTERVAL / 1000); // long enough to complete active period under all circumstances.
8895
stopWaveform(_pin);
8996
_attached = false;
90-
digitalWrite(_pin, LOW);
97+
_valueUs = DEFAULT_NEUTRAL_PULSE_WIDTH;
9198
}
9299
}
93100

94101
void Servo::write(int value)
95102
{
96-
// treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
97-
if (value < _minUs) {
103+
// treat any value less than 200 as angle in degrees (values equal or larger are handled as microseconds)
104+
if (value < 200) {
98105
// assumed to be 0-180 degrees servo
99106
value = constrain(value, 0, 180);
100-
// writeMicroseconds will contrain the calculated value for us
101-
// for any user defined min and max, but we must use default min max
102107
value = improved_map(value, 0, 180, _minUs, _maxUs);
103108
}
104109
writeMicroseconds(value);
105110
}
106111

107112
void Servo::writeMicroseconds(int value)
108113
{
114+
value = constrain(value, _minUs, _maxUs);
109115
_valueUs = value;
110116
if (_attached) {
111117
_servoMap &= ~(1 << _pin);
@@ -117,8 +123,7 @@ void Servo::writeMicroseconds(int value)
117123

118124
int Servo::read() // return the value as degrees
119125
{
120-
// read returns the angle for an assumed 0-180, so we calculate using
121-
// the normal min/max constants and not user defined ones
126+
// read returns the angle for an assumed 0-180
122127
return improved_map(readMicroseconds(), _minUs, _maxUs, 0, 180);
123128
}
124129

libraries/Servo/src/Servo.h

+24-12
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
//
2828
// Servo - Class for manipulating servo motors connected to Arduino pins.
2929
//
30-
// attach(pin ) - Attaches a servo motor to an i/o pin.
31-
// attach(pin, min, max ) - Attaches to a pin setting min and max values in microseconds
32-
// default min is 544, max is 2400
30+
// attach(pin) - Attaches a servo motor to an i/o pin.
31+
// attach(pin, min, max) - Attaches to a pin setting min and max values in microseconds
32+
// default min is 1000, max is 2000
3333
//
3434
// write() - Sets the servo angle in degrees. (invalid angle that is valid as pulse in microseconds is treated as microseconds)
3535
// writeMicroseconds() - Sets the servo pulse width in microseconds
@@ -44,13 +44,17 @@
4444

4545
#include <Arduino.h>
4646

47-
// the following are in us (microseconds)
48-
//
49-
#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo
50-
#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo
51-
#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached
52-
#define REFRESH_INTERVAL 20000 // minumim time to refresh servos in microseconds
53-
#define MAX_SERVOS 12
47+
// The following values are in us (microseconds).
48+
// Since the defaults can be overwritten in the new attach() member function,
49+
// they were modified from the Arduino AVR defaults to be in the safe range
50+
// of publically available specifications. While this implies that many 180°
51+
// servos do not operate the full 0° to 180° sweep using these, it also prevents
52+
// unsuspecting damage. For Arduino AVR, the same change is being discussed.
53+
#define DEFAULT_MIN_PULSE_WIDTH 1000 // uncalibrated default, the shortest duty cycle sent to a servo
54+
#define DEFAULT_MAX_PULSE_WIDTH 2000 // uncalibrated default, the longest duty cycle sent to a servo
55+
#define DEFAULT_NEUTRAL_PULSE_WIDTH 1500 // default duty cycle when servo is attached
56+
#define REFRESH_INTERVAL 20000 // classic default period to refresh servos in microseconds
57+
#define MAX_SERVOS 9 // D0-D8
5458

5559
#if !defined(ESP8266)
5660

@@ -63,8 +67,16 @@ class Servo
6367
public:
6468
Servo();
6569
~Servo();
66-
uint8_t attach(int pin); // attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure
67-
uint8_t attach(int pin, uint16_t min, uint16_t max); // as above but also sets min and max values for writes.
70+
// attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure.
71+
// returns channel number or 0 if failure.
72+
uint8_t attach(int pin);
73+
// attach the given pin to the next free channel, sets pinMode, min, and max values for write().
74+
// returns channel number or 0 if failure.
75+
uint8_t attach(int pin, uint16_t min, uint16_t max);
76+
// attach the given pin to the next free channel, sets pinMode, min, and max values for write(),
77+
// and sets the initial value, the same as write().
78+
// returns channel number or 0 if failure.
79+
uint8_t attach(int pin, uint16_t min, uint16_t max, int value);
6880
void detach();
6981
void write(int value); // if value is < 200 its treated as an angle, otherwise as pulse width in microseconds
7082
void writeMicroseconds(int value); // Write pulse width in microseconds

0 commit comments

Comments
 (0)