mirror of
https://github.com/KevinMidboe/python-gpiozero.git
synced 2025-10-29 09:40:36 +00:00
777 lines
24 KiB
Python
777 lines
24 KiB
Python
from __future__ import (
|
|
unicode_literals,
|
|
print_function,
|
|
absolute_import,
|
|
division,
|
|
)
|
|
|
|
import warnings
|
|
from time import sleep
|
|
from threading import Lock
|
|
from itertools import repeat, cycle, chain
|
|
|
|
from .exc import OutputDeviceBadValue, GPIOPinMissing, GPIODeviceClosed
|
|
from .devices import GPIODevice, GPIOThread, CompositeDevice, SourceMixin
|
|
|
|
|
|
class OutputDevice(SourceMixin, GPIODevice):
|
|
"""
|
|
Represents a generic GPIO output device.
|
|
|
|
This class extends :class:`GPIODevice` to add facilities common to GPIO
|
|
output devices: an :meth:`on` method to switch the device on, and a
|
|
corresponding :meth:`off` method.
|
|
|
|
:param int pin:
|
|
The GPIO pin (in BCM numbering) that the device is connected to. If
|
|
this is ``None`` a :exc:`GPIOPinMissing` will be raised.
|
|
|
|
: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 ``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, initial_value=False):
|
|
self._active_high = active_high
|
|
super(OutputDevice, self).__init__(pin)
|
|
self._active_state = True if active_high else False
|
|
self._inactive_state = False if active_high else True
|
|
if initial_value is None:
|
|
self.pin.function = 'output'
|
|
elif initial_value:
|
|
self.pin.output_with_state(self._active_state)
|
|
else:
|
|
self.pin.output_with_state(self._inactive_state)
|
|
|
|
def _write(self, value):
|
|
if not self.active_high:
|
|
value = not value
|
|
try:
|
|
self.pin.state = bool(value)
|
|
except AttributeError:
|
|
self._check_open()
|
|
raise
|
|
|
|
def on(self):
|
|
"""
|
|
Turns the device on.
|
|
"""
|
|
self._write(True)
|
|
|
|
def off(self):
|
|
"""
|
|
Turns the device off.
|
|
"""
|
|
self._write(False)
|
|
|
|
@property
|
|
def value(self):
|
|
return super(OutputDevice, self).value
|
|
|
|
@value.setter
|
|
def value(self, value):
|
|
self._write(value)
|
|
|
|
@property
|
|
def active_high(self):
|
|
return self._active_high
|
|
|
|
def __repr__(self):
|
|
try:
|
|
return '<gpiozero.%s object on pin %r, active_high=%s, is_active=%s>' % (
|
|
self.__class__.__name__, self.pin, self.active_high, self.is_active)
|
|
except:
|
|
return super(OutputDevice, self).__repr__()
|
|
|
|
|
|
class DigitalOutputDevice(OutputDevice):
|
|
"""
|
|
Represents a generic output device with typical on/off behaviour.
|
|
|
|
This class extends :class:`OutputDevice` with a :meth:`toggle` method to
|
|
switch the device between its on and off states, and a :meth:`blink` method
|
|
which uses an optional background thread to handle toggling the device
|
|
state without further interaction.
|
|
"""
|
|
def __init__(self, pin=None, active_high=True, initial_value=False):
|
|
self._blink_thread = None
|
|
super(DigitalOutputDevice, self).__init__(pin, active_high, initial_value)
|
|
self._lock = Lock()
|
|
|
|
def close(self):
|
|
self._stop_blink()
|
|
super(DigitalOutputDevice, self).close()
|
|
|
|
def on(self):
|
|
self._stop_blink()
|
|
self._write(True)
|
|
|
|
def off(self):
|
|
self._stop_blink()
|
|
self._write(False)
|
|
|
|
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 blink(self, on_time=1, off_time=1, 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 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_device, args=(on_time, off_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_device(self, on_time, off_time, n):
|
|
iterable = repeat(0) if n is None else repeat(0, n)
|
|
for i in iterable:
|
|
self._write(True)
|
|
if self._blink_thread.stopping.wait(on_time):
|
|
break
|
|
self._write(False)
|
|
if self._blink_thread.stopping.wait(off_time):
|
|
break
|
|
|
|
|
|
class LED(DigitalOutputDevice):
|
|
"""
|
|
Extends :class:`DigitalOutputDevice` and represents a light emitting diode
|
|
(LED).
|
|
|
|
Connect the cathode (short leg, flat side) of the LED to a ground pin;
|
|
connect the anode (longer leg) to a limiting resistor; connect the other
|
|
side of the limiting resistor to a GPIO pin (the limiting resistor can be
|
|
placed either side of the LED).
|
|
|
|
The following example will light the LED::
|
|
|
|
from gpiozero import LED
|
|
|
|
led = LED(17)
|
|
led.on()
|
|
|
|
:param int pin:
|
|
The GPIO pin which the LED is attached to. See :doc:`notes` for valid
|
|
pin numbers.
|
|
|
|
:param bool active_high:
|
|
If ``True`` (the default), the LED will operate normally with the
|
|
circuit described above. If ``False`` you should wire the cathode to
|
|
the GPIO pin, and the anode to a 3V3 pin (via a limiting resistor).
|
|
|
|
:param bool initial_value:
|
|
If ``False`` (the default), the LED will be off initially. If
|
|
``None``, the LED will be left in whatever state the pin is found in
|
|
when configured for output (warning: this can be on). If ``True``, the
|
|
LED will be switched on initially.
|
|
"""
|
|
pass
|
|
|
|
LED.is_lit = LED.is_active
|
|
|
|
|
|
class Buzzer(DigitalOutputDevice):
|
|
"""
|
|
Extends :class:`DigitalOutputDevice` and represents a digital buzzer
|
|
component.
|
|
|
|
Connect the cathode (negative pin) of the buzzer to a ground pin; connect
|
|
the other side to any GPIO pin.
|
|
|
|
The following example will sound the buzzer::
|
|
|
|
from gpiozero import Buzzer
|
|
|
|
bz = Buzzer(3)
|
|
bz.on()
|
|
|
|
:param int pin:
|
|
The GPIO pin which the buzzer is attached to. See :doc:`notes` for
|
|
valid pin numbers.
|
|
|
|
:param bool active_high:
|
|
If ``True`` (the default), the buzzer will operate normally with the
|
|
circuit described above. If ``False`` you should wire the cathode to
|
|
the GPIO pin, and the anode to a 3V3 pin.
|
|
|
|
:param bool initial_value:
|
|
If ``False`` (the default), the buzzer will be silent initially. If
|
|
``None``, the buzzer will be left in whatever state the pin is found in
|
|
when configured for output (warning: this can be on). If ``True``, the
|
|
buzzer will be switched on initially.
|
|
"""
|
|
pass
|
|
|
|
Buzzer.beep = Buzzer.blink
|
|
|
|
|
|
class PWMOutputDevice(OutputDevice):
|
|
"""
|
|
Generic output device configured for pulse-width modulation (PWM).
|
|
|
|
:param int pin:
|
|
The GPIO pin which the device is attached to. See :doc:`notes` for
|
|
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:
|
|
The frequency (in Hz) of pulses emitted to drive the device. Defaults
|
|
to 100Hz.
|
|
"""
|
|
def __init__(self, pin=None, active_high=True, initial_value=0, frequency=100):
|
|
self._blink_thread = None
|
|
if not 0 <= initial_value <= 1:
|
|
raise OutputDeviceBadValue("initial_value must be between 0 and 1")
|
|
super(PWMOutputDevice, self).__init__(pin, active_high)
|
|
try:
|
|
# XXX need a way of setting these together
|
|
self.pin.frequency = frequency
|
|
self.value = initial_value
|
|
except:
|
|
self.close()
|
|
raise
|
|
|
|
def close(self):
|
|
self._stop_blink()
|
|
try:
|
|
self.pin.frequency = None
|
|
except AttributeError:
|
|
# If the pin's already None, ignore the exception
|
|
pass
|
|
super(PWMOutputDevice, self).close()
|
|
|
|
def _read(self):
|
|
self._check_open()
|
|
if self.active_high:
|
|
return self.pin.state
|
|
else:
|
|
return 1 - self.pin.state
|
|
|
|
def _write(self, value):
|
|
if not self.active_high:
|
|
value = 1 - value
|
|
if not 0 <= value <= 1:
|
|
raise OutputDeviceBadValue("PWM value must be between 0 and 1")
|
|
try:
|
|
self.pin.state = value
|
|
except AttributeError:
|
|
self._check_open()
|
|
raise
|
|
|
|
@property
|
|
def value(self):
|
|
"""
|
|
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.
|
|
"""
|
|
return self._read()
|
|
|
|
@value.setter
|
|
def value(self, value):
|
|
self._stop_blink()
|
|
self._write(value)
|
|
|
|
def on(self):
|
|
self._stop_blink()
|
|
self._write(1)
|
|
|
|
def off(self):
|
|
self._stop_blink()
|
|
self._write(0)
|
|
|
|
def toggle(self):
|
|
"""
|
|
Toggle the state of the device. If the device is currently off
|
|
(:attr:`value` is 0.0), this changes it to "fully" on (:attr:`value` is
|
|
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 - self.value
|
|
|
|
@property
|
|
def is_active(self):
|
|
"""
|
|
Returns ``True`` if the device is currently active (:attr:`value` is
|
|
non-zero) and ``False`` otherwise.
|
|
"""
|
|
return self.value != 0
|
|
|
|
@property
|
|
def frequency(self):
|
|
"""
|
|
The frequency of the pulses used with the PWM device, in Hz. The
|
|
default is 100Hz.
|
|
"""
|
|
return self.pin.frequency
|
|
|
|
@frequency.setter
|
|
def frequency(self, value):
|
|
self.pin.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_device,
|
|
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_device(
|
|
self, on_time, off_time, fade_in_time, fade_out_time, n, fps=50):
|
|
sequence = []
|
|
if fade_in_time > 0:
|
|
sequence += [
|
|
(i * (1 / fps) / fade_in_time, 1 / fps)
|
|
for i in range(int(fps * fade_in_time))
|
|
]
|
|
sequence.append((1, on_time))
|
|
if fade_out_time > 0:
|
|
sequence += [
|
|
(1 - (i * (1 / fps) / fade_out_time), 1 / fps)
|
|
for i in range(int(fps * fade_out_time))
|
|
]
|
|
sequence.append((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):
|
|
"""
|
|
Extends :class:`PWMOutputDevice` and represents a light emitting diode
|
|
(LED) with variable brightness.
|
|
|
|
A typical configuration of such a device is to connect a GPIO pin to the
|
|
anode (long leg) of the LED, and the cathode (short leg) to ground, with
|
|
an optional resistor to prevent the LED from burning out.
|
|
|
|
:param int pin:
|
|
The GPIO pin which the LED is attached to. See :doc:`notes` for
|
|
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 LED will be off initially. Other values
|
|
between 0 and 1 can be specified as an initial brightness for the LED.
|
|
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:
|
|
The frequency (in Hz) of pulses emitted to drive the LED. Defaults
|
|
to 100Hz.
|
|
"""
|
|
pass
|
|
|
|
PWMLED.is_lit = PWMLED.is_active
|
|
|
|
|
|
def _led_property(index, doc=None):
|
|
def getter(self):
|
|
return self._leds[index].value
|
|
def setter(self, value):
|
|
self._stop_blink()
|
|
self._leds[index].value = value
|
|
return property(getter, setter, doc=doc)
|
|
|
|
|
|
class RGBLED(SourceMixin, CompositeDevice):
|
|
"""
|
|
Extends :class:`CompositeDevice` and represents a full color LED component
|
|
(composed of red, green, and blue LEDs).
|
|
|
|
Connect the common cathode (longest leg) to a ground pin; connect each of
|
|
the other legs (representing the red, green, and blue anodes) to any GPIO
|
|
pins. You can either use three limiting resistors (one per anode) or a
|
|
single limiting resistor on the cathode.
|
|
|
|
The following code will make the LED purple::
|
|
|
|
from gpiozero import RGBLED
|
|
|
|
led = RGBLED(2, 3, 4)
|
|
led.color = (1, 0, 1)
|
|
|
|
:param int red:
|
|
The GPIO pin that controls the red component of the RGB LED.
|
|
|
|
:param int green:
|
|
The GPIO pin that controls the green component of the RGB LED.
|
|
|
|
:param int blue:
|
|
The GPIO pin that controls the blue component of the RGB LED.
|
|
|
|
:param bool active_high:
|
|
Set to ``True`` (the default) for common cathode RGB LEDs. If you are
|
|
using a common anode RGB LED, set this to ``False``.
|
|
|
|
:param bool initial_value:
|
|
The initial color for the LED. Defaults to black ``(0, 0, 0)``.
|
|
"""
|
|
def __init__(
|
|
self, red=None, green=None, blue=None, active_high=True,
|
|
initial_value=(0, 0, 0)):
|
|
self._leds = ()
|
|
self._blink_thread = None
|
|
if not all([red, green, blue]):
|
|
raise GPIOPinMissing('red, green, and blue pins must be provided')
|
|
super(RGBLED, self).__init__()
|
|
self._leds = tuple(PWMLED(pin, active_high) for pin in (red, green, blue))
|
|
self.value = initial_value
|
|
|
|
red = _led_property(0)
|
|
green = _led_property(1)
|
|
blue = _led_property(2)
|
|
|
|
@property
|
|
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.
|
|
|
|
For example, purple would be ``(1, 0, 1)`` and yellow would be ``(1, 1,
|
|
0)``, while orange would be ``(1, 0.5, 0)``.
|
|
"""
|
|
return (self.red, self.green, self.blue)
|
|
|
|
@value.setter
|
|
def value(self, value):
|
|
self.red, self.green, self.blue = value
|
|
|
|
@property
|
|
def is_active(self):
|
|
"""
|
|
Returns ``True`` if the LED is currently active (not black) and
|
|
``False`` otherwise.
|
|
"""
|
|
return self.value != (0, 0, 0)
|
|
|
|
is_lit = is_active
|
|
color = value
|
|
|
|
def on(self):
|
|
"""
|
|
Turn the LED on. This equivalent to setting the LED color to white
|
|
``(1, 1, 1)``.
|
|
"""
|
|
self.value = (1, 1, 1)
|
|
|
|
def off(self):
|
|
"""
|
|
Turn the LED off. This is equivalent to setting the LED color to black
|
|
``(0, 0, 0)``.
|
|
"""
|
|
self.value = (0, 0, 0)
|
|
|
|
def toggle(self):
|
|
"""
|
|
Toggle the state of the device. If the device is currently off
|
|
(:attr:`value` is ``(0, 0, 0)``), this changes it to "fully" on
|
|
(:attr:`value` is ``(1, 1, 1)``). If the device has a specific color,
|
|
this method inverts the color.
|
|
"""
|
|
r, g, b = self.value
|
|
self.value = (1 - r, 1 - g, 1 - b)
|
|
|
|
def close(self):
|
|
self._stop_blink()
|
|
for led in self._leds:
|
|
led.close()
|
|
|
|
def blink(
|
|
self, on_time=1, off_time=1, fade_in_time=0, fade_out_time=0,
|
|
on_color=(1, 1, 1), off_color=(0, 0, 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 tuple on_color:
|
|
The color to use when the LED is "on". Defaults to white.
|
|
|
|
:param tuple off_color:
|
|
The color to use when the LED is "off". Defaults to black.
|
|
|
|
: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_device,
|
|
args=(on_time, off_time, fade_in_time, fade_out_time, on_color, off_color, 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_device(
|
|
self, on_time, off_time, fade_in_time, fade_out_time, on_color,
|
|
off_color, n, fps=50):
|
|
# Define some simple lambdas to perform linear interpolation between
|
|
# off_color and on_color
|
|
lerp = lambda t, fade_in: tuple(
|
|
(1 - t) * off + t * on
|
|
if fade_in else
|
|
(1 - t) * on + t * off
|
|
for off, on in zip(off_color, on_color)
|
|
)
|
|
sequence = []
|
|
if fade_in_time > 0:
|
|
sequence += [
|
|
(lerp(i * (1 / fps) / fade_in_time, True), 1 / fps)
|
|
for i in range(int(fps * fade_in_time))
|
|
]
|
|
sequence.append((on_color, on_time))
|
|
if fade_out_time > 0:
|
|
sequence += [
|
|
(lerp(i * (1 / fps) / fade_out_time, False), 1 / fps)
|
|
for i in range(int(fps * fade_out_time))
|
|
]
|
|
sequence.append((off_color, off_time))
|
|
sequence = (
|
|
cycle(sequence) if n is None else
|
|
chain.from_iterable(repeat(sequence, n))
|
|
)
|
|
for value, delay in sequence:
|
|
self._leds[0].value, self._leds[1].value, self._leds[2].value = value
|
|
if self._blink_thread.stopping.wait(delay):
|
|
break
|
|
|
|
|
|
class Motor(SourceMixin, CompositeDevice):
|
|
"""
|
|
Extends :class:`CompositeDevice` and represents a generic motor connected
|
|
to a bi-directional motor driver circuit (i.e. an `H-bridge`_).
|
|
|
|
Attach an `H-bridge`_ motor controller to your Pi; connect a power source
|
|
(e.g. a battery pack or the 5V pin) to the controller; connect the outputs
|
|
of the controller board to the two terminals of the motor; connect the
|
|
inputs of the controller board to two GPIO pins.
|
|
|
|
.. _H-bridge: https://en.wikipedia.org/wiki/H_bridge
|
|
|
|
The following code will make the motor turn "forwards"::
|
|
|
|
from gpiozero import Motor
|
|
|
|
motor = Motor(17, 18)
|
|
motor.forward()
|
|
|
|
:param int forward:
|
|
The GPIO pin that the forward input of the motor driver chip is
|
|
connected to.
|
|
|
|
:param int backward:
|
|
The GPIO pin that the backward input of the motor driver chip is
|
|
connected to.
|
|
"""
|
|
def __init__(self, forward=None, backward=None):
|
|
if not all([forward, backward]):
|
|
raise GPIOPinMissing(
|
|
'forward and backward pins must be provided'
|
|
)
|
|
super(Motor, self).__init__()
|
|
self._forward = PWMOutputDevice(forward)
|
|
self._backward = PWMOutputDevice(backward)
|
|
|
|
def close(self):
|
|
self._forward.close()
|
|
self._backward.close()
|
|
|
|
@property
|
|
def closed(self):
|
|
return self._forward.closed and self._backward.closed
|
|
|
|
@property
|
|
def forward_device(self):
|
|
"""
|
|
Returns the `PWMOutputDevice` representing the forward pin of the motor
|
|
controller.
|
|
"""
|
|
return self._forward
|
|
|
|
@property
|
|
def backward_device(self):
|
|
"""
|
|
Returns the `PWMOutputDevice` representing the backward pin of the
|
|
motor controller.
|
|
"""
|
|
return self._backward
|
|
|
|
@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).
|
|
"""
|
|
return self._forward.value - self._backward.value
|
|
|
|
@value.setter
|
|
def value(self, value):
|
|
if not -1 <= value <= 1:
|
|
raise OutputDeviceBadValue("Motor value must be between -1 and 1")
|
|
if value > 0:
|
|
self.forward(value)
|
|
elif value < 0:
|
|
self.backward(-value)
|
|
else:
|
|
self.stop()
|
|
|
|
@property
|
|
def is_active(self):
|
|
"""
|
|
Returns ``True`` if the motor is currently running and ``False``
|
|
otherwise.
|
|
"""
|
|
return self.value != 0
|
|
|
|
def forward(self, speed=1):
|
|
"""
|
|
Drive the motor forwards.
|
|
|
|
:param float speed:
|
|
The speed at which the motor should turn. Can be any value between
|
|
0 (stopped) and the default 1 (maximum speed).
|
|
"""
|
|
self._backward.off()
|
|
self._forward.value = speed
|
|
|
|
def backward(self, speed=1):
|
|
"""
|
|
Drive the motor backwards.
|
|
|
|
:param float speed:
|
|
The speed at which the motor should turn. Can be any value between
|
|
0 (stopped) and the default 1 (maximum speed).
|
|
"""
|
|
self._forward.off()
|
|
self._backward.value = speed
|
|
|
|
def reverse(self):
|
|
"""
|
|
Reverse the current direction of the motor. If the motor is currently
|
|
idle this does nothing. Otherwise, the motor's direction will be
|
|
reversed at the current speed.
|
|
"""
|
|
self.value = -self.value
|
|
|
|
def stop(self):
|
|
"""
|
|
Stop the motor.
|
|
"""
|
|
self._forward.off()
|
|
self._backward.off()
|