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:
Dave Jones
2016-04-01 16:20:54 +01:00
parent a7b7fc8dec
commit d6af02933a
5 changed files with 187 additions and 77 deletions

View File

@@ -11,7 +11,7 @@ from threading import Lock
from itertools import repeat, cycle, chain
from .exc import OutputDeviceBadValue, GPIOPinMissing
from .devices import GPIODevice, CompositeDevice, SourceMixin
from .devices import GPIODevice, Device, CompositeDevice, SourceMixin
from .threads import GPIOThread
@@ -121,6 +121,16 @@ class DigitalOutputDevice(OutputDevice):
self._blink_thread = None
super(DigitalOutputDevice, self).__init__(pin, active_high, initial_value)
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):
self._stop_blink()
@@ -174,6 +184,9 @@ class DigitalOutputDevice(OutputDevice):
self._blink_thread = None
def _stop_blink(self):
if self._controller:
self._controller._stop_blink(self)
self._controller = None
if self._blink_thread:
self._blink_thread.stop()
self._blink_thread = None
@@ -286,6 +299,7 @@ class PWMOutputDevice(OutputDevice):
"""
def __init__(self, pin=None, active_high=True, initial_value=0, frequency=100):
self._blink_thread = None
self._controller = None
if not 0 <= initial_value <= 1:
raise OutputDeviceBadValue("initial_value must be between 0 and 1")
super(PWMOutputDevice, self).__init__(pin, active_high)
@@ -437,6 +451,9 @@ class PWMOutputDevice(OutputDevice):
)
def _stop_blink(self):
if self._controller:
self._controller._stop_blink(self)
self._controller = None
if self._blink_thread:
self._blink_thread.stop()
self._blink_thread = None
@@ -508,10 +525,10 @@ def _led_property(index, doc=None):
return property(getter, setter, doc=doc)
class RGBLED(SourceMixin, CompositeDevice):
class RGBLED(SourceMixin, Device):
"""
Extends :class:`CompositeDevice` and represents a full color LED component
(composed of red, green, and blue LEDs).
Extends :class:`Device` and represents a full color LED component (composed
of red, green, and blue LEDs).
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
@@ -556,6 +573,18 @@ class RGBLED(SourceMixin, CompositeDevice):
green = _led_property(1)
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
def value(self):
"""
@@ -569,6 +598,7 @@ class RGBLED(SourceMixin, CompositeDevice):
@value.setter
def value(self, value):
self._stop_blink()
self.red, self.green, self.blue = value
@property
@@ -606,11 +636,6 @@ class RGBLED(SourceMixin, CompositeDevice):
r, g, b = self.value
self.value = (1 - r, 1 - g, 1 - b)
def close(self):
self._stop_blink()
for led in self._leds:
led.close()
def blink(
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):
@@ -654,7 +679,8 @@ class RGBLED(SourceMixin, CompositeDevice):
self._blink_thread.join()
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:
self._blink_thread.stop()
self._blink_thread = None
@@ -687,16 +713,19 @@ class RGBLED(SourceMixin, CompositeDevice):
cycle(sequence) if n is None else
chain.from_iterable(repeat(sequence, n))
)
for l in self._leds:
l._controller = self
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):
break
class Motor(SourceMixin, CompositeDevice):
"""
Extends :class:`CompositeDevice` and represents a generic motor connected
to a bi-directional motor driver circuit (i.e. an `H-bridge`_).
Extends :class:`CompositeDevice` and represents a generic motor
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
(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(
'forward and backward pins must be provided'
)
super(Motor, self).__init__()
self._forward = PWMOutputDevice(forward)
self._backward = 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
super(Motor, self).__init__(
forward_device=PWMOutputDevice(forward),
backward_device=PWMOutputDevice(backward))
@property
def value(self):
@@ -759,7 +764,7 @@ class Motor(SourceMixin, CompositeDevice):
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
return self.forward_device.value - self.backward_device.value
@value.setter
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
0 (stopped) and the default 1 (maximum speed).
"""
self._backward.off()
self._forward.value = speed
self.backward_device.off()
self.forward_device.value = speed
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
0 (stopped) and the default 1 (maximum speed).
"""
self._forward.off()
self._backward.value = speed
self.forward_device.off()
self.backward_device.value = speed
def reverse(self):
"""
@@ -814,5 +819,5 @@ class Motor(SourceMixin, CompositeDevice):
"""
Stop the motor.
"""
self._forward.off()
self._backward.off()
self.forward_device.off()
self.backward_device.off()