Add a pwm option to the RGBLED and Motor constructors

...along with the other necessary changes required, to allow them to
optionally be used with non-PWM-capable pins
This commit is contained in:
Andrew Scheller
2016-04-29 12:06:29 +01:00
parent 1b7dad5fa4
commit c9461c50d3
7 changed files with 378 additions and 141 deletions

View File

@@ -28,7 +28,7 @@ PWMLED
RGBLED
======
.. autoclass:: RGBLED(red, green, blue, active_high=True, initial_value=(0, 0, 0))
.. autoclass:: RGBLED(red, green, blue, active_high=True, initial_value=(0, 0, 0), pwm=True)
:members: on, off, toggle, blink, pulse, red, green, blue, is_lit, color
Buzzer
@@ -40,7 +40,7 @@ Buzzer
Motor
=====
.. autoclass:: Motor(forward, backward)
.. autoclass:: Motor(forward, backward, pwm=True)
:members: forward, backward, stop
Base Classes

View File

@@ -5,6 +5,7 @@ digraph classes {
node [shape=rect, style=filled, color="#298029", fontname=Sans, fontcolor="#ffffff", fontsize=10];
edge [arrowhead=onormal, style=dashed];
RGBLED->LED;
RGBLED->PWMLED;
LEDBoard->LED;
LEDBoard->PWMLED;
@@ -16,5 +17,6 @@ digraph classes {
TrafficLightsBuzzer->Button;
Robot->Motor;
Motor->DigitalOutputDevice;
Motor->PWMOutputDevice;
}

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -4,120 +4,135 @@
<!-- Generated by graphviz version 2.36.0 (20140111.2315)
-->
<!-- Title: classes Pages: 1 -->
<svg width="563pt" height="188pt"
viewBox="0.00 0.00 563.00 188.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="575pt" height="188pt"
viewBox="0.00 0.00 575.00 188.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 184)">
<title>classes</title>
<polygon fill="white" stroke="none" points="-4,4 -4,-184 559,-184 559,4 -4,4"/>
<polygon fill="white" stroke="none" points="-4,4 -4,-184 571,-184 571,4 -4,4"/>
<!-- RGBLED -->
<g id="node1" class="node"><title>RGBLED</title>
<polygon fill="#298029" stroke="#298029" points="55.5,-180 0.5,-180 0.5,-144 55.5,-144 55.5,-180"/>
<text text-anchor="middle" x="28" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">RGBLED</text>
</g>
<!-- LED -->
<g id="node2" class="node"><title>LED</title>
<polygon fill="#298029" stroke="#298029" points="78,-108 24,-108 24,-72 78,-72 78,-108"/>
<text text-anchor="middle" x="51" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">LED</text>
</g>
<!-- RGBLED&#45;&gt;LED -->
<g id="edge1" class="edge"><title>RGBLED&#45;&gt;LED</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M33.6854,-143.697C36.2478,-135.898 39.3329,-126.509 42.1847,-117.829"/>
<polygon fill="none" stroke="black" points="45.5835,-118.697 45.38,-108.104 38.9333,-116.512 45.5835,-118.697"/>
</g>
<!-- PWMLED -->
<g id="node2" class="node"><title>PWMLED</title>
<polygon fill="#298029" stroke="#298029" points="143.5,-108 86.5,-108 86.5,-72 143.5,-72 143.5,-108"/>
<text text-anchor="middle" x="115" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">PWMLED</text>
<g id="node3" class="node"><title>PWMLED</title>
<polygon fill="#298029" stroke="#298029" points="153.5,-108 96.5,-108 96.5,-72 153.5,-72 153.5,-108"/>
<text text-anchor="middle" x="125" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">PWMLED</text>
</g>
<!-- RGBLED&#45;&gt;PWMLED -->
<g id="edge1" class="edge"><title>RGBLED&#45;&gt;PWMLED</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M49.5056,-143.697C60.4625,-134.881 73.9459,-124.032 85.8249,-114.474"/>
<polygon fill="none" stroke="black" points="88.1446,-117.1 93.7418,-108.104 83.7565,-111.646 88.1446,-117.1"/>
<g id="edge2" class="edge"><title>RGBLED&#45;&gt;PWMLED</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M51.9775,-143.697C64.3113,-134.796 79.5164,-123.823 92.853,-114.199"/>
<polygon fill="none" stroke="black" points="95.2374,-116.794 101.298,-108.104 91.1411,-111.118 95.2374,-116.794"/>
</g>
<!-- LEDBoard -->
<g id="node3" class="node"><title>LEDBoard</title>
<polygon fill="#298029" stroke="#298029" points="238,-180 174,-180 174,-144 238,-144 238,-180"/>
<text text-anchor="middle" x="206" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">LEDBoard</text>
</g>
<!-- LEDBoard&#45;&gt;PWMLED -->
<g id="edge3" class="edge"><title>LEDBoard&#45;&gt;PWMLED</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M183.506,-143.697C172.045,-134.881 157.942,-124.032 145.516,-114.474"/>
<polygon fill="none" stroke="black" points="147.296,-111.427 137.236,-108.104 143.028,-116.976 147.296,-111.427"/>
</g>
<!-- LED -->
<g id="node4" class="node"><title>LED</title>
<polygon fill="#298029" stroke="#298029" points="224,-108 170,-108 170,-72 224,-72 224,-108"/>
<text text-anchor="middle" x="197" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">LED</text>
<g id="node4" class="node"><title>LEDBoard</title>
<polygon fill="#298029" stroke="#298029" points="138,-180 74,-180 74,-144 138,-144 138,-180"/>
<text text-anchor="middle" x="106" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">LEDBoard</text>
</g>
<!-- LEDBoard&#45;&gt;LED -->
<g id="edge2" class="edge"><title>LEDBoard&#45;&gt;LED</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M203.775,-143.697C202.783,-135.983 201.592,-126.712 200.486,-118.112"/>
<polygon fill="none" stroke="black" points="203.946,-117.576 199.199,-108.104 197.003,-118.469 203.946,-117.576"/>
<g id="edge3" class="edge"><title>LEDBoard&#45;&gt;LED</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M92.4045,-143.697C85.8773,-135.389 77.932,-125.277 70.7534,-116.141"/>
<polygon fill="none" stroke="black" points="73.3695,-113.805 64.4391,-108.104 67.8653,-118.13 73.3695,-113.805"/>
</g>
<!-- LEDBoard&#45;&gt;PWMLED -->
<g id="edge4" class="edge"><title>LEDBoard&#45;&gt;PWMLED</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M110.697,-143.697C112.813,-135.898 115.362,-126.509 117.718,-117.829"/>
<polygon fill="none" stroke="black" points="121.116,-118.672 120.357,-108.104 114.36,-116.838 121.116,-118.672"/>
</g>
<!-- LEDBarGraph -->
<g id="node5" class="node"><title>LEDBarGraph</title>
<polygon fill="#298029" stroke="#298029" points="155.25,-180 74.75,-180 74.75,-144 155.25,-144 155.25,-180"/>
<text text-anchor="middle" x="115" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">LEDBarGraph</text>
</g>
<!-- LEDBarGraph&#45;&gt;PWMLED -->
<g id="edge5" class="edge"><title>LEDBarGraph&#45;&gt;PWMLED</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M115,-143.697C115,-135.983 115,-126.712 115,-118.112"/>
<polygon fill="none" stroke="black" points="118.5,-118.104 115,-108.104 111.5,-118.104 118.5,-118.104"/>
<polygon fill="#298029" stroke="#298029" points="237.25,-180 156.75,-180 156.75,-144 237.25,-144 237.25,-180"/>
<text text-anchor="middle" x="197" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">LEDBarGraph</text>
</g>
<!-- LEDBarGraph&#45;&gt;LED -->
<g id="edge4" class="edge"><title>LEDBarGraph&#45;&gt;LED</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M135.27,-143.697C145.498,-134.965 158.061,-124.24 169.178,-114.75"/>
<polygon fill="none" stroke="black" points="171.63,-117.259 176.963,-108.104 167.085,-111.935 171.63,-117.259"/>
<g id="edge5" class="edge"><title>LEDBarGraph&#45;&gt;LED</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M161.284,-143.876C138.829,-133.11 109.939,-119.258 87.3611,-108.433"/>
<polygon fill="none" stroke="black" points="88.639,-105.165 78.1086,-103.997 85.6126,-111.477 88.639,-105.165"/>
</g>
<!-- LEDBarGraph&#45;&gt;PWMLED -->
<g id="edge6" class="edge"><title>LEDBarGraph&#45;&gt;PWMLED</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M179.202,-143.697C170.396,-135.135 159.618,-124.656 149.999,-115.304"/>
<polygon fill="none" stroke="black" points="152.203,-112.566 142.593,-108.104 147.323,-117.585 152.203,-112.566"/>
</g>
<!-- TrafficLightsBuzzer -->
<g id="node6" class="node"><title>TrafficLightsBuzzer</title>
<polygon fill="#298029" stroke="#298029" points="411.25,-180 306.75,-180 306.75,-144 411.25,-144 411.25,-180"/>
<text text-anchor="middle" x="359" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">TrafficLightsBuzzer</text>
<polygon fill="#298029" stroke="#298029" points="360.25,-180 255.75,-180 255.75,-144 360.25,-144 360.25,-180"/>
<text text-anchor="middle" x="308" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">TrafficLightsBuzzer</text>
</g>
<!-- TrafficLights -->
<g id="node7" class="node"><title>TrafficLights</title>
<polygon fill="#298029" stroke="#298029" points="314,-108 242,-108 242,-72 314,-72 314,-108"/>
<text text-anchor="middle" x="278" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">TrafficLights</text>
<polygon fill="#298029" stroke="#298029" points="263,-108 191,-108 191,-72 263,-72 263,-108"/>
<text text-anchor="middle" x="227" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">TrafficLights</text>
</g>
<!-- TrafficLightsBuzzer&#45;&gt;TrafficLights -->
<g id="edge6" class="edge"><title>TrafficLightsBuzzer&#45;&gt;TrafficLights</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M338.978,-143.697C328.874,-134.965 316.464,-124.24 305.482,-114.75"/>
<polygon fill="none" stroke="black" points="307.647,-111.995 297.792,-108.104 303.07,-117.291 307.647,-111.995"/>
<g id="edge7" class="edge"><title>TrafficLightsBuzzer&#45;&gt;TrafficLights</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M287.978,-143.697C277.874,-134.965 265.464,-124.24 254.482,-114.75"/>
<polygon fill="none" stroke="black" points="256.647,-111.995 246.792,-108.104 252.07,-117.291 256.647,-111.995"/>
</g>
<!-- Buzzer -->
<g id="node8" class="node"><title>Buzzer</title>
<polygon fill="#298029" stroke="#298029" points="386,-108 332,-108 332,-72 386,-72 386,-108"/>
<text text-anchor="middle" x="359" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">Buzzer</text>
<polygon fill="#298029" stroke="#298029" points="335,-108 281,-108 281,-72 335,-72 335,-108"/>
<text text-anchor="middle" x="308" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">Buzzer</text>
</g>
<!-- TrafficLightsBuzzer&#45;&gt;Buzzer -->
<g id="edge7" class="edge"><title>TrafficLightsBuzzer&#45;&gt;Buzzer</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M359,-143.697C359,-135.983 359,-126.712 359,-118.112"/>
<polygon fill="none" stroke="black" points="362.5,-118.104 359,-108.104 355.5,-118.104 362.5,-118.104"/>
<g id="edge8" class="edge"><title>TrafficLightsBuzzer&#45;&gt;Buzzer</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M308,-143.697C308,-135.983 308,-126.712 308,-118.112"/>
<polygon fill="none" stroke="black" points="311.5,-118.104 308,-108.104 304.5,-118.104 311.5,-118.104"/>
</g>
<!-- Button -->
<g id="node9" class="node"><title>Button</title>
<polygon fill="#298029" stroke="#298029" points="458,-108 404,-108 404,-72 458,-72 458,-108"/>
<text text-anchor="middle" x="431" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">Button</text>
<polygon fill="#298029" stroke="#298029" points="407,-108 353,-108 353,-72 407,-72 407,-108"/>
<text text-anchor="middle" x="380" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">Button</text>
</g>
<!-- TrafficLightsBuzzer&#45;&gt;Button -->
<g id="edge8" class="edge"><title>TrafficLightsBuzzer&#45;&gt;Button</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M376.798,-143.697C385.604,-135.135 396.382,-124.656 406.001,-115.304"/>
<polygon fill="none" stroke="black" points="408.677,-117.585 413.407,-108.104 403.797,-112.566 408.677,-117.585"/>
<g id="edge9" class="edge"><title>TrafficLightsBuzzer&#45;&gt;Button</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M325.798,-143.697C334.604,-135.135 345.382,-124.656 355.001,-115.304"/>
<polygon fill="none" stroke="black" points="357.677,-117.585 362.407,-108.104 352.797,-112.566 357.677,-117.585"/>
</g>
<!-- Robot -->
<g id="node10" class="node"><title>Robot</title>
<polygon fill="#298029" stroke="#298029" points="530,-180 476,-180 476,-144 530,-144 530,-180"/>
<text text-anchor="middle" x="503" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">Robot</text>
<polygon fill="#298029" stroke="#298029" points="479,-180 425,-180 425,-144 479,-144 479,-180"/>
<text text-anchor="middle" x="452" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">Robot</text>
</g>
<!-- Motor -->
<g id="node11" class="node"><title>Motor</title>
<polygon fill="#298029" stroke="#298029" points="530,-108 476,-108 476,-72 530,-72 530,-108"/>
<text text-anchor="middle" x="503" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">Motor</text>
<polygon fill="#298029" stroke="#298029" points="479,-108 425,-108 425,-72 479,-72 479,-108"/>
<text text-anchor="middle" x="452" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">Motor</text>
</g>
<!-- Robot&#45;&gt;Motor -->
<g id="edge9" class="edge"><title>Robot&#45;&gt;Motor</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M503,-143.697C503,-135.983 503,-126.712 503,-118.112"/>
<polygon fill="none" stroke="black" points="506.5,-118.104 503,-108.104 499.5,-118.104 506.5,-118.104"/>
<g id="edge10" class="edge"><title>Robot&#45;&gt;Motor</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M452,-143.697C452,-135.983 452,-126.712 452,-118.112"/>
<polygon fill="none" stroke="black" points="455.5,-118.104 452,-108.104 448.5,-118.104 455.5,-118.104"/>
</g>
<!-- DigitalOutputDevice -->
<g id="node12" class="node"><title>DigitalOutputDevice</title>
<polygon fill="#298029" stroke="#298029" points="444.5,-36 333.5,-36 333.5,-0 444.5,-0 444.5,-36"/>
<text text-anchor="middle" x="389" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">DigitalOutputDevice</text>
</g>
<!-- Motor&#45;&gt;DigitalOutputDevice -->
<g id="edge11" class="edge"><title>Motor&#45;&gt;DigitalOutputDevice</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M436.427,-71.6966C428.798,-63.2198 419.477,-52.8636 411.124,-43.5826"/>
<polygon fill="none" stroke="black" points="413.685,-41.1959 404.394,-36.1043 408.482,-45.8787 413.685,-41.1959"/>
</g>
<!-- PWMOutputDevice -->
<g id="node12" class="node"><title>PWMOutputDevice</title>
<polygon fill="#298029" stroke="#298029" points="555,-36 451,-36 451,-0 555,-0 555,-36"/>
<text text-anchor="middle" x="503" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">PWMOutputDevice</text>
<g id="node13" class="node"><title>PWMOutputDevice</title>
<polygon fill="#298029" stroke="#298029" points="567,-36 463,-36 463,-0 567,-0 567,-36"/>
<text text-anchor="middle" x="515" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">PWMOutputDevice</text>
</g>
<!-- Motor&#45;&gt;PWMOutputDevice -->
<g id="edge10" class="edge"><title>Motor&#45;&gt;PWMOutputDevice</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M503,-71.6966C503,-63.9827 503,-54.7125 503,-46.1124"/>
<polygon fill="none" stroke="black" points="506.5,-46.1043 503,-36.1043 499.5,-46.1044 506.5,-46.1043"/>
<g id="edge12" class="edge"><title>Motor&#45;&gt;PWMOutputDevice</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M467.573,-71.6966C475.202,-63.2198 484.523,-52.8636 492.876,-43.5826"/>
<polygon fill="none" stroke="black" points="495.518,-45.8787 499.606,-36.1043 490.315,-41.1959 495.518,-45.8787"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

@@ -555,16 +555,22 @@ class RGBLED(SourceMixin, Device):
:param bool initial_value:
The initial color for the LED. Defaults to black ``(0, 0, 0)``.
:param bool pwm:
If ``True`` (the default), construct :class:`PWMLED` instances for
each component of the RGBLED. If ``False``, construct regular
:class:`LED` instances, which prevents smooth color graduations.
"""
def __init__(
self, red=None, green=None, blue=None, active_high=True,
initial_value=(0, 0, 0)):
initial_value=(0, 0, 0), pwm=True):
self._leds = ()
self._blink_thread = None
if not all([red, green, blue]):
raise GPIOPinMissing('red, green, and blue pins must be provided')
LEDClass = PWMLED if pwm else LED
super(RGBLED, self).__init__()
self._leds = tuple(PWMLED(pin, active_high) for pin in (red, green, blue))
self._leds = tuple(LEDClass(pin, active_high) for pin in (red, green, blue))
self.value = initial_value
red = _led_property(0)
@@ -587,7 +593,8 @@ class RGBLED(SourceMixin, Device):
def value(self):
"""
Represents the color of the LED as an RGB 3-tuple of ``(red, green,
blue)`` where each value is between 0 and 1.
blue)`` where each value is between 0 and 1 if ``pwm`` was ``True``
when the class was constructed (and only 0 or 1 if not).
For example, purple would be ``(1, 0, 1)`` and yellow would be ``(1, 1,
0)``, while orange would be ``(1, 0.5, 0)``.
@@ -596,6 +603,12 @@ class RGBLED(SourceMixin, Device):
@value.setter
def value(self, value):
for component in value:
if not 0 <= component <= 1:
raise OutputDeviceBadValue('each RGB color component must be between 0 and 1')
if isinstance(self._leds[0], LED):
if component not in (0, 1):
raise OutputDeviceBadValue('each RGB color component must be 0 or 1 with non-PWM RGBLEDs')
self._stop_blink()
self.red, self.green, self.blue = value
@@ -647,10 +660,14 @@ class RGBLED(SourceMixin, Device):
Number of seconds off. Defaults to 1 second.
:param float fade_in_time:
Number of seconds to spend fading in. Defaults to 0.
Number of seconds to spend fading in. Defaults to 0. Must be 0 if
``pwm`` was ``False`` when the class was constructed
(:exc:`ValueError` will be raised if not).
:param float fade_out_time:
Number of seconds to spend fading out. Defaults to 0.
Number of seconds to spend fading out. Defaults to 0. Must be 0 if
``pwm`` was ``False`` when the class was constructed
(:exc:`ValueError` will be raised if not).
:param tuple on_color:
The color to use when the LED is "on". Defaults to white.
@@ -667,6 +684,11 @@ class RGBLED(SourceMixin, Device):
blink is finished (warning: the default value of *n* will result in
this method never returning).
"""
if isinstance(self._leds[0], LED):
if fade_in_time:
raise ValueError('fade_in_time must be 0 with non-PWM RGBLEDs')
if fade_out_time:
raise ValueError('fade_out_time must be 0 with non-PWM RGBLEDs')
self._stop_blink()
self._blink_thread = GPIOThread(
target=self._blink_device,
@@ -782,22 +804,31 @@ class Motor(SourceMixin, CompositeDevice):
:param int backward:
The GPIO pin that the backward input of the motor driver chip is
connected to.
:param bool pwm:
If ``True`` (the default), construct :class:`PWMOutputDevice`
instances for the motor controller pins, allowing both direction and
variable speed control. If ``False``, construct
:class:`DigitalOutputDevice` instances, allowing only direction
control.
"""
def __init__(self, forward=None, backward=None):
def __init__(self, forward=None, backward=None, pwm=True):
if not all([forward, backward]):
raise GPIOPinMissing(
'forward and backward pins must be provided'
)
PinClass = PWMOutputDevice if pwm else DigitalOutputDevice
super(Motor, self).__init__(
forward_device=PWMOutputDevice(forward),
backward_device=PWMOutputDevice(backward),
forward_device=PinClass(forward),
backward_device=PinClass(backward),
_order=('forward_device', 'backward_device'))
@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).
(full speed backward) and 1 (full speed forward), with 0 representing
stopped.
"""
return self.forward_device.value - self.backward_device.value
@@ -806,9 +837,15 @@ class Motor(SourceMixin, CompositeDevice):
if not -1 <= value <= 1:
raise OutputDeviceBadValue("Motor value must be between -1 and 1")
if value > 0:
self.forward(value)
try:
self.forward(value)
except ValueError as e:
raise OutputDeviceBadValue(e)
elif value < 0:
self.backward(-value)
try:
self.backward(-value)
except ValueError as e:
raise OutputDeviceBadValue(e)
else:
self.stop()
@@ -826,8 +863,14 @@ class Motor(SourceMixin, CompositeDevice):
:param float speed:
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) if ``pwm`` was
``True`` when the class was constructed (and only 0 or 1 if not).
"""
if not 0 <= speed <= 1:
raise ValueError('forward speed must be between 0 and 1')
if isinstance(self.forward_device, DigitalOutputDevice):
if speed not in (0, 1):
raise ValueError('forward speed must be 0 or 1 with non-PWM Motors')
self.backward_device.off()
self.forward_device.value = speed
@@ -837,8 +880,14 @@ class Motor(SourceMixin, CompositeDevice):
:param float speed:
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) if ``pwm`` was
``True`` when the class was constructed (and only 0 or 1 if not).
"""
if not 0 <= speed <= 1:
raise ValueError('backward speed must be between 0 and 1')
if isinstance(self.backward_device, DigitalOutputDevice):
if speed not in (0, 1):
raise ValueError('backward speed must be 0 or 1 with non-PWM Motors')
self.forward_device.off()
self.backward_device.value = speed

View File

@@ -378,67 +378,6 @@ def test_output_pwm_pulse_foreground():
(0.04, 0),
])
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_output_pwm_pulse_background():
pin = MockPWMPin(2)
with PWMOutputDevice(pin) as device:
device.pulse(0.2, 0.2, n=2)
device._blink_thread.join()
pin.assert_states_and_times([
(0.0, 0),
(0.04, 0.2),
(0.04, 0.4),
(0.04, 0.6),
(0.04, 0.8),
(0.04, 1),
(0.04, 0.8),
(0.04, 0.6),
(0.04, 0.4),
(0.04, 0.2),
(0.04, 0),
(0.04, 0.2),
(0.04, 0.4),
(0.04, 0.6),
(0.04, 0.8),
(0.04, 1),
(0.04, 0.8),
(0.04, 0.6),
(0.04, 0.4),
(0.04, 0.2),
(0.04, 0),
])
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_output_pwm_pulse_foreground():
pin = MockPWMPin(2)
with PWMOutputDevice(pin) as device:
device.pulse(0.2, 0.2, n=2, background=False)
pin.assert_states_and_times([
(0.0, 0),
(0.04, 0.2),
(0.04, 0.4),
(0.04, 0.6),
(0.04, 0.8),
(0.04, 1),
(0.04, 0.8),
(0.04, 0.6),
(0.04, 0.4),
(0.04, 0.2),
(0.04, 0),
(0.04, 0.2),
(0.04, 0.4),
(0.04, 0.6),
(0.04, 0.8),
(0.04, 1),
(0.04, 0.8),
(0.04, 0.6),
(0.04, 0.4),
(0.04, 0.2),
(0.04, 0),
])
def test_output_pwm_blink_interrupt():
pin = MockPWMPin(2)
with PWMOutputDevice(pin) as device:
@@ -461,9 +400,47 @@ def test_rgbled_initial_value():
assert isclose(g.state, 0.2)
assert isclose(b.state, 0.0)
def test_rgbled_initial_value_nonpwm():
r, g, b = (MockPin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False, initial_value=(0, 1, 1)) as device:
assert r.state == 0
assert g.state == 1
assert b.state == 1
def test_rgbled_initial_bad_value():
r, g, b = (MockPWMPin(i) for i in (1, 2, 3))
with pytest.raises(ValueError):
RGBLED(r, g, b, initial_value=(0.1, 0.2, 1.2))
def test_rgbled_initial_bad_value_nonpwm():
r, g, b = (MockPin(i) for i in (1, 2, 3))
with pytest.raises(ValueError):
RGBLED(r, g, b, pwm=False, initial_value=(0.1, 0.2, 0))
def test_rgbled_value():
r, g, b = (MockPWMPin(i) for i in (1, 2, 3))
with RGBLED(r, g, b) as device:
assert isinstance(device._leds[0], PWMLED)
assert isinstance(device._leds[1], PWMLED)
assert isinstance(device._leds[2], PWMLED)
assert not device.is_active
assert device.value == (0, 0, 0)
device.on()
assert device.is_active
assert device.value == (1, 1, 1)
device.off()
assert not device.is_active
assert device.value == (0, 0, 0)
device.value = (0.5, 0.5, 0.5)
assert device.is_active
assert device.value == (0.5, 0.5, 0.5)
def test_rgbled_value_nonpwm():
r, g, b = (MockPin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device:
assert isinstance(device._leds[0], LED)
assert isinstance(device._leds[1], LED)
assert isinstance(device._leds[2], LED)
assert not device.is_active
assert device.value == (0, 0, 0)
device.on()
@@ -473,6 +450,33 @@ def test_rgbled_value():
assert not device.is_active
assert device.value == (0, 0, 0)
def test_rgbled_bad_value():
r, g, b = (MockPWMPin(i) for i in (1, 2, 3))
with RGBLED(r, g, b) as device:
with pytest.raises(ValueError):
device.value = (2, 0, 0)
with RGBLED(r, g, b) as device:
with pytest.raises(ValueError):
device.value = (0, -1, 0)
def test_rgbled_bad_value_nonpwm():
r, g, b = (MockPin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device:
with pytest.raises(ValueError):
device.value = (2, 0, 0)
with RGBLED(r, g, b, pwm=False) as device:
with pytest.raises(ValueError):
device.value = (0, -1, 0)
with RGBLED(r, g, b, pwm=False) as device:
with pytest.raises(ValueError):
device.value = (0.5, 0, 0)
with RGBLED(r, g, b, pwm=False) as device:
with pytest.raises(ValueError):
device.value = (0, 0.5, 0)
with RGBLED(r, g, b, pwm=False) as device:
with pytest.raises(ValueError):
device.value = (0, 0, 0.5)
def test_rgbled_toggle():
r, g, b = (MockPWMPin(i) for i in (1, 2, 3))
with RGBLED(r, g, b) as device:
@@ -485,6 +489,18 @@ def test_rgbled_toggle():
assert not device.is_active
assert device.value == (0, 0, 0)
def test_rgbled_toggle_nonpwm():
r, g, b = (MockPin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device:
assert not device.is_active
assert device.value == (0, 0, 0)
device.toggle()
assert device.is_active
assert device.value == (1, 1, 1)
device.toggle()
assert not device.is_active
assert device.value == (0, 0, 0)
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_rgbled_blink_background():
@@ -506,6 +522,27 @@ def test_rgbled_blink_background():
g.assert_states_and_times(expected)
b.assert_states_and_times(expected)
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_rgbled_blink_background_nonpwm():
r, g, b = (MockPin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device:
start = time()
device.blink(0.1, 0.1, n=2)
assert isclose(time() - start, 0, abs_tol=0.05)
device._blink_thread.join()
assert isclose(time() - start, 0.4, abs_tol=0.05)
expected = [
(0.0, 0),
(0.0, 1),
(0.1, 0),
(0.1, 1),
(0.1, 0)
]
r.assert_states_and_times(expected)
g.assert_states_and_times(expected)
b.assert_states_and_times(expected)
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_rgbled_blink_foreground():
@@ -525,6 +562,25 @@ def test_rgbled_blink_foreground():
g.assert_states_and_times(expected)
b.assert_states_and_times(expected)
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_rgbled_blink_foreground_nonpwm():
r, g, b = (MockPin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device:
start = time()
device.blink(0.1, 0.1, n=2, background=False)
assert isclose(time() - start, 0.4, abs_tol=0.05)
expected = [
(0.0, 0),
(0.0, 1),
(0.1, 0),
(0.1, 1),
(0.1, 0)
]
r.assert_states_and_times(expected)
g.assert_states_and_times(expected)
b.assert_states_and_times(expected)
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_rgbled_fade_background():
@@ -562,6 +618,12 @@ def test_rgbled_fade_background():
g.assert_states_and_times(expected)
b.assert_states_and_times(expected)
def test_rgbled_fade_background_nonpwm():
r, g, b = (MockPin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device:
with pytest.raises(ValueError):
device.blink(0, 0, 0.2, 0.2, n=2)
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_rgbled_fade_foreground():
@@ -597,6 +659,12 @@ def test_rgbled_fade_foreground():
g.assert_states_and_times(expected)
b.assert_states_and_times(expected)
def test_rgbled_fade_foreground_nonpwm():
r, g, b = (MockPin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device:
with pytest.raises(ValueError):
device.blink(0, 0, 0.2, 0.2, n=2, background=False)
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_rgbled_pulse_background():
@@ -634,6 +702,12 @@ def test_rgbled_pulse_background():
g.assert_states_and_times(expected)
b.assert_states_and_times(expected)
def test_rgbled_pulse_background_nonpwm():
r, g, b = (MockPin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device:
with pytest.raises(ValueError):
device.pulse(0.2, 0.2, n=2)
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_rgbled_pulse_foreground():
@@ -669,6 +743,12 @@ def test_rgbled_pulse_foreground():
g.assert_states_and_times(expected)
b.assert_states_and_times(expected)
def test_rgbled_pulse_foreground_nonpwm():
r, g, b = (MockPin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device:
with pytest.raises(ValueError):
device.pulse(0.2, 0.2, n=2, background=False)
def test_rgbled_blink_interrupt():
r, g, b = (MockPWMPin(i) for i in (1, 2, 3))
with RGBLED(r, g, b) as device:
@@ -679,6 +759,16 @@ def test_rgbled_blink_interrupt():
g.assert_states([0, 1, 0])
b.assert_states([0, 1, 0])
def test_rgbled_blink_interrupt_nonpwm():
r, g, b = (MockPin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device:
device.blink(1, 0.1)
sleep(0.2)
device.off() # should interrupt while on
r.assert_states([0, 1, 0])
g.assert_states([0, 1, 0])
b.assert_states([0, 1, 0])
def test_rgbled_close():
r, g, b = (MockPWMPin(i) for i in (1, 2, 3))
with RGBLED(r, g, b) as device:
@@ -688,6 +778,15 @@ def test_rgbled_close():
device.close()
assert device.closed
def test_rgbled_close_nonpwm():
r, g, b = (MockPin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device:
assert not device.closed
device.close()
assert device.closed
device.close()
assert device.closed
def test_motor_missing_pins():
with pytest.raises(ValueError):
Motor()
@@ -697,7 +796,18 @@ def test_motor_pins():
b = MockPWMPin(2)
with Motor(f, b) as device:
assert device.forward_device.pin is f
assert isinstance(device.forward_device, PWMOutputDevice)
assert device.backward_device.pin is b
assert isinstance(device.backward_device, PWMOutputDevice)
def test_motor_pins_nonpwm():
f = MockPin(1)
b = MockPin(2)
with Motor(f, b, pwm=False) as device:
assert device.forward_device.pin is f
assert isinstance(device.forward_device, DigitalOutputDevice)
assert device.backward_device.pin is b
assert isinstance(device.backward_device, DigitalOutputDevice)
def test_motor_close():
f = MockPWMPin(1)
@@ -710,6 +820,15 @@ def test_motor_close():
device.close()
assert device.closed
def test_motor_close_nonpwm():
f = MockPin(1)
b = MockPin(2)
with Motor(f, b, pwm=False) as device:
device.close()
assert device.closed
assert device.forward_device.pin is None
assert device.backward_device.pin is None
def test_motor_value():
f = MockPWMPin(1)
b = MockPWMPin(2)
@@ -726,6 +845,27 @@ def test_motor_value():
assert device.is_active
assert device.value == 0.5
assert b.state == 0 and f.state == 0.5
device.value = -0.5
assert device.is_active
assert device.value == -0.5
assert b.state == 0.5 and f.state == 0
device.value = 0
assert not device.is_active
assert not device.value
assert b.state == 0 and f.state == 0
def test_motor_value_nonpwm():
f = MockPin(1)
b = MockPin(2)
with Motor(f, b, pwm=False) as device:
device.value = -1
assert device.is_active
assert device.value == -1
assert b.state == 1 and f.state == 0
device.value = 1
assert device.is_active
assert device.value == 1
assert b.state == 0 and f.state == 1
device.value = 0
assert not device.is_active
assert not device.value
@@ -735,9 +875,24 @@ def test_motor_bad_value():
f = MockPWMPin(1)
b = MockPWMPin(2)
with Motor(f, b) as device:
with pytest.raises(ValueError):
device.value = -2
with pytest.raises(ValueError):
device.value = 2
def test_motor_bad_value_nonpwm():
f = MockPin(1)
b = MockPin(2)
with Motor(f, b, pwm=False) as device:
with pytest.raises(ValueError):
device.value = -2
with pytest.raises(ValueError):
device.value = 2
with pytest.raises(ValueError):
device.value = 0.5
with pytest.raises(ValueError):
device.value = -0.5
def test_motor_reverse():
f = MockPWMPin(1)
b = MockPWMPin(2)
@@ -748,4 +903,20 @@ def test_motor_reverse():
device.reverse()
assert device.value == -1
assert b.state == 1 and f.state == 0
device.backward(0.5)
assert device.value == -0.5
assert b.state == 0.5 and f.state == 0
device.reverse()
assert device.value == 0.5
assert b.state == 0 and f.state == 0.5
def test_motor_reverse_nonpwm():
f = MockPin(1)
b = MockPin(2)
with Motor(f, b, pwm=False) as device:
device.forward()
assert device.value == 1
assert b.state == 0 and f.state == 1
device.reverse()
assert device.value == -1
assert b.state == 1 and f.state == 0