Merge pull request #157 from waveform80/output-initial-value

Fix #118
This commit is contained in:
Dave Jones
2016-01-31 18:05:51 +00:00

View File

@@ -32,8 +32,14 @@ class OutputDevice(SourceMixin, GPIODevice):
If ``True`` (the default), the :meth:`on` method will set the GPIO to If ``True`` (the default), the :meth:`on` method will set the GPIO to
HIGH. If ``False``, the :meth:`on` method will set the GPIO to LOW (the HIGH. If ``False``, the :meth:`on` method will set the GPIO to LOW (the
:meth:`off` method always does the opposite). :meth:`off` method always does the opposite).
:param bool initial_value:
If ``False`` (the default), the device will be off initially. If
``None``, the device will be left in whatever state the pin is found in
when configured for output (warning: this can be on). If ``True``, the
device will be switched on initially.
""" """
def __init__(self, pin=None, active_high=True): def __init__(self, pin=None, active_high=True, initial_value=False):
self._active_high = active_high self._active_high = active_high
super(OutputDevice, self).__init__(pin) super(OutputDevice, self).__init__(pin)
self._active_state = GPIO.HIGH if active_high else GPIO.LOW self._active_state = GPIO.HIGH if active_high else GPIO.LOW
@@ -42,6 +48,12 @@ class OutputDevice(SourceMixin, GPIODevice):
# NOTE: catch_warnings isn't thread-safe but hopefully no-one's # NOTE: catch_warnings isn't thread-safe but hopefully no-one's
# messing around with GPIO init within background threads... # messing around with GPIO init within background threads...
with warnings.catch_warnings(record=True) as w: with warnings.catch_warnings(record=True) as w:
# This is horrid, but we can't specify initial=None with setup
if initial_value is None:
GPIO.setup(pin, GPIO.OUT)
else:
GPIO.setup(pin, GPIO.OUT, initial=
[self._inactive_state, self._active_state][bool(initial_value)])
GPIO.setup(pin, GPIO.OUT) GPIO.setup(pin, GPIO.OUT)
# The only warning we want to squash is a RuntimeWarning that is # The only warning we want to squash is a RuntimeWarning that is
# thrown when setting pins 2 or 3. Anything else should be replayed # thrown when setting pins 2 or 3. Anything else should be replayed
@@ -56,6 +68,8 @@ class OutputDevice(SourceMixin, GPIODevice):
raise raise
def _write(self, value): def _write(self, value):
if not self.active_high:
value = not value
try: try:
GPIO.output(self.pin, bool(value)) GPIO.output(self.pin, bool(value))
except ValueError: except ValueError:
@@ -66,13 +80,13 @@ class OutputDevice(SourceMixin, GPIODevice):
""" """
Turns the device on. Turns the device on.
""" """
self._write(self._active_state) self._write(True)
def off(self): def off(self):
""" """
Turns the device off. Turns the device off.
""" """
self._write(self._inactive_state) self._write(False)
@property @property
def value(self): def value(self):
@@ -103,9 +117,9 @@ class DigitalOutputDevice(OutputDevice):
which uses an optional background thread to handle toggling the device which uses an optional background thread to handle toggling the device
state without further interaction. state without further interaction.
""" """
def __init__(self, pin=None, active_high=True): def __init__(self, pin=None, active_high=True, initial_value=False):
self._blink_thread = None self._blink_thread = None
super(DigitalOutputDevice, self).__init__(pin, active_high) super(DigitalOutputDevice, self).__init__(pin, active_high, initial_value)
self._lock = Lock() self._lock = Lock()
def close(self): def close(self):
@@ -114,11 +128,11 @@ class DigitalOutputDevice(OutputDevice):
def on(self): def on(self):
self._stop_blink() self._stop_blink()
self._write(self._active_state) self._write(True)
def off(self): def off(self):
self._stop_blink() self._stop_blink()
self._write(self._inactive_state) self._write(False)
def toggle(self): def toggle(self):
""" """
@@ -167,10 +181,10 @@ class DigitalOutputDevice(OutputDevice):
def _blink_led(self, on_time, off_time, n): def _blink_led(self, on_time, off_time, n):
iterable = repeat(0) if n is None else repeat(0, n) iterable = repeat(0) if n is None else repeat(0, n)
for i in iterable: for i in iterable:
self._write(self._active_state) self._write(True)
if self._blink_thread.stopping.wait(on_time): if self._blink_thread.stopping.wait(on_time):
break break
self._write(self._inactive_state) self._write(False)
if self._blink_thread.stopping.wait(off_time): if self._blink_thread.stopping.wait(off_time):
break break
@@ -241,19 +255,34 @@ class PWMOutputDevice(OutputDevice):
The GPIO pin which the device is attached to. See :doc:`notes` for The GPIO pin which the device is attached to. See :doc:`notes` for
valid pin numbers. valid pin numbers.
:param bool active_high:
If ``True`` (the default), the :meth:`on` method will set the GPIO to
HIGH. If ``False``, the :meth:`on` method will set the GPIO to LOW (the
:meth:`off` method always does the opposite).
:param bool initial_value:
If ``0`` (the default), the device's duty cycle will be 0 initially.
Other values between 0 and 1 can be specified as an initial duty cycle.
Note that ``None`` cannot be specified (unlike the parent class) as
there is no way to tell PWM not to alter the state of the pin.
:param int frequency: :param int frequency:
The frequency (in Hz) of pulses emitted to drive the device. Defaults The frequency (in Hz) of pulses emitted to drive the device. Defaults
to 100Hz. to 100Hz.
""" """
def __init__(self, pin=None, active_high=True, frequency=100): def __init__(self, pin=None, active_high=True, initial_value=0, frequency=100):
self._pwm = None self._pwm = None
self._blink_thread = None self._blink_thread = None
if not 0 <= initial_value <= 1:
raise OutputDeviceError("initial_value must be between 0 and 1")
super(PWMOutputDevice, self).__init__(pin, active_high) super(PWMOutputDevice, self).__init__(pin, active_high)
try: try:
self._pwm = GPIO.PWM(self.pin, frequency) self._pwm = GPIO.PWM(self.pin, frequency)
self._pwm.start(0.0) self._value = initial_value
if not active_high:
initial_value = 1 - initial_value
self._pwm.start(100 * initial_value)
self._frequency = frequency self._frequency = frequency
self._value = 0.0
except: except:
self.close() self.close()
raise raise
@@ -271,9 +300,14 @@ class PWMOutputDevice(OutputDevice):
def _read(self): def _read(self):
self._check_open() self._check_open()
return self._value if self.active_high:
return self._value
else:
return 1 - self._value
def _write(self, value): def _write(self, value):
if not self.active_high:
value = 1 - value
if not 0 <= value <= 1: if not 0 <= value <= 1:
raise OutputDeviceError("PWM value must be between 0 and 1") raise OutputDeviceError("PWM value must be between 0 and 1")
try: try:
@@ -289,26 +323,20 @@ class PWMOutputDevice(OutputDevice):
The duty cycle of the PWM device. 0.0 is off, 1.0 is fully on. Values The duty cycle of the PWM device. 0.0 is off, 1.0 is fully on. Values
in between may be specified for varying levels of power in the device. in between may be specified for varying levels of power in the device.
""" """
if self.active_high: return self._read()
return self._read()
else:
return 1 - self._read()
@value.setter @value.setter
def value(self, value): def value(self, value):
self._stop_blink() self._stop_blink()
if self._active_high: self._write(value)
self._write(value)
else:
self._write(1 - value)
def on(self): def on(self):
self._stop_blink() self._stop_blink()
self._write(self._active_state) self._write(1)
def off(self): def off(self):
self._stop_blink() self._stop_blink()
self._write(self._inactive_state) self._write(0)
def toggle(self): def toggle(self):
""" """
@@ -386,20 +414,18 @@ class PWMOutputDevice(OutputDevice):
def _blink_led( def _blink_led(
self, on_time, off_time, fade_in_time, fade_out_time, n, fps=50): self, on_time, off_time, fade_in_time, fade_out_time, n, fps=50):
sequence = [] sequence = []
if fade_in_time > 0.0: if fade_in_time > 0:
sequence += [ sequence += [
(i * (1 / fps) / fade_in_time, 1 / fps) (i * (1 / fps) / fade_in_time, 1 / fps)
for i in range(int(fps * fade_in_time)) for i in range(int(fps * fade_in_time))
] ]
sequence.append((1.0, on_time)) sequence.append((1, on_time))
if fade_out_time > 0.0: if fade_out_time > 0:
sequence += [ sequence += [
(1 - (i * (1 / fps) / fade_out_time), 1 / fps) (1 - (i * (1 / fps) / fade_out_time), 1 / fps)
for i in range(int(fps * fade_out_time)) for i in range(int(fps * fade_out_time))
] ]
sequence.append((0.0, off_time)) sequence.append((0, off_time))
if not self.active_high:
sequence = [(1 - value, delay) for (value, delay) in sequence]
sequence = ( sequence = (
cycle(sequence) if n is None else cycle(sequence) if n is None else
chain.from_iterable(repeat(sequence, n)) chain.from_iterable(repeat(sequence, n))