Skip to content

Commit e0b709f

Browse files
authored
Merge pull request #10 from dhalbert/check_fraction_range
Check fraction range; add post-constructor access to actuation_range and pulse widths.
2 parents c34abe2 + 38a9b60 commit e0b709f

File tree

3 files changed

+39
-21
lines changed

3 files changed

+39
-21
lines changed

adafruit_motor/motor.py

+7-13
Original file line numberDiff line numberDiff line change
@@ -51,27 +51,21 @@ class DCMotor:
5151
def __init__(self, positive_pwm, negative_pwm):
5252
self._positive = positive_pwm
5353
self._negative = negative_pwm
54+
self._throttle = None
5455

5556
@property
5657
def throttle(self):
57-
"""How much power is being delivered to the motor. Values range from ``-1.0`` (full
58-
throttle reverse) to ``1.0`` (full throttle forwards.) ``0`` will stop the motor from
59-
spinning and ``None`` will let the motor spin freely."""
60-
if self._positive.duty_cycle == 0 and self._negative.duty_cycle == 0:
61-
return None
62-
if self._positive.duty_cycle == 0xffff and self._negative.duty_cycle == 0xffff:
63-
return float(0)
64-
if self._positive.duty_cycle > 0 and self._negative.duty_cycle > 0:
65-
raise RuntimeError("PWMs in invalid state")
66-
value = max(self._positive.duty_cycle, self._negative.duty_cycle) / 0xffff
67-
if self._negative.duty_cycle > 0:
68-
return -1 * value
69-
return value
58+
"""Motor speed, ranging from -1.0 (full speed reverse) to 1.0 (full speed forward),
59+
or ``None``.
60+
If ``None``, both PWMs are turned full off. If ``0.0``, both PWMs are turned full on.
61+
"""
62+
return self._throttle
7063

7164
@throttle.setter
7265
def throttle(self, value):
7366
if value is not None and (value > 1.0 or value < -1.0):
7467
raise ValueError("Throttle must be None or between -1.0 and 1.0")
68+
self._throttle = value
7569
if value is None:
7670
self._positive.duty_cycle = 0
7771
self._negative.duty_cycle = 0

adafruit_motor/servo.py

+22-8
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,14 @@ class _BaseServo: # pylint: disable-msg=too-few-public-methods
4141
:param int min_pulse: The minimum pulse length of the servo in microseconds.
4242
:param int max_pulse: The maximum pulse length of the servo in microseconds."""
4343
def __init__(self, pwm_out, *, min_pulse=750, max_pulse=2250):
44-
self._min_duty = int((min_pulse * pwm_out.frequency) / 1000000 * 0xffff)
45-
max_duty = (max_pulse * pwm_out.frequency) / 1000000 * 0xffff
46-
self._duty_range = int(max_duty - self._min_duty)
4744
self._pwm_out = pwm_out
45+
self.set_pulse_width_range(min_pulse, max_pulse)
46+
47+
def set_pulse_width_range(self, min_pulse=750, max_pulse=2250):
48+
"""Change min and max pulse widths."""
49+
self._min_duty = int((min_pulse * self._pwm_out.frequency) / 1000000 * 0xffff)
50+
max_duty = (max_pulse * self._pwm_out.frequency) / 1000000 * 0xffff
51+
self._duty_range = int(max_duty - self._min_duty)
4852

4953
@property
5054
def fraction(self):
@@ -56,6 +60,8 @@ def fraction(self):
5660

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

@@ -68,6 +74,13 @@ class Servo(_BaseServo):
6874
:param int min_pulse: The minimum pulse width of the servo in microseconds.
6975
:param int max_pulse: The maximum pulse width of the servo in microseconds.
7076
77+
``actuation_range`` is an exposed property and can be changed at any time:
78+
79+
.. code-block:: python
80+
81+
servo = Servo(pwm)
82+
servo.actuation_range = 135
83+
7184
The specified pulse width range of a servo has historically been 1000-2000us,
7285
for a 90 degree range of motion. But nearly all modern servos have a 170-180
7386
degree range, and the pulse widths can go well out of the range to achieve this
@@ -80,22 +93,23 @@ class Servo(_BaseServo):
8093
get a wider range of movement. But if you go too low or too high,
8194
the servo mechanism may hit the end stops, buzz, and draw extra current as it stalls.
8295
Test carefully to find the safe minimum and maximum.
83-
"""
96+
"""
8497
def __init__(self, pwm_out, *, actuation_range=180, min_pulse=750, max_pulse=2250):
8598
super().__init__(pwm_out, min_pulse=min_pulse, max_pulse=max_pulse)
86-
self._actuation_range = actuation_range
99+
self.actuation_range = actuation_range
100+
"""The physical range of motion of the servo in degrees."""
87101
self._pwm = pwm_out
88102

89103
@property
90104
def angle(self):
91105
"""The servo angle in degrees. Must be in the range ``0`` to ``actuation_range``."""
92-
return self._actuation_range * self.fraction
106+
return self.actuation_range * self.fraction
93107

94108
@angle.setter
95109
def angle(self, new_angle):
96-
if new_angle < 0 or new_angle > self._actuation_range:
110+
if new_angle < 0 or new_angle > self.actuation_range:
97111
raise ValueError("Angle out of range")
98-
self.fraction = new_angle / self._actuation_range
112+
self.fraction = new_angle / self.actuation_range
99113

100114
class ContinuousServo(_BaseServo):
101115
"""Control a continuous rotation servo.

docs/conf.py

+10
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,16 @@
7474
# If this is True, todo emits a warning for each TODO entries. The default is False.
7575
todo_emit_warnings = True
7676

77+
# Avoid an `= None` on instance attributes with their own doc strings.
78+
# Workaround from here: https://github.com/sphinx-doc/sphinx/issues/2044#issuecomment-285888160
79+
#
80+
from sphinx.ext.autodoc import (
81+
ClassLevelDocumenter, InstanceAttributeDocumenter)
82+
83+
def iad_add_directive_header(self, sig):
84+
ClassLevelDocumenter.add_directive_header(self, sig)
85+
86+
InstanceAttributeDocumenter.add_directive_header = iad_add_directive_header
7787

7888
# -- Options for HTML output ----------------------------------------------
7989

0 commit comments

Comments
 (0)