mirror of
https://github.com/KevinMidboe/python-gpiozero.git
synced 2025-10-29 17:50:37 +00:00
Fix #459 - properly support remote SPI with pigpio
Sorry! Dave's messing around with the pin implementations again. Hopefully the last time. The pin_factory is now really a factory object which can be asked to produce individual pins or pin-based interfaces like SPI (which can be supported properly via pigpio).
This commit is contained in:
@@ -59,6 +59,20 @@ Errors
|
|||||||
|
|
||||||
.. autoexception:: SPIBadArgs
|
.. autoexception:: SPIBadArgs
|
||||||
|
|
||||||
|
.. autoexception:: SPIBadChannel
|
||||||
|
|
||||||
|
.. autoexception:: SPIFixedClockMode
|
||||||
|
|
||||||
|
.. autoexception:: SPIInvalidClockMode
|
||||||
|
|
||||||
|
.. autoexception:: SPIFixedBitOrder
|
||||||
|
|
||||||
|
.. autoexception:: SPIFixedSelect
|
||||||
|
|
||||||
|
.. autoexception:: SPIFixedWordSize
|
||||||
|
|
||||||
|
.. autoexception:: SPIInvalidWordSize
|
||||||
|
|
||||||
.. autoexception:: GPIODeviceError
|
.. autoexception:: GPIODeviceError
|
||||||
|
|
||||||
.. autoexception:: GPIODeviceClosed
|
.. autoexception:: GPIODeviceClosed
|
||||||
@@ -83,23 +97,31 @@ Errors
|
|||||||
|
|
||||||
.. autoexception:: PinInvalidEdges
|
.. autoexception:: PinInvalidEdges
|
||||||
|
|
||||||
|
.. autoexception:: PinInvalidBounce
|
||||||
|
|
||||||
.. autoexception:: PinSetInput
|
.. autoexception:: PinSetInput
|
||||||
|
|
||||||
.. autoexception:: PinFixedPull
|
.. autoexception:: PinFixedPull
|
||||||
|
|
||||||
.. autoexception:: PinEdgeDetectUnsupported
|
.. autoexception:: PinEdgeDetectUnsupported
|
||||||
|
|
||||||
|
.. autoexception:: PinGPIOUnsupported
|
||||||
|
|
||||||
|
.. autoexception:: PinSPIUnsupported
|
||||||
|
|
||||||
.. autoexception:: PinPWMError
|
.. autoexception:: PinPWMError
|
||||||
|
|
||||||
.. autoexception:: PinPWMUnsupported
|
.. autoexception:: PinPWMUnsupported
|
||||||
|
|
||||||
.. autoexception:: PinPWMFixedValue
|
.. autoexception:: PinPWMFixedValue
|
||||||
|
|
||||||
|
.. autoexception:: PinUnknownPi
|
||||||
|
|
||||||
.. autoexception:: PinMultiplePins
|
.. autoexception:: PinMultiplePins
|
||||||
|
|
||||||
.. autoexception:: PinNoPins
|
.. autoexception:: PinNoPins
|
||||||
|
|
||||||
.. autoexception:: PinUnknownPi
|
.. autoexception:: PinInvalidPin
|
||||||
|
|
||||||
Warnings
|
Warnings
|
||||||
========
|
========
|
||||||
@@ -110,3 +132,7 @@ Warnings
|
|||||||
|
|
||||||
.. autoexception:: SPISoftwareFallback
|
.. autoexception:: SPISoftwareFallback
|
||||||
|
|
||||||
|
.. autoexception:: PinFactoryFallback
|
||||||
|
|
||||||
|
.. autoexception:: PinNonPhysical
|
||||||
|
|
||||||
|
|||||||
@@ -11,68 +11,68 @@ are concerned with. However, some users may wish to take advantage of the
|
|||||||
capabilities of alternative GPIO implementations or (in future) use GPIO
|
capabilities of alternative GPIO implementations or (in future) use GPIO
|
||||||
extender chips. This is the purpose of the pins portion of the library.
|
extender chips. This is the purpose of the pins portion of the library.
|
||||||
|
|
||||||
When you construct a device, you pass in a GPIO pin number. However, what the
|
When you construct a device, you pass in a pin specification. However, what the
|
||||||
library actually expects is a :class:`Pin` implementation. If it finds a simple
|
library actually expects is a :class:`Pin` implementation. If it finds anything
|
||||||
integer number instead, it uses one of the following classes to provide the
|
else, it uses the existing ``Device._pin_factory`` to construct a :class:`Pin`
|
||||||
:class:`Pin` implementation (classes are listed in favoured order):
|
implementation based on the specification.
|
||||||
|
|
||||||
1. :class:`gpiozero.pins.rpigpio.RPiGPIOPin`
|
Changing the pin factory
|
||||||
|
========================
|
||||||
|
|
||||||
2. :class:`gpiozero.pins.rpio.RPIOPin`
|
The default pin factory can be replaced by specifying a value for the
|
||||||
|
``GPIOZERO_PIN_FACTORY`` environment variable. For example:
|
||||||
|
|
||||||
3. :class:`gpiozero.pins.pigpiod.PiGPIOPin`
|
.. code-block:: console
|
||||||
|
|
||||||
4. :class:`gpiozero.pins.native.NativePin`
|
pi@raspberrypi $ GPIOZERO_PIN_FACTORY=native python
|
||||||
|
Python 3.4.2 (default, Oct 19 2014, 13:31:11)
|
||||||
|
[GCC 4.9.1] on linux
|
||||||
|
Type "help", "copyright", "credits" or "license" for more information.
|
||||||
|
>>> import gpiozero
|
||||||
|
>>> gpiozero.Device._pin_factory
|
||||||
|
<gpiozero.pins.native.NativeFactory object at 0x762c26b0>
|
||||||
|
|
||||||
You can change the default pin implementation by over-writing the
|
The following values, and the corresponding :class:`Factory` and :class:`Pin`
|
||||||
``pin_factory`` global in the ``devices`` module like so::
|
classes are listed in the table below. Factories are listed in the order that
|
||||||
|
they are tried by default.
|
||||||
|
|
||||||
from gpiozero.pins.native import NativePin
|
+---------+-----------------------------------------------+-------------------------------------------+
|
||||||
import gpiozero.devices
|
| Name | Factory class | Pin class |
|
||||||
# Force the default pin implementation to be NativePin
|
+=========+===============================================+===========================================+
|
||||||
gpiozero.devices.pin_factory = NativePin
|
| rpigpio | :class:`gpiozero.pins.rpigpio.RPiGPIOFactory` | :class:`gpiozero.pins.rpigpio.RPiGPIOPin` |
|
||||||
|
+---------+-----------------------------------------------+-------------------------------------------+
|
||||||
|
| rpio | :class:`gpiozero.pins.rpio.RPIOFactory` | :class:`gpiozero.pins.rpio.RPIOPin` |
|
||||||
|
+---------+-----------------------------------------------+-------------------------------------------+
|
||||||
|
| pigpio | :class:`gpiozero.pins.pigpiod.PiGPIOFactory` | :class:`gpiozero.pins.pigpiod.PiGPIOPin` |
|
||||||
|
+---------+-----------------------------------------------+-------------------------------------------+
|
||||||
|
| native | :class:`gpiozero.pins.native.NativeFactory` | :class:`gpiozero.pins.native.NativePin` |
|
||||||
|
+---------+-----------------------------------------------+-------------------------------------------+
|
||||||
|
|
||||||
|
If you need to change the default pin factory from within a script, use the
|
||||||
|
``Device._set_pin_factory`` class method, passing in the instance of the new
|
||||||
|
factory to use. This is only supported at script startup (replacing the factory
|
||||||
|
closes all existing pin instances which can have interesting consequences for
|
||||||
|
any devices using them)::
|
||||||
|
|
||||||
|
from gpiozero.pins.native import NativeFactory
|
||||||
|
from gpiozero import *
|
||||||
|
Device._set_pin_factory(NativeFactory())
|
||||||
|
|
||||||
from gpiozero import LED
|
from gpiozero import LED
|
||||||
|
|
||||||
# This will now use NativePin instead of RPiGPIOPin
|
# This will now use NativePin instead of RPiGPIOPin
|
||||||
led = LED(16)
|
led = LED(16)
|
||||||
|
|
||||||
``pin_factory`` is a concrete descendent of the abstract :class:`Pin` class.
|
Certain factories may take default information from additional sources.
|
||||||
The descendent may take additional parameters in its constructor provided they
|
|
||||||
are optional; GPIO Zero will expect to be able to construct instances with
|
|
||||||
nothing more than an integer pin number.
|
|
||||||
|
|
||||||
However, the descendent may take default information from additional sources.
|
|
||||||
For example, to default to creating pins with
|
For example, to default to creating pins with
|
||||||
:class:`gpiozero.pins.pigpiod.PiGPIOPin` on a remote pi called ``remote-pi``
|
:class:`gpiozero.pins.pigpiod.PiGPIOPin` on a remote pi called ``remote-pi``
|
||||||
you can set the :envvar:`PIGPIO_ADDR` environment variable when running your
|
you can set the :envvar:`PIGPIO_ADDR` environment variable when running your
|
||||||
script::
|
script:
|
||||||
|
|
||||||
$ PIGPIO_ADDR=remote-pi python my_script.py
|
.. code-block:: console
|
||||||
|
|
||||||
It is worth noting that instead of passing an integer to device constructors,
|
$ export GPIOZERO_PIN_FACTORY=pigpio
|
||||||
you can pass an object derived from :class:`Pin` itself::
|
$ PIGPIO_ADDR=remote-pi python3 my_script.py
|
||||||
|
|
||||||
from gpiozero.pins.native import NativePin
|
|
||||||
from gpiozero import LED
|
|
||||||
|
|
||||||
led = LED(NativePin(16))
|
|
||||||
|
|
||||||
In future, this separation of pins and devices should also permit the library
|
|
||||||
to utilize pins that are part of IO extender chips. For example::
|
|
||||||
|
|
||||||
from gpiozero import IOExtender, LED
|
|
||||||
|
|
||||||
ext = IOExtender()
|
|
||||||
led = LED(ext.pins[0])
|
|
||||||
led.on()
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
While the devices API is now considered stable and won't change in
|
|
||||||
backwards incompatible ways, the pins API is *not* yet considered stable.
|
|
||||||
It is potentially subject to change in future versions. We welcome any
|
|
||||||
comments from testers!
|
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
@@ -83,41 +83,48 @@ to utilize pins that are part of IO extender chips. For example::
|
|||||||
actual raspberry pie, you have only yourself to blame.
|
actual raspberry pie, you have only yourself to blame.
|
||||||
|
|
||||||
|
|
||||||
RPiGPIOPin
|
RPi.GPIO
|
||||||
==========
|
========
|
||||||
|
|
||||||
|
.. autoclass:: gpiozero.pins.rpigpio.RPiGPIOFactory
|
||||||
|
|
||||||
.. autoclass:: gpiozero.pins.rpigpio.RPiGPIOPin
|
.. autoclass:: gpiozero.pins.rpigpio.RPiGPIOPin
|
||||||
|
|
||||||
|
|
||||||
RPIOPin
|
RPIO
|
||||||
=======
|
====
|
||||||
|
|
||||||
|
.. autoclass:: gpiozero.pins.rpio.RPIOFactory
|
||||||
|
|
||||||
.. autoclass:: gpiozero.pins.rpio.RPIOPin
|
.. autoclass:: gpiozero.pins.rpio.RPIOPin
|
||||||
|
|
||||||
|
|
||||||
PiGPIOPin
|
PiGPIO
|
||||||
=========
|
======
|
||||||
|
|
||||||
|
.. autoclass:: gpiozero.pins.pigpiod.PiGPIOFactory
|
||||||
|
|
||||||
.. autoclass:: gpiozero.pins.pigpiod.PiGPIOPin
|
.. autoclass:: gpiozero.pins.pigpiod.PiGPIOPin
|
||||||
|
|
||||||
|
|
||||||
NativePin
|
Native
|
||||||
=========
|
======
|
||||||
|
|
||||||
|
.. autoclass:: gpiozero.pins.native.NativeFactory
|
||||||
|
|
||||||
.. autoclass:: gpiozero.pins.native.NativePin
|
.. autoclass:: gpiozero.pins.native.NativePin
|
||||||
|
|
||||||
|
|
||||||
Abstract Pin
|
Base classes
|
||||||
============
|
============
|
||||||
|
|
||||||
|
.. autoclass:: Factory
|
||||||
|
:members:
|
||||||
|
|
||||||
.. autoclass:: Pin
|
.. autoclass:: Pin
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: SPI
|
||||||
Local Pin
|
|
||||||
=========
|
|
||||||
|
|
||||||
.. autoclass:: LocalPin
|
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
@@ -4,6 +4,7 @@ Notes
|
|||||||
|
|
||||||
.. currentmodule:: gpiozero
|
.. currentmodule:: gpiozero
|
||||||
|
|
||||||
|
|
||||||
.. _keep-your-script-running:
|
.. _keep-your-script-running:
|
||||||
|
|
||||||
Keep your script running
|
Keep your script running
|
||||||
@@ -46,6 +47,7 @@ events to be detected::
|
|||||||
button.when_pressed = hello
|
button.when_pressed = hello
|
||||||
pause()
|
pause()
|
||||||
|
|
||||||
|
|
||||||
Importing from GPIO Zero
|
Importing from GPIO Zero
|
||||||
========================
|
========================
|
||||||
|
|
||||||
@@ -70,12 +72,15 @@ In this case, all references to items within GPIO Zero must be prefixed::
|
|||||||
|
|
||||||
button = gpiozero.Button(2)
|
button = gpiozero.Button(2)
|
||||||
|
|
||||||
|
|
||||||
How can I tell what version of gpiozero I have installed?
|
How can I tell what version of gpiozero I have installed?
|
||||||
=========================================================
|
=========================================================
|
||||||
|
|
||||||
The gpiozero library relies on the setuptools package for installation
|
The gpiozero library relies on the setuptools package for installation
|
||||||
services. You can use the setuptools ``pkg_resources`` API to query which
|
services. You can use the setuptools ``pkg_resources`` API to query which
|
||||||
version of gpiozero is available in your Python environment like so::
|
version of gpiozero is available in your Python environment like so:
|
||||||
|
|
||||||
|
.. code-block:: pycon
|
||||||
|
|
||||||
>>> from pkg_resources import require
|
>>> from pkg_resources import require
|
||||||
>>> require('gpiozero')
|
>>> require('gpiozero')
|
||||||
@@ -89,7 +94,9 @@ the first entry in the list will be the version that ``import gpiozero`` will
|
|||||||
import.
|
import.
|
||||||
|
|
||||||
If you receive the error "No module named pkg_resources", you need to install
|
If you receive the error "No module named pkg_resources", you need to install
|
||||||
the ``pip`` utility. This can be done with the following command in Raspbian::
|
the ``pip`` utility. This can be done with the following command in Raspbian:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
$ sudo apt-get install python-pip
|
$ sudo apt-get install python-pip
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ library. Please note that all recipes are written assuming Python 3. Recipes
|
|||||||
*may* work under Python 2, but no guarantees!
|
*may* work under Python 2, but no guarantees!
|
||||||
|
|
||||||
|
|
||||||
.. _pin_numbering:
|
.. _pin-numbering:
|
||||||
|
|
||||||
Pin Numbering
|
Pin Numbering
|
||||||
=============
|
=============
|
||||||
@@ -429,7 +429,9 @@ functionality without the need to wire up your own LEDs (also useful because
|
|||||||
the power and activity LEDs are "known good").
|
the power and activity LEDs are "known good").
|
||||||
|
|
||||||
Firstly you need to disable the usual triggers for the built-in LEDs. This can
|
Firstly you need to disable the usual triggers for the built-in LEDs. This can
|
||||||
be done from the terminal with the following commands::
|
be done from the terminal with the following commands:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
$ echo none | sudo tee /sys/class/leds/led0/trigger
|
$ echo none | sudo tee /sys/class/leds/led0/trigger
|
||||||
$ echo gpio | sudo tee /sys/class/leds/led1/trigger
|
$ echo gpio | sudo tee /sys/class/leds/led1/trigger
|
||||||
@@ -439,7 +441,9 @@ Now you can control the LEDs with gpiozero like so:
|
|||||||
.. literalinclude:: examples/led_builtin.py
|
.. literalinclude:: examples/led_builtin.py
|
||||||
|
|
||||||
To revert the LEDs to their usual purpose you can either reboot your Pi or
|
To revert the LEDs to their usual purpose you can either reboot your Pi or
|
||||||
run the following commands::
|
run the following commands:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
$ echo mmc0 | sudo tee /sys/class/leds/led0/trigger
|
$ echo mmc0 | sudo tee /sys/class/leds/led0/trigger
|
||||||
$ echo input | sudo tee /sys/class/leds/led1/trigger
|
$ echo input | sudo tee /sys/class/leds/led1/trigger
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ from __future__ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from .pins import (
|
from .pins import (
|
||||||
|
Factory,
|
||||||
Pin,
|
Pin,
|
||||||
LocalPin,
|
SPI,
|
||||||
)
|
)
|
||||||
from .pins.data import (
|
from .pins.data import (
|
||||||
PiBoardInfo,
|
PiBoardInfo,
|
||||||
@@ -15,47 +16,9 @@ from .pins.data import (
|
|||||||
PinInfo,
|
PinInfo,
|
||||||
pi_info,
|
pi_info,
|
||||||
)
|
)
|
||||||
from .exc import (
|
# Yes, import * is naughty, but exc imports nothing else so there's no cross
|
||||||
GPIOZeroError,
|
# contamination here ... and besides, have you *seen* the list lately?!
|
||||||
DeviceClosed,
|
from .exc import *
|
||||||
BadEventHandler,
|
|
||||||
BadWaitTime,
|
|
||||||
BadQueueLen,
|
|
||||||
CompositeDeviceError,
|
|
||||||
CompositeDeviceBadName,
|
|
||||||
CompositeDeviceBadOrder,
|
|
||||||
CompositeDeviceBadDevice,
|
|
||||||
SPIError,
|
|
||||||
SPIBadArgs,
|
|
||||||
EnergenieSocketMissing,
|
|
||||||
EnergenieBadSocket,
|
|
||||||
GPIODeviceError,
|
|
||||||
GPIODeviceClosed,
|
|
||||||
GPIOPinInUse,
|
|
||||||
GPIOPinMissing,
|
|
||||||
InputDeviceError,
|
|
||||||
OutputDeviceError,
|
|
||||||
OutputDeviceBadValue,
|
|
||||||
PinError,
|
|
||||||
PinInvalidFunction,
|
|
||||||
PinInvalidState,
|
|
||||||
PinInvalidPull,
|
|
||||||
PinInvalidEdges,
|
|
||||||
PinSetInput,
|
|
||||||
PinFixedPull,
|
|
||||||
PinEdgeDetectUnsupported,
|
|
||||||
PinPWMError,
|
|
||||||
PinPWMUnsupported,
|
|
||||||
PinPWMFixedValue,
|
|
||||||
PinUnknownPi,
|
|
||||||
PinMultiplePins,
|
|
||||||
PinNoPins,
|
|
||||||
GPIOZeroWarning,
|
|
||||||
SPIWarning,
|
|
||||||
SPISoftwareFallback,
|
|
||||||
PinWarning,
|
|
||||||
PinNonPhysical,
|
|
||||||
)
|
|
||||||
from .devices import (
|
from .devices import (
|
||||||
Device,
|
Device,
|
||||||
GPIODevice,
|
GPIODevice,
|
||||||
|
|||||||
@@ -10,15 +10,16 @@ str = type('')
|
|||||||
import os
|
import os
|
||||||
import atexit
|
import atexit
|
||||||
import weakref
|
import weakref
|
||||||
|
import warnings
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from types import FunctionType
|
from types import FunctionType
|
||||||
from threading import RLock
|
from threading import Lock
|
||||||
|
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
|
||||||
|
from .pins import Pin
|
||||||
from .threads import _threads_shutdown
|
from .threads import _threads_shutdown
|
||||||
from .pins import _pins_shutdown
|
|
||||||
from .mixins import (
|
from .mixins import (
|
||||||
ValuesMixin,
|
ValuesMixin,
|
||||||
SharedMixin,
|
SharedMixin,
|
||||||
@@ -32,52 +33,11 @@ from .exc import (
|
|||||||
GPIOPinMissing,
|
GPIOPinMissing,
|
||||||
GPIOPinInUse,
|
GPIOPinInUse,
|
||||||
GPIODeviceClosed,
|
GPIODeviceClosed,
|
||||||
|
PinFactoryFallback,
|
||||||
)
|
)
|
||||||
from .compat import frozendict
|
from .compat import frozendict
|
||||||
|
|
||||||
|
|
||||||
def _default_pin_factory(name=os.getenv('GPIOZERO_PIN_FACTORY', None)):
|
|
||||||
group = 'gpiozero_pin_factories'
|
|
||||||
if name is None:
|
|
||||||
# If no factory is explicitly specified, try various names in
|
|
||||||
# "preferred" order. Note that in this case we only select from
|
|
||||||
# gpiozero distribution so without explicitly specifying a name (via
|
|
||||||
# the environment) it's impossible to auto-select a factory from
|
|
||||||
# outside the base distribution
|
|
||||||
#
|
|
||||||
# We prefer RPi.GPIO here as it supports PWM, and all Pi revisions. If
|
|
||||||
# no third-party libraries are available, however, we fall back to a
|
|
||||||
# pure Python implementation which supports platforms like PyPy
|
|
||||||
dist = pkg_resources.get_distribution('gpiozero')
|
|
||||||
for name in ('RPiGPIOPin', 'RPIOPin', 'PiGPIOPin', 'NativePin'):
|
|
||||||
try:
|
|
||||||
return pkg_resources.load_entry_point(dist, group, name)
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
raise BadPinFactory('Unable to locate any default pin factory!')
|
|
||||||
else:
|
|
||||||
for factory in pkg_resources.iter_entry_points(group, name):
|
|
||||||
return factory.load()
|
|
||||||
raise BadPinFactory('Unable to locate pin factory "%s"' % name)
|
|
||||||
|
|
||||||
pin_factory = _default_pin_factory()
|
|
||||||
|
|
||||||
|
|
||||||
_PINS = set()
|
|
||||||
_PINS_LOCK = RLock() # Yes, this needs to be re-entrant
|
|
||||||
|
|
||||||
def _shutdown():
|
|
||||||
_threads_shutdown()
|
|
||||||
with _PINS_LOCK:
|
|
||||||
while _PINS:
|
|
||||||
_PINS.pop().close()
|
|
||||||
# Any cleanup routines registered by pins libraries must be called *after*
|
|
||||||
# cleanup of pin objects used by devices
|
|
||||||
_pins_shutdown()
|
|
||||||
|
|
||||||
atexit.register(_shutdown)
|
|
||||||
|
|
||||||
|
|
||||||
class GPIOMeta(type):
|
class GPIOMeta(type):
|
||||||
# NOTE Yes, this is a metaclass. Don't be scared - it's a simple one.
|
# NOTE Yes, this is a metaclass. Don't be scared - it's a simple one.
|
||||||
|
|
||||||
@@ -106,7 +66,7 @@ class GPIOMeta(type):
|
|||||||
# already exists. Only construct the instance if the key's new.
|
# already exists. Only construct the instance if the key's new.
|
||||||
key = cls._shared_key(*args, **kwargs)
|
key = cls._shared_key(*args, **kwargs)
|
||||||
try:
|
try:
|
||||||
self = cls._INSTANCES[key]
|
self = cls._instances[key]
|
||||||
self._refs += 1
|
self._refs += 1
|
||||||
except (KeyError, ReferenceError) as e:
|
except (KeyError, ReferenceError) as e:
|
||||||
self = super(GPIOMeta, cls).__call__(*args, **kwargs)
|
self = super(GPIOMeta, cls).__call__(*args, **kwargs)
|
||||||
@@ -122,14 +82,14 @@ class GPIOMeta(type):
|
|||||||
old_close()
|
old_close()
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
del cls._INSTANCES[key]
|
del cls._instances[key]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# If the _refs go negative (too many closes)
|
# If the _refs go negative (too many closes)
|
||||||
# just ignore the resulting KeyError here -
|
# just ignore the resulting KeyError here -
|
||||||
# it's already gone
|
# it's already gone
|
||||||
pass
|
pass
|
||||||
self.close = close
|
self.close = close
|
||||||
cls._INSTANCES[key] = weakref.proxy(self)
|
cls._instances[key] = weakref.proxy(self)
|
||||||
else:
|
else:
|
||||||
# Construct the instance as normal
|
# Construct the instance as normal
|
||||||
self = super(GPIOMeta, cls).__call__(*args, **kwargs)
|
self = super(GPIOMeta, cls).__call__(*args, **kwargs)
|
||||||
@@ -229,13 +189,100 @@ class GPIOBase(GPIOMeta(nstr('GPIOBase'), (), {})):
|
|||||||
class Device(ValuesMixin, GPIOBase):
|
class Device(ValuesMixin, GPIOBase):
|
||||||
"""
|
"""
|
||||||
Represents a single device of any type; GPIO-based, SPI-based, I2C-based,
|
Represents a single device of any type; GPIO-based, SPI-based, I2C-based,
|
||||||
etc. This is the base class of the device hierarchy. It defines the
|
etc. This is the base class of the device hierarchy. It defines the basic
|
||||||
basic services applicable to all devices (specifically the :attr:`is_active`
|
services applicable to all devices (specifically the :attr:`is_active`
|
||||||
property, the :attr:`value` property, and the :meth:`close` method).
|
property, the :attr:`value` property, and the :meth:`close` method).
|
||||||
"""
|
"""
|
||||||
|
_pin_factory = None # instance of a Factory sub-class
|
||||||
|
_reservations = {} # maps pin addresses to lists of devices
|
||||||
|
_res_lock = Lock()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<gpiozero.%s object>" % (self.__class__.__name__)
|
return "<gpiozero.%s object>" % (self.__class__.__name__)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _set_pin_factory(cls, new_factory):
|
||||||
|
if cls._pin_factory is not None:
|
||||||
|
cls._pin_factory.close()
|
||||||
|
cls._pin_factory = new_factory
|
||||||
|
|
||||||
|
def _reserve_pins(self, *pins_or_addresses):
|
||||||
|
"""
|
||||||
|
Called to indicate that the device reserves the right to use the
|
||||||
|
specified *pins_or_addresses*. This should be done during device
|
||||||
|
construction. If pins are reserved, you must ensure that the
|
||||||
|
reservation is released by eventually called :meth:`_release_pins`.
|
||||||
|
|
||||||
|
The *pins_or_addresses* can be actual :class:`Pin` instances or the
|
||||||
|
addresses of pin instances (each address is a tuple of strings). The
|
||||||
|
latter form is permitted to ensure that devices do not have to
|
||||||
|
construct :class:`Pin` objects to reserve pins. This is important as
|
||||||
|
constructing a pin often configures it (e.g. as an input) which
|
||||||
|
conflicts with alternate pin functions like SPI.
|
||||||
|
"""
|
||||||
|
addresses = (
|
||||||
|
p.address if isinstance(p, Pin) else p
|
||||||
|
for p in pins_or_addresses
|
||||||
|
)
|
||||||
|
with self._res_lock:
|
||||||
|
for address in addresses:
|
||||||
|
try:
|
||||||
|
conflictors = self._reservations[address]
|
||||||
|
except KeyError:
|
||||||
|
conflictors = []
|
||||||
|
self._reservations[address] = conflictors
|
||||||
|
for device_ref in conflictors:
|
||||||
|
device = device_ref()
|
||||||
|
if device is not None and self._conflicts_with(device):
|
||||||
|
raise GPIOPinInUse(
|
||||||
|
'pin %s is already in use by %r' % (
|
||||||
|
'/'.join(address), device)
|
||||||
|
)
|
||||||
|
conflictors.append(weakref.ref(self))
|
||||||
|
|
||||||
|
def _release_pins(self, *pins_or_addresses):
|
||||||
|
"""
|
||||||
|
Releases the reservation of this device against *pins_or_addresses*.
|
||||||
|
This is typically called during :meth:`close` to clean up reservations
|
||||||
|
taken during construction. Releasing a reservation that is not
|
||||||
|
currently held will be silently ignored (to permit clean-up after
|
||||||
|
failed / partial construction).
|
||||||
|
"""
|
||||||
|
addresses = (
|
||||||
|
p.address if isinstance(p, Pin) else p
|
||||||
|
for p in pins_or_addresses
|
||||||
|
)
|
||||||
|
with self._res_lock:
|
||||||
|
for address in addresses:
|
||||||
|
self._reservations[address] = [
|
||||||
|
ref for ref in self._reservations[address]
|
||||||
|
if ref() not in (self, None) # may as well clean up dead refs
|
||||||
|
]
|
||||||
|
|
||||||
|
def _release_all(self):
|
||||||
|
"""
|
||||||
|
Releases all pin reservations taken out by this device. See
|
||||||
|
:meth:`_release_pins` for further information).
|
||||||
|
"""
|
||||||
|
with self._res_lock:
|
||||||
|
Device._reservations = {
|
||||||
|
address: [
|
||||||
|
ref for ref in conflictors
|
||||||
|
if ref() not in (self, None)
|
||||||
|
]
|
||||||
|
for address, conflictors in self._reservations.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
def _conflicts_with(self, other):
|
||||||
|
"""
|
||||||
|
Called by :meth:`_reserve_pin` to test whether the *other*
|
||||||
|
:class:`Device` using a common pin conflicts with this device's intent
|
||||||
|
to use it. The default is ``True`` indicating that all devices conflict
|
||||||
|
with common pins. Sub-classes may override this to permit more nuanced
|
||||||
|
replies.
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def value(self):
|
def value(self):
|
||||||
"""
|
"""
|
||||||
@@ -378,14 +425,12 @@ class GPIODevice(Device):
|
|||||||
self._pin = None
|
self._pin = None
|
||||||
if pin is None:
|
if pin is None:
|
||||||
raise GPIOPinMissing('No pin given')
|
raise GPIOPinMissing('No pin given')
|
||||||
if isinstance(pin, int):
|
if isinstance(pin, Pin):
|
||||||
pin = pin_factory(pin)
|
self._reserve_pins(pin)
|
||||||
with _PINS_LOCK:
|
else:
|
||||||
if pin in _PINS:
|
# Check you can reserve *before* constructing the pin
|
||||||
raise GPIOPinInUse(
|
self._reserve_pins(self._pin_factory.pin_address(pin))
|
||||||
'pin %r is already in use by another gpiozero object' % pin
|
pin = self._pin_factory.pin(pin)
|
||||||
)
|
|
||||||
_PINS.add(pin)
|
|
||||||
self._pin = pin
|
self._pin = pin
|
||||||
self._active_state = True
|
self._active_state = True
|
||||||
self._inactive_state = False
|
self._inactive_state = False
|
||||||
@@ -402,12 +447,10 @@ class GPIODevice(Device):
|
|||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
super(GPIODevice, self).close()
|
super(GPIODevice, self).close()
|
||||||
with _PINS_LOCK:
|
if self._pin is not None:
|
||||||
pin = self._pin
|
self._release_pins(self._pin)
|
||||||
|
self._pin.close()
|
||||||
self._pin = None
|
self._pin = None
|
||||||
if pin in _PINS:
|
|
||||||
_PINS.remove(pin)
|
|
||||||
pin.close()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def closed(self):
|
def closed(self):
|
||||||
@@ -441,3 +484,41 @@ class GPIODevice(Device):
|
|||||||
except DeviceClosed:
|
except DeviceClosed:
|
||||||
return "<gpiozero.%s object closed>" % self.__class__.__name__
|
return "<gpiozero.%s object closed>" % self.__class__.__name__
|
||||||
|
|
||||||
|
|
||||||
|
# Defined last to ensure Device is defined before attempting to load any pin
|
||||||
|
# factory; pin factories want to load spi which in turn relies on devices (for
|
||||||
|
# the soft-SPI implementation)
|
||||||
|
def _default_pin_factory(name=os.getenv('GPIOZERO_PIN_FACTORY', None)):
|
||||||
|
group = 'gpiozero_pin_factories'
|
||||||
|
if name is None:
|
||||||
|
# If no factory is explicitly specified, try various names in
|
||||||
|
# "preferred" order. Note that in this case we only select from
|
||||||
|
# gpiozero distribution so without explicitly specifying a name (via
|
||||||
|
# the environment) it's impossible to auto-select a factory from
|
||||||
|
# outside the base distribution
|
||||||
|
#
|
||||||
|
# We prefer RPi.GPIO here as it supports PWM, and all Pi revisions. If
|
||||||
|
# no third-party libraries are available, however, we fall back to a
|
||||||
|
# pure Python implementation which supports platforms like PyPy
|
||||||
|
dist = pkg_resources.get_distribution('gpiozero')
|
||||||
|
for name in ('rpigpio', 'rpio', 'pigpio', 'native'):
|
||||||
|
try:
|
||||||
|
return pkg_resources.load_entry_point(dist, group, name)()
|
||||||
|
except Exception as e:
|
||||||
|
warnings.warn(
|
||||||
|
PinFactoryFallback(
|
||||||
|
'Failed to load factory %s: %s' % (name, str(e))))
|
||||||
|
raise BadPinFactory('Unable to load any default pin factory!')
|
||||||
|
else:
|
||||||
|
for factory in pkg_resources.iter_entry_points(group, name.lower()):
|
||||||
|
return factory.load()()
|
||||||
|
raise BadPinFactory('Unable to find pin factory "%s"' % name)
|
||||||
|
|
||||||
|
Device._set_pin_factory(_default_pin_factory())
|
||||||
|
|
||||||
|
def _shutdown():
|
||||||
|
_threads_shutdown()
|
||||||
|
Device._set_pin_factory(None)
|
||||||
|
|
||||||
|
atexit.register(_shutdown)
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,24 @@ class SPIBadArgs(SPIError, ValueError):
|
|||||||
class SPIBadChannel(SPIError, ValueError):
|
class SPIBadChannel(SPIError, ValueError):
|
||||||
"Error raised when an invalid channel is given to an :class:`AnalogInputDevice`"
|
"Error raised when an invalid channel is given to an :class:`AnalogInputDevice`"
|
||||||
|
|
||||||
|
class SPIFixedClockMode(SPIError, AttributeError):
|
||||||
|
"Error raised when the SPI clock mode cannot be changed"
|
||||||
|
|
||||||
|
class SPIInvalidClockMode(SPIError, ValueError):
|
||||||
|
"Error raised when an invalid clock mode is given to an SPI implementation"
|
||||||
|
|
||||||
|
class SPIFixedBitOrder(SPIError, AttributeError):
|
||||||
|
"Error raised when the SPI bit-endianness cannot be changed"
|
||||||
|
|
||||||
|
class SPIFixedSelect(SPIError, AttributeError):
|
||||||
|
"Error raised when the SPI select polarity cannot be changed"
|
||||||
|
|
||||||
|
class SPIFixedWordSize(SPIError, AttributeError):
|
||||||
|
"Error raised when the number of bits per word cannot be changed"
|
||||||
|
|
||||||
|
class SPIInvalidWordSize(SPIError, ValueError):
|
||||||
|
"Error raised when an invalid (out of range) number of bits per word is specified"
|
||||||
|
|
||||||
class GPIODeviceError(GPIOZeroError):
|
class GPIODeviceError(GPIOZeroError):
|
||||||
"Base class for errors specific to the GPIODevice hierarchy"
|
"Base class for errors specific to the GPIODevice hierarchy"
|
||||||
|
|
||||||
@@ -62,7 +80,7 @@ class GPIOPinInUse(GPIODeviceError):
|
|||||||
"Error raised when attempting to use a pin already in use by another device"
|
"Error raised when attempting to use a pin already in use by another device"
|
||||||
|
|
||||||
class GPIOPinMissing(GPIODeviceError, ValueError):
|
class GPIOPinMissing(GPIODeviceError, ValueError):
|
||||||
"Error raised when a pin number is not specified"
|
"Error raised when a pin specification is not given"
|
||||||
|
|
||||||
class InputDeviceError(GPIODeviceError):
|
class InputDeviceError(GPIODeviceError):
|
||||||
"Base class for errors specific to the InputDevice hierarchy"
|
"Base class for errors specific to the InputDevice hierarchy"
|
||||||
@@ -100,6 +118,12 @@ class PinFixedPull(PinError, AttributeError):
|
|||||||
class PinEdgeDetectUnsupported(PinError, AttributeError):
|
class PinEdgeDetectUnsupported(PinError, AttributeError):
|
||||||
"Error raised when attempting to use edge detection on unsupported pins"
|
"Error raised when attempting to use edge detection on unsupported pins"
|
||||||
|
|
||||||
|
class PinGPIOUnsupported(PinError, NotImplementedError):
|
||||||
|
"Error raised when attempting to obtain a GPIO interface on unsupported pins"
|
||||||
|
|
||||||
|
class PinSPIUnsupported(PinError, NotImplementedError):
|
||||||
|
"Error raised when attempting to obtain an SPI interface on unsupported pins"
|
||||||
|
|
||||||
class PinPWMError(PinError):
|
class PinPWMError(PinError):
|
||||||
"Base class for errors related to PWM implementations"
|
"Base class for errors related to PWM implementations"
|
||||||
|
|
||||||
@@ -118,6 +142,9 @@ class PinMultiplePins(PinError, RuntimeError):
|
|||||||
class PinNoPins(PinError, RuntimeError):
|
class PinNoPins(PinError, RuntimeError):
|
||||||
"Error raised when no pins support the requested function"
|
"Error raised when no pins support the requested function"
|
||||||
|
|
||||||
|
class PinInvalidPin(PinError, ValueError):
|
||||||
|
"Error raised when an invalid pin specification is provided"
|
||||||
|
|
||||||
class GPIOZeroWarning(Warning):
|
class GPIOZeroWarning(Warning):
|
||||||
"Base class for all warnings in GPIO Zero"
|
"Base class for all warnings in GPIO Zero"
|
||||||
|
|
||||||
@@ -130,6 +157,9 @@ class SPISoftwareFallback(SPIWarning):
|
|||||||
class PinWarning(GPIOZeroWarning):
|
class PinWarning(GPIOZeroWarning):
|
||||||
"Base class for warnings related to pin implementations"
|
"Base class for warnings related to pin implementations"
|
||||||
|
|
||||||
|
class PinFactoryFallback(PinWarning):
|
||||||
|
"Warning raised when a default pin factory fails to load and a fallback is tried"
|
||||||
|
|
||||||
class PinNonPhysical(PinWarning):
|
class PinNonPhysical(PinWarning):
|
||||||
"Warning raised when a non-physical pin is specified in a constructor"
|
"Warning raised when a non-physical pin is specified in a constructor"
|
||||||
|
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ class SmoothedInputDevice(EventsMixin, InputDevice):
|
|||||||
if self.partial or self._queue.full.is_set():
|
if self.partial or self._queue.full.is_set():
|
||||||
return super(SmoothedInputDevice, self).__repr__()
|
return super(SmoothedInputDevice, self).__repr__()
|
||||||
else:
|
else:
|
||||||
return "<gpiozero.%s object on pin=%r, pull_up=%s>" % (
|
return "<gpiozero.%s object on pin %r, pull_up=%s>" % (
|
||||||
self.__class__.__name__, self.pin, self.pull_up)
|
self.__class__.__name__, self.pin, self.pull_up)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -240,7 +240,7 @@ class Button(HoldMixin, DigitalInputDevice):
|
|||||||
print("The button was pressed!")
|
print("The button was pressed!")
|
||||||
|
|
||||||
:param int pin:
|
:param int pin:
|
||||||
The GPIO pin which the button is attached to. See :ref:`pin_numbering`
|
The GPIO pin which the button is attached to. See :ref:`pin-numbering`
|
||||||
for valid pin numbers.
|
for valid pin numbers.
|
||||||
|
|
||||||
:param bool pull_up:
|
:param bool pull_up:
|
||||||
@@ -302,7 +302,7 @@ class LineSensor(SmoothedInputDevice):
|
|||||||
pause()
|
pause()
|
||||||
|
|
||||||
:param int pin:
|
:param int pin:
|
||||||
The GPIO pin which the sensor is attached to. See :ref:`pin_numbering`
|
The GPIO pin which the sensor is attached to. See :ref:`pin-numbering`
|
||||||
for valid pin numbers.
|
for valid pin numbers.
|
||||||
|
|
||||||
:param int queue_len:
|
:param int queue_len:
|
||||||
@@ -371,7 +371,7 @@ class MotionSensor(SmoothedInputDevice):
|
|||||||
print("Motion detected!")
|
print("Motion detected!")
|
||||||
|
|
||||||
:param int pin:
|
:param int pin:
|
||||||
The GPIO pin which the sensor is attached to. See :ref:`pin_numbering`
|
The GPIO pin which the sensor is attached to. See :ref:`pin-numbering`
|
||||||
for valid pin numbers.
|
for valid pin numbers.
|
||||||
|
|
||||||
:param int queue_len:
|
:param int queue_len:
|
||||||
@@ -435,7 +435,7 @@ class LightSensor(SmoothedInputDevice):
|
|||||||
print("Light detected!")
|
print("Light detected!")
|
||||||
|
|
||||||
:param int pin:
|
:param int pin:
|
||||||
The GPIO pin which the sensor is attached to. See :ref:`pin_numbering`
|
The GPIO pin which the sensor is attached to. See :ref:`pin-numbering`
|
||||||
for valid pin numbers.
|
for valid pin numbers.
|
||||||
|
|
||||||
:param int queue_len:
|
:param int queue_len:
|
||||||
@@ -543,11 +543,11 @@ class DistanceSensor(SmoothedInputDevice):
|
|||||||
|
|
||||||
:param int echo:
|
:param int echo:
|
||||||
The GPIO pin which the ECHO pin is attached to. See
|
The GPIO pin which the ECHO pin is attached to. See
|
||||||
:ref:`pin_numbering` for valid pin numbers.
|
:ref:`pin-numbering` for valid pin numbers.
|
||||||
|
|
||||||
:param int trigger:
|
:param int trigger:
|
||||||
The GPIO pin which the TRIG pin is attached to. See
|
The GPIO pin which the TRIG pin is attached to. See
|
||||||
:ref:`pin_numbering` for valid pin numbers.
|
:ref:`pin-numbering` for valid pin numbers.
|
||||||
|
|
||||||
:param int queue_len:
|
:param int queue_len:
|
||||||
The length of the queue used to store values read from the sensor.
|
The length of the queue used to store values read from the sensor.
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ class SharedMixin(object):
|
|||||||
When :meth:`close` is called, an internal reference counter will be
|
When :meth:`close` is called, an internal reference counter will be
|
||||||
decremented and the instance will only close when it reaches zero.
|
decremented and the instance will only close when it reaches zero.
|
||||||
"""
|
"""
|
||||||
_INSTANCES = {}
|
_instances = {}
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
self._refs = 0
|
self._refs = 0
|
||||||
|
|||||||
@@ -128,8 +128,8 @@ class DigitalOutputDevice(OutputDevice):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, pin=None, active_high=True, initial_value=False):
|
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, initial_value)
|
|
||||||
self._controller = None
|
self._controller = None
|
||||||
|
super(DigitalOutputDevice, self).__init__(pin, active_high, initial_value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def value(self):
|
def value(self):
|
||||||
@@ -217,7 +217,7 @@ class LED(DigitalOutputDevice):
|
|||||||
led.on()
|
led.on()
|
||||||
|
|
||||||
:param int pin:
|
:param int pin:
|
||||||
The GPIO pin which the LED is attached to. See :ref:`pin_numbering` for
|
The GPIO pin which the LED is attached to. See :ref:`pin-numbering` for
|
||||||
valid pin numbers.
|
valid pin numbers.
|
||||||
|
|
||||||
:param bool active_high:
|
:param bool active_high:
|
||||||
@@ -252,7 +252,7 @@ class Buzzer(DigitalOutputDevice):
|
|||||||
bz.on()
|
bz.on()
|
||||||
|
|
||||||
:param int pin:
|
:param int pin:
|
||||||
The GPIO pin which the buzzer is attached to. See :ref:`pin_numbering`
|
The GPIO pin which the buzzer is attached to. See :ref:`pin-numbering`
|
||||||
for valid pin numbers.
|
for valid pin numbers.
|
||||||
|
|
||||||
:param bool active_high:
|
:param bool active_high:
|
||||||
@@ -276,7 +276,7 @@ class PWMOutputDevice(OutputDevice):
|
|||||||
Generic output device configured for pulse-width modulation (PWM).
|
Generic output device configured for pulse-width modulation (PWM).
|
||||||
|
|
||||||
:param int pin:
|
:param int pin:
|
||||||
The GPIO pin which the device is attached to. See :ref:`pin_numbering`
|
The GPIO pin which the device is attached to. See :ref:`pin-numbering`
|
||||||
for valid pin numbers.
|
for valid pin numbers.
|
||||||
|
|
||||||
:param bool active_high:
|
:param bool active_high:
|
||||||
@@ -483,7 +483,7 @@ class PWMLED(PWMOutputDevice):
|
|||||||
an optional resistor to prevent the LED from burning out.
|
an optional resistor to prevent the LED from burning out.
|
||||||
|
|
||||||
:param int pin:
|
:param int pin:
|
||||||
The GPIO pin which the LED is attached to. See :ref:`pin_numbering` for
|
The GPIO pin which the LED is attached to. See :ref:`pin-numbering` for
|
||||||
valid pin numbers.
|
valid pin numbers.
|
||||||
|
|
||||||
:param bool active_high:
|
:param bool active_high:
|
||||||
@@ -562,8 +562,12 @@ class RGBLED(SourceMixin, Device):
|
|||||||
raise GPIOPinMissing('red, green, and blue pins must be provided')
|
raise GPIOPinMissing('red, green, and blue pins must be provided')
|
||||||
LEDClass = PWMLED if pwm else LED
|
LEDClass = PWMLED if pwm else LED
|
||||||
super(RGBLED, self).__init__()
|
super(RGBLED, self).__init__()
|
||||||
self._leds = tuple(LEDClass(pin, active_high) for pin in (red, green, blue))
|
try:
|
||||||
self.value = initial_value
|
self._leds = tuple(LEDClass(pin, active_high) for pin in (red, green, blue))
|
||||||
|
self.value = initial_value
|
||||||
|
except:
|
||||||
|
self.close()
|
||||||
|
raise
|
||||||
|
|
||||||
red = _led_property(0)
|
red = _led_property(0)
|
||||||
green = _led_property(1)
|
green = _led_property(1)
|
||||||
@@ -926,7 +930,7 @@ class Servo(SourceMixin, CompositeDevice):
|
|||||||
sleep(1)
|
sleep(1)
|
||||||
|
|
||||||
:param int pin:
|
:param int pin:
|
||||||
The GPIO pin which the device is attached to. See :ref:`pin_numbering`
|
The GPIO pin which the device is attached to. See :ref:`pin-numbering`
|
||||||
for valid pin numbers.
|
for valid pin numbers.
|
||||||
|
|
||||||
:param float initial_value:
|
:param float initial_value:
|
||||||
@@ -1116,7 +1120,7 @@ class AngularServo(Servo):
|
|||||||
expectations of minimum and maximum.
|
expectations of minimum and maximum.
|
||||||
|
|
||||||
:param int pin:
|
:param int pin:
|
||||||
The GPIO pin which the device is attached to. See :ref:`pin_numbering`
|
The GPIO pin which the device is attached to. See :ref:`pin-numbering`
|
||||||
for valid pin numbers.
|
for valid pin numbers.
|
||||||
|
|
||||||
:param float initial_angle:
|
:param float initial_angle:
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# vim: set fileencoding=utf-8:
|
||||||
|
|
||||||
from __future__ import (
|
from __future__ import (
|
||||||
unicode_literals,
|
unicode_literals,
|
||||||
absolute_import,
|
absolute_import,
|
||||||
@@ -6,32 +8,124 @@ from __future__ import (
|
|||||||
)
|
)
|
||||||
str = type('')
|
str = type('')
|
||||||
|
|
||||||
import io
|
|
||||||
|
|
||||||
from .data import pi_info
|
|
||||||
from ..exc import (
|
from ..exc import (
|
||||||
PinInvalidFunction,
|
PinInvalidFunction,
|
||||||
PinSetInput,
|
PinSetInput,
|
||||||
PinFixedPull,
|
PinFixedPull,
|
||||||
|
PinSPIUnsupported,
|
||||||
PinPWMUnsupported,
|
PinPWMUnsupported,
|
||||||
PinEdgeDetectUnsupported,
|
PinEdgeDetectUnsupported,
|
||||||
|
SPIFixedClockMode,
|
||||||
|
SPIFixedBitOrder,
|
||||||
|
SPIFixedSelect,
|
||||||
|
SPIFixedWordSize,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
PINS_CLEANUP = []
|
class Factory(object):
|
||||||
def _pins_shutdown():
|
"""
|
||||||
for routine in PINS_CLEANUP:
|
Generates pins, SPI, and I2C interfaces for devices. This is an abstract
|
||||||
routine()
|
base class for pin factories. Descendents must override:
|
||||||
|
|
||||||
|
* :meth:`_get_address`
|
||||||
|
* :meth:`pin_address`
|
||||||
|
|
||||||
|
Descendents may override:
|
||||||
|
|
||||||
|
* :meth:`close`
|
||||||
|
* :meth:`pin`
|
||||||
|
* :meth:`spi`
|
||||||
|
* :meth:`_get_pi_info`
|
||||||
|
"""
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""
|
||||||
|
Closes the pin factory. This is expected to clean up all resources
|
||||||
|
manipulated by the factory. It it typically called at script
|
||||||
|
termination.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def pin(self, spec):
|
||||||
|
"""
|
||||||
|
Creates an instance of a :class:`Pin` descendent representing the
|
||||||
|
specified pin.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
Descendents must ensure that pin instances representing the same
|
||||||
|
hardware are identical; i.e. two separate invocations of
|
||||||
|
:meth:`pin` for the same pin specification must return the same
|
||||||
|
object.
|
||||||
|
"""
|
||||||
|
raise PinGPIOUnsupported("GPIO not supported by this pin factory")
|
||||||
|
|
||||||
|
def pin_address(self, spec):
|
||||||
|
"""
|
||||||
|
Returns the address that a pin *would* have if constructed from the
|
||||||
|
given *spec*.
|
||||||
|
|
||||||
|
This unusual method is used by the pin reservation system to check
|
||||||
|
for conflicts *prior* to pin construction; with most implementations,
|
||||||
|
pin construction implicitly alters the state of the pin (e.g. setting
|
||||||
|
it to an input). This allows pin reservation to take place without
|
||||||
|
affecting the state of other components.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def spi(self, **spi_args):
|
||||||
|
"""
|
||||||
|
Returns an instance of an :class:`SPI` interface, for the specified SPI
|
||||||
|
*port* and *device*, or for the specified pins (*clock_pin*,
|
||||||
|
*mosi_pin*, *miso_pin*, and *select_pin*). Only one of the schemes can
|
||||||
|
be used; attempting to mix *port* and *device* with pin numbers will
|
||||||
|
raise :exc:`SPIBadArgs`.
|
||||||
|
"""
|
||||||
|
raise PinSPIUnsupported('SPI not supported by this pin factory')
|
||||||
|
|
||||||
|
def _get_address(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
address = property(
|
||||||
|
lambda self: self._get_address(),
|
||||||
|
doc="""\
|
||||||
|
Returns a tuple of strings representing the address of the factory.
|
||||||
|
For the Pi itself this is a tuple of one string representing the Pi's
|
||||||
|
address (e.g. "localhost"). Expander chips can return a tuple appending
|
||||||
|
whatever string they require to uniquely identify the expander chip
|
||||||
|
amongst all factories in the system.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This property *must* return an immutable object capable of being
|
||||||
|
used as a dictionary key.
|
||||||
|
""")
|
||||||
|
|
||||||
|
def _get_pi_info(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
pi_info = property(
|
||||||
|
lambda self: self._get_pi_info(),
|
||||||
|
doc="""\
|
||||||
|
Returns a :class:`PiBoardInfo` instance representing the Pi that
|
||||||
|
instances generated by this factory will be attached to.
|
||||||
|
|
||||||
|
If the pins represented by this class are not *directly* attached to a
|
||||||
|
Pi (e.g. the pin is attached to a board attached to the Pi, or the pins
|
||||||
|
are not on a Pi at all), this may return ``None``.
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
class Pin(object):
|
class Pin(object):
|
||||||
"""
|
"""
|
||||||
Abstract base class representing a GPIO pin or a pin from an IO extender.
|
Abstract base class representing a pin attached to some form of controller,
|
||||||
|
be it GPIO, SPI, ADC, etc.
|
||||||
|
|
||||||
Descendents should override property getters and setters to accurately
|
Descendents should override property getters and setters to accurately
|
||||||
represent the capabilities of pins. The following functions *must* be
|
represent the capabilities of pins. The following functions *must* be
|
||||||
overridden:
|
overridden:
|
||||||
|
|
||||||
|
* :meth:`_get_address`
|
||||||
* :meth:`_get_function`
|
* :meth:`_get_function`
|
||||||
* :meth:`_set_function`
|
* :meth:`_set_function`
|
||||||
* :meth:`_get_state`
|
* :meth:`_get_state`
|
||||||
@@ -39,6 +133,8 @@ class Pin(object):
|
|||||||
The following functions *may* be overridden if applicable:
|
The following functions *may* be overridden if applicable:
|
||||||
|
|
||||||
* :meth:`close`
|
* :meth:`close`
|
||||||
|
* :meth:`output_with_state`
|
||||||
|
* :meth:`input_with_pull`
|
||||||
* :meth:`_set_state`
|
* :meth:`_set_state`
|
||||||
* :meth:`_get_frequency`
|
* :meth:`_get_frequency`
|
||||||
* :meth:`_set_frequency`
|
* :meth:`_set_frequency`
|
||||||
@@ -50,20 +146,10 @@ class Pin(object):
|
|||||||
* :meth:`_set_edges`
|
* :meth:`_set_edges`
|
||||||
* :meth:`_get_when_changed`
|
* :meth:`_get_when_changed`
|
||||||
* :meth:`_set_when_changed`
|
* :meth:`_set_when_changed`
|
||||||
* :meth:`pi_info`
|
|
||||||
* :meth:`output_with_state`
|
|
||||||
* :meth:`input_with_pull`
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
Descendents must ensure that pin instances representing the same
|
|
||||||
physical hardware are identical, right down to object identity. The
|
|
||||||
framework relies on this to correctly clean up resources at interpreter
|
|
||||||
shutdown.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "Abstract pin"
|
return self.address[-1]
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""
|
"""
|
||||||
@@ -105,6 +191,18 @@ class Pin(object):
|
|||||||
self.function = 'input'
|
self.function = 'input'
|
||||||
self.pull = pull
|
self.pull = pull
|
||||||
|
|
||||||
|
def _get_address(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
address = property(
|
||||||
|
lambda self: self._get_address(),
|
||||||
|
doc="""\
|
||||||
|
The address of the pin. This property is a tuple of strings constructed
|
||||||
|
from the owning factory's address with the unique address of the pin
|
||||||
|
appended to it. The tuple as a whole uniquely identifies the pin
|
||||||
|
amongst all pins attached to the system.
|
||||||
|
""")
|
||||||
|
|
||||||
def _get_function(self):
|
def _get_function(self):
|
||||||
return "input"
|
return "input"
|
||||||
|
|
||||||
@@ -140,10 +238,19 @@ class Pin(object):
|
|||||||
doc="""\
|
doc="""\
|
||||||
The state of the pin. This is 0 for low, and 1 for high. As a low level
|
The state of the pin. This is 0 for low, and 1 for high. As a low level
|
||||||
view of the pin, no swapping is performed in the case of pull ups (see
|
view of the pin, no swapping is performed in the case of pull ups (see
|
||||||
:attr:`pull` for more information).
|
:attr:`pull` for more information):
|
||||||
|
|
||||||
If PWM is currently active (when :attr:`frequency` is not ``None``),
|
.. code-block:: text
|
||||||
this represents the PWM duty cycle as a value between 0.0 and 1.0.
|
|
||||||
|
HIGH - - - - > ,----------------------
|
||||||
|
|
|
||||||
|
|
|
||||||
|
LOW ----------------'
|
||||||
|
|
||||||
|
Descendents which implement analog, or analog-like capabilities can
|
||||||
|
return values between 0 and 1. For example, pins implementing PWM
|
||||||
|
(where :attr:`frequency` is not ``None``) return a value between 0.0
|
||||||
|
and 1.0 representing the current PWM duty cycle.
|
||||||
|
|
||||||
If a pin is currently configured for input, and an attempt is made to
|
If a pin is currently configured for input, and an attempt is made to
|
||||||
set this attribute, :exc:`PinSetInput` will be raised. If an invalid
|
set this attribute, :exc:`PinSetInput` will be raised. If an invalid
|
||||||
@@ -205,6 +312,26 @@ class Pin(object):
|
|||||||
detection, measured in seconds. If bounce detection is not currently in
|
detection, measured in seconds. If bounce detection is not currently in
|
||||||
use, this is ``None``.
|
use, this is ``None``.
|
||||||
|
|
||||||
|
For example, if :attr:`edge` is currently "rising", :attr:`bounce` is
|
||||||
|
currently 5/1000 (5ms), then the waveform below will only fire
|
||||||
|
:attr:`when_changed` on two occasions despite there being three rising
|
||||||
|
edges:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
TIME 0...1...2...3...4...5...6...7...8...9...10..11..12 ms
|
||||||
|
|
||||||
|
bounce elimination |===================| |==============
|
||||||
|
|
||||||
|
HIGH - - - - > ,--. ,--------------. ,--.
|
||||||
|
| | | | | |
|
||||||
|
| | | | | |
|
||||||
|
LOW ----------------' `-' `-' `-----------
|
||||||
|
: :
|
||||||
|
: :
|
||||||
|
when_changed when_changed
|
||||||
|
fires fires
|
||||||
|
|
||||||
If the pin does not support edge detection, attempts to set this
|
If the pin does not support edge detection, attempts to set this
|
||||||
property will raise :exc:`PinEdgeDetectUnsupported`. If the pin
|
property will raise :exc:`PinEdgeDetectUnsupported`. If the pin
|
||||||
supports edge detection, the class must implement bounce detection,
|
supports edge detection, the class must implement bounce detection,
|
||||||
@@ -223,7 +350,18 @@ class Pin(object):
|
|||||||
doc="""\
|
doc="""\
|
||||||
The edge that will trigger execution of the function or bound method
|
The edge that will trigger execution of the function or bound method
|
||||||
assigned to :attr:`when_changed`. This can be one of the strings
|
assigned to :attr:`when_changed`. This can be one of the strings
|
||||||
"both" (the default), "rising", "falling", or "none".
|
"both" (the default), "rising", "falling", or "none":
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
HIGH - - - - > ,--------------.
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
LOW --------------------' `--------------
|
||||||
|
: :
|
||||||
|
: :
|
||||||
|
Fires when_changed "both" "both"
|
||||||
|
when edges is ... "rising" "falling"
|
||||||
|
|
||||||
If the pin does not support edge detection, attempts to set this
|
If the pin does not support edge detection, attempts to set this
|
||||||
property will raise :exc:`PinEdgeDetectUnsupported`.
|
property will raise :exc:`PinEdgeDetectUnsupported`.
|
||||||
@@ -247,48 +385,300 @@ class Pin(object):
|
|||||||
property will raise :exc:`PinEdgeDetectUnsupported`.
|
property will raise :exc:`PinEdgeDetectUnsupported`.
|
||||||
""")
|
""")
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def pi_info(cls):
|
|
||||||
"""
|
|
||||||
Returns a :class:`PiBoardInfo` instance representing the Pi that
|
|
||||||
instances of this pin class will be attached to.
|
|
||||||
|
|
||||||
If the pins represented by this class are not *directly* attached to a
|
class SPI(object):
|
||||||
Pi (e.g. the pin is attached to a board attached to the Pi, or the pins
|
|
||||||
are not on a Pi at all), this may return ``None``.
|
|
||||||
"""
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class LocalPin(Pin):
|
|
||||||
"""
|
"""
|
||||||
Abstract base class representing pins attached locally to a Pi. This forms
|
Abstract interface for `Serial Peripheral Interface`_ (SPI) implementations.
|
||||||
the base class for local-only pin interfaces (:class:`RPiGPIOPin`,
|
Descendents *must* override the following:
|
||||||
:class:`RPIOPin`, and :class:`NativePin`).
|
|
||||||
|
* :meth:`transfer`
|
||||||
|
* :meth:`_get_clock_mode`
|
||||||
|
|
||||||
|
Descendents *may* override the following methods:
|
||||||
|
|
||||||
|
* :meth:`read`
|
||||||
|
* :meth:`write`
|
||||||
|
* :meth:`_set_clock_mode`
|
||||||
|
* :meth:`_get_lsb_first`
|
||||||
|
* :meth:`_set_lsb_first`
|
||||||
|
* :meth:`_get_select_high`
|
||||||
|
* :meth:`_set_select_high`
|
||||||
|
* :meth:`_get_bits_per_word`
|
||||||
|
* :meth:`_set_bits_per_word`
|
||||||
|
|
||||||
|
.. _Serial Peripheral Interface: https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
|
||||||
"""
|
"""
|
||||||
_PI_REVISION = None
|
|
||||||
|
|
||||||
@classmethod
|
def read(self, n):
|
||||||
def pi_info(cls):
|
|
||||||
"""
|
"""
|
||||||
Returns a :class:`PiBoardInfo` instance representing the local Pi.
|
Read *n* words of data from the SPI interface, returning them as a
|
||||||
The Pi's revision is determined by reading :file:`/proc/cpuinfo`. If
|
sequence of unsigned ints, each no larger than the configured
|
||||||
no valid revision is found, returns ``None``.
|
:attr:`bits_per_word` of the interface.
|
||||||
"""
|
|
||||||
# Cache the result as we can reasonably assume it won't change during
|
This method is typically used with read-only devices that feature
|
||||||
# runtime (this is LocalPin after all; descendents that deal with
|
half-duplex communication. See :meth:`transfer` for full duplex
|
||||||
# remote Pis should inherit from Pin instead)
|
communication.
|
||||||
if cls._PI_REVISION is None:
|
"""
|
||||||
with io.open('/proc/cpuinfo', 'r') as f:
|
return self.transfer((0,) * n)
|
||||||
for line in f:
|
|
||||||
if line.startswith('Revision'):
|
def write(self, data):
|
||||||
revision = line.split(':')[1].strip().lower()
|
"""
|
||||||
overvolted = revision.startswith('100')
|
Write *data* to the SPI interface. *data* must be a sequence of
|
||||||
if overvolted:
|
unsigned integer words each of which will fit within the configured
|
||||||
revision = revision[-4:]
|
:attr:`bits_per_word` of the interface. The method returns the number
|
||||||
cls._PI_REVISION = revision
|
of words written to the interface (which may be less than or equal to
|
||||||
break
|
the length of *data*).
|
||||||
if cls._PI_REVISION is None:
|
|
||||||
return None # something weird going on
|
This method is typically used with write-only devices that feature
|
||||||
return pi_info(cls._PI_REVISION)
|
half-duplex communication. See :meth:`transfer` for full duplex
|
||||||
|
communication.
|
||||||
|
"""
|
||||||
|
return len(self.transfer(data))
|
||||||
|
|
||||||
|
def transfer(self, data):
|
||||||
|
"""
|
||||||
|
Write *data* to the SPI interface. *data* must be a sequence of
|
||||||
|
unsigned integer words each of which will fit within the configured
|
||||||
|
:attr:`bits_per_word` of the interface. The method returns the sequence
|
||||||
|
of words read from the interface while writing occurred (full duplex
|
||||||
|
communication).
|
||||||
|
|
||||||
|
The length of the sequence returned dictates the number of words of
|
||||||
|
*data* written to the interface. Each word in the returned sequence
|
||||||
|
will be an unsigned integer no larger than the configured
|
||||||
|
:attr:`bits_per_word` of the interface.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@property
|
||||||
|
def clock_polarity(self):
|
||||||
|
"""
|
||||||
|
The polarity of the SPI clock pin. If this is ``False`` (the default),
|
||||||
|
the clock pin will idle low, and pulse high. Setting this to ``True``
|
||||||
|
will cause the clock pin to idle high, and pulse low. On many data
|
||||||
|
sheets this is documented as the CPOL value.
|
||||||
|
|
||||||
|
The following diagram illustrates the waveform when
|
||||||
|
:attr:`clock_polarity` is ``False`` (the default), equivalent to CPOL
|
||||||
|
0:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
on on on on on on on
|
||||||
|
,---. ,---. ,---. ,---. ,---. ,---. ,---.
|
||||||
|
CLK | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | |
|
||||||
|
------' `---' `---' `---' `---' `---' `---' `------
|
||||||
|
idle off off off off off off idle
|
||||||
|
|
||||||
|
The following diagram illustrates the waveform when
|
||||||
|
:attr:`clock_polarity` is ``True``, equivalent to CPOL 1:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
idle off off off off off off idle
|
||||||
|
------. ,---. ,---. ,---. ,---. ,---. ,---. ,------
|
||||||
|
| | | | | | | | | | | | | |
|
||||||
|
CLK | | | | | | | | | | | | | |
|
||||||
|
`---' `---' `---' `---' `---' `---' `---'
|
||||||
|
on on on on on on on
|
||||||
|
"""
|
||||||
|
return bool(self.clock_mode & 2)
|
||||||
|
|
||||||
|
@clock_polarity.setter
|
||||||
|
def clock_polarity(self, value):
|
||||||
|
self.clock_mode = self.clock_mode & (~2) | (bool(value) << 1)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def clock_phase(self):
|
||||||
|
"""
|
||||||
|
The phase of the SPI clock pin. If this is ``False`` (the default),
|
||||||
|
data will be read from the MISO pin when the clock pin activates.
|
||||||
|
Setting this to ``True`` will cause data to be read from the MISO pin
|
||||||
|
when the clock pin deactivates. On many data sheets this is documented
|
||||||
|
as the CPHA value. Whether the clock edge is rising or falling when the
|
||||||
|
clock is considered activated is controlled by the
|
||||||
|
:attr:`clock_polarity` attribute (corresponding to CPOL).
|
||||||
|
|
||||||
|
The following diagram indicates when data is read when
|
||||||
|
:attr:`clock_polarity` is ``False``, and :attr:`clock_phase` is
|
||||||
|
``False`` (the default), equivalent to CPHA 0:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
,---. ,---. ,---. ,---. ,---. ,---. ,---.
|
||||||
|
CLK | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | |
|
||||||
|
----' `---' `---' `---' `---' `---' `---' `-------
|
||||||
|
: : : : : : :
|
||||||
|
MISO---. ,---. ,---. ,---. ,---. ,---. ,---.
|
||||||
|
/ \ / \ / \ / \ / \ / \ / \\
|
||||||
|
-{ Bit X Bit X Bit X Bit X Bit X Bit X Bit }------
|
||||||
|
\ / \ / \ / \ / \ / \ / \ /
|
||||||
|
`---' `---' `---' `---' `---' `---' `---'
|
||||||
|
|
||||||
|
The following diagram indicates when data is read when
|
||||||
|
:attr:`clock_polarity` is ``False``, but :attr:`clock_phase` is
|
||||||
|
``True``, equivalent to CPHA 1:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
,---. ,---. ,---. ,---. ,---. ,---. ,---.
|
||||||
|
CLK | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | |
|
||||||
|
----' `---' `---' `---' `---' `---' `---' `-------
|
||||||
|
: : : : : : :
|
||||||
|
MISO ,---. ,---. ,---. ,---. ,---. ,---. ,---.
|
||||||
|
/ \ / \ / \ / \ / \ / \ / \\
|
||||||
|
-----{ Bit X Bit X Bit X Bit X Bit X Bit X Bit }--
|
||||||
|
\ / \ / \ / \ / \ / \ / \ /
|
||||||
|
`---' `---' `---' `---' `---' `---' `---'
|
||||||
|
"""
|
||||||
|
return bool(self.clock_mode & 1)
|
||||||
|
|
||||||
|
@clock_phase.setter
|
||||||
|
def clock_phase(self, value):
|
||||||
|
self.clock_mode = self.clock_mode & (~1) | bool(value)
|
||||||
|
|
||||||
|
def _get_clock_mode(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def _set_clock_mode(self, value):
|
||||||
|
raise SPIFixedClockMode("clock_mode cannot be changed on %r" % self)
|
||||||
|
|
||||||
|
clock_mode = property(
|
||||||
|
lambda self: self._get_clock_mode(),
|
||||||
|
lambda self, value: self._set_clock_mode(value),
|
||||||
|
doc="""\
|
||||||
|
Presents a value representing the :attr:`clock_polarity` and
|
||||||
|
:attr:`clock_phase` attributes combined according to the following
|
||||||
|
table:
|
||||||
|
|
||||||
|
+------+-----------------+--------------+
|
||||||
|
| mode | polarity (CPOL) | phase (CPHA) |
|
||||||
|
+======+=================+==============+
|
||||||
|
| 0 | False | False |
|
||||||
|
| 1 | False | True |
|
||||||
|
| 2 | True | False |
|
||||||
|
| 3 | True | True |
|
||||||
|
+------+-----------------+--------------+
|
||||||
|
|
||||||
|
Adjusting this value adjusts both the :attr:`clock_polarity` and
|
||||||
|
:attr:`clock_phase` attributes simultaneously.
|
||||||
|
""")
|
||||||
|
|
||||||
|
def _get_lsb_first(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _set_lsb_first(self, value):
|
||||||
|
raise SPIFixedBitOrder("lsb_first cannot be changed on %r" % self)
|
||||||
|
|
||||||
|
lsb_first = property(
|
||||||
|
lambda self: self._get_lsb_first(),
|
||||||
|
lambda self, value: self._set_lsb_first(value),
|
||||||
|
doc="""\
|
||||||
|
Controls whether words are read and written LSB in (Least Significant
|
||||||
|
Bit first) order. The default is ``False`` indicating that words are
|
||||||
|
read and written in MSB (Most Significant Bit first) order.
|
||||||
|
Effectively, this controls the `Bit endianness`_ of the connection.
|
||||||
|
|
||||||
|
The following diagram shows the a word containing the number 5 (binary
|
||||||
|
0101) transmitted on MISO with :attr:`bits_per_word` set to 4, and
|
||||||
|
:attr:`clock_mode` set to 0, when :attr:`lsb_first` is ``False`` (the
|
||||||
|
default):
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
,---. ,---. ,---. ,---.
|
||||||
|
CLK | | | | | | | |
|
||||||
|
| | | | | | | |
|
||||||
|
----' `---' `---' `---' `-----
|
||||||
|
: ,-------. : ,-------.
|
||||||
|
MISO: | : | : | : |
|
||||||
|
: | : | : | : |
|
||||||
|
----------' : `-------' : `----
|
||||||
|
: : : :
|
||||||
|
MSB LSB
|
||||||
|
|
||||||
|
And now with :attr:`lsb_first` set to ``True`` (and all other
|
||||||
|
parameters the same):
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
,---. ,---. ,---. ,---.
|
||||||
|
CLK | | | | | | | |
|
||||||
|
| | | | | | | |
|
||||||
|
----' `---' `---' `---' `-----
|
||||||
|
,-------. : ,-------. :
|
||||||
|
MISO: | : | : | :
|
||||||
|
| : | : | : | :
|
||||||
|
--' : `-------' : `-----------
|
||||||
|
: : : :
|
||||||
|
LSB MSB
|
||||||
|
|
||||||
|
.. _Bit endianness: https://en.wikipedia.org/wiki/Endianness#Bit_endianness
|
||||||
|
""")
|
||||||
|
|
||||||
|
def _get_select_high(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _set_select_high(self, value):
|
||||||
|
raise SPIFixedSelect("select_high cannot be changed on %r" % self)
|
||||||
|
|
||||||
|
select_high = property(
|
||||||
|
lambda self: self._get_select_high(),
|
||||||
|
lambda self, value: self._set_select_high(value),
|
||||||
|
doc="""\
|
||||||
|
If ``False`` (the default), the chip select line is considered active
|
||||||
|
when it is pulled low. When set to ``True``, the chip select line is
|
||||||
|
considered active when it is driven high.
|
||||||
|
|
||||||
|
The following diagram shows the waveform of the chip select line, and
|
||||||
|
the clock when :attr:`clock_polarity` is ``False``, and
|
||||||
|
:attr:`select_high` is ``False`` (the default):
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
---. ,------
|
||||||
|
__ | |
|
||||||
|
CS | chip is selected, and will react to clock | idle
|
||||||
|
`-----------------------------------------------------'
|
||||||
|
|
||||||
|
,---. ,---. ,---. ,---. ,---. ,---. ,---.
|
||||||
|
CLK | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | |
|
||||||
|
----' `---' `---' `---' `---' `---' `---' `-------
|
||||||
|
|
||||||
|
And when :attr:`select_high` is ``True``:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
,-----------------------------------------------------.
|
||||||
|
CS | chip is selected, and will react to clock | idle
|
||||||
|
| |
|
||||||
|
---' `------
|
||||||
|
|
||||||
|
,---. ,---. ,---. ,---. ,---. ,---. ,---.
|
||||||
|
CLK | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | |
|
||||||
|
----' `---' `---' `---' `---' `---' `---' `-------
|
||||||
|
""")
|
||||||
|
|
||||||
|
def _get_bits_per_word(self):
|
||||||
|
return 8
|
||||||
|
|
||||||
|
def _set_bits_per_word(self, value):
|
||||||
|
raise SPIFixedWordSize("bits_per_word cannot be changed on %r" % self)
|
||||||
|
|
||||||
|
bits_per_word = property(
|
||||||
|
lambda self: self._get_bits_per_word(),
|
||||||
|
lambda self, value: self._set_bits_per_word(value),
|
||||||
|
doc="""\
|
||||||
|
Controls the number of bits that make up a word, and thus where the
|
||||||
|
word boundaries appear in the data stream, and the maximum value of a
|
||||||
|
word. Defaults to 8 meaning that words are effectively bytes.
|
||||||
|
|
||||||
|
Several implementations do not support non-byte-sized words.
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from itertools import cycle
|
|||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
from ..exc import PinUnknownPi, PinMultiplePins, PinNoPins
|
from ..exc import PinUnknownPi, PinMultiplePins, PinNoPins, PinInvalidPin
|
||||||
|
|
||||||
|
|
||||||
# Some useful constants for describing pins
|
# Some useful constants for describing pins
|
||||||
@@ -119,8 +119,8 @@ A_BOARD = """\
|
|||||||
|
|
||||||
BPLUS_BOARD = """\
|
BPLUS_BOARD = """\
|
||||||
{style:white on green},--------------------------------.{style:reset}
|
{style:white on green},--------------------------------.{style:reset}
|
||||||
{style:white on green}| {P1:{style} col2}{style:white on green} P1 {style:black on white}+===={style:reset}
|
{style:white on green}| {J8:{style} col2}{style:white on green} J8 {style:black on white}+===={style:reset}
|
||||||
{style:white on green}| {P1:{style} col1}{style:white on green} {style:black on white}| USB{style:reset}
|
{style:white on green}| {J8:{style} col1}{style:white on green} {style:black on white}| USB{style:reset}
|
||||||
{style:white on green}| {style:black on white}+===={style:reset}
|
{style:white on green}| {style:black on white}+===={style:reset}
|
||||||
{style:white on green}| {style:bold}Pi Model {model:2s} V{pcb_revision:3s}{style:normal} |{style:reset}
|
{style:white on green}| {style:bold}Pi Model {model:2s} V{pcb_revision:3s}{style:normal} |{style:reset}
|
||||||
{style:white on green}| {style:on black}+----+{style:on green} {style:black on white}+===={style:reset}
|
{style:white on green}| {style:on black}+----+{style:on green} {style:black on white}+===={style:reset}
|
||||||
@@ -134,8 +134,8 @@ BPLUS_BOARD = """\
|
|||||||
|
|
||||||
APLUS_BOARD = """\
|
APLUS_BOARD = """\
|
||||||
{style:white on green},--------------------------.{style:reset}
|
{style:white on green},--------------------------.{style:reset}
|
||||||
{style:white on green}| {P1:{style} col2}{style:white on green} P1 |{style:reset}
|
{style:white on green}| {J8:{style} col2}{style:white on green} J8 |{style:reset}
|
||||||
{style:white on green}| {P1:{style} col1}{style:white on green} |{style:reset}
|
{style:white on green}| {J8:{style} col1}{style:white on green} |{style:reset}
|
||||||
{style:white on green}| |{style:reset}
|
{style:white on green}| |{style:reset}
|
||||||
{style:white on green}| {style:bold}Pi Model {model:2s} V{pcb_revision:3s}{style:normal} |{style:reset}
|
{style:white on green}| {style:bold}Pi Model {model:2s} V{pcb_revision:3s}{style:normal} |{style:reset}
|
||||||
{style:white on green}| {style:on black}+----+{style:on green} {style:black on white}+===={style:reset}
|
{style:white on green}| {style:on black}+----+{style:on green} {style:black on white}+===={style:reset}
|
||||||
@@ -149,8 +149,8 @@ APLUS_BOARD = """\
|
|||||||
|
|
||||||
ZERO12_BOARD = """\
|
ZERO12_BOARD = """\
|
||||||
{style:white on green},-------------------------.{style:reset}
|
{style:white on green},-------------------------.{style:reset}
|
||||||
{style:white on green}| {P1:{style} col2}{style:white on green} P1 |{style:reset}
|
{style:white on green}| {J8:{style} col2}{style:white on green} J8 |{style:reset}
|
||||||
{style:white on green}| {P1:{style} col1}{style:white on green} |{style:reset}
|
{style:white on green}| {J8:{style} col1}{style:white on green} |{style:reset}
|
||||||
{style:black on white}---+{style:white on green} {style:on black}+---+{style:on green} {style:bold}PiZero{style:normal} |{style:reset}
|
{style:black on white}---+{style:white on green} {style:on black}+---+{style:on green} {style:bold}PiZero{style:normal} |{style:reset}
|
||||||
{style:black on white} sd|{style:white on green} {style:on black}|SoC|{style:on green} {style:bold}V{pcb_revision:3s}{style:normal} |{style:reset}
|
{style:black on white} sd|{style:white on green} {style:on black}|SoC|{style:on green} {style:bold}V{pcb_revision:3s}{style:normal} |{style:reset}
|
||||||
{style:black on white}---+|hdmi|{style:white on green} {style:on black}+---+{style:on green} {style:black on white}usb{style:on green} {style:black on white}pwr{style:white on green} |{style:reset}
|
{style:black on white}---+|hdmi|{style:white on green} {style:on black}+---+{style:on green} {style:black on white}usb{style:on green} {style:black on white}pwr{style:white on green} |{style:reset}
|
||||||
@@ -158,8 +158,8 @@ ZERO12_BOARD = """\
|
|||||||
|
|
||||||
ZERO13_BOARD = """\
|
ZERO13_BOARD = """\
|
||||||
{style:white on green}.-------------------------.{style:reset}
|
{style:white on green}.-------------------------.{style:reset}
|
||||||
{style:white on green}| {P1:{style} col2}{style:white on green} P1 |{style:reset}
|
{style:white on green}| {J8:{style} col2}{style:white on green} J8 |{style:reset}
|
||||||
{style:white on green}| {P1:{style} col1}{style:white on green} {style:black on white}|c{style:reset}
|
{style:white on green}| {J8:{style} col1}{style:white on green} {style:black on white}|c{style:reset}
|
||||||
{style:black on white}---+{style:white on green} {style:on black}+---+{style:on green} {style:bold}Pi{model:6s}{style:normal}{style:black on white}|s{style:reset}
|
{style:black on white}---+{style:white on green} {style:on black}+---+{style:on green} {style:bold}Pi{model:6s}{style:normal}{style:black on white}|s{style:reset}
|
||||||
{style:black on white} sd|{style:white on green} {style:on black}|SoC|{style:on green} {style:bold}V{pcb_revision:3s}{style:normal} {style:black on white}|i{style:reset}
|
{style:black on white} sd|{style:white on green} {style:on black}|SoC|{style:on green} {style:bold}V{pcb_revision:3s}{style:normal} {style:black on white}|i{style:reset}
|
||||||
{style:black on white}---+|hdmi|{style:white on green} {style:on black}+---+{style:on green} {style:black on white}usb{style:on green} {style:on white}pwr{style:white on green} |{style:reset}
|
{style:black on white}---+|hdmi|{style:white on green} {style:on black}+---+{style:on green} {style:black on white}usb{style:on green} {style:on white}pwr{style:white on green} |{style:reset}
|
||||||
@@ -216,7 +216,7 @@ REV2_P5 = {
|
|||||||
7: (GND, False), 8: (GND, False),
|
7: (GND, False), 8: (GND, False),
|
||||||
}
|
}
|
||||||
|
|
||||||
PLUS_P1 = {
|
PLUS_J8 = {
|
||||||
1: (V3_3, False), 2: (V5, False),
|
1: (V3_3, False), 2: (V5, False),
|
||||||
3: (GPIO2, True), 4: (V5, False),
|
3: (GPIO2, True), 4: (V5, False),
|
||||||
5: (GPIO3, True), 6: (GND, False),
|
5: (GPIO3, True), 6: (GND, False),
|
||||||
@@ -379,12 +379,12 @@ PI_REVISIONS = {
|
|||||||
0xd: ('B', '2.0', '2012Q4', 'BCM2835', 'Egoman', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5}, REV2_BOARD, ),
|
0xd: ('B', '2.0', '2012Q4', 'BCM2835', 'Egoman', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5}, REV2_BOARD, ),
|
||||||
0xe: ('B', '2.0', '2012Q4', 'BCM2835', 'Sony', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5}, REV2_BOARD, ),
|
0xe: ('B', '2.0', '2012Q4', 'BCM2835', 'Sony', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5}, REV2_BOARD, ),
|
||||||
0xf: ('B', '2.0', '2012Q4', 'BCM2835', 'Qisda', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5}, REV2_BOARD, ),
|
0xf: ('B', '2.0', '2012Q4', 'BCM2835', 'Qisda', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5}, REV2_BOARD, ),
|
||||||
0x10: ('B+', '1.2', '2014Q3', 'BCM2835', 'Sony', 512, 'MicroSD', 4, 1, False, False, 1, 1, {'P1': PLUS_P1}, BPLUS_BOARD, ),
|
0x10: ('B+', '1.2', '2014Q3', 'BCM2835', 'Sony', 512, 'MicroSD', 4, 1, False, False, 1, 1, {'J8': PLUS_J8}, BPLUS_BOARD, ),
|
||||||
0x11: ('CM', '1.1', '2014Q2', 'BCM2835', 'Sony', 512, 'eMMC', 1, 0, False, False, 2, 2, {'SODIMM': CM_SODIMM}, CM_BOARD, ),
|
0x11: ('CM', '1.1', '2014Q2', 'BCM2835', 'Sony', 512, 'eMMC', 1, 0, False, False, 2, 2, {'SODIMM': CM_SODIMM}, CM_BOARD, ),
|
||||||
0x12: ('A+', '1.1', '2014Q4', 'BCM2835', 'Sony', 256, 'MicroSD', 1, 0, False, False, 1, 1, {'P1': PLUS_P1}, APLUS_BOARD, ),
|
0x12: ('A+', '1.1', '2014Q4', 'BCM2835', 'Sony', 256, 'MicroSD', 1, 0, False, False, 1, 1, {'J8': PLUS_J8}, APLUS_BOARD, ),
|
||||||
0x13: ('B+', '1.2', '2015Q1', 'BCM2835', 'Egoman', 512, 'MicroSD', 4, 1, False, False, 1, 1, {'P1': PLUS_P1}, BPLUS_BOARD, ),
|
0x13: ('B+', '1.2', '2015Q1', 'BCM2835', 'Egoman', 512, 'MicroSD', 4, 1, False, False, 1, 1, {'J8': PLUS_J8}, BPLUS_BOARD, ),
|
||||||
0x14: ('CM', '1.1', '2014Q2', 'BCM2835', 'Embest', 512, 'eMMC', 1, 0, False, False, 2, 2, {'SODIMM': CM_SODIMM}, CM_BOARD, ),
|
0x14: ('CM', '1.1', '2014Q2', 'BCM2835', 'Embest', 512, 'eMMC', 1, 0, False, False, 2, 2, {'SODIMM': CM_SODIMM}, CM_BOARD, ),
|
||||||
0x15: ('A+', '1.1', '2014Q4', 'BCM2835', 'Embest', 256, 'MicroSD', 1, 0, False, False, 1, 1, {'P1': PLUS_P1}, APLUS_BOARD, ),
|
0x15: ('A+', '1.1', '2014Q4', 'BCM2835', 'Embest', 256, 'MicroSD', 1, 0, False, False, 1, 1, {'J8': PLUS_J8}, APLUS_BOARD, ),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -529,7 +529,8 @@ class HeaderInfo(namedtuple('HeaderInfo', (
|
|||||||
|
|
||||||
from gpiozero import *
|
from gpiozero import *
|
||||||
|
|
||||||
print('{0:full}'.format(pi_info().headers['P1']))
|
print('{0}'.format(pi_info().headers['J8']))
|
||||||
|
print('{0:full}'.format(pi_info().headers['J8']))
|
||||||
print('{0:col2}'.format(pi_info().headers['P1']))
|
print('{0:col2}'.format(pi_info().headers['P1']))
|
||||||
print('{0:row1}'.format(pi_info().headers['P1']))
|
print('{0:row1}'.format(pi_info().headers['P1']))
|
||||||
|
|
||||||
@@ -537,10 +538,9 @@ class HeaderInfo(namedtuple('HeaderInfo', (
|
|||||||
the use of `ANSI color codes`_. If neither is specified, ANSI codes will
|
the use of `ANSI color codes`_. If neither is specified, ANSI codes will
|
||||||
only be used if stdout is detected to be a tty::
|
only be used if stdout is detected to be a tty::
|
||||||
|
|
||||||
print('{0:color row2}'.format(pi_info().headers['P1'])) # force use of ANSI codes
|
print('{0:color row2}'.format(pi_info().headers['J8'])) # force use of ANSI codes
|
||||||
print('{0:mono row2}'.format(pi_info().headers['P1'])) # force plain ASCII
|
print('{0:mono row2}'.format(pi_info().headers['P1'])) # force plain ASCII
|
||||||
|
|
||||||
.. _ANSI color codes: https://en.wikipedia.org/wiki/ANSI_escape_code
|
|
||||||
The following attributes are defined:
|
The following attributes are defined:
|
||||||
|
|
||||||
.. automethod:: pprint
|
.. automethod:: pprint
|
||||||
@@ -548,7 +548,7 @@ class HeaderInfo(namedtuple('HeaderInfo', (
|
|||||||
.. attribute:: name
|
.. attribute:: name
|
||||||
|
|
||||||
The name of the header, typically as it appears silk-screened on the
|
The name of the header, typically as it appears silk-screened on the
|
||||||
board (e.g. "P1").
|
board (e.g. "P1" or "J8").
|
||||||
|
|
||||||
.. attribute:: rows
|
.. attribute:: rows
|
||||||
|
|
||||||
@@ -561,6 +561,8 @@ class HeaderInfo(namedtuple('HeaderInfo', (
|
|||||||
.. attribute:: pins
|
.. attribute:: pins
|
||||||
|
|
||||||
A dictionary mapping physical pin numbers to :class:`PinInfo` tuples.
|
A dictionary mapping physical pin numbers to :class:`PinInfo` tuples.
|
||||||
|
|
||||||
|
.. _ANSI color codes: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||||
"""
|
"""
|
||||||
__slots__ = () # workaround python issue #24931
|
__slots__ = () # workaround python issue #24931
|
||||||
|
|
||||||
@@ -685,6 +687,7 @@ class PiBoardInfo(namedtuple('PiBoardInfo', (
|
|||||||
|
|
||||||
from gpiozero import *
|
from gpiozero import *
|
||||||
|
|
||||||
|
print('{0}'.format(pi_info()))
|
||||||
print('{0:full}'.format(pi_info()))
|
print('{0:full}'.format(pi_info()))
|
||||||
print('{0:board}'.format(pi_info()))
|
print('{0:board}'.format(pi_info()))
|
||||||
print('{0:specs}'.format(pi_info()))
|
print('{0:specs}'.format(pi_info()))
|
||||||
@@ -801,8 +804,8 @@ class PiBoardInfo(namedtuple('PiBoardInfo', (
|
|||||||
|
|
||||||
A dictionary which maps header labels to :class:`HeaderInfo` tuples.
|
A dictionary which maps header labels to :class:`HeaderInfo` tuples.
|
||||||
For example, to obtain information about header P1 you would query
|
For example, to obtain information about header P1 you would query
|
||||||
``headers['P1']``. To obtain information about pin 12 on header P1 you
|
``headers['P1']``. To obtain information about pin 12 on header J8 you
|
||||||
would query ``headers['P1'].pins[12]``.
|
would query ``headers['J8'].pins[12]``.
|
||||||
|
|
||||||
A rendered version of this data can be obtained by using the
|
A rendered version of this data can be obtained by using the
|
||||||
:class:`PiBoardInfo` object in a format string::
|
:class:`PiBoardInfo` object in a format string::
|
||||||
@@ -937,10 +940,10 @@ class PiBoardInfo(namedtuple('PiBoardInfo', (
|
|||||||
}.get(model, csi)
|
}.get(model, csi)
|
||||||
headers = {
|
headers = {
|
||||||
'A': {'P1': REV2_P1, 'P5': REV2_P5},
|
'A': {'P1': REV2_P1, 'P5': REV2_P5},
|
||||||
'B': {'P1': REV2_P1, 'P5': REV2_P5} if pcb_revision == '2.0' else {'P1': REV1_P1},
|
'B': {'P1': REV1_P1} if pcb_revision == '1.0' else {'P1': REV2_P1, 'P5': REV2_P5},
|
||||||
'CM': {'SODIMM': CM_SODIMM},
|
'CM': {'SODIMM': CM_SODIMM},
|
||||||
'CM3': {'SODIMM': CM3_SODIMM},
|
'CM3': {'SODIMM': CM3_SODIMM},
|
||||||
}.get(model, {'P1': PLUS_P1})
|
}.get(model, {'J8': PLUS_J8})
|
||||||
board = {
|
board = {
|
||||||
'A': A_BOARD,
|
'A': A_BOARD,
|
||||||
'B': REV1_BOARD if pcb_revision == '1.0' else REV2_BOARD,
|
'B': REV1_BOARD if pcb_revision == '1.0' else REV2_BOARD,
|
||||||
@@ -1115,8 +1118,8 @@ class PiBoardInfo(namedtuple('PiBoardInfo', (
|
|||||||
"""
|
"""
|
||||||
Pretty-print a representation of the board along with header diagrams.
|
Pretty-print a representation of the board along with header diagrams.
|
||||||
|
|
||||||
If *color* is ``None`` (the default, the diagram will include ANSI
|
If *color* is ``None`` (the default), the diagram will include ANSI
|
||||||
color codes if stdout is a color-capable terminal). Otherwise *color*
|
color codes if stdout is a color-capable terminal. Otherwise *color*
|
||||||
can be set to ``True`` or ``False`` to force color or monochrome
|
can be set to ``True`` or ``False`` to force color or monochrome
|
||||||
output.
|
output.
|
||||||
"""
|
"""
|
||||||
@@ -1134,13 +1137,10 @@ def pi_info(revision=None):
|
|||||||
the model of Pi it is running on and return information about that.
|
the model of Pi it is running on and return information about that.
|
||||||
"""
|
"""
|
||||||
if revision is None:
|
if revision is None:
|
||||||
# NOTE: This import is declared locally for two reasons. Firstly it
|
# The reason this import is located here is to avoid a circular
|
||||||
# avoids a circular dependency (devices->pins->pins.data->devices).
|
# dependency; devices->pins.local->pins.data->devices
|
||||||
# Secondly, pin_factory is one global which might potentially be
|
from ..devices import Device
|
||||||
# re-written by a user's script at runtime hence we should re-import
|
result = Device._pin_factory.pi_info
|
||||||
# here in case it's changed since initialization
|
|
||||||
from ..devices import pin_factory
|
|
||||||
result = pin_factory.pi_info()
|
|
||||||
if result is None:
|
if result is None:
|
||||||
raise PinUnknownPi('The default pin_factory is not attached to a Pi')
|
raise PinUnknownPi('The default pin_factory is not attached to a Pi')
|
||||||
else:
|
else:
|
||||||
|
|||||||
241
gpiozero/pins/local.py
Normal file
241
gpiozero/pins/local.py
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
from __future__ import (
|
||||||
|
unicode_literals,
|
||||||
|
absolute_import,
|
||||||
|
print_function,
|
||||||
|
division,
|
||||||
|
)
|
||||||
|
str = type('')
|
||||||
|
|
||||||
|
import io
|
||||||
|
import weakref
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
try:
|
||||||
|
from spidev import SpiDev
|
||||||
|
except ImportError:
|
||||||
|
SpiDev = None
|
||||||
|
|
||||||
|
from . import SPI
|
||||||
|
from .pi import PiFactory, PiPin
|
||||||
|
from .spi import SPISoftwareBus
|
||||||
|
from ..devices import Device, SharedMixin
|
||||||
|
from ..output_devices import OutputDevice
|
||||||
|
from ..exc import DeviceClosed, PinUnknownPi, SPIInvalidClockMode
|
||||||
|
|
||||||
|
|
||||||
|
class LocalPiFactory(PiFactory):
|
||||||
|
"""
|
||||||
|
Abstract base class representing pins attached locally to a Pi. This forms
|
||||||
|
the base class for local-only pin interfaces (:class:`RPiGPIOPin`,
|
||||||
|
:class:`RPIOPin`, and :class:`NativePin`).
|
||||||
|
"""
|
||||||
|
pins = {}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(LocalPiFactory, self).__init__()
|
||||||
|
self.spi_hardware_class = LocalPiHardwareSPI
|
||||||
|
self.spi_software_class = LocalPiSoftwareSPI
|
||||||
|
self.shared_spi_hardware_class = LocalPiHardwareSPIShared
|
||||||
|
self.shared_spi_software_class = LocalPiSoftwareSPIShared
|
||||||
|
# Override the pins dict to be this class' pins dict. This is a bit of
|
||||||
|
# a dirty hack, but ensures that anyone evil enough to mix pin
|
||||||
|
# implementations doesn't try and control the same pin with different
|
||||||
|
# backends
|
||||||
|
self.pins = LocalPiFactory.pins
|
||||||
|
|
||||||
|
def _get_address(self):
|
||||||
|
return ('localhost',)
|
||||||
|
|
||||||
|
def _get_revision(self):
|
||||||
|
# Cache the result as we can reasonably assume it won't change during
|
||||||
|
# runtime (this is LocalPin after all; descendents that deal with
|
||||||
|
# remote Pis should inherit from Pin instead)
|
||||||
|
with io.open('/proc/cpuinfo', 'r') as f:
|
||||||
|
for line in f:
|
||||||
|
if line.startswith('Revision'):
|
||||||
|
revision = line.split(':')[1].strip().lower()
|
||||||
|
overvolted = revision.startswith('100')
|
||||||
|
if overvolted:
|
||||||
|
revision = revision[-4:]
|
||||||
|
return revision
|
||||||
|
raise PinUnknownPi('unable to locate Pi revision in /proc/cpuinfo')
|
||||||
|
|
||||||
|
|
||||||
|
class LocalPiPin(PiPin):
|
||||||
|
"""
|
||||||
|
Abstract base class representing a multi-function GPIO pin attached to the
|
||||||
|
local Raspberry Pi.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class LocalPiHardwareSPI(SPI, Device):
|
||||||
|
def __init__(self, factory, port, device):
|
||||||
|
if SpiDev is None:
|
||||||
|
raise ImportError('failed to import spidev')
|
||||||
|
self._port = port
|
||||||
|
self._device = device
|
||||||
|
self._intf = None
|
||||||
|
self._address = factory.address + ('SPI(port=%d, device=%d)' % (port, device),)
|
||||||
|
super(LocalPiHardwareSPI, self).__init__()
|
||||||
|
self._reserve_pins(
|
||||||
|
factory.pin_address(11),
|
||||||
|
factory.pin_address(10),
|
||||||
|
factory.pin_address(9),
|
||||||
|
factory.pin_address((8, 7)[device])
|
||||||
|
)
|
||||||
|
self._intf = SpiDev()
|
||||||
|
self._intf.open(port, device)
|
||||||
|
self._intf.max_speed_hz = 500000
|
||||||
|
|
||||||
|
def _conflicts_with(self, other):
|
||||||
|
return not (
|
||||||
|
isinstance(other, LocalPiHardwareSPI) and
|
||||||
|
(self._port, self._device) != (other._port, other._device)
|
||||||
|
)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self._intf:
|
||||||
|
try:
|
||||||
|
self._intf.close()
|
||||||
|
finally:
|
||||||
|
self._intf = None
|
||||||
|
self._release_all()
|
||||||
|
super(LocalPiHardwareSPI, self).close()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def closed(self):
|
||||||
|
return self._intf is None
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
try:
|
||||||
|
self._check_open()
|
||||||
|
return 'SPI(port=%d, device=%d)' % (self._port, self._device)
|
||||||
|
except DeviceClosed:
|
||||||
|
return 'SPI(closed)'
|
||||||
|
|
||||||
|
def transfer(self, data):
|
||||||
|
"""
|
||||||
|
Writes data (a list of integer words where each word is assumed to have
|
||||||
|
:attr:`bits_per_word` bits or less) to the SPI interface, and reads an
|
||||||
|
equivalent number of words, returning them as a list of integers.
|
||||||
|
"""
|
||||||
|
return self._intf.xfer2(data)
|
||||||
|
|
||||||
|
def _get_clock_mode(self):
|
||||||
|
return self._intf.mode
|
||||||
|
|
||||||
|
def _set_clock_mode(self, value):
|
||||||
|
self._intf.mode = value
|
||||||
|
|
||||||
|
def _get_lsb_first(self):
|
||||||
|
return self._intf.lsbfirst
|
||||||
|
|
||||||
|
def _set_lsb_first(self, value):
|
||||||
|
self._intf.lsbfirst = bool(value)
|
||||||
|
|
||||||
|
def _get_select_high(self):
|
||||||
|
return self._intf.cshigh
|
||||||
|
|
||||||
|
def _set_select_high(self, value):
|
||||||
|
self._intf.cshigh = bool(value)
|
||||||
|
|
||||||
|
def _get_bits_per_word(self):
|
||||||
|
return self._intf.bits_per_word
|
||||||
|
|
||||||
|
def _set_bits_per_word(self, value):
|
||||||
|
self._intf.bits_per_word = value
|
||||||
|
|
||||||
|
|
||||||
|
class LocalPiSoftwareSPI(SPI, OutputDevice):
|
||||||
|
def __init__(self, factory, clock_pin, mosi_pin, miso_pin, select_pin):
|
||||||
|
self._bus = None
|
||||||
|
self._address = factory.address + (
|
||||||
|
'SPI(clock_pin=%d, mosi_pin=%d, miso_pin=%d, select_pin=%d)' % (
|
||||||
|
clock_pin, mosi_pin, miso_pin, select_pin),
|
||||||
|
)
|
||||||
|
super(LocalPiSoftwareSPI, self).__init__(select_pin, active_high=False)
|
||||||
|
try:
|
||||||
|
self._clock_phase = False
|
||||||
|
self._lsb_first = False
|
||||||
|
self._bits_per_word = 8
|
||||||
|
self._bus = SPISoftwareBus(clock_pin, mosi_pin, miso_pin)
|
||||||
|
except:
|
||||||
|
self.close()
|
||||||
|
raise
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self._bus:
|
||||||
|
self._bus.close()
|
||||||
|
self._bus = None
|
||||||
|
super(LocalPiSoftwareSPI, self).close()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def closed(self):
|
||||||
|
return self._bus is None
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
try:
|
||||||
|
self._check_open()
|
||||||
|
return 'SPI(clock_pin=%d, mosi_pin=%d, miso_pin=%d, select_pin=%d)' % (
|
||||||
|
self._bus.clock.pin.number,
|
||||||
|
self._bus.mosi.pin.number,
|
||||||
|
self._bus.miso.pin.number,
|
||||||
|
self.pin.number)
|
||||||
|
except DeviceClosed:
|
||||||
|
return 'SPI(closed)'
|
||||||
|
|
||||||
|
def transfer(self, data):
|
||||||
|
with self._bus.lock:
|
||||||
|
self.on()
|
||||||
|
try:
|
||||||
|
return self._bus.transfer(
|
||||||
|
data, self._clock_phase, self._lsb_first, self._bits_per_word)
|
||||||
|
finally:
|
||||||
|
self.off()
|
||||||
|
|
||||||
|
def _get_clock_mode(self):
|
||||||
|
with self._bus.lock:
|
||||||
|
return (not self._bus.clock.active_high) << 1 | self._clock_phase
|
||||||
|
|
||||||
|
def _set_clock_mode(self, value):
|
||||||
|
if not (0 <= value < 4):
|
||||||
|
raise SPIInvalidClockMode("%d is not a valid clock mode" % value)
|
||||||
|
with self._bus.lock:
|
||||||
|
self._bus.clock.active_high = not (value & 2)
|
||||||
|
self._clock_phase = bool(value & 1)
|
||||||
|
|
||||||
|
def _get_lsb_first(self):
|
||||||
|
return self._lsb_first
|
||||||
|
|
||||||
|
def _set_lsb_first(self, value):
|
||||||
|
self._lsb_first = bool(value)
|
||||||
|
|
||||||
|
def _get_bits_per_word(self):
|
||||||
|
return self._bits_per_word
|
||||||
|
|
||||||
|
def _set_bits_per_word(self, value):
|
||||||
|
if value < 1:
|
||||||
|
raise ValueError('bits_per_word must be positive')
|
||||||
|
self._bits_per_word = int(value)
|
||||||
|
|
||||||
|
def _get_select_high(self):
|
||||||
|
return self.active_high
|
||||||
|
|
||||||
|
def _set_select_high(self, value):
|
||||||
|
with self._bus.lock:
|
||||||
|
self.active_high = value
|
||||||
|
self.off()
|
||||||
|
|
||||||
|
|
||||||
|
class LocalPiHardwareSPIShared(SharedMixin, LocalPiHardwareSPI):
|
||||||
|
@classmethod
|
||||||
|
def _shared_key(cls, factory, port, device):
|
||||||
|
return (port, device)
|
||||||
|
|
||||||
|
|
||||||
|
class LocalPiSoftwareSPIShared(SharedMixin, LocalPiSoftwareSPI):
|
||||||
|
@classmethod
|
||||||
|
def _shared_key(cls, factory, clock_pin, mosi_pin, miso_pin, select_pin):
|
||||||
|
return (select_pin,)
|
||||||
|
|
||||||
@@ -15,56 +15,28 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
from ..compat import isclose
|
from ..compat import isclose
|
||||||
|
|
||||||
from . import Pin
|
from ..exc import PinPWMUnsupported, PinSetInput, PinFixedPull
|
||||||
from .data import pi_info
|
from ..devices import Device
|
||||||
from ..exc import PinSetInput, PinPWMUnsupported, PinFixedPull
|
from .pi import PiPin
|
||||||
|
from .local import LocalPiFactory
|
||||||
|
|
||||||
|
|
||||||
PinState = namedtuple('PinState', ('timestamp', 'state'))
|
PinState = namedtuple('PinState', ('timestamp', 'state'))
|
||||||
|
|
||||||
class MockPin(Pin):
|
class MockPin(PiPin):
|
||||||
"""
|
"""
|
||||||
A mock pin used primarily for testing. This class does *not* support PWM.
|
A mock pin used primarily for testing. This class does *not* support PWM.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_PINS = {}
|
def __init__(self, factory, number):
|
||||||
|
super(MockPin, self).__init__(factory, number)
|
||||||
@classmethod
|
self._function = 'input'
|
||||||
def clear_pins(cls):
|
self._state = False
|
||||||
cls._PINS.clear()
|
self._pull = 'floating'
|
||||||
|
self._bounce = None
|
||||||
@classmethod
|
self._edges = 'both'
|
||||||
def pi_info(cls):
|
self._when_changed = None
|
||||||
return pi_info('a21041') # Pretend we're a Pi 2B
|
self.clear_states()
|
||||||
|
|
||||||
def __new__(cls, number):
|
|
||||||
if not (0 <= number < 54):
|
|
||||||
raise ValueError('invalid pin %d specified (must be 0..53)' % number)
|
|
||||||
try:
|
|
||||||
old_pin = cls._PINS[number]
|
|
||||||
except KeyError:
|
|
||||||
self = super(MockPin, cls).__new__(cls)
|
|
||||||
cls._PINS[number] = self
|
|
||||||
self._number = number
|
|
||||||
self._function = 'input'
|
|
||||||
self._state = False
|
|
||||||
self._pull = 'floating'
|
|
||||||
self._bounce = None
|
|
||||||
self._edges = 'both'
|
|
||||||
self._when_changed = None
|
|
||||||
self.clear_states()
|
|
||||||
return self
|
|
||||||
# Ensure the pin class expected supports PWM (or not)
|
|
||||||
if issubclass(cls, MockPWMPin) != isinstance(old_pin, MockPWMPin):
|
|
||||||
raise ValueError('pin %d is already in use as a %s' % (number, old_pin.__class__.__name__))
|
|
||||||
return old_pin
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return 'MOCK%d' % self._number
|
|
||||||
|
|
||||||
@property
|
|
||||||
def number(self):
|
|
||||||
return self._number
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.when_changed = None
|
self.when_changed = None
|
||||||
@@ -186,8 +158,8 @@ class MockChargingPin(MockPin):
|
|||||||
(as if attached to, e.g. a typical circuit using an LDR and a capacitor
|
(as if attached to, e.g. a typical circuit using an LDR and a capacitor
|
||||||
to time the charging rate).
|
to time the charging rate).
|
||||||
"""
|
"""
|
||||||
def __init__(self, number):
|
def __init__(self, factory, number):
|
||||||
super(MockChargingPin, self).__init__()
|
super(MockChargingPin, self).__init__(factory, number)
|
||||||
self.charge_time = 0.01 # dark charging time
|
self.charge_time = 0.01 # dark charging time
|
||||||
self._charge_stop = Event()
|
self._charge_stop = Event()
|
||||||
self._charge_thread = None
|
self._charge_thread = None
|
||||||
@@ -225,8 +197,8 @@ class MockTriggerPin(MockPin):
|
|||||||
corresponding pin instance. When this pin is driven high it will trigger
|
corresponding pin instance. When this pin is driven high it will trigger
|
||||||
the echo pin to drive high for the echo time.
|
the echo pin to drive high for the echo time.
|
||||||
"""
|
"""
|
||||||
def __init__(self, number):
|
def __init__(self, factory, number):
|
||||||
super(MockTriggerPin, self).__init__()
|
super(MockTriggerPin, self).__init__(factory, number)
|
||||||
self.echo_pin = None
|
self.echo_pin = None
|
||||||
self.echo_time = 0.04 # longest echo time
|
self.echo_time = 0.04 # longest echo time
|
||||||
self._echo_thread = None
|
self._echo_thread = None
|
||||||
@@ -250,8 +222,8 @@ class MockPWMPin(MockPin):
|
|||||||
"""
|
"""
|
||||||
This derivative of :class:`MockPin` adds PWM support.
|
This derivative of :class:`MockPin` adds PWM support.
|
||||||
"""
|
"""
|
||||||
def __init__(self, number):
|
def __init__(self, factory, number):
|
||||||
super(MockPWMPin, self).__init__()
|
super(MockPWMPin, self).__init__(factory, number)
|
||||||
self._frequency = None
|
self._frequency = None
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
@@ -283,8 +255,8 @@ class MockSPIClockPin(MockPin):
|
|||||||
rather, construct a :class:`MockSPIDevice` with various pin numbers, and
|
rather, construct a :class:`MockSPIDevice` with various pin numbers, and
|
||||||
this class will be used for the clock pin.
|
this class will be used for the clock pin.
|
||||||
"""
|
"""
|
||||||
def __init__(self, number):
|
def __init__(self, factory, number):
|
||||||
super(MockSPIClockPin, self).__init__()
|
super(MockSPIClockPin, self).__init__(factory, number)
|
||||||
if not hasattr(self, 'spi_devices'):
|
if not hasattr(self, 'spi_devices'):
|
||||||
self.spi_devices = []
|
self.spi_devices = []
|
||||||
|
|
||||||
@@ -301,8 +273,8 @@ class MockSPISelectPin(MockPin):
|
|||||||
tests; rather, construct a :class:`MockSPIDevice` with various pin numbers,
|
tests; rather, construct a :class:`MockSPIDevice` with various pin numbers,
|
||||||
and this class will be used for the select pin.
|
and this class will be used for the select pin.
|
||||||
"""
|
"""
|
||||||
def __init__(self, number):
|
def __init__(self, factory, number):
|
||||||
super(MockSPISelectPin, self).__init__()
|
super(MockSPISelectPin, self).__init__(factory, number)
|
||||||
if not hasattr(self, 'spi_device'):
|
if not hasattr(self, 'spi_device'):
|
||||||
self.spi_device = None
|
self.spi_device = None
|
||||||
|
|
||||||
@@ -314,13 +286,13 @@ class MockSPISelectPin(MockPin):
|
|||||||
|
|
||||||
class MockSPIDevice(object):
|
class MockSPIDevice(object):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, clock_pin, mosi_pin, miso_pin, select_pin=None,
|
self, clock_pin, mosi_pin=None, miso_pin=None, select_pin=None,
|
||||||
clock_polarity=False, clock_phase=False, lsb_first=False,
|
clock_polarity=False, clock_phase=False, lsb_first=False,
|
||||||
bits_per_word=8, select_high=False):
|
bits_per_word=8, select_high=False):
|
||||||
self.clock_pin = MockSPIClockPin(clock_pin)
|
self.clock_pin = Device._pin_factory.pin(clock_pin, pin_class=MockSPIClockPin)
|
||||||
self.mosi_pin = None if mosi_pin is None else MockPin(mosi_pin)
|
self.mosi_pin = None if mosi_pin is None else Device._pin_factory.pin(mosi_pin)
|
||||||
self.miso_pin = None if miso_pin is None else MockPin(miso_pin)
|
self.miso_pin = None if miso_pin is None else Device._pin_factory.pin(miso_pin)
|
||||||
self.select_pin = None if select_pin is None else MockSPISelectPin(select_pin)
|
self.select_pin = None if select_pin is None else Device._pin_factory.pin(select_pin, pin_class=MockSPISelectPin)
|
||||||
self.clock_polarity = clock_polarity
|
self.clock_polarity = clock_polarity
|
||||||
self.clock_phase = clock_phase
|
self.clock_phase = clock_phase
|
||||||
self.lsb_first = lsb_first
|
self.lsb_first = lsb_first
|
||||||
@@ -413,3 +385,34 @@ class MockSPIDevice(object):
|
|||||||
bits = reversed(bits)
|
bits = reversed(bits)
|
||||||
self.tx_buf.extend(bits)
|
self.tx_buf.extend(bits)
|
||||||
|
|
||||||
|
|
||||||
|
class MockFactory(LocalPiFactory):
|
||||||
|
def __init__(self, revision='a21041', pin_class=MockPin):
|
||||||
|
super(MockFactory, self).__init__()
|
||||||
|
self._revision = revision
|
||||||
|
self.pin_class = pin_class
|
||||||
|
|
||||||
|
def _get_address(self):
|
||||||
|
return ('mock',)
|
||||||
|
|
||||||
|
def _get_revision(self):
|
||||||
|
return self._revision
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.pins.clear()
|
||||||
|
|
||||||
|
def pin(self, spec, pin_class=None):
|
||||||
|
if pin_class is None:
|
||||||
|
pin_class = self.pin_class
|
||||||
|
n = self._to_gpio(spec)
|
||||||
|
try:
|
||||||
|
pin = self.pins[n]
|
||||||
|
except KeyError:
|
||||||
|
pin = pin_class(self, n)
|
||||||
|
self.pins[n] = pin
|
||||||
|
else:
|
||||||
|
# Ensure the pin class expected supports PWM (or not)
|
||||||
|
if issubclass(pin_class, MockPWMPin) != isinstance(pin, MockPWMPin):
|
||||||
|
raise ValueError('pin %d is already in use as a %s' % (n, pin.__class__.__name__))
|
||||||
|
return pin
|
||||||
|
|
||||||
|
|||||||
@@ -13,20 +13,18 @@ import mmap
|
|||||||
import errno
|
import errno
|
||||||
import struct
|
import struct
|
||||||
import warnings
|
import warnings
|
||||||
|
import weakref
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from threading import Thread, Event, Lock
|
from threading import Thread, Event, Lock
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
|
|
||||||
from . import LocalPin, PINS_CLEANUP
|
from .local import LocalPiPin, LocalPiFactory
|
||||||
from .data import pi_info
|
|
||||||
from ..exc import (
|
from ..exc import (
|
||||||
PinInvalidPull,
|
PinInvalidPull,
|
||||||
PinInvalidEdges,
|
PinInvalidEdges,
|
||||||
PinInvalidFunction,
|
PinInvalidFunction,
|
||||||
PinFixedPull,
|
PinFixedPull,
|
||||||
PinSetInput,
|
PinSetInput,
|
||||||
PinNonPhysical,
|
|
||||||
PinNoPins,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -149,7 +147,7 @@ class GPIOFS(object):
|
|||||||
f.write(str(pin).encode('ascii'))
|
f.write(str(pin).encode('ascii'))
|
||||||
|
|
||||||
|
|
||||||
class NativePin(LocalPin):
|
class NativeFactory(LocalPiFactory):
|
||||||
"""
|
"""
|
||||||
Uses a built-in pure Python implementation to interface to the Pi's GPIO
|
Uses a built-in pure Python implementation to interface to the Pi's GPIO
|
||||||
pins. This is the default pin implementation if no third-party libraries
|
pins. This is the default pin implementation if no third-party libraries
|
||||||
@@ -169,10 +167,17 @@ class NativePin(LocalPin):
|
|||||||
|
|
||||||
led = LED(NativePin(12))
|
led = LED(NativePin(12))
|
||||||
"""
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
super(NativeFactory, self).__init__()
|
||||||
|
self.mem = GPIOMemory()
|
||||||
|
self.pin_class = NativePin
|
||||||
|
|
||||||
_MEM = None
|
def close(self):
|
||||||
_PINS = {}
|
super(NativeFactory, self).close()
|
||||||
|
self.mem.close()
|
||||||
|
|
||||||
|
|
||||||
|
class NativePin(LocalPiPin):
|
||||||
GPIO_FUNCTIONS = {
|
GPIO_FUNCTIONS = {
|
||||||
'input': 0b000,
|
'input': 0b000,
|
||||||
'output': 0b001,
|
'output': 0b001,
|
||||||
@@ -202,89 +207,62 @@ class NativePin(LocalPin):
|
|||||||
GPIO_PULL_UP_NAMES = {v: k for (k, v) in GPIO_PULL_UPS.items()}
|
GPIO_PULL_UP_NAMES = {v: k for (k, v) in GPIO_PULL_UPS.items()}
|
||||||
GPIO_EDGES_NAMES = {v: k for (k, v) in GPIO_EDGES.items()}
|
GPIO_EDGES_NAMES = {v: k for (k, v) in GPIO_EDGES.items()}
|
||||||
|
|
||||||
PI_INFO = None
|
def __init__(self, factory, number):
|
||||||
|
super(NativePin, self).__init__(factory, number)
|
||||||
def __new__(cls, number):
|
self._func_offset = self.factory.mem.GPFSEL_OFFSET + (number // 10)
|
||||||
if not cls._PINS:
|
self._func_shift = (number % 10) * 3
|
||||||
cls._MEM = GPIOMemory()
|
self._set_offset = self.factory.mem.GPSET_OFFSET + (number // 32)
|
||||||
PINS_CLEANUP.append(cls._MEM.close)
|
self._set_shift = number % 32
|
||||||
if cls.PI_INFO is None:
|
self._clear_offset = self.factory.mem.GPCLR_OFFSET + (number // 32)
|
||||||
cls.PI_INFO = pi_info()
|
self._clear_shift = number % 32
|
||||||
if not (0 <= number < 54):
|
self._level_offset = self.factory.mem.GPLEV_OFFSET + (number // 32)
|
||||||
raise ValueError('invalid pin %d specified (must be 0..53)' % number)
|
self._level_shift = number % 32
|
||||||
try:
|
self._pull_offset = self.factory.mem.GPPUDCLK_OFFSET + (number // 32)
|
||||||
return cls._PINS[number]
|
self._pull_shift = number % 32
|
||||||
except KeyError:
|
self._edge_offset = self.factory.mem.GPEDS_OFFSET + (number // 32)
|
||||||
self = super(NativePin, cls).__new__(cls)
|
self._edge_shift = number % 32
|
||||||
try:
|
self._rising_offset = self.factory.mem.GPREN_OFFSET + (number // 32)
|
||||||
cls.PI_INFO.physical_pin('GPIO%d' % number)
|
self._rising_shift = number % 32
|
||||||
except PinNoPins:
|
self._falling_offset = self.factory.mem.GPFEN_OFFSET + (number // 32)
|
||||||
warnings.warn(
|
self._falling_shift = number % 32
|
||||||
PinNonPhysical(
|
self._when_changed = None
|
||||||
'no physical pins exist for GPIO%d' % number))
|
self._change_thread = None
|
||||||
self._number = number
|
self._change_event = Event()
|
||||||
self._func_offset = self._MEM.GPFSEL_OFFSET + (number // 10)
|
self.function = 'input'
|
||||||
self._func_shift = (number % 10) * 3
|
self.pull = 'up' if factory.pi_info.pulled_up('GPIO%d' % number) else 'floating'
|
||||||
self._set_offset = self._MEM.GPSET_OFFSET + (number // 32)
|
self.bounce = None
|
||||||
self._set_shift = number % 32
|
self.edges = 'both'
|
||||||
self._clear_offset = self._MEM.GPCLR_OFFSET + (number // 32)
|
|
||||||
self._clear_shift = number % 32
|
|
||||||
self._level_offset = self._MEM.GPLEV_OFFSET + (number // 32)
|
|
||||||
self._level_shift = number % 32
|
|
||||||
self._pull_offset = self._MEM.GPPUDCLK_OFFSET + (number // 32)
|
|
||||||
self._pull_shift = number % 32
|
|
||||||
self._edge_offset = self._MEM.GPEDS_OFFSET + (number // 32)
|
|
||||||
self._edge_shift = number % 32
|
|
||||||
self._rising_offset = self._MEM.GPREN_OFFSET + (number // 32)
|
|
||||||
self._rising_shift = number % 32
|
|
||||||
self._falling_offset = self._MEM.GPFEN_OFFSET + (number // 32)
|
|
||||||
self._falling_shift = number % 32
|
|
||||||
self._when_changed = None
|
|
||||||
self._change_thread = None
|
|
||||||
self._change_event = Event()
|
|
||||||
self.function = 'input'
|
|
||||||
self.pull = 'up' if cls.PI_INFO.pulled_up('GPIO%d' % number) else 'floating'
|
|
||||||
self.bounce = None
|
|
||||||
self.edges = 'both'
|
|
||||||
cls._PINS[number] = self
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "GPIO%d" % self._number
|
|
||||||
|
|
||||||
@property
|
|
||||||
def number(self):
|
|
||||||
return self._number
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
self.frequency = None
|
||||||
self.when_changed = None
|
self.when_changed = None
|
||||||
self.function = 'input'
|
self.function = 'input'
|
||||||
self.pull = 'up' if self.PI_INFO.pulled_up('GPIO%d' % self.number) else 'floating'
|
self.pull = 'up' if self.factory.pi_info.pulled_up('GPIO%d' % self.number) else 'floating'
|
||||||
|
|
||||||
def _get_function(self):
|
def _get_function(self):
|
||||||
return self.GPIO_FUNCTION_NAMES[(self._MEM[self._func_offset] >> self._func_shift) & 7]
|
return self.GPIO_FUNCTION_NAMES[(self.factory.mem[self._func_offset] >> self._func_shift) & 7]
|
||||||
|
|
||||||
def _set_function(self, value):
|
def _set_function(self, value):
|
||||||
try:
|
try:
|
||||||
value = self.GPIO_FUNCTIONS[value]
|
value = self.GPIO_FUNCTIONS[value]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise PinInvalidFunction('invalid function "%s" for pin %r' % (value, self))
|
raise PinInvalidFunction('invalid function "%s" for pin %r' % (value, self))
|
||||||
self._MEM[self._func_offset] = (
|
self.factory.mem[self._func_offset] = (
|
||||||
self._MEM[self._func_offset]
|
self.factory.mem[self._func_offset]
|
||||||
& ~(7 << self._func_shift)
|
& ~(7 << self._func_shift)
|
||||||
| (value << self._func_shift)
|
| (value << self._func_shift)
|
||||||
)
|
)
|
||||||
|
|
||||||
def _get_state(self):
|
def _get_state(self):
|
||||||
return bool(self._MEM[self._level_offset] & (1 << self._level_shift))
|
return bool(self.factory.mem[self._level_offset] & (1 << self._level_shift))
|
||||||
|
|
||||||
def _set_state(self, value):
|
def _set_state(self, value):
|
||||||
if self.function == 'input':
|
if self.function == 'input':
|
||||||
raise PinSetInput('cannot set state of pin %r' % self)
|
raise PinSetInput('cannot set state of pin %r' % self)
|
||||||
if value:
|
if value:
|
||||||
self._MEM[self._set_offset] = 1 << self._set_shift
|
self.factory.mem[self._set_offset] = 1 << self._set_shift
|
||||||
else:
|
else:
|
||||||
self._MEM[self._clear_offset] = 1 << self._clear_shift
|
self.factory.mem[self._clear_offset] = 1 << self._clear_shift
|
||||||
|
|
||||||
def _get_pull(self):
|
def _get_pull(self):
|
||||||
return self.GPIO_PULL_UP_NAMES[self._pull]
|
return self.GPIO_PULL_UP_NAMES[self._pull]
|
||||||
@@ -292,23 +270,23 @@ class NativePin(LocalPin):
|
|||||||
def _set_pull(self, value):
|
def _set_pull(self, value):
|
||||||
if self.function != 'input':
|
if self.function != 'input':
|
||||||
raise PinFixedPull('cannot set pull on non-input pin %r' % self)
|
raise PinFixedPull('cannot set pull on non-input pin %r' % self)
|
||||||
if value != 'up' and self.PI_INFO.pulled_up('GPIO%d' % self.number):
|
if value != 'up' and self.factory.pi_info.pulled_up('GPIO%d' % self.number):
|
||||||
raise PinFixedPull('%r has a physical pull-up resistor' % self)
|
raise PinFixedPull('%r has a physical pull-up resistor' % self)
|
||||||
try:
|
try:
|
||||||
value = self.GPIO_PULL_UPS[value]
|
value = self.GPIO_PULL_UPS[value]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise PinInvalidPull('invalid pull direction "%s" for pin %r' % (value, self))
|
raise PinInvalidPull('invalid pull direction "%s" for pin %r' % (value, self))
|
||||||
self._MEM[self._MEM.GPPUD_OFFSET] = value
|
self.factory.mem[self.factory.mem.GPPUD_OFFSET] = value
|
||||||
sleep(0.000000214)
|
sleep(0.000000214)
|
||||||
self._MEM[self._pull_offset] = 1 << self._pull_shift
|
self.factory.mem[self._pull_offset] = 1 << self._pull_shift
|
||||||
sleep(0.000000214)
|
sleep(0.000000214)
|
||||||
self._MEM[self._MEM.GPPUD_OFFSET] = 0
|
self.factory.mem[self.factory.mem.GPPUD_OFFSET] = 0
|
||||||
self._MEM[self._pull_offset] = 0
|
self.factory.mem[self._pull_offset] = 0
|
||||||
self._pull = value
|
self._pull = value
|
||||||
|
|
||||||
def _get_edges(self):
|
def _get_edges(self):
|
||||||
rising = bool(self._MEM[self._rising_offset] & (1 << self._rising_shift))
|
rising = bool(self.factory.mem[self._rising_offset] & (1 << self._rising_shift))
|
||||||
falling = bool(self._MEM[self._falling_offset] & (1 << self._falling_shift))
|
falling = bool(self.factory.mem[self._falling_offset] & (1 << self._falling_shift))
|
||||||
return self.GPIO_EDGES_NAMES[(rising, falling)]
|
return self.GPIO_EDGES_NAMES[(rising, falling)]
|
||||||
|
|
||||||
def _set_edges(self, value):
|
def _set_edges(self, value):
|
||||||
@@ -319,13 +297,13 @@ class NativePin(LocalPin):
|
|||||||
f = self.when_changed
|
f = self.when_changed
|
||||||
self.when_changed = None
|
self.when_changed = None
|
||||||
try:
|
try:
|
||||||
self._MEM[self._rising_offset] = (
|
self.factory.mem[self._rising_offset] = (
|
||||||
self._MEM[self._rising_offset]
|
self.factory.mem[self._rising_offset]
|
||||||
& ~(1 << self._rising_shift)
|
& ~(1 << self._rising_shift)
|
||||||
| (rising << self._rising_shift)
|
| (rising << self._rising_shift)
|
||||||
)
|
)
|
||||||
self._MEM[self._falling_offset] = (
|
self.factory.mem[self._falling_offset] = (
|
||||||
self._MEM[self._falling_offset]
|
self.factory.mem[self._falling_offset]
|
||||||
& ~(1 << self._falling_shift)
|
& ~(1 << self._falling_shift)
|
||||||
| (falling << self._falling_shift)
|
| (falling << self._falling_shift)
|
||||||
)
|
)
|
||||||
@@ -353,9 +331,9 @@ class NativePin(LocalPin):
|
|||||||
def _change_watch(self):
|
def _change_watch(self):
|
||||||
offset = self._edge_offset
|
offset = self._edge_offset
|
||||||
mask = 1 << self._edge_shift
|
mask = 1 << self._edge_shift
|
||||||
self._MEM[offset] = mask # clear any existing detection bit
|
self.factory.mem[offset] = mask # clear any existing detection bit
|
||||||
while not self._change_event.wait(0.001):
|
while not self._change_event.wait(0.001):
|
||||||
if self._MEM[offset] & mask:
|
if self.factory.mem[offset] & mask:
|
||||||
self._MEM[offset] = mask
|
self.factory.mem[offset] = mask
|
||||||
self._when_changed()
|
self._when_changed()
|
||||||
|
|
||||||
|
|||||||
214
gpiozero/pins/pi.py
Normal file
214
gpiozero/pins/pi.py
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
from __future__ import (
|
||||||
|
unicode_literals,
|
||||||
|
absolute_import,
|
||||||
|
print_function,
|
||||||
|
division,
|
||||||
|
)
|
||||||
|
str = type('')
|
||||||
|
|
||||||
|
import io
|
||||||
|
import weakref
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
try:
|
||||||
|
from spidev import SpiDev
|
||||||
|
except ImportError:
|
||||||
|
SpiDev = None
|
||||||
|
|
||||||
|
from . import Factory, Pin
|
||||||
|
from .data import pi_info
|
||||||
|
from ..exc import (
|
||||||
|
PinNoPins,
|
||||||
|
PinNonPhysical,
|
||||||
|
PinInvalidPin,
|
||||||
|
SPIBadArgs,
|
||||||
|
SPISoftwareFallback,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PiFactory(Factory):
|
||||||
|
"""
|
||||||
|
Abstract base class representing hardware attached to a Raspberry Pi. This
|
||||||
|
forms the base of :class:`LocalPiFactory`.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self._info = None
|
||||||
|
self.pins = {}
|
||||||
|
self.pin_class = None
|
||||||
|
self.spi_hardware_class = None
|
||||||
|
self.spi_software_class = None
|
||||||
|
self.shared_spi_hardware_class = None
|
||||||
|
self.shared_spi_software_class = None
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
for pin in self.pins.values():
|
||||||
|
pin.close()
|
||||||
|
self.pins.clear()
|
||||||
|
|
||||||
|
def pin(self, spec):
|
||||||
|
n = self._to_gpio(spec)
|
||||||
|
try:
|
||||||
|
pin = self.pins[n]
|
||||||
|
except KeyError:
|
||||||
|
pin = self.pin_class(self, n)
|
||||||
|
self.pins[n] = pin
|
||||||
|
return pin
|
||||||
|
|
||||||
|
def pin_address(self, spec):
|
||||||
|
n = self._to_gpio(spec)
|
||||||
|
return self.address + ('GPIO%d' % n,)
|
||||||
|
|
||||||
|
def _to_gpio(self, spec):
|
||||||
|
"""
|
||||||
|
Converts the pin *spec* to a GPIO port number.
|
||||||
|
"""
|
||||||
|
if not 0 <= spec < 54:
|
||||||
|
raise PinInvalidPin('invalid GPIO port %d specified (range 0..53) ' % spec)
|
||||||
|
return spec
|
||||||
|
|
||||||
|
def _get_revision(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def _get_pi_info(self):
|
||||||
|
if self._info is None:
|
||||||
|
self._info = pi_info(self._get_revision())
|
||||||
|
return self._info
|
||||||
|
|
||||||
|
def spi(self, **spi_args):
|
||||||
|
"""
|
||||||
|
Returns an SPI interface, for the specified SPI *port* and *device*, or
|
||||||
|
for the specified pins (*clock_pin*, *mosi_pin*, *miso_pin*, and
|
||||||
|
*select_pin*). Only one of the schemes can be used; attempting to mix
|
||||||
|
*port* and *device* with pin numbers will raise :exc:`SPIBadArgs`.
|
||||||
|
|
||||||
|
If the pins specified match the hardware SPI pins (clock on GPIO11,
|
||||||
|
MOSI on GPIO10, MISO on GPIO9, and chip select on GPIO8 or GPIO7), and
|
||||||
|
the spidev module can be imported, a :class:`SPIHardwareInterface`
|
||||||
|
instance will be returned. Otherwise, a :class:`SPISoftwareInterface`
|
||||||
|
will be returned which will use simple bit-banging to communicate.
|
||||||
|
|
||||||
|
Both interfaces have the same API, support clock polarity and phase
|
||||||
|
attributes, and can handle half and full duplex communications, but the
|
||||||
|
hardware interface is significantly faster (though for many things this
|
||||||
|
doesn't matter).
|
||||||
|
"""
|
||||||
|
spi_args, kwargs = self._extract_spi_args(**spi_args)
|
||||||
|
shared = kwargs.pop('shared', False)
|
||||||
|
if kwargs:
|
||||||
|
raise SPIBadArgs(
|
||||||
|
'unrecognized keyword argument %s' % kwargs.popitem()[0])
|
||||||
|
if all((
|
||||||
|
spi_args['clock_pin'] == 11,
|
||||||
|
spi_args['mosi_pin'] == 10,
|
||||||
|
spi_args['miso_pin'] == 9,
|
||||||
|
spi_args['select_pin'] in (7, 8),
|
||||||
|
)):
|
||||||
|
try:
|
||||||
|
hardware_spi_args = {
|
||||||
|
'port': 0,
|
||||||
|
'device': {8: 0, 7: 1}[spi_args['select_pin']],
|
||||||
|
}
|
||||||
|
if shared:
|
||||||
|
return self.shared_spi_hardware_class(self, **hardware_spi_args)
|
||||||
|
else:
|
||||||
|
return self.spi_hardware_class(self, **hardware_spi_args)
|
||||||
|
except Exception as e:
|
||||||
|
warnings.warn(
|
||||||
|
SPISoftwareFallback(
|
||||||
|
'failed to initialize hardware SPI, falling back to '
|
||||||
|
'software (error was: %s)' % str(e)))
|
||||||
|
# Convert all pin arguments to integer GPIO numbers. This is necessary
|
||||||
|
# to ensure the shared-key for shared implementations get matched
|
||||||
|
# correctly, and is a bit of a hack for the pigpio bit-bang
|
||||||
|
# implementation which just wants the pin numbers too.
|
||||||
|
spi_args = {
|
||||||
|
key: pin.number if isinstance(pin, Pin) else pin
|
||||||
|
for key, pin in spi_args.items()
|
||||||
|
}
|
||||||
|
if shared:
|
||||||
|
return self.shared_spi_software_class(self, **spi_args)
|
||||||
|
else:
|
||||||
|
return self.spi_software_class(self, **spi_args)
|
||||||
|
|
||||||
|
def _extract_spi_args(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Given a set of keyword arguments, splits it into those relevant to SPI
|
||||||
|
implementations and all the rest. SPI arguments are augmented with
|
||||||
|
defaults and converted into the pin format (from the port/device
|
||||||
|
format) if necessary.
|
||||||
|
|
||||||
|
Returns a tuple of ``(spi_args, other_args)``.
|
||||||
|
"""
|
||||||
|
pin_defaults = {
|
||||||
|
'clock_pin': 11,
|
||||||
|
'mosi_pin': 10,
|
||||||
|
'miso_pin': 9,
|
||||||
|
'select_pin': 8,
|
||||||
|
}
|
||||||
|
dev_defaults = {
|
||||||
|
'port': 0,
|
||||||
|
'device': 0,
|
||||||
|
}
|
||||||
|
spi_args = {
|
||||||
|
key: value for (key, value) in kwargs.items()
|
||||||
|
if key in pin_defaults or key in dev_defaults
|
||||||
|
}
|
||||||
|
kwargs = {
|
||||||
|
key: value for (key, value) in kwargs.items()
|
||||||
|
if key not in spi_args
|
||||||
|
}
|
||||||
|
if not spi_args:
|
||||||
|
spi_args = pin_defaults
|
||||||
|
elif set(spi_args) <= set(pin_defaults):
|
||||||
|
spi_args = {
|
||||||
|
key: self._to_gpio(spi_args.get(key, default))
|
||||||
|
for key, default in pin_defaults.items()
|
||||||
|
}
|
||||||
|
elif set(spi_args) <= set(dev_defaults):
|
||||||
|
spi_args = {
|
||||||
|
key: spi_args.get(key, default)
|
||||||
|
for key, default in dev_defaults.items()
|
||||||
|
}
|
||||||
|
if spi_args['port'] != 0:
|
||||||
|
raise SPIBadArgs('port 0 is the only valid SPI port')
|
||||||
|
if spi_args['device'] not in (0, 1):
|
||||||
|
raise SPIBadArgs('device must be 0 or 1')
|
||||||
|
spi_args = {
|
||||||
|
key: value if key != 'select_pin' else (8, 7)[spi_args['device']]
|
||||||
|
for key, value in pin_defaults.items()
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
raise SPIBadArgs(
|
||||||
|
'you must either specify port and device, or clock_pin, '
|
||||||
|
'mosi_pin, miso_pin, and select_pin; combinations of the two '
|
||||||
|
'schemes (e.g. port and clock_pin) are not permitted')
|
||||||
|
return spi_args, kwargs
|
||||||
|
|
||||||
|
|
||||||
|
class PiPin(Pin):
|
||||||
|
"""
|
||||||
|
Abstract base class representing a multi-function GPIO pin attached to a
|
||||||
|
Raspberry Pi.
|
||||||
|
"""
|
||||||
|
def __init__(self, factory, number):
|
||||||
|
super(PiPin, self).__init__()
|
||||||
|
try:
|
||||||
|
factory.pi_info.physical_pin('GPIO%d' % number)
|
||||||
|
except PinNoPins:
|
||||||
|
warnings.warn(
|
||||||
|
PinNonPhysical(
|
||||||
|
'no physical pins exist for GPIO%d' % number))
|
||||||
|
self._factory = weakref.proxy(factory)
|
||||||
|
self._number = number
|
||||||
|
|
||||||
|
@property
|
||||||
|
def number(self):
|
||||||
|
return self._number
|
||||||
|
|
||||||
|
@property
|
||||||
|
def factory(self):
|
||||||
|
return self._factory
|
||||||
|
|
||||||
|
def _get_address(self):
|
||||||
|
return self.factory.address + ('GPIO%d' % self.number,)
|
||||||
|
|
||||||
@@ -6,12 +6,15 @@ from __future__ import (
|
|||||||
)
|
)
|
||||||
str = type('')
|
str = type('')
|
||||||
|
|
||||||
import warnings
|
import weakref
|
||||||
import pigpio
|
import pigpio
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from . import Pin
|
from . import SPI
|
||||||
|
from .pi import PiPin, PiFactory
|
||||||
from .data import pi_info
|
from .data import pi_info
|
||||||
|
from ..devices import Device
|
||||||
|
from ..mixins import SharedMixin
|
||||||
from ..exc import (
|
from ..exc import (
|
||||||
PinInvalidFunction,
|
PinInvalidFunction,
|
||||||
PinSetInput,
|
PinSetInput,
|
||||||
@@ -19,12 +22,12 @@ from ..exc import (
|
|||||||
PinInvalidPull,
|
PinInvalidPull,
|
||||||
PinInvalidBounce,
|
PinInvalidBounce,
|
||||||
PinInvalidState,
|
PinInvalidState,
|
||||||
PinNonPhysical,
|
SPIBadArgs,
|
||||||
PinNoPins,
|
SPIInvalidClockMode,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class PiGPIOPin(Pin):
|
class PiGPIOFactory(PiFactory):
|
||||||
"""
|
"""
|
||||||
Uses the `pigpio`_ library to interface to the Pi's GPIO pins. The pigpio
|
Uses the `pigpio`_ library to interface to the Pi's GPIO pins. The pigpio
|
||||||
library relies on a daemon (``pigpiod``) to be running as root to provide
|
library relies on a daemon (``pigpiod``) to be running as root to provide
|
||||||
@@ -68,10 +71,65 @@ class PiGPIOPin(Pin):
|
|||||||
|
|
||||||
.. _pigpio: http://abyz.co.uk/rpi/pigpio/
|
.. _pigpio: http://abyz.co.uk/rpi/pigpio/
|
||||||
"""
|
"""
|
||||||
|
def __init__(
|
||||||
|
self, host=os.getenv('PIGPIO_ADDR', 'localhost'),
|
||||||
|
port=int(os.getenv('PIGPIO_PORT', 8888))):
|
||||||
|
super(PiGPIOFactory, self).__init__()
|
||||||
|
self.pin_class = PiGPIOPin
|
||||||
|
self.spi_hardware_class = PiGPIOHardwareSPI
|
||||||
|
self.spi_software_class = PiGPIOSoftwareSPI
|
||||||
|
self.shared_spi_hardware_class = PiGPIOHardwareSPIShared
|
||||||
|
self.shared_spi_software_class = PiGPIOSoftwareSPIShared
|
||||||
|
self._connection = pigpio.pi(host, port)
|
||||||
|
self._host = host
|
||||||
|
self._port = port
|
||||||
|
self._spis = []
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
super(PiGPIOFactory, self).close()
|
||||||
|
# We *have* to keep track of SPI interfaces constructed with pigpio;
|
||||||
|
# if we fail to close them they prevent future interfaces from using
|
||||||
|
# the same pins
|
||||||
|
if self.connection:
|
||||||
|
while self._spis:
|
||||||
|
self._spis[0].close()
|
||||||
|
self.connection.stop()
|
||||||
|
self._connection = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def connection(self):
|
||||||
|
# If we're shutting down, the connection may have disconnected itself
|
||||||
|
# already. Unfortunately, the connection's "connected" property is
|
||||||
|
# rather buggy - disconnecting doesn't set it to False! So we're
|
||||||
|
# naughty and check an internal variable instead...
|
||||||
|
try:
|
||||||
|
if self._connection.sl.s is not None:
|
||||||
|
return self._connection
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def host(self):
|
||||||
|
return self._host
|
||||||
|
|
||||||
|
@property
|
||||||
|
def port(self):
|
||||||
|
return self._port
|
||||||
|
|
||||||
|
def _get_revision(self):
|
||||||
|
return self.connection.get_hardware_revision()
|
||||||
|
|
||||||
|
def _get_address(self):
|
||||||
|
return ("%s:%d" % (self.host, self.port),)
|
||||||
|
|
||||||
|
def spi(self, **spi_args):
|
||||||
|
intf = super(PiGPIOFactory, self).spi(**spi_args)
|
||||||
|
self._spis.append(intf)
|
||||||
|
return intf
|
||||||
|
|
||||||
|
|
||||||
|
class PiGPIOPin(PiPin):
|
||||||
_CONNECTIONS = {} # maps (host, port) to (connection, pi_info)
|
_CONNECTIONS = {} # maps (host, port) to (connection, pi_info)
|
||||||
_PINS = {}
|
|
||||||
|
|
||||||
GPIO_FUNCTIONS = {
|
GPIO_FUNCTIONS = {
|
||||||
'input': pigpio.INPUT,
|
'input': pigpio.INPUT,
|
||||||
'output': pigpio.OUTPUT,
|
'output': pigpio.OUTPUT,
|
||||||
@@ -99,101 +157,64 @@ class PiGPIOPin(Pin):
|
|||||||
GPIO_PULL_UP_NAMES = {v: k for (k, v) in GPIO_PULL_UPS.items()}
|
GPIO_PULL_UP_NAMES = {v: k for (k, v) in GPIO_PULL_UPS.items()}
|
||||||
GPIO_EDGES_NAMES = {v: k for (k, v) in GPIO_EDGES.items()}
|
GPIO_EDGES_NAMES = {v: k for (k, v) in GPIO_EDGES.items()}
|
||||||
|
|
||||||
def __new__(
|
def __init__(self, factory, number):
|
||||||
cls, number, host=os.getenv('PIGPIO_ADDR', 'localhost'),
|
super(PiGPIOPin, self).__init__(factory, number)
|
||||||
port=int(os.getenv('PIGPIO_PORT', 8888))):
|
self._pull = 'up' if factory.pi_info.pulled_up('GPIO%d' % number) else 'floating'
|
||||||
|
self._pwm = False
|
||||||
|
self._bounce = None
|
||||||
|
self._when_changed = None
|
||||||
|
self._callback = None
|
||||||
|
self._edges = pigpio.EITHER_EDGE
|
||||||
try:
|
try:
|
||||||
return cls._PINS[(host, port, number)]
|
self.factory.connection.set_mode(self.number, pigpio.INPUT)
|
||||||
except KeyError:
|
except pigpio.error as e:
|
||||||
self = super(PiGPIOPin, cls).__new__(cls)
|
raise ValueError(e)
|
||||||
cls.pi_info(host, port) # implicitly creates connection
|
self.factory.connection.set_pull_up_down(self.number, self.GPIO_PULL_UPS[self._pull])
|
||||||
self._connection, self._pi_info = cls._CONNECTIONS[(host, port)]
|
self.factory.connection.set_glitch_filter(self.number, 0)
|
||||||
try:
|
|
||||||
self._pi_info.physical_pin('GPIO%d' % number)
|
|
||||||
except PinNoPins:
|
|
||||||
warnings.warn(
|
|
||||||
PinNonPhysical(
|
|
||||||
'no physical pins exist for GPIO%d' % number))
|
|
||||||
self._host = host
|
|
||||||
self._port = port
|
|
||||||
self._number = number
|
|
||||||
self._pull = 'up' if self._pi_info.pulled_up('GPIO%d' % number) else 'floating'
|
|
||||||
self._pwm = False
|
|
||||||
self._bounce = None
|
|
||||||
self._when_changed = None
|
|
||||||
self._callback = None
|
|
||||||
self._edges = pigpio.EITHER_EDGE
|
|
||||||
try:
|
|
||||||
self._connection.set_mode(self._number, pigpio.INPUT)
|
|
||||||
except pigpio.error as e:
|
|
||||||
raise ValueError(e)
|
|
||||||
self._connection.set_pull_up_down(self._number, self.GPIO_PULL_UPS[self._pull])
|
|
||||||
self._connection.set_glitch_filter(self._number, 0)
|
|
||||||
cls._PINS[(host, port, number)] = self
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
if self._host == 'localhost':
|
|
||||||
return "GPIO%d" % self._number
|
|
||||||
else:
|
|
||||||
return "GPIO%d on %s:%d" % (self._number, self._host, self._port)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def host(self):
|
|
||||||
return self._host
|
|
||||||
|
|
||||||
@property
|
|
||||||
def port(self):
|
|
||||||
return self._port
|
|
||||||
|
|
||||||
@property
|
|
||||||
def number(self):
|
|
||||||
return self._number
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
# If we're shutting down, the connection may have disconnected itself
|
if self.factory.connection:
|
||||||
# already. Unfortunately, the connection's "connected" property is
|
|
||||||
# rather buggy - disconnecting doesn't set it to False! So we're
|
|
||||||
# naughty and check an internal variable instead...
|
|
||||||
if self._connection.sl.s is not None:
|
|
||||||
self.frequency = None
|
self.frequency = None
|
||||||
self.when_changed = None
|
self.when_changed = None
|
||||||
self.function = 'input'
|
self.function = 'input'
|
||||||
self.pull = 'up' if self._pi_info.pulled_up('GPIO%d' % self.number) else 'floating'
|
self.pull = 'up' if self.factory.pi_info.pulled_up('GPIO%d' % self.number) else 'floating'
|
||||||
|
|
||||||
|
def _get_address(self):
|
||||||
|
return self.factory.address + ('GPIO%d' % self.number,)
|
||||||
|
|
||||||
def _get_function(self):
|
def _get_function(self):
|
||||||
return self.GPIO_FUNCTION_NAMES[self._connection.get_mode(self._number)]
|
return self.GPIO_FUNCTION_NAMES[self.factory.connection.get_mode(self.number)]
|
||||||
|
|
||||||
def _set_function(self, value):
|
def _set_function(self, value):
|
||||||
if value != 'input':
|
if value != 'input':
|
||||||
self._pull = 'floating'
|
self._pull = 'floating'
|
||||||
try:
|
try:
|
||||||
self._connection.set_mode(self._number, self.GPIO_FUNCTIONS[value])
|
self.factory.connection.set_mode(self.number, self.GPIO_FUNCTIONS[value])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise PinInvalidFunction('invalid function "%s" for pin %r' % (value, self))
|
raise PinInvalidFunction('invalid function "%s" for pin %r' % (value, self))
|
||||||
|
|
||||||
def _get_state(self):
|
def _get_state(self):
|
||||||
if self._pwm:
|
if self._pwm:
|
||||||
return (
|
return (
|
||||||
self._connection.get_PWM_dutycycle(self._number) /
|
self.factory.connection.get_PWM_dutycycle(self.number) /
|
||||||
self._connection.get_PWM_range(self._number)
|
self.factory.connection.get_PWM_range(self.number)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return bool(self._connection.read(self._number))
|
return bool(self.factory.connection.read(self.number))
|
||||||
|
|
||||||
def _set_state(self, value):
|
def _set_state(self, value):
|
||||||
if self._pwm:
|
if self._pwm:
|
||||||
try:
|
try:
|
||||||
value = int(value * self._connection.get_PWM_range(self._number))
|
value = int(value * self.factory.connection.get_PWM_range(self.number))
|
||||||
if value != self._connection.get_PWM_dutycycle(self._number):
|
if value != self.factory.connection.get_PWM_dutycycle(self.number):
|
||||||
self._connection.set_PWM_dutycycle(self._number, value)
|
self.factory.connection.set_PWM_dutycycle(self.number, value)
|
||||||
except pigpio.error:
|
except pigpio.error:
|
||||||
raise PinInvalidState('invalid state "%s" for pin %r' % (value, self))
|
raise PinInvalidState('invalid state "%s" for pin %r' % (value, self))
|
||||||
elif self.function == 'input':
|
elif self.function == 'input':
|
||||||
raise PinSetInput('cannot set state of pin %r' % self)
|
raise PinSetInput('cannot set state of pin %r' % self)
|
||||||
else:
|
else:
|
||||||
# write forces pin to OUTPUT, hence the check above
|
# write forces pin to OUTPUT, hence the check above
|
||||||
self._connection.write(self._number, bool(value))
|
self.factory.connection.write(self.number, bool(value))
|
||||||
|
|
||||||
def _get_pull(self):
|
def _get_pull(self):
|
||||||
return self._pull
|
return self._pull
|
||||||
@@ -201,31 +222,31 @@ class PiGPIOPin(Pin):
|
|||||||
def _set_pull(self, value):
|
def _set_pull(self, value):
|
||||||
if self.function != 'input':
|
if self.function != 'input':
|
||||||
raise PinFixedPull('cannot set pull on non-input pin %r' % self)
|
raise PinFixedPull('cannot set pull on non-input pin %r' % self)
|
||||||
if value != 'up' and self._pi_info.pulled_up('GPIO%d' % self._number):
|
if value != 'up' and self.factory.pi_info.pulled_up('GPIO%d' % self.number):
|
||||||
raise PinFixedPull('%r has a physical pull-up resistor' % self)
|
raise PinFixedPull('%r has a physical pull-up resistor' % self)
|
||||||
try:
|
try:
|
||||||
self._connection.set_pull_up_down(self._number, self.GPIO_PULL_UPS[value])
|
self.factory.connection.set_pull_up_down(self.number, self.GPIO_PULL_UPS[value])
|
||||||
self._pull = value
|
self._pull = value
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise PinInvalidPull('invalid pull "%s" for pin %r' % (value, self))
|
raise PinInvalidPull('invalid pull "%s" for pin %r' % (value, self))
|
||||||
|
|
||||||
def _get_frequency(self):
|
def _get_frequency(self):
|
||||||
if self._pwm:
|
if self._pwm:
|
||||||
return self._connection.get_PWM_frequency(self._number)
|
return self.factory.connection.get_PWM_frequency(self.number)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _set_frequency(self, value):
|
def _set_frequency(self, value):
|
||||||
if not self._pwm and value is not None:
|
if not self._pwm and value is not None:
|
||||||
self._connection.set_PWM_frequency(self._number, value)
|
self.factory.connection.set_PWM_frequency(self.number, value)
|
||||||
self._connection.set_PWM_range(self._number, 10000)
|
self.factory.connection.set_PWM_range(self.number, 10000)
|
||||||
self._connection.set_PWM_dutycycle(self._number, 0)
|
self.factory.connection.set_PWM_dutycycle(self.number, 0)
|
||||||
self._pwm = True
|
self._pwm = True
|
||||||
elif self._pwm and value is not None:
|
elif self._pwm and value is not None:
|
||||||
if value != self._connection.get_PWM_frequency(self._number):
|
if value != self.factory.connection.get_PWM_frequency(self.number):
|
||||||
self._connection.set_PWM_frequency(self._number, value)
|
self.factory.connection.set_PWM_frequency(self.number, value)
|
||||||
self._connection.set_PWM_range(self._number, 10000)
|
self.factory.connection.set_PWM_range(self.number, 10000)
|
||||||
elif self._pwm and value is None:
|
elif self._pwm and value is None:
|
||||||
self._connection.write(self._number, 0)
|
self.factory.connection.write(self.number, 0)
|
||||||
self._pwm = False
|
self._pwm = False
|
||||||
|
|
||||||
def _get_bounce(self):
|
def _get_bounce(self):
|
||||||
@@ -236,7 +257,7 @@ class PiGPIOPin(Pin):
|
|||||||
value = 0
|
value = 0
|
||||||
elif value < 0:
|
elif value < 0:
|
||||||
raise PinInvalidBounce('bounce must be 0 or greater')
|
raise PinInvalidBounce('bounce must be 0 or greater')
|
||||||
self._connection.set_glitch_filter(self._number, int(value * 1000000))
|
self.factory.connection.set_glitch_filter(self.number, int(value * 1000000))
|
||||||
|
|
||||||
def _get_edges(self):
|
def _get_edges(self):
|
||||||
return self.GPIO_EDGES_NAMES[self._edges]
|
return self.GPIO_EDGES_NAMES[self._edges]
|
||||||
@@ -259,20 +280,224 @@ class PiGPIOPin(Pin):
|
|||||||
self._callback.cancel()
|
self._callback.cancel()
|
||||||
self._callback = None
|
self._callback = None
|
||||||
if value is not None:
|
if value is not None:
|
||||||
self._callback = self._connection.callback(
|
self._callback = self.factory.connection.callback(
|
||||||
self._number, self._edges,
|
self.number, self._edges,
|
||||||
lambda gpio, level, tick: value())
|
lambda gpio, level, tick: value())
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def pi_info(
|
class PiGPIOHardwareSPI(SPI, Device):
|
||||||
cls, host=os.getenv('PIGPIO_ADDR', 'localhost'),
|
def __init__(self, factory, port, device):
|
||||||
port=int(os.getenv('PIGPIO_PORT', 8888))):
|
self._port = port
|
||||||
try:
|
self._device = device
|
||||||
connection, info = cls._CONNECTIONS[(host, port)]
|
self._factory = weakref.proxy(factory)
|
||||||
except KeyError:
|
super(PiGPIOHardwareSPI, self).__init__()
|
||||||
connection = pigpio.pi(host, port)
|
self._reserve_pins(*(
|
||||||
revision = '%04x' % connection.get_hardware_revision()
|
factory.address + ('GPIO%d' % pin,)
|
||||||
info = pi_info(revision)
|
for pin in (11, 10, 9, (8, 7)[device])
|
||||||
cls._CONNECTIONS[(host, port)] = (connection, info)
|
))
|
||||||
return info
|
self._mode = 0
|
||||||
|
self._select_high = False
|
||||||
|
self._bits_per_word = 8
|
||||||
|
self._baud = 500000
|
||||||
|
self._handle = self._factory.connection.spi_open(
|
||||||
|
device, self._baud, self._spi_flags())
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
try:
|
||||||
|
self._factory._spis.remove(self)
|
||||||
|
except (ReferenceError, ValueError):
|
||||||
|
# If the factory has died already or we're not present in its
|
||||||
|
# internal list, ignore the error
|
||||||
|
pass
|
||||||
|
if not self.closed:
|
||||||
|
self._factory.connection.spi_close(self._handle)
|
||||||
|
self._handle = None
|
||||||
|
self._release_all()
|
||||||
|
super(PiGPIOHardwareSPI, self).close()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def closed(self):
|
||||||
|
return self._handle is None or self._factory.connection is None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def factory(self):
|
||||||
|
return self._factory
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
try:
|
||||||
|
self._check_open()
|
||||||
|
return 'SPI(port=%d, device=%d)' % (self._port, self._device)
|
||||||
|
except DeviceClosed:
|
||||||
|
return 'SPI(closed)'
|
||||||
|
|
||||||
|
def _spi_flags(self):
|
||||||
|
return (
|
||||||
|
self._mode << 0 |
|
||||||
|
self._select_high << (2 + self._device) |
|
||||||
|
self._bits_per_word << 16
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_clock_mode(self):
|
||||||
|
return self._clock_mode
|
||||||
|
|
||||||
|
def _set_clock_mode(self, value):
|
||||||
|
self._check_open()
|
||||||
|
if not 0 <= value < 4:
|
||||||
|
raise SPIInvalidClockmode("%d is not a valid SPI clock mode" % value)
|
||||||
|
self._factory.connection.spi_close(self._handle)
|
||||||
|
self._clock_mode = value
|
||||||
|
self._handle = self._factory.connection.spi_open(
|
||||||
|
self._device, self._baud, self._spi_flags())
|
||||||
|
|
||||||
|
def _get_select_high(self):
|
||||||
|
return self._select_high
|
||||||
|
|
||||||
|
def _set_select_high(self, value):
|
||||||
|
self._check_open()
|
||||||
|
self._factory.connection.spi_close(self._handle)
|
||||||
|
self._select_high = bool(value)
|
||||||
|
self._handle = self._factory.connection.spi_open(
|
||||||
|
self._device, self._baud, self._spi_flags())
|
||||||
|
|
||||||
|
def _get_bits_per_word(self):
|
||||||
|
return self._bits_per_word
|
||||||
|
|
||||||
|
def _set_bits_per_word(self, value):
|
||||||
|
self._check_open()
|
||||||
|
self._factory.connection.spi_close(self._handle)
|
||||||
|
self._bits_per_word = value
|
||||||
|
self._handle = self._factory.connection.spi_open(
|
||||||
|
self._device, self._baud, self._spi_flags())
|
||||||
|
|
||||||
|
def transfer(self, data):
|
||||||
|
self._check_open()
|
||||||
|
count, data = self._factory.connection.spi_xfer(self._handle, data)
|
||||||
|
if count < 0:
|
||||||
|
raise IOError('SPI transfer error %d' % count)
|
||||||
|
# Convert returned bytearray to list of ints. XXX Not sure how non-byte
|
||||||
|
# sized words (aux intf only) are returned ... padded to 16/32-bits?
|
||||||
|
return [int(b) for b in data]
|
||||||
|
|
||||||
|
|
||||||
|
class PiGPIOSoftwareSPI(SPI, Device):
|
||||||
|
def __init__(self, factory, clock_pin, mosi_pin, miso_pin, select_pin):
|
||||||
|
self._select_pin = None
|
||||||
|
self._factory = weakref.proxy(factory)
|
||||||
|
self._address = factory.address + (
|
||||||
|
)
|
||||||
|
super(PiGPIOSoftwareSPI, self).__init__()
|
||||||
|
self._reserve_pins(
|
||||||
|
factory.pin_address(clock_pin),
|
||||||
|
factory.pin_address(mosi_pin),
|
||||||
|
factory.pin_address(miso_pin),
|
||||||
|
factory.pin_address(select_pin),
|
||||||
|
)
|
||||||
|
self._mode = 0
|
||||||
|
self._select_high = False
|
||||||
|
self._lsb_first = False
|
||||||
|
self._baud = 100000
|
||||||
|
try:
|
||||||
|
self._factory.connection.bb_spi_open(
|
||||||
|
select_pin, miso_pin, mosi_pin, clock_pin,
|
||||||
|
self._baud, self._spi_flags())
|
||||||
|
# Only set after opening bb_spi; if that fails then close() will
|
||||||
|
# also fail if bb_spi_close is attempted on an un-open interface
|
||||||
|
self._select_pin = select_pin
|
||||||
|
self._clock_pin = clock_pin
|
||||||
|
self._mosi_pin = mosi_pin
|
||||||
|
self._miso_pin = miso_pin
|
||||||
|
except:
|
||||||
|
self.close()
|
||||||
|
raise
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
try:
|
||||||
|
self._factory._spis.remove(self)
|
||||||
|
except (ReferenceError, ValueError):
|
||||||
|
# If the factory has died already or we're not present in its
|
||||||
|
# internal list, ignore the error
|
||||||
|
pass
|
||||||
|
if not self.closed:
|
||||||
|
self._factory.connection.bb_spi_close(self._select_pin)
|
||||||
|
self._select_pin = None
|
||||||
|
self._release_all()
|
||||||
|
super(PiGPIOSoftwareSPI, self).close()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def closed(self):
|
||||||
|
return self._select_pin is None or self._factory.connection is None
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
try:
|
||||||
|
self._check_open()
|
||||||
|
return (
|
||||||
|
'SPI(clock_pin=%d, mosi_pin=%d, miso_pin=%d, select_pin=%d)' % (
|
||||||
|
self._clock_pin, self._mosi_pin, self._miso_pin, self._select_pin
|
||||||
|
))
|
||||||
|
except DeviceClosed:
|
||||||
|
return 'SPI(closed)'
|
||||||
|
|
||||||
|
def _spi_flags(self):
|
||||||
|
return (
|
||||||
|
self._mode << 0 |
|
||||||
|
self._select_high << 2 |
|
||||||
|
self._lsb_first << 14 |
|
||||||
|
self._lsb_first << 15
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_clock_mode(self):
|
||||||
|
return self._clock_mode
|
||||||
|
|
||||||
|
def _set_clock_mode(self, value):
|
||||||
|
self._check_open()
|
||||||
|
if not 0 <= value < 4:
|
||||||
|
raise SPIInvalidClockmode("%d is not a valid SPI clock mode" % value)
|
||||||
|
self._factory.connection.bb_spi_close(self._select_pin)
|
||||||
|
self._clock_mode = value
|
||||||
|
self._factory.connection.bb_spi_open(
|
||||||
|
self._select_pin, self._miso_pin, self._mosi_pin, self._clock_pin,
|
||||||
|
self._baud, self._spi_flags())
|
||||||
|
|
||||||
|
def _get_select_high(self):
|
||||||
|
return self._select_high
|
||||||
|
|
||||||
|
def _set_select_high(self, value):
|
||||||
|
self._check_open()
|
||||||
|
self._factory.connection.bb_spi_close(self._select_pin)
|
||||||
|
self._select_high = bool(value)
|
||||||
|
self._factory.connection.bb_spi_open(
|
||||||
|
self._select_pin, self._miso_pin, self._mosi_pin, self._clock_pin,
|
||||||
|
self._baud, self._spi_flags())
|
||||||
|
|
||||||
|
def _get_lsb_first(self):
|
||||||
|
return self._lsb_first
|
||||||
|
|
||||||
|
def _set_lsb_first(self, value):
|
||||||
|
self._check_open()
|
||||||
|
self._factory.connection.bb_spi_close(self._select_pin)
|
||||||
|
self._lsb_first = bool(value)
|
||||||
|
self._factory.connection.bb_spi_open(
|
||||||
|
self._select_pin, self._miso_pin, self._mosi_pin, self._clock_pin,
|
||||||
|
self._baud, self._spi_flags())
|
||||||
|
|
||||||
|
def transfer(self, data):
|
||||||
|
self._check_open()
|
||||||
|
count, data = self._factory.connection.bb_spi_xfer(self._select_pin, data)
|
||||||
|
if count < 0:
|
||||||
|
raise IOError('SPI transfer error %d' % count)
|
||||||
|
# Convert returned bytearray to list of ints. bb_spi only supports
|
||||||
|
# byte-sized words so no issues here
|
||||||
|
return [int(b) for b in data]
|
||||||
|
|
||||||
|
|
||||||
|
class PiGPIOHardwareSPIShared(SharedMixin, PiGPIOHardwareSPI):
|
||||||
|
@classmethod
|
||||||
|
def _shared_key(cls, factory, port, device):
|
||||||
|
return (factory, port, device)
|
||||||
|
|
||||||
|
|
||||||
|
class PiGPIOSoftwareSPIShared(SharedMixin, PiGPIOSoftwareSPI):
|
||||||
|
@classmethod
|
||||||
|
def _shared_key(cls, factory, clock_pin, mosi_pin, miso_pin, select_pin):
|
||||||
|
return (factory, select_pin)
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ from __future__ import (
|
|||||||
str = type('')
|
str = type('')
|
||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
import weakref
|
||||||
from RPi import GPIO
|
from RPi import GPIO
|
||||||
|
|
||||||
from . import LocalPin
|
from .local import LocalPiFactory, LocalPiPin
|
||||||
from .data import pi_info
|
|
||||||
from ..exc import (
|
from ..exc import (
|
||||||
PinInvalidFunction,
|
PinInvalidFunction,
|
||||||
PinSetInput,
|
PinSetInput,
|
||||||
@@ -19,12 +19,10 @@ from ..exc import (
|
|||||||
PinInvalidState,
|
PinInvalidState,
|
||||||
PinInvalidBounce,
|
PinInvalidBounce,
|
||||||
PinPWMFixedValue,
|
PinPWMFixedValue,
|
||||||
PinNonPhysical,
|
|
||||||
PinNoPins,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RPiGPIOPin(LocalPin):
|
class RPiGPIOFactory(LocalPiFactory):
|
||||||
"""
|
"""
|
||||||
Uses the `RPi.GPIO`_ library to interface to the Pi's GPIO pins. This is
|
Uses the `RPi.GPIO`_ library to interface to the Pi's GPIO pins. This is
|
||||||
the default pin implementation if the RPi.GPIO library is installed.
|
the default pin implementation if the RPi.GPIO library is installed.
|
||||||
@@ -39,7 +37,7 @@ class RPiGPIOPin(LocalPin):
|
|||||||
|
|
||||||
However, you can also construct RPi.GPIO pins manually if you wish::
|
However, you can also construct RPi.GPIO pins manually if you wish::
|
||||||
|
|
||||||
from gpiozero.pins.rpigpio import RPiGPIOPin
|
from gpiozero.pins.rpigpio import RPiGPIOFactory
|
||||||
from gpiozero import LED
|
from gpiozero import LED
|
||||||
|
|
||||||
led = LED(RPiGPIOPin(12))
|
led = LED(RPiGPIOPin(12))
|
||||||
@@ -47,8 +45,18 @@ class RPiGPIOPin(LocalPin):
|
|||||||
.. _RPi.GPIO: https://pypi.python.org/pypi/RPi.GPIO
|
.. _RPi.GPIO: https://pypi.python.org/pypi/RPi.GPIO
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_PINS = {}
|
def __init__(self):
|
||||||
|
super(RPiGPIOFactory, self).__init__()
|
||||||
|
GPIO.setmode(GPIO.BCM)
|
||||||
|
GPIO.setwarnings(False)
|
||||||
|
self.pin_class = RPiGPIOPin
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
super(RPiGPIOFactory, self).close()
|
||||||
|
GPIO.cleanup()
|
||||||
|
|
||||||
|
|
||||||
|
class RPiGPIOPin(LocalPiPin):
|
||||||
GPIO_FUNCTIONS = {
|
GPIO_FUNCTIONS = {
|
||||||
'input': GPIO.IN,
|
'input': GPIO.IN,
|
||||||
'output': GPIO.OUT,
|
'output': GPIO.OUT,
|
||||||
@@ -75,69 +83,43 @@ class RPiGPIOPin(LocalPin):
|
|||||||
GPIO_PULL_UP_NAMES = {v: k for (k, v) in GPIO_PULL_UPS.items()}
|
GPIO_PULL_UP_NAMES = {v: k for (k, v) in GPIO_PULL_UPS.items()}
|
||||||
GPIO_EDGES_NAMES = {v: k for (k, v) in GPIO_EDGES.items()}
|
GPIO_EDGES_NAMES = {v: k for (k, v) in GPIO_EDGES.items()}
|
||||||
|
|
||||||
PI_INFO = None
|
def __init__(self, factory, number):
|
||||||
|
super(RPiGPIOPin, self).__init__(factory, number)
|
||||||
def __new__(cls, number):
|
self._pull = 'up' if factory.pi_info.pulled_up('GPIO%d' % number) else 'floating'
|
||||||
if not cls._PINS:
|
self._pwm = None
|
||||||
GPIO.setmode(GPIO.BCM)
|
self._frequency = None
|
||||||
GPIO.setwarnings(False)
|
self._duty_cycle = None
|
||||||
if cls.PI_INFO is None:
|
self._bounce = -666
|
||||||
cls.PI_INFO = pi_info()
|
self._when_changed = None
|
||||||
try:
|
self._edges = GPIO.BOTH
|
||||||
return cls._PINS[number]
|
GPIO.setup(self.number, GPIO.IN, self.GPIO_PULL_UPS[self._pull])
|
||||||
except KeyError:
|
|
||||||
self = super(RPiGPIOPin, cls).__new__(cls)
|
|
||||||
try:
|
|
||||||
cls.PI_INFO.physical_pin('GPIO%d' % number)
|
|
||||||
except PinNoPins:
|
|
||||||
warnings.warn(
|
|
||||||
PinNonPhysical(
|
|
||||||
'no physical pins exist for GPIO%d' % number))
|
|
||||||
self._number = number
|
|
||||||
self._pull = 'up' if cls.PI_INFO.pulled_up('GPIO%d' % number) else 'floating'
|
|
||||||
self._pwm = None
|
|
||||||
self._frequency = None
|
|
||||||
self._duty_cycle = None
|
|
||||||
self._bounce = -666
|
|
||||||
self._when_changed = None
|
|
||||||
self._edges = GPIO.BOTH
|
|
||||||
GPIO.setup(self._number, GPIO.IN, self.GPIO_PULL_UPS[self._pull])
|
|
||||||
cls._PINS[number] = self
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "GPIO%d" % self._number
|
|
||||||
|
|
||||||
@property
|
|
||||||
def number(self):
|
|
||||||
return self._number
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.frequency = None
|
self.frequency = None
|
||||||
self.when_changed = None
|
self.when_changed = None
|
||||||
GPIO.cleanup(self._number)
|
GPIO.cleanup(self.number)
|
||||||
|
|
||||||
def output_with_state(self, state):
|
def output_with_state(self, state):
|
||||||
self._pull = 'floating'
|
self._pull = 'floating'
|
||||||
GPIO.setup(self._number, GPIO.OUT, initial=state)
|
GPIO.setup(self.number, GPIO.OUT, initial=state)
|
||||||
|
|
||||||
def input_with_pull(self, pull):
|
def input_with_pull(self, pull):
|
||||||
if pull != 'up' and self.PI_INFO.pulled_up('GPIO%d' % self._number):
|
if pull != 'up' and self.factory.pi_info.pulled_up('GPIO%d' % self.number):
|
||||||
raise PinFixedPull('%r has a physical pull-up resistor' % self)
|
raise PinFixedPull('%r has a physical pull-up resistor' % self)
|
||||||
try:
|
try:
|
||||||
GPIO.setup(self._number, GPIO.IN, self.GPIO_PULL_UPS[pull])
|
GPIO.setup(self.number, GPIO.IN, self.GPIO_PULL_UPS[pull])
|
||||||
self._pull = pull
|
self._pull = pull
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise PinInvalidPull('invalid pull "%s" for pin %r' % (pull, self))
|
raise PinInvalidPull('invalid pull "%s" for pin %r' % (pull, self))
|
||||||
|
|
||||||
def _get_function(self):
|
def _get_function(self):
|
||||||
return self.GPIO_FUNCTION_NAMES[GPIO.gpio_function(self._number)]
|
return self.GPIO_FUNCTION_NAMES[GPIO.gpio_function(self.number)]
|
||||||
|
|
||||||
def _set_function(self, value):
|
def _set_function(self, value):
|
||||||
if value != 'input':
|
if value != 'input':
|
||||||
self._pull = 'floating'
|
self._pull = 'floating'
|
||||||
if value in ('input', 'output') and value in self.GPIO_FUNCTIONS:
|
if value in ('input', 'output') and value in self.GPIO_FUNCTIONS:
|
||||||
GPIO.setup(self._number, self.GPIO_FUNCTIONS[value], self.GPIO_PULL_UPS[self._pull])
|
GPIO.setup(self.number, self.GPIO_FUNCTIONS[value], self.GPIO_PULL_UPS[self._pull])
|
||||||
else:
|
else:
|
||||||
raise PinInvalidFunction('invalid function "%s" for pin %r' % (value, self))
|
raise PinInvalidFunction('invalid function "%s" for pin %r' % (value, self))
|
||||||
|
|
||||||
@@ -145,7 +127,7 @@ class RPiGPIOPin(LocalPin):
|
|||||||
if self._pwm:
|
if self._pwm:
|
||||||
return self._duty_cycle
|
return self._duty_cycle
|
||||||
else:
|
else:
|
||||||
return GPIO.input(self._number)
|
return GPIO.input(self.number)
|
||||||
|
|
||||||
def _set_state(self, value):
|
def _set_state(self, value):
|
||||||
if self._pwm:
|
if self._pwm:
|
||||||
@@ -156,7 +138,7 @@ class RPiGPIOPin(LocalPin):
|
|||||||
self._duty_cycle = value
|
self._duty_cycle = value
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
GPIO.output(self._number, value)
|
GPIO.output(self.number, value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise PinInvalidState('invalid state "%s" for pin %r' % (value, self))
|
raise PinInvalidState('invalid state "%s" for pin %r' % (value, self))
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
@@ -168,10 +150,10 @@ class RPiGPIOPin(LocalPin):
|
|||||||
def _set_pull(self, value):
|
def _set_pull(self, value):
|
||||||
if self.function != 'input':
|
if self.function != 'input':
|
||||||
raise PinFixedPull('cannot set pull on non-input pin %r' % self)
|
raise PinFixedPull('cannot set pull on non-input pin %r' % self)
|
||||||
if value != 'up' and self.PI_INFO.pulled_up('GPIO%d' % self._number):
|
if value != 'up' and self.factory.pi_info.pulled_up('GPIO%d' % self.number):
|
||||||
raise PinFixedPull('%r has a physical pull-up resistor' % self)
|
raise PinFixedPull('%r has a physical pull-up resistor' % self)
|
||||||
try:
|
try:
|
||||||
GPIO.setup(self._number, GPIO.IN, self.GPIO_PULL_UPS[value])
|
GPIO.setup(self.number, GPIO.IN, self.GPIO_PULL_UPS[value])
|
||||||
self._pull = value
|
self._pull = value
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise PinInvalidPull('invalid pull "%s" for pin %r' % (value, self))
|
raise PinInvalidPull('invalid pull "%s" for pin %r' % (value, self))
|
||||||
@@ -182,7 +164,7 @@ class RPiGPIOPin(LocalPin):
|
|||||||
def _set_frequency(self, value):
|
def _set_frequency(self, value):
|
||||||
if self._frequency is None and value is not None:
|
if self._frequency is None and value is not None:
|
||||||
try:
|
try:
|
||||||
self._pwm = GPIO.PWM(self._number, value)
|
self._pwm = GPIO.PWM(self.number, value)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
raise PinPWMFixedValue('cannot start PWM on pin %r' % self)
|
raise PinPWMFixedValue('cannot start PWM on pin %r' % self)
|
||||||
self._pwm.start(0)
|
self._pwm.start(0)
|
||||||
@@ -228,11 +210,11 @@ class RPiGPIOPin(LocalPin):
|
|||||||
if self._when_changed is None and value is not None:
|
if self._when_changed is None and value is not None:
|
||||||
self._when_changed = value
|
self._when_changed = value
|
||||||
GPIO.add_event_detect(
|
GPIO.add_event_detect(
|
||||||
self._number, self._edges,
|
self.number, self._edges,
|
||||||
callback=lambda channel: self._when_changed(),
|
callback=lambda channel: self._when_changed(),
|
||||||
bouncetime=self._bounce)
|
bouncetime=self._bounce)
|
||||||
elif self._when_changed is not None and value is None:
|
elif self._when_changed is not None and value is None:
|
||||||
GPIO.remove_event_detect(self._number)
|
GPIO.remove_event_detect(self.number)
|
||||||
self._when_changed = None
|
self._when_changed = None
|
||||||
else:
|
else:
|
||||||
self._when_changed = value
|
self._when_changed = value
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ str = type('')
|
|||||||
|
|
||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
import weakref
|
||||||
import RPIO
|
import RPIO
|
||||||
import RPIO.PWM
|
import RPIO.PWM
|
||||||
from RPIO.Exceptions import InvalidChannelException
|
from RPIO.Exceptions import InvalidChannelException
|
||||||
|
|
||||||
from . import LocalPin, PINS_CLEANUP
|
from .local import LocalPiPin, LocalPiFactory
|
||||||
from .data import pi_info
|
from .data import pi_info
|
||||||
from ..exc import (
|
from ..exc import (
|
||||||
PinInvalidFunction,
|
PinInvalidFunction,
|
||||||
@@ -22,12 +23,10 @@ from ..exc import (
|
|||||||
PinInvalidBounce,
|
PinInvalidBounce,
|
||||||
PinInvalidState,
|
PinInvalidState,
|
||||||
PinPWMError,
|
PinPWMError,
|
||||||
PinNonPhysical,
|
|
||||||
PinNoPins,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RPIOPin(LocalPin):
|
class RPIOFactory(LocalPiFactory):
|
||||||
"""
|
"""
|
||||||
Uses the `RPIO`_ library to interface to the Pi's GPIO pins. This is
|
Uses the `RPIO`_ library to interface to the Pi's GPIO pins. This is
|
||||||
the default pin implementation if the RPi.GPIO library is not installed,
|
the default pin implementation if the RPi.GPIO library is not installed,
|
||||||
@@ -48,9 +47,22 @@ class RPIOPin(LocalPin):
|
|||||||
|
|
||||||
.. _RPIO: https://pythonhosted.org/RPIO/
|
.. _RPIO: https://pythonhosted.org/RPIO/
|
||||||
"""
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
super(RPIOFactory, self).__init__()
|
||||||
|
RPIO.setmode(RPIO.BCM)
|
||||||
|
RPIO.setwarnings(False)
|
||||||
|
RPIO.wait_for_interrupts(threaded=True)
|
||||||
|
RPIO.PWM.setup()
|
||||||
|
RPIO.PWM.init_channel(0, 10000)
|
||||||
|
self.pin_class = RPIOPin
|
||||||
|
|
||||||
_PINS = {}
|
def close(self):
|
||||||
|
RPIO.PWM.cleanup()
|
||||||
|
RPIO.stop_waiting_for_interrupts()
|
||||||
|
RPIO.cleanup()
|
||||||
|
|
||||||
|
|
||||||
|
class RPIOPin(LocalPiPin):
|
||||||
GPIO_FUNCTIONS = {
|
GPIO_FUNCTIONS = {
|
||||||
'input': RPIO.IN,
|
'input': RPIO.IN,
|
||||||
'output': RPIO.OUT,
|
'output': RPIO.OUT,
|
||||||
@@ -66,64 +78,32 @@ class RPIOPin(LocalPin):
|
|||||||
GPIO_FUNCTION_NAMES = {v: k for (k, v) in GPIO_FUNCTIONS.items()}
|
GPIO_FUNCTION_NAMES = {v: k for (k, v) in GPIO_FUNCTIONS.items()}
|
||||||
GPIO_PULL_UP_NAMES = {v: k for (k, v) in GPIO_PULL_UPS.items()}
|
GPIO_PULL_UP_NAMES = {v: k for (k, v) in GPIO_PULL_UPS.items()}
|
||||||
|
|
||||||
PI_INFO = None
|
def __init__(self, factory, number):
|
||||||
|
super(RPIOPin, self).__init__(factory, number)
|
||||||
def __new__(cls, number):
|
self._pull = 'up' if factory.pi_info.pulled_up('GPIO%d' % number) else 'floating'
|
||||||
if not cls._PINS:
|
self._pwm = False
|
||||||
RPIO.setmode(RPIO.BCM)
|
self._duty_cycle = None
|
||||||
RPIO.setwarnings(False)
|
self._bounce = None
|
||||||
RPIO.wait_for_interrupts(threaded=True)
|
self._when_changed = None
|
||||||
RPIO.PWM.setup()
|
self._edges = 'both'
|
||||||
RPIO.PWM.init_channel(0, 10000)
|
|
||||||
PINS_CLEANUP.append(RPIO.PWM.cleanup)
|
|
||||||
PINS_CLEANUP.append(RPIO.stop_waiting_for_interrupts)
|
|
||||||
PINS_CLEANUP.append(RPIO.cleanup)
|
|
||||||
if cls.PI_INFO is None:
|
|
||||||
cls.PI_INFO = pi_info()
|
|
||||||
try:
|
try:
|
||||||
return cls._PINS[number]
|
RPIO.setup(self.number, RPIO.IN, self.GPIO_PULL_UPS[self._pull])
|
||||||
except KeyError:
|
except InvalidChannelException as e:
|
||||||
self = super(RPIOPin, cls).__new__(cls)
|
raise ValueError(e)
|
||||||
try:
|
|
||||||
cls.PI_INFO.physical_pin('GPIO%d' % number)
|
|
||||||
except PinNoPins:
|
|
||||||
warnings.warn(
|
|
||||||
PinNonPhysical(
|
|
||||||
'no physical pins exist for GPIO%d' % number))
|
|
||||||
self._number = number
|
|
||||||
self._pull = 'up' if cls.PI_INFO.pulled_up('GPIO%d' % number) else 'floating'
|
|
||||||
self._pwm = False
|
|
||||||
self._duty_cycle = None
|
|
||||||
self._bounce = None
|
|
||||||
self._when_changed = None
|
|
||||||
self._edges = 'both'
|
|
||||||
try:
|
|
||||||
RPIO.setup(self._number, RPIO.IN, self.GPIO_PULL_UPS[self._pull])
|
|
||||||
except InvalidChannelException as e:
|
|
||||||
raise ValueError(e)
|
|
||||||
cls._PINS[number] = self
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "GPIO%d" % self._number
|
|
||||||
|
|
||||||
@property
|
|
||||||
def number(self):
|
|
||||||
return self._number
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.frequency = None
|
self.frequency = None
|
||||||
self.when_changed = None
|
self.when_changed = None
|
||||||
RPIO.setup(self._number, RPIO.IN, RPIO.PUD_OFF)
|
RPIO.setup(self.number, RPIO.IN, RPIO.PUD_OFF)
|
||||||
|
|
||||||
def _get_function(self):
|
def _get_function(self):
|
||||||
return self.GPIO_FUNCTION_NAMES[RPIO.gpio_function(self._number)]
|
return self.GPIO_FUNCTION_NAMES[RPIO.gpio_function(self.number)]
|
||||||
|
|
||||||
def _set_function(self, value):
|
def _set_function(self, value):
|
||||||
if value != 'input':
|
if value != 'input':
|
||||||
self._pull = 'floating'
|
self._pull = 'floating'
|
||||||
try:
|
try:
|
||||||
RPIO.setup(self._number, self.GPIO_FUNCTIONS[value], self.GPIO_PULL_UPS[self._pull])
|
RPIO.setup(self.number, self.GPIO_FUNCTIONS[value], self.GPIO_PULL_UPS[self._pull])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise PinInvalidFunction('invalid function "%s" for pin %r' % (value, self))
|
raise PinInvalidFunction('invalid function "%s" for pin %r' % (value, self))
|
||||||
|
|
||||||
@@ -131,23 +111,23 @@ class RPIOPin(LocalPin):
|
|||||||
if self._pwm:
|
if self._pwm:
|
||||||
return self._duty_cycle
|
return self._duty_cycle
|
||||||
else:
|
else:
|
||||||
return RPIO.input(self._number)
|
return RPIO.input(self.number)
|
||||||
|
|
||||||
def _set_state(self, value):
|
def _set_state(self, value):
|
||||||
if not 0 <= value <= 1:
|
if not 0 <= value <= 1:
|
||||||
raise PinInvalidState('invalid state "%s" for pin %r' % (value, self))
|
raise PinInvalidState('invalid state "%s" for pin %r' % (value, self))
|
||||||
if self._pwm:
|
if self._pwm:
|
||||||
RPIO.PWM.clear_channel_gpio(0, self._number)
|
RPIO.PWM.clear_channel_gpio(0, self.number)
|
||||||
if value == 0:
|
if value == 0:
|
||||||
RPIO.output(self._number, False)
|
RPIO.output(self.number, False)
|
||||||
elif value == 1:
|
elif value == 1:
|
||||||
RPIO.output(self._number, True)
|
RPIO.output(self.number, True)
|
||||||
else:
|
else:
|
||||||
RPIO.PWM.add_channel_pulse(0, self._number, start=0, width=int(1000 * value))
|
RPIO.PWM.add_channel_pulse(0, self.number, start=0, width=int(1000 * value))
|
||||||
self._duty_cycle = value
|
self._duty_cycle = value
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
RPIO.output(self._number, value)
|
RPIO.output(self.number, value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise PinInvalidState('invalid state "%s" for pin %r' % (value, self))
|
raise PinInvalidState('invalid state "%s" for pin %r' % (value, self))
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
@@ -159,10 +139,10 @@ class RPIOPin(LocalPin):
|
|||||||
def _set_pull(self, value):
|
def _set_pull(self, value):
|
||||||
if self.function != 'input':
|
if self.function != 'input':
|
||||||
raise PinFixedPull('cannot set pull on non-input pin %r' % self)
|
raise PinFixedPull('cannot set pull on non-input pin %r' % self)
|
||||||
if value != 'up' and self.PI_INFO.pulled_up('GPIO%d' % self._number):
|
if value != 'up' and self.factory.pi_info.pulled_up('GPIO%d' % self.number):
|
||||||
raise PinFixedPull('%r has a physical pull-up resistor' % self)
|
raise PinFixedPull('%r has a physical pull-up resistor' % self)
|
||||||
try:
|
try:
|
||||||
RPIO.setup(self._number, RPIO.IN, self.GPIO_PULL_UPS[value])
|
RPIO.setup(self.number, RPIO.IN, self.GPIO_PULL_UPS[value])
|
||||||
self._pull = value
|
self._pull = value
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise PinInvalidPull('invalid pull "%s" for pin %r' % (value, self))
|
raise PinInvalidPull('invalid pull "%s" for pin %r' % (value, self))
|
||||||
@@ -182,10 +162,10 @@ class RPIOPin(LocalPin):
|
|||||||
self._pwm = True
|
self._pwm = True
|
||||||
# Dirty hack to get RPIO's PWM support to setup, but do nothing,
|
# Dirty hack to get RPIO's PWM support to setup, but do nothing,
|
||||||
# for a given GPIO pin
|
# for a given GPIO pin
|
||||||
RPIO.PWM.add_channel_pulse(0, self._number, start=0, width=0)
|
RPIO.PWM.add_channel_pulse(0, self.number, start=0, width=0)
|
||||||
RPIO.PWM.clear_channel_gpio(0, self._number)
|
RPIO.PWM.clear_channel_gpio(0, self.number)
|
||||||
elif self._pwm and value is None:
|
elif self._pwm and value is None:
|
||||||
RPIO.PWM.clear_channel_gpio(0, self._number)
|
RPIO.PWM.clear_channel_gpio(0, self.number)
|
||||||
self._pwm = False
|
self._pwm = False
|
||||||
|
|
||||||
def _get_bounce(self):
|
def _get_bounce(self):
|
||||||
@@ -219,12 +199,12 @@ class RPIOPin(LocalPin):
|
|||||||
if self._when_changed is None and value is not None:
|
if self._when_changed is None and value is not None:
|
||||||
self._when_changed = value
|
self._when_changed = value
|
||||||
RPIO.add_interrupt_callback(
|
RPIO.add_interrupt_callback(
|
||||||
self._number,
|
self.number,
|
||||||
lambda channel, value: self._when_changed(),
|
lambda channel, value: self._when_changed(),
|
||||||
self._edges, self.GPIO_PULL_UPS[self._pull], self._bounce)
|
self._edges, self.GPIO_PULL_UPS[self._pull], self._bounce)
|
||||||
elif self._when_changed is not None and value is None:
|
elif self._when_changed is not None and value is None:
|
||||||
try:
|
try:
|
||||||
RPIO.del_interrupt_callback(self._number)
|
RPIO.del_interrupt_callback(self.number)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# Ignore this exception which occurs during shutdown; this
|
# Ignore this exception which occurs during shutdown; this
|
||||||
# simply means RPIO's built-in cleanup has already run and
|
# simply means RPIO's built-in cleanup has already run and
|
||||||
|
|||||||
86
gpiozero/pins/spi.py
Normal file
86
gpiozero/pins/spi.py
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
from __future__ import (
|
||||||
|
unicode_literals,
|
||||||
|
print_function,
|
||||||
|
absolute_import,
|
||||||
|
division,
|
||||||
|
)
|
||||||
|
str = type('')
|
||||||
|
|
||||||
|
|
||||||
|
import operator
|
||||||
|
from threading import RLock
|
||||||
|
|
||||||
|
from ..devices import Device, SharedMixin
|
||||||
|
from ..input_devices import InputDevice
|
||||||
|
from ..output_devices import OutputDevice
|
||||||
|
|
||||||
|
|
||||||
|
class SPISoftwareBus(SharedMixin, Device):
|
||||||
|
def __init__(self, clock_pin, mosi_pin, miso_pin):
|
||||||
|
self.lock = None
|
||||||
|
self.clock = None
|
||||||
|
self.mosi = None
|
||||||
|
self.miso = None
|
||||||
|
super(SPISoftwareBus, self).__init__()
|
||||||
|
self.lock = RLock()
|
||||||
|
try:
|
||||||
|
self.clock = OutputDevice(clock_pin, active_high=True)
|
||||||
|
if mosi_pin is not None:
|
||||||
|
self.mosi = OutputDevice(mosi_pin)
|
||||||
|
if miso_pin is not None:
|
||||||
|
self.miso = InputDevice(miso_pin)
|
||||||
|
except:
|
||||||
|
self.close()
|
||||||
|
raise
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
super(SPISoftwareBus, self).close()
|
||||||
|
if self.lock:
|
||||||
|
with self.lock:
|
||||||
|
if self.miso is not None:
|
||||||
|
self.miso.close()
|
||||||
|
self.miso = None
|
||||||
|
if self.mosi is not None:
|
||||||
|
self.mosi.close()
|
||||||
|
self.mosi = None
|
||||||
|
if self.clock is not None:
|
||||||
|
self.clock.close()
|
||||||
|
self.clock = None
|
||||||
|
self.lock = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def closed(self):
|
||||||
|
return self.lock is None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _shared_key(cls, clock_pin, mosi_pin, miso_pin):
|
||||||
|
return (clock_pin, mosi_pin, miso_pin)
|
||||||
|
|
||||||
|
def transfer(self, data, clock_phase=False, lsb_first=False, bits_per_word=8):
|
||||||
|
"""
|
||||||
|
Writes data (a list of integer words where each word is assumed to have
|
||||||
|
:attr:`bits_per_word` bits or less) to the SPI interface, and reads an
|
||||||
|
equivalent number of words, returning them as a list of integers.
|
||||||
|
"""
|
||||||
|
result = []
|
||||||
|
with self.lock:
|
||||||
|
shift = operator.lshift if lsb_first else operator.rshift
|
||||||
|
for write_word in data:
|
||||||
|
mask = 1 if lsb_first else 1 << (bits_per_word - 1)
|
||||||
|
read_word = 0
|
||||||
|
for _ in range(bits_per_word):
|
||||||
|
if self.mosi is not None:
|
||||||
|
self.mosi.value = bool(write_word & mask)
|
||||||
|
self.clock.on()
|
||||||
|
if self.miso is not None and not clock_phase:
|
||||||
|
if self.miso.value:
|
||||||
|
read_word |= mask
|
||||||
|
self.clock.off()
|
||||||
|
if self.miso is not None and clock_phase:
|
||||||
|
if self.miso.value:
|
||||||
|
read_word |= mask
|
||||||
|
mask = shift(mask, 1)
|
||||||
|
result.append(read_word)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
419
gpiozero/spi.py
419
gpiozero/spi.py
@@ -1,419 +0,0 @@
|
|||||||
from __future__ import (
|
|
||||||
unicode_literals,
|
|
||||||
print_function,
|
|
||||||
absolute_import,
|
|
||||||
division,
|
|
||||||
)
|
|
||||||
str = type('')
|
|
||||||
|
|
||||||
|
|
||||||
import warnings
|
|
||||||
import operator
|
|
||||||
from threading import RLock
|
|
||||||
|
|
||||||
try:
|
|
||||||
from spidev import SpiDev
|
|
||||||
except ImportError:
|
|
||||||
SpiDev = None
|
|
||||||
|
|
||||||
from .devices import Device, SharedMixin, _PINS, _PINS_LOCK
|
|
||||||
from .input_devices import InputDevice
|
|
||||||
from .output_devices import OutputDevice
|
|
||||||
from .exc import SPIBadArgs, SPISoftwareFallback, GPIOPinInUse, DeviceClosed
|
|
||||||
|
|
||||||
|
|
||||||
class SPIHardwareInterface(Device):
|
|
||||||
def __init__(self, port, device):
|
|
||||||
self._device = None
|
|
||||||
super(SPIHardwareInterface, self).__init__()
|
|
||||||
# XXX How can we detect conflicts with existing GPIO instances? This
|
|
||||||
# isn't ideal ... in fact, it's downright crap and doesn't guard
|
|
||||||
# against conflicts created *after* this instance, but it's all I can
|
|
||||||
# come up with right now ...
|
|
||||||
conflicts = (11, 10, 9, (8, 7)[device])
|
|
||||||
with _PINS_LOCK:
|
|
||||||
for pin in _PINS:
|
|
||||||
if pin.number in conflicts:
|
|
||||||
raise GPIOPinInUse(
|
|
||||||
'pin %r is already in use by another gpiozero object' % pin
|
|
||||||
)
|
|
||||||
self._device_num = device
|
|
||||||
self._device = SpiDev()
|
|
||||||
self._device.open(port, device)
|
|
||||||
self._device.max_speed_hz = 500000
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
if self._device:
|
|
||||||
try:
|
|
||||||
self._device.close()
|
|
||||||
finally:
|
|
||||||
self._device = None
|
|
||||||
super(SPIHardwareInterface, self).close()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def closed(self):
|
|
||||||
return self._device is None
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
try:
|
|
||||||
self._check_open()
|
|
||||||
return (
|
|
||||||
"hardware SPI on clock_pin=11, mosi_pin=10, miso_pin=9, "
|
|
||||||
"select_pin=%d" % (
|
|
||||||
8 if self._device_num == 0 else 7))
|
|
||||||
except DeviceClosed:
|
|
||||||
return "hardware SPI closed"
|
|
||||||
|
|
||||||
def read(self, n):
|
|
||||||
return self.transfer((0,) * n)
|
|
||||||
|
|
||||||
def write(self, data):
|
|
||||||
return len(self.transfer(data))
|
|
||||||
|
|
||||||
def transfer(self, data):
|
|
||||||
"""
|
|
||||||
Writes data (a list of integer words where each word is assumed to have
|
|
||||||
:attr:`bits_per_word` bits or less) to the SPI interface, and reads an
|
|
||||||
equivalent number of words, returning them as a list of integers.
|
|
||||||
"""
|
|
||||||
return self._device.xfer2(data)
|
|
||||||
|
|
||||||
def _get_clock_mode(self):
|
|
||||||
return self._device.mode
|
|
||||||
|
|
||||||
def _set_clock_mode(self, value):
|
|
||||||
self._device.mode = value
|
|
||||||
|
|
||||||
def _get_clock_polarity(self):
|
|
||||||
return bool(self.clock_mode & 2)
|
|
||||||
|
|
||||||
def _set_clock_polarity(self, value):
|
|
||||||
self.clock_mode = self.clock_mode & (~2) | (bool(value) << 1)
|
|
||||||
|
|
||||||
def _get_clock_phase(self):
|
|
||||||
return bool(self.clock_mode & 1)
|
|
||||||
|
|
||||||
def _set_clock_phase(self, value):
|
|
||||||
self.clock_mode = self.clock_mode & (~1) | bool(value)
|
|
||||||
|
|
||||||
def _get_lsb_first(self):
|
|
||||||
return self._device.lsbfirst
|
|
||||||
|
|
||||||
def _set_lsb_first(self, value):
|
|
||||||
self._device.lsbfirst = bool(value)
|
|
||||||
|
|
||||||
def _get_select_high(self):
|
|
||||||
return self._device.cshigh
|
|
||||||
|
|
||||||
def _set_select_high(self, value):
|
|
||||||
self._device.cshigh = bool(value)
|
|
||||||
|
|
||||||
def _get_bits_per_word(self):
|
|
||||||
return self._device.bits_per_word
|
|
||||||
|
|
||||||
def _set_bits_per_word(self, value):
|
|
||||||
self._device.bits_per_word = value
|
|
||||||
|
|
||||||
clock_polarity = property(_get_clock_polarity, _set_clock_polarity)
|
|
||||||
clock_phase = property(_get_clock_phase, _set_clock_phase)
|
|
||||||
clock_mode = property(_get_clock_mode, _set_clock_mode)
|
|
||||||
lsb_first = property(_get_lsb_first, _set_lsb_first)
|
|
||||||
select_high = property(_get_select_high, _set_select_high)
|
|
||||||
bits_per_word = property(_get_bits_per_word, _set_bits_per_word)
|
|
||||||
|
|
||||||
|
|
||||||
class SPISoftwareBus(SharedMixin, Device):
|
|
||||||
def __init__(self, clock_pin, mosi_pin, miso_pin):
|
|
||||||
self.lock = None
|
|
||||||
self.clock = None
|
|
||||||
self.mosi = None
|
|
||||||
self.miso = None
|
|
||||||
super(SPISoftwareBus, self).__init__()
|
|
||||||
self.lock = RLock()
|
|
||||||
try:
|
|
||||||
self.clock = OutputDevice(clock_pin, active_high=True)
|
|
||||||
if mosi_pin is not None:
|
|
||||||
self.mosi = OutputDevice(mosi_pin)
|
|
||||||
if miso_pin is not None:
|
|
||||||
self.miso = InputDevice(miso_pin)
|
|
||||||
except:
|
|
||||||
self.close()
|
|
||||||
raise
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
super(SPISoftwareBus, self).close()
|
|
||||||
if self.lock:
|
|
||||||
with self.lock:
|
|
||||||
if self.miso is not None:
|
|
||||||
self.miso.close()
|
|
||||||
self.miso = None
|
|
||||||
if self.mosi is not None:
|
|
||||||
self.mosi.close()
|
|
||||||
self.mosi = None
|
|
||||||
if self.clock is not None:
|
|
||||||
self.clock.close()
|
|
||||||
self.clock = None
|
|
||||||
self.lock = None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def closed(self):
|
|
||||||
return self.lock is None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _shared_key(cls, clock_pin, mosi_pin, miso_pin):
|
|
||||||
return (clock_pin, mosi_pin, miso_pin)
|
|
||||||
|
|
||||||
def transfer(self, data, clock_phase=False, lsb_first=False, bits_per_word=8):
|
|
||||||
"""
|
|
||||||
Writes data (a list of integer words where each word is assumed to have
|
|
||||||
:attr:`bits_per_word` bits or less) to the SPI interface, and reads an
|
|
||||||
equivalent number of words, returning them as a list of integers.
|
|
||||||
"""
|
|
||||||
result = []
|
|
||||||
with self.lock:
|
|
||||||
shift = operator.lshift if lsb_first else operator.rshift
|
|
||||||
for write_word in data:
|
|
||||||
mask = 1 if lsb_first else 1 << (bits_per_word - 1)
|
|
||||||
read_word = 0
|
|
||||||
for _ in range(bits_per_word):
|
|
||||||
if self.mosi is not None:
|
|
||||||
self.mosi.value = bool(write_word & mask)
|
|
||||||
self.clock.on()
|
|
||||||
if self.miso is not None and not clock_phase:
|
|
||||||
if self.miso.value:
|
|
||||||
read_word |= mask
|
|
||||||
self.clock.off()
|
|
||||||
if self.miso is not None and clock_phase:
|
|
||||||
if self.miso.value:
|
|
||||||
read_word |= mask
|
|
||||||
mask = shift(mask, 1)
|
|
||||||
result.append(read_word)
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
class SPISoftwareInterface(OutputDevice):
|
|
||||||
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin):
|
|
||||||
self._bus = None
|
|
||||||
super(SPISoftwareInterface, self).__init__(select_pin, active_high=False)
|
|
||||||
try:
|
|
||||||
self._clock_phase = False
|
|
||||||
self._lsb_first = False
|
|
||||||
self._bits_per_word = 8
|
|
||||||
self._bus = SPISoftwareBus(clock_pin, mosi_pin, miso_pin)
|
|
||||||
except:
|
|
||||||
self.close()
|
|
||||||
raise
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
if self._bus:
|
|
||||||
self._bus.close()
|
|
||||||
self._bus = None
|
|
||||||
super(SPISoftwareInterface, self).close()
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
try:
|
|
||||||
self._check_open()
|
|
||||||
return (
|
|
||||||
"software SPI on clock_pin=%d, mosi_pin=%d, miso_pin=%d, "
|
|
||||||
"select_pin=%d" % (
|
|
||||||
self._bus.clock.pin.number,
|
|
||||||
self._bus.mosi.pin.number,
|
|
||||||
self._bus.miso.pin.number,
|
|
||||||
self.pin.number))
|
|
||||||
except DeviceClosed:
|
|
||||||
return "software SPI closed"
|
|
||||||
|
|
||||||
def read(self, n):
|
|
||||||
return self.transfer((0,) * n)
|
|
||||||
|
|
||||||
def write(self, data):
|
|
||||||
return len(self.transfer(data))
|
|
||||||
|
|
||||||
def transfer(self, data):
|
|
||||||
with self._bus.lock:
|
|
||||||
self.on()
|
|
||||||
try:
|
|
||||||
return self._bus.transfer(
|
|
||||||
data, self._clock_phase, self._lsb_first, self._bits_per_word)
|
|
||||||
finally:
|
|
||||||
self.off()
|
|
||||||
|
|
||||||
def _get_clock_mode(self):
|
|
||||||
return (self.clock_polarity << 1) | self.clock_phase
|
|
||||||
|
|
||||||
def _set_clock_mode(self, value):
|
|
||||||
value = int(value)
|
|
||||||
if not 0 <= value <= 3:
|
|
||||||
raise ValueError('clock_mode must be a value between 0 and 3 inclusive')
|
|
||||||
self.clock_polarity = bool(value & 2)
|
|
||||||
self.clock_phase = bool(value & 1)
|
|
||||||
|
|
||||||
def _get_clock_polarity(self):
|
|
||||||
with self._bus.lock:
|
|
||||||
return not self._bus.clock.active_high
|
|
||||||
|
|
||||||
def _set_clock_polarity(self, value):
|
|
||||||
with self._bus.lock:
|
|
||||||
self._bus.clock.active_high = not value
|
|
||||||
self._bus.clock.off()
|
|
||||||
|
|
||||||
def _get_clock_phase(self):
|
|
||||||
return self._clock_phase
|
|
||||||
|
|
||||||
def _set_clock_phase(self, value):
|
|
||||||
self._clock_phase = bool(value)
|
|
||||||
|
|
||||||
def _get_lsb_first(self):
|
|
||||||
return self._lsb_first
|
|
||||||
|
|
||||||
def _set_lsb_first(self, value):
|
|
||||||
self._lsb_first = bool(value)
|
|
||||||
|
|
||||||
def _get_bits_per_word(self):
|
|
||||||
return self._bits_per_word
|
|
||||||
|
|
||||||
def _set_bits_per_word(self, value):
|
|
||||||
if value < 1:
|
|
||||||
raise ValueError('bits_per_word must be positive')
|
|
||||||
self._bits_per_word = int(value)
|
|
||||||
|
|
||||||
def _get_select_high(self):
|
|
||||||
return self.active_high
|
|
||||||
|
|
||||||
def _set_select_high(self, value):
|
|
||||||
with self._bus.lock:
|
|
||||||
self.active_high = value
|
|
||||||
self.off()
|
|
||||||
|
|
||||||
clock_polarity = property(_get_clock_polarity, _set_clock_polarity)
|
|
||||||
clock_phase = property(_get_clock_phase, _set_clock_phase)
|
|
||||||
clock_mode = property(_get_clock_mode, _set_clock_mode)
|
|
||||||
lsb_first = property(_get_lsb_first, _set_lsb_first)
|
|
||||||
bits_per_word = property(_get_bits_per_word, _set_bits_per_word)
|
|
||||||
select_high = property(_get_select_high, _set_select_high)
|
|
||||||
|
|
||||||
|
|
||||||
class SharedSPIHardwareInterface(SharedMixin, SPIHardwareInterface):
|
|
||||||
@classmethod
|
|
||||||
def _shared_key(cls, port, device):
|
|
||||||
return (port, device)
|
|
||||||
|
|
||||||
|
|
||||||
class SharedSPISoftwareInterface(SharedMixin, SPISoftwareInterface):
|
|
||||||
@classmethod
|
|
||||||
def _shared_key(cls, clock_pin, mosi_pin, miso_pin, select_pin):
|
|
||||||
return (clock_pin, mosi_pin, miso_pin, select_pin)
|
|
||||||
|
|
||||||
|
|
||||||
def extract_spi_args(**kwargs):
|
|
||||||
"""
|
|
||||||
Given a set of keyword arguments, splits it into those relevant to SPI
|
|
||||||
implementations and all the rest. SPI arguments are augmented with defaults
|
|
||||||
and converted into the pin format (from the port/device format) if
|
|
||||||
necessary.
|
|
||||||
|
|
||||||
Returns a tuple of ``(spi_args, other_args)``.
|
|
||||||
"""
|
|
||||||
pin_defaults = {
|
|
||||||
'clock_pin': 11,
|
|
||||||
'mosi_pin': 10,
|
|
||||||
'miso_pin': 9,
|
|
||||||
'select_pin': 8,
|
|
||||||
}
|
|
||||||
dev_defaults = {
|
|
||||||
'port': 0,
|
|
||||||
'device': 0,
|
|
||||||
}
|
|
||||||
spi_args = {
|
|
||||||
key: value for (key, value) in kwargs.items()
|
|
||||||
if key in pin_defaults or key in dev_defaults
|
|
||||||
}
|
|
||||||
kwargs = {
|
|
||||||
key: value for (key, value) in kwargs.items()
|
|
||||||
if key not in spi_args
|
|
||||||
}
|
|
||||||
if not spi_args:
|
|
||||||
spi_args = pin_defaults
|
|
||||||
elif set(spi_args) <= set(pin_defaults):
|
|
||||||
spi_args = {
|
|
||||||
key: spi_args.get(key, default)
|
|
||||||
for key, default in pin_defaults.items()
|
|
||||||
}
|
|
||||||
elif set(spi_args) <= set(dev_defaults):
|
|
||||||
spi_args = {
|
|
||||||
key: spi_args.get(key, default)
|
|
||||||
for key, default in dev_defaults.items()
|
|
||||||
}
|
|
||||||
if spi_args['port'] != 0:
|
|
||||||
raise SPIBadArgs('port 0 is the only valid SPI port')
|
|
||||||
if spi_args['device'] not in (0, 1):
|
|
||||||
raise SPIBadArgs('device must be 0 or 1')
|
|
||||||
spi_args = {
|
|
||||||
key: value if key != 'select_pin' else (8, 7)[spi_args['device']]
|
|
||||||
for key, value in pin_defaults.items()
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
raise SPIBadArgs(
|
|
||||||
'you must either specify port and device, or clock_pin, mosi_pin, '
|
|
||||||
'miso_pin, and select_pin; combinations of the two schemes (e.g. '
|
|
||||||
'port and clock_pin) are not permitted')
|
|
||||||
return spi_args, kwargs
|
|
||||||
|
|
||||||
|
|
||||||
def SPI(**spi_args):
|
|
||||||
"""
|
|
||||||
Returns an SPI interface, for the specified SPI *port* and *device*, or for
|
|
||||||
the specified pins (*clock_pin*, *mosi_pin*, *miso_pin*, and *select_pin*).
|
|
||||||
Only one of the schemes can be used; attempting to mix *port* and *device*
|
|
||||||
with pin numbers will raise :exc:`SPIBadArgs`.
|
|
||||||
|
|
||||||
If the pins specified match the hardware SPI pins (clock on GPIO11, MOSI on
|
|
||||||
GPIO10, MISO on GPIO9, and chip select on GPIO8 or GPIO7), and the spidev
|
|
||||||
module can be imported, a :class:`SPIHardwareInterface` instance will be
|
|
||||||
returned. Otherwise, a :class:`SPISoftwareInterface` will be returned which
|
|
||||||
will use simple bit-banging to communicate.
|
|
||||||
|
|
||||||
Both interfaces have the same API, support clock polarity and phase
|
|
||||||
attributes, and can handle half and full duplex communications, but the
|
|
||||||
hardware interface is significantly faster (though for many things this
|
|
||||||
doesn't matter).
|
|
||||||
|
|
||||||
Finally, the *shared* keyword argument specifies whether the resulting
|
|
||||||
SPI interface can be repeatedly created and used by multiple devices
|
|
||||||
(useful with multi-channel devices like numerous ADCs).
|
|
||||||
"""
|
|
||||||
spi_args, kwargs = extract_spi_args(**spi_args)
|
|
||||||
shared = kwargs.pop('shared', False)
|
|
||||||
if kwargs:
|
|
||||||
raise SPIBadArgs(
|
|
||||||
'unrecognized keyword argument %s' % kwargs.popitem()[0])
|
|
||||||
if all((
|
|
||||||
spi_args['clock_pin'] == 11,
|
|
||||||
spi_args['mosi_pin'] == 10,
|
|
||||||
spi_args['miso_pin'] == 9,
|
|
||||||
spi_args['select_pin'] in (7, 8),
|
|
||||||
)):
|
|
||||||
if SpiDev is None:
|
|
||||||
warnings.warn(
|
|
||||||
SPISoftwareFallback(
|
|
||||||
'failed to import spidev, falling back to software SPI'))
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
hardware_spi_args = {
|
|
||||||
'port': 0,
|
|
||||||
'device': {8: 0, 7: 1}[spi_args['select_pin']],
|
|
||||||
}
|
|
||||||
if shared:
|
|
||||||
return SharedSPIHardwareInterface(**hardware_spi_args)
|
|
||||||
else:
|
|
||||||
return SPIHardwareInterface(**hardware_spi_args)
|
|
||||||
except Exception as e:
|
|
||||||
warnings.warn(
|
|
||||||
SPISoftwareFallback(
|
|
||||||
'failed to initialize hardware SPI, falling back to '
|
|
||||||
'software (error was: %s)' % str(e)))
|
|
||||||
if shared:
|
|
||||||
return SharedSPISoftwareInterface(**spi_args)
|
|
||||||
else:
|
|
||||||
return SPISoftwareInterface(**spi_args)
|
|
||||||
|
|
||||||
@@ -16,7 +16,6 @@ except ImportError:
|
|||||||
|
|
||||||
from .exc import DeviceClosed, SPIBadChannel
|
from .exc import DeviceClosed, SPIBadChannel
|
||||||
from .devices import Device
|
from .devices import Device
|
||||||
from .spi import SPI
|
|
||||||
|
|
||||||
|
|
||||||
class SPIDevice(Device):
|
class SPIDevice(Device):
|
||||||
@@ -28,13 +27,12 @@ class SPIDevice(Device):
|
|||||||
specified with the constructor.
|
specified with the constructor.
|
||||||
"""
|
"""
|
||||||
def __init__(self, **spi_args):
|
def __init__(self, **spi_args):
|
||||||
self._spi = SPI(**spi_args)
|
self._spi = self._pin_factory.spi(**spi_args)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
if self._spi:
|
if self._spi:
|
||||||
s = self._spi
|
self._spi.close()
|
||||||
self._spi = None
|
self._spi = None
|
||||||
s.close()
|
|
||||||
super(SPIDevice, self).close()
|
super(SPIDevice, self).close()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
12
setup.py
12
setup.py
@@ -68,12 +68,12 @@ if sys.version_info[:2] == (3, 2):
|
|||||||
|
|
||||||
__entry_points__ = {
|
__entry_points__ = {
|
||||||
'gpiozero_pin_factories': [
|
'gpiozero_pin_factories': [
|
||||||
'PiGPIOPin = gpiozero.pins.pigpiod:PiGPIOPin',
|
'pigpio = gpiozero.pins.pigpiod:PiGPIOFactory',
|
||||||
'RPiGPIOPin = gpiozero.pins.rpigpio:RPiGPIOPin',
|
'rpigpio = gpiozero.pins.rpigpio:RPiGPIOFactory',
|
||||||
'RPIOPin = gpiozero.pins.rpio:RPIOPin',
|
'rpio = gpiozero.pins.rpio:RPIOFactory',
|
||||||
'NativePin = gpiozero.pins.native:NativePin',
|
'native = gpiozero.pins.native:NativeFactory',
|
||||||
'MockPin = gpiozero.pins.mock:MockPin',
|
'mock = gpiozero.pins.mock:MockFactory',
|
||||||
'MockPWMPin = gpiozero.pins.mock:MockPWMPin',
|
'mockpwm = gpiozero.pins.mock:MockPWMFactory',
|
||||||
],
|
],
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
'pinout = gpiozero.cli.pinout:main',
|
'pinout = gpiozero.cli.pinout:main',
|
||||||
|
|||||||
10
tests/conftest.py
Normal file
10
tests/conftest.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from __future__ import (
|
||||||
|
unicode_literals,
|
||||||
|
print_function,
|
||||||
|
absolute_import,
|
||||||
|
division,
|
||||||
|
)
|
||||||
|
str = type('')
|
||||||
|
|
||||||
|
import os
|
||||||
|
os.environ['GPIOZERO_PIN_FACTORY'] = 'mock'
|
||||||
@@ -11,26 +11,39 @@ import sys
|
|||||||
import pytest
|
import pytest
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
from gpiozero.pins.mock import MockPin, MockPWMPin
|
|
||||||
from gpiozero import *
|
from gpiozero import *
|
||||||
|
from gpiozero.pins.mock import MockPWMPin, MockPin
|
||||||
|
|
||||||
|
|
||||||
def setup_function(function):
|
def setup_function(function):
|
||||||
import gpiozero.devices
|
|
||||||
# dirty, but it does the job
|
# dirty, but it does the job
|
||||||
if function.__name__ in ('test_robot', 'test_ryanteck_robot', 'test_camjam_kit_robot', 'test_led_borg', 'test_snow_pi_initial_value_pwm'):
|
Device._pin_factory.pin_class = MockPWMPin if function.__name__ in (
|
||||||
gpiozero.devices.pin_factory = MockPWMPin
|
'test_robot',
|
||||||
else:
|
'test_ryanteck_robot',
|
||||||
gpiozero.devices.pin_factory = MockPin
|
'test_camjam_kit_robot',
|
||||||
|
'test_led_borg',
|
||||||
|
'test_led_board_pwm_value',
|
||||||
|
'test_led_board_pwm_bad_value',
|
||||||
|
'test_snow_pi_initial_value_pwm',
|
||||||
|
'test_led_board_pwm_initial_value',
|
||||||
|
'test_led_board_pwm_bad_initial_value',
|
||||||
|
'test_led_board_fade_background',
|
||||||
|
'test_led_bar_graph_pwm_value',
|
||||||
|
'test_led_bar_graph_pwm_initial_value',
|
||||||
|
) else MockPin
|
||||||
|
|
||||||
def teardown_function(function):
|
def teardown_function(function):
|
||||||
MockPin.clear_pins()
|
Device._pin_factory.reset()
|
||||||
|
|
||||||
|
def teardown_module(module):
|
||||||
|
# make sure we reset the default
|
||||||
|
Device._pin_factory.pwm = False
|
||||||
|
|
||||||
|
|
||||||
def test_composite_output_on_off():
|
def test_composite_output_on_off():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) as device:
|
with CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) as device:
|
||||||
device.on()
|
device.on()
|
||||||
assert all((pin1.state, pin2.state, pin3.state))
|
assert all((pin1.state, pin2.state, pin3.state))
|
||||||
@@ -38,9 +51,9 @@ def test_composite_output_on_off():
|
|||||||
assert not any((pin1.state, pin2.state, pin3.state))
|
assert not any((pin1.state, pin2.state, pin3.state))
|
||||||
|
|
||||||
def test_composite_output_toggle():
|
def test_composite_output_toggle():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) as device:
|
with CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) as device:
|
||||||
device.toggle()
|
device.toggle()
|
||||||
assert all((pin1.state, pin2.state, pin3.state))
|
assert all((pin1.state, pin2.state, pin3.state))
|
||||||
@@ -51,9 +64,9 @@ def test_composite_output_toggle():
|
|||||||
assert not pin3.state
|
assert not pin3.state
|
||||||
|
|
||||||
def test_composite_output_value():
|
def test_composite_output_value():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) as device:
|
with CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) as device:
|
||||||
assert device.value == (0, 0, 0)
|
assert device.value == (0, 0, 0)
|
||||||
device.toggle()
|
device.toggle()
|
||||||
@@ -64,9 +77,9 @@ def test_composite_output_value():
|
|||||||
assert device[2].is_active
|
assert device[2].is_active
|
||||||
|
|
||||||
def test_led_board_on_off():
|
def test_led_board_on_off():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBoard(pin1, pin2, foo=pin3) as board:
|
with LEDBoard(pin1, pin2, foo=pin3) as board:
|
||||||
assert isinstance(board[0], LED)
|
assert isinstance(board[0], LED)
|
||||||
assert isinstance(board[1], LED)
|
assert isinstance(board[1], LED)
|
||||||
@@ -121,9 +134,9 @@ def test_led_board_on_off():
|
|||||||
assert pin3.state
|
assert pin3.state
|
||||||
|
|
||||||
def test_led_board_active_low():
|
def test_led_board_active_low():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBoard(pin1, pin2, foo=pin3, active_high=False) as board:
|
with LEDBoard(pin1, pin2, foo=pin3, active_high=False) as board:
|
||||||
assert not board.active_high
|
assert not board.active_high
|
||||||
assert not board[0].active_high
|
assert not board[0].active_high
|
||||||
@@ -145,9 +158,9 @@ def test_led_board_active_low():
|
|||||||
assert not pin3.state
|
assert not pin3.state
|
||||||
|
|
||||||
def test_led_board_value():
|
def test_led_board_value():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBoard(pin1, pin2, foo=pin3) as board:
|
with LEDBoard(pin1, pin2, foo=pin3) as board:
|
||||||
assert board.value == (0, 0, 0)
|
assert board.value == (0, 0, 0)
|
||||||
board.value = (0, 1, 0)
|
board.value = (0, 1, 0)
|
||||||
@@ -156,9 +169,9 @@ def test_led_board_value():
|
|||||||
assert board.value == (1, 0, 1)
|
assert board.value == (1, 0, 1)
|
||||||
|
|
||||||
def test_led_board_pwm_value():
|
def test_led_board_pwm_value():
|
||||||
pin1 = MockPWMPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPWMPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPWMPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBoard(pin1, pin2, foo=pin3, pwm=True) as board:
|
with LEDBoard(pin1, pin2, foo=pin3, pwm=True) as board:
|
||||||
assert board.value == (0, 0, 0)
|
assert board.value == (0, 0, 0)
|
||||||
board.value = (0, 1, 0)
|
board.value = (0, 1, 0)
|
||||||
@@ -167,9 +180,9 @@ def test_led_board_pwm_value():
|
|||||||
assert board.value == (0.5, 0, 0.75)
|
assert board.value == (0.5, 0, 0.75)
|
||||||
|
|
||||||
def test_led_board_pwm_bad_value():
|
def test_led_board_pwm_bad_value():
|
||||||
pin1 = MockPWMPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPWMPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPWMPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBoard(pin1, pin2, foo=pin3, pwm=True) as board:
|
with LEDBoard(pin1, pin2, foo=pin3, pwm=True) as board:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
board.value = (-1, 0, 0)
|
board.value = (-1, 0, 0)
|
||||||
@@ -177,18 +190,18 @@ def test_led_board_pwm_bad_value():
|
|||||||
board.value = (0, 2, 0)
|
board.value = (0, 2, 0)
|
||||||
|
|
||||||
def test_led_board_initial_value():
|
def test_led_board_initial_value():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBoard(pin1, pin2, foo=pin3, initial_value=0) as board:
|
with LEDBoard(pin1, pin2, foo=pin3, initial_value=0) as board:
|
||||||
assert board.value == (0, 0, 0)
|
assert board.value == (0, 0, 0)
|
||||||
with LEDBoard(pin1, pin2, foo=pin3, initial_value=1) as board:
|
with LEDBoard(pin1, pin2, foo=pin3, initial_value=1) as board:
|
||||||
assert board.value == (1, 1, 1)
|
assert board.value == (1, 1, 1)
|
||||||
|
|
||||||
def test_led_board_pwm_initial_value():
|
def test_led_board_pwm_initial_value():
|
||||||
pin1 = MockPWMPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPWMPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPWMPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=0) as board:
|
with LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=0) as board:
|
||||||
assert board.value == (0, 0, 0)
|
assert board.value == (0, 0, 0)
|
||||||
with LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=1) as board:
|
with LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=1) as board:
|
||||||
@@ -197,18 +210,18 @@ def test_led_board_pwm_initial_value():
|
|||||||
assert board.value == (0.5, 0.5, 0.5)
|
assert board.value == (0.5, 0.5, 0.5)
|
||||||
|
|
||||||
def test_led_board_pwm_bad_initial_value():
|
def test_led_board_pwm_bad_initial_value():
|
||||||
pin1 = MockPWMPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPWMPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPWMPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=-1)
|
LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=-1)
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=2)
|
LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=2)
|
||||||
|
|
||||||
def test_led_board_nested():
|
def test_led_board_nested():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
|
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
|
||||||
assert list(led.pin for led in board.leds) == [pin1, pin2, pin3]
|
assert list(led.pin for led in board.leds) == [pin1, pin2, pin3]
|
||||||
assert board.value == (0, (0, 0))
|
assert board.value == (0, (0, 0))
|
||||||
@@ -218,9 +231,9 @@ def test_led_board_nested():
|
|||||||
assert pin3.state
|
assert pin3.state
|
||||||
|
|
||||||
def test_led_board_bad_blink():
|
def test_led_board_bad_blink():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
|
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
board.blink(fade_in_time=1, fade_out_time=1)
|
board.blink(fade_in_time=1, fade_out_time=1)
|
||||||
@@ -232,9 +245,9 @@ def test_led_board_bad_blink():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_led_board_blink_background():
|
def test_led_board_blink_background():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
|
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
|
||||||
board.blink(0.1, 0.1, n=2)
|
board.blink(0.1, 0.1, n=2)
|
||||||
board._blink_thread.join() # naughty, but ensures no arbitrary waits in the test
|
board._blink_thread.join() # naughty, but ensures no arbitrary waits in the test
|
||||||
@@ -252,9 +265,9 @@ def test_led_board_blink_background():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_led_board_blink_foreground():
|
def test_led_board_blink_foreground():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
|
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
|
||||||
board.blink(0.1, 0.1, n=2, background=False)
|
board.blink(0.1, 0.1, n=2, background=False)
|
||||||
test = [
|
test = [
|
||||||
@@ -271,9 +284,9 @@ def test_led_board_blink_foreground():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_led_board_blink_control():
|
def test_led_board_blink_control():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
|
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
|
||||||
board.blink(0.1, 0.1, n=2)
|
board.blink(0.1, 0.1, n=2)
|
||||||
# make sure the blink thread's started
|
# make sure the blink thread's started
|
||||||
@@ -296,9 +309,9 @@ def test_led_board_blink_control():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_led_board_blink_take_over():
|
def test_led_board_blink_take_over():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
|
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
|
||||||
board[1].blink(0.1, 0.1, n=2)
|
board[1].blink(0.1, 0.1, n=2)
|
||||||
board.blink(0.1, 0.1, n=2) # immediately take over blinking
|
board.blink(0.1, 0.1, n=2) # immediately take over blinking
|
||||||
@@ -318,9 +331,9 @@ def test_led_board_blink_take_over():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_led_board_blink_control_all():
|
def test_led_board_blink_control_all():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
|
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
|
||||||
board.blink(0.1, 0.1, n=2)
|
board.blink(0.1, 0.1, n=2)
|
||||||
# make sure the blink thread's started
|
# make sure the blink thread's started
|
||||||
@@ -340,9 +353,9 @@ def test_led_board_blink_control_all():
|
|||||||
pin3.assert_states_and_times(test)
|
pin3.assert_states_and_times(test)
|
||||||
|
|
||||||
def test_led_board_blink_interrupt_on():
|
def test_led_board_blink_interrupt_on():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
|
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
|
||||||
board.blink(1, 0.1)
|
board.blink(1, 0.1)
|
||||||
sleep(0.2)
|
sleep(0.2)
|
||||||
@@ -352,9 +365,9 @@ def test_led_board_blink_interrupt_on():
|
|||||||
pin3.assert_states([False, True, False])
|
pin3.assert_states([False, True, False])
|
||||||
|
|
||||||
def test_led_board_blink_interrupt_off():
|
def test_led_board_blink_interrupt_off():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
|
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
|
||||||
board.blink(0.1, 1)
|
board.blink(0.1, 1)
|
||||||
sleep(0.2)
|
sleep(0.2)
|
||||||
@@ -366,9 +379,9 @@ def test_led_board_blink_interrupt_off():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_led_board_fade_background():
|
def test_led_board_fade_background():
|
||||||
pin1 = MockPWMPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPWMPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPWMPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBoard(pin1, LEDBoard(pin2, pin3, pwm=True), pwm=True) as board:
|
with LEDBoard(pin1, LEDBoard(pin2, pin3, pwm=True), pwm=True) as board:
|
||||||
board.blink(0, 0, 0.2, 0.2, n=2)
|
board.blink(0, 0, 0.2, 0.2, n=2)
|
||||||
board._blink_thread.join()
|
board._blink_thread.join()
|
||||||
@@ -400,9 +413,9 @@ def test_led_board_fade_background():
|
|||||||
pin3.assert_states_and_times(test)
|
pin3.assert_states_and_times(test)
|
||||||
|
|
||||||
def test_led_bar_graph_value():
|
def test_led_bar_graph_value():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBarGraph(pin1, pin2, pin3) as graph:
|
with LEDBarGraph(pin1, pin2, pin3) as graph:
|
||||||
assert isinstance(graph[0], LED)
|
assert isinstance(graph[0], LED)
|
||||||
assert isinstance(graph[1], LED)
|
assert isinstance(graph[1], LED)
|
||||||
@@ -433,9 +446,9 @@ def test_led_bar_graph_value():
|
|||||||
assert graph.value == -2/3
|
assert graph.value == -2/3
|
||||||
|
|
||||||
def test_led_bar_graph_active_low():
|
def test_led_bar_graph_active_low():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBarGraph(pin1, pin2, pin3, active_high=False) as graph:
|
with LEDBarGraph(pin1, pin2, pin3, active_high=False) as graph:
|
||||||
assert not graph.active_high
|
assert not graph.active_high
|
||||||
assert not graph[0].active_high
|
assert not graph[0].active_high
|
||||||
@@ -455,9 +468,9 @@ def test_led_bar_graph_active_low():
|
|||||||
assert not pin3.state and pin1.state and pin2.state
|
assert not pin3.state and pin1.state and pin2.state
|
||||||
|
|
||||||
def test_led_bar_graph_pwm_value():
|
def test_led_bar_graph_pwm_value():
|
||||||
pin1 = MockPWMPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPWMPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPWMPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBarGraph(pin1, pin2, pin3, pwm=True) as graph:
|
with LEDBarGraph(pin1, pin2, pin3, pwm=True) as graph:
|
||||||
assert isinstance(graph[0], PWMLED)
|
assert isinstance(graph[0], PWMLED)
|
||||||
assert isinstance(graph[1], PWMLED)
|
assert isinstance(graph[1], PWMLED)
|
||||||
@@ -482,9 +495,9 @@ def test_led_bar_graph_pwm_value():
|
|||||||
assert graph.value == -1/2
|
assert graph.value == -1/2
|
||||||
|
|
||||||
def test_led_bar_graph_bad_value():
|
def test_led_bar_graph_bad_value():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBarGraph(pin1, pin2, pin3) as graph:
|
with LEDBarGraph(pin1, pin2, pin3) as graph:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
graph.value = -2
|
graph.value = -2
|
||||||
@@ -492,9 +505,9 @@ def test_led_bar_graph_bad_value():
|
|||||||
graph.value = 2
|
graph.value = 2
|
||||||
|
|
||||||
def test_led_bar_graph_bad_init():
|
def test_led_bar_graph_bad_init():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
LEDBarGraph(pin1, pin2, foo=pin3)
|
LEDBarGraph(pin1, pin2, foo=pin3)
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
@@ -503,9 +516,9 @@ def test_led_bar_graph_bad_init():
|
|||||||
LEDBarGraph(pin1, pin2, pin3, initial_value=2)
|
LEDBarGraph(pin1, pin2, pin3, initial_value=2)
|
||||||
|
|
||||||
def test_led_bar_graph_initial_value():
|
def test_led_bar_graph_initial_value():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBarGraph(pin1, pin2, pin3, initial_value=1/3) as graph:
|
with LEDBarGraph(pin1, pin2, pin3, initial_value=1/3) as graph:
|
||||||
assert graph.value == 1/3
|
assert graph.value == 1/3
|
||||||
assert pin1.state and not (pin2.state or pin3.state)
|
assert pin1.state and not (pin2.state or pin3.state)
|
||||||
@@ -514,9 +527,9 @@ def test_led_bar_graph_initial_value():
|
|||||||
assert pin3.state and not (pin1.state or pin2.state)
|
assert pin3.state and not (pin1.state or pin2.state)
|
||||||
|
|
||||||
def test_led_bar_graph_pwm_initial_value():
|
def test_led_bar_graph_pwm_initial_value():
|
||||||
pin1 = MockPWMPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPWMPin(3)
|
pin2 = Device._pin_factory.pin(3)
|
||||||
pin3 = MockPWMPin(4)
|
pin3 = Device._pin_factory.pin(4)
|
||||||
with LEDBarGraph(pin1, pin2, pin3, pwm=True, initial_value=0.5) as graph:
|
with LEDBarGraph(pin1, pin2, pin3, pwm=True, initial_value=0.5) as graph:
|
||||||
assert graph.value == 0.5
|
assert graph.value == 0.5
|
||||||
assert (pin1.state, pin2.state, pin3.state) == (1, 0.5, 0)
|
assert (pin1.state, pin2.state, pin3.state) == (1, 0.5, 0)
|
||||||
@@ -525,17 +538,17 @@ def test_led_bar_graph_pwm_initial_value():
|
|||||||
assert (pin1.state, pin2.state, pin3.state) == (0, 0.5, 1)
|
assert (pin1.state, pin2.state, pin3.state) == (0, 0.5, 1)
|
||||||
|
|
||||||
def test_led_borg():
|
def test_led_borg():
|
||||||
pins = [MockPWMPin(n) for n in (17, 27, 22)]
|
pins = [Device._pin_factory.pin(n) for n in (17, 27, 22)]
|
||||||
with LedBorg() as board:
|
with LedBorg() as board:
|
||||||
assert [device.pin for device in board._leds] == pins
|
assert [device.pin for device in board._leds] == pins
|
||||||
|
|
||||||
def test_pi_liter():
|
def test_pi_liter():
|
||||||
pins = [MockPin(n) for n in (4, 17, 27, 18, 22, 23, 24, 25)]
|
pins = [Device._pin_factory.pin(n) for n in (4, 17, 27, 18, 22, 23, 24, 25)]
|
||||||
with PiLiter() as board:
|
with PiLiter() as board:
|
||||||
assert [device.pin for device in board] == pins
|
assert [device.pin for device in board] == pins
|
||||||
|
|
||||||
def test_pi_liter_graph():
|
def test_pi_liter_graph():
|
||||||
pins = [MockPin(n) for n in (4, 17, 27, 18, 22, 23, 24, 25)]
|
pins = [Device._pin_factory.pin(n) for n in (4, 17, 27, 18, 22, 23, 24, 25)]
|
||||||
with PiLiterBarGraph() as board:
|
with PiLiterBarGraph() as board:
|
||||||
board.value = 0.5
|
board.value = 0.5
|
||||||
assert [pin.state for pin in pins] == [1, 1, 1, 1, 0, 0, 0, 0]
|
assert [pin.state for pin in pins] == [1, 1, 1, 1, 0, 0, 0, 0]
|
||||||
@@ -543,9 +556,9 @@ def test_pi_liter_graph():
|
|||||||
assert board.value == 5/8
|
assert board.value == 5/8
|
||||||
|
|
||||||
def test_traffic_lights():
|
def test_traffic_lights():
|
||||||
red_pin = MockPin(2)
|
red_pin = Device._pin_factory.pin(2)
|
||||||
amber_pin = MockPin(3)
|
amber_pin = Device._pin_factory.pin(3)
|
||||||
green_pin = MockPin(4)
|
green_pin = Device._pin_factory.pin(4)
|
||||||
with TrafficLights(red_pin, amber_pin, green_pin) as board:
|
with TrafficLights(red_pin, amber_pin, green_pin) as board:
|
||||||
board.red.on()
|
board.red.on()
|
||||||
assert board.red.value
|
assert board.red.value
|
||||||
@@ -574,15 +587,15 @@ def test_traffic_lights():
|
|||||||
def test_traffic_lights_bad_init():
|
def test_traffic_lights_bad_init():
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
TrafficLights()
|
TrafficLights()
|
||||||
red_pin = MockPin(2)
|
red_pin = Device._pin_factory.pin(2)
|
||||||
amber_pin = MockPin(3)
|
amber_pin = Device._pin_factory.pin(3)
|
||||||
green_pin = MockPin(4)
|
green_pin = Device._pin_factory.pin(4)
|
||||||
yellow_pin = MockPin(5)
|
yellow_pin = Device._pin_factory.pin(5)
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
TrafficLights(red=red_pin, amber=amber_pin, yellow=yellow_pin, green=green_pin)
|
TrafficLights(red=red_pin, amber=amber_pin, yellow=yellow_pin, green=green_pin)
|
||||||
|
|
||||||
def test_pi_traffic():
|
def test_pi_traffic():
|
||||||
pins = [MockPin(n) for n in (9, 10, 11)]
|
pins = [Device._pin_factory.pin(n) for n in (9, 10, 11)]
|
||||||
with PiTraffic() as board:
|
with PiTraffic() as board:
|
||||||
assert [device.pin for device in board] == pins
|
assert [device.pin for device in board] == pins
|
||||||
|
|
||||||
@@ -591,27 +604,27 @@ def test_pi_stop():
|
|||||||
PiStop()
|
PiStop()
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
PiStop('E')
|
PiStop('E')
|
||||||
pins_a = [MockPin(n) for n in (7, 8, 25)]
|
pins_a = [Device._pin_factory.pin(n) for n in (7, 8, 25)]
|
||||||
with PiStop('A') as board:
|
with PiStop('A') as board:
|
||||||
assert [device.pin for device in board] == pins_a
|
assert [device.pin for device in board] == pins_a
|
||||||
pins_aplus = [MockPin(n) for n in (21, 20, 16)]
|
pins_aplus = [Device._pin_factory.pin(n) for n in (21, 20, 16)]
|
||||||
with PiStop('A+') as board:
|
with PiStop('A+') as board:
|
||||||
assert [device.pin for device in board] == pins_aplus
|
assert [device.pin for device in board] == pins_aplus
|
||||||
pins_b = [MockPin(n) for n in (10, 9, 11)]
|
pins_b = [Device._pin_factory.pin(n) for n in (10, 9, 11)]
|
||||||
with PiStop('B') as board:
|
with PiStop('B') as board:
|
||||||
assert [device.pin for device in board] == pins_b
|
assert [device.pin for device in board] == pins_b
|
||||||
pins_bplus = [MockPin(n) for n in (13, 19, 26)]
|
pins_bplus = [Device._pin_factory.pin(n) for n in (13, 19, 26)]
|
||||||
with PiStop('B+') as board:
|
with PiStop('B+') as board:
|
||||||
assert [device.pin for device in board] == pins_bplus
|
assert [device.pin for device in board] == pins_bplus
|
||||||
pins_c = [MockPin(n) for n in (18, 15, 14)]
|
pins_c = [Device._pin_factory.pin(n) for n in (18, 15, 14)]
|
||||||
with PiStop('C') as board:
|
with PiStop('C') as board:
|
||||||
assert [device.pin for device in board] == pins_c
|
assert [device.pin for device in board] == pins_c
|
||||||
pins_d = [MockPin(n) for n in (2, 3, 4)]
|
pins_d = [Device._pin_factory.pin(n) for n in (2, 3, 4)]
|
||||||
with PiStop('D') as board:
|
with PiStop('D') as board:
|
||||||
assert [device.pin for device in board] == pins_d
|
assert [device.pin for device in board] == pins_d
|
||||||
|
|
||||||
def test_snow_pi():
|
def test_snow_pi():
|
||||||
pins = [MockPin(n) for n in (23, 24, 25, 17, 18, 22, 7, 8, 9)]
|
pins = [Device._pin_factory.pin(n) for n in (23, 24, 25, 17, 18, 22, 7, 8, 9)]
|
||||||
with SnowPi() as board:
|
with SnowPi() as board:
|
||||||
assert [device.pin for device in board.leds] == pins
|
assert [device.pin for device in board.leds] == pins
|
||||||
|
|
||||||
@@ -626,17 +639,17 @@ def test_snow_pi_initial_value():
|
|||||||
assert all(device.pin.state == True for device in board.leds)
|
assert all(device.pin.state == True for device in board.leds)
|
||||||
|
|
||||||
def test_snow_pi_initial_value_pwm():
|
def test_snow_pi_initial_value_pwm():
|
||||||
pins = [MockPWMPin(n) for n in (23, 24, 25, 17, 18, 22, 7, 8, 9)]
|
pins = [Device._pin_factory.pin(n) for n in (23, 24, 25, 17, 18, 22, 7, 8, 9)]
|
||||||
with SnowPi(pwm=True, initial_value=0.5) as board:
|
with SnowPi(pwm=True, initial_value=0.5) as board:
|
||||||
assert [device.pin for device in board.leds] == pins
|
assert [device.pin for device in board.leds] == pins
|
||||||
assert all(device.pin.state == 0.5 for device in board.leds)
|
assert all(device.pin.state == 0.5 for device in board.leds)
|
||||||
|
|
||||||
def test_traffic_lights_buzzer():
|
def test_traffic_lights_buzzer():
|
||||||
red_pin = MockPin(2)
|
red_pin = Device._pin_factory.pin(2)
|
||||||
amber_pin = MockPin(3)
|
amber_pin = Device._pin_factory.pin(3)
|
||||||
green_pin = MockPin(4)
|
green_pin = Device._pin_factory.pin(4)
|
||||||
buzzer_pin = MockPin(5)
|
buzzer_pin = Device._pin_factory.pin(5)
|
||||||
button_pin = MockPin(6)
|
button_pin = Device._pin_factory.pin(6)
|
||||||
with TrafficLightsBuzzer(
|
with TrafficLightsBuzzer(
|
||||||
TrafficLights(red_pin, amber_pin, green_pin),
|
TrafficLights(red_pin, amber_pin, green_pin),
|
||||||
Buzzer(buzzer_pin),
|
Buzzer(buzzer_pin),
|
||||||
@@ -651,17 +664,17 @@ def test_traffic_lights_buzzer():
|
|||||||
assert board.button.is_active
|
assert board.button.is_active
|
||||||
|
|
||||||
def test_fish_dish():
|
def test_fish_dish():
|
||||||
pins = [MockPin(n) for n in (9, 22, 4, 8, 7)]
|
pins = [Device._pin_factory.pin(n) for n in (9, 22, 4, 8, 7)]
|
||||||
with FishDish() as board:
|
with FishDish() as board:
|
||||||
assert [led.pin for led in board.lights] + [board.buzzer.pin, board.button.pin] == pins
|
assert [led.pin for led in board.lights] + [board.buzzer.pin, board.button.pin] == pins
|
||||||
|
|
||||||
def test_traffic_hat():
|
def test_traffic_hat():
|
||||||
pins = [MockPin(n) for n in (24, 23, 22, 5, 25)]
|
pins = [Device._pin_factory.pin(n) for n in (24, 23, 22, 5, 25)]
|
||||||
with TrafficHat() as board:
|
with TrafficHat() as board:
|
||||||
assert [led.pin for led in board.lights] + [board.buzzer.pin, board.button.pin] == pins
|
assert [led.pin for led in board.lights] + [board.buzzer.pin, board.button.pin] == pins
|
||||||
|
|
||||||
def test_robot():
|
def test_robot():
|
||||||
pins = [MockPWMPin(n) for n in (2, 3, 4, 5)]
|
pins = [Device._pin_factory.pin(n) for n in (2, 3, 4, 5)]
|
||||||
with Robot((2, 3), (4, 5)) as robot:
|
with Robot((2, 3), (4, 5)) as robot:
|
||||||
assert (
|
assert (
|
||||||
[device.pin for device in robot.left_motor] +
|
[device.pin for device in robot.left_motor] +
|
||||||
@@ -696,12 +709,12 @@ def test_robot():
|
|||||||
assert robot.value == (0, -0.5)
|
assert robot.value == (0, -0.5)
|
||||||
|
|
||||||
def test_ryanteck_robot():
|
def test_ryanteck_robot():
|
||||||
pins = [MockPWMPin(n) for n in (17, 18, 22, 23)]
|
pins = [Device._pin_factory.pin(n) for n in (17, 18, 22, 23)]
|
||||||
with RyanteckRobot() as board:
|
with RyanteckRobot() as board:
|
||||||
assert [device.pin for motor in board for device in motor] == pins
|
assert [device.pin for motor in board for device in motor] == pins
|
||||||
|
|
||||||
def test_camjam_kit_robot():
|
def test_camjam_kit_robot():
|
||||||
pins = [MockPWMPin(n) for n in (9, 10, 7, 8)]
|
pins = [Device._pin_factory.pin(n) for n in (9, 10, 7, 8)]
|
||||||
with CamJamKitRobot() as board:
|
with CamJamKitRobot() as board:
|
||||||
assert [device.pin for motor in board for device in motor] == pins
|
assert [device.pin for motor in board for device in motor] == pins
|
||||||
|
|
||||||
@@ -714,7 +727,7 @@ def test_energenie_bad_init():
|
|||||||
Energenie(5)
|
Energenie(5)
|
||||||
|
|
||||||
def test_energenie():
|
def test_energenie():
|
||||||
pins = [MockPin(n) for n in (17, 22, 23, 27, 24, 25)]
|
pins = [Device._pin_factory.pin(n) for n in (17, 22, 23, 27, 24, 25)]
|
||||||
with Energenie(1, initial_value=True) as device1, \
|
with Energenie(1, initial_value=True) as device1, \
|
||||||
Energenie(2, initial_value=False) as device2:
|
Energenie(2, initial_value=False) as device2:
|
||||||
assert repr(device1) == '<gpiozero.Energenie object on socket 1>'
|
assert repr(device1) == '<gpiozero.Energenie object on socket 1>'
|
||||||
|
|||||||
@@ -6,86 +6,87 @@ from __future__ import (
|
|||||||
)
|
)
|
||||||
str = type('')
|
str = type('')
|
||||||
|
|
||||||
|
import warnings
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from gpiozero.pins.mock import MockPin
|
|
||||||
from gpiozero import *
|
from gpiozero import *
|
||||||
|
|
||||||
|
|
||||||
def teardown_function(function):
|
def teardown_function(function):
|
||||||
MockPin.clear_pins()
|
Device._pin_factory.reset()
|
||||||
|
|
||||||
|
|
||||||
# TODO add more devices tests!
|
# TODO add more devices tests!
|
||||||
|
|
||||||
def test_device_no_pin():
|
def test_device_bad_pin():
|
||||||
with pytest.raises(GPIOPinMissing):
|
with pytest.raises(GPIOPinMissing):
|
||||||
device = GPIODevice()
|
device = GPIODevice()
|
||||||
|
with pytest.raises(PinInvalidPin):
|
||||||
|
device = GPIODevice(60)
|
||||||
|
|
||||||
|
def test_device_non_physical():
|
||||||
|
with warnings.catch_warnings(record=True) as w:
|
||||||
|
device = GPIODevice(37)
|
||||||
|
assert len(w) == 1
|
||||||
|
assert w[0].category == PinNonPhysical
|
||||||
|
|
||||||
def test_device_init():
|
def test_device_init():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with GPIODevice(pin) as device:
|
with GPIODevice(pin) as device:
|
||||||
assert not device.closed
|
assert not device.closed
|
||||||
assert device.pin == pin
|
assert device.pin == pin
|
||||||
|
|
||||||
def test_device_init_twice_same_pin():
|
def test_device_init_twice_same_pin():
|
||||||
pin = MockPin(2)
|
with GPIODevice(2) as device:
|
||||||
with GPIODevice(pin) as device:
|
|
||||||
with pytest.raises(GPIOPinInUse):
|
with pytest.raises(GPIOPinInUse):
|
||||||
device2 = GPIODevice(pin)
|
GPIODevice(2)
|
||||||
|
|
||||||
def test_device_init_twice_different_pin():
|
def test_device_init_twice_different_pin():
|
||||||
pin = MockPin(2)
|
with GPIODevice(2) as device:
|
||||||
pin2 = MockPin(3)
|
with GPIODevice(3) as device2:
|
||||||
with GPIODevice(pin) as device:
|
|
||||||
with GPIODevice(pin2) as device2:
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_device_close():
|
def test_device_close():
|
||||||
pin = MockPin(2)
|
device = GPIODevice(2)
|
||||||
device = GPIODevice(pin)
|
|
||||||
device.close()
|
device.close()
|
||||||
assert device.closed
|
assert device.closed
|
||||||
assert device.pin is None
|
assert device.pin is None
|
||||||
|
|
||||||
def test_device_reopen_same_pin():
|
def test_device_reopen_same_pin():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
device = GPIODevice(pin)
|
device = GPIODevice(pin)
|
||||||
device.close()
|
device.close()
|
||||||
device2 = GPIODevice(pin)
|
device2 = GPIODevice(pin)
|
||||||
assert not device2.closed
|
assert not device2.closed
|
||||||
assert device2.pin == pin
|
assert device2.pin is pin
|
||||||
assert device.closed
|
assert device.closed
|
||||||
assert device.pin is None
|
assert device.pin is None
|
||||||
device2.close()
|
device2.close()
|
||||||
|
|
||||||
def test_device_repr():
|
def test_device_repr():
|
||||||
pin = MockPin(2)
|
with GPIODevice(2) as device:
|
||||||
with GPIODevice(pin) as device:
|
assert repr(device) == '<gpiozero.GPIODevice object on pin %s, is_active=False>' % device.pin
|
||||||
assert repr(device) == '<gpiozero.GPIODevice object on pin %s, is_active=False>' % pin
|
|
||||||
|
|
||||||
def test_device_repr_after_close():
|
def test_device_repr_after_close():
|
||||||
pin = MockPin(2)
|
device = GPIODevice(2)
|
||||||
device = GPIODevice(pin)
|
|
||||||
device.close()
|
device.close()
|
||||||
assert repr(device) == '<gpiozero.GPIODevice object closed>'
|
assert repr(device) == '<gpiozero.GPIODevice object closed>'
|
||||||
|
|
||||||
def test_device_unknown_attr():
|
def test_device_unknown_attr():
|
||||||
pin = MockPin(2)
|
with GPIODevice(2) as device:
|
||||||
with GPIODevice(pin) as device:
|
|
||||||
with pytest.raises(AttributeError):
|
with pytest.raises(AttributeError):
|
||||||
device.foo = 1
|
device.foo = 1
|
||||||
|
|
||||||
def test_device_context_manager():
|
def test_device_context_manager():
|
||||||
pin = MockPin(2)
|
with GPIODevice(2) as device:
|
||||||
with GPIODevice(pin) as device:
|
|
||||||
assert not device.closed
|
assert not device.closed
|
||||||
assert device.closed
|
assert device.closed
|
||||||
|
|
||||||
def test_composite_device_sequence():
|
def test_composite_device_sequence():
|
||||||
with CompositeDevice(
|
with CompositeDevice(
|
||||||
InputDevice(MockPin(2)),
|
InputDevice(2),
|
||||||
InputDevice(MockPin(3))
|
InputDevice(3)
|
||||||
) as device:
|
) as device:
|
||||||
assert len(device) == 2
|
assert len(device) == 2
|
||||||
assert device[0].pin.number == 2
|
assert device[0].pin.number == 2
|
||||||
@@ -94,8 +95,8 @@ def test_composite_device_sequence():
|
|||||||
|
|
||||||
def test_composite_device_values():
|
def test_composite_device_values():
|
||||||
with CompositeDevice(
|
with CompositeDevice(
|
||||||
InputDevice(MockPin(2)),
|
InputDevice(2),
|
||||||
InputDevice(MockPin(3))
|
InputDevice(3)
|
||||||
) as device:
|
) as device:
|
||||||
assert device.value == (0, 0)
|
assert device.value == (0, 0)
|
||||||
assert not device.is_active
|
assert not device.is_active
|
||||||
@@ -105,8 +106,8 @@ def test_composite_device_values():
|
|||||||
|
|
||||||
def test_composite_device_named():
|
def test_composite_device_named():
|
||||||
with CompositeDevice(
|
with CompositeDevice(
|
||||||
foo=InputDevice(MockPin(2)),
|
foo=InputDevice(2),
|
||||||
bar=InputDevice(MockPin(3)),
|
bar=InputDevice(3),
|
||||||
_order=('foo', 'bar')
|
_order=('foo', 'bar')
|
||||||
) as device:
|
) as device:
|
||||||
assert device.namedtuple._fields == ('foo', 'bar')
|
assert device.namedtuple._fields == ('foo', 'bar')
|
||||||
@@ -121,13 +122,13 @@ def test_composite_device_bad_init():
|
|||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
CompositeDevice(2)
|
CompositeDevice(2)
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
CompositeDevice(MockPin(2))
|
CompositeDevice(Device._pin_factory.pin(2))
|
||||||
|
|
||||||
def test_composite_device_read_only():
|
def test_composite_device_read_only():
|
||||||
device = CompositeDevice(
|
with CompositeDevice(
|
||||||
foo=InputDevice(MockPin(2)),
|
foo=InputDevice(2),
|
||||||
bar=InputDevice(MockPin(3))
|
bar=InputDevice(3)
|
||||||
)
|
) as device:
|
||||||
with pytest.raises(AttributeError):
|
with pytest.raises(AttributeError):
|
||||||
device.foo = 1
|
device.foo = 1
|
||||||
|
|
||||||
|
|||||||
@@ -12,57 +12,52 @@ import pytest
|
|||||||
from threading import Event
|
from threading import Event
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from gpiozero.pins.mock import (
|
from gpiozero.pins.mock import MockPulledUpPin, MockChargingPin, MockTriggerPin
|
||||||
MockPin,
|
|
||||||
MockPulledUpPin,
|
|
||||||
MockChargingPin,
|
|
||||||
MockTriggerPin,
|
|
||||||
)
|
|
||||||
from gpiozero import *
|
from gpiozero import *
|
||||||
|
|
||||||
|
|
||||||
def teardown_function(function):
|
def teardown_function(function):
|
||||||
MockPin.clear_pins()
|
Device._pin_factory.reset()
|
||||||
|
|
||||||
|
|
||||||
def test_input_initial_values():
|
def test_input_initial_values():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with InputDevice(pin, pull_up=True) as device:
|
with InputDevice(pin, pull_up=True) as device:
|
||||||
assert pin.function == 'input'
|
assert pin.function == 'input'
|
||||||
assert pin.pull == 'up'
|
assert pin.pull == 'up'
|
||||||
assert device.pull_up
|
assert device.pull_up
|
||||||
device.close()
|
with InputDevice(pin, pull_up=False) as device:
|
||||||
device = InputDevice(pin, pull_up=False)
|
|
||||||
assert pin.pull == 'down'
|
assert pin.pull == 'down'
|
||||||
assert not device.pull_up
|
assert not device.pull_up
|
||||||
|
|
||||||
def test_input_is_active_low():
|
def test_input_is_active_low():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with InputDevice(pin, pull_up=True) as device:
|
with InputDevice(pin, pull_up=True) as device:
|
||||||
pin.drive_high()
|
pin.drive_high()
|
||||||
assert not device.is_active
|
assert not device.is_active
|
||||||
assert repr(device) == '<gpiozero.InputDevice object on pin MOCK2, pull_up=True, is_active=False>'
|
assert repr(device) == '<gpiozero.InputDevice object on pin GPIO2, pull_up=True, is_active=False>'
|
||||||
pin.drive_low()
|
pin.drive_low()
|
||||||
assert device.is_active
|
assert device.is_active
|
||||||
assert repr(device) == '<gpiozero.InputDevice object on pin MOCK2, pull_up=True, is_active=True>'
|
assert repr(device) == '<gpiozero.InputDevice object on pin GPIO2, pull_up=True, is_active=True>'
|
||||||
|
|
||||||
def test_input_is_active_high():
|
def test_input_is_active_high():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with InputDevice(pin, pull_up=False) as device:
|
with InputDevice(pin, pull_up=False) as device:
|
||||||
pin.drive_high()
|
pin.drive_high()
|
||||||
assert device.is_active
|
assert device.is_active
|
||||||
assert repr(device) == '<gpiozero.InputDevice object on pin MOCK2, pull_up=False, is_active=True>'
|
assert repr(device) == '<gpiozero.InputDevice object on pin GPIO2, pull_up=False, is_active=True>'
|
||||||
pin.drive_low()
|
pin.drive_low()
|
||||||
assert not device.is_active
|
assert not device.is_active
|
||||||
assert repr(device) == '<gpiozero.InputDevice object on pin MOCK2, pull_up=False, is_active=False>'
|
assert repr(device) == '<gpiozero.InputDevice object on pin GPIO2, pull_up=False, is_active=False>'
|
||||||
|
|
||||||
def test_input_pulled_up():
|
def test_input_pulled_up():
|
||||||
pin = MockPulledUpPin(2)
|
pin = Device._pin_factory.pin(2, pin_class=MockPulledUpPin)
|
||||||
with pytest.raises(PinFixedPull):
|
with pytest.raises(PinFixedPull):
|
||||||
InputDevice(pin, pull_up=False)
|
InputDevice(pin, pull_up=False)
|
||||||
|
|
||||||
def test_input_event_activated():
|
def test_input_event_activated():
|
||||||
event = Event()
|
event = Event()
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with DigitalInputDevice(pin) as device:
|
with DigitalInputDevice(pin) as device:
|
||||||
device.when_activated = lambda: event.set()
|
device.when_activated = lambda: event.set()
|
||||||
assert not event.is_set()
|
assert not event.is_set()
|
||||||
@@ -71,7 +66,7 @@ def test_input_event_activated():
|
|||||||
|
|
||||||
def test_input_event_deactivated():
|
def test_input_event_deactivated():
|
||||||
event = Event()
|
event = Event()
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with DigitalInputDevice(pin) as device:
|
with DigitalInputDevice(pin) as device:
|
||||||
device.when_deactivated = lambda: event.set()
|
device.when_deactivated = lambda: event.set()
|
||||||
assert not event.is_set()
|
assert not event.is_set()
|
||||||
@@ -82,7 +77,7 @@ def test_input_event_deactivated():
|
|||||||
|
|
||||||
def test_input_partial_callback():
|
def test_input_partial_callback():
|
||||||
event = Event()
|
event = Event()
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
def foo(a, b):
|
def foo(a, b):
|
||||||
event.set()
|
event.set()
|
||||||
return a + b
|
return a + b
|
||||||
@@ -95,22 +90,22 @@ def test_input_partial_callback():
|
|||||||
assert event.is_set()
|
assert event.is_set()
|
||||||
|
|
||||||
def test_input_wait_active():
|
def test_input_wait_active():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with DigitalInputDevice(pin) as device:
|
with DigitalInputDevice(pin) as device:
|
||||||
pin.drive_high()
|
pin.drive_high()
|
||||||
assert device.wait_for_active(1)
|
assert device.wait_for_active(1)
|
||||||
assert not device.wait_for_inactive(0)
|
assert not device.wait_for_inactive(0)
|
||||||
|
|
||||||
def test_input_wait_inactive():
|
def test_input_wait_inactive():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with DigitalInputDevice(pin) as device:
|
with DigitalInputDevice(pin) as device:
|
||||||
assert device.wait_for_inactive(1)
|
assert device.wait_for_inactive(1)
|
||||||
assert not device.wait_for_active(0)
|
assert not device.wait_for_active(0)
|
||||||
|
|
||||||
def test_input_smoothed_attrib():
|
def test_input_smoothed_attrib():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with SmoothedInputDevice(pin, threshold=0.5, queue_len=5, partial=False) as device:
|
with SmoothedInputDevice(pin, threshold=0.5, queue_len=5, partial=False) as device:
|
||||||
assert repr(device) == '<gpiozero.SmoothedInputDevice object on pin=MOCK2, pull_up=False>'
|
assert repr(device) == '<gpiozero.SmoothedInputDevice object on pin GPIO2, pull_up=False>'
|
||||||
assert device.threshold == 0.5
|
assert device.threshold == 0.5
|
||||||
assert device.queue_len == 5
|
assert device.queue_len == 5
|
||||||
assert not device.partial
|
assert not device.partial
|
||||||
@@ -120,7 +115,7 @@ def test_input_smoothed_attrib():
|
|||||||
device.threshold = 1
|
device.threshold = 1
|
||||||
|
|
||||||
def test_input_smoothed_values():
|
def test_input_smoothed_values():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with SmoothedInputDevice(pin) as device:
|
with SmoothedInputDevice(pin) as device:
|
||||||
device._queue.start()
|
device._queue.start()
|
||||||
assert not device.is_active
|
assert not device.is_active
|
||||||
@@ -130,7 +125,7 @@ def test_input_smoothed_values():
|
|||||||
assert device.wait_for_inactive(1)
|
assert device.wait_for_inactive(1)
|
||||||
|
|
||||||
def test_input_button():
|
def test_input_button():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with Button(pin) as button:
|
with Button(pin) as button:
|
||||||
assert pin.pull == 'up'
|
assert pin.pull == 'up'
|
||||||
assert not button.is_pressed
|
assert not button.is_pressed
|
||||||
@@ -142,7 +137,7 @@ def test_input_button():
|
|||||||
assert button.wait_for_release(1)
|
assert button.wait_for_release(1)
|
||||||
|
|
||||||
def test_input_line_sensor():
|
def test_input_line_sensor():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with LineSensor(pin) as sensor:
|
with LineSensor(pin) as sensor:
|
||||||
pin.drive_low() # logic is inverted for line sensor
|
pin.drive_low() # logic is inverted for line sensor
|
||||||
assert sensor.wait_for_line(1)
|
assert sensor.wait_for_line(1)
|
||||||
@@ -152,7 +147,7 @@ def test_input_line_sensor():
|
|||||||
assert not sensor.line_detected
|
assert not sensor.line_detected
|
||||||
|
|
||||||
def test_input_motion_sensor():
|
def test_input_motion_sensor():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with MotionSensor(pin) as sensor:
|
with MotionSensor(pin) as sensor:
|
||||||
pin.drive_high()
|
pin.drive_high()
|
||||||
assert sensor.wait_for_motion(1)
|
assert sensor.wait_for_motion(1)
|
||||||
@@ -164,7 +159,7 @@ def test_input_motion_sensor():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_input_light_sensor():
|
def test_input_light_sensor():
|
||||||
pin = MockChargingPin(2)
|
pin = Device._pin_factory.pin(2, pin_class=MockChargingPin)
|
||||||
with LightSensor(pin) as sensor:
|
with LightSensor(pin) as sensor:
|
||||||
pin.charge_time = 0.1
|
pin.charge_time = 0.1
|
||||||
assert sensor.wait_for_dark(1)
|
assert sensor.wait_for_dark(1)
|
||||||
@@ -174,8 +169,8 @@ def test_input_light_sensor():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_input_distance_sensor():
|
def test_input_distance_sensor():
|
||||||
echo_pin = MockPin(2)
|
echo_pin = Device._pin_factory.pin(2)
|
||||||
trig_pin = MockTriggerPin(3)
|
trig_pin = Device._pin_factory.pin(3, pin_class=MockTriggerPin)
|
||||||
trig_pin.echo_pin = echo_pin
|
trig_pin.echo_pin = echo_pin
|
||||||
trig_pin.echo_time = 0.02
|
trig_pin.echo_time = 0.02
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
|
|||||||
@@ -11,25 +11,24 @@ from threading import Event
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from gpiozero.pins.mock import MockPin, MockPWMPin
|
from gpiozero.pins.mock import MockPWMPin, MockPin
|
||||||
from gpiozero import *
|
from gpiozero import *
|
||||||
|
|
||||||
|
|
||||||
def teardown_function(function):
|
def teardown_function(function):
|
||||||
MockPin.clear_pins()
|
Device._pin_factory.reset()
|
||||||
|
|
||||||
|
|
||||||
# Some rough tests to make sure our MockPin is up to snuff. This is just
|
# Some rough tests to make sure our MockPin is up to snuff. This is just
|
||||||
# enough to get reasonable coverage but it's by no means comprehensive...
|
# enough to get reasonable coverage but it's by no means comprehensive...
|
||||||
|
|
||||||
def test_mock_pin_init():
|
def test_mock_pin_init():
|
||||||
with pytest.raises(TypeError):
|
|
||||||
MockPin()
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
MockPin(60)
|
Device._pin_factory.pin(60)
|
||||||
assert MockPin(2).number == 2
|
assert Device._pin_factory.pin(2).number == 2
|
||||||
|
|
||||||
def test_mock_pin_defaults():
|
def test_mock_pin_defaults():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
assert pin.bounce == None
|
assert pin.bounce == None
|
||||||
assert pin.edges == 'both'
|
assert pin.edges == 'both'
|
||||||
assert pin.frequency == None
|
assert pin.frequency == None
|
||||||
@@ -39,30 +38,23 @@ def test_mock_pin_defaults():
|
|||||||
assert pin.when_changed == None
|
assert pin.when_changed == None
|
||||||
|
|
||||||
def test_mock_pin_open_close():
|
def test_mock_pin_open_close():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
pin.close()
|
pin.close()
|
||||||
|
|
||||||
def test_mock_pin_init_twice_same_pin():
|
def test_mock_pin_init_twice_same_pin():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(pin1.number)
|
pin2 = Device._pin_factory.pin(pin1.number)
|
||||||
assert pin1 is pin2
|
assert pin1 is pin2
|
||||||
|
|
||||||
def test_mock_pin_init_twice_different_pin():
|
def test_mock_pin_init_twice_different_pin():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2)
|
||||||
pin2 = MockPin(pin1.number+1)
|
pin2 = Device._pin_factory.pin(pin1.number+1)
|
||||||
assert pin1 != pin2
|
assert pin1 != pin2
|
||||||
assert pin1.number == 2
|
assert pin1.number == 2
|
||||||
assert pin2.number == pin1.number+1
|
assert pin2.number == pin1.number+1
|
||||||
|
|
||||||
def test_mock_pwm_pin_init():
|
|
||||||
with pytest.raises(TypeError):
|
|
||||||
MockPWMPin()
|
|
||||||
with pytest.raises(ValueError):
|
|
||||||
MockPWMPin(60)
|
|
||||||
assert MockPWMPin(2).number == 2
|
|
||||||
|
|
||||||
def test_mock_pwm_pin_defaults():
|
def test_mock_pwm_pin_defaults():
|
||||||
pin = MockPWMPin(2)
|
pin = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
assert pin.bounce == None
|
assert pin.bounce == None
|
||||||
assert pin.edges == 'both'
|
assert pin.edges == 'both'
|
||||||
assert pin.frequency == None
|
assert pin.frequency == None
|
||||||
@@ -72,38 +64,38 @@ def test_mock_pwm_pin_defaults():
|
|||||||
assert pin.when_changed == None
|
assert pin.when_changed == None
|
||||||
|
|
||||||
def test_mock_pwm_pin_open_close():
|
def test_mock_pwm_pin_open_close():
|
||||||
pin = MockPWMPin(2)
|
pin = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
pin.close()
|
pin.close()
|
||||||
|
|
||||||
def test_mock_pwm_pin_init_twice_same_pin():
|
def test_mock_pwm_pin_init_twice_same_pin():
|
||||||
pin1 = MockPWMPin(2)
|
pin1 = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
pin2 = MockPWMPin(pin1.number)
|
pin2 = Device._pin_factory.pin(pin1.number, pin_class=MockPWMPin)
|
||||||
assert pin1 is pin2
|
assert pin1 is pin2
|
||||||
|
|
||||||
def test_mock_pwm_pin_init_twice_different_pin():
|
def test_mock_pwm_pin_init_twice_different_pin():
|
||||||
pin1 = MockPWMPin(2)
|
pin1 = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
pin2 = MockPWMPin(pin1.number+1)
|
pin2 = Device._pin_factory.pin(pin1.number + 1, pin_class=MockPWMPin)
|
||||||
assert pin1 != pin2
|
assert pin1 != pin2
|
||||||
assert pin1.number == 2
|
assert pin1.number == 2
|
||||||
assert pin2.number == pin1.number+1
|
assert pin2.number == pin1.number+1
|
||||||
|
|
||||||
def test_mock_pin_init_twice_different_modes():
|
def test_mock_pin_init_twice_different_modes():
|
||||||
pin1 = MockPin(2)
|
pin1 = Device._pin_factory.pin(2, pin_class=MockPin)
|
||||||
pin2 = MockPWMPin(pin1.number+1)
|
pin2 = Device._pin_factory.pin(pin1.number + 1, pin_class=MockPWMPin)
|
||||||
assert pin1 != pin2
|
assert pin1 != pin2
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
pin3 = MockPWMPin(pin1.number)
|
Device._pin_factory.pin(pin1.number, pin_class=MockPWMPin)
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
pin4 = MockPin(pin2.number)
|
Device._pin_factory.pin(pin2.number, pin_class=MockPin)
|
||||||
|
|
||||||
def test_mock_pin_frequency_unsupported():
|
def test_mock_pin_frequency_unsupported():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
pin.frequency = None
|
pin.frequency = None
|
||||||
with pytest.raises(PinPWMUnsupported):
|
with pytest.raises(PinPWMUnsupported):
|
||||||
pin.frequency = 100
|
pin.frequency = 100
|
||||||
|
|
||||||
def test_mock_pin_frequency_supported():
|
def test_mock_pin_frequency_supported():
|
||||||
pin = MockPWMPin(2)
|
pin = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
pin.function = 'output'
|
pin.function = 'output'
|
||||||
assert pin.frequency is None
|
assert pin.frequency is None
|
||||||
pin.frequency = 100
|
pin.frequency = 100
|
||||||
@@ -112,7 +104,7 @@ def test_mock_pin_frequency_supported():
|
|||||||
assert not pin.state
|
assert not pin.state
|
||||||
|
|
||||||
def test_mock_pin_pull():
|
def test_mock_pin_pull():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
pin.function = 'input'
|
pin.function = 'input'
|
||||||
assert pin.pull == 'floating'
|
assert pin.pull == 'floating'
|
||||||
pin.pull = 'up'
|
pin.pull = 'up'
|
||||||
@@ -121,7 +113,7 @@ def test_mock_pin_pull():
|
|||||||
assert not pin.state
|
assert not pin.state
|
||||||
|
|
||||||
def test_mock_pin_state():
|
def test_mock_pin_state():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with pytest.raises(PinSetInput):
|
with pytest.raises(PinSetInput):
|
||||||
pin.state = 1
|
pin.state = 1
|
||||||
pin.function = 'output'
|
pin.function = 'output'
|
||||||
@@ -134,7 +126,7 @@ def test_mock_pin_state():
|
|||||||
assert pin.state == 1
|
assert pin.state == 1
|
||||||
|
|
||||||
def test_mock_pwm_pin_state():
|
def test_mock_pwm_pin_state():
|
||||||
pin = MockPWMPin(2)
|
pin = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with pytest.raises(PinSetInput):
|
with pytest.raises(PinSetInput):
|
||||||
pin.state = 1
|
pin.state = 1
|
||||||
pin.function = 'output'
|
pin.function = 'output'
|
||||||
@@ -147,7 +139,7 @@ def test_mock_pwm_pin_state():
|
|||||||
assert pin.state == 0.5
|
assert pin.state == 0.5
|
||||||
|
|
||||||
def test_mock_pin_edges():
|
def test_mock_pin_edges():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
assert pin.when_changed is None
|
assert pin.when_changed is None
|
||||||
fired = Event()
|
fired = Event()
|
||||||
pin.function = 'input'
|
pin.function = 'input'
|
||||||
|
|||||||
@@ -16,15 +16,16 @@ except ImportError:
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from gpiozero.pins.mock import MockPin, MockPWMPin
|
from gpiozero.pins.mock import MockPWMPin
|
||||||
from gpiozero import *
|
from gpiozero import *
|
||||||
|
|
||||||
|
|
||||||
def teardown_function(function):
|
def teardown_function(function):
|
||||||
MockPin.clear_pins()
|
Device._pin_factory.reset()
|
||||||
|
|
||||||
|
|
||||||
def test_output_initial_values():
|
def test_output_initial_values():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with OutputDevice(pin, initial_value=False) as device:
|
with OutputDevice(pin, initial_value=False) as device:
|
||||||
assert pin.function == 'output'
|
assert pin.function == 'output'
|
||||||
assert not pin.state
|
assert not pin.state
|
||||||
@@ -35,7 +36,7 @@ def test_output_initial_values():
|
|||||||
assert state == pin.state
|
assert state == pin.state
|
||||||
|
|
||||||
def test_output_write_active_high():
|
def test_output_write_active_high():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with OutputDevice(pin) as device:
|
with OutputDevice(pin) as device:
|
||||||
device.on()
|
device.on()
|
||||||
assert pin.state
|
assert pin.state
|
||||||
@@ -43,7 +44,7 @@ def test_output_write_active_high():
|
|||||||
assert not pin.state
|
assert not pin.state
|
||||||
|
|
||||||
def test_output_write_active_low():
|
def test_output_write_active_low():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with OutputDevice(pin, active_high=False) as device:
|
with OutputDevice(pin, active_high=False) as device:
|
||||||
device.on()
|
device.on()
|
||||||
assert not pin.state
|
assert not pin.state
|
||||||
@@ -51,7 +52,7 @@ def test_output_write_active_low():
|
|||||||
assert pin.state
|
assert pin.state
|
||||||
|
|
||||||
def test_output_write_closed():
|
def test_output_write_closed():
|
||||||
with OutputDevice(MockPin(2)) as device:
|
with OutputDevice(Device._pin_factory.pin(2)) as device:
|
||||||
device.close()
|
device.close()
|
||||||
assert device.closed
|
assert device.closed
|
||||||
device.close()
|
device.close()
|
||||||
@@ -60,14 +61,14 @@ def test_output_write_closed():
|
|||||||
device.on()
|
device.on()
|
||||||
|
|
||||||
def test_output_write_silly():
|
def test_output_write_silly():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with OutputDevice(pin) as device:
|
with OutputDevice(pin) as device:
|
||||||
pin.function = 'input'
|
pin.function = 'input'
|
||||||
with pytest.raises(AttributeError):
|
with pytest.raises(AttributeError):
|
||||||
device.on()
|
device.on()
|
||||||
|
|
||||||
def test_output_value():
|
def test_output_value():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with OutputDevice(pin) as device:
|
with OutputDevice(pin) as device:
|
||||||
assert not device.value
|
assert not device.value
|
||||||
assert not pin.state
|
assert not pin.state
|
||||||
@@ -79,7 +80,7 @@ def test_output_value():
|
|||||||
assert not pin.state
|
assert not pin.state
|
||||||
|
|
||||||
def test_output_digital_toggle():
|
def test_output_digital_toggle():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with DigitalOutputDevice(pin) as device:
|
with DigitalOutputDevice(pin) as device:
|
||||||
assert not device.value
|
assert not device.value
|
||||||
assert not pin.state
|
assert not pin.state
|
||||||
@@ -93,7 +94,7 @@ def test_output_digital_toggle():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_output_blink_background():
|
def test_output_blink_background():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with DigitalOutputDevice(pin) as device:
|
with DigitalOutputDevice(pin) as device:
|
||||||
start = time()
|
start = time()
|
||||||
device.blink(0.1, 0.1, n=2)
|
device.blink(0.1, 0.1, n=2)
|
||||||
@@ -111,7 +112,7 @@ def test_output_blink_background():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_output_blink_foreground():
|
def test_output_blink_foreground():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with DigitalOutputDevice(pin) as device:
|
with DigitalOutputDevice(pin) as device:
|
||||||
start = time()
|
start = time()
|
||||||
device.blink(0.1, 0.1, n=2, background=False)
|
device.blink(0.1, 0.1, n=2, background=False)
|
||||||
@@ -125,7 +126,7 @@ def test_output_blink_foreground():
|
|||||||
])
|
])
|
||||||
|
|
||||||
def test_output_blink_interrupt_on():
|
def test_output_blink_interrupt_on():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with DigitalOutputDevice(pin) as device:
|
with DigitalOutputDevice(pin) as device:
|
||||||
device.blink(1, 0.1)
|
device.blink(1, 0.1)
|
||||||
sleep(0.2)
|
sleep(0.2)
|
||||||
@@ -133,7 +134,7 @@ def test_output_blink_interrupt_on():
|
|||||||
pin.assert_states([False, True, False])
|
pin.assert_states([False, True, False])
|
||||||
|
|
||||||
def test_output_blink_interrupt_off():
|
def test_output_blink_interrupt_off():
|
||||||
pin = MockPin(2)
|
pin = Device._pin_factory.pin(2)
|
||||||
with DigitalOutputDevice(pin) as device:
|
with DigitalOutputDevice(pin) as device:
|
||||||
device.blink(0.1, 1)
|
device.blink(0.1, 1)
|
||||||
sleep(0.2)
|
sleep(0.2)
|
||||||
@@ -142,14 +143,14 @@ def test_output_blink_interrupt_off():
|
|||||||
|
|
||||||
def test_output_pwm_bad_initial_value():
|
def test_output_pwm_bad_initial_value():
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
PWMOutputDevice(MockPin(2), initial_value=2)
|
PWMOutputDevice(Device._pin_factory.pin(2), initial_value=2)
|
||||||
|
|
||||||
def test_output_pwm_not_supported():
|
def test_output_pwm_not_supported():
|
||||||
with pytest.raises(AttributeError):
|
with pytest.raises(AttributeError):
|
||||||
PWMOutputDevice(MockPin(2))
|
PWMOutputDevice(Device._pin_factory.pin(2))
|
||||||
|
|
||||||
def test_output_pwm_states():
|
def test_output_pwm_states():
|
||||||
pin = MockPWMPin(2)
|
pin = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with PWMOutputDevice(pin) as device:
|
with PWMOutputDevice(pin) as device:
|
||||||
device.value = 0.1
|
device.value = 0.1
|
||||||
device.value = 0.2
|
device.value = 0.2
|
||||||
@@ -157,7 +158,7 @@ def test_output_pwm_states():
|
|||||||
pin.assert_states([0.0, 0.1, 0.2, 0.0])
|
pin.assert_states([0.0, 0.1, 0.2, 0.0])
|
||||||
|
|
||||||
def test_output_pwm_read():
|
def test_output_pwm_read():
|
||||||
pin = MockPWMPin(2)
|
pin = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with PWMOutputDevice(pin, frequency=100) as device:
|
with PWMOutputDevice(pin, frequency=100) as device:
|
||||||
assert device.frequency == 100
|
assert device.frequency == 100
|
||||||
device.value = 0.1
|
device.value = 0.1
|
||||||
@@ -170,14 +171,14 @@ def test_output_pwm_read():
|
|||||||
assert device.frequency is None
|
assert device.frequency is None
|
||||||
|
|
||||||
def test_output_pwm_write():
|
def test_output_pwm_write():
|
||||||
pin = MockPWMPin(2)
|
pin = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with PWMOutputDevice(pin) as device:
|
with PWMOutputDevice(pin) as device:
|
||||||
device.on()
|
device.on()
|
||||||
device.off()
|
device.off()
|
||||||
pin.assert_states([False, True, False])
|
pin.assert_states([False, True, False])
|
||||||
|
|
||||||
def test_output_pwm_toggle():
|
def test_output_pwm_toggle():
|
||||||
pin = MockPWMPin(2)
|
pin = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with PWMOutputDevice(pin) as device:
|
with PWMOutputDevice(pin) as device:
|
||||||
device.toggle()
|
device.toggle()
|
||||||
device.value = 0.5
|
device.value = 0.5
|
||||||
@@ -187,7 +188,7 @@ def test_output_pwm_toggle():
|
|||||||
pin.assert_states([False, True, 0.5, 0.1, 0.9, False])
|
pin.assert_states([False, True, 0.5, 0.1, 0.9, False])
|
||||||
|
|
||||||
def test_output_pwm_active_high_read():
|
def test_output_pwm_active_high_read():
|
||||||
pin = MockPWMPin(2)
|
pin = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with PWMOutputDevice(pin, active_high=False) as device:
|
with PWMOutputDevice(pin, active_high=False) as device:
|
||||||
device.value = 0.1
|
device.value = 0.1
|
||||||
assert isclose(device.value, 0.1)
|
assert isclose(device.value, 0.1)
|
||||||
@@ -196,17 +197,18 @@ def test_output_pwm_active_high_read():
|
|||||||
assert device.value
|
assert device.value
|
||||||
|
|
||||||
def test_output_pwm_bad_value():
|
def test_output_pwm_bad_value():
|
||||||
with pytest.raises(ValueError):
|
with PWMOutputDevice(Device._pin_factory.pin(2, pin_class=MockPWMPin)) as device:
|
||||||
PWMOutputDevice(MockPWMPin(2)).value = 2
|
with pytest.raises(ValueError):
|
||||||
|
device.value = 2
|
||||||
|
|
||||||
def test_output_pwm_write_closed():
|
def test_output_pwm_write_closed():
|
||||||
device = PWMOutputDevice(MockPWMPin(2))
|
with PWMOutputDevice(Device._pin_factory.pin(2, pin_class=MockPWMPin)) as device:
|
||||||
device.close()
|
device.close()
|
||||||
with pytest.raises(GPIODeviceClosed):
|
with pytest.raises(GPIODeviceClosed):
|
||||||
device.on()
|
device.on()
|
||||||
|
|
||||||
def test_output_pwm_write_silly():
|
def test_output_pwm_write_silly():
|
||||||
pin = MockPWMPin(2)
|
pin = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with PWMOutputDevice(pin) as device:
|
with PWMOutputDevice(pin) as device:
|
||||||
pin.function = 'input'
|
pin.function = 'input'
|
||||||
with pytest.raises(AttributeError):
|
with pytest.raises(AttributeError):
|
||||||
@@ -215,7 +217,7 @@ def test_output_pwm_write_silly():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_output_pwm_blink_background():
|
def test_output_pwm_blink_background():
|
||||||
pin = MockPWMPin(2)
|
pin = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with PWMOutputDevice(pin) as device:
|
with PWMOutputDevice(pin) as device:
|
||||||
start = time()
|
start = time()
|
||||||
device.blink(0.1, 0.1, n=2)
|
device.blink(0.1, 0.1, n=2)
|
||||||
@@ -233,7 +235,7 @@ def test_output_pwm_blink_background():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_output_pwm_blink_foreground():
|
def test_output_pwm_blink_foreground():
|
||||||
pin = MockPWMPin(2)
|
pin = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with PWMOutputDevice(pin) as device:
|
with PWMOutputDevice(pin) as device:
|
||||||
start = time()
|
start = time()
|
||||||
device.blink(0.1, 0.1, n=2, background=False)
|
device.blink(0.1, 0.1, n=2, background=False)
|
||||||
@@ -249,7 +251,7 @@ def test_output_pwm_blink_foreground():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_output_pwm_fade_background():
|
def test_output_pwm_fade_background():
|
||||||
pin = MockPWMPin(2)
|
pin = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with PWMOutputDevice(pin) as device:
|
with PWMOutputDevice(pin) as device:
|
||||||
start = time()
|
start = time()
|
||||||
device.blink(0, 0, 0.2, 0.2, n=2)
|
device.blink(0, 0, 0.2, 0.2, n=2)
|
||||||
@@ -283,7 +285,7 @@ def test_output_pwm_fade_background():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_output_pwm_fade_foreground():
|
def test_output_pwm_fade_foreground():
|
||||||
pin = MockPWMPin(2)
|
pin = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with PWMOutputDevice(pin) as device:
|
with PWMOutputDevice(pin) as device:
|
||||||
start = time()
|
start = time()
|
||||||
device.blink(0, 0, 0.2, 0.2, n=2, background=False)
|
device.blink(0, 0, 0.2, 0.2, n=2, background=False)
|
||||||
@@ -315,7 +317,7 @@ def test_output_pwm_fade_foreground():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_output_pwm_pulse_background():
|
def test_output_pwm_pulse_background():
|
||||||
pin = MockPWMPin(2)
|
pin = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with PWMOutputDevice(pin) as device:
|
with PWMOutputDevice(pin) as device:
|
||||||
start = time()
|
start = time()
|
||||||
device.pulse(0.2, 0.2, n=2)
|
device.pulse(0.2, 0.2, n=2)
|
||||||
@@ -349,7 +351,7 @@ def test_output_pwm_pulse_background():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_output_pwm_pulse_foreground():
|
def test_output_pwm_pulse_foreground():
|
||||||
pin = MockPWMPin(2)
|
pin = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with PWMOutputDevice(pin) as device:
|
with PWMOutputDevice(pin) as device:
|
||||||
start = time()
|
start = time()
|
||||||
device.pulse(0.2, 0.2, n=2, background=False)
|
device.pulse(0.2, 0.2, n=2, background=False)
|
||||||
@@ -379,7 +381,7 @@ def test_output_pwm_pulse_foreground():
|
|||||||
])
|
])
|
||||||
|
|
||||||
def test_output_pwm_blink_interrupt():
|
def test_output_pwm_blink_interrupt():
|
||||||
pin = MockPWMPin(2)
|
pin = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with PWMOutputDevice(pin) as device:
|
with PWMOutputDevice(pin) as device:
|
||||||
device.blink(1, 0.1)
|
device.blink(1, 0.1)
|
||||||
sleep(0.2)
|
sleep(0.2)
|
||||||
@@ -391,7 +393,7 @@ def test_rgbled_missing_pins():
|
|||||||
RGBLED()
|
RGBLED()
|
||||||
|
|
||||||
def test_rgbled_initial_value():
|
def test_rgbled_initial_value():
|
||||||
r, g, b = (MockPWMPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b, initial_value=(0.1, 0.2, 0)) as device:
|
with RGBLED(r, g, b, initial_value=(0.1, 0.2, 0)) as device:
|
||||||
assert r.frequency
|
assert r.frequency
|
||||||
assert g.frequency
|
assert g.frequency
|
||||||
@@ -401,24 +403,24 @@ def test_rgbled_initial_value():
|
|||||||
assert isclose(b.state, 0.0)
|
assert isclose(b.state, 0.0)
|
||||||
|
|
||||||
def test_rgbled_initial_value_nonpwm():
|
def test_rgbled_initial_value_nonpwm():
|
||||||
r, g, b = (MockPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b, pwm=False, initial_value=(0, 1, 1)) as device:
|
with RGBLED(r, g, b, pwm=False, initial_value=(0, 1, 1)) as device:
|
||||||
assert r.state == 0
|
assert r.state == 0
|
||||||
assert g.state == 1
|
assert g.state == 1
|
||||||
assert b.state == 1
|
assert b.state == 1
|
||||||
|
|
||||||
def test_rgbled_initial_bad_value():
|
def test_rgbled_initial_bad_value():
|
||||||
r, g, b = (MockPWMPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3))
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
RGBLED(r, g, b, initial_value=(0.1, 0.2, 1.2))
|
RGBLED(r, g, b, initial_value=(0.1, 0.2, 1.2))
|
||||||
|
|
||||||
def test_rgbled_initial_bad_value_nonpwm():
|
def test_rgbled_initial_bad_value_nonpwm():
|
||||||
r, g, b = (MockPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i) for i in (1, 2, 3))
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
RGBLED(r, g, b, pwm=False, initial_value=(0.1, 0.2, 0))
|
RGBLED(r, g, b, pwm=False, initial_value=(0.1, 0.2, 0))
|
||||||
|
|
||||||
def test_rgbled_value():
|
def test_rgbled_value():
|
||||||
r, g, b = (MockPWMPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b) as device:
|
with RGBLED(r, g, b) as device:
|
||||||
assert isinstance(device._leds[0], PWMLED)
|
assert isinstance(device._leds[0], PWMLED)
|
||||||
assert isinstance(device._leds[1], PWMLED)
|
assert isinstance(device._leds[1], PWMLED)
|
||||||
@@ -436,7 +438,7 @@ def test_rgbled_value():
|
|||||||
assert device.value == (0.5, 0.5, 0.5)
|
assert device.value == (0.5, 0.5, 0.5)
|
||||||
|
|
||||||
def test_rgbled_value_nonpwm():
|
def test_rgbled_value_nonpwm():
|
||||||
r, g, b = (MockPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b, pwm=False) as device:
|
with RGBLED(r, g, b, pwm=False) as device:
|
||||||
assert isinstance(device._leds[0], LED)
|
assert isinstance(device._leds[0], LED)
|
||||||
assert isinstance(device._leds[1], LED)
|
assert isinstance(device._leds[1], LED)
|
||||||
@@ -451,7 +453,7 @@ def test_rgbled_value_nonpwm():
|
|||||||
assert device.value == (0, 0, 0)
|
assert device.value == (0, 0, 0)
|
||||||
|
|
||||||
def test_rgbled_bad_value():
|
def test_rgbled_bad_value():
|
||||||
r, g, b = (MockPWMPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b) as device:
|
with RGBLED(r, g, b) as device:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
device.value = (2, 0, 0)
|
device.value = (2, 0, 0)
|
||||||
@@ -460,7 +462,7 @@ def test_rgbled_bad_value():
|
|||||||
device.value = (0, -1, 0)
|
device.value = (0, -1, 0)
|
||||||
|
|
||||||
def test_rgbled_bad_value_nonpwm():
|
def test_rgbled_bad_value_nonpwm():
|
||||||
r, g, b = (MockPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b, pwm=False) as device:
|
with RGBLED(r, g, b, pwm=False) as device:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
device.value = (2, 0, 0)
|
device.value = (2, 0, 0)
|
||||||
@@ -478,7 +480,7 @@ def test_rgbled_bad_value_nonpwm():
|
|||||||
device.value = (0, 0, 0.5)
|
device.value = (0, 0, 0.5)
|
||||||
|
|
||||||
def test_rgbled_toggle():
|
def test_rgbled_toggle():
|
||||||
r, g, b = (MockPWMPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b) as device:
|
with RGBLED(r, g, b) as device:
|
||||||
assert not device.is_active
|
assert not device.is_active
|
||||||
assert device.value == (0, 0, 0)
|
assert device.value == (0, 0, 0)
|
||||||
@@ -490,7 +492,7 @@ def test_rgbled_toggle():
|
|||||||
assert device.value == (0, 0, 0)
|
assert device.value == (0, 0, 0)
|
||||||
|
|
||||||
def test_rgbled_toggle_nonpwm():
|
def test_rgbled_toggle_nonpwm():
|
||||||
r, g, b = (MockPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b, pwm=False) as device:
|
with RGBLED(r, g, b, pwm=False) as device:
|
||||||
assert not device.is_active
|
assert not device.is_active
|
||||||
assert device.value == (0, 0, 0)
|
assert device.value == (0, 0, 0)
|
||||||
@@ -501,10 +503,18 @@ def test_rgbled_toggle_nonpwm():
|
|||||||
assert not device.is_active
|
assert not device.is_active
|
||||||
assert device.value == (0, 0, 0)
|
assert device.value == (0, 0, 0)
|
||||||
|
|
||||||
|
def test_rgbled_blink_nonpwm():
|
||||||
|
r, g, b = (Device._pin_factory.pin(i) for i in (1, 2, 3))
|
||||||
|
with RGBLED(r, g, b, pwm=False) as device:
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
device.blink(fade_in_time=1)
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
device.blink(fade_out_time=1)
|
||||||
|
|
||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_rgbled_blink_background():
|
def test_rgbled_blink_background():
|
||||||
r, g, b = (MockPWMPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b) as device:
|
with RGBLED(r, g, b) as device:
|
||||||
start = time()
|
start = time()
|
||||||
device.blink(0.1, 0.1, n=2)
|
device.blink(0.1, 0.1, n=2)
|
||||||
@@ -525,7 +535,7 @@ def test_rgbled_blink_background():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_rgbled_blink_background_nonpwm():
|
def test_rgbled_blink_background_nonpwm():
|
||||||
r, g, b = (MockPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b, pwm=False) as device:
|
with RGBLED(r, g, b, pwm=False) as device:
|
||||||
start = time()
|
start = time()
|
||||||
device.blink(0.1, 0.1, n=2)
|
device.blink(0.1, 0.1, n=2)
|
||||||
@@ -546,7 +556,7 @@ def test_rgbled_blink_background_nonpwm():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_rgbled_blink_foreground():
|
def test_rgbled_blink_foreground():
|
||||||
r, g, b = (MockPWMPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b) as device:
|
with RGBLED(r, g, b) as device:
|
||||||
start = time()
|
start = time()
|
||||||
device.blink(0.1, 0.1, n=2, background=False)
|
device.blink(0.1, 0.1, n=2, background=False)
|
||||||
@@ -565,7 +575,7 @@ def test_rgbled_blink_foreground():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_rgbled_blink_foreground_nonpwm():
|
def test_rgbled_blink_foreground_nonpwm():
|
||||||
r, g, b = (MockPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b, pwm=False) as device:
|
with RGBLED(r, g, b, pwm=False) as device:
|
||||||
start = time()
|
start = time()
|
||||||
device.blink(0.1, 0.1, n=2, background=False)
|
device.blink(0.1, 0.1, n=2, background=False)
|
||||||
@@ -584,7 +594,7 @@ def test_rgbled_blink_foreground_nonpwm():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_rgbled_fade_background():
|
def test_rgbled_fade_background():
|
||||||
r, g, b = (MockPWMPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b) as device:
|
with RGBLED(r, g, b) as device:
|
||||||
start = time()
|
start = time()
|
||||||
device.blink(0, 0, 0.2, 0.2, n=2)
|
device.blink(0, 0, 0.2, 0.2, n=2)
|
||||||
@@ -619,7 +629,7 @@ def test_rgbled_fade_background():
|
|||||||
b.assert_states_and_times(expected)
|
b.assert_states_and_times(expected)
|
||||||
|
|
||||||
def test_rgbled_fade_background_nonpwm():
|
def test_rgbled_fade_background_nonpwm():
|
||||||
r, g, b = (MockPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b, pwm=False) as device:
|
with RGBLED(r, g, b, pwm=False) as device:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
device.blink(0, 0, 0.2, 0.2, n=2)
|
device.blink(0, 0, 0.2, 0.2, n=2)
|
||||||
@@ -627,7 +637,7 @@ def test_rgbled_fade_background_nonpwm():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_rgbled_fade_foreground():
|
def test_rgbled_fade_foreground():
|
||||||
r, g, b = (MockPWMPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b) as device:
|
with RGBLED(r, g, b) as device:
|
||||||
start = time()
|
start = time()
|
||||||
device.blink(0, 0, 0.2, 0.2, n=2, background=False)
|
device.blink(0, 0, 0.2, 0.2, n=2, background=False)
|
||||||
@@ -660,7 +670,7 @@ def test_rgbled_fade_foreground():
|
|||||||
b.assert_states_and_times(expected)
|
b.assert_states_and_times(expected)
|
||||||
|
|
||||||
def test_rgbled_fade_foreground_nonpwm():
|
def test_rgbled_fade_foreground_nonpwm():
|
||||||
r, g, b = (MockPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b, pwm=False) as device:
|
with RGBLED(r, g, b, pwm=False) as device:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
device.blink(0, 0, 0.2, 0.2, n=2, background=False)
|
device.blink(0, 0, 0.2, 0.2, n=2, background=False)
|
||||||
@@ -668,7 +678,7 @@ def test_rgbled_fade_foreground_nonpwm():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_rgbled_pulse_background():
|
def test_rgbled_pulse_background():
|
||||||
r, g, b = (MockPWMPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b) as device:
|
with RGBLED(r, g, b) as device:
|
||||||
start = time()
|
start = time()
|
||||||
device.pulse(0.2, 0.2, n=2)
|
device.pulse(0.2, 0.2, n=2)
|
||||||
@@ -703,7 +713,7 @@ def test_rgbled_pulse_background():
|
|||||||
b.assert_states_and_times(expected)
|
b.assert_states_and_times(expected)
|
||||||
|
|
||||||
def test_rgbled_pulse_background_nonpwm():
|
def test_rgbled_pulse_background_nonpwm():
|
||||||
r, g, b = (MockPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b, pwm=False) as device:
|
with RGBLED(r, g, b, pwm=False) as device:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
device.pulse(0.2, 0.2, n=2)
|
device.pulse(0.2, 0.2, n=2)
|
||||||
@@ -711,7 +721,7 @@ def test_rgbled_pulse_background_nonpwm():
|
|||||||
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
|
||||||
reason='timing is too random on pypy')
|
reason='timing is too random on pypy')
|
||||||
def test_rgbled_pulse_foreground():
|
def test_rgbled_pulse_foreground():
|
||||||
r, g, b = (MockPWMPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b) as device:
|
with RGBLED(r, g, b) as device:
|
||||||
start = time()
|
start = time()
|
||||||
device.pulse(0.2, 0.2, n=2, background=False)
|
device.pulse(0.2, 0.2, n=2, background=False)
|
||||||
@@ -744,13 +754,13 @@ def test_rgbled_pulse_foreground():
|
|||||||
b.assert_states_and_times(expected)
|
b.assert_states_and_times(expected)
|
||||||
|
|
||||||
def test_rgbled_pulse_foreground_nonpwm():
|
def test_rgbled_pulse_foreground_nonpwm():
|
||||||
r, g, b = (MockPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b, pwm=False) as device:
|
with RGBLED(r, g, b, pwm=False) as device:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
device.pulse(0.2, 0.2, n=2, background=False)
|
device.pulse(0.2, 0.2, n=2, background=False)
|
||||||
|
|
||||||
def test_rgbled_blink_interrupt():
|
def test_rgbled_blink_interrupt():
|
||||||
r, g, b = (MockPWMPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b) as device:
|
with RGBLED(r, g, b) as device:
|
||||||
device.blink(1, 0.1)
|
device.blink(1, 0.1)
|
||||||
sleep(0.2)
|
sleep(0.2)
|
||||||
@@ -760,7 +770,7 @@ def test_rgbled_blink_interrupt():
|
|||||||
b.assert_states([0, 1, 0])
|
b.assert_states([0, 1, 0])
|
||||||
|
|
||||||
def test_rgbled_blink_interrupt_nonpwm():
|
def test_rgbled_blink_interrupt_nonpwm():
|
||||||
r, g, b = (MockPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b, pwm=False) as device:
|
with RGBLED(r, g, b, pwm=False) as device:
|
||||||
device.blink(1, 0.1)
|
device.blink(1, 0.1)
|
||||||
sleep(0.2)
|
sleep(0.2)
|
||||||
@@ -770,7 +780,7 @@ def test_rgbled_blink_interrupt_nonpwm():
|
|||||||
b.assert_states([0, 1, 0])
|
b.assert_states([0, 1, 0])
|
||||||
|
|
||||||
def test_rgbled_close():
|
def test_rgbled_close():
|
||||||
r, g, b = (MockPWMPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b) as device:
|
with RGBLED(r, g, b) as device:
|
||||||
assert not device.closed
|
assert not device.closed
|
||||||
device.close()
|
device.close()
|
||||||
@@ -779,7 +789,7 @@ def test_rgbled_close():
|
|||||||
assert device.closed
|
assert device.closed
|
||||||
|
|
||||||
def test_rgbled_close_nonpwm():
|
def test_rgbled_close_nonpwm():
|
||||||
r, g, b = (MockPin(i) for i in (1, 2, 3))
|
r, g, b = (Device._pin_factory.pin(i) for i in (1, 2, 3))
|
||||||
with RGBLED(r, g, b, pwm=False) as device:
|
with RGBLED(r, g, b, pwm=False) as device:
|
||||||
assert not device.closed
|
assert not device.closed
|
||||||
device.close()
|
device.close()
|
||||||
@@ -792,8 +802,8 @@ def test_motor_missing_pins():
|
|||||||
Motor()
|
Motor()
|
||||||
|
|
||||||
def test_motor_pins():
|
def test_motor_pins():
|
||||||
f = MockPWMPin(1)
|
f = Device._pin_factory.pin(1, pin_class=MockPWMPin)
|
||||||
b = MockPWMPin(2)
|
b = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with Motor(f, b) as device:
|
with Motor(f, b) as device:
|
||||||
assert device.forward_device.pin is f
|
assert device.forward_device.pin is f
|
||||||
assert isinstance(device.forward_device, PWMOutputDevice)
|
assert isinstance(device.forward_device, PWMOutputDevice)
|
||||||
@@ -801,8 +811,8 @@ def test_motor_pins():
|
|||||||
assert isinstance(device.backward_device, PWMOutputDevice)
|
assert isinstance(device.backward_device, PWMOutputDevice)
|
||||||
|
|
||||||
def test_motor_pins_nonpwm():
|
def test_motor_pins_nonpwm():
|
||||||
f = MockPin(1)
|
f = Device._pin_factory.pin(1)
|
||||||
b = MockPin(2)
|
b = Device._pin_factory.pin(2)
|
||||||
with Motor(f, b, pwm=False) as device:
|
with Motor(f, b, pwm=False) as device:
|
||||||
assert device.forward_device.pin is f
|
assert device.forward_device.pin is f
|
||||||
assert isinstance(device.forward_device, DigitalOutputDevice)
|
assert isinstance(device.forward_device, DigitalOutputDevice)
|
||||||
@@ -810,8 +820,8 @@ def test_motor_pins_nonpwm():
|
|||||||
assert isinstance(device.backward_device, DigitalOutputDevice)
|
assert isinstance(device.backward_device, DigitalOutputDevice)
|
||||||
|
|
||||||
def test_motor_close():
|
def test_motor_close():
|
||||||
f = MockPWMPin(1)
|
f = Device._pin_factory.pin(1, pin_class=MockPWMPin)
|
||||||
b = MockPWMPin(2)
|
b = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with Motor(f, b) as device:
|
with Motor(f, b) as device:
|
||||||
device.close()
|
device.close()
|
||||||
assert device.closed
|
assert device.closed
|
||||||
@@ -821,8 +831,8 @@ def test_motor_close():
|
|||||||
assert device.closed
|
assert device.closed
|
||||||
|
|
||||||
def test_motor_close_nonpwm():
|
def test_motor_close_nonpwm():
|
||||||
f = MockPin(1)
|
f = Device._pin_factory.pin(1)
|
||||||
b = MockPin(2)
|
b = Device._pin_factory.pin(2)
|
||||||
with Motor(f, b, pwm=False) as device:
|
with Motor(f, b, pwm=False) as device:
|
||||||
device.close()
|
device.close()
|
||||||
assert device.closed
|
assert device.closed
|
||||||
@@ -830,8 +840,8 @@ def test_motor_close_nonpwm():
|
|||||||
assert device.backward_device.pin is None
|
assert device.backward_device.pin is None
|
||||||
|
|
||||||
def test_motor_value():
|
def test_motor_value():
|
||||||
f = MockPWMPin(1)
|
f = Device._pin_factory.pin(1, pin_class=MockPWMPin)
|
||||||
b = MockPWMPin(2)
|
b = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with Motor(f, b) as device:
|
with Motor(f, b) as device:
|
||||||
device.value = -1
|
device.value = -1
|
||||||
assert device.is_active
|
assert device.is_active
|
||||||
@@ -855,8 +865,8 @@ def test_motor_value():
|
|||||||
assert b.state == 0 and f.state == 0
|
assert b.state == 0 and f.state == 0
|
||||||
|
|
||||||
def test_motor_value_nonpwm():
|
def test_motor_value_nonpwm():
|
||||||
f = MockPin(1)
|
f = Device._pin_factory.pin(1)
|
||||||
b = MockPin(2)
|
b = Device._pin_factory.pin(2)
|
||||||
with Motor(f, b, pwm=False) as device:
|
with Motor(f, b, pwm=False) as device:
|
||||||
device.value = -1
|
device.value = -1
|
||||||
assert device.is_active
|
assert device.is_active
|
||||||
@@ -872,17 +882,21 @@ def test_motor_value_nonpwm():
|
|||||||
assert b.state == 0 and f.state == 0
|
assert b.state == 0 and f.state == 0
|
||||||
|
|
||||||
def test_motor_bad_value():
|
def test_motor_bad_value():
|
||||||
f = MockPWMPin(1)
|
f = Device._pin_factory.pin(1, pin_class=MockPWMPin)
|
||||||
b = MockPWMPin(2)
|
b = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with Motor(f, b) as device:
|
with Motor(f, b) as device:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
device.value = -2
|
device.value = -2
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
device.value = 2
|
device.value = 2
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
device.forward(2)
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
device.backward(2)
|
||||||
|
|
||||||
def test_motor_bad_value_nonpwm():
|
def test_motor_bad_value_nonpwm():
|
||||||
f = MockPin(1)
|
f = Device._pin_factory.pin(1)
|
||||||
b = MockPin(2)
|
b = Device._pin_factory.pin(2)
|
||||||
with Motor(f, b, pwm=False) as device:
|
with Motor(f, b, pwm=False) as device:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
device.value = -2
|
device.value = -2
|
||||||
@@ -894,8 +908,8 @@ def test_motor_bad_value_nonpwm():
|
|||||||
device.value = -0.5
|
device.value = -0.5
|
||||||
|
|
||||||
def test_motor_reverse():
|
def test_motor_reverse():
|
||||||
f = MockPWMPin(1)
|
f = Device._pin_factory.pin(1, pin_class=MockPWMPin)
|
||||||
b = MockPWMPin(2)
|
b = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with Motor(f, b) as device:
|
with Motor(f, b) as device:
|
||||||
device.forward()
|
device.forward()
|
||||||
assert device.value == 1
|
assert device.value == 1
|
||||||
@@ -911,8 +925,8 @@ def test_motor_reverse():
|
|||||||
assert b.state == 0 and f.state == 0.5
|
assert b.state == 0 and f.state == 0.5
|
||||||
|
|
||||||
def test_motor_reverse_nonpwm():
|
def test_motor_reverse_nonpwm():
|
||||||
f = MockPin(1)
|
f = Device._pin_factory.pin(1)
|
||||||
b = MockPin(2)
|
b = Device._pin_factory.pin(2)
|
||||||
with Motor(f, b, pwm=False) as device:
|
with Motor(f, b, pwm=False) as device:
|
||||||
device.forward()
|
device.forward()
|
||||||
assert device.value == 1
|
assert device.value == 1
|
||||||
@@ -922,13 +936,13 @@ def test_motor_reverse_nonpwm():
|
|||||||
assert b.state == 1 and f.state == 0
|
assert b.state == 1 and f.state == 0
|
||||||
|
|
||||||
def test_servo_pins():
|
def test_servo_pins():
|
||||||
p = MockPWMPin(1)
|
p = Device._pin_factory.pin(1, pin_class=MockPWMPin)
|
||||||
with Servo(p) as device:
|
with Servo(p) as device:
|
||||||
assert device.pwm_device.pin is p
|
assert device.pwm_device.pin is p
|
||||||
assert isinstance(device.pwm_device, PWMOutputDevice)
|
assert isinstance(device.pwm_device, PWMOutputDevice)
|
||||||
|
|
||||||
def test_servo_bad_value():
|
def test_servo_bad_value():
|
||||||
p = MockPWMPin(1)
|
p = Device._pin_factory.pin(1, pin_class=MockPWMPin)
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
Servo(p, initial_value=2)
|
Servo(p, initial_value=2)
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
@@ -937,12 +951,12 @@ def test_servo_bad_value():
|
|||||||
Servo(p, max_pulse_width=30/1000)
|
Servo(p, max_pulse_width=30/1000)
|
||||||
|
|
||||||
def test_servo_pins_nonpwm():
|
def test_servo_pins_nonpwm():
|
||||||
p = MockPin(2)
|
p = Device._pin_factory.pin(2)
|
||||||
with pytest.raises(PinPWMUnsupported):
|
with pytest.raises(PinPWMUnsupported):
|
||||||
Servo(p)
|
Servo(p)
|
||||||
|
|
||||||
def test_servo_close():
|
def test_servo_close():
|
||||||
p = MockPWMPin(2)
|
p = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with Servo(p) as device:
|
with Servo(p) as device:
|
||||||
device.close()
|
device.close()
|
||||||
assert device.closed
|
assert device.closed
|
||||||
@@ -951,7 +965,7 @@ def test_servo_close():
|
|||||||
assert device.closed
|
assert device.closed
|
||||||
|
|
||||||
def test_servo_pulse_width():
|
def test_servo_pulse_width():
|
||||||
p = MockPWMPin(2)
|
p = Device._pin_factory.pin(2, pin_class=MockPWMPin)
|
||||||
with Servo(p, min_pulse_width=5/10000, max_pulse_width=25/10000) as device:
|
with Servo(p, min_pulse_width=5/10000, max_pulse_width=25/10000) as device:
|
||||||
assert isclose(device.min_pulse_width, 5/10000)
|
assert isclose(device.min_pulse_width, 5/10000)
|
||||||
assert isclose(device.max_pulse_width, 25/10000)
|
assert isclose(device.max_pulse_width, 25/10000)
|
||||||
@@ -965,7 +979,7 @@ def test_servo_pulse_width():
|
|||||||
assert device.pulse_width is None
|
assert device.pulse_width is None
|
||||||
|
|
||||||
def test_servo_values():
|
def test_servo_values():
|
||||||
p = MockPWMPin(1)
|
p = Device._pin_factory.pin(1, pin_class=MockPWMPin)
|
||||||
with Servo(p) as device:
|
with Servo(p) as device:
|
||||||
device.min()
|
device.min()
|
||||||
assert device.is_active
|
assert device.is_active
|
||||||
@@ -992,13 +1006,13 @@ def test_servo_values():
|
|||||||
assert device.value is None
|
assert device.value is None
|
||||||
|
|
||||||
def test_angular_servo_range():
|
def test_angular_servo_range():
|
||||||
p = MockPWMPin(1)
|
p = Device._pin_factory.pin(1, pin_class=MockPWMPin)
|
||||||
with AngularServo(p, initial_angle=15, min_angle=0, max_angle=90) as device:
|
with AngularServo(p, initial_angle=15, min_angle=0, max_angle=90) as device:
|
||||||
assert device.min_angle == 0
|
assert device.min_angle == 0
|
||||||
assert device.max_angle == 90
|
assert device.max_angle == 90
|
||||||
|
|
||||||
def test_angular_servo_angles():
|
def test_angular_servo_angles():
|
||||||
p = MockPWMPin(1)
|
p = Device._pin_factory.pin(1, pin_class=MockPWMPin)
|
||||||
with AngularServo(p) as device:
|
with AngularServo(p) as device:
|
||||||
device.angle = 0
|
device.angle = 0
|
||||||
assert device.angle == 0
|
assert device.angle == 0
|
||||||
|
|||||||
@@ -11,44 +11,44 @@ import re
|
|||||||
import pytest
|
import pytest
|
||||||
from mock import patch, MagicMock
|
from mock import patch, MagicMock
|
||||||
|
|
||||||
import gpiozero.devices
|
|
||||||
import gpiozero.pins.data
|
import gpiozero.pins.data
|
||||||
import gpiozero.pins.native
|
import gpiozero.pins.local
|
||||||
from gpiozero.pins.data import pi_info, Style, HeaderInfo, PinInfo
|
from gpiozero.pins.local import LocalPiFactory
|
||||||
from gpiozero import PinMultiplePins, PinNoPins, PinUnknownPi
|
from gpiozero.pins.data import Style, HeaderInfo, PinInfo
|
||||||
|
from gpiozero import *
|
||||||
|
|
||||||
|
|
||||||
def test_pi_revision():
|
def test_pi_revision():
|
||||||
save_factory = gpiozero.devices.pin_factory
|
# We're not using _set_pin_factory here because we don't want to implicitly
|
||||||
try:
|
# close the old instance, just replace it while we test stuff
|
||||||
|
with patch('gpiozero.devices.Device._pin_factory', LocalPiFactory()):
|
||||||
# Can't use MockPin for this as we want something that'll actually try
|
# Can't use MockPin for this as we want something that'll actually try
|
||||||
# and read /proc/cpuinfo (MockPin simply parrots the 2B's data);
|
# and read /proc/cpuinfo (MockPin simply parrots the 2B's data);
|
||||||
# NativePin is used as we're guaranteed to be able to import it
|
# LocalPiFactory is used as we can definitely instantiate it (strictly
|
||||||
gpiozero.devices.pin_factory = gpiozero.pins.native.NativePin
|
# speaking it's abstract but we're only interested in the pi_info
|
||||||
|
# stuff)
|
||||||
with patch('io.open') as m:
|
with patch('io.open') as m:
|
||||||
m.return_value.__enter__.return_value = ['lots of irrelevant', 'lines', 'followed by', 'Revision: 0002', 'Serial: xxxxxxxxxxx']
|
m.return_value.__enter__.return_value = ['lots of irrelevant', 'lines', 'followed by', 'Revision: 0002', 'Serial: xxxxxxxxxxx']
|
||||||
assert pi_info().revision == '0002'
|
assert pi_info().revision == '0002'
|
||||||
# LocalPin caches the revision (because realistically it isn't going to
|
# LocalPiFactory caches the revision (because realistically it
|
||||||
# change at runtime); we need to wipe it here though
|
# isn't going to change at runtime); we need to wipe it here though
|
||||||
gpiozero.pins.native.NativePin._PI_REVISION = None
|
Device._pin_factory._info = None
|
||||||
m.return_value.__enter__.return_value = ['Revision: a21042']
|
m.return_value.__enter__.return_value = ['Revision: a21042']
|
||||||
assert pi_info().revision == 'a21042'
|
assert pi_info().revision == 'a21042'
|
||||||
# Check over-volting result (some argument over whether this is 7 or
|
# Check over-volting result (some argument over whether this is 7 or
|
||||||
# 8 character result; make sure both work)
|
# 8 character result; make sure both work)
|
||||||
gpiozero.pins.native.NativePin._PI_REVISION = None
|
Device._pin_factory._info = None
|
||||||
m.return_value.__enter__.return_value = ['Revision: 1000003']
|
m.return_value.__enter__.return_value = ['Revision: 1000003']
|
||||||
assert pi_info().revision == '0003'
|
assert pi_info().revision == '0003'
|
||||||
gpiozero.pins.native.NativePin._PI_REVISION = None
|
Device._pin_factory._info = None
|
||||||
m.return_value.__enter__.return_value = ['Revision: 100003']
|
m.return_value.__enter__.return_value = ['Revision: 100003']
|
||||||
assert pi_info().revision == '0003'
|
assert pi_info().revision == '0003'
|
||||||
with pytest.raises(PinUnknownPi):
|
with pytest.raises(PinUnknownPi):
|
||||||
m.return_value.__enter__.return_value = ['nothing', 'relevant', 'at all']
|
m.return_value.__enter__.return_value = ['nothing', 'relevant', 'at all']
|
||||||
gpiozero.pins.native.NativePin._PI_REVISION = None
|
Device._pin_factory._info = None
|
||||||
pi_info()
|
pi_info()
|
||||||
with pytest.raises(PinUnknownPi):
|
with pytest.raises(PinUnknownPi):
|
||||||
pi_info('0fff')
|
pi_info('0fff')
|
||||||
finally:
|
|
||||||
gpiozero.devices.pin_factory = save_factory
|
|
||||||
|
|
||||||
def test_pi_info():
|
def test_pi_info():
|
||||||
r = pi_info('900011')
|
r = pi_info('900011')
|
||||||
@@ -73,14 +73,14 @@ def test_pi_info_other_types():
|
|||||||
|
|
||||||
def test_physical_pins():
|
def test_physical_pins():
|
||||||
# Assert physical pins for some well-known Pi's; a21041 is a Pi2B
|
# Assert physical pins for some well-known Pi's; a21041 is a Pi2B
|
||||||
assert pi_info('a21041').physical_pins('3V3') == {('P1', 1), ('P1', 17)}
|
assert pi_info('a21041').physical_pins('3V3') == {('J8', 1), ('J8', 17)}
|
||||||
assert pi_info('a21041').physical_pins('GPIO2') == {('P1', 3)}
|
assert pi_info('a21041').physical_pins('GPIO2') == {('J8', 3)}
|
||||||
assert pi_info('a21041').physical_pins('GPIO47') == set()
|
assert pi_info('a21041').physical_pins('GPIO47') == set()
|
||||||
|
|
||||||
def test_physical_pin():
|
def test_physical_pin():
|
||||||
with pytest.raises(PinMultiplePins):
|
with pytest.raises(PinMultiplePins):
|
||||||
assert pi_info('a21041').physical_pin('GND')
|
assert pi_info('a21041').physical_pin('GND')
|
||||||
assert pi_info('a21041').physical_pin('GPIO3') == ('P1', 5)
|
assert pi_info('a21041').physical_pin('GPIO3') == ('J8', 5)
|
||||||
with pytest.raises(PinNoPins):
|
with pytest.raises(PinNoPins):
|
||||||
assert pi_info('a21041').physical_pin('GPIO47')
|
assert pi_info('a21041').physical_pin('GPIO47')
|
||||||
|
|
||||||
@@ -114,6 +114,18 @@ def test_pprint_content():
|
|||||||
pi_info('0014').headers['SODIMM'].pprint(color=False)
|
pi_info('0014').headers['SODIMM'].pprint(color=False)
|
||||||
assert len(''.join(stdout.output).splitlines()) == 100
|
assert len(''.join(stdout.output).splitlines()) == 100
|
||||||
|
|
||||||
|
def test_format_content():
|
||||||
|
with patch('sys.stdout') as stdout:
|
||||||
|
stdout.output = []
|
||||||
|
stdout.write = lambda buf: stdout.output.append(buf)
|
||||||
|
pi_info('900092').pprint(color=False)
|
||||||
|
s = ''.join(stdout.output)
|
||||||
|
assert '{0:mono}\n'.format(pi_info('900092')) == s
|
||||||
|
stdout.output = []
|
||||||
|
pi_info('900092').pprint(color=True)
|
||||||
|
s = ''.join(stdout.output)
|
||||||
|
assert '{0:color full}\n'.format(pi_info('900092')) == s
|
||||||
|
|
||||||
def test_pprint_headers():
|
def test_pprint_headers():
|
||||||
assert len(pi_info('0002').headers) == 1
|
assert len(pi_info('0002').headers) == 1
|
||||||
assert len(pi_info('000e').headers) == 2
|
assert len(pi_info('000e').headers) == 2
|
||||||
@@ -133,7 +145,8 @@ def test_pprint_headers():
|
|||||||
stdout.output = []
|
stdout.output = []
|
||||||
pi_info('900092').pprint()
|
pi_info('900092').pprint()
|
||||||
s = ''.join(stdout.output)
|
s = ''.join(stdout.output)
|
||||||
assert 'P1:\n' in s
|
assert 'J8:\n' in s
|
||||||
|
assert 'P1:\n' not in s
|
||||||
assert 'P5:\n' not in s
|
assert 'P5:\n' not in s
|
||||||
|
|
||||||
def test_pprint_color():
|
def test_pprint_color():
|
||||||
@@ -194,11 +207,12 @@ def test_pprint_missing_pin():
|
|||||||
assert ('(%d)' % i)
|
assert ('(%d)' % i)
|
||||||
|
|
||||||
def test_pprint_rows_cols():
|
def test_pprint_rows_cols():
|
||||||
assert '{0:row1}'.format(pi_info('900092').headers['P1']) == '1o'
|
assert '{0:row1}'.format(pi_info('900092').headers['J8']) == '1o'
|
||||||
assert '{0:row2}'.format(pi_info('900092').headers['P1']) == 'oo'
|
assert '{0:row2}'.format(pi_info('900092').headers['J8']) == 'oo'
|
||||||
assert '{0:col1}'.format(pi_info('0002').headers['P1']) == '1oooooooooooo'
|
assert '{0:col1}'.format(pi_info('0002').headers['P1']) == '1oooooooooooo'
|
||||||
assert '{0:col2}'.format(pi_info('0002').headers['P1']) == 'ooooooooooooo'
|
assert '{0:col2}'.format(pi_info('0002').headers['P1']) == 'ooooooooooooo'
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
'{0:row16}'.format(pi_info('0002').headers['P1'])
|
'{0:row16}'.format(pi_info('0002').headers['P1'])
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
'{0:col3}'.format(pi_info('0002').headers['P1'])
|
'{0:col3}'.format(pi_info('0002').headers['P1'])
|
||||||
|
|
||||||
|
|||||||
@@ -4,93 +4,113 @@ from __future__ import (
|
|||||||
print_function,
|
print_function,
|
||||||
division,
|
division,
|
||||||
)
|
)
|
||||||
|
nstr = str
|
||||||
str = type('')
|
str = type('')
|
||||||
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import mock
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from array import array
|
||||||
|
from mock import patch
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
|
from gpiozero.pins.native import NativeFactory
|
||||||
|
from gpiozero.pins.local import (
|
||||||
|
LocalPiHardwareSPI,
|
||||||
|
LocalPiSoftwareSPI,
|
||||||
|
LocalPiHardwareSPIShared,
|
||||||
|
LocalPiSoftwareSPIShared,
|
||||||
|
)
|
||||||
|
from gpiozero.pins.mock import MockSPIDevice
|
||||||
from gpiozero import *
|
from gpiozero import *
|
||||||
from gpiozero.pins.mock import MockPin, MockSPIDevice
|
|
||||||
from gpiozero.spi import *
|
|
||||||
|
|
||||||
|
|
||||||
def setup_function(function):
|
|
||||||
import gpiozero.devices
|
|
||||||
gpiozero.devices.pin_factory = MockPin
|
|
||||||
|
|
||||||
def teardown_function(function):
|
def teardown_function(function):
|
||||||
MockPin.clear_pins()
|
Device._pin_factory.reset()
|
||||||
|
|
||||||
|
|
||||||
def test_spi_hardware_params():
|
def test_spi_hardware_params():
|
||||||
with mock.patch('gpiozero.spi.SpiDev') as spidev:
|
with patch('os.open'), patch('mmap.mmap') as mmap_mmap, patch('io.open') as io_open:
|
||||||
with SPI() as device:
|
mmap_mmap.return_value = array(nstr('B'), (0,) * 4096)
|
||||||
assert isinstance(device, SPIHardwareInterface)
|
io_open.return_value.__enter__.return_value = ['Revision: a21042']
|
||||||
with SPI(port=0, device=0) as device:
|
with patch('gpiozero.devices.Device._pin_factory', NativeFactory()), \
|
||||||
assert isinstance(device, SPIHardwareInterface)
|
patch('gpiozero.pins.local.SpiDev'):
|
||||||
with SPI(port=0, device=1) as device:
|
with Device._pin_factory.spi() as device:
|
||||||
assert isinstance(device, SPIHardwareInterface)
|
assert isinstance(device, LocalPiHardwareSPI)
|
||||||
with SPI(clock_pin=11) as device:
|
with Device._pin_factory.spi(port=0, device=0) as device:
|
||||||
assert isinstance(device, SPIHardwareInterface)
|
assert isinstance(device, LocalPiHardwareSPI)
|
||||||
with SPI(clock_pin=11, mosi_pin=10, select_pin=8) as device:
|
with Device._pin_factory.spi(port=0, device=1) as device:
|
||||||
assert isinstance(device, SPIHardwareInterface)
|
assert isinstance(device, LocalPiHardwareSPI)
|
||||||
with SPI(clock_pin=11, mosi_pin=10, select_pin=7) as device:
|
with Device._pin_factory.spi(clock_pin=11) as device:
|
||||||
assert isinstance(device, SPIHardwareInterface)
|
assert isinstance(device, LocalPiHardwareSPI)
|
||||||
with SPI(shared=True) as device:
|
with Device._pin_factory.spi(clock_pin=11, mosi_pin=10, select_pin=8) as device:
|
||||||
assert isinstance(device, SharedSPIHardwareInterface)
|
assert isinstance(device, LocalPiHardwareSPI)
|
||||||
with pytest.raises(ValueError):
|
with Device._pin_factory.spi(clock_pin=11, mosi_pin=10, select_pin=7) as device:
|
||||||
SPI(port=1)
|
assert isinstance(device, LocalPiHardwareSPI)
|
||||||
with pytest.raises(ValueError):
|
with Device._pin_factory.spi(shared=True) as device:
|
||||||
SPI(device=2)
|
assert isinstance(device, LocalPiHardwareSPIShared)
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
SPI(port=0, clock_pin=12)
|
Device._pin_factory.spi(port=1)
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
SPI(foo='bar')
|
Device._pin_factory.spi(device=2)
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
Device._pin_factory.spi(port=0, clock_pin=12)
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
Device._pin_factory.spi(foo='bar')
|
||||||
|
|
||||||
def test_spi_software_params():
|
def test_spi_software_params():
|
||||||
with mock.patch('gpiozero.spi.SpiDev') as spidev:
|
with patch('os.open'), patch('mmap.mmap') as mmap_mmap, patch('io.open') as io_open:
|
||||||
with SPI(select_pin=6) as device:
|
mmap_mmap.return_value = array(nstr('B'), (0,) * 4096)
|
||||||
assert isinstance(device, SPISoftwareInterface)
|
io_open.return_value.__enter__.return_value = ['Revision: a21042']
|
||||||
with SPI(clock_pin=11, mosi_pin=9, miso_pin=10) as device:
|
with patch('gpiozero.devices.Device._pin_factory', NativeFactory()), \
|
||||||
assert isinstance(device, SPISoftwareInterface)
|
patch('gpiozero.pins.local.SpiDev'):
|
||||||
with SPI(select_pin=6, shared=True) as device:
|
with Device._pin_factory.spi(select_pin=6) as device:
|
||||||
assert isinstance(device, SharedSPISoftwareInterface)
|
assert isinstance(device, LocalPiSoftwareSPI)
|
||||||
# Ensure software fallback works when SpiDev isn't present
|
with Device._pin_factory.spi(clock_pin=11, mosi_pin=9, miso_pin=10) as device:
|
||||||
with SPI() as device:
|
assert isinstance(device, LocalPiSoftwareSPI)
|
||||||
assert isinstance(device, SPISoftwareInterface)
|
with Device._pin_factory.spi(select_pin=6, shared=True) as device:
|
||||||
|
assert isinstance(device, LocalPiSoftwareSPIShared)
|
||||||
|
with patch('gpiozero.devices.Device._pin_factory', NativeFactory()):
|
||||||
|
# Clear out the old factory's pins cache (this is only necessary
|
||||||
|
# because we're being very naughty switching out pin factories)
|
||||||
|
Device._pin_factory.pins.clear()
|
||||||
|
# Ensure software fallback works when SpiDev isn't present
|
||||||
|
with Device._pin_factory.spi() as device:
|
||||||
|
assert isinstance(device, LocalPiSoftwareSPI)
|
||||||
|
|
||||||
def test_spi_hardware_conflict():
|
def test_spi_hardware_conflict():
|
||||||
with mock.patch('gpiozero.spi.SpiDev') as spidev:
|
with patch('gpiozero.pins.local.SpiDev') as spidev:
|
||||||
with LED(11) as led:
|
with LED(11) as led:
|
||||||
with pytest.raises(GPIOPinInUse):
|
with pytest.raises(GPIOPinInUse):
|
||||||
SPI(port=0, device=0)
|
Device._pin_factory.spi(port=0, device=0)
|
||||||
|
with patch('gpiozero.pins.local.SpiDev') as spidev:
|
||||||
|
with Device._pin_factory.spi(port=0, device=0) as spi:
|
||||||
|
with pytest.raises(GPIOPinInUse):
|
||||||
|
LED(11)
|
||||||
|
|
||||||
def test_spi_hardware_read():
|
def test_spi_hardware_read():
|
||||||
with mock.patch('gpiozero.spi.SpiDev') as spidev:
|
with patch('gpiozero.pins.local.SpiDev') as spidev:
|
||||||
spidev.return_value.xfer2.side_effect = lambda data: list(range(10))[:len(data)]
|
spidev.return_value.xfer2.side_effect = lambda data: list(range(10))[:len(data)]
|
||||||
with SPI() as device:
|
with Device._pin_factory.spi() as device:
|
||||||
assert device.read(3) == [0, 1, 2]
|
assert device.read(3) == [0, 1, 2]
|
||||||
assert device.read(6) == list(range(6))
|
assert device.read(6) == list(range(6))
|
||||||
|
|
||||||
def test_spi_hardware_write():
|
def test_spi_hardware_write():
|
||||||
with mock.patch('gpiozero.spi.SpiDev') as spidev:
|
with patch('gpiozero.pins.local.SpiDev') as spidev:
|
||||||
spidev.return_value.xfer2.side_effect = lambda data: list(range(10))[:len(data)]
|
spidev.return_value.xfer2.side_effect = lambda data: list(range(10))[:len(data)]
|
||||||
with SPI() as device:
|
with Device._pin_factory.spi() as device:
|
||||||
assert device.write([0, 1, 2]) == 3
|
assert device.write([0, 1, 2]) == 3
|
||||||
assert spidev.return_value.xfer2.called_with([0, 1, 2])
|
assert spidev.return_value.xfer2.called_with([0, 1, 2])
|
||||||
assert device.write(list(range(6))) == 6
|
assert device.write(list(range(6))) == 6
|
||||||
assert spidev.return_value.xfer2.called_with(list(range(6)))
|
assert spidev.return_value.xfer2.called_with(list(range(6)))
|
||||||
|
|
||||||
def test_spi_hardware_modes():
|
def test_spi_hardware_modes():
|
||||||
with mock.patch('gpiozero.spi.SpiDev') as spidev:
|
with patch('gpiozero.pins.local.SpiDev') as spidev:
|
||||||
spidev.return_value.mode = 0
|
spidev.return_value.mode = 0
|
||||||
spidev.return_value.lsbfirst = False
|
spidev.return_value.lsbfirst = False
|
||||||
spidev.return_value.cshigh = True
|
spidev.return_value.cshigh = True
|
||||||
spidev.return_value.bits_per_word = 8
|
spidev.return_value.bits_per_word = 8
|
||||||
with SPI() as device:
|
with Device._pin_factory.spi() as device:
|
||||||
assert device.clock_mode == 0
|
assert device.clock_mode == 0
|
||||||
assert not device.clock_polarity
|
assert not device.clock_polarity
|
||||||
assert not device.clock_phase
|
assert not device.clock_phase
|
||||||
@@ -116,7 +136,7 @@ def test_spi_software_read():
|
|||||||
super(SPISlave, self).on_start()
|
super(SPISlave, self).on_start()
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
self.tx_word(i)
|
self.tx_word(i)
|
||||||
with SPISlave(11, 10, 9, 8) as slave, SPI() as master:
|
with SPISlave(11, 10, 9, 8) as slave, Device._pin_factory.spi() as master:
|
||||||
assert master.read(3) == [0, 1, 2]
|
assert master.read(3) == [0, 1, 2]
|
||||||
assert master.read(6) == [0, 1, 2, 3, 4, 5]
|
assert master.read(6) == [0, 1, 2, 3, 4, 5]
|
||||||
slave.clock_phase = True
|
slave.clock_phase = True
|
||||||
@@ -125,7 +145,7 @@ def test_spi_software_read():
|
|||||||
assert master.read(6) == [0, 1, 2, 3, 4, 5]
|
assert master.read(6) == [0, 1, 2, 3, 4, 5]
|
||||||
|
|
||||||
def test_spi_software_write():
|
def test_spi_software_write():
|
||||||
with MockSPIDevice(11, 10, 9, 8) as test_device, SPI() as master:
|
with MockSPIDevice(11, 10, 9, 8) as test_device, Device._pin_factory.spi() as master:
|
||||||
master.write([0])
|
master.write([0])
|
||||||
assert test_device.rx_word() == 0
|
assert test_device.rx_word() == 0
|
||||||
master.write([2, 0])
|
master.write([2, 0])
|
||||||
@@ -134,7 +154,7 @@ def test_spi_software_write():
|
|||||||
assert test_device.rx_word() == 257
|
assert test_device.rx_word() == 257
|
||||||
|
|
||||||
def test_spi_software_clock_mode():
|
def test_spi_software_clock_mode():
|
||||||
with SPI() as master:
|
with Device._pin_factory.spi() as master:
|
||||||
assert master.clock_mode == 0
|
assert master.clock_mode == 0
|
||||||
assert not master.clock_polarity
|
assert not master.clock_polarity
|
||||||
assert not master.clock_phase
|
assert not master.clock_phase
|
||||||
@@ -151,7 +171,7 @@ def test_spi_software_clock_mode():
|
|||||||
master.clock_mode = 5
|
master.clock_mode = 5
|
||||||
|
|
||||||
def test_spi_software_attr():
|
def test_spi_software_attr():
|
||||||
with SPI() as master:
|
with Device._pin_factory.spi() as master:
|
||||||
assert not master.lsb_first
|
assert not master.lsb_first
|
||||||
assert not master.select_high
|
assert not master.select_high
|
||||||
assert master.bits_per_word == 8
|
assert master.bits_per_word == 8
|
||||||
|
|||||||
@@ -15,16 +15,12 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
from gpiozero.compat import isclose
|
from gpiozero.compat import isclose
|
||||||
|
|
||||||
from gpiozero import *
|
|
||||||
from gpiozero.pins.mock import MockSPIDevice, MockPin
|
from gpiozero.pins.mock import MockSPIDevice, MockPin
|
||||||
|
from gpiozero import *
|
||||||
|
|
||||||
|
|
||||||
def setup_function(function):
|
|
||||||
import gpiozero.devices
|
|
||||||
gpiozero.devices.pin_factory = MockPin
|
|
||||||
|
|
||||||
def teardown_function(function):
|
def teardown_function(function):
|
||||||
MockPin.clear_pins()
|
Device._pin_factory.reset()
|
||||||
|
|
||||||
def clamp(v, min_value, max_value):
|
def clamp(v, min_value, max_value):
|
||||||
return min(max_value, max(min_value, v))
|
return min(max_value, max(min_value, v))
|
||||||
|
|||||||
Reference in New Issue
Block a user