/ adafruit_motorkit.py
adafruit_motorkit.py
  1  # The MIT License (MIT)
  2  #
  3  # Copyright (c) 2017 Scott Shawcroft for Adafruit Industries
  4  # Copyright (c) 2018 Kattni Rembor for Adafruit Industries
  5  #
  6  # Permission is hereby granted, free of charge, to any person obtaining a copy
  7  # of this software and associated documentation files (the "Software"), to deal
  8  # in the Software without restriction, including without limitation the rights
  9  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10  # copies of the Software, and to permit persons to whom the Software is
 11  # furnished to do so, subject to the following conditions:
 12  #
 13  # The above copyright notice and this permission notice shall be included in
 14  # all copies or substantial portions of the Software.
 15  #
 16  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 22  # THE SOFTWARE.
 23  """
 24  `adafruit_motorkit`
 25  ====================================================
 26  
 27  CircuitPython helper library for DC & Stepper Motor FeatherWing, Shield, and Pi Hat kits.
 28  
 29  * Author(s): Scott Shawcroft, Kattni Rembor
 30  
 31  Implementation Notes
 32  --------------------
 33  
 34  **Hardware:**
 35  
 36     "* `DC Motor + Stepper FeatherWing <https://www.adafruit.com/product/2927>`_"
 37     "* `Adafruit Motor/Stepper/Servo Shield for Arduino v2 Kit
 38     <https://www.adafruit.com/product/1438>`_"
 39     "* `Adafruit DC & Stepper Motor HAT for Raspberry Pi - Mini Kit
 40     <https://www.adafruit.com/product/2348>`_"
 41  
 42  **Software and Dependencies:**
 43  
 44  * Adafruit CircuitPython firmware for the supported boards:
 45    https://github.com/adafruit/circuitpython/releases
 46  
 47   * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
 48   * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register
 49   * Adafruit's PCA9685 library: https://github.com/adafruit/Adafruit_CircuitPython_PCA9685
 50   * Adafruit's Motor library: https://github.com/adafruit/Adafruit_CircuitPython_Motor
 51  
 52  """
 53  
 54  
 55  import board
 56  from adafruit_pca9685 import PCA9685
 57  
 58  __version__ = "0.0.0-auto.0"
 59  __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_MotorKit.git"
 60  
 61  
 62  class MotorKit:
 63      """Class representing an Adafruit DC & Stepper Motor FeatherWing, Shield or Pi Hat kit.
 64  
 65         Automatically uses the I2C bus on a Feather, Metro or Raspberry Pi if no I2C bus
 66         is supplied.
 67  
 68         Alternately, if using with multiple I2C devices, you can specify the I2C bus."""
 69  
 70      def __init__(self, address=0x60, i2c=None, steppers_microsteps=16):
 71          self._motor1 = None
 72          self._motor2 = None
 73          self._motor3 = None
 74          self._motor4 = None
 75          self._stepper1 = None
 76          self._stepper2 = None
 77          if i2c is None:
 78              i2c = board.I2C()
 79          self._pca = PCA9685(i2c, address=address)
 80          self._pca.frequency = 1600
 81          self._steppers_microsteps = steppers_microsteps
 82  
 83      # We can save memory usage (~300 bytes) by deduplicating the construction of the objects for
 84      # each motor. This saves both code size and the number of raw strings (the error message)
 85      # stored. The same technique is a net loss for stepper because there is less duplication.
 86      def _motor(self, motor_name, channels, stepper_name):
 87          from adafruit_motor import motor  # pylint: disable=import-outside-toplevel
 88  
 89          motor_name = "_motor" + str(motor_name)
 90          stepper_name = "_stepper" + str(stepper_name)
 91          if not getattr(self, motor_name):
 92              if getattr(self, stepper_name):
 93                  raise RuntimeError(
 94                      "Cannot use {} at the same time as {}.".format(
 95                          motor_name[1:], stepper_name[1:]
 96                      )
 97                  )
 98              self._pca.channels[channels[0]].duty_cycle = 0xFFFF
 99              setattr(
100                  self,
101                  motor_name,
102                  motor.DCMotor(
103                      self._pca.channels[channels[1]], self._pca.channels[channels[2]]
104                  ),
105              )
106          return getattr(self, motor_name)
107  
108      @property
109      def motor1(self):
110          """:py:class:``~adafruit_motor.motor.DCMotor`` controls for motor 1.
111  
112              The following image shows the location of the M1 terminal on the DC/Stepper FeatherWing.
113              The label on the FeatherWing is found on the bottom of the board.
114              The terminal is labeled on the top of the Shield and Pi Hat.
115  
116              .. image :: ../docs/_static/motor_featherwing/m1.jpg
117                :alt: Motor 1 location
118  
119              This example moves the motor forwards for one fifth of a second at full speed.
120  
121              .. code-block:: python
122  
123                  import time
124                  from adafruit_motorkit import motorkit
125  
126                  kit = MotorKit()
127  
128                  kit.motor1.throttle = 1.0
129                  time.sleep(0.2)
130  
131                  kit.motor1.throttle = 0
132          """
133          return self._motor(1, (8, 9, 10), 1)
134  
135      @property
136      def motor2(self):
137          """:py:class:``~adafruit_motor.motor.DCMotor`` controls for motor 2.
138  
139              The following image shows the location of the M2 terminal on the DC/Stepper FeatherWing.
140              The label on the FeatherWing is found on the bottom of the board.
141              The terminal is labeled on the top of the Shield and Pi Hat.
142  
143              .. image :: ../docs/_static/motor_featherwing/m2.jpg
144                :alt: Motor 2 location
145  
146              This example moves the motor forwards for one fifth of a second at full speed.
147  
148              .. code-block:: python
149  
150                  import time
151                  from adafruit_motorkit import motorkit
152  
153                  kit = MotorKit()
154  
155                  kit.motor2.throttle = 1.0
156                  time.sleep(0.2)
157  
158                  kit.motor1.throttle = 0
159          """
160          return self._motor(2, (13, 11, 12), 1)
161  
162      @property
163      def motor3(self):
164          """:py:class:``~adafruit_motor.motor.DCMotor`` controls for motor 3.
165  
166              The following image shows the location of the M2 terminal on the DC/Stepper FeatherWing.
167              The label on the FeatherWing is found on the bottom of the board.
168              The terminal is labeled on the top of the Shield and Pi Hat.
169  
170              .. image :: ../docs/_static/motor_featherwing/m3.jpg
171                :alt: Motor 3 location
172  
173              This example moves the motor forwards for one fifth of a second at full speed.
174  
175              .. code-block:: python
176  
177                  import time
178                  from adafruit_motorkit import motorkit
179  
180                  kit = MotorKit()
181  
182                  kit.motor3.throttle = 1.0
183                  time.sleep(0.2)
184  
185                  kit.motor1.throttle = 0
186          """
187          return self._motor(3, (2, 3, 4), 2)
188  
189      @property
190      def motor4(self):
191          """:py:class:``~adafruit_motor.motor.DCMotor`` controls for motor 4.
192  
193              .. image :: ../docs/_static/motor_featherwing/m4.jpg
194                :alt: Motor 4 location
195  
196              This example moves the motor forwards for one fifth of a second at full speed.
197  
198              .. code-block:: python
199  
200                  import time
201                  from adafruit_motorkit import motorkit
202  
203                  kit = MotorKit()
204  
205                  kit.motor4.throttle = 1.0
206                  time.sleep(0.2)
207  
208                  kit.motor1.throttle = 0
209          """
210          return self._motor(4, (7, 5, 6), 2)
211  
212      @property
213      def stepper1(self):
214          """:py:class:``~adafruit_motor.stepper.StepperMotor`` controls for one connected to stepper
215             1 (also labeled motor 1 and motor 2).
216  
217              The following image shows the location of the stepper1 terminals on the DC/Stepper
218              FeatherWing. stepper1 is made up of the M1 and M2 terminals.
219              The labels on the FeatherWing are found on the bottom of the board.
220              The terminals are labeled on the top of the Shield and Pi Hat.
221  
222              .. image :: ../docs/_static/motor_featherwing/stepper1.jpg
223                :alt: Stepper 1 location
224  
225              This example moves the stepper motor 100 steps forwards.
226  
227              .. code-block:: python
228  
229                  from adafruit_motorkit import MotorKit
230  
231                  kit = MotorKit()
232  
233                  for i in range(100):
234                      kit.stepper1.onestep()
235          """
236          if not self._stepper1:
237              from adafruit_motor import (  # pylint: disable=import-outside-toplevel
238                  stepper,
239              )
240  
241              if self._motor1 or self._motor2:
242                  raise RuntimeError(
243                      "Cannot use stepper1 at the same time as motor1 or motor2."
244                  )
245              self._pca.channels[8].duty_cycle = 0xFFFF
246              self._pca.channels[13].duty_cycle = 0xFFFF
247              self._stepper1 = stepper.StepperMotor(
248                  self._pca.channels[10],
249                  self._pca.channels[9],
250                  self._pca.channels[11],
251                  self._pca.channels[12],
252                  microsteps=self._steppers_microsteps,
253              )
254          return self._stepper1
255  
256      @property
257      def stepper2(self):
258          """:py:class:``~adafruit_motor.stepper.StepperMotor`` controls for one connected to stepper
259             2 (also labeled motor 3 and motor 4).
260  
261              The following image shows the location of the stepper2 terminals on the DC/Stepper
262              FeatherWing. stepper2 is made up of the M3 and M4 terminals.
263              The labels on the FeatherWing are found on the bottom of the board.
264              The terminals are labeled on the top of the Shield and Pi Hat.
265  
266              .. image :: ../docs/_static/motor_featherwing/stepper2.jpg
267                :alt: Stepper 2 location
268  
269              This example moves the stepper motor 100 steps forwards.
270  
271              .. code-block:: python
272  
273                  from adafruit_motorkit import MotorKit
274  
275                  kit = MotorKit()
276  
277                  for i in range(100):
278                      kit.stepper2.onestep()
279          """
280          if not self._stepper2:
281              from adafruit_motor import (  # pylint: disable=import-outside-toplevel
282                  stepper,
283              )
284  
285              if self._motor3 or self._motor4:
286                  raise RuntimeError(
287                      "Cannot use stepper2 at the same time as motor3 or motor4."
288                  )
289              self._pca.channels[7].duty_cycle = 0xFFFF
290              self._pca.channels[2].duty_cycle = 0xFFFF
291              self._stepper2 = stepper.StepperMotor(
292                  self._pca.channels[4],
293                  self._pca.channels[3],
294                  self._pca.channels[5],
295                  self._pca.channels[6],
296                  microsteps=self._steppers_microsteps,
297              )
298          return self._stepper2