Skip to content

Commit 74da596

Browse files
committed
Merge pull request esp8266#1503 from Makuna/ServoValueRoundOffFix
Servo value read and write fixes
2 parents efc8dda + acb7dc0 commit 74da596

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
@@ -56,6 +56,21 @@ static uint8_t s_servoCount = 0; // the total number of attached s_se
5656
#define SERVO_INDEX_TO_TIMER(servoIndex) ((ServoTimerSequence)(servoIndex / SERVOS_PER_TIMER)) // returns the timer controlling this servo
5757
#define SERVO_INDEX(timerId, channel) ((timerId * SERVOS_PER_TIMER) + channel) // macro to access servo index by timer and channel
5858

59+
// similiar to map but will have increased accuracy that provides a more
60+
// symetric api (call it and use result to reverse will provide the original value)
61+
//
62+
int improved_map(int value, int minIn, int maxIn, int minOut, int maxOut)
63+
{
64+
const int rangeIn = maxIn - minIn;
65+
const int rangeOut = maxOut - minOut;
66+
const int deltaIn = value - minIn;
67+
// fixed point math constants to improve accurancy of divide and rounding
68+
const int fixedHalfDecimal = 1;
69+
const int fixedDecimal = fixedHalfDecimal * 2;
70+
71+
return ((deltaIn * rangeOut * fixedDecimal) / (rangeIn) + fixedHalfDecimal) / fixedDecimal + minOut;
72+
}
73+
5974
//------------------------------------------------------------------------------
6075
// Interrupt handler template method that takes a class that implements
6176
// a standard set of methods for the timer abstraction
@@ -246,8 +261,10 @@ void Servo::write(int value)
246261
// treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
247262
if (value < MIN_PULSE_WIDTH) {
248263
// assumed to be 0-180 degrees servo
249-
value = max(0, min(180, value));
250-
value = map(value, 0, 180, _minUs, _maxUs);
264+
value = constrain(value, 0, 180);
265+
// writeMicroseconds will contrain the calculated value for us
266+
// for any user defined min and max, but we must use default min max
267+
value = improved_map(value, 0, 180, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
251268
}
252269
writeMicroseconds(value);
253270
}
@@ -257,15 +274,17 @@ void Servo::writeMicroseconds(int value)
257274
// ensure channel is valid
258275
if ((_servoIndex < MAX_SERVOS)) {
259276
// ensure pulse width is valid
260-
value = max(_minUs, min(_maxUs, value));
277+
value = constrain(value, _minUs, _maxUs);
261278

262279
s_servos[_servoIndex].usPulse = value;
263280
}
264281
}
265282

266283
int Servo::read() // return the value as degrees
267284
{
268-
return map(readMicroseconds(), _minUs, _maxUs, 0, 180);
285+
// read returns the angle for an assumed 0-180, so we calculate using
286+
// the normal min/max constants and not user defined ones
287+
return improved_map(readMicroseconds(), MIN_PULSE_WIDTH, MAX_PULSE_WIDTH, 0, 180);
269288
}
270289

271290
int Servo::readMicroseconds()

0 commit comments

Comments
 (0)