# SPDX-FileCopyrightText: 2017 <NAME> for Adafruit Industries
# SPDX-FileCopyrightText: 2018 <NAME> for Adafruit Industries
#
# SPDX-License-Identifier: MIT

"""
`adafruit_motorkit`
====================================================

CircuitPython helper library for DC & Stepper Motor FeatherWing, Shield, and Pi Hat kits.

* Author(s): <NAME>, <NAME>

Implementation Notes
--------------------

**Hardware:**

   "* `DC Motor + Stepper FeatherWing <https://www.adafruit.com/product/2927>`_"
   "* `Adafruit Motor/Stepper/Servo Shield for Arduino v2 Kit
   <https://www.adafruit.com/product/1438>`_"
   "* `Adafruit DC & Stepper Motor HAT for Raspberry Pi - Mini Kit
   <https://www.adafruit.com/product/2348>`_"

**Software and Dependencies:**

* Adafruit CircuitPython firmware for the supported boards:
  https://github.com/adafruit/circuitpython/releases

 * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
 * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register
 * Adafruit's PCA9685 library: https://github.com/adafruit/Adafruit_CircuitPython_PCA9685
 * Adafruit's Motor library: https://github.com/adafruit/Adafruit_CircuitPython_Motor

"""

import board
from adafruit_pca9685 import PCA9685

__version__ = "1.6.1"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_MotorKit.git"


