419 lines
13 KiB
Python
Executable file
419 lines
13 KiB
Python
Executable file
# 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 <https://www.adafruit.com/3093>`_
|
|
`Adafruit Crickit FeatherWing <https://www.adafruit.com/3343>`_
|
|
|
|
**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
|