# The MIT License (MIT) # # Copyright (c) 2018 Dan Halbert for Adafruit Industries # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. """ `adafruit_crickit` ========================== Convenience library for using the Adafruit Crickit robotics boards. * Author(s): Dan Halbert Implementation Notes -------------------- **Hardware:** `Adafruit Crickit for Circuit Playground Express `_ `Adafruit Crickit FeatherWing `_ **Software and Dependencies:** * Adafruit CircuitPython firmware for the supported boards: https://github.com/adafruit/circuitpython/releases """ import sys import board from micropython import const # pylint: disable=wrong-import-position try: lib_index = sys.path.index("/lib") # pylint: disable=invalid-name if lib_index < sys.path.index(".frozen"): # Prefer frozen modules over those in /lib. sys.path.insert(lib_index, ".frozen") except ValueError: # Don't change sys.path if it doesn't contain "lib" or ".frozen". pass from adafruit_seesaw.seesaw import Seesaw from adafruit_seesaw.crickit import Crickit_Pinmap from adafruit_seesaw.pwmout import PWMOut from adafruit_motor.servo import Servo, ContinuousServo from adafruit_motor.motor import DCMotor from adafruit_motor.stepper import StepperMotor __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Crickit.git" _SERVO1 = const(17) _SERVO2 = const(16) _SERVO3 = const(15) _SERVO4 = const(14) _MOTOR1 = (22, 23) _MOTOR2 = (19, 18) # Order as needed for steppers. _MOTOR_STEPPER = _MOTOR1 + _MOTOR2 _DRIVE1 = const(13) _DRIVE2 = const(12) _DRIVE3 = const(43) _DRIVE4 = const(42) # Order as needed for steppers. _DRIVE_STEPPER = (_DRIVE1, _DRIVE3, _DRIVE2, _DRIVE4) _TOUCH1 = const(4) _TOUCH2 = const(5) _TOUCH3 = const(6) _TOUCH4 = const(7) _NEOPIXEL = const(20) _SS_PIXEL = const(27) # pylint: disable=too-few-public-methods class CrickitTouchIn: """Imitate touchio.TouchIn.""" def __init__(self, seesaw, pin): self._seesaw = seesaw self._pin = pin self.threshold = self.raw_value + 100 @property def raw_value(self): """The raw touch measurement as an `int`. (read-only)""" return self._seesaw.touch_read(self._pin) @property def value(self): """Whether the touch pad is being touched or not. (read-only)""" return self.raw_value > self.threshold # pylint: disable=too-many-public-methods class Crickit: """Represents a Crickit board. Provides a number of devices available via properties, such as ``servo_1``. Devices are created on demand the first time they are referenced. It's fine to refer a device multiple times via its property, but it's faster and results in more compact code to assign a device to a variable. .. code-block:: python import time from adafruit_crickit import crickit # This is fine: crickit.servo_1.angle = 0 time.sleep(1) crickit.servo_1.angle = 90 time.sleep(1) # This is slightly faster and more compact: servo_1 = crickit.servo_1 servo_1.angle = 0 time.sleep(1) servo_1.angle = 90 time.sleep(1) """ SIGNAL1 = 2 """Signal 1 terminal""" SIGNAL2 = 3 """Signal 2 terminal""" SIGNAL3 = 40 """Signal 3 terminal""" SIGNAL4 = 41 """Signal 4 terminal""" SIGNAL5 = 11 """Signal 5 terminal""" SIGNAL6 = 10 """Signal 6 terminal""" SIGNAL7 = 9 """Signal 7 terminal""" SIGNAL8 = 8 """Signal 8 terminal""" def __init__(self, seesaw): self._seesaw = seesaw self._seesaw.pin_mapping = Crickit_Pinmap # Associate terminal(s) with certain devices. # Used to find existing devices. self._devices = dict() self._neopixel = None self._onboard_pixel = None @property def seesaw(self): """The Seesaw object that talks to the Crickit. Use this object to manipulate the signal pins that correspond to Crickit terminals. .. code-block:: python from adafruit_crickit import crickit ss = crickit.seesaw ss.pin_mode(crickit.SIGNAL4, ss.OUTPUT) ss.digital_write(crickit.SIGNAL4], True) """ return self._seesaw @property def servo_1(self): """``adafruit_motor.servo.Servo`` object on Servo 1 terminal""" return self._servo(_SERVO1, Servo) @property def servo_2(self): """``adafruit_motor.servo.Servo`` object on Servo 2 terminal""" return self._servo(_SERVO2, Servo) @property def servo_3(self): """``adafruit_motor.servo.Servo`` object on Servo 3 terminal""" return self._servo(_SERVO3, Servo) @property def servo_4(self): """``adafruit_motor.servo.Servo`` object on Servo 4 terminal""" return self._servo(_SERVO4, Servo) @property def continuous_servo_1(self): """``adafruit_motor.servo.ContinuousServo`` object on Servo 1 terminal""" return self._servo(_SERVO1, ContinuousServo) @property def continuous_servo_2(self): """``adafruit_motor.servo.ContinuousServo`` object on Servo 2 terminal""" return self._servo(_SERVO2, ContinuousServo) @property def continuous_servo_3(self): """``adafruit_motor.servo.ContinuousServo`` object on Servo 3 terminal""" return self._servo(_SERVO3, ContinuousServo) @property def continuous_servo_4(self): """``adafruit_motor.servo.ContinuousServo`` object on Servo 4 terminal""" return self._servo(_SERVO4, ContinuousServo) def _servo(self, terminal, servo_class): device = self._devices.get(terminal, None) if not isinstance(device, servo_class): pwm = PWMOut(self._seesaw, terminal) pwm.frequency = 50 device = servo_class(pwm) self._devices[terminal] = device return device @property def dc_motor_1(self): """``adafruit_motor.motor.DCMotor`` object on Motor 1 terminals""" return self._motor(_MOTOR1, DCMotor) @property def dc_motor_2(self): """``adafruit_motor.motor.DCMotor`` object on Motor 2 terminals""" return self._motor(_MOTOR2, DCMotor) @property def stepper_motor(self): """``adafruit_motor.motor.StepperMotor`` object on Motor 1 and Motor 2 terminals""" return self._motor(_MOTOR_STEPPER, StepperMotor) @property def drive_stepper_motor(self): """``adafruit_motor.motor.StepperMotor`` object on Drive terminals""" return self._motor(_DRIVE_STEPPER, StepperMotor) @property def feather_drive_stepper_motor(self): """``adafruit_motor.motor.StepperMotor`` object on Drive terminals on Crickit FeatherWing""" return self._motor(tuple(reversed(_DRIVE_STEPPER)), StepperMotor) def _motor(self, terminals, motor_class): device = self._devices.get(terminals, None) if not isinstance(device, motor_class): device = motor_class( *(PWMOut(self._seesaw, terminal) for terminal in terminals) ) self._devices[terminals] = device return device @property def drive_1(self): """``adafruit_seesaw.pwmout.PWMOut`` object on Drive 1 terminal, with ``frequency=1000``""" return self._drive(_DRIVE1) @property def drive_2(self): """``adafruit_seesaw.pwmout.PWMOut`` object on Drive 2 terminal, with ``frequency=1000``""" return self._drive(_DRIVE2) @property def drive_3(self): """``adafruit_seesaw.pwmout.PWMOut`` object on Drive 3 terminal, with ``frequency=1000``""" return self._drive(_DRIVE3) @property def drive_4(self): """``adafruit_seesaw.pwmout.PWMOut`` object on Drive 4 terminal, with ``frequency=1000``""" return self._drive(_DRIVE4) feather_drive_1 = drive_4 """``adafruit_seesaw.pwmout.PWMOut`` object on Crickit Featherwing Drive 1 terminal, with ``frequency=1000`` """ feather_drive_2 = drive_3 """``adafruit_seesaw.pwmout.PWMOut`` object on Crickit Featherwing Drive 2 terminal, with ``frequency=1000`` """ feather_drive_3 = drive_2 """``adafruit_seesaw.pwmout.PWMOut`` object on Crickit Featherwing Drive 3 terminal, with ``frequency=1000`` """ feather_drive_4 = drive_1 """``adafruit_seesaw.pwmout.PWMOut`` object on Crickit Featherwing Drive 4 terminal, with ``frequency=1000`` """ def _drive(self, terminal): device = self._devices.get(terminal, None) if not isinstance(device, PWMOut): device = PWMOut(self._seesaw, terminal) device.frequency = 1000 self._devices[terminal] = device return device @property def touch_1(self): """``adafruit_crickit.CrickitTouchIn`` object on Touch 1 terminal""" return self._touch(_TOUCH1) @property def touch_2(self): """``adafruit_crickit.CrickitTouchIn`` object on Touch 2 terminal""" return self._touch(_TOUCH2) @property def touch_3(self): """``adafruit_crickit.CrickitTouchIn`` object on Touch 3 terminal""" return self._touch(_TOUCH3) @property def touch_4(self): """``adafruit_crickit.CrickitTouchIn`` object on Touch 4 terminal""" return self._touch(_TOUCH4) def _touch(self, terminal): touch_in = self._devices.get(terminal, None) if not touch_in: touch_in = CrickitTouchIn(self._seesaw, terminal) self._devices[terminal] = touch_in return touch_in @property def neopixel(self): """```adafruit_seesaw.neopixel`` object on NeoPixel terminal. Raises ValueError if ``init_neopixel`` has not been called. """ if not self._neopixel: raise ValueError("Call init_neopixel first") return self._neopixel def init_neopixel( self, n, *, bpp=3, brightness=1.0, auto_write=True, pixel_order=None ): """Set up a seesaw.NeoPixel object .. note:: On the CPX Crickit board, the NeoPixel terminal is by default controlled by CPX pin A1, and is not controlled by seesaw. So this object will not be usable. Instead, use the regular NeoPixel library and specify ``board.A1`` as the pin. You can change the jumper connection on the bottom of the CPX Crickit board to move control of the NeoPixel terminal to seesaw pin #20 (terminal.NEOPIXEL). In addition, the Crickit FeatherWing always uses seesaw pin #20. In either of those cases, this object will work. .. code-block:: python from adafruit_crickit.crickit import crickit crickit.init_neopixel(24) crickit.neopixel.fill((100, 0, 0)) """ from adafruit_seesaw.neopixel import ( # pylint: disable=import-outside-toplevel NeoPixel, ) self._neopixel = NeoPixel( self._seesaw, _NEOPIXEL, n, bpp=bpp, brightness=brightness, auto_write=auto_write, pixel_order=pixel_order, ) @property def onboard_pixel(self): """```adafruit_seesaw.neopixel`` object on the Seesaw on-board NeoPixel. Initialize on-board NeoPixel and clear upon first use. """ if not self._onboard_pixel: from adafruit_seesaw.neopixel import ( # pylint: disable=import-outside-toplevel NeoPixel, ) self._onboard_pixel = NeoPixel( self._seesaw, _SS_PIXEL, 1, bpp=3, brightness=1.0, auto_write=True, pixel_order=None, ) self._onboard_pixel.fill((0, 0, 0)) return self._onboard_pixel def reset(self): """Reset the whole Crickit board.""" self._seesaw.sw_reset() crickit = None # pylint: disable=invalid-name """A singleton instance to control a single Crickit board, controlled by the default I2C pins.""" # Sphinx's board is missing real pins so skip the constructor in that case. if "I2C" in dir(board): crickit = Crickit(Seesaw(board.I2C())) # pylint: disable=invalid-name