class MotorKit:
    """Class representing an Adafruit DC & Stepper Motor FeatherWing, Shield or Pi Hat kit.

    Automatically uses the I2C bus on a Feather, Metro or Raspberry Pi if no I2C bus
    is supplied.

    Alternately, if using with multiple I2C devices, you can specify the I2C bus."""

    def __init__(
        self, address=0x60, i2c=None, steppers_microsteps=16, pwm_frequency=1600
    ):
        self._motor1 = None
        self._motor2 = None
        self._motor3 = None
        self._motor4 = None
        self._stepper1 = None
        self._stepper2 = None
        if i2c is None:
            i2c = board.I2C()
        self._pca = PCA9685(i2c, address=address)
        self._pca.frequency = pwm_frequency
        self._steppers_microsteps = steppers_microsteps

    # We can save memory usage (~300 bytes) by deduplicating the construction of the objects for
    # each motor. This saves both code size and the number of raw strings (the error message)
    # stored. The same technique is a net loss for stepper because there is less duplication.
    def _motor(self, motor_name, channels, stepper_name):
        from adafruit_motor import motor  # pylint: disable=import-outside-toplevel

        motor_name = "_motor" + str(motor_name)
        stepper_name = "_stepper" + str(stepper_name)
        if not getattr(self, motor_name):
            if getattr(self, stepper_name):
                raise RuntimeError(
                    "Cannot use {} at the same time as {}.".format(
                        motor_name[1:], stepper_name[1:]
                    )
                )
            self._pca.channels[channels[0]].duty_cycle = 0xFFFF
            setattr(
                self,
                motor_name,
                motor.DCMotor(
                    self._pca.channels[channels[1]], self._pca.channels[channels[2]]
                ),
            )
        return getattr(self, motor_name)

    @property
    def motor1(self):
        """:py:class:``~adafruit_motor.motor.DCMotor`` controls for motor 1.

        The following image shows the location of the M1 terminal on the DC/Stepper FeatherWing.
        The label on the FeatherWing is found on the bottom of the board.
        The terminal is labeled on the top of the Shield and Pi Hat.

        .. image :: ../docs/_static/motor_featherwing/m1.jpg
          :alt: Motor 1 location

        This example moves the motor forwards for one fifth of a second at full speed.

        .. code-block:: python

            import time
            from adafruit_motorkit import motorkit

            kit = MotorKit()

            kit.motor1.throttle = 1.0
            time.sleep(0.2)

            kit.motor1.throttle = 0
        """
        return self._motor(1, (8, 9, 10), 1)

    @property
    def motor2(self):
        """:py:class:``~adafruit_motor.motor.DCMotor`` controls for motor 2.

        The following image shows the location of the M2 terminal on the DC/Stepper FeatherWing.
        The label on the FeatherWing is found on the bottom of the board.
        The terminal is labeled on the top of the Shield and Pi Hat.

        .. image :: ../docs/_static/motor_featherwing/m2.jpg
          :alt: Motor 2 location

        This example moves the motor forwards for one fifth of a second at full speed.

        .. code-block:: python

            import time
            from adafruit_motorkit import motorkit

            kit = MotorKit()

            kit.motor2.throttle = 1.0
            time.sleep(0.2)

            kit.motor1.throttle = 0
        """
        return self._motor(2, (13, 11, 12), 1)

    @property
    def motor3(self):
        """:py:class:``~adafruit_motor.motor.DCMotor`` controls for motor 3.

        The following image shows the location of the M2 terminal on the DC/Stepper FeatherWing.
        The label on the FeatherWing is found on the bottom of the board.
        The terminal is labeled on the top of the Shield and Pi Hat.

        .. image :: ../docs/_static/motor_featherwing/m3.jpg
          :alt: Motor 3 location

        This example moves the motor forwards for one fifth of a second at full speed.

        .. code-block:: python

            import time
            from adafruit_motorkit import motorkit

            kit = MotorKit()

            kit.motor3.throttle = 1.0
            time.sleep(0.2)

            kit.motor1.throttle = 0
        """
        return self._motor(3, (2, 3, 4), 2)

    @property
    def motor4(self):
        """:py:class:``~adafruit_motor.motor.DCMotor`` controls for motor 4.

        .. image :: ../docs/_static/motor_featherwing/m4.jpg
          :alt: Motor 4 location

        This example moves the motor forwards for one fifth of a second at full speed.

        .. code-block:: python

            import time
            from adafruit_motorkit import motorkit

            kit = MotorKit()

            kit.motor4.throttle = 1.0
            time.sleep(0.2)

            kit.motor1.throttle = 0
        """
        return self._motor(4, (7, 5, 6), 2)

    @property
    def stepper1(self):
        """:py:class:``~adafruit_motor.stepper.StepperMotor`` controls for one connected to stepper
        1 (also labeled motor 1 and motor 2).

         The following image shows the location of the stepper1 terminals on the DC/Stepper
         FeatherWing. stepper1 is made up of the M1 and M2 terminals.
         The labels on the FeatherWing are found on the bottom of the board.
         The terminals are labeled on the top of the Shield and Pi Hat.

         .. image :: ../docs/_static/motor_featherwing/stepper1.jpg
           :alt: Stepper 1 location

         This example moves the stepper motor 100 steps forwards.

         .. code-block:: python

             from adafruit_motorkit import MotorKit

             kit = MotorKit()

             for i in range(100):
                 kit.stepper1.onestep()
        """
        if not self._stepper1:
            from adafruit_motor import (  # pylint: disable=import-outside-toplevel
                stepper,
            )

            if self._motor1 or self._motor2:
                raise RuntimeError(
                    "Cannot use stepper1 at the same time as motor1 or motor2."
                )
            self._pca.channels[8].duty_cycle = 0xFFFF
            self._pca.channels[13].duty_cycle = 0xFFFF
            self._stepper1 = stepper.StepperMotor(
                self._pca.channels[10],
                self._pca.channels[9],
                self._pca.channels[11],
                self._pca.channels[12],
                microsteps=self._steppers_microsteps,
            )
        return self._stepper1

    @property
    def stepper2(self):
        """:py:class:``~adafruit_motor.stepper.StepperMotor`` controls for one connected to stepper
        2 (also labeled motor 3 and motor 4).

         The following image shows the location of the stepper2 terminals on the DC/Stepper
         FeatherWing. stepper2 is made up of the M3 and M4 terminals.
         The labels on the FeatherWing are found on the bottom of the board.
         The terminals are labeled on the top of the Shield and Pi Hat.

         .. image :: ../docs/_static/motor_featherwing/stepper2.jpg
           :alt: Stepper 2 location

         This example moves the stepper motor 100 steps forwards.

         .. code-block:: python

             from adafruit_motorkit import MotorKit

             kit = MotorKit()

             for i in range(100):
                 kit.stepper2.onestep()
        """
        if not self._stepper2:
            from adafruit_motor import (  # pylint: disable=import-outside-toplevel
                stepper,
            )

            if self._motor3 or self._motor4:
                raise RuntimeError(
                    "Cannot use stepper2 at the same time as motor3 or motor4."
                )
            self._pca.channels[7].duty_cycle = 0xFFFF
            self._pca.channels[2].duty_cycle = 0xFFFF
            self._stepper2 = stepper.StepperMotor(
                self._pca.channels[4],
                self._pca.channels[3],
                self._pca.channels[5],
                self._pca.channels[6],
                microsteps=self._steppers_microsteps,
            )
        return self._stepper2

    @property
    def frequency(self):
        """The overall PCA9685 PWM frequency in Hertz."""
        return self._pca.frequency

    @frequency.setter
    def frequency(self, pwm_frequency=1600):
        self._pca.frequency = pwm_frequency

