mirror of
https://github.com/KevinMidboe/python-gpiozero.git
synced 2025-12-08 20:39:01 +00:00
@@ -1,6 +1,12 @@
|
|||||||
from __future__ import absolute_import
|
from __future__ import (
|
||||||
|
unicode_literals,
|
||||||
|
print_function,
|
||||||
|
absolute_import,
|
||||||
|
division,
|
||||||
|
)
|
||||||
|
|
||||||
from .devices import (
|
from .devices import (
|
||||||
|
GPIODeviceClosed,
|
||||||
GPIODeviceError,
|
GPIODeviceError,
|
||||||
GPIODevice,
|
GPIODevice,
|
||||||
)
|
)
|
||||||
@@ -16,6 +22,7 @@ from .input_devices import (
|
|||||||
MCP3004,
|
MCP3004,
|
||||||
)
|
)
|
||||||
from .output_devices import (
|
from .output_devices import (
|
||||||
|
OutputDeviceError,
|
||||||
OutputDevice,
|
OutputDevice,
|
||||||
PWMOutputDevice,
|
PWMOutputDevice,
|
||||||
PWMLED,
|
PWMLED,
|
||||||
|
|||||||
@@ -1,19 +1,48 @@
|
|||||||
from .input_devices import Button
|
from __future__ import (
|
||||||
from .output_devices import LED, Buzzer, Motor
|
unicode_literals,
|
||||||
from .devices import GPIODeviceError
|
print_function,
|
||||||
|
absolute_import,
|
||||||
|
division,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
from itertools import izip as zip
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
from .input_devices import InputDeviceError, Button
|
||||||
|
from .output_devices import OutputDeviceError, LED, Buzzer, Motor
|
||||||
|
from .devices import CompositeDevice, SourceMixin
|
||||||
|
|
||||||
|
|
||||||
class LEDBoard(object):
|
class LEDBoard(SourceMixin, CompositeDevice):
|
||||||
"""
|
"""
|
||||||
A Generic LED Board or collection of LEDs.
|
A Generic LED Board or collection of LEDs.
|
||||||
"""
|
"""
|
||||||
def __init__(self, leds):
|
def __init__(self, *pins):
|
||||||
self._leds = tuple(LED(led) for led in leds)
|
super(LEDBoard, self).__init__()
|
||||||
|
self._leds = tuple(LED(pin) for pin in pins)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
"""
|
||||||
|
A tuple containing a boolean value for each LED on the board. This
|
||||||
|
property can also be set to update the state of all LEDs on the board.
|
||||||
|
"""
|
||||||
|
return tuple(led.value for led in self._leds)
|
||||||
|
|
||||||
|
@value.setter
|
||||||
|
def value(self, value):
|
||||||
|
for l, v in zip(self._leds, value):
|
||||||
|
l.value = v
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def leds(self):
|
def leds(self):
|
||||||
|
"""
|
||||||
|
A tuple of all the `LED` objects contained by the instance.
|
||||||
|
"""
|
||||||
return self._leds
|
return self._leds
|
||||||
|
|
||||||
def on(self):
|
def on(self):
|
||||||
@@ -66,10 +95,11 @@ class PiLiter(LEDBoard):
|
|||||||
Ciseco Pi-LITEr: strip of 8 very bright LEDs.
|
Ciseco Pi-LITEr: strip of 8 very bright LEDs.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
leds = (4, 17, 27, 18, 22, 23, 24, 25)
|
super(PiLiter, self).__init__(4, 17, 27, 18, 22, 23, 24, 25)
|
||||||
super(PiLiter, self).__init__(leds)
|
|
||||||
|
|
||||||
|
|
||||||
|
TrafficLightTuple = namedtuple('TrafficLightTuple', ('red', 'amber', 'green'))
|
||||||
|
|
||||||
class TrafficLights(LEDBoard):
|
class TrafficLights(LEDBoard):
|
||||||
"""
|
"""
|
||||||
Generic Traffic Lights set.
|
Generic Traffic Lights set.
|
||||||
@@ -85,12 +115,37 @@ class TrafficLights(LEDBoard):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, red=None, amber=None, green=None):
|
def __init__(self, red=None, amber=None, green=None):
|
||||||
if not all([red, amber, green]):
|
if not all([red, amber, green]):
|
||||||
raise GPIODeviceError('Red, Amber and Green pins must be provided')
|
raise OutputDeviceError('red, amber and green pins must be provided')
|
||||||
|
super(TrafficLights, self).__init__(red, amber, green)
|
||||||
|
|
||||||
self.red = LED(red)
|
@property
|
||||||
self.amber = LED(amber)
|
def value(self):
|
||||||
self.green = LED(green)
|
return TrafficLightTuple(*super(TrafficLights, self).value)
|
||||||
self._leds = (self.red, self.amber, self.green)
|
|
||||||
|
@value.setter
|
||||||
|
def value(self, value):
|
||||||
|
super(TrafficLights, self).value = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def red(self):
|
||||||
|
"""
|
||||||
|
The `LED` object representing the red LED.
|
||||||
|
"""
|
||||||
|
return self.leds[0]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def amber(self):
|
||||||
|
"""
|
||||||
|
The `LED` object representing the red LED.
|
||||||
|
"""
|
||||||
|
return self.leds[1]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def green(self):
|
||||||
|
"""
|
||||||
|
The `LED` object representing the green LED.
|
||||||
|
"""
|
||||||
|
return self.leds[2]
|
||||||
|
|
||||||
|
|
||||||
class PiTraffic(TrafficLights):
|
class PiTraffic(TrafficLights):
|
||||||
@@ -99,25 +154,47 @@ class PiTraffic(TrafficLights):
|
|||||||
and 11.
|
and 11.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
red, amber, green = (9, 10, 11)
|
super(PiTraffic, self).__init__(9, 10, 11)
|
||||||
super(PiTraffic, self).__init__(red, amber, green)
|
|
||||||
|
|
||||||
|
|
||||||
|
FishDishTuple = namedtuple('FishDishTuple', ('red', 'amber', 'green', 'buzzer'))
|
||||||
|
|
||||||
class FishDish(TrafficLights):
|
class FishDish(TrafficLights):
|
||||||
"""
|
"""
|
||||||
Pi Supply FishDish: traffic light LEDs, a button and a buzzer.
|
Pi Supply FishDish: traffic light LEDs, a button and a buzzer.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
red, amber, green = (9, 22, 4)
|
super(FishDish, self).__init__(9, 22, 4)
|
||||||
super(FishDish, self).__init__(red, amber, green)
|
|
||||||
self.buzzer = Buzzer(8)
|
self.buzzer = Buzzer(8)
|
||||||
self.button = Button(pin=7, pull_up=False)
|
self.button = Button(7, pull_up=False)
|
||||||
self._all = self._leds + (self.buzzer,)
|
self._all = self.leds + (self.buzzer,)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def all(self):
|
def all(self):
|
||||||
|
"""
|
||||||
|
A tuple containing objects for all the items on the board (several
|
||||||
|
`LED` objects, a `Buzzer`, and a `Button`).
|
||||||
|
"""
|
||||||
return self._all
|
return self._all
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
"""
|
||||||
|
Returns a named-tuple containing values representing the states of
|
||||||
|
the LEDs, and the buzzer. This property can also be set to a 4-tuple
|
||||||
|
to update the state of all the board's components.
|
||||||
|
"""
|
||||||
|
return FishDishTuple(
|
||||||
|
self.red.value,
|
||||||
|
self.amber.value,
|
||||||
|
self.green.value,
|
||||||
|
self.buzzer.value)
|
||||||
|
|
||||||
|
@value.setter
|
||||||
|
def value(self, value):
|
||||||
|
for i, v in zip(self._all, value):
|
||||||
|
i.value = v
|
||||||
|
|
||||||
def on(self):
|
def on(self):
|
||||||
"""
|
"""
|
||||||
Turn all the board's components on.
|
Turn all the board's components on.
|
||||||
@@ -208,97 +285,79 @@ class TrafficHat(FishDish):
|
|||||||
Ryanteck Traffic HAT: traffic light LEDs, a button and a buzzer.
|
Ryanteck Traffic HAT: traffic light LEDs, a button and a buzzer.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
green, amber, red = (22, 23, 24)
|
super(FishDish, self).__init__(22, 23, 24)
|
||||||
super(FishDish, self).__init__(red, amber, green)
|
|
||||||
self.buzzer = Buzzer(5)
|
self.buzzer = Buzzer(5)
|
||||||
self.button = Button(25)
|
self.button = Button(25)
|
||||||
self._all = self._leds + (self.buzzer,)
|
self._all = self._leds + (self.buzzer,)
|
||||||
|
|
||||||
|
|
||||||
class Robot(object):
|
RobotTuple = namedtuple('RobotTuple', ('left', 'right'))
|
||||||
|
|
||||||
|
class Robot(SourceMixin, CompositeDevice):
|
||||||
"""
|
"""
|
||||||
Generic dual-motor Robot.
|
Generic dual-motor Robot.
|
||||||
"""
|
"""
|
||||||
def __init__(self, left=None, right=None):
|
def __init__(self, left=None, right=None):
|
||||||
if not all([left, right]):
|
if not all([left, right]):
|
||||||
raise GPIODeviceError('left and right motor pins must be provided')
|
raise OutputDeviceError('left and right motor pins must be provided')
|
||||||
|
super(Robot, self).__init__()
|
||||||
|
self._left = Motor(*left)
|
||||||
|
self._right = Motor(*right)
|
||||||
|
|
||||||
left_forward, left_back = left
|
@property
|
||||||
right_forward, right_back = right
|
def value(self):
|
||||||
|
"""
|
||||||
|
Returns a tuple of two floating point values (-1 to 1) representing the
|
||||||
|
speeds of the robot's two motors (left and right). This property can
|
||||||
|
also be set to alter the speed of both motors.
|
||||||
|
"""
|
||||||
|
return RobotTuple(self._left.value, self._right.value)
|
||||||
|
|
||||||
self._left = Motor(forward=left_forward, back=left_back)
|
@value.setter
|
||||||
self._right = Motor(forward=right_forward, back=right_back)
|
def value(self, value):
|
||||||
|
self._left.value, self._right.value = value
|
||||||
self._min_pwm = self._left._min_pwm
|
|
||||||
self._max_pwm = self._left._max_pwm
|
|
||||||
|
|
||||||
def forward(self, speed=1):
|
def forward(self, speed=1):
|
||||||
"""
|
"""
|
||||||
Drive the robot forward.
|
Drive the robot forward by running both motors forward.
|
||||||
|
|
||||||
speed: `1`
|
speed: `1`
|
||||||
Speed at which to drive the motors, 0 to 1.
|
Speed at which to drive the motors, 0 to 1.
|
||||||
"""
|
"""
|
||||||
self._left._backward.off()
|
self._left.forward(speed)
|
||||||
self._right._backward.off()
|
self._right.forward(speed)
|
||||||
|
|
||||||
self._left._forward.on()
|
|
||||||
self._right._forward.on()
|
|
||||||
if speed < 1:
|
|
||||||
sleep(0.1) # warm up the motors
|
|
||||||
self._left._forward.value = speed
|
|
||||||
self._right._forward.value = speed
|
|
||||||
|
|
||||||
def backward(self, speed=1):
|
def backward(self, speed=1):
|
||||||
"""
|
"""
|
||||||
Drive the robot backward.
|
Drive the robot backward by running both motors backward.
|
||||||
|
|
||||||
speed: `1`
|
speed: `1`
|
||||||
Speed at which to drive the motors, 0 to 1.
|
Speed at which to drive the motors, 0 to 1.
|
||||||
"""
|
"""
|
||||||
self._left._forward.off()
|
self._left.backward(speed)
|
||||||
self._right._forward.off()
|
self._right.backward(speed)
|
||||||
|
|
||||||
self._left._backward.on()
|
|
||||||
self._right._backward.on()
|
|
||||||
if speed < 1:
|
|
||||||
sleep(0.1) # warm up the motors
|
|
||||||
self._left._backward.value = speed
|
|
||||||
self._right._backward.value = speed
|
|
||||||
|
|
||||||
def left(self, speed=1):
|
def left(self, speed=1):
|
||||||
"""
|
"""
|
||||||
Make the robot turn left.
|
Make the robot turn left by running the right motor forward and left
|
||||||
|
motor backward.
|
||||||
|
|
||||||
speed: `1`
|
speed: `1`
|
||||||
Speed at which to drive the motors, 0 to 1.
|
Speed at which to drive the motors, 0 to 1.
|
||||||
"""
|
"""
|
||||||
self._right._backward.off()
|
self._right.forward(speed)
|
||||||
self._left._forward.off()
|
self._left.backward(speed)
|
||||||
|
|
||||||
self._right._forward.on()
|
|
||||||
self._left._backward.on()
|
|
||||||
if speed < 1:
|
|
||||||
sleep(0.1) # warm up the motors
|
|
||||||
self._right._forward.value = speed
|
|
||||||
self._left._backward.value = speed
|
|
||||||
|
|
||||||
def right(self, speed=1):
|
def right(self, speed=1):
|
||||||
"""
|
"""
|
||||||
Make the robot turn right.
|
Make the robot turn right by running the left motor forward and right
|
||||||
|
motor backward.
|
||||||
|
|
||||||
speed: `1`
|
speed: `1`
|
||||||
Speed at which to drive the motors, 0 to 1.
|
Speed at which to drive the motors, 0 to 1.
|
||||||
"""
|
"""
|
||||||
self._left._backward.off()
|
self._left.forward(speed)
|
||||||
self._right._forward.off()
|
self._right.backward(speed)
|
||||||
|
|
||||||
self._left._forward.on()
|
|
||||||
self._right._backward.on()
|
|
||||||
if speed < 1:
|
|
||||||
sleep(0.1) # warm up the motors
|
|
||||||
self._left._forward.value = speed
|
|
||||||
self._right._backward.value = speed
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""
|
"""
|
||||||
@@ -313,6 +372,4 @@ class RyanteckRobot(Robot):
|
|||||||
RTK MCB Robot. Generic robot controller with pre-configured pin numbers.
|
RTK MCB Robot. Generic robot controller with pre-configured pin numbers.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
left = (17, 18)
|
super(RyanteckRobot, self).__init__((17, 18), (22, 23))
|
||||||
right = (22, 23)
|
|
||||||
super(RyanteckRobot, self).__init__(left=left, right=right)
|
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
from __future__ import (
|
||||||
|
unicode_literals,
|
||||||
|
print_function,
|
||||||
|
absolute_import,
|
||||||
|
division,
|
||||||
|
)
|
||||||
|
|
||||||
import atexit
|
import atexit
|
||||||
import weakref
|
import weakref
|
||||||
from threading import Thread, Event, RLock
|
from threading import Thread, Event, RLock
|
||||||
@@ -30,11 +37,123 @@ GPIO.setwarnings(False)
|
|||||||
class GPIODeviceError(Exception):
|
class GPIODeviceError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class GPIODeviceClosed(GPIODeviceError):
|
class GPIODeviceClosed(GPIODeviceError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class GPIODevice(object):
|
class GPIOFixedAttrs(type):
|
||||||
|
# NOTE Yes, this is a metaclass. Don't be scared - it's a simple one.
|
||||||
|
|
||||||
|
def __call__(cls, *args, **kwargs):
|
||||||
|
# Construct the class as normal and ensure it's a subclass of GPIOBase
|
||||||
|
# (defined below with a custom __setattrs__)
|
||||||
|
result = super(GPIOFixedAttrs, cls).__call__(*args, **kwargs)
|
||||||
|
assert isinstance(result, GPIOBase)
|
||||||
|
# At this point __new__ and __init__ have all been run. We now fix the
|
||||||
|
# set of attributes on the class by dir'ing the instance and creating a
|
||||||
|
# frozenset of the result called __attrs__ (which is queried by
|
||||||
|
# GPIOBase.__setattr__)
|
||||||
|
result.__attrs__ = frozenset(dir(result))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class GPIOBase(object):
|
||||||
|
__metaclass__ = GPIOFixedAttrs
|
||||||
|
|
||||||
|
def __setattr__(self, name, value):
|
||||||
|
# This overridden __setattr__ simply ensures that additional attributes
|
||||||
|
# cannot be set on the class after construction (it manages this in
|
||||||
|
# conjunction with the meta-class above). Traditionally, this is
|
||||||
|
# managed with __slots__; however, this doesn't work with Python's
|
||||||
|
# multiple inheritance system which we need to use in order to avoid
|
||||||
|
# repeating the "source" and "values" property code in myriad places
|
||||||
|
if hasattr(self, '__attrs__') and name not in self.__attrs__:
|
||||||
|
raise AttributeError(
|
||||||
|
"'%s' object has no attribute '%s'" % (
|
||||||
|
self.__class__.__name__, name))
|
||||||
|
return super(GPIOBase, self).__setattr__(name, value)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
# This is a placeholder which is simply here to ensure close() can be
|
||||||
|
# safely called from subclasses without worrying whether super-class'
|
||||||
|
# have it (which in turn is useful in conjunction with the SourceMixin
|
||||||
|
# class).
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_value, exc_tb):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
|
class ValuesMixin(object):
|
||||||
|
# NOTE Use this mixin *first* in the parent list
|
||||||
|
|
||||||
|
@property
|
||||||
|
def values(self):
|
||||||
|
"""
|
||||||
|
An infinite iterator of values read from `value`.
|
||||||
|
"""
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
yield self.value
|
||||||
|
except GPIODeviceClosed:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
class SourceMixin(object):
|
||||||
|
# NOTE Use this mixin *first* in the parent list
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self._source = None
|
||||||
|
self._source_thread = None
|
||||||
|
super(SourceMixin, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
try:
|
||||||
|
super(SourceMixin, self).close()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
self.source = None
|
||||||
|
|
||||||
|
def _copy_values(self, source):
|
||||||
|
for v in source:
|
||||||
|
self.value = v
|
||||||
|
if self._source_thread.stopping.wait(0):
|
||||||
|
break
|
||||||
|
|
||||||
|
@property
|
||||||
|
def source(self):
|
||||||
|
"""
|
||||||
|
The iterable to use as a source of values for `value`.
|
||||||
|
"""
|
||||||
|
return self._source
|
||||||
|
|
||||||
|
@source.setter
|
||||||
|
def source(self, value):
|
||||||
|
if self._source_thread is not None:
|
||||||
|
self._source_thread.stop()
|
||||||
|
self._source_thread = None
|
||||||
|
self._source = value
|
||||||
|
if value is not None:
|
||||||
|
self._source_thread = GPIOThread(target=self._copy_values, args=(value,))
|
||||||
|
self._source_thread.start()
|
||||||
|
|
||||||
|
|
||||||
|
class CompositeDevice(ValuesMixin, GPIOBase):
|
||||||
|
"""
|
||||||
|
Represents a device composed of multiple GPIO devices like simple HATs,
|
||||||
|
H-bridge motor controllers, robots composed of multiple motors, etc.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class GPIODevice(ValuesMixin, GPIOBase):
|
||||||
"""
|
"""
|
||||||
Represents a generic GPIO device.
|
Represents a generic GPIO device.
|
||||||
|
|
||||||
@@ -47,9 +166,6 @@ class GPIODevice(object):
|
|||||||
The GPIO pin (in BCM numbering) that the device is connected to. If
|
The GPIO pin (in BCM numbering) that the device is connected to. If
|
||||||
this is `None` a `GPIODeviceError` will be raised.
|
this is `None` a `GPIODeviceError` will be raised.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('_pin', '_active_state', '_inactive_state')
|
|
||||||
|
|
||||||
def __init__(self, pin=None):
|
def __init__(self, pin=None):
|
||||||
super(GPIODevice, self).__init__()
|
super(GPIODevice, self).__init__()
|
||||||
# self._pin must be set before any possible exceptions can be raised
|
# self._pin must be set before any possible exceptions can be raised
|
||||||
@@ -68,9 +184,6 @@ class GPIODevice(object):
|
|||||||
self._active_state = GPIO.HIGH
|
self._active_state = GPIO.HIGH
|
||||||
self._inactive_state = GPIO.LOW
|
self._inactive_state = GPIO.LOW
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
def _read(self):
|
def _read(self):
|
||||||
try:
|
try:
|
||||||
return GPIO.input(self.pin) == self._active_state
|
return GPIO.input(self.pin) == self._active_state
|
||||||
@@ -132,6 +245,7 @@ class GPIODevice(object):
|
|||||||
... led.on()
|
... led.on()
|
||||||
...
|
...
|
||||||
"""
|
"""
|
||||||
|
super(GPIODevice, self).close()
|
||||||
with _GPIO_PINS_LOCK:
|
with _GPIO_PINS_LOCK:
|
||||||
pin = self._pin
|
pin = self._pin
|
||||||
self._pin = None
|
self._pin = None
|
||||||
@@ -140,12 +254,6 @@ class GPIODevice(object):
|
|||||||
GPIO.remove_event_detect(pin)
|
GPIO.remove_event_detect(pin)
|
||||||
GPIO.cleanup(pin)
|
GPIO.cleanup(pin)
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, exc_tb):
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pin(self):
|
def pin(self):
|
||||||
"""
|
"""
|
||||||
@@ -163,17 +271,6 @@ class GPIODevice(object):
|
|||||||
|
|
||||||
is_active = value
|
is_active = value
|
||||||
|
|
||||||
@property
|
|
||||||
def values(self):
|
|
||||||
"""
|
|
||||||
An infinite iterator of values read from `value`.
|
|
||||||
"""
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
yield self.value
|
|
||||||
except GPIODeviceClosed:
|
|
||||||
break
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
try:
|
try:
|
||||||
return "<gpiozero.%s object on pin=%d, is_active=%s>" % (
|
return "<gpiozero.%s object on pin=%d, is_active=%s>" % (
|
||||||
@@ -238,3 +335,4 @@ class GPIOQueue(GPIOThread):
|
|||||||
except ReferenceError:
|
except ReferenceError:
|
||||||
# Parent is dead; time to die!
|
# Parent is dead; time to die!
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
from __future__ import division
|
from __future__ import (
|
||||||
|
unicode_literals,
|
||||||
|
print_function,
|
||||||
|
absolute_import,
|
||||||
|
division,
|
||||||
|
)
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
import warnings
|
import warnings
|
||||||
@@ -10,7 +15,13 @@ from RPi import GPIO
|
|||||||
from w1thermsensor import W1ThermSensor
|
from w1thermsensor import W1ThermSensor
|
||||||
from spidev import SpiDev
|
from spidev import SpiDev
|
||||||
|
|
||||||
from .devices import GPIODeviceError, GPIODeviceClosed, GPIODevice, GPIOQueue
|
from .devices import (
|
||||||
|
GPIODeviceError,
|
||||||
|
GPIODeviceClosed,
|
||||||
|
GPIODevice,
|
||||||
|
CompositeDevice,
|
||||||
|
GPIOQueue,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class InputDeviceError(GPIODeviceError):
|
class InputDeviceError(GPIODeviceError):
|
||||||
@@ -35,13 +46,6 @@ class InputDevice(GPIODevice):
|
|||||||
If `True`, the pin will be pulled high with an internal resistor. If
|
If `True`, the pin will be pulled high with an internal resistor. If
|
||||||
`False` (the default), the pin will be pulled low.
|
`False` (the default), the pin will be pulled low.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = (
|
|
||||||
'_pull_up',
|
|
||||||
'_active_edge',
|
|
||||||
'_inactive_edge',
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, pin=None, pull_up=False):
|
def __init__(self, pin=None, pull_up=False):
|
||||||
if pin in (2, 3) and not pull_up:
|
if pin in (2, 3) and not pull_up:
|
||||||
raise InputDeviceError(
|
raise InputDeviceError(
|
||||||
@@ -79,6 +83,10 @@ class InputDevice(GPIODevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def pull_up(self):
|
def pull_up(self):
|
||||||
|
"""
|
||||||
|
If `True`, the device uses a pull-up resistor to set the GPIO pin
|
||||||
|
"high" by default. Defaults to `False`.
|
||||||
|
"""
|
||||||
return self._pull_up
|
return self._pull_up
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@@ -102,15 +110,6 @@ class WaitableInputDevice(InputDevice):
|
|||||||
Note that this class provides no means of actually firing its events; it's
|
Note that this class provides no means of actually firing its events; it's
|
||||||
effectively an abstract base class.
|
effectively an abstract base class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = (
|
|
||||||
'_active_event',
|
|
||||||
'_inactive_event',
|
|
||||||
'_when_activated',
|
|
||||||
'_when_deactivated',
|
|
||||||
'_last_state',
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, pin=None, pull_up=False):
|
def __init__(self, pin=None, pull_up=False):
|
||||||
super(WaitableInputDevice, self).__init__(pin, pull_up)
|
super(WaitableInputDevice, self).__init__(pin, pull_up)
|
||||||
self._active_event = Event()
|
self._active_event = Event()
|
||||||
@@ -121,7 +120,7 @@ class WaitableInputDevice(InputDevice):
|
|||||||
|
|
||||||
def wait_for_active(self, timeout=None):
|
def wait_for_active(self, timeout=None):
|
||||||
"""
|
"""
|
||||||
Halt the program until the device is activated, or the timeout is
|
Pause the script until the device is activated, or the timeout is
|
||||||
reached.
|
reached.
|
||||||
|
|
||||||
timeout: `None`
|
timeout: `None`
|
||||||
@@ -132,7 +131,7 @@ class WaitableInputDevice(InputDevice):
|
|||||||
|
|
||||||
def wait_for_inactive(self, timeout=None):
|
def wait_for_inactive(self, timeout=None):
|
||||||
"""
|
"""
|
||||||
Halt the program until the device is deactivated, or the timeout is
|
Pause the script until the device is deactivated, or the timeout is
|
||||||
reached.
|
reached.
|
||||||
|
|
||||||
timeout: `None`
|
timeout: `None`
|
||||||
@@ -248,9 +247,6 @@ class DigitalInputDevice(WaitableInputDevice):
|
|||||||
ignore changes in state after an initial change. This defaults to
|
ignore changes in state after an initial change. This defaults to
|
||||||
`None` which indicates that no bounce compensation will be performed.
|
`None` which indicates that no bounce compensation will be performed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ()
|
|
||||||
|
|
||||||
def __init__(self, pin=None, pull_up=False, bounce_time=None):
|
def __init__(self, pin=None, pull_up=False, bounce_time=None):
|
||||||
super(DigitalInputDevice, self).__init__(pin, pull_up)
|
super(DigitalInputDevice, self).__init__(pin, pull_up)
|
||||||
try:
|
try:
|
||||||
@@ -301,9 +297,6 @@ class SmoothedInputDevice(WaitableInputDevice):
|
|||||||
If `True`, a value will be returned immediately, but be aware that this
|
If `True`, a value will be returned immediately, but be aware that this
|
||||||
value is likely to fluctuate excessively.
|
value is likely to fluctuate excessively.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('_queue', '_threshold', '__weakref__')
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, pin=None, pull_up=False, threshold=0.5,
|
self, pin=None, pull_up=False, threshold=0.5,
|
||||||
queue_len=5, sample_wait=0.0, partial=False):
|
queue_len=5, sample_wait=0.0, partial=False):
|
||||||
@@ -388,6 +381,9 @@ class SmoothedInputDevice(WaitableInputDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def is_active(self):
|
def is_active(self):
|
||||||
|
"""
|
||||||
|
Returns `True` if the device is currently active and `False` otherwise.
|
||||||
|
"""
|
||||||
return self.value > self.threshold
|
return self.value > self.threshold
|
||||||
|
|
||||||
|
|
||||||
@@ -399,9 +395,6 @@ class Button(DigitalInputDevice):
|
|||||||
side of the switch, and ground to the other (the default `pull_up` value
|
side of the switch, and ground to the other (the default `pull_up` value
|
||||||
is `True`).
|
is `True`).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ()
|
|
||||||
|
|
||||||
def __init__(self, pin=None, pull_up=True, bouncetime=None):
|
def __init__(self, pin=None, pull_up=True, bouncetime=None):
|
||||||
super(Button, self).__init__(pin, pull_up, bouncetime)
|
super(Button, self).__init__(pin, pull_up, bouncetime)
|
||||||
|
|
||||||
@@ -426,9 +419,6 @@ class MotionSensor(SmoothedInputDevice):
|
|||||||
particularly "jittery" you may wish to set this to a higher value (e.g. 5)
|
particularly "jittery" you may wish to set this to a higher value (e.g. 5)
|
||||||
to mitigate this.
|
to mitigate this.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ()
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, pin=None, queue_len=1, sample_rate=10, threshold=0.5,
|
self, pin=None, queue_len=1, sample_rate=10, threshold=0.5,
|
||||||
partial=False):
|
partial=False):
|
||||||
@@ -458,12 +448,6 @@ class LightSensor(SmoothedInputDevice):
|
|||||||
class repeatedly discharges the capacitor, then times the duration it takes
|
class repeatedly discharges the capacitor, then times the duration it takes
|
||||||
to charge (which will vary according to the light falling on the LDR).
|
to charge (which will vary according to the light falling on the LDR).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = (
|
|
||||||
'_charge_time_limit',
|
|
||||||
'_charged',
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, pin=None, queue_len=5, charge_time_limit=0.01,
|
self, pin=None, queue_len=5, charge_time_limit=0.01,
|
||||||
threshold=0.1, partial=False):
|
threshold=0.1, partial=False):
|
||||||
@@ -517,17 +501,11 @@ class TemperatureSensor(W1ThermSensor):
|
|||||||
return self.get_temperature()
|
return self.get_temperature()
|
||||||
|
|
||||||
|
|
||||||
class AnalogInputDevice(object):
|
class AnalogInputDevice(CompositeDevice):
|
||||||
"""
|
"""
|
||||||
Represents an analog input device connected to SPI (serial interface).
|
Represents an analog input device connected to SPI (serial interface).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = (
|
|
||||||
'_device',
|
|
||||||
'_bits',
|
|
||||||
'_spi',
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, device=0, bits=None):
|
def __init__(self, device=0, bits=None):
|
||||||
if bits is None:
|
if bits is None:
|
||||||
raise InputDeviceError('you must specify the bit resolution of the device')
|
raise InputDeviceError('you must specify the bit resolution of the device')
|
||||||
@@ -537,25 +515,32 @@ class AnalogInputDevice(object):
|
|||||||
self._bits = bits
|
self._bits = bits
|
||||||
self._spi = SpiDev()
|
self._spi = SpiDev()
|
||||||
self._spi.open(0, self.device)
|
self._spi.open(0, self.device)
|
||||||
|
super(AnalogInputDevice, self).__init__()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
"""
|
||||||
|
Shut down the device and release all associated resources.
|
||||||
|
"""
|
||||||
if self._spi:
|
if self._spi:
|
||||||
s = self._spi
|
s = self._spi
|
||||||
self._spi = None
|
self._spi = None
|
||||||
s.close()
|
s.close()
|
||||||
|
super(AnalogInputDevice, self).close()
|
||||||
def __enter__(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, exc_tb):
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bus(self):
|
def bus(self):
|
||||||
|
"""
|
||||||
|
The SPI bus that the device is connected to. As the Pi only has a
|
||||||
|
single (user accessible) SPI bus, this always returns 0.
|
||||||
|
"""
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device(self):
|
def device(self):
|
||||||
|
"""
|
||||||
|
The select pin that the device is connected to. The Pi has two select
|
||||||
|
pins so this will be 0 or 1.
|
||||||
|
"""
|
||||||
return self._device
|
return self._device
|
||||||
|
|
||||||
def _read(self):
|
def _read(self):
|
||||||
@@ -563,13 +548,15 @@ class AnalogInputDevice(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def value(self):
|
def value(self):
|
||||||
|
"""
|
||||||
|
A value read from the device. This will be a floating point value
|
||||||
|
between 0 and 1 (scaled according to the number of bits supported by
|
||||||
|
the device).
|
||||||
|
"""
|
||||||
return self._read() / (2**self._bits - 1)
|
return self._read() / (2**self._bits - 1)
|
||||||
|
|
||||||
|
|
||||||
class MCP3008(AnalogInputDevice):
|
class MCP3008(AnalogInputDevice):
|
||||||
|
|
||||||
__slots__ = ('_channel')
|
|
||||||
|
|
||||||
def __init__(self, device=0, channel=0):
|
def __init__(self, device=0, channel=0):
|
||||||
if not 0 <= channel < 8:
|
if not 0 <= channel < 8:
|
||||||
raise InputDeviceError('channel must be between 0 and 7')
|
raise InputDeviceError('channel must be between 0 and 7')
|
||||||
@@ -578,6 +565,10 @@ class MCP3008(AnalogInputDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def channel(self):
|
def channel(self):
|
||||||
|
"""
|
||||||
|
The channel to read data from. The MCP3008 has 8 channels (so this will
|
||||||
|
be between 0 and 7) while the MCP3004 has 4 channels (range 0 to 3).
|
||||||
|
"""
|
||||||
return self._channel
|
return self._channel
|
||||||
|
|
||||||
def _read(self):
|
def _read(self):
|
||||||
@@ -602,11 +593,10 @@ class MCP3008(AnalogInputDevice):
|
|||||||
|
|
||||||
|
|
||||||
class MCP3004(MCP3008):
|
class MCP3004(MCP3008):
|
||||||
|
|
||||||
__slots__ = ()
|
|
||||||
|
|
||||||
def __init__(self, device=0, channel=0):
|
def __init__(self, device=0, channel=0):
|
||||||
# MCP3004 protocol is identical to MCP3008 but the top bit of the
|
# MCP3004 protocol is identical to MCP3008 but the top bit of the
|
||||||
# channel number must be 0 (effectively restricting it to 4 channels)
|
# channel number must be 0 (effectively restricting it to 4 channels)
|
||||||
if not 0 <= channel < 4:
|
if not 0 <= channel < 4:
|
||||||
raise InputDeviceError('channel must be between 0 and 3')
|
raise InputDeviceError('channel must be between 0 and 3')
|
||||||
|
super(MCP3004, self).__init__(device, channel)
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
from __future__ import (
|
||||||
|
unicode_literals,
|
||||||
|
print_function,
|
||||||
|
absolute_import,
|
||||||
|
division,
|
||||||
|
)
|
||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
@@ -5,14 +12,21 @@ from itertools import repeat
|
|||||||
|
|
||||||
from RPi import GPIO
|
from RPi import GPIO
|
||||||
|
|
||||||
from .devices import GPIODeviceError, GPIODevice, GPIOThread
|
from .devices import (
|
||||||
|
GPIODeviceError,
|
||||||
|
GPIODeviceClosed,
|
||||||
|
GPIODevice,
|
||||||
|
GPIOThread,
|
||||||
|
CompositeDevice,
|
||||||
|
SourceMixin,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class OutputDeviceError(GPIODeviceError):
|
class OutputDeviceError(GPIODeviceError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class OutputDevice(GPIODevice):
|
class OutputDevice(SourceMixin, GPIODevice):
|
||||||
"""
|
"""
|
||||||
Represents a generic GPIO output device.
|
Represents a generic GPIO output device.
|
||||||
|
|
||||||
@@ -25,13 +39,8 @@ class OutputDevice(GPIODevice):
|
|||||||
`False`, the `on` method will set the GPIO to LOW (the `off` method
|
`False`, the `on` method will set the GPIO to LOW (the `off` method
|
||||||
always does the opposite).
|
always does the opposite).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('_active_high', '_source', '_source_thread')
|
|
||||||
|
|
||||||
def __init__(self, pin=None, active_high=True):
|
def __init__(self, pin=None, active_high=True):
|
||||||
self._active_high = active_high
|
self._active_high = active_high
|
||||||
self._source = None
|
|
||||||
self._source_thread = None
|
|
||||||
super(OutputDevice, self).__init__(pin)
|
super(OutputDevice, self).__init__(pin)
|
||||||
self._active_state = GPIO.HIGH if active_high else GPIO.LOW
|
self._active_state = GPIO.HIGH if active_high else GPIO.LOW
|
||||||
self._inactive_state = GPIO.LOW if active_high else GPIO.HIGH
|
self._inactive_state = GPIO.LOW if active_high else GPIO.HIGH
|
||||||
@@ -52,10 +61,6 @@ class OutputDevice(GPIODevice):
|
|||||||
self.close()
|
self.close()
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self.source = None
|
|
||||||
super(OutputDevice, self).close()
|
|
||||||
|
|
||||||
def _write(self, value):
|
def _write(self, value):
|
||||||
GPIO.output(self.pin, bool(value))
|
GPIO.output(self.pin, bool(value))
|
||||||
|
|
||||||
@@ -79,26 +84,6 @@ class OutputDevice(GPIODevice):
|
|||||||
def value(self, value):
|
def value(self, value):
|
||||||
self._write(value)
|
self._write(value)
|
||||||
|
|
||||||
def _copy_values(self, source):
|
|
||||||
for v in source:
|
|
||||||
self.value = v
|
|
||||||
if self._source_thread.stopping.wait(0):
|
|
||||||
break
|
|
||||||
|
|
||||||
@property
|
|
||||||
def source(self):
|
|
||||||
return self._source
|
|
||||||
|
|
||||||
@source.setter
|
|
||||||
def source(self, value):
|
|
||||||
if self._source_thread is not None:
|
|
||||||
self._source_thread.stop()
|
|
||||||
self._source_thread = None
|
|
||||||
self._source = value
|
|
||||||
if value is not None:
|
|
||||||
self._source_thread = GPIOThread(target=self._copy_values, args=(value,))
|
|
||||||
self._source_thread.start()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def active_high(self):
|
def active_high(self):
|
||||||
return self._active_high
|
return self._active_high
|
||||||
@@ -120,12 +105,9 @@ class DigitalOutputDevice(OutputDevice):
|
|||||||
optional background thread to handle toggling the device state without
|
optional background thread to handle toggling the device state without
|
||||||
further interaction.
|
further interaction.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('_blink_thread', '_lock')
|
|
||||||
|
|
||||||
def __init__(self, pin=None, active_high=True):
|
def __init__(self, pin=None, active_high=True):
|
||||||
super(DigitalOutputDevice, self).__init__(pin, active_high)
|
|
||||||
self._blink_thread = None
|
self._blink_thread = None
|
||||||
|
super(DigitalOutputDevice, self).__init__(pin, active_high)
|
||||||
self._lock = Lock()
|
self._lock = Lock()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
@@ -148,8 +130,8 @@ class DigitalOutputDevice(OutputDevice):
|
|||||||
|
|
||||||
def toggle(self):
|
def toggle(self):
|
||||||
"""
|
"""
|
||||||
Reverse the state of the device.
|
Reverse the state of the device. If it's on, turn it off; if it's off,
|
||||||
If it's on, turn it off; if it's off, turn it on.
|
turn it on.
|
||||||
"""
|
"""
|
||||||
with self._lock:
|
with self._lock:
|
||||||
if self.is_active:
|
if self.is_active:
|
||||||
@@ -161,20 +143,20 @@ class DigitalOutputDevice(OutputDevice):
|
|||||||
"""
|
"""
|
||||||
Make the device turn on and off repeatedly.
|
Make the device turn on and off repeatedly.
|
||||||
|
|
||||||
on_time: 1
|
on_time: `1`
|
||||||
Number of seconds on
|
Number of seconds on
|
||||||
|
|
||||||
off_time: 1
|
off_time: `1`
|
||||||
Number of seconds off
|
Number of seconds off
|
||||||
|
|
||||||
n: None
|
n: `None`
|
||||||
Number of times to blink; None means forever
|
Number of times to blink; `None` means forever
|
||||||
|
|
||||||
background: True
|
background: `True`
|
||||||
If True, start a background thread to continue blinking and return
|
If `True`, start a background thread to continue blinking and
|
||||||
immediately. If False, only return when the blink is finished
|
return immediately. If `False`, only return when the blink is
|
||||||
(warning: the default value of n will result in this method never
|
finished (warning: the default value of n will result in this
|
||||||
returning).
|
method never returning).
|
||||||
"""
|
"""
|
||||||
self._stop_blink()
|
self._stop_blink()
|
||||||
self._blink_thread = GPIOThread(
|
self._blink_thread = GPIOThread(
|
||||||
@@ -209,7 +191,7 @@ class LED(DigitalOutputDevice):
|
|||||||
anode (long leg) of the LED, and the cathode (short leg) to ground, with
|
anode (long leg) of the LED, and the cathode (short leg) to ground, with
|
||||||
an optional resistor to prevent the LED from burning out.
|
an optional resistor to prevent the LED from burning out.
|
||||||
"""
|
"""
|
||||||
__slots__ = ()
|
pass
|
||||||
|
|
||||||
LED.is_lit = LED.is_active
|
LED.is_lit = LED.is_active
|
||||||
|
|
||||||
@@ -221,16 +203,13 @@ class Buzzer(DigitalOutputDevice):
|
|||||||
A typical configuration of such a device is to connect a GPIO pin to the
|
A typical configuration of such a device is to connect a GPIO pin to the
|
||||||
anode (long leg) of the buzzer, and the cathode (short leg) to ground.
|
anode (long leg) of the buzzer, and the cathode (short leg) to ground.
|
||||||
"""
|
"""
|
||||||
__slots__ = ()
|
pass
|
||||||
|
|
||||||
|
|
||||||
class PWMOutputDevice(DigitalOutputDevice):
|
class PWMOutputDevice(DigitalOutputDevice):
|
||||||
"""
|
"""
|
||||||
Generic Output device configured for PWM (Pulse-Width Modulation).
|
Generic Output device configured for PWM (Pulse-Width Modulation).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('_pwm', '_frequency', '_value')
|
|
||||||
|
|
||||||
def __init__(self, pin=None, frequency=100):
|
def __init__(self, pin=None, frequency=100):
|
||||||
self._pwm = None
|
self._pwm = None
|
||||||
super(PWMOutputDevice, self).__init__(pin)
|
super(PWMOutputDevice, self).__init__(pin)
|
||||||
@@ -278,6 +257,9 @@ class PWMOutputDevice(DigitalOutputDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def is_active(self):
|
def is_active(self):
|
||||||
|
"""
|
||||||
|
Returns `True` if the device is currently active and `False` otherwise.
|
||||||
|
"""
|
||||||
return self.value > 0.0
|
return self.value > 0.0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -295,8 +277,17 @@ class PWMOutputDevice(DigitalOutputDevice):
|
|||||||
|
|
||||||
|
|
||||||
class PWMLED(PWMOutputDevice):
|
class PWMLED(PWMOutputDevice):
|
||||||
|
"""
|
||||||
|
An LED (Light Emmitting Diode) component with variable brightness.
|
||||||
|
|
||||||
|
A typical configuration of such a device is to connect a GPIO pin to the
|
||||||
|
anode (long leg) of the LED, and the cathode (short leg) to ground, with
|
||||||
|
an optional resistor to prevent the LED from burning out.
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
PWMLED.is_lit = PWMLED.is_active
|
||||||
|
|
||||||
|
|
||||||
def _led_property(index, doc=None):
|
def _led_property(index, doc=None):
|
||||||
return property(
|
return property(
|
||||||
@@ -306,7 +297,7 @@ def _led_property(index, doc=None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RGBLED(object):
|
class RGBLED(SourceMixin, CompositeDevice):
|
||||||
"""
|
"""
|
||||||
Single LED with individually controllable red, green and blue components.
|
Single LED with individually controllable red, green and blue components.
|
||||||
|
|
||||||
@@ -319,10 +310,10 @@ class RGBLED(object):
|
|||||||
blue: `None`
|
blue: `None`
|
||||||
The GPIO pin that controls the blue component of the RGB LED.
|
The GPIO pin that controls the blue component of the RGB LED.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('_leds')
|
|
||||||
|
|
||||||
def __init__(self, red=None, green=None, blue=None):
|
def __init__(self, red=None, green=None, blue=None):
|
||||||
|
if not all([red, green, blue]):
|
||||||
|
raise OutputDeviceError('red, green, and blue pins must be provided')
|
||||||
|
super(RGBLED, self).__init__()
|
||||||
self._leds = tuple(PWMOutputDevice(pin) for pin in (red, green, blue))
|
self._leds = tuple(PWMOutputDevice(pin) for pin in (red, green, blue))
|
||||||
|
|
||||||
red = _led_property(0)
|
red = _led_property(0)
|
||||||
@@ -330,78 +321,98 @@ class RGBLED(object):
|
|||||||
blue = _led_property(2)
|
blue = _led_property(2)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def color(self):
|
def value(self):
|
||||||
"""
|
"""
|
||||||
Set the color of the LED from an RGB 3-tuple of `(red, green, blue)`
|
Represents the color of the LED as an RGB 3-tuple of `(red, green,
|
||||||
where each value between 0 and 1.
|
blue)` where each value is between 0 and 1.
|
||||||
|
|
||||||
For example, purple would be `(1, 0, 1)` and yellow would be `(1, 1,
|
For example, purple would be `(1, 0, 1)` and yellow would be `(1, 1,
|
||||||
0)`, while orange would be `(1, 0.5, 0)`.
|
0)`, while orange would be `(1, 0.5, 0)`.
|
||||||
"""
|
"""
|
||||||
return (self.red, self.green, self.blue)
|
return (self.red, self.green, self.blue)
|
||||||
|
|
||||||
@color.setter
|
@value.setter
|
||||||
def color(self, value):
|
def value(self, value):
|
||||||
self.red, self.green, self.blue = value
|
self.red, self.green, self.blue = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_active(self):
|
||||||
|
"""
|
||||||
|
Returns `True` if the LED is currently active and `False` otherwise.
|
||||||
|
"""
|
||||||
|
return self.value != (0, 0, 0)
|
||||||
|
|
||||||
|
color = value
|
||||||
|
|
||||||
def on(self):
|
def on(self):
|
||||||
"""
|
"""
|
||||||
Turn the device on. This equivalent to setting the device color to
|
Turn the device on. This equivalent to setting the device color to
|
||||||
white `(1, 1, 1)`.
|
white `(1, 1, 1)`.
|
||||||
"""
|
"""
|
||||||
self.color = (1, 1, 1)
|
self.value = (1, 1, 1)
|
||||||
|
|
||||||
def off(self):
|
def off(self):
|
||||||
"""
|
"""
|
||||||
Turn the device off. This is equivalent to setting the device color
|
Turn the device off. This is equivalent to setting the device color
|
||||||
to black `(0, 0, 0)`.
|
to black `(0, 0, 0)`.
|
||||||
"""
|
"""
|
||||||
self.color = (0, 0, 0)
|
self.value = (0, 0, 0)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
for led in self._leds:
|
for led in self._leds:
|
||||||
led.close()
|
led.close()
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, exc_tb):
|
class Motor(SourceMixin, CompositeDevice):
|
||||||
self.close()
|
|
||||||
|
|
||||||
|
|
||||||
class Motor(object):
|
|
||||||
"""
|
"""
|
||||||
Generic bi-directional motor.
|
Generic bi-directional motor.
|
||||||
"""
|
"""
|
||||||
|
def __init__(self, forward=None, backward=None):
|
||||||
__slots__ = ('_forward', '_backward')
|
|
||||||
|
|
||||||
def __init__(self, forward=None, back=None):
|
|
||||||
if not all([forward, back]):
|
if not all([forward, back]):
|
||||||
raise GPIODeviceError('forward and back pins must be provided')
|
raise OutputDeviceError('forward and back pins must be provided')
|
||||||
|
super(Motor, self).__init__()
|
||||||
self._forward = PWMOutputDevice(forward)
|
self._forward = PWMOutputDevice(forward)
|
||||||
self._backward = PWMOutputDevice(back)
|
self._backward = PWMOutputDevice(backward)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
"""
|
||||||
|
Represents the speed of the motor as a floating point value between -1
|
||||||
|
(full speed backward) and 1 (full speed forward).
|
||||||
|
"""
|
||||||
|
return self._forward.value - self._backward.value
|
||||||
|
|
||||||
|
@value.setter
|
||||||
|
def value(self, value):
|
||||||
|
if not -1 <= value <= 1:
|
||||||
|
raise OutputDeviceError("Motor value must be between -1 and 1")
|
||||||
|
if value > 0:
|
||||||
|
self.forward(value)
|
||||||
|
elif value < 0:
|
||||||
|
self.backward(-value)
|
||||||
|
else:
|
||||||
|
self.stop()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_active(self):
|
||||||
|
"""
|
||||||
|
Returns `True` if the motor is currently active and `False` otherwise.
|
||||||
|
"""
|
||||||
|
return self.value != 0
|
||||||
|
|
||||||
def forward(self, speed=1):
|
def forward(self, speed=1):
|
||||||
"""
|
"""
|
||||||
Drive the motor forwards
|
Drive the motor forwards
|
||||||
"""
|
"""
|
||||||
self._backward.off()
|
self._backward.off()
|
||||||
self._forward.on()
|
self._forward.value = speed
|
||||||
if speed < 1:
|
|
||||||
sleep(0.1) # warm up the motor
|
|
||||||
self._forward.value = speed
|
|
||||||
|
|
||||||
def backward(self, speed=1):
|
def backward(self, speed=1):
|
||||||
"""
|
"""
|
||||||
Drive the motor backwards
|
Drive the motor backwards
|
||||||
"""
|
"""
|
||||||
self._forward.off()
|
self._forward.off()
|
||||||
self._backward.on()
|
self._backward.value = speed
|
||||||
if speed < 1:
|
|
||||||
sleep(0.1) # warm up the motor
|
|
||||||
self._backward.value = speed
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""
|
"""
|
||||||
@@ -409,3 +420,4 @@ class Motor(object):
|
|||||||
"""
|
"""
|
||||||
self._forward.off()
|
self._forward.off()
|
||||||
self._backward.off()
|
self._backward.off()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user