Skip to content

Check fraction range; add post-constructor access to actuation_range and pulse widths. #10

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

Merged
merged 5 commits into from
Jul 3, 2018
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 20 additions & 7 deletions adafruit_motor/servo.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,14 @@ class _BaseServo: # pylint: disable-msg=too-few-public-methods
:param int min_pulse: The minimum pulse length of the servo in microseconds.
:param int max_pulse: The maximum pulse length of the servo in microseconds."""
def __init__(self, pwm_out, *, min_pulse=750, max_pulse=2250):
self._min_duty = int((min_pulse * pwm_out.frequency) / 1000000 * 0xffff)
max_duty = (max_pulse * pwm_out.frequency) / 1000000 * 0xffff
self._duty_range = int(max_duty - self._min_duty)
self._pwm_out = pwm_out
self.set_pulse_widths(min_pulse, max_pulse)

def set_pulse_widths(self, min_pulse=750, max_pulse=2250):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like set_pulse_width_range more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dropped the s too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it shouldn't have default values for the args, so you have to give both? I'm vacillating on this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having default values was what motivated the original choice of name.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like defaults because it allows one to use the name in the call. aka servo.set_pulse_width_range(min_pulse=700, max_pulse=2000) You could even force it with: def set_pulse_widths(self, *, min_pulse=750, max_pulse=2250):

"""Change pulse widths."""
self._min_duty = int((min_pulse * self._pwm_out.frequency) / 1000000 * 0xffff)
max_duty = (max_pulse * self._pwm_out.frequency) / 1000000 * 0xffff
self._duty_range = int(max_duty - self._min_duty)

@property
def fraction(self):
Expand All @@ -56,6 +60,8 @@ def fraction(self):

@fraction.setter
def fraction(self, value):
if not 0.0 <= value <= 1.0:
raise ValueError("Must be 0.0 to 1.0")
duty_cycle = self._min_duty + int(value * self._duty_range)
self._pwm_out.duty_cycle = duty_cycle

Expand All @@ -68,6 +74,13 @@ class Servo(_BaseServo):
:param int min_pulse: The minimum pulse width of the servo in microseconds.
:param int max_pulse: The maximum pulse width of the servo in microseconds.

``actuation_range`` is an exposed property and can be changed at any time:

.. code-block:: python

servo = Servo(pwm)
servo.actuation_range = 135

The specified pulse width range of a servo has historically been 1000-2000us,
for a 90 degree range of motion. But nearly all modern servos have a 170-180
degree range, and the pulse widths can go well out of the range to achieve this
Expand All @@ -83,19 +96,19 @@ class Servo(_BaseServo):
"""
def __init__(self, pwm_out, *, actuation_range=180, min_pulse=750, max_pulse=2250):
super().__init__(pwm_out, min_pulse=min_pulse, max_pulse=max_pulse)
self._actuation_range = actuation_range
self.actuation_range = actuation_range
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a triple quoted doc string here so it is included in RTD.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the Servo class has a docstring, it's already in RTD. See https://circuitpython.readthedocs.io/projects/motor/en/latest/api.html#adafruit_motor.servo.Servo.

Alphabetically ContinuousServo came first -- maybe that was confusing?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does for the argument but not the object attribute (like angle).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. actuation_angle should look like angle too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was impressively difficult to do this. I can add a docstring after the self.actuation_range = actuation_range, but then it shows up in the RTD as "actuation_range = None". After some Googling, found a hack to add to conf.py to fix it.

The other alternative is to document actuation_range as an :ivar, but then it shows up with the parameters. The current new way looks better.

self._pwm = pwm_out

@property
def angle(self):
"""The servo angle in degrees. Must be in the range ``0`` to ``actuation_range``."""
return self._actuation_range * self.fraction
return self.actuation_range * self.fraction

@angle.setter
def angle(self, new_angle):
if new_angle < 0 or new_angle > self._actuation_range:
if new_angle < 0 or new_angle > self.actuation_range:
raise ValueError("Angle out of range")
self.fraction = new_angle / self._actuation_range
self.fraction = new_angle / self.actuation_range

class ContinuousServo(_BaseServo):
"""Control a continuous rotation servo.
Expand Down