Skip to content

Commit acb7dc0

Browse files
committed
Servo value read and write fixes
Values that were written and then read would be different due to using map. If user defined a different min/max pulse width would cause the read/write to incorrectly calculate the pulse width.
1 parent e75c3d8 commit acb7dc0

File tree

1 file changed

+23
-4
lines changed

1 file changed

+23
-4
lines changed

libraries/Servo/src/esp8266/Servo.cpp

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,21 @@ static uint8_t s_servoCount = 0; // the total number of attached s_se
5353
#define SERVO_INDEX_TO_TIMER(servoIndex) ((ServoTimerSequence)(servoIndex / SERVOS_PER_TIMER)) // returns the timer controlling this servo
5454
#define SERVO_INDEX(timerId, channel) ((timerId * SERVOS_PER_TIMER) + channel) // macro to access servo index by timer and channel
5555

56+
// similiar to map but will have increased accuracy that provides a more
57+
// symetric api (call it and use result to reverse will provide the original value)
58+
//
59+
int improved_map(int value, int minIn, int maxIn, int minOut, int maxOut)
60+
{
61+
const int rangeIn = maxIn - minIn;
62+
const int rangeOut = maxOut - minOut;
63+
const int deltaIn = value - minIn;
64+
// fixed point math constants to improve accurancy of divide and rounding
65+
const int fixedHalfDecimal = 1;
66+
const int fixedDecimal = fixedHalfDecimal * 2;
67+
68+
return ((deltaIn * rangeOut * fixedDecimal) / (rangeIn) + fixedHalfDecimal) / fixedDecimal + minOut;
69+
}
70+
5671
//------------------------------------------------------------------------------
5772
// Interrupt handler template method that takes a class that implements
5873
// a standard set of methods for the timer abstraction
@@ -218,8 +233,10 @@ void Servo::write(int value)
218233
// treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
219234
if (value < MIN_PULSE_WIDTH) {
220235
// assumed to be 0-180 degrees servo
221-
value = max(0, min(180, value));
222-
value = map(value, 0, 180, _minUs, _maxUs);
236+
value = constrain(value, 0, 180);
237+
// writeMicroseconds will contrain the calculated value for us
238+
// for any user defined min and max, but we must use default min max
239+
value = improved_map(value, 0, 180, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
223240
}
224241
writeMicroseconds(value);
225242
}
@@ -229,15 +246,17 @@ void Servo::writeMicroseconds(int value)
229246
// ensure channel is valid
230247
if ((_servoIndex < MAX_SERVOS)) {
231248
// ensure pulse width is valid
232-
value = max(_minUs, min(_maxUs, value));
249+
value = constrain(value, _minUs, _maxUs);
233250

234251
s_servos[_servoIndex].usPulse = value;
235252
}
236253
}
237254

238255
int Servo::read() // return the value as degrees
239256
{
240-
return map(readMicroseconds(), _minUs, _maxUs, 0, 180);
257+
// read returns the angle for an assumed 0-180, so we calculate using
258+
// the normal min/max constants and not user defined ones
259+
return improved_map(readMicroseconds(), MIN_PULSE_WIDTH, MAX_PULSE_WIDTH, 0, 180);
241260
}
242261

243262
int Servo::readMicroseconds()

0 commit comments

Comments
 (0)