# SPDX-FileCopyrightText: 2017 <NAME> for Adafruit Industries
# SPDX-FileCopyrightText: 2018 <NAME> for Adafruit Industries
#
# SPDX-License-Identifier: MIT

"""
`adafruit_motorkit`
====================================================

CircuitPython helper library for DC & Stepper Motor FeatherWing, Shield, and Pi Hat kits.

* Author(s): <NAME>, <NAME>

Implementation Notes
--------------------

**Hardware:**

   "* `DC Motor + Stepper FeatherWing <https://www.adafruit.com/product/2927>`_"
   "* `Adafruit Motor/Stepper/Servo Shield for Arduino v2 Kit
   <https://www.adafruit.com/product/1438>`_"
   "* `Adafruit DC & Stepper Motor HAT for Raspberry Pi - Mini Kit
   <https://www.adafruit.com/product/2348>`_"

**Software and Dependencies:**

* Adafruit CircuitPython firmware for the supported boards:
  https://github.com/adafruit/circuitpython/releases

 * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
 * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register
 * Adafruit's PCA9685 library: https://github.com/adafruit/Adafruit_CircuitPython_PCA9685
 * Adafruit's Motor library: https://github.com/adafruit/Adafruit_CircuitPython_Motor

"""

import board
from adafruit_pca9685 import PCA9685

__version__ = "1.6.1"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_MotorKit.git"


class MotorKit:
    """Class representing an Adafruit DC & Stepper Motor FeatherWing, Shield or Pi Hat kit.

    Automatically uses the I2C bus on a Feather, Metro or Raspberry Pi if no I2C bus
    is supplied.

    Alternately, if using with multiple I2C devices, you can specify the I2C bus."""

    def __init__(
        self, address=0x60, i2c=None, steppers_microsteps=16, pwm_frequency=1600
    ):
        self._motor1 = None
        self._motor2 = None
        self._motor3 = None
        self._motor4 = None
        self._stepper1 = None
        self._stepper2 = None
        if i2c is None:
            i2c = board.I2C()
        self._pca = PCA9685(i2c, address=address)
        self._pca.frequency = pwm_frequency
        self._steppers_microsteps = steppers_microsteps

    # We can save memory usage (~300 bytes) by deduplicating the construction of the objects for
    # each motor. This saves both code size and the number of raw strings (the error message)
    # stored. The same technique is a net loss for stepper because there is less duplication.
    def _motor(self, motor_name, channels, stepper_name):
        from adafruit_motor import motor  # pylint: disable=import-outside-toplevel

        motor_name = "_motor" + str(motor_name)
        stepper_name = "_stepper" + str(stepper_name)
        if not getattr(self, motor_name):
            if getattr(self, stepper_name):
                raise RuntimeError(
                    "Cannot use {} at the same time as {}.".format(
                        motor_name[1:], stepper_name[1:]
                    )
                )
            self._pca.channels[channels[0]].duty_cycle = 0xFFFF
            setattr(
                self,
                motor_name,
                motor.DCMotor(
                    self._pca.channels[channels[1]], self._pca.channels[channels[2]]
                ),
            )
        return getattr(self, motor_name)

    @property
    def motor1(self):
        """:py:class:``~adafruit_motor.motor.DCMotor`` controls for motor 1.

        The following image shows the location of the M1 terminal on the DC/Stepper FeatherWing.
        The label on the FeatherWing is found on the bottom of the board.
        The terminal is labeled on the top of the Shield and Pi Hat.

        .. image :: ../docs/_static/motor_featherwing/m1.jpg
          :alt: Motor 1 location

        This example moves the motor forwards for one fifth of a second at full speed.

        .. code-block:: python

            import time
            from adafruit_motorkit import motorkit

            kit = MotorKit()

            kit.motor1.throttle = 1.0
            time.sleep(0.2)

            kit.motor1.throttle = 0
        """
        return self._motor(1, (8, 9, 10), 1)

    @property
    def motor2(self):
        """:py:class:``~adafruit_motor.motor.DCMotor`` controls for motor 2.

        The following image shows the location of the M2 terminal on the DC/Stepper FeatherWing.
        The label on the FeatherWing is found on the bottom of the board.
        The terminal is labeled on the top of the Shield and Pi Hat.

        .. image :: ../docs/_static/motor_featherwing/m2.jpg
          :alt: Motor 2 location

        This example moves the motor forwards for one fifth of a second at full speed.

        .. code-block:: python

            import time
            from adafruit_motorkit import motorkit

            kit = MotorKit()

            kit.motor2.throttle = 1.0
            time.sleep(0.2)

            kit.motor1.throttle = 0
        """
        return self._motor(2, (13, 11, 12), 1)

    @property
    def motor3(self):
        """:py:class:``~adafruit_motor.motor.DCMotor`` controls for motor 3.

        The following image shows the location of the M2 terminal on the DC/Stepper FeatherWing.
        The label on the FeatherWing is found on the bottom of the board.
        The terminal is labeled on the top of the Shield and Pi Hat.

        .. image :: ../docs/_static/motor_featherwing/m3.jpg
          :alt: Motor 3 location

        This example moves the motor forwards for one fifth of a second at full speed.

        .. code-block:: python

            import time
            from adafruit_motorkit import motorkit

            kit = MotorKit()

            kit.motor3.throttle = 1.0
            time.sleep(0.2)

            kit.motor1.throttle = 0
        """
        return self._motor(3, (2, 3, 4), 2)

    @property
    def motor4(self):
        """:py:class:``~adafruit_motor.motor.DCMotor`` controls for motor 4.

        .. image :: ../docs/_static/motor_featherwing/m4.jpg
          :alt: Motor 4 location

        This example moves the motor forwards for one fifth of a second at full speed.

        .. code-block:: python

            import time
            from adafruit_motorkit import motorkit

            kit = MotorKit()

            kit.motor4.throttle = 1.0
            time.sleep(0.2)

            kit.motor1.throttle = 0
        """
        return self._motor(4, (7, 5, 6), 2)

    @property
    def stepper1(self):
        """:py:class:``~adafruit_motor.stepper.StepperMotor`` controls for one connected to stepper
        1 (also labeled motor 1 and motor 2).

         The following image shows the location of the stepper1 terminals on the DC/Stepper
         FeatherWing. stepper1 is made up of the M1 and M2 terminals.
         The labels on the FeatherWing are found on the bottom of the board.
         The terminals are labeled on the top of the Shield and Pi Hat.

         .. image :: ../docs/_static/motor_featherwing/stepper1.jpg
           :alt: Stepper 1 location

         This example moves the stepper motor 100 steps forwards.

         .. code-block:: python

             from adafruit_motorkit import MotorKit

             kit = MotorKit()

             for i in range(100):
                 kit.stepper1.onestep()
        """
        if not self._stepper1:
            from adafruit_motor import (  # pylint: disable=import-outside-toplevel
                stepper,
            )

            if self._motor1 or self._motor2:
                raise RuntimeError(
                    "Cannot use stepper1 at the same time as motor1 or motor2."
                )
            self._pca.channels[8].duty_cycle = 0xFFFF
            self._pca.channels[13].duty_cycle = 0xFFFF
            self._stepper1 = stepper.StepperMotor(
                self._pca.channels[10],
                self._pca.channels[9],
                self._pca.channels[11],
                self._pca.channels[12],
                microsteps=self._steppers_microsteps,
            )
        return self._stepper1

    @property
    def stepper2(self):
        """:py:class:``~adafruit_motor.stepper.StepperMotor`` controls for one connected to stepper
        2 (also labeled motor 3 and motor 4).

         The following image shows the location of the stepper2 terminals on the DC/Stepper
         FeatherWing. stepper2 is made up of the M3 and M4 terminals.
         The labels on the FeatherWing are found on the bottom of the board.
         The terminals are labeled on the top of the Shield and Pi Hat.

         .. image :: ../docs/_static/motor_featherwing/stepper2.jpg
           :alt: Stepper 2 location

         This example moves the stepper motor 100 steps forwards.

         .. code-block:: python

             from adafruit_motorkit import MotorKit

             kit = MotorKit()

             for i in range(100):
                 kit.stepper2.onestep()
        """
        if not self._stepper2:
            from adafruit_motor import (  # pylint: disable=import-outside-toplevel
                stepper,
            )

            if self._motor3 or self._motor4:
                raise RuntimeError(
                    "Cannot use stepper2 at the same time as motor3 or motor4."
                )
            self._pca.channels[7].duty_cycle = 0xFFFF
            self._pca.channels[2].duty_cycle = 0xFFFF
            self._stepper2 = stepper.StepperMotor(
                self._pca.channels[4],
                self._pca.channels[3],
                self._pca.channels[5],
                self._pca.channels[6],
                microsteps=self._steppers_microsteps,
            )
        return self._stepper2

    @property
    def frequency(self):
        """The overall PCA9685 PWM frequency in Hertz."""
        return self._pca.frequency

    @frequency.setter
    def frequency(self, pwm_frequency=1600):
        self._pca.frequency = pwm_frequency

# SPDX-FileCopyrightText: 2017 <NAME> for Adafruit Industries # SPDX-FileCopyrightText: 2018 <NAME> for Adafruit Industries # # SPDX-License-Identifier: MIT `adafruit_motorkit` ====================================================  CircuitPython helper library for DC & Stepper Motor FeatherWing, Shield, and Pi Hat kits.  * Author(s): <NAME>, <NAME>  Implementation Notes --------------------  **Hardware:**     "* `DC Motor + Stepper FeatherWing <https://www.adafruit.com/product/2927>`_"    "* `Adafruit Motor/Stepper/Servo Shield for Arduino v2 Kit    <https://www.adafruit.com/product/1438>`_"    "* `Adafruit DC & Stepper Motor HAT for Raspberry Pi - Mini Kit    <https://www.adafruit.com/product/2348>`_"  **Software and Dependencies:**  * Adafruit CircuitPython firmware for the supported boards:   https://github.com/adafruit/circuitpython/releases   * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice  * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register  * Adafruit's PCA9685 library: https://github.com/adafruit/Adafruit_CircuitPython_PCA9685  * Adafruit's Motor library: https://github.com/adafruit/Adafruit_CircuitPython_Motor Class representing an Adafruit DC & Stepper Motor FeatherWing, Shield or Pi Hat kit.      Automatically uses the I2C bus on a Feather, Metro or Raspberry Pi if no I2C bus     is supplied.      Alternately, if using with multiple I2C devices, you can specify the I2C bus. # We can save memory usage (~300 bytes) by deduplicating the construction of the objects for # each motor. This saves both code size and the number of raw strings (the error message) # stored. The same technique is a net loss for stepper because there is less duplication. # pylint: disable=import-outside-toplevel :py:class:``~adafruit_motor.motor.DCMotor`` controls for motor 1.          The following image shows the location of the M1 terminal on the DC/Stepper FeatherWing.         The label on the FeatherWing is found on the bottom of the board.         The terminal is labeled on the top of the Shield and Pi Hat.          .. image :: ../docs/_static/motor_featherwing/m1.jpg           :alt: Motor 1 location          This example moves the motor forwards for one fifth of a second at full speed.          .. code-block:: python              import time             from adafruit_motorkit import motorkit              kit = MotorKit()              kit.motor1.throttle = 1.0             time.sleep(0.2)              kit.motor1.throttle = 0 :py:class:``~adafruit_motor.motor.DCMotor`` controls for motor 2.          The following image shows the location of the M2 terminal on the DC/Stepper FeatherWing.         The label on the FeatherWing is found on the bottom of the board.         The terminal is labeled on the top of the Shield and Pi Hat.          .. image :: ../docs/_static/motor_featherwing/m2.jpg           :alt: Motor 2 location          This example moves the motor forwards for one fifth of a second at full speed.          .. code-block:: python              import time             from adafruit_motorkit import motorkit              kit = MotorKit()              kit.motor2.throttle = 1.0             time.sleep(0.2)              kit.motor1.throttle = 0 :py:class:``~adafruit_motor.motor.DCMotor`` controls for motor 3.          The following image shows the location of the M2 terminal on the DC/Stepper FeatherWing.         The label on the FeatherWing is found on the bottom of the board.         The terminal is labeled on the top of the Shield and Pi Hat.          .. image :: ../docs/_static/motor_featherwing/m3.jpg           :alt: Motor 3 location          This example moves the motor forwards for one fifth of a second at full speed.          .. code-block:: python              import time             from adafruit_motorkit import motorkit              kit = MotorKit()              kit.motor3.throttle = 1.0             time.sleep(0.2)              kit.motor1.throttle = 0 :py:class:``~adafruit_motor.motor.DCMotor`` controls for motor 4.          .. image :: ../docs/_static/motor_featherwing/m4.jpg           :alt: Motor 4 location          This example moves the motor forwards for one fifth of a second at full speed.          .. code-block:: python              import time             from adafruit_motorkit import motorkit              kit = MotorKit()              kit.motor4.throttle = 1.0             time.sleep(0.2)              kit.motor1.throttle = 0 :py:class:``~adafruit_motor.stepper.StepperMotor`` controls for one connected to stepper         1 (also labeled motor 1 and motor 2).           The following image shows the location of the stepper1 terminals on the DC/Stepper          FeatherWing. stepper1 is made up of the M1 and M2 terminals.          The labels on the FeatherWing are found on the bottom of the board.          The terminals are labeled on the top of the Shield and Pi Hat.           .. image :: ../docs/_static/motor_featherwing/stepper1.jpg            :alt: Stepper 1 location           This example moves the stepper motor 100 steps forwards.           .. code-block:: python               from adafruit_motorkit import MotorKit               kit = MotorKit()               for i in range(100):                  kit.stepper1.onestep() # pylint: disable=import-outside-toplevel :py:class:``~adafruit_motor.stepper.StepperMotor`` controls for one connected to stepper         2 (also labeled motor 3 and motor 4).           The following image shows the location of the stepper2 terminals on the DC/Stepper          FeatherWing. stepper2 is made up of the M3 and M4 terminals.          The labels on the FeatherWing are found on the bottom of the board.          The terminals are labeled on the top of the Shield and Pi Hat.           .. image :: ../docs/_static/motor_featherwing/stepper2.jpg            :alt: Stepper 2 location           This example moves the stepper motor 100 steps forwards.           .. code-block:: python               from adafruit_motorkit import MotorKit               kit = MotorKit()               for i in range(100):                  kit.stepper2.onestep() # pylint: disable=import-outside-toplevel The overall PCA9685 PWM frequency in Hertz.