From f0a1b0a8bd8f8d800131fa42ad433d65a7fb0c32 Mon Sep 17 00:00:00 2001 From: ladyada Date: Thu, 16 May 2019 14:48:36 -0400 Subject: [PATCH 1/3] Add the ability to disable servo motor by setting `angle` or `fraction` to `None`, also renamed examples to pca9685 to separate them from plain-GPIO examples --- adafruit_motor/servo.py | 15 ++++- ...o.py => motor_pca9685_continuous_servo.py} | 0 ..._dc_motor.py => motor_pca9685_dc_motor.py} | 0 examples/motor_pca9685_servo_sweep.py | 56 ++++++++++++++++++ ...otor.py => motor_pca9685_stepper_motor.py} | 0 examples/motor_servo_sweep.py | 58 +++++++++---------- 6 files changed, 96 insertions(+), 33 deletions(-) rename examples/{motor_continuous_servo.py => motor_pca9685_continuous_servo.py} (100%) rename examples/{motor_dc_motor.py => motor_pca9685_dc_motor.py} (100%) create mode 100644 examples/motor_pca9685_servo_sweep.py rename examples/{motor_stepper_motor.py => motor_pca9685_stepper_motor.py} (100%) diff --git a/adafruit_motor/servo.py b/adafruit_motor/servo.py index 0db2fda..b2a5e67 100644 --- a/adafruit_motor/servo.py +++ b/adafruit_motor/servo.py @@ -54,12 +54,17 @@ def set_pulse_width_range(self, min_pulse=750, max_pulse=2250): def fraction(self): """Pulse width expressed as fraction between 0.0 (`min_pulse`) and 1.0 (`max_pulse`). For conventional servos, corresponds to the servo position as a fraction - of the actuation range. + of the actuation range. Is None when servo is diabled (pulsewidth of 0ms). """ + if self._pwm_out.duty_cycle == 0: # Special case for disabled servos + return None return (self._pwm_out.duty_cycle - self._min_duty) / self._duty_range @fraction.setter def fraction(self, value): + if value is None: + self._pwm_out.duty_cycle = 0 # disable the motor + return 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) @@ -102,11 +107,17 @@ def __init__(self, pwm_out, *, actuation_range=180, min_pulse=750, max_pulse=225 @property def angle(self): - """The servo angle in degrees. Must be in the range ``0`` to ``actuation_range``.""" + """The servo angle in degrees. Must be in the range ``0`` to ``actuation_range``. + Is None when servo is disabled.""" + if self.fraction is None: # special case for disabled servos + return None return self.actuation_range * self.fraction @angle.setter def angle(self, new_angle): + if new_angle is None: # disable the servo by sending 0 signal + self.fraction = None + return if new_angle < 0 or new_angle > self.actuation_range: raise ValueError("Angle out of range") self.fraction = new_angle / self.actuation_range diff --git a/examples/motor_continuous_servo.py b/examples/motor_pca9685_continuous_servo.py similarity index 100% rename from examples/motor_continuous_servo.py rename to examples/motor_pca9685_continuous_servo.py diff --git a/examples/motor_dc_motor.py b/examples/motor_pca9685_dc_motor.py similarity index 100% rename from examples/motor_dc_motor.py rename to examples/motor_pca9685_dc_motor.py diff --git a/examples/motor_pca9685_servo_sweep.py b/examples/motor_pca9685_servo_sweep.py new file mode 100644 index 0000000..037a47f --- /dev/null +++ b/examples/motor_pca9685_servo_sweep.py @@ -0,0 +1,56 @@ +import time + +from board import SCL, SDA +import busio + +# Import the PCA9685 module. Available in the bundle and here: +# https://github.com/adafruit/Adafruit_CircuitPython_PCA9685 +from adafruit_pca9685 import PCA9685 +from adafruit_motor import servo + +i2c = busio.I2C(SCL, SDA) + +# Create a simple PCA9685 class instance. +pca = PCA9685(i2c) +# You can optionally provide a finer tuned reference clock speed to improve the accuracy of the +# timing pulses. This calibration will be specific to each board and its environment. See the +# calibration.py example in the PCA9685 driver. +# pca = PCA9685(i2c, reference_clock_speed=25630710) +pca.frequency = 50 + +# To get the full range of the servo you will likely need to adjust the min_pulse and max_pulse to +# match the stall points of the servo. +# This is an example for the Sub-micro servo: https://www.adafruit.com/product/2201 +# servo7 = servo.Servo(pca.channels[7], min_pulse=580, max_pulse=2350) +# This is an example for the Micro Servo - High Powered, High Torque Metal Gear: +# https://www.adafruit.com/product/2307 +# servo7 = servo.Servo(pca.channels[7], min_pulse=500, max_pulse=2600) +# This is an example for the Standard servo - TowerPro SG-5010 - 5010: +# https://www.adafruit.com/product/155 +# servo7 = servo.Servo(pca.channels[7], min_pulse=400, max_pulse=2400) +# This is an example for the Analog Feedback Servo: https://www.adafruit.com/product/1404 +# servo7 = servo.Servo(pca.channels[7], min_pulse=600, max_pulse=2500) +# This is an example for the Micro servo - TowerPro SG-92R: https://www.adafruit.com/product/169 +# servo7 = servo.Servo(pca.channels[7], min_pulse=500, max_pulse=2400) + +# The pulse range is 750 - 2250 by default. This range typically gives 135 degrees of +# range, but the default is to use 180 degrees. You can specify the expected range if you wish: +# servo7 = servo.Servo(pca.channels[7], actuation_range=135) +servo7 = servo.Servo(pca.channels[7]) + +# We sleep in the loops to give the servo time to move into position. +for i in range(180): + servo7.angle = i + time.sleep(0.03) +for i in range(180): + servo7.angle = 180 - i + time.sleep(0.03) + +# You can also specify the movement fractionally. +fraction = 0.0 +while fraction < 1.0: + servo7.fraction = fraction + fraction += 0.01 + time.sleep(0.03) + +pca.deinit() diff --git a/examples/motor_stepper_motor.py b/examples/motor_pca9685_stepper_motor.py similarity index 100% rename from examples/motor_stepper_motor.py rename to examples/motor_pca9685_stepper_motor.py diff --git a/examples/motor_servo_sweep.py b/examples/motor_servo_sweep.py index 037a47f..ca99dd0 100644 --- a/examples/motor_servo_sweep.py +++ b/examples/motor_servo_sweep.py @@ -1,56 +1,52 @@ import time - -from board import SCL, SDA -import busio - -# Import the PCA9685 module. Available in the bundle and here: -# https://github.com/adafruit/Adafruit_CircuitPython_PCA9685 -from adafruit_pca9685 import PCA9685 +import board +import pulseio from adafruit_motor import servo -i2c = busio.I2C(SCL, SDA) - -# Create a simple PCA9685 class instance. -pca = PCA9685(i2c) -# You can optionally provide a finer tuned reference clock speed to improve the accuracy of the -# timing pulses. This calibration will be specific to each board and its environment. See the -# calibration.py example in the PCA9685 driver. -# pca = PCA9685(i2c, reference_clock_speed=25630710) -pca.frequency = 50 +# create a PWMOut object on the control pin. +pwm = pulseio.PWMOut(board.D5, duty_cycle=0, frequency=50) # To get the full range of the servo you will likely need to adjust the min_pulse and max_pulse to # match the stall points of the servo. # This is an example for the Sub-micro servo: https://www.adafruit.com/product/2201 -# servo7 = servo.Servo(pca.channels[7], min_pulse=580, max_pulse=2350) +# servo = servo.Servo(pwm, min_pulse=580, max_pulse=2350) # This is an example for the Micro Servo - High Powered, High Torque Metal Gear: # https://www.adafruit.com/product/2307 -# servo7 = servo.Servo(pca.channels[7], min_pulse=500, max_pulse=2600) +# servo = servo.Servo(pwm, min_pulse=500, max_pulse=2600) # This is an example for the Standard servo - TowerPro SG-5010 - 5010: # https://www.adafruit.com/product/155 -# servo7 = servo.Servo(pca.channels[7], min_pulse=400, max_pulse=2400) +# servo = servo.Servo(pwm, min_pulse=400, max_pulse=2400) # This is an example for the Analog Feedback Servo: https://www.adafruit.com/product/1404 -# servo7 = servo.Servo(pca.channels[7], min_pulse=600, max_pulse=2500) +# servo = servo.Servo(pwm, min_pulse=600, max_pulse=2500) # This is an example for the Micro servo - TowerPro SG-92R: https://www.adafruit.com/product/169 -# servo7 = servo.Servo(pca.channels[7], min_pulse=500, max_pulse=2400) +# servo = servo.Servo(pwm, min_pulse=500, max_pulse=2400) # The pulse range is 750 - 2250 by default. This range typically gives 135 degrees of # range, but the default is to use 180 degrees. You can specify the expected range if you wish: -# servo7 = servo.Servo(pca.channels[7], actuation_range=135) -servo7 = servo.Servo(pca.channels[7]) +# servo = servo.Servo(board.D5, actuation_range=135) +servo = servo.Servo(pwm) # We sleep in the loops to give the servo time to move into position. +print("Sweep from 0 to 180") for i in range(180): - servo7.angle = i - time.sleep(0.03) + servo.angle = i + time.sleep(0.01) +print("Sweep from 180 to 0") for i in range(180): - servo7.angle = 180 - i - time.sleep(0.03) + servo.angle = 180 - i + time.sleep(0.01) + +print("Move to 90 degrees") +servo.angle = 90 +time.sleep(1) +print("Release servo motor for 10 seconds") +servo.fraction = None +time.sleep(10) # You can also specify the movement fractionally. +print("Sweep from 0 to 1.0 fractionally") fraction = 0.0 while fraction < 1.0: - servo7.fraction = fraction + servo.fraction = fraction fraction += 0.01 - time.sleep(0.03) - -pca.deinit() + time.sleep(0.01) From 8115aeea59afb172618e7085d190a1415465fe8b Mon Sep 17 00:00:00 2001 From: ladyada Date: Thu, 16 May 2019 14:53:07 -0400 Subject: [PATCH 2/3] updated dox for new filenames and new demo --- docs/examples.rst | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/examples.rst b/docs/examples.rst index cfdef33..36f79d9 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -3,18 +3,22 @@ Simple tests Ensure your device works with this simple test. -.. literalinclude:: ../examples/motor_dc_motor.py - :caption: examples/motor_dc_motor.py +.. literalinclude:: ../examples/motor_servo_sweep.py + :caption: examplesmotor_servo_sweep.py :linenos: -.. literalinclude:: ../examples/motor_stepper_motor.py - :caption: examples/motor_stepper_motor.py +.. literalinclude:: ../examples/motor_pca9685_dc_motor.py + :caption: examples/motor_pca9685_dc_motor.py :linenos: -.. literalinclude:: ../examples/motor_servo_sweep.py - :caption: examples/motor_servo_sweep.py +.. literalinclude:: ../examples/motor_pca9685_stepper_motor.py + :caption: examples/motor_pca9685_stepper_motor.py + :linenos: + +.. literalinclude:: ../examples/motor_pca9685_servo_sweep.py + :caption: examples/motor_pca9685_servo_sweep.py :linenos: .. literalinclude:: ../examples/motor_continuous_servo.py - :caption: examples/motor_continuous_servo.py + :caption: examples/motor_pca9685_continuous_servo.py :linenos: From 183984800ef39b561ccfb75d9732f6e525d6e7f4 Mon Sep 17 00:00:00 2001 From: ladyada Date: Thu, 16 May 2019 14:56:18 -0400 Subject: [PATCH 3/3] missed one --- docs/examples.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples.rst b/docs/examples.rst index 36f79d9..854c0b4 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -19,6 +19,6 @@ Ensure your device works with this simple test. :caption: examples/motor_pca9685_servo_sweep.py :linenos: -.. literalinclude:: ../examples/motor_continuous_servo.py +.. literalinclude:: ../examples/motor_pca9685_continuous_servo.py :caption: examples/motor_pca9685_continuous_servo.py :linenos: