/ adafruit_servokit.py
adafruit_servokit.py
1 # The MIT License (MIT) 2 # 3 # Copyright (c) 2018 Kattni Rembor for Adafruit Industries 4 # 5 # Permission is hereby granted, free of charge, to any person obtaining a copy 6 # of this software and associated documentation files (the "Software"), to deal 7 # in the Software without restriction, including without limitation the rights 8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 # copies of the Software, and to permit persons to whom the Software is 10 # furnished to do so, subject to the following conditions: 11 # 12 # The above copyright notice and this permission notice shall be included in 13 # all copies or substantial portions of the Software. 14 # 15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 # THE SOFTWARE. 22 """ 23 `adafruit_servokit` 24 ==================================================== 25 26 CircuitPython helper library for the PWM/Servo FeatherWing, Shield and Pi HAT and Bonnet kits. 27 28 * Author(s): Kattni Rembor 29 30 Implementation Notes 31 -------------------- 32 33 **Hardware:** 34 35 * `8-Channel PWM or Servo FeatherWing <https://www.adafruit.com/product/2928>`_ 36 * `Adafruit 16-Channel 12-bit PWM/Servo Shield <https://www.adafruit.com/product/1411>`_ 37 * `Adafruit 16-Channel PWM/Servo HAT for Raspberry Pi <https://www.adafruit.com/product/2327>`_ 38 * `Adafruit 16-Channel PWM/Servo Bonnet for Raspberry Pi <https://www.adafruit.com/product/3416>`_ 39 40 **Software and Dependencies:** 41 42 * Adafruit CircuitPython firmware for the supported boards: 43 https://github.com/adafruit/circuitpython/releases 44 45 * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice 46 * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register 47 * Adafruit's PCA9685 library: https://github.com/adafruit/Adafruit_CircuitPython_PCA9685 48 * Adafruit's Motor library: https://github.com/adafruit/Adafruit_CircuitPython_Motor 49 50 """ 51 52 import board 53 from adafruit_pca9685 import PCA9685 54 55 __version__ = "0.0.0-auto.0" 56 __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ServoKit.git" 57 58 59 class ServoKit: 60 """Class representing an Adafruit PWM/Servo FeatherWing, Shield or Pi HAT and Bonnet kits. 61 62 Automatically uses the I2C bus on a Feather, Metro or Raspberry Pi. 63 64 Initialise the PCA9685 chip at ``address``. 65 66 The internal reference clock is 25MHz but may vary slightly with environmental conditions and 67 manufacturing variances. Providing a more precise ``reference_clock_speed`` can improve the 68 accuracy of the frequency and duty_cycle computations. See the ``calibration.py`` example in 69 the `PCA9685 GitHub repo <https://github.com/adafruit/Adafruit_CircuitPython_PCA9685>`_ for 70 how to derive this value by measuring the resulting pulse widths. 71 72 :param int channels: The number of servo channels available. Must be 8 or 16. The FeatherWing 73 has 8 channels. The Shield, HAT, and Bonnet have 16 channels. 74 :param int address: The I2C address of the PCA9685. Default address is ``0x40``. 75 :param int reference_clock_speed: The frequency of the internal reference clock in Hertz. 76 Default reference clock speed is ``25000000``. 77 :param int frequency: The overall PWM frequency of the PCA9685 in Hertz. 78 Default frequency is ``50``. 79 80 """ 81 82 def __init__( 83 self, 84 *, 85 channels, 86 i2c=None, 87 address=0x40, 88 reference_clock_speed=25000000, 89 frequency=50 90 ): 91 if channels not in [8, 16]: 92 raise ValueError("servo_channels must be 8 or 16!") 93 self._items = [None] * channels 94 self._channels = channels 95 if i2c is None: 96 i2c = board.I2C() 97 self._pca = PCA9685( 98 i2c, address=address, reference_clock_speed=reference_clock_speed 99 ) 100 self._pca.frequency = frequency 101 102 self._servo = _Servo(self) 103 self._continuous_servo = _ContinuousServo(self) 104 105 @property 106 def servo(self): 107 """:py:class:``~adafruit_motor.servo.Servo`` controls for standard servos. 108 109 This FeatherWing example rotates a servo on channel ``0`` to ``180`` degrees for one second, 110 and then returns it to ``0`` degrees. 111 112 .. code-block:: python 113 114 import time 115 from adafruit_servokit import ServoKit 116 117 kit = ServoKit(channels=8) 118 119 kit.servo[0].angle = 180 120 time.sleep(1) 121 kit.servo[0].angle = 0 122 123 """ 124 return self._servo 125 126 @property 127 def continuous_servo(self): 128 """:py:class:``~adafruit_motor.servo.ContinuousServo`` controls for continuous rotation 129 servos. 130 131 This FeatherWing example rotates a continuous rotation servo on channel ``0`` forward for 132 one second, then backward for one second, and then stops the rotation. 133 134 .. code-block:: python 135 136 import time 137 from adafruit_servokit import ServoKit 138 139 kit = ServoKit(channels=8) 140 141 kit.continuous_servo[0].throttle = 1 142 time.sleep(1) 143 kit.continuous_servo[0].throttle = -1 144 time.sleep(1) 145 kit.continuous_servo[0].throttle = 0 146 147 """ 148 return self._continuous_servo 149 150 151 class _Servo: 152 # pylint: disable=protected-access 153 def __init__(self, kit): 154 self.kit = kit 155 156 def __getitem__(self, servo_channel): 157 import adafruit_motor.servo # pylint: disable=import-outside-toplevel 158 159 num_channels = self.kit._channels 160 if servo_channel >= num_channels or servo_channel < 0: 161 raise ValueError("servo must be 0-{}!".format(num_channels - 1)) 162 servo = self.kit._items[servo_channel] 163 if servo is None: 164 servo = adafruit_motor.servo.Servo(self.kit._pca.channels[servo_channel]) 165 self.kit._items[servo_channel] = servo 166 return servo 167 if isinstance(self.kit._items[servo_channel], adafruit_motor.servo.Servo): 168 return servo 169 raise ValueError("Channel {} is already in use.".format(servo_channel)) 170 171 def __len__(self): 172 return len(self.kit._items) 173 174 175 class _ContinuousServo: 176 # pylint: disable=protected-access 177 def __init__(self, kit): 178 self.kit = kit 179 180 def __getitem__(self, servo_channel): 181 import adafruit_motor.servo # pylint: disable=import-outside-toplevel 182 183 num_channels = self.kit._channels 184 if servo_channel >= num_channels or servo_channel < 0: 185 raise ValueError("servo must be 0-{}!".format(num_channels - 1)) 186 servo = self.kit._items[servo_channel] 187 if servo is None: 188 servo = adafruit_motor.servo.ContinuousServo( 189 self.kit._pca.channels[servo_channel] 190 ) 191 self.kit._items[servo_channel] = servo 192 return servo 193 if isinstance( 194 self.kit._items[servo_channel], adafruit_motor.servo.ContinuousServo 195 ): 196 return servo 197 raise ValueError("Channel {} is already in use.".format(servo_channel)) 198 199 def __len__(self): 200 return len(self.kit._items)