Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix and improve pulse width range #24

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

rileyjshaw
Copy link

This collects two functional commits (931445b, 36f25f4) which aim to simplify and improve behavior around the minimum/maximum pulse widths sent to the servo. Most notably, this brings Arduino's servo library in line with standard R/C pulse width expectations. It also allows for a wider range of servo motors to be used, by increasing the configurable pulse width range. Finally, it removes a few bugs and todos, bringing it in line with how the docs say this library should work.

Each commit has a detailed message about the changes. I can rebase these before merging if you'd prefer, but I thought they'd be easier to review broken up like this.

Cheers,

R/C servos have a standard pulse width range of 1000 to 2000µs<sup name="a1">[1](#f1)</sup>, with the zero point between the two at 1500µs. Currently, Arduino's Servo library sets:

- [`#define MIN_PULSE_WIDTH 544`](https://github.com/arduino-libraries/Servo/blob/4970d615a13f4c0d08026ee361cc8a01974924a2/src/Servo.h#L80)
- [`#define MAX_PULSE_WIDTH 2400`](https://github.com/arduino-libraries/Servo/blob/4970d615a13f4c0d08026ee361cc8a01974924a2/src/Servo.h#L81)
- [`#define DEFAULT_PULSE_WIDTH 1500`](https://github.com/arduino-libraries/Servo/blob/4970d615a13f4c0d08026ee361cc8a01974924a2/src/Servo.h#L82)

This causes a lot of confusion<sup name="a2">[2](#f2)</sup>, especially since [the docs say `write(90)` should correspond to the mid-point] (https://www.arduino.cc/en/Reference/ServoWrite); in actuality, it results in a call to `writeMicroseconds(1472)`<sup name="a3">[3](#f3)</sup>.

This change adjusts the defaults to align with R/C standards. Specifically,

- `write(0)` now corresponds to the standard min pulse width of 1000µs.
- `write(90)` now corresponds to the standard zero point pulse width, and aligns with the library's `DEFAULT_PULSE_WIDTH` variable.
- `write(180)` now corresponds to the standard max pulse width of 2000µs.

Tested on an Arduino Uno with a [Tower Pro Micro Servo SG90](http://www.ee.ic.ac.uk/pcheung/teaching/DE1_EE/stores/sg90_datasheet.pdf), and a [Parallax Feedback 360° High-Speed Servo](https://parallax.com/sites/default/files/downloads/900-00360-Feedback-360-HS-Servo-v1.2.pdf).

---

<a name="f1" href="#a1">1</a>: For example, http://www.ee.ic.ac.uk/pcheung/teaching/DE1_EE/stores/sg90_datasheet.pdf

<a name="f2" href="#a2">2</a>: For instance:

 - julianduque/beaglebone-io#54
 - arduino-libraries#3
 - https://toolguyd.com/oscilloscope-arduino-servo-pwm-signal-mistakes/
 - https://makezine.com/2014/04/23/arduinos-servo-library-angles-microseconds-and-optional-command-parameters/

I also see a _lot_ of posts on https://forum.arduino.cc about this.

<a name="f3" href="#a3">3</a>: There is actually no way to set a standard servo to the zero-point using `write(angle)`; the closest you can get is `write(92)`, for a pulse of 1504µs.
Different servo models can accept a wide range of pulse widths. Even different servos of the same model might vary a bit. Currently, the Arduino Servo library has a severely restricted hard limit on the pulse widths that can be sent to servos. Specifically:

- Minimum pulse width must be between [32, 1052].
- Maximum pulse width must be between [1888, 2908].

Many popular servos have min/max pulse widths that fall in that unavailable range between (1052, 1888). For instance, the [Parallax Feedback 360° High-Speed Servo](https://parallax.com/sites/default/files/downloads/900-00360-Feedback-360-HS-Servo-v1.2.pdf) operates between [1280, 1720].

Before this commit, each instance of `Servo` stored their `min` and `max` values as `int8_t`. Since that only leaves room for values in the range [-128, 127], it can't store meaningful servo pulse widths, which are typically in the ~[1000, 2000]µs range. To compensate, `min` and `max` store the distance from the default values, divided by 4…

There are two problems with this:

- The first, mentioned above, is that you can never stray more than 512µs from `MIN_PULSE_WIDTH` and `MAX_PULSE_WIDTH`.
- The second is that unexpected and unnecessary rounding occurs.

Simply storing `min` and `max` as `uint16_t` and using the values directly solves this problem, and reduces the complexity involved in working around it. This commit makes the library faster, and allows it to work with a wider range of servos. It also fixes some subtle bugs where the minimum value was hardcoded to `MIN_PULSE_WIDTH`.

Tested on an Arduino Uno with a [Tower Pro Micro Servo SG90](http://www.ee.ic.ac.uk/pcheung/teaching/DE1_EE/stores/sg90_datasheet.pdf), and a [Parallax Feedback 360° High-Speed Servo](https://parallax.com/sites/default/files/downloads/900-00360-Feedback-360-HS-Servo-v1.2.pdf).
While working on 36f25f4 and 931445b, I encountered some
typos, trailing whitespace, etc. I've moved the fixes into its own
commit to reduce noise in the functional diffs.

The only logic change here is removing some boundary checking
redundancy. I removed a redundant bounds check from `write()`, since
the value is `constrain`ed in `writeMicroseconds()` anyway. In
`writeMicroseconds()`, I changed:

```.cpp
if (value < this->min)          // ensure pulse width is valid
  value = this->min;
else if (value > this->max)
  value = this->max;
```

to:

```.cpp
value = constrain(value, this->min, this->max); // ensure pulse width is valid
```

They are functionally equivalent.
Since 36f25f4 and 931445b make changes to this library's public
API, I thought it was worth bumping to the next major version. Feel
free to drop this commit if you disagree, or have a larger release
planned.
@rileyjshaw
Copy link
Author

@pnndra tagging you since you were active on this repository recently. I can update this to fix the conflicts, but I wonder if you have any initial thoughts are before I continue.

@uniphil
Copy link

uniphil commented Aug 23, 2019

@facchinm any chance you can take a look at this?

@CLAassistant
Copy link

CLAassistant commented Apr 9, 2021

CLA assistant check
All committers have signed the CLA.

@uhertlein
Copy link

This seems like a reasonable and desired change to have.

@rileyjshaw
Copy link
Author

Thanks @uhertlein! @facchinm I’m still willing to fix the merge conflicts if you’re interested in the change.

@PaulStoffregen
Copy link

Does this change result in a change to public API, where existing projects with specific numbers to myServo.write() will have different behavior?

@rileyjshaw
Copy link
Author

Does this change result in a change to public API, where existing projects with specific numbers to myServo.write() will have different behavior?

It does, yes. Two options:

  • Add a separate method to the public API, for instance myServo.writeStandard().
  • Bump the major version number by 1 and release this as a breaking change.

Also, Paul, I want to take this opportunity to say thanks for all you’ve done and continue to do for the community! Between Teensy, your shared libraries, and your inspiring and well-documented projects, you’ve made a huge impact. Thanks for all your hard work, and for sharing it so generously.

@per1234 per1234 added type: enhancement Proposed improvement topic: code Related to content of the project itself labels Jun 26, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: code Related to content of the project itself type: enhancement Proposed improvement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants