mirror of
https://github.com/KevinMidboe/python-gpiozero.git
synced 2025-10-29 17:50:37 +00:00
This PR implements SnowPi, adds the ability for LEDBoard's to own other LEDBoards as well as LEDs, and enhances blink so that manually controlling a LED automatically stops it from blinking (no matter whether it's blinking itself or a LEDBoard is blinking it). It also fixes up RGBLED and Motor which I managed to break with the last PR ...
This commit is contained in:
@@ -95,6 +95,7 @@ from .boards import (
|
|||||||
PiLiterBarGraph,
|
PiLiterBarGraph,
|
||||||
TrafficLights,
|
TrafficLights,
|
||||||
PiTraffic,
|
PiTraffic,
|
||||||
|
SnowPi,
|
||||||
TrafficLightsBuzzer,
|
TrafficLightsBuzzer,
|
||||||
FishDish,
|
FishDish,
|
||||||
TrafficHat,
|
TrafficHat,
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class CompositeOutputDevice(SourceMixin, CompositeDevice):
|
|||||||
Turn all the output devices on.
|
Turn all the output devices on.
|
||||||
"""
|
"""
|
||||||
for device in self.all:
|
for device in self.all:
|
||||||
if isinstance(device, OutputDevice):
|
if isinstance(device, (OutputDevice, CompositeOutputDevice)):
|
||||||
device.on()
|
device.on()
|
||||||
|
|
||||||
def off(self):
|
def off(self):
|
||||||
@@ -45,7 +45,7 @@ class CompositeOutputDevice(SourceMixin, CompositeDevice):
|
|||||||
Turn all the output devices off.
|
Turn all the output devices off.
|
||||||
"""
|
"""
|
||||||
for device in self.all:
|
for device in self.all:
|
||||||
if isinstance(device, OutputDevice):
|
if isinstance(device, (OutputDevice, CompositeOutputDevice)):
|
||||||
device.off()
|
device.off()
|
||||||
|
|
||||||
def toggle(self):
|
def toggle(self):
|
||||||
@@ -54,21 +54,21 @@ class CompositeOutputDevice(SourceMixin, CompositeDevice):
|
|||||||
off; if it's off, turn it on.
|
off; if it's off, turn it on.
|
||||||
"""
|
"""
|
||||||
for device in self.all:
|
for device in self.all:
|
||||||
if isinstance(device, OutputDevice):
|
if isinstance(device, (OutputDevice, CompositeOutputDevice)):
|
||||||
device.toggle()
|
device.toggle()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def value(self):
|
def value(self):
|
||||||
"""
|
"""
|
||||||
A tuple containing a value for each subordinate device. This property
|
A tuple containing a value for each subordinate device. This property
|
||||||
can also be set to update the state of all output subordinate devices.
|
can also be set to update the state of all subordinate output devices.
|
||||||
"""
|
"""
|
||||||
return super(CompositeOutputDevice, self).value
|
return super(CompositeOutputDevice, self).value
|
||||||
|
|
||||||
@value.setter
|
@value.setter
|
||||||
def value(self, value):
|
def value(self, value):
|
||||||
for device, v in zip(self.all, value):
|
for device, v in zip(self.all, value):
|
||||||
if isinstance(device, OutputDevice):
|
if isinstance(device, (OutputDevice, CompositeOutputDevice)):
|
||||||
device.value = v
|
device.value = v
|
||||||
# Simply ignore values for non-output devices
|
# Simply ignore values for non-output devices
|
||||||
|
|
||||||
@@ -87,17 +87,32 @@ class LEDCollection(CompositeOutputDevice):
|
|||||||
order = kwargs.pop('_order', None)
|
order = kwargs.pop('_order', None)
|
||||||
LEDClass = PWMLED if pwm else LED
|
LEDClass = PWMLED if pwm else LED
|
||||||
super(LEDCollection, self).__init__(
|
super(LEDCollection, self).__init__(
|
||||||
*(LEDClass(pin, active_high, initial_value) for pin in args),
|
*(
|
||||||
|
pin_or_collection
|
||||||
|
if isinstance(pin_or_collection, LEDCollection) else
|
||||||
|
LEDClass(pin_or_collection, active_high, initial_value)
|
||||||
|
for pin_or_collection in args
|
||||||
|
),
|
||||||
_order=order,
|
_order=order,
|
||||||
**{name: LEDClass(pin, active_high, initial_value) for name, pin in kwargs.items()})
|
**{
|
||||||
|
name: pin_or_collection
|
||||||
|
if isinstance(pin_or_collection, LEDCollection) else
|
||||||
|
LEDClass(pin_or_collection, active_high, initial_value)
|
||||||
|
for name, pin_or_collection in kwargs.items()
|
||||||
|
})
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def leds(self):
|
def leds(self):
|
||||||
"""
|
"""
|
||||||
A tuple of all the :class:`LED` or :class:`PWMLED` objects contained by
|
A flat iterator over all LEDs contained in this collection (and all
|
||||||
the instance.
|
sub-collections).
|
||||||
"""
|
"""
|
||||||
return self.all
|
for item in self:
|
||||||
|
if isinstance(item, LEDCollection):
|
||||||
|
for subitem in item.leds:
|
||||||
|
yield subitem
|
||||||
|
else:
|
||||||
|
yield item
|
||||||
|
|
||||||
|
|
||||||
class LEDBoard(LEDCollection):
|
class LEDBoard(LEDCollection):
|
||||||
@@ -115,7 +130,8 @@ class LEDBoard(LEDCollection):
|
|||||||
|
|
||||||
:param int \*pins:
|
:param int \*pins:
|
||||||
Specify the GPIO pins that the LEDs of the board are attached to. You
|
Specify the GPIO pins that the LEDs of the board are attached to. You
|
||||||
can designate as many pins as necessary.
|
can designate as many pins as necessary. You can also specify
|
||||||
|
:class:`LEDBoard` instances to create trees of LEDs.
|
||||||
|
|
||||||
:param bool pwm:
|
:param bool pwm:
|
||||||
If ``True``, construct :class:`PWMLED` instances for each pin. If
|
If ``True``, construct :class:`PWMLED` instances for each pin. If
|
||||||
@@ -132,7 +148,18 @@ class LEDBoard(LEDCollection):
|
|||||||
``None``, each device will be left in whatever state the pin is found
|
``None``, each device will be left in whatever state the pin is found
|
||||||
in when configured for output (warning: this can be on). The ``True``,
|
in when configured for output (warning: this can be on). The ``True``,
|
||||||
the device will be switched on initially.
|
the device will be switched on initially.
|
||||||
|
|
||||||
|
:param \*\*named_pins:
|
||||||
|
Sepcify GPIO pins that LEDs of the board are attached to, associated
|
||||||
|
each LED with a property name. You can designate as many pins as
|
||||||
|
necessary and any name provided it's not already in use by something
|
||||||
|
else. You can also specify :class:`LEDBoard` instances to create
|
||||||
|
trees of LEDs.
|
||||||
"""
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self._blink_leds = []
|
||||||
|
self._blink_lock = Lock()
|
||||||
|
super(LEDBoard, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self._stop_blink()
|
self._stop_blink()
|
||||||
@@ -181,11 +208,12 @@ class LEDBoard(LEDCollection):
|
|||||||
finished (warning: the default value of *n* will result in this
|
finished (warning: the default value of *n* will result in this
|
||||||
method never returning).
|
method never returning).
|
||||||
"""
|
"""
|
||||||
if isinstance(self.leds[0], LED):
|
for led in self.leds:
|
||||||
if fade_in_time:
|
if isinstance(led, LED):
|
||||||
raise ValueError('fade_in_time must be 0 with non-PWM LEDs')
|
if fade_in_time:
|
||||||
if fade_out_time:
|
raise ValueError('fade_in_time must be 0 with non-PWM LEDs')
|
||||||
raise ValueError('fade_out_time must be 0 with non-PWM LEDs')
|
if fade_out_time:
|
||||||
|
raise ValueError('fade_out_time must be 0 with non-PWM LEDs')
|
||||||
self._stop_blink()
|
self._stop_blink()
|
||||||
self._blink_thread = GPIOThread(
|
self._blink_thread = GPIOThread(
|
||||||
target=self._blink_device,
|
target=self._blink_device,
|
||||||
@@ -196,10 +224,14 @@ class LEDBoard(LEDCollection):
|
|||||||
self._blink_thread.join()
|
self._blink_thread.join()
|
||||||
self._blink_thread = None
|
self._blink_thread = None
|
||||||
|
|
||||||
def _stop_blink(self):
|
def _stop_blink(self, led=None):
|
||||||
if self._blink_thread:
|
if led is None:
|
||||||
self._blink_thread.stop()
|
if self._blink_thread:
|
||||||
self._blink_thread = None
|
self._blink_thread.stop()
|
||||||
|
self._blink_thread = None
|
||||||
|
else:
|
||||||
|
with self._blink_lock:
|
||||||
|
self._blink_leds.remove(led)
|
||||||
|
|
||||||
def pulse(self, fade_in_time=1, fade_out_time=1, n=None, background=True):
|
def pulse(self, fade_in_time=1, fade_out_time=1, n=None, background=True):
|
||||||
"""
|
"""
|
||||||
@@ -243,9 +275,18 @@ class LEDBoard(LEDCollection):
|
|||||||
cycle(sequence) if n is None else
|
cycle(sequence) if n is None else
|
||||||
chain.from_iterable(repeat(sequence, n))
|
chain.from_iterable(repeat(sequence, n))
|
||||||
)
|
)
|
||||||
|
with self._blink_lock:
|
||||||
|
self._blink_leds = list(self.leds)
|
||||||
|
for led in self._blink_leds:
|
||||||
|
if led._controller not in (None, self):
|
||||||
|
led._controller._stop_blink(led)
|
||||||
|
led._controller = self
|
||||||
for value, delay in sequence:
|
for value, delay in sequence:
|
||||||
for led in self.leds:
|
with self._blink_lock:
|
||||||
led.value = value
|
if not self._blink_leds:
|
||||||
|
break
|
||||||
|
for led in self._blink_leds:
|
||||||
|
led._write(value)
|
||||||
if self._blink_thread.stopping.wait(delay):
|
if self._blink_thread.stopping.wait(delay):
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -432,7 +473,7 @@ class TrafficLights(LEDBoard):
|
|||||||
|
|
||||||
class PiTraffic(TrafficLights):
|
class PiTraffic(TrafficLights):
|
||||||
"""
|
"""
|
||||||
Extends :class:`TrafficLights` for the Low Voltage Labs PI-TRAFFIC:
|
Extends :class:`TrafficLights` for the `Low Voltage Labs PI-TRAFFIC`_:
|
||||||
vertical traffic lights board when attached to GPIO pins 9, 10, and 11.
|
vertical traffic lights board when attached to GPIO pins 9, 10, and 11.
|
||||||
|
|
||||||
There's no need to specify the pins if the PI-TRAFFIC is connected to the
|
There's no need to specify the pins if the PI-TRAFFIC is connected to the
|
||||||
@@ -446,12 +487,54 @@ class PiTraffic(TrafficLights):
|
|||||||
|
|
||||||
To use the PI-TRAFFIC board when attached to a non-standard set of pins,
|
To use the PI-TRAFFIC board when attached to a non-standard set of pins,
|
||||||
simply use the parent class, :class:`TrafficLights`.
|
simply use the parent class, :class:`TrafficLights`.
|
||||||
|
|
||||||
|
.. _Low Voltage Labs PI-TRAFFIC: http://lowvoltagelabs.com/products/pi-traffic/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(PiTraffic, self).__init__(9, 10, 11)
|
super(PiTraffic, self).__init__(9, 10, 11)
|
||||||
|
|
||||||
|
|
||||||
|
class SnowPi(LEDBoard):
|
||||||
|
"""
|
||||||
|
Extends :class:`LEDBoard` for the `Ryanteck SnowPi`_ board.
|
||||||
|
|
||||||
|
The SnowPi pins are fixed and therefore there's no need to specify them
|
||||||
|
when constructing this class. The following example turns on the eyes, sets
|
||||||
|
the nose pulsing, and the arms blinking::
|
||||||
|
|
||||||
|
from gpiozero import SnowPi
|
||||||
|
|
||||||
|
snowman = SnowPi(pwm=True)
|
||||||
|
snowman.eyes.on()
|
||||||
|
snowman.nose.pulse()
|
||||||
|
snowman.arms.blink()
|
||||||
|
|
||||||
|
:param bool pwm:
|
||||||
|
If ``True``, construct :class:`PWMLED` instances to represent each
|
||||||
|
LED. If ``False`` (the default), construct regular :class:`LED`
|
||||||
|
instances.
|
||||||
|
|
||||||
|
.. _Ryanteck SnowPi: https://ryanteck.uk/raspberry-pi/114-snowpi-the-gpio-snowman-for-raspberry-pi-0635648608303.html
|
||||||
|
"""
|
||||||
|
def __init__(self, pwm=False):
|
||||||
|
super(SnowPi, self).__init__(
|
||||||
|
arms=LEDBoard(
|
||||||
|
left=LEDBoard(
|
||||||
|
top=17, middle=18, bottom=22, pwm=pwm,
|
||||||
|
_order=('top', 'middle', 'bottom')),
|
||||||
|
right=LEDBoard(
|
||||||
|
top=7, middle=8, bottom=9, pwm=pwm,
|
||||||
|
_order=('top', 'middle', 'bottom'))
|
||||||
|
),
|
||||||
|
eyes=LEDBoard(
|
||||||
|
left=23, right=24, pwm=pwm,
|
||||||
|
_order=('left', 'right')
|
||||||
|
),
|
||||||
|
nose=25, pwm=pwm
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TrafficLightsBuzzer(CompositeOutputDevice):
|
class TrafficLightsBuzzer(CompositeOutputDevice):
|
||||||
"""
|
"""
|
||||||
Extends :class:`CompositeDevice` and is a generic class for HATs with
|
Extends :class:`CompositeDevice` and is a generic class for HATs with
|
||||||
|
|||||||
@@ -356,7 +356,7 @@ class CompositeDevice(Device):
|
|||||||
raise CompositeDeviceBadName('%s is a reserved name' % name)
|
raise CompositeDeviceBadName('%s is a reserved name' % name)
|
||||||
self._all = args + tuple(kwargs[v] for v in self._order)
|
self._all = args + tuple(kwargs[v] for v in self._order)
|
||||||
self._named = kwargs
|
self._named = kwargs
|
||||||
self._tuple = namedtuple('CompositeDeviceValue', chain(
|
self._tuple = namedtuple('%sValue' % self.__class__.__name__, chain(
|
||||||
(str(i) for i in range(len(args))), self._order),
|
(str(i) for i in range(len(args))), self._order),
|
||||||
rename=True)
|
rename=True)
|
||||||
|
|
||||||
@@ -375,18 +375,39 @@ class CompositeDevice(Device):
|
|||||||
raise AttributeError("can't set attribute %s" % name)
|
raise AttributeError("can't set attribute %s" % name)
|
||||||
return super(CompositeDevice, self).__setattr__(name, value)
|
return super(CompositeDevice, self).__setattr__(name, value)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
try:
|
||||||
|
self._check_open()
|
||||||
|
return "<gpiozero.%s object containing %d devices: %s and %d unnamed>" % (
|
||||||
|
self.__class__.__name__,
|
||||||
|
len(self), ','.join(self._named),
|
||||||
|
len(self) - len(self._named)
|
||||||
|
)
|
||||||
|
except DeviceClosed:
|
||||||
|
return "<gpiozero.%s object closed>"
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._all)
|
||||||
|
|
||||||
|
def __getitem__(self, index):
|
||||||
|
return self._all[index]
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self._all)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def all(self):
|
def all(self):
|
||||||
|
# XXX Deprecate this in favour of using the instance as a container
|
||||||
return self._all
|
return self._all
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
for device in self._all:
|
if self._all:
|
||||||
device.close()
|
for device in self._all:
|
||||||
self._all = ()
|
device.close()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def closed(self):
|
def closed(self):
|
||||||
return bool(self._all)
|
return all(device.closed for device in self)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tuple(self):
|
def tuple(self):
|
||||||
@@ -394,7 +415,7 @@ class CompositeDevice(Device):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def value(self):
|
def value(self):
|
||||||
return self.tuple(*(device.value for device in self._all))
|
return self.tuple(*(device.value for device in self))
|
||||||
|
|
||||||
|
|
||||||
class GPIODevice(Device):
|
class GPIODevice(Device):
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class SPIBadArgs(SPIError, ValueError):
|
|||||||
class GPIODeviceError(GPIOZeroError):
|
class GPIODeviceError(GPIOZeroError):
|
||||||
"Base class for errors specific to the GPIODevice hierarchy"
|
"Base class for errors specific to the GPIODevice hierarchy"
|
||||||
|
|
||||||
class GPIODeviceClosed(GPIODeviceError):
|
class GPIODeviceClosed(GPIODeviceError, DeviceClosed):
|
||||||
"Deprecated descendent of :exc:`DeviceClosed`"
|
"Deprecated descendent of :exc:`DeviceClosed`"
|
||||||
|
|
||||||
class GPIOPinInUse(GPIODeviceError):
|
class GPIOPinInUse(GPIODeviceError):
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from threading import Lock
|
|||||||
from itertools import repeat, cycle, chain
|
from itertools import repeat, cycle, chain
|
||||||
|
|
||||||
from .exc import OutputDeviceBadValue, GPIOPinMissing
|
from .exc import OutputDeviceBadValue, GPIOPinMissing
|
||||||
from .devices import GPIODevice, CompositeDevice, SourceMixin
|
from .devices import GPIODevice, Device, CompositeDevice, SourceMixin
|
||||||
from .threads import GPIOThread
|
from .threads import GPIOThread
|
||||||
|
|
||||||
|
|
||||||
@@ -121,6 +121,16 @@ class DigitalOutputDevice(OutputDevice):
|
|||||||
self._blink_thread = None
|
self._blink_thread = None
|
||||||
super(DigitalOutputDevice, self).__init__(pin, active_high, initial_value)
|
super(DigitalOutputDevice, self).__init__(pin, active_high, initial_value)
|
||||||
self._lock = Lock()
|
self._lock = Lock()
|
||||||
|
self._controller = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
return self._read()
|
||||||
|
|
||||||
|
@value.setter
|
||||||
|
def value(self, value):
|
||||||
|
self._stop_blink()
|
||||||
|
self._write(value)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self._stop_blink()
|
self._stop_blink()
|
||||||
@@ -174,6 +184,9 @@ class DigitalOutputDevice(OutputDevice):
|
|||||||
self._blink_thread = None
|
self._blink_thread = None
|
||||||
|
|
||||||
def _stop_blink(self):
|
def _stop_blink(self):
|
||||||
|
if self._controller:
|
||||||
|
self._controller._stop_blink(self)
|
||||||
|
self._controller = None
|
||||||
if self._blink_thread:
|
if self._blink_thread:
|
||||||
self._blink_thread.stop()
|
self._blink_thread.stop()
|
||||||
self._blink_thread = None
|
self._blink_thread = None
|
||||||
@@ -286,6 +299,7 @@ class PWMOutputDevice(OutputDevice):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, pin=None, active_high=True, initial_value=0, frequency=100):
|
def __init__(self, pin=None, active_high=True, initial_value=0, frequency=100):
|
||||||
self._blink_thread = None
|
self._blink_thread = None
|
||||||
|
self._controller = None
|
||||||
if not 0 <= initial_value <= 1:
|
if not 0 <= initial_value <= 1:
|
||||||
raise OutputDeviceBadValue("initial_value must be between 0 and 1")
|
raise OutputDeviceBadValue("initial_value must be between 0 and 1")
|
||||||
super(PWMOutputDevice, self).__init__(pin, active_high)
|
super(PWMOutputDevice, self).__init__(pin, active_high)
|
||||||
@@ -437,6 +451,9 @@ class PWMOutputDevice(OutputDevice):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _stop_blink(self):
|
def _stop_blink(self):
|
||||||
|
if self._controller:
|
||||||
|
self._controller._stop_blink(self)
|
||||||
|
self._controller = None
|
||||||
if self._blink_thread:
|
if self._blink_thread:
|
||||||
self._blink_thread.stop()
|
self._blink_thread.stop()
|
||||||
self._blink_thread = None
|
self._blink_thread = None
|
||||||
@@ -508,10 +525,10 @@ def _led_property(index, doc=None):
|
|||||||
return property(getter, setter, doc=doc)
|
return property(getter, setter, doc=doc)
|
||||||
|
|
||||||
|
|
||||||
class RGBLED(SourceMixin, CompositeDevice):
|
class RGBLED(SourceMixin, Device):
|
||||||
"""
|
"""
|
||||||
Extends :class:`CompositeDevice` and represents a full color LED component
|
Extends :class:`Device` and represents a full color LED component (composed
|
||||||
(composed of red, green, and blue LEDs).
|
of red, green, and blue LEDs).
|
||||||
|
|
||||||
Connect the common cathode (longest leg) to a ground pin; connect each of
|
Connect the common cathode (longest leg) to a ground pin; connect each of
|
||||||
the other legs (representing the red, green, and blue anodes) to any GPIO
|
the other legs (representing the red, green, and blue anodes) to any GPIO
|
||||||
@@ -556,6 +573,18 @@ class RGBLED(SourceMixin, CompositeDevice):
|
|||||||
green = _led_property(1)
|
green = _led_property(1)
|
||||||
blue = _led_property(2)
|
blue = _led_property(2)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self._leds:
|
||||||
|
self._stop_blink()
|
||||||
|
for led in self._leds:
|
||||||
|
led.close()
|
||||||
|
self._leds = ()
|
||||||
|
super(RGBLED, self).close()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def closed(self):
|
||||||
|
return bool(self._leds)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def value(self):
|
def value(self):
|
||||||
"""
|
"""
|
||||||
@@ -569,6 +598,7 @@ class RGBLED(SourceMixin, CompositeDevice):
|
|||||||
|
|
||||||
@value.setter
|
@value.setter
|
||||||
def value(self, value):
|
def value(self, value):
|
||||||
|
self._stop_blink()
|
||||||
self.red, self.green, self.blue = value
|
self.red, self.green, self.blue = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -606,11 +636,6 @@ class RGBLED(SourceMixin, CompositeDevice):
|
|||||||
r, g, b = self.value
|
r, g, b = self.value
|
||||||
self.value = (1 - r, 1 - g, 1 - b)
|
self.value = (1 - r, 1 - g, 1 - b)
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self._stop_blink()
|
|
||||||
for led in self._leds:
|
|
||||||
led.close()
|
|
||||||
|
|
||||||
def blink(
|
def blink(
|
||||||
self, on_time=1, off_time=1, fade_in_time=0, fade_out_time=0,
|
self, on_time=1, off_time=1, fade_in_time=0, fade_out_time=0,
|
||||||
on_color=(1, 1, 1), off_color=(0, 0, 0), n=None, background=True):
|
on_color=(1, 1, 1), off_color=(0, 0, 0), n=None, background=True):
|
||||||
@@ -654,7 +679,8 @@ class RGBLED(SourceMixin, CompositeDevice):
|
|||||||
self._blink_thread.join()
|
self._blink_thread.join()
|
||||||
self._blink_thread = None
|
self._blink_thread = None
|
||||||
|
|
||||||
def _stop_blink(self):
|
def _stop_blink(self, led=None):
|
||||||
|
# If this is called with a single led, we stop all blinking anyway
|
||||||
if self._blink_thread:
|
if self._blink_thread:
|
||||||
self._blink_thread.stop()
|
self._blink_thread.stop()
|
||||||
self._blink_thread = None
|
self._blink_thread = None
|
||||||
@@ -687,16 +713,19 @@ class RGBLED(SourceMixin, CompositeDevice):
|
|||||||
cycle(sequence) if n is None else
|
cycle(sequence) if n is None else
|
||||||
chain.from_iterable(repeat(sequence, n))
|
chain.from_iterable(repeat(sequence, n))
|
||||||
)
|
)
|
||||||
|
for l in self._leds:
|
||||||
|
l._controller = self
|
||||||
for value, delay in sequence:
|
for value, delay in sequence:
|
||||||
self._leds[0].value, self._leds[1].value, self._leds[2].value = value
|
for l, v in zip(self._leds, value):
|
||||||
|
l._write(v)
|
||||||
if self._blink_thread.stopping.wait(delay):
|
if self._blink_thread.stopping.wait(delay):
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
class Motor(SourceMixin, CompositeDevice):
|
class Motor(SourceMixin, CompositeDevice):
|
||||||
"""
|
"""
|
||||||
Extends :class:`CompositeDevice` and represents a generic motor connected
|
Extends :class:`CompositeDevice` and represents a generic motor
|
||||||
to a bi-directional motor driver circuit (i.e. an `H-bridge`_).
|
connected to a bi-directional motor driver circuit (i.e. an `H-bridge`_).
|
||||||
|
|
||||||
Attach an `H-bridge`_ motor controller to your Pi; connect a power source
|
Attach an `H-bridge`_ motor controller to your Pi; connect a power source
|
||||||
(e.g. a battery pack or the 5V pin) to the controller; connect the outputs
|
(e.g. a battery pack or the 5V pin) to the controller; connect the outputs
|
||||||
@@ -725,33 +754,9 @@ class Motor(SourceMixin, CompositeDevice):
|
|||||||
raise GPIOPinMissing(
|
raise GPIOPinMissing(
|
||||||
'forward and backward pins must be provided'
|
'forward and backward pins must be provided'
|
||||||
)
|
)
|
||||||
super(Motor, self).__init__()
|
super(Motor, self).__init__(
|
||||||
self._forward = PWMOutputDevice(forward)
|
forward_device=PWMOutputDevice(forward),
|
||||||
self._backward = PWMOutputDevice(backward)
|
backward_device=PWMOutputDevice(backward))
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self._forward.close()
|
|
||||||
self._backward.close()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def closed(self):
|
|
||||||
return self._forward.closed and self._backward.closed
|
|
||||||
|
|
||||||
@property
|
|
||||||
def forward_device(self):
|
|
||||||
"""
|
|
||||||
Returns the `PWMOutputDevice` representing the forward pin of the motor
|
|
||||||
controller.
|
|
||||||
"""
|
|
||||||
return self._forward
|
|
||||||
|
|
||||||
@property
|
|
||||||
def backward_device(self):
|
|
||||||
"""
|
|
||||||
Returns the `PWMOutputDevice` representing the backward pin of the
|
|
||||||
motor controller.
|
|
||||||
"""
|
|
||||||
return self._backward
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def value(self):
|
def value(self):
|
||||||
@@ -759,7 +764,7 @@ class Motor(SourceMixin, CompositeDevice):
|
|||||||
Represents the speed of the motor as a floating point value between -1
|
Represents the speed of the motor as a floating point value between -1
|
||||||
(full speed backward) and 1 (full speed forward).
|
(full speed backward) and 1 (full speed forward).
|
||||||
"""
|
"""
|
||||||
return self._forward.value - self._backward.value
|
return self.forward_device.value - self.backward_device.value
|
||||||
|
|
||||||
@value.setter
|
@value.setter
|
||||||
def value(self, value):
|
def value(self, value):
|
||||||
@@ -788,8 +793,8 @@ class Motor(SourceMixin, CompositeDevice):
|
|||||||
The speed at which the motor should turn. Can be any value between
|
The speed at which the motor should turn. Can be any value between
|
||||||
0 (stopped) and the default 1 (maximum speed).
|
0 (stopped) and the default 1 (maximum speed).
|
||||||
"""
|
"""
|
||||||
self._backward.off()
|
self.backward_device.off()
|
||||||
self._forward.value = speed
|
self.forward_device.value = speed
|
||||||
|
|
||||||
def backward(self, speed=1):
|
def backward(self, speed=1):
|
||||||
"""
|
"""
|
||||||
@@ -799,8 +804,8 @@ class Motor(SourceMixin, CompositeDevice):
|
|||||||
The speed at which the motor should turn. Can be any value between
|
The speed at which the motor should turn. Can be any value between
|
||||||
0 (stopped) and the default 1 (maximum speed).
|
0 (stopped) and the default 1 (maximum speed).
|
||||||
"""
|
"""
|
||||||
self._forward.off()
|
self.forward_device.off()
|
||||||
self._backward.value = speed
|
self.backward_device.value = speed
|
||||||
|
|
||||||
def reverse(self):
|
def reverse(self):
|
||||||
"""
|
"""
|
||||||
@@ -814,5 +819,5 @@ class Motor(SourceMixin, CompositeDevice):
|
|||||||
"""
|
"""
|
||||||
Stop the motor.
|
Stop the motor.
|
||||||
"""
|
"""
|
||||||
self._forward.off()
|
self.forward_device.off()
|
||||||
self._backward.off()
|
self.backward_device.off()
|
||||||
|
|||||||
Reference in New Issue
Block a user