Change parent of PWMOutputDevice to OutputDevice and implement blink to
maintain compatibility. The version of blink implemented here is
slightly extended to include functionality like Explorer HAT's "pulse".
The parameter defaults behave identically to OutputDevice's blink but
can be adjusted to have the device smoothly fade in and out.
This commit is contained in:
Dave Jones
2016-01-31 13:31:59 +00:00
parent 9dc4a2208a
commit cf18fb971e
5 changed files with 141 additions and 65 deletions

View File

@@ -17,7 +17,7 @@ digraph classes {
DigitalOutputDevice->OutputDevice;
LED->DigitalOutputDevice;
Buzzer->DigitalOutputDevice;
PWMOutputDevice->DigitalOutputDevice;
PWMOutputDevice->OutputDevice;
PWMLED->PWMOutputDevice;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -4,105 +4,105 @@
<!-- Generated by graphviz version 2.36.0 (20140111.2315)
-->
<!-- Title: classes Pages: 1 -->
<svg width="569pt" height="332pt"
viewBox="0.00 0.00 569.00 332.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="563pt" height="332pt"
viewBox="0.00 0.00 563.00 332.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 328)">
<title>classes</title>
<polygon fill="white" stroke="none" points="-4,4 -4,-328 565,-328 565,4 -4,4"/>
<polygon fill="white" stroke="none" points="-4,4 -4,-328 559,-328 559,4 -4,4"/>
<!-- InputDevice -->
<g id="node1" class="node"><title>InputDevice</title>
<polygon fill="#2980b9" stroke="#2980b9" points="338.5,-252 265.5,-252 265.5,-216 338.5,-216 338.5,-252"/>
<text text-anchor="middle" x="302" y="-231.5" font-family="Sans" font-size="10.00" fill="#ffffff">InputDevice</text>
<polygon fill="#2980b9" stroke="#2980b9" points="346.5,-252 273.5,-252 273.5,-216 346.5,-216 346.5,-252"/>
<text text-anchor="middle" x="310" y="-231.5" font-family="Sans" font-size="10.00" fill="#ffffff">InputDevice</text>
</g>
<!-- GPIODevice -->
<g id="node2" class="node"><title>GPIODevice</title>
<polygon fill="#2980b9" stroke="#2980b9" points="274,-324 202,-324 202,-288 274,-288 274,-324"/>
<text text-anchor="middle" x="238" y="-303.5" font-family="Sans" font-size="10.00" fill="#ffffff">GPIODevice</text>
<polygon fill="#2980b9" stroke="#2980b9" points="290,-324 218,-324 218,-288 290,-288 290,-324"/>
<text text-anchor="middle" x="254" y="-303.5" font-family="Sans" font-size="10.00" fill="#ffffff">GPIODevice</text>
</g>
<!-- InputDevice&#45;&gt;GPIODevice -->
<g id="edge1" class="edge"><title>InputDevice&#45;&gt;GPIODevice</title>
<path fill="none" stroke="black" d="M286.18,-252.303C278.43,-260.78 268.961,-271.136 260.476,-280.417"/>
<polygon fill="black" stroke="black" points="257.803,-278.154 253.638,-287.896 262.969,-282.877 257.803,-278.154"/>
<path fill="none" stroke="black" d="M296.157,-252.303C289.511,-260.611 281.422,-270.723 274.113,-279.859"/>
<polygon fill="black" stroke="black" points="271.197,-277.901 267.683,-287.896 276.664,-282.273 271.197,-277.901"/>
</g>
<!-- WaitableInputDevice -->
<g id="node3" class="node"><title>WaitableInputDevice</title>
<polygon fill="#2980b9" stroke="#2980b9" points="374.5,-180 261.5,-180 261.5,-144 374.5,-144 374.5,-180"/>
<text text-anchor="middle" x="318" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">WaitableInputDevice</text>
<polygon fill="#2980b9" stroke="#2980b9" points="382.5,-180 269.5,-180 269.5,-144 382.5,-144 382.5,-180"/>
<text text-anchor="middle" x="326" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">WaitableInputDevice</text>
</g>
<!-- WaitableInputDevice&#45;&gt;InputDevice -->
<g id="edge2" class="edge"><title>WaitableInputDevice&#45;&gt;InputDevice</title>
<path fill="none" stroke="black" d="M314.045,-180.303C312.282,-188.017 310.163,-197.288 308.197,-205.888"/>
<polygon fill="black" stroke="black" points="304.726,-205.367 305.91,-215.896 311.55,-206.927 304.726,-205.367"/>
<path fill="none" stroke="black" d="M322.045,-180.303C320.282,-188.017 318.163,-197.288 316.197,-205.888"/>
<polygon fill="black" stroke="black" points="312.726,-205.367 313.91,-215.896 319.55,-206.927 312.726,-205.367"/>
</g>
<!-- DigitalInputDevice -->
<g id="node4" class="node"><title>DigitalInputDevice</title>
<polygon fill="#2980b9" stroke="#2980b9" points="369.5,-108 266.5,-108 266.5,-72 369.5,-72 369.5,-108"/>
<text text-anchor="middle" x="318" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">DigitalInputDevice</text>
<polygon fill="#2980b9" stroke="#2980b9" points="363.5,-108 260.5,-108 260.5,-72 363.5,-72 363.5,-108"/>
<text text-anchor="middle" x="312" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">DigitalInputDevice</text>
</g>
<!-- DigitalInputDevice&#45;&gt;WaitableInputDevice -->
<g id="edge3" class="edge"><title>DigitalInputDevice&#45;&gt;WaitableInputDevice</title>
<path fill="none" stroke="black" d="M318,-108.303C318,-116.017 318,-125.288 318,-133.888"/>
<polygon fill="black" stroke="black" points="314.5,-133.896 318,-143.896 321.5,-133.896 314.5,-133.896"/>
<path fill="none" stroke="black" d="M315.461,-108.303C317.003,-116.017 318.858,-125.288 320.578,-133.888"/>
<polygon fill="black" stroke="black" points="317.186,-134.776 322.579,-143.896 324.05,-133.403 317.186,-134.776"/>
</g>
<!-- SmoothedInputDevice -->
<g id="node5" class="node"><title>SmoothedInputDevice</title>
<polygon fill="#2980b9" stroke="#2980b9" points="510.5,-108 387.5,-108 387.5,-72 510.5,-72 510.5,-108"/>
<text text-anchor="middle" x="449" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">SmoothedInputDevice</text>
<polygon fill="#2980b9" stroke="#2980b9" points="504.5,-108 381.5,-108 381.5,-72 504.5,-72 504.5,-108"/>
<text text-anchor="middle" x="443" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">SmoothedInputDevice</text>
</g>
<!-- SmoothedInputDevice&#45;&gt;WaitableInputDevice -->
<g id="edge4" class="edge"><title>SmoothedInputDevice&#45;&gt;WaitableInputDevice</title>
<path fill="none" stroke="black" d="M416.954,-108.124C399.508,-117.446 377.735,-129.081 359.057,-139.061"/>
<polygon fill="black" stroke="black" points="357.34,-136.01 350.169,-143.81 360.639,-142.184 357.34,-136.01"/>
<path fill="none" stroke="black" d="M414.379,-108.124C399.081,-117.276 380.057,-128.658 363.582,-138.515"/>
<polygon fill="black" stroke="black" points="361.516,-135.673 354.731,-143.81 365.11,-141.68 361.516,-135.673"/>
</g>
<!-- Button -->
<g id="node6" class="node"><title>Button</title>
<polygon fill="#2980b9" stroke="#2980b9" points="345,-36 291,-36 291,-0 345,-0 345,-36"/>
<text text-anchor="middle" x="318" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">Button</text>
<polygon fill="#2980b9" stroke="#2980b9" points="339,-36 285,-36 285,-0 339,-0 339,-36"/>
<text text-anchor="middle" x="312" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">Button</text>
</g>
<!-- Button&#45;&gt;DigitalInputDevice -->
<g id="edge5" class="edge"><title>Button&#45;&gt;DigitalInputDevice</title>
<path fill="none" stroke="black" d="M318,-36.3034C318,-44.0173 318,-53.2875 318,-61.8876"/>
<polygon fill="black" stroke="black" points="314.5,-61.8956 318,-71.8957 321.5,-61.8957 314.5,-61.8956"/>
<path fill="none" stroke="black" d="M312,-36.3034C312,-44.0173 312,-53.2875 312,-61.8876"/>
<polygon fill="black" stroke="black" points="308.5,-61.8956 312,-71.8957 315.5,-61.8957 308.5,-61.8956"/>
</g>
<!-- MotionSensor -->
<g id="node7" class="node"><title>MotionSensor</title>
<polygon fill="#2980b9" stroke="#2980b9" points="468.5,-36 385.5,-36 385.5,-0 468.5,-0 468.5,-36"/>
<text text-anchor="middle" x="427" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MotionSensor</text>
<polygon fill="#2980b9" stroke="#2980b9" points="462.5,-36 379.5,-36 379.5,-0 462.5,-0 462.5,-36"/>
<text text-anchor="middle" x="421" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MotionSensor</text>
</g>
<!-- MotionSensor&#45;&gt;SmoothedInputDevice -->
<g id="edge6" class="edge"><title>MotionSensor&#45;&gt;SmoothedInputDevice</title>
<path fill="none" stroke="black" d="M432.438,-36.3034C434.889,-44.1021 437.84,-53.4915 440.568,-62.1708"/>
<polygon fill="black" stroke="black" points="437.287,-63.4051 443.624,-71.8957 443.965,-61.3063 437.287,-63.4051"/>
<path fill="none" stroke="black" d="M426.438,-36.3034C428.889,-44.1021 431.84,-53.4915 434.568,-62.1708"/>
<polygon fill="black" stroke="black" points="431.287,-63.4051 437.624,-71.8957 437.965,-61.3063 431.287,-63.4051"/>
</g>
<!-- LightSensor -->
<g id="node8" class="node"><title>LightSensor</title>
<polygon fill="#2980b9" stroke="#2980b9" points="561,-36 487,-36 487,-0 561,-0 561,-36"/>
<text text-anchor="middle" x="524" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">LightSensor</text>
<polygon fill="#2980b9" stroke="#2980b9" points="555,-36 481,-36 481,-0 555,-0 555,-36"/>
<text text-anchor="middle" x="518" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">LightSensor</text>
</g>
<!-- LightSensor&#45;&gt;SmoothedInputDevice -->
<g id="edge7" class="edge"><title>LightSensor&#45;&gt;SmoothedInputDevice</title>
<path fill="none" stroke="black" d="M505.461,-36.3034C496.197,-44.9497 484.838,-55.5514 474.743,-64.973"/>
<polygon fill="black" stroke="black" points="472.249,-62.5137 467.326,-71.8957 477.025,-67.6312 472.249,-62.5137"/>
<path fill="none" stroke="black" d="M499.461,-36.3034C490.197,-44.9497 478.838,-55.5514 468.743,-64.973"/>
<polygon fill="black" stroke="black" points="466.249,-62.5137 461.326,-71.8957 471.025,-67.6312 466.249,-62.5137"/>
</g>
<!-- OutputDevice -->
<g id="node9" class="node"><title>OutputDevice</title>
<polygon fill="#2980b9" stroke="#2980b9" points="216,-252 134,-252 134,-216 216,-216 216,-252"/>
<text text-anchor="middle" x="175" y="-231.5" font-family="Sans" font-size="10.00" fill="#ffffff">OutputDevice</text>
<polygon fill="#2980b9" stroke="#2980b9" points="240,-252 158,-252 158,-216 240,-216 240,-252"/>
<text text-anchor="middle" x="199" y="-231.5" font-family="Sans" font-size="10.00" fill="#ffffff">OutputDevice</text>
</g>
<!-- OutputDevice&#45;&gt;GPIODevice -->
<g id="edge8" class="edge"><title>OutputDevice&#45;&gt;GPIODevice</title>
<path fill="none" stroke="black" d="M190.573,-252.303C198.202,-260.78 207.523,-271.136 215.876,-280.417"/>
<polygon fill="black" stroke="black" points="213.315,-282.804 222.606,-287.896 218.518,-278.121 213.315,-282.804"/>
<path fill="none" stroke="black" d="M212.596,-252.303C219.123,-260.611 227.068,-270.723 234.247,-279.859"/>
<polygon fill="black" stroke="black" points="231.631,-282.195 240.561,-287.896 237.135,-277.87 231.631,-282.195"/>
</g>
<!-- DigitalOutputDevice -->
<g id="node10" class="node"><title>DigitalOutputDevice</title>
<polygon fill="#2980b9" stroke="#2980b9" points="170,-180 58,-180 58,-144 170,-144 170,-180"/>
<text text-anchor="middle" x="114" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">DigitalOutputDevice</text>
<polygon fill="#2980b9" stroke="#2980b9" points="129,-180 17,-180 17,-144 129,-144 129,-180"/>
<text text-anchor="middle" x="73" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">DigitalOutputDevice</text>
</g>
<!-- DigitalOutputDevice&#45;&gt;OutputDevice -->
<g id="edge9" class="edge"><title>DigitalOutputDevice&#45;&gt;OutputDevice</title>
<path fill="none" stroke="black" d="M129.079,-180.303C136.392,-188.695 145.31,-198.93 153.335,-208.139"/>
<polygon fill="black" stroke="black" points="150.886,-210.656 160.095,-215.896 156.164,-206.057 150.886,-210.656"/>
<path fill="none" stroke="black" d="M103.823,-180.124C120.45,-189.361 141.165,-200.869 159.019,-210.788"/>
<polygon fill="black" stroke="black" points="157.617,-214.013 168.058,-215.81 161.017,-207.894 157.617,-214.013"/>
</g>
<!-- LED -->
<g id="node11" class="node"><title>LED</title>
@@ -111,8 +111,8 @@
</g>
<!-- LED&#45;&gt;DigitalOutputDevice -->
<g id="edge10" class="edge"><title>LED&#45;&gt;DigitalOutputDevice</title>
<path fill="none" stroke="black" d="M48.5056,-108.303C59.4625,-117.119 72.9459,-127.968 84.8249,-137.526"/>
<polygon fill="black" stroke="black" points="82.7565,-140.354 92.7418,-143.896 87.1446,-134.9 82.7565,-140.354"/>
<path fill="none" stroke="black" d="M38.3708,-108.303C43.7185,-116.441 50.2043,-126.311 56.1106,-135.299"/>
<polygon fill="black" stroke="black" points="53.3432,-137.461 61.76,-143.896 59.1932,-133.616 53.3432,-137.461"/>
</g>
<!-- Buzzer -->
<g id="node12" class="node"><title>Buzzer</title>
@@ -121,28 +121,28 @@
</g>
<!-- Buzzer&#45;&gt;DigitalOutputDevice -->
<g id="edge11" class="edge"><title>Buzzer&#45;&gt;DigitalOutputDevice</title>
<path fill="none" stroke="black" d="M102.708,-108.303C104.361,-116.017 106.347,-125.288 108.19,-133.888"/>
<polygon fill="black" stroke="black" points="104.817,-134.851 110.335,-143.896 111.662,-133.384 104.817,-134.851"/>
<path fill="none" stroke="black" d="M92.573,-108.303C89.6449,-116.187 86.113,-125.696 82.8601,-134.454"/>
<polygon fill="black" stroke="black" points="79.554,-133.303 79.353,-143.896 86.116,-135.74 79.554,-133.303"/>
</g>
<!-- PWMOutputDevice -->
<g id="node13" class="node"><title>PWMOutputDevice</title>
<polygon fill="#2980b9" stroke="#2980b9" points="248,-108 144,-108 144,-72 248,-72 248,-108"/>
<text text-anchor="middle" x="196" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">PWMOutputDevice</text>
<polygon fill="#2980b9" stroke="#2980b9" points="251,-180 147,-180 147,-144 251,-144 251,-180"/>
<text text-anchor="middle" x="199" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">PWMOutputDevice</text>
</g>
<!-- PWMOutputDevice&#45;&gt;DigitalOutputDevice -->
<g id="edge12" class="edge"><title>PWMOutputDevice&#45;&gt;DigitalOutputDevice</title>
<path fill="none" stroke="black" d="M175.73,-108.303C165.502,-117.035 152.939,-127.76 141.822,-137.25"/>
<polygon fill="black" stroke="black" points="139.37,-134.741 134.037,-143.896 143.915,-140.065 139.37,-134.741"/>
<!-- PWMOutputDevice&#45;&gt;OutputDevice -->
<g id="edge12" class="edge"><title>PWMOutputDevice&#45;&gt;OutputDevice</title>
<path fill="none" stroke="black" d="M199,-180.303C199,-188.017 199,-197.288 199,-205.888"/>
<polygon fill="black" stroke="black" points="195.5,-205.896 199,-215.896 202.5,-205.896 195.5,-205.896"/>
</g>
<!-- PWMLED -->
<g id="node14" class="node"><title>PWMLED</title>
<polygon fill="#2980b9" stroke="#2980b9" points="225,-36 167,-36 167,-0 225,-0 225,-36"/>
<text text-anchor="middle" x="196" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">PWMLED</text>
<polygon fill="#2980b9" stroke="#2980b9" points="228,-108 170,-108 170,-72 228,-72 228,-108"/>
<text text-anchor="middle" x="199" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">PWMLED</text>
</g>
<!-- PWMLED&#45;&gt;PWMOutputDevice -->
<g id="edge13" class="edge"><title>PWMLED&#45;&gt;PWMOutputDevice</title>
<path fill="none" stroke="black" d="M196,-36.3034C196,-44.0173 196,-53.2875 196,-61.8876"/>
<polygon fill="black" stroke="black" points="192.5,-61.8956 196,-71.8957 199.5,-61.8957 192.5,-61.8956"/>
<path fill="none" stroke="black" d="M199,-108.303C199,-116.017 199,-125.288 199,-133.888"/>
<polygon fill="black" stroke="black" points="195.5,-133.896 199,-143.896 202.5,-133.896 195.5,-133.896"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@@ -8,7 +8,7 @@ from __future__ import (
import warnings
from time import sleep
from threading import Lock
from itertools import repeat
from itertools import repeat, cycle, chain
from RPi import GPIO
@@ -136,19 +136,19 @@ class DigitalOutputDevice(OutputDevice):
Make the device turn on and off repeatedly.
:param float on_time:
Number of seconds on
Number of seconds on. Defaults to 1 second.
:param float off_time:
Number of seconds off
Number of seconds off. Defaults to 1 second.
:param int n:
Number of times to blink; ``None`` means forever
Number of times to blink; ``None`` (the default) means forever.
:param bool background:
If ``True``, start a background thread to continue blinking and
return immediately. If ``False``, only return when the blink is
finished (warning: the default value of *n* will result in this
method never returning).
If ``True`` (the default), start a background thread to continue
blinking and return immediately. If ``False``, only return when the
blink is finished (warning: the default value of *n* will result in
this method never returning).
"""
self._stop_blink()
self._blink_thread = GPIOThread(
@@ -233,7 +233,7 @@ class Buzzer(DigitalOutputDevice):
Buzzer.beep = Buzzer.blink
class PWMOutputDevice(DigitalOutputDevice):
class PWMOutputDevice(OutputDevice):
"""
Generic output device configured for pulse-width modulation (PWM).
@@ -247,6 +247,7 @@ class PWMOutputDevice(DigitalOutputDevice):
"""
def __init__(self, pin=None, frequency=100):
self._pwm = None
self._blink_thread = None
super(PWMOutputDevice, self).__init__(pin)
try:
self._pwm = GPIO.PWM(self.pin, frequency)
@@ -295,6 +296,14 @@ class PWMOutputDevice(DigitalOutputDevice):
self._stop_blink()
self._write(value)
def on(self):
self._stop_blink()
self._write(self._active_state)
def off(self):
self._stop_blink()
self._write(self._inactive_state)
def toggle(self):
"""
Toggle the state of the device. If the device is currently off
@@ -302,6 +311,7 @@ class PWMOutputDevice(DigitalOutputDevice):
1.0). If the device has a duty cycle (:attr:`value`) of 0.1, this will
toggle it to 0.9, and so on.
"""
self._stop_blink()
self.value = 1.0 - self.value
@property
@@ -325,6 +335,72 @@ class PWMOutputDevice(DigitalOutputDevice):
self._pwm.ChangeFrequency(value)
self._frequency = value
def blink(
self, on_time=1, off_time=1, fade_in_time=0, fade_out_time=0,
n=None, background=True):
"""
Make the device turn on and off repeatedly.
:param float on_time:
Number of seconds on. Defaults to 1 second.
:param float off_time:
Number of seconds off. Defaults to 1 second.
:param float fade_in_time:
Number of seconds to spend fading in. Defaults to 0.
:param float fade_out_time:
Number of seconds to spend fading out. Defaults to 0.
:param int n:
Number of times to blink; ``None`` (the default) means forever.
:param bool background:
If ``True`` (the default), start a background thread to continue
blinking and return immediately. If ``False``, only return when the
blink is finished (warning: the default value of *n* will result in
this method never returning).
"""
self._stop_blink()
self._blink_thread = GPIOThread(
target=self._blink_led,
args=(on_time, off_time, fade_in_time, fade_out_time, n)
)
self._blink_thread.start()
if not background:
self._blink_thread.join()
self._blink_thread = None
def _stop_blink(self):
if self._blink_thread:
self._blink_thread.stop()
self._blink_thread = None
def _blink_led(
self, on_time, off_time, fade_in_time, fade_out_time, n, fps=50):
sequence = []
if fade_in_time > 0.0:
sequence += [
(i * (1 / fps) / fade_in_time, 1 / fps)
for i in range(int(50 * fade_in_time))
]
sequence.append((1.0, on_time))
if fade_out_time > 0.0:
sequence += [
(1 - (i * (1 / fps) / fade_out_time), 1 / fps)
for i in range(int(50 * fade_out_time))
]
sequence.append((0.0, off_time))
sequence = (
cycle(sequence) if n is None else
chain.from_iterable(repeat(sequence, n))
)
for value, delay in sequence:
self._write(value)
if self._blink_thread.stopping.wait(delay):
break
class PWMLED(PWMOutputDevice):
"""