diff --git a/gpiozero/__init__.py b/gpiozero/__init__.py index 745ea0c..b0d8175 100644 --- a/gpiozero/__init__.py +++ b/gpiozero/__init__.py @@ -4,14 +4,11 @@ import atexit from RPi import GPIO - -def gpiozero_shutdown(): - GPIO.cleanup() - -atexit.register(gpiozero_shutdown) -GPIO.setmode(GPIO.BCM) -GPIO.setwarnings(False) - +from .devices import ( + _gpio_threads_shutdown, + GPIODeviceError, + GPIODevice, + ) from .input_devices import ( InputDeviceError, InputDevice, @@ -26,3 +23,13 @@ from .output_devices import ( Buzzer, Motor, ) + + +def gpiozero_shutdown(): + _gpio_threads_shutdown() + GPIO.cleanup() + +atexit.register(gpiozero_shutdown) +GPIO.setmode(GPIO.BCM) +GPIO.setwarnings(False) + diff --git a/gpiozero/devices.py b/gpiozero/devices.py new file mode 100644 index 0000000..81818df --- /dev/null +++ b/gpiozero/devices.py @@ -0,0 +1,43 @@ +from threading import Thread, Event + +from RPi import GPIO + + +class GPIODeviceError(Exception): + pass + + +class GPIODevice(object): + def __init__(self, pin=None): + if pin is None: + raise GPIODeviceError('No GPIO pin number given') + self._pin = pin + + @property + def pin(self): + return self._pin + + +_GPIO_THREADS = set() +def _gpio_threads_shutdown(): + while _GPIO_THREADS: + for t in _GPIO_THREADS.copy(): + t.stop() + + +class GPIOThread(Thread): + def __init__(self, group=None, target=None, name=None, args=(), kwargs={}): + super(GPIOThread, self).__init__(group, target, name, args, kwargs) + self.stopping = Event() + self.daemon = True + + def start(self): + self.stopping.clear() + _GPIO_THREADS.add(self) + super(GPIOThread, self).start() + + def stop(self): + self.stopping.set() + self.join() + _GPIO_THREADS.discard(self) + diff --git a/gpiozero/input_devices.py b/gpiozero/input_devices.py index 2d7f27a..12dee4a 100644 --- a/gpiozero/input_devices.py +++ b/gpiozero/input_devices.py @@ -1,29 +1,28 @@ from __future__ import division from time import sleep -from threading import Thread, Event +from threading import Event from collections import deque from RPi import GPIO from w1thermsensor import W1ThermSensor +from .devices import GPIODeviceError, GPIODevice, GPIOThread -class InputDevice(object): + +class InputDeviceError(GPIODeviceError): + pass + + +class InputDevice(GPIODevice): def __init__(self, pin=None): - if pin is None: - raise InputDeviceError('No GPIO pin number given') - - self._pin = pin + super(InputDevice, self).__init__(pin) self._pull = GPIO.PUD_UP self._edge = GPIO.FALLING self._active_state = 0 self._inactive_state = 1 GPIO.setup(pin, GPIO.IN, self._pull) - @property - def pin(self): - return self._pin - @property def is_active(self): return GPIO.input(self.pin) == self._active_state @@ -34,7 +33,6 @@ class InputDevice(object): def add_callback(self, callback=None, bouncetime=1000): if callback is None: raise InputDeviceError('No callback function given') - GPIO.add_event_detect(self.pin, self._edge, callback, bouncetime) def remove_callback(self): @@ -48,30 +46,30 @@ class Button(InputDevice): class MotionSensor(InputDevice): def __init__(self, pin=None, queue_len=20, sample_rate=10, partial=False): super(MotionSensor, self).__init__(pin) - self._sample_rate = sample_rate - self._partial = partial + self.sample_rate = sample_rate + self.partial = partial self._queue = deque(maxlen=queue_len) self._queue_full = Event() - self._terminated = False - self._queue_thread = Thread(target=self._fill_queue) + self._queue_thread = GPIOThread(target=self._fill_queue) self._queue_thread.start() - def __del__(self): - self._terminated = True - self._queue_thread.join() + @property + def queue_len(self): + return self._queue.maxlen def _fill_queue(self): - while not self._terminated and len(self._queue) < self._queue.maxlen: + while ( + not self._queue_thread.stopping.wait(1 / self.sample_rate) and + len(self._queue) < self._queue.maxlen + ): self._queue.append(self.is_active) - sleep(1 / self._sample_rate) self._queue_full.set() - while not self._terminated: + while not self._queue_thread.stopping.wait(1 / self.sample_rate): self._queue.append(self.is_active) - sleep(1 / self._sample_rate) @property def motion_detected(self): - if not self._partial: + if not self.partial: self._queue_full.wait() return sum(self._queue) > (len(self._queue) / 2) @@ -124,5 +122,3 @@ class TemperatureSensor(W1ThermSensor): return self.get_temperature() -class InputDeviceError(Exception): - pass diff --git a/gpiozero/output_devices.py b/gpiozero/output_devices.py index 6129423..d219c62 100644 --- a/gpiozero/output_devices.py +++ b/gpiozero/output_devices.py @@ -1,9 +1,15 @@ from RPi import GPIO +from .devices import GPIODeviceError, GPIODevice, GPIOThread -class OutputDevice(object): - def __init__(self, pin): - self.pin = pin + +class OutputDeviceError(GPIODeviceError): + pass + + +class OutputDevice(GPIODevice): + def __init__(self, pin=None): + super(OutputDevice, self).__init__(pin) GPIO.setup(pin, GPIO.OUT) def on(self): @@ -14,7 +20,36 @@ class OutputDevice(object): class LED(OutputDevice): - pass + def __init__(self, pin=None): + super(LED, self).__init__(pin) + self._blink_thread = None + + def blink(self, on_time, off_time): + self._stop_blink() + self._blink_thread = GPIOThread(target=self._blink_led, args=(on_time, off_time)) + self._blink_thread.start() + + def _stop_blink(self): + if self._blink_thread: + self._blink_thread.stop() + self._blink_thread = None + + def _blink_led(self, on_time, off_time): + while True: + super(LED, self).on() + if self._blink_thread.stopping.wait(on_time): + break + super(LED, self).off() + if self._blink_thread.stopping.wait(off_time): + break + + def on(self): + self._stop_blink() + super(LED, self).on() + + def off(self): + self._stop_blink() + super(LED, self).off() class Buzzer(OutputDevice):