Files
python-gpiozero/gpiozero/output_devices.py
Dave Jones c35dd1698b Fix #27
2015-09-27 17:29:21 +01:00

282 lines
7.3 KiB
Python

import warnings
from time import sleep
from threading import Lock
from itertools import repeat
from RPi import GPIO
from .devices import GPIODeviceError, GPIODevice, GPIOThread
class OutputDeviceError(GPIODeviceError):
pass
class OutputDevice(GPIODevice):
"""
Generic GPIO Output Device (on/off).
"""
def __init__(self, pin=None):
super(OutputDevice, self).__init__(pin)
# NOTE: catch_warnings isn't thread-safe but hopefully no-one's messing
# around with GPIO init within background threads...
with warnings.catch_warnings(record=True) as w:
GPIO.setup(pin, GPIO.OUT)
# The only warning we want to squash is a RuntimeWarning that is thrown
# when setting pins 2 or 3. Anything else should be replayed
for warning in w:
if warning.category != RuntimeWarning or pin not in (2, 3):
warnings.showwarning(
warning.message, warning.category, warning.filename,
warning.lineno, warning.file, warning.line)
def on(self):
"""
Turns the device on.
"""
GPIO.output(self.pin, True)
def off(self):
"""
Turns the device off.
"""
GPIO.output(self.pin, False)
class DigitalOutputDevice(OutputDevice):
"""
Generic Digital GPIO Output Device (on/off/blink/toggle/flash).
"""
def __init__(self, pin=None):
super(DigitalOutputDevice, self).__init__(pin)
self._blink_thread = None
self._lock = Lock()
def on(self):
"""
Turns the device on.
"""
self._stop_blink()
super(DigitalOutputDevice, self).on()
def off(self):
"""
Turns the device off.
"""
self._stop_blink()
super(DigitalOutputDevice, self).off()
def blink(self, on_time=1, off_time=1, n=None, background=True):
"""
Make the device turn on and off repeatedly in the background.
on_time: 1
Number of seconds on
off_time: 1
Number of seconds off
n: None
Number of times to blink; None means forever
background: True
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).
"""
self._stop_blink()
self._blink_thread = GPIOThread(
target=self._blink_led, args=(on_time, off_time, n)
)
self._blink_thread.start()
if not background:
self._blink_thread.join()
self._blink_thread = None
def toggle(self):
"""
Reverse the state of the device.
If it's on, turn it off; if it's off, turn it on.
"""
with self._lock:
if self.is_active:
self.off()
else:
self.on()
def _stop_blink(self):
if self._blink_thread:
self._blink_thread.stop()
self._blink_thread = None
def _blink_led(self, on_time, off_time, n):
iterable = repeat(0) if n is None else repeat(0, n)
for i in iterable:
super(DigitalOutputDevice, self).on()
if self._blink_thread.stopping.wait(on_time):
break
super(DigitalOutputDevice, self).off()
if self._blink_thread.stopping.wait(off_time):
break
class LED(DigitalOutputDevice):
"""
An LED (Light Emmitting Diode) component.
"""
def on(self):
"""
Turn the LED on.
"""
super(LED, self).on()
def off(self):
"""
Turn the LED off.
"""
super(LED, self).off()
def blink(self, on_time=1, off_time=1, n=None, background=True):
"""
Make the LED turn on and off repeatedly in the background.
on_time: 1
Number of seconds on
off_time: 1
Number of seconds off
n: None
Number of times to blink; None means forever
background: True
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).
"""
super(LED, self).blink(on_time, off_time, n, background)
def toggle(self):
"""
Reverse the state of the LED.
If it's on, turn it off; if it's off, turn it on.
"""
super(LED, self).toggle()
class Buzzer(DigitalOutputDevice):
"""
A Buzzer component.
"""
def on(self):
"""
Turn the Buzzer on.
"""
super(Buzzer, self).on()
def off(self):
"""
Turn the Buzzer off.
"""
super(Buzzer, self).off()
def blink(self, on_time=1, off_time=1, n=None, background=True):
"""
Make the Buzzer turn on and off repeatedly in the background.
on_time: 1
Number of seconds on
off_time: 1
Number of seconds off
n: None
Number of times to blink; None means forever
background: True
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).
"""
super(Buzzer, self).blink(on_time, off_time, n, background)
def toggle(self):
"""
Reverse the state of the Buzzer.
If it's on, turn it off; if it's off, turn it on.
"""
super(Buzzer, self).toggle()
class Motor(OutputDevice):
def on(self):
"""
Turns the Motor on.
"""
super(Motor, self).toggle()
def off(self):
"""
Turns the Motor off.
"""
super(Motor, self).toggle()
class Robot(object):
"""
Generic single-direction dual-motor Robot.
"""
def __init__(self, left=None, right=None):
if not all([left, right]):
raise GPIODeviceError('left and right pins must be provided')
self._left = Motor(left)
self._right = Motor(right)
def left(self, seconds=None):
"""
Turns left for a given number of seconds.
seconds: None
Number of seconds to turn left for
"""
self._left.on()
if seconds is not None:
sleep(seconds)
self._left.off()
def right(self, seconds=None):
"""
Turns right for a given number of seconds.
seconds: None
Number of seconds to turn right for
"""
self._right.on()
if seconds is not None:
sleep(seconds)
self._right.off()
def forwards(self, seconds=None):
"""
Drives forward for a given number of seconds.
seconds: None
Number of seconds to drive forward for
"""
self.left()
self.right()
if seconds is not None:
sleep(seconds)
self.stop()
def stop(self):
"""
Stops both motors.
"""
self._left.off()
self._right.off()