From eafae5c31aa2f7c847beec84eaacc924a6249170 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Fri, 14 Jul 2017 13:51:29 +0100 Subject: [PATCH 1/9] Fix #568 Update examples in pin factory docs so they work correctly. --- docs/api_pins.rst | 8 ++++---- gpiozero/pins/native.py | 5 +++-- gpiozero/pins/pigpio.py | 11 ++++++----- gpiozero/pins/rpigpio.py | 3 ++- gpiozero/pins/rpio.py | 5 +++-- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/docs/api_pins.rst b/docs/api_pins.rst index 340d8b9..e79e020 100644 --- a/docs/api_pins.rst +++ b/docs/api_pins.rst @@ -127,10 +127,10 @@ Like the ``GPIOZERO_PIN_FACTORY`` value, these can be exported from your .. warning:: The astute and mischievous reader may note that it is possible to mix - strictly local pin implementations, e.g. using ``RPiGPIOPin`` for one pin, - and ``NativePin`` for another. This is unsupported, and if it results in - your script crashing, your components failing, or your Raspberry Pi turning - into an actual raspberry pie, you have only yourself to blame. + factories, e.g. using ``RPiGPIOFactory`` for one pin, and ``NativeFactory`` + for another. This is unsupported, and if it results in your script + crashing, your components failing, or your Raspberry Pi turning into an + actual raspberry pie, you have only yourself to blame. Sensible uses of multiple pin factories are given in :doc:`remote_gpio`. diff --git a/gpiozero/pins/native.py b/gpiozero/pins/native.py index b7de08a..cdfd84d 100644 --- a/gpiozero/pins/native.py +++ b/gpiozero/pins/native.py @@ -161,10 +161,11 @@ class NativeFactory(LocalPiFactory): You can construct native pin instances manually like so:: - from gpiozero.pins.native import NativePin + from gpiozero.pins.native import NativeFactory from gpiozero import LED - led = LED(NativePin(12)) + factory = NativeFactory() + led = LED(12, pin_factory=factory) """ def __init__(self): super(NativeFactory, self).__init__() diff --git a/gpiozero/pins/pigpio.py b/gpiozero/pins/pigpio.py index af82597..b1f9f18 100644 --- a/gpiozero/pins/pigpio.py +++ b/gpiozero/pins/pigpio.py @@ -46,20 +46,21 @@ class PiGPIOFactory(PiFactory): You can construct pigpio pins manually like so:: - from gpiozero.pins.pigpio import PiGPIOPin + from gpiozero.pins.pigpio import PiGPIOFactory from gpiozero import LED - led = LED(PiGPIOPin(12)) + factory = PiGPIOFactory() + led = LED(12, pin_factory=factory) This is particularly useful for controlling pins on a remote machine. To accomplish this simply specify the host (and optionally port) when constructing the pin:: - from gpiozero.pins.pigpio import PiGPIOPin + from gpiozero.pins.pigpio import PiGPIOFactory from gpiozero import LED - from signal import pause - led = LED(PiGPIOPin(12, host='192.168.0.2')) + factory = PiGPIOFactory(host='192.168.0.2') + led = LED(12, pin_factory=factory) .. note:: diff --git a/gpiozero/pins/rpigpio.py b/gpiozero/pins/rpigpio.py index 4c43712..332c1c2 100644 --- a/gpiozero/pins/rpigpio.py +++ b/gpiozero/pins/rpigpio.py @@ -40,7 +40,8 @@ class RPiGPIOFactory(LocalPiFactory): from gpiozero.pins.rpigpio import RPiGPIOFactory from gpiozero import LED - led = LED(RPiGPIOPin(12)) + factory = RPiGPIOFactory() + led = LED(12, pin_factory=factory) .. _RPi.GPIO: https://pypi.python.org/pypi/RPi.GPIO """ diff --git a/gpiozero/pins/rpio.py b/gpiozero/pins/rpio.py index 5a51e41..553916b 100644 --- a/gpiozero/pins/rpio.py +++ b/gpiozero/pins/rpio.py @@ -40,10 +40,11 @@ class RPIOFactory(LocalPiFactory): You can construct RPIO pins manually like so:: - from gpiozero.pins.rpio import RPIOPin + from gpiozero.pins.rpio import RPIOFactory from gpiozero import LED - led = LED(RPIOPin(12)) + factory = RPIOFactory() + led = LED(12, pin_factory=factory) .. _RPIO: https://pythonhosted.org/RPIO/ """ From b2e4237a57e3928f4005cefd5562817870b3e4dc Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Fri, 14 Jul 2017 14:01:29 +0100 Subject: [PATCH 2/9] Correct remote GPIO recipes --- docs/examples/led_button_remote_1.py | 6 +++-- docs/examples/led_button_remote_2.py | 9 ++++--- docs/examples/led_remote_1.py | 5 ++-- docs/examples/led_remote_2.py | 8 +++--- docs/examples/led_remote_3.py | 5 ++-- docs/examples/led_remote_4.py | 5 ++-- docs/examples/led_remote_5.py | 9 ++++--- docs/examples/pi_zero_remote.py | 5 ++-- docs/examples/sense_hat_remote.py | 5 ++-- docs/examples/sense_hat_remote_2.py | 5 ++-- docs/examples/traffichat_remote_1.py | 2 +- docs/examples/traffichat_remote_2.py | 5 ++-- docs/remote_gpio.rst | 37 ++++++++++++++-------------- 13 files changed, 62 insertions(+), 44 deletions(-) diff --git a/docs/examples/led_button_remote_1.py b/docs/examples/led_button_remote_1.py index 7fb7363..4b75ec2 100644 --- a/docs/examples/led_button_remote_1.py +++ b/docs/examples/led_button_remote_1.py @@ -1,9 +1,11 @@ from gpiozero import LED -from gpiozero.pins.pigpio import PiGPIOPin +from gpiozero.pins.pigpio import PiGPIOFactory from signal import pause +factory = PiGPIOFactory(host='192.168.1.3') + button = Button(2) -led = LED(PiGPIOPin(17, host='192.168.1.3')) +led = LED(17, pin_factory=factory) led.source = button.values diff --git a/docs/examples/led_button_remote_2.py b/docs/examples/led_button_remote_2.py index bacc443..dfe60b8 100644 --- a/docs/examples/led_button_remote_2.py +++ b/docs/examples/led_button_remote_2.py @@ -1,11 +1,14 @@ from gpiozero import LED -from gpiozero.pins.pigpio import PiGPIOPin +from gpiozero.pins.pigpio import PiGPIOFactory from gpiozero.tools import all_values from signal import pause +factory3 = PiGPIOFactory(host='192.168.1.3') +factory4 = PiGPIOFactory(host='192.168.1.3') + led = LED(17) -button_1 = Button(PiGPIOPin(17, host='192.168.1.3')) -button_2 = Button(PiGPIOPin(17, host='192.168.1.4')) +button_1 = Button(17, pin_factory=factory3) +button_2 = Button(17, pin_factory=factory4) led.source = all_values(button_1.values, button_2.values) diff --git a/docs/examples/led_remote_1.py b/docs/examples/led_remote_1.py index 0bfd9f4..68d2135 100644 --- a/docs/examples/led_remote_1.py +++ b/docs/examples/led_remote_1.py @@ -1,8 +1,9 @@ from gpiozero import LED -from gpiozero.pins.pigpio import PiGPIOPin +from gpiozero.pins.pigpio import PiGPIOFactory from time import sleep -led = LED(PiGPIOPin(17, host='192.168.1.3')) +factory = PiGPIOFactory(host='192.168.1.3') +led = LED(17, pin_factory=factory) while True: led.on() diff --git a/docs/examples/led_remote_2.py b/docs/examples/led_remote_2.py index c3bdb02..387646c 100644 --- a/docs/examples/led_remote_2.py +++ b/docs/examples/led_remote_2.py @@ -1,9 +1,11 @@ from gpiozero import LED -from gpiozero.pins.pigpio import PiGPIOPin +from gpiozero.pins.pigpio import PiGPIOFactory from time import sleep -led_1 = LED(PiGPIOPin(17, host='192.168.1.3')) -led_2 = LED(PiGPIOPin(17, host='192.168.1.4')) +factory3 = PiGPIOFactory(host='192.168.1.3') +factory4 = PiGPIOFactory(host='192.168.1.4') +led_1 = LED(17, pin_factory=factory3) +led_2 = LED(17, pin_factory=factory4) while True: led_1.on() diff --git a/docs/examples/led_remote_3.py b/docs/examples/led_remote_3.py index 1e7133b..9e4c49c 100644 --- a/docs/examples/led_remote_3.py +++ b/docs/examples/led_remote_3.py @@ -1,9 +1,10 @@ from gpiozero import LED -from gpiozero.pins.pigpio import PiGPIOPin +from gpiozero.pins.pigpio import PiGPIOFactory from time import sleep +remote_factory = PiGPIOFactory(host='192.168.1.3') led_1 = LED(17) # local pin -led_2 = LED(PiGPIOPin(17, host='192.168.1.3')) # remote pin +led_2 = LED(17, pin_factory=remote_factory) # remote pin while True: led_1.on() diff --git a/docs/examples/led_remote_4.py b/docs/examples/led_remote_4.py index 5d88d8b..a9396f7 100644 --- a/docs/examples/led_remote_4.py +++ b/docs/examples/led_remote_4.py @@ -1,8 +1,9 @@ from gpiozero import LED -from gpiozero.pins.rpigpio import RPiGPIOPin +from gpiozero.pins.rpigpio import RPiGPIOFactory from time import sleep -led_1 = LED(RPiGPIOPin(17)) # local pin +local_factory = RPiGPIOFactory() +led_1 = LED(17, pin_factory=local_factory) # local pin led_2 = LED(17) # remote pin while True: diff --git a/docs/examples/led_remote_5.py b/docs/examples/led_remote_5.py index ab403ee..f498db4 100644 --- a/docs/examples/led_remote_5.py +++ b/docs/examples/led_remote_5.py @@ -1,10 +1,13 @@ from gpiozero import LED -from gpiozero.pins.pigpio import PiGPIOPin +from gpiozero.pins.pigpio import PiGPIOFactory from time import sleep +factory3 = PiGPIOFactory(host='192.168.1.3') +factory4 = PiGPIOFactory(host='192.168.1.4') + led_1 = LED(17) # local pin -led_2 = LED(PiGPIOPin(17, host='192.168.1.3')) # remote pin on one pi -led_3 = LED(PiGPIOPin(17, host='192.168.1.4')) # remote pin on another pi +led_2 = LED(17, pin_factory=factory3) # remote pin on one pi +led_3 = LED(17, pin_factory=factory4) # remote pin on another pi while True: led_1.on() diff --git a/docs/examples/pi_zero_remote.py b/docs/examples/pi_zero_remote.py index 553460d..086683a 100644 --- a/docs/examples/pi_zero_remote.py +++ b/docs/examples/pi_zero_remote.py @@ -1,8 +1,9 @@ from gpiozero import LED -from gpiozero.pins.pigpio import PiGPIOPin +from gpiozero.pins.pigpio import PiGPIOFactory from signal import pause -led = LED(PiGPIOPin(17, host='raspberrypi.local')) +factory = PiGPIOFactory(host='raspberrypi.local') +led = LED(17, pin_factory=factory) led.blink() diff --git a/docs/examples/sense_hat_remote.py b/docs/examples/sense_hat_remote.py index 0b93178..93a18bc 100644 --- a/docs/examples/sense_hat_remote.py +++ b/docs/examples/sense_hat_remote.py @@ -1,8 +1,9 @@ from gpiozero import MotionSensor -from gpiozero.pins.pigpio import PiGPIOPin +from gpiozero.pins.pigpio import PiGPIOFactory from sense_hat import SenseHat -pir = MotionSensor(PiGPIOPin(4, host='192.168.1.4')) # remote motion sensor +remote_factory = PiGPIOFactory(host='192.198.1.4') +pir = MotionSensor(4, pin_factory=remote_factory) # remote motion sensor sense = SenseHat() # local sense hat while True: diff --git a/docs/examples/sense_hat_remote_2.py b/docs/examples/sense_hat_remote_2.py index c13eba9..9bf2527 100644 --- a/docs/examples/sense_hat_remote_2.py +++ b/docs/examples/sense_hat_remote_2.py @@ -1,8 +1,9 @@ from gpiozero import LightSensor -from gpiozero.pins.pigpio import PiGPIOPin +from gpiozero.pins.pigpio import PiGPIOFactory from sense_hat import SenseHat -light = LightSensor(PiGPIOPin(4, host='192.168.1.4')) # remote motion sensor +remote_factory = PiGPIOFactory(host='192.168.1.4') +light = LightSensor(4, pin_factory=remote_factory) # remote motion sensor sense = SenseHat() # local sense hat blue = (0, 0, 255) diff --git a/docs/examples/traffichat_remote_1.py b/docs/examples/traffichat_remote_1.py index 9b08eb7..0bc74d6 100644 --- a/docs/examples/traffichat_remote_1.py +++ b/docs/examples/traffichat_remote_1.py @@ -3,5 +3,5 @@ from gpiozero import TrafficHat from gpiozero.pins.pigpio import PiGPIOFactory from time import sleep -gpiozero.Device._set_pin_factory(PiGPIOFactory(host='192.168.1.3')) +gpiozero.Device.pin_factory = PiGPIOFactory(host='192.168.1.3') th = TrafficHat() # traffic hat on 192.168.1.3 using remote pins diff --git a/docs/examples/traffichat_remote_2.py b/docs/examples/traffichat_remote_2.py index 5795455..b46a147 100644 --- a/docs/examples/traffichat_remote_2.py +++ b/docs/examples/traffichat_remote_2.py @@ -3,6 +3,7 @@ from gpiozero import TrafficHat from gpiozero.pins.pigpio import PiGPIOFactory from time import sleep +remote_factory = PiGPIOFactory(host='192.168.1.3') + th_1 = TrafficHat() # traffic hat using local pins -gpiozero.Device._set_pin_factory(PiGPIOFactory(host='192.168.1.3')) -th_2 = TrafficHat() # traffic hat on 192.168.1.3 using remote pins +th_2 = TrafficHat(pin_factory=remote_factory) # traffic hat on 192.168.1.3 using remote pins diff --git a/docs/remote_gpio.rst b/docs/remote_gpio.rst index 997425d..1686d3d 100644 --- a/docs/remote_gpio.rst +++ b/docs/remote_gpio.rst @@ -12,8 +12,8 @@ documentation page. One of the pin libraries supported, `pigpio`_, provides the ability to control GPIO pins remotely over the network, which means you can use GPIO Zero to -control devices connected to a Raspberry Pi on the network. You can do this from -another Raspberry Pi, or even from a PC. +control devices connected to a Raspberry Pi on the network. You can do this +from another Raspberry Pi, or even from a PC. See the :doc:`recipes_remote_gpio` page for examples on how remote pins can be used. @@ -22,8 +22,8 @@ Preparing the Raspberry Pi ========================== If you're using Raspbian Jessie (desktop - not Jessie Lite) then you have -everything you need to use the remote GPIO feature. If you're using Jessie Lite, -or another distribution, you'll need to install pigpio: +everything you need to use the remote GPIO feature. If you're using Jessie +Lite, or another distribution, you'll need to install pigpio:: .. code-block:: console @@ -182,16 +182,16 @@ If you are running this from a PC (not a Raspberry Pi) with gpiozero and the pigpio Python library installed, this will work with no further configuration. However, if you are running this from a Raspberry Pi, you will also need to ensure the default pin factory is set to ``PiGPIOPin``. If ``RPi.GPIO`` is -installed, this will be selected as the default pin factory, so either uninstall -it, or use another environment variable to set it to ``PiGPIOPin``: +installed, this will be selected as the default pin factory, so either +uninstall it, or use another environment variable to set it to ``PiGPIOPin``: .. code-block:: console $ GPIOZERO_PIN_FACTORY=pigpio PIGPIO_ADDR=192.168.1.3 python3 hello.py -This usage will set the pin factory to :class:`PiGPIOPin` with a default host of -``192.168.1.3``. The pin factory can be changed inline in the code, as seen in -the following sections. +This usage will set the pin factory to :class:`PiGPIOFactory` with a default +host of ``192.168.1.3``. The pin factory can be changed inline in the code, as +seen in the following sections. With this usage, you can write gpiozero code like you would on a Raspberry Pi, with no modifications needed. For example: @@ -218,9 +218,9 @@ Pin objects =========== An alternative (or additional) method of configuring gpiozero objects to use -remote pins is to create instances of :class:PiGPIOPin objects, and -instantiating device objects with those pin objects, rather than just numbers. -For example, with no environment variables set: +remote pins is to create instances of :class:`PiGPIOFactory` objects, and use +them when instantiating device objects. For example, with no environment +variables set: .. literalinclude:: examples/led_remote_1.py @@ -230,8 +230,8 @@ This allows devices on multiple Raspberry Pis to be used in the same script: You can, of course, continue to create gpiozero device objects as normal, and create others using remote pins. For example, if run on a Raspberry Pi, the -following script will flash an LED on the host Pi, and also on another Pi on the -network: +following script will flash an LED on the host Pi, and also on another Pi on +the network: .. literalinclude:: examples/led_remote_3.py @@ -249,7 +249,8 @@ Note that these examples use the :class:`LED` class, which takes a ``pin`` argument to initialise. Some classes, particularly those representing HATs and other add-on boards, do not require their pin numbers to be specified. However, it is still possible to use remote pins with these devices, either using -environment variables, or by using :meth:`~Device._set_pin_factory`: +environment variables, :attr:`Device.pin_factory`, or the ``pin_factory`` +keyword argument: .. literalinclude:: examples/traffichat_remote_1.py @@ -299,10 +300,10 @@ from the computer, referencing the host by its hostname, like so: .. note:: - When running code directly on a Raspberry Pi, any pin type can be used + When running code directly on a Raspberry Pi, any pin factory can be used (assuming the relevant library is installed), but when a device is used - remotely, only :class:`PiGPIOPin` can be used, as pigpio is the only pin - library which supports remote GPIO. + remotely, only :class:`PiGPIOFactory` can be used, as pigpio is the only + pin library which supports remote GPIO. .. _RPi.GPIO: https://pypi.python.org/pypi/RPi.GPIO From 49d3bbc1da01b916044e2bf67d4ee2bc7e9e0af3 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Mon, 3 Jul 2017 14:20:07 +0100 Subject: [PATCH 3/9] Consistency in factory references --- gpiozero/pins/pigpio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpiozero/pins/pigpio.py b/gpiozero/pins/pigpio.py index b1f9f18..dc46ed3 100644 --- a/gpiozero/pins/pigpio.py +++ b/gpiozero/pins/pigpio.py @@ -163,7 +163,7 @@ class PiGPIOPin(PiPin): def __init__(self, factory, number): super(PiGPIOPin, self).__init__(factory, number) - self._pull = 'up' if factory.pi_info.pulled_up(repr(self)) else 'floating' + self._pull = 'up' if self.factory.pi_info.pulled_up(repr(self)) else 'floating' self._pwm = False self._bounce = None self._callback = None From d502dbc8e8cd6d95fb43337f0c68caed095cba05 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Mon, 3 Jul 2017 16:08:17 +0100 Subject: [PATCH 4/9] Stupid mistakes... Spotted by @lurch's eagle eyes as usual :) --- docs/examples/led_button_remote_2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/led_button_remote_2.py b/docs/examples/led_button_remote_2.py index dfe60b8..364ce4e 100644 --- a/docs/examples/led_button_remote_2.py +++ b/docs/examples/led_button_remote_2.py @@ -4,7 +4,7 @@ from gpiozero.tools import all_values from signal import pause factory3 = PiGPIOFactory(host='192.168.1.3') -factory4 = PiGPIOFactory(host='192.168.1.3') +factory4 = PiGPIOFactory(host='192.168.1.4') led = LED(17) button_1 = Button(17, pin_factory=factory3) From a68610d73788128e4894308f2244ded53c858ebb Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Fri, 14 Jul 2017 14:11:32 +0100 Subject: [PATCH 5/9] Minor formatting fixes --- docs/remote_gpio.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/remote_gpio.rst b/docs/remote_gpio.rst index 1686d3d..d33ac61 100644 --- a/docs/remote_gpio.rst +++ b/docs/remote_gpio.rst @@ -23,7 +23,7 @@ Preparing the Raspberry Pi If you're using Raspbian Jessie (desktop - not Jessie Lite) then you have everything you need to use the remote GPIO feature. If you're using Jessie -Lite, or another distribution, you'll need to install pigpio:: +Lite, or another distribution, you'll need to install pigpio: .. code-block:: console @@ -52,7 +52,9 @@ example: $ sudo pigpiod -n localhost -n 192.168.1.65 # allow localhost and 192.168.1.65 only You will need to launch the pigpio daemon every time you wish to use this -feature. To automate running the daemon at boot time:: +feature. To automate running the daemon at boot time: + +.. code-block:: console $ sudo systemctl enable pigpiod From 13f5e5ea3bf50891c54519dcdfc5dba3d1a54786 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Fri, 14 Jul 2017 14:21:08 +0100 Subject: [PATCH 6/9] Fix old references to PiGPIOPin --- docs/remote_gpio.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/remote_gpio.rst b/docs/remote_gpio.rst index d33ac61..e527e77 100644 --- a/docs/remote_gpio.rst +++ b/docs/remote_gpio.rst @@ -183,9 +183,10 @@ following: If you are running this from a PC (not a Raspberry Pi) with gpiozero and the pigpio Python library installed, this will work with no further configuration. However, if you are running this from a Raspberry Pi, you will also need to -ensure the default pin factory is set to ``PiGPIOPin``. If ``RPi.GPIO`` is +ensure the default pin factory is set to ``PiGPIOFactory``. If ``RPi.GPIO`` is installed, this will be selected as the default pin factory, so either -uninstall it, or use another environment variable to set it to ``PiGPIOPin``: +uninstall it, or use another environment variable to set it to +``PiGPIOFactory``: .. code-block:: console From 4009bf39df6e7f37e209ca614608924c5dd48d70 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Fri, 14 Jul 2017 14:29:36 +0100 Subject: [PATCH 7/9] Fix #572 Tempted to add a FAQ entry about this anyway as it's come up multiple times --- gpiozero/devices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpiozero/devices.py b/gpiozero/devices.py index 9cbc9c8..15adc0f 100644 --- a/gpiozero/devices.py +++ b/gpiozero/devices.py @@ -448,7 +448,7 @@ def _default_pin_factory(name=os.getenv('GPIOZERO_PIN_FACTORY', None)): except Exception as e: warnings.warn( PinFactoryFallback( - 'Failed to load factory %s: %s' % (name, str(e)))) + 'Falling back from %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()): From a0d784082d59fbfa2e19226dfb083ced6cc20b91 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Fri, 14 Jul 2017 15:11:59 +0100 Subject: [PATCH 8/9] Fix #565 Add mock pins docs and tidy up some other bits of the pins docs --- docs/api_pins.rst | 104 +++++++++++++++++++++++-------------- docs/api_utils.rst | 20 +++++++ docs/examples/mock_demo.py | 28 ++++++++++ docs/index.rst | 1 + gpiozero/pins/__init__.py | 15 ++---- gpiozero/pins/mock.py | 22 +++++++- gpiozero/pins/native.py | 3 ++ gpiozero/pins/pigpio.py | 18 +++++++ gpiozero/pins/rpigpio.py | 6 +++ gpiozero/pins/rpio.py | 6 +++ 10 files changed, 171 insertions(+), 52 deletions(-) create mode 100644 docs/api_utils.rst create mode 100644 docs/examples/mock_demo.py diff --git a/docs/api_pins.rst b/docs/api_pins.rst index e79e020..2342e60 100644 --- a/docs/api_pins.rst +++ b/docs/api_pins.rst @@ -135,36 +135,22 @@ Like the ``GPIOZERO_PIN_FACTORY`` value, these can be exported from your Sensible uses of multiple pin factories are given in :doc:`remote_gpio`. -RPi.GPIO -======== +Mock pins +========= -.. autoclass:: gpiozero.pins.rpigpio.RPiGPIOFactory +There's also a :class:`gpiozero.pins.mock.MockFactory` which generates entirely +fake pins. This was originally intended for GPIO Zero developers who wish to +write tests for devices without having to have the physical device wired in to +their Pi. However, they have also proven relatively useful in developing GPIO +Zero scripts without having a Pi to hand. This pin factory will never be loaded +by default; it must be explicitly specified. For example: -.. autoclass:: gpiozero.pins.rpigpio.RPiGPIOPin +.. literalinclude:: examples/mock_demo.py - -RPIO -==== - -.. autoclass:: gpiozero.pins.rpio.RPIOFactory - -.. autoclass:: gpiozero.pins.rpio.RPIOPin - - -PiGPIO -====== - -.. autoclass:: gpiozero.pins.pigpio.PiGPIOFactory - -.. autoclass:: gpiozero.pins.pigpio.PiGPIOPin - - -Native -====== - -.. autoclass:: gpiozero.pins.native.NativeFactory - -.. autoclass:: gpiozero.pins.native.NativePin +Several sub-classes of mock pins exist for emulating various other things +(pins that do/don't support PWM, pins that are connected together, pins that +drive high after a delay, etc). Interested users are invited to read the GPIO +Zero test suite for further examples of usage. Base classes @@ -196,22 +182,62 @@ Base classes :members: -Utilities -========= +RPi.GPIO +======== -The pins module also contains a database of information about the various -revisions of Raspberry Pi. This is used internally to raise warnings when -non-physical pins are used, or to raise exceptions when pull-downs are -requested on pins with physical pull-up resistors attached. The following -functions and classes can be used to query this database: +.. currentmodule:: gpiozero.pins.rpigpio -.. currentmodule:: gpiozero +.. autoclass:: gpiozero.pins.rpigpio.RPiGPIOFactory -.. autofunction:: pi_info +.. autoclass:: gpiozero.pins.rpigpio.RPiGPIOPin -.. autoclass:: PiBoardInfo -.. autoclass:: HeaderInfo +RPIO +==== -.. autoclass:: PinInfo +.. currentmodule:: gpiozero.pins.rpio + +.. autoclass:: gpiozero.pins.rpio.RPIOFactory + +.. autoclass:: gpiozero.pins.rpio.RPIOPin + + +PiGPIO +====== + +.. currentmodule:: gpiozero.pins.pigpio + +.. autoclass:: gpiozero.pins.pigpio.PiGPIOFactory + +.. autoclass:: gpiozero.pins.pigpio.PiGPIOPin + + +Native +====== + +.. currentmodule:: gpiozero.pins.native + +.. autoclass:: gpiozero.pins.native.NativeFactory + +.. autoclass:: gpiozero.pins.native.NativePin + + +Mock +==== + +.. currentmodule:: gpiozero.pins.mock + +.. autoclass:: gpiozero.pins.mock.MockFactory + :members: + +.. autoclass:: gpiozero.pins.mock.MockPin + :members: + +.. autoclass:: gpiozero.pins.mock.MockPWMPin + +.. autoclass:: gpiozero.pins.mock.MockConnectedPin + +.. autoclass:: gpiozero.pins.mock.MockChargingPin + +.. autoclass:: gpiozero.pins.mock.MockTriggerPin diff --git a/docs/api_utils.rst b/docs/api_utils.rst new file mode 100644 index 0000000..6c22b1f --- /dev/null +++ b/docs/api_utils.rst @@ -0,0 +1,20 @@ +========= +Utilities +========= + +.. currentmodule:: gpiozero + +The GPIO Zero library also contains a database of information about the various +revisions of Raspberry Pi. This is used internally to raise warnings when +non-physical pins are used, or to raise exceptions when pull-downs are +requested on pins with physical pull-up resistors attached. The following +functions and classes can be used to query this database: + +.. autofunction:: pi_info + +.. autoclass:: PiBoardInfo + +.. autoclass:: HeaderInfo + +.. autoclass:: PinInfo + diff --git a/docs/examples/mock_demo.py b/docs/examples/mock_demo.py new file mode 100644 index 0000000..d0878e2 --- /dev/null +++ b/docs/examples/mock_demo.py @@ -0,0 +1,28 @@ +from gpiozero.pins.mock import MockFactory +from gpiozero import Device, Button, LED +from time import sleep + +# Set the default pin factory to a mock factory +Device.pin_factory = MockFactory() + +# Construct a couple of devices attached to mock pins 16 and 17, and link the +# devices +led = LED(17) +btn = Button(16) +led.source = btn.values + +# Here the button isn't "pushed" so the LED's value should be False +print(led.value) + +# Get a reference to mock pin 16 (used by the button) +btn_pin = Device.pin_factory.pin(16) + +# Drive the pin low (this is what would happen eletrically when the button is +# pushed) +btn_pin.drive_low() +sleep(0.1) # give source some time to re-read the button state +print(led.value) + +btn_pin.drive_high() +sleep(0.1) +print(led.value) diff --git a/docs/index.rst b/docs/index.rst index 1ded9f9..ad2fa6e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -17,6 +17,7 @@ Table of Contents api_generic api_tools api_pins + api_utils api_exc cli_tools source_values diff --git a/gpiozero/pins/__init__.py b/gpiozero/pins/__init__.py index 83ebc0c..70febc1 100644 --- a/gpiozero/pins/__init__.py +++ b/gpiozero/pins/__init__.py @@ -30,15 +30,9 @@ from ..exc import ( class Factory(object): """ - Generates pins, SPI, and I2C interfaces for devices. This is an abstract - base class for pin factories. Descendents *must* override the following - methods: - - * :meth:`_get_address` - * :meth:`pin_address` - - Descendents *may* additionally override the following methods, if - applicable: + Generates pins and SPI interfaces for devices. This is an abstract + base class for pin factories. Descendents *may* override the following + methods, if applicable: * :meth:`close` * :meth:`reserve_pins` @@ -129,9 +123,6 @@ class Factory(object): """ raise PinSPIUnsupported('SPI not supported by this pin factory') - def _get_address(self): - raise NotImplementedError - def _get_pi_info(self): return None diff --git a/gpiozero/pins/mock.py b/gpiozero/pins/mock.py index 742e01a..bc1b7a1 100644 --- a/gpiozero/pins/mock.py +++ b/gpiozero/pins/mock.py @@ -216,7 +216,7 @@ class MockChargingPin(MockPin): class MockTriggerPin(MockPin): """ This derivative of :class:`MockPin` is intended to be used with another - :class:`MockPin` to emulate a distance sensor. Set :attr:`echo_pin` to the + :class:`MockPin` to emulate a distance sensor. Set *echo_pin* to the corresponding pin instance. When this pin is driven high it will trigger the echo pin to drive high for the echo time. """ @@ -410,6 +410,14 @@ class MockSPIDevice(object): class MockFactory(LocalPiFactory): + """ + Factory for generating mock pins. The *revision* parameter specifies what + revision of Pi the mock factory pretends to be (this affects the result of + the :attr:`pi_info` attribute as well as where pull-ups are assumed to be). + The *pin_class* attribute specifies which mock pin class will be generated + by the :meth:`pin` method by default. This can be changed after + construction by modifying the :attr:`pin_class` attribute. + """ def __init__( self, revision=os.getenv('GPIOZERO_MOCK_REVISION', 'a02082'), pin_class=os.getenv('GPIOZERO_MOCK_PIN_CLASS', MockPin)): @@ -427,10 +435,22 @@ class MockFactory(LocalPiFactory): return self._revision def reset(self): + """ + Clears the pins and reservations sets. This is primarily useful in + test suites to ensure the pin factory is back in a "clean" state before + the next set of tests are run. + """ self.pins.clear() self._reservations.clear() def pin(self, spec, pin_class=None, **kwargs): + """ + The pin method for :class:`MockFactory` additionally takes a *pin_class* + attribute which can be used to override the class' :attr:`pin_class` + attribute. Any additional keyword arguments will be passed along to the + pin constructor (useful with things like :class:`MockConnectedPin` which + expect to be constructed with another pin). + """ if pin_class is None: pin_class = self.pin_class n = self._to_gpio(spec) diff --git a/gpiozero/pins/native.py b/gpiozero/pins/native.py index cdfd84d..399fdfc 100644 --- a/gpiozero/pins/native.py +++ b/gpiozero/pins/native.py @@ -178,6 +178,9 @@ class NativeFactory(LocalPiFactory): class NativePin(LocalPiPin): + """ + Native pin implementation. See :class:`NativeFactory` for more information. + """ GPIO_FUNCTIONS = { 'input': 0b000, 'output': 0b001, diff --git a/gpiozero/pins/pigpio.py b/gpiozero/pins/pigpio.py index dc46ed3..bc466b9 100644 --- a/gpiozero/pins/pigpio.py +++ b/gpiozero/pins/pigpio.py @@ -133,6 +133,12 @@ class PiGPIOFactory(PiFactory): class PiGPIOPin(PiPin): + """ + Pin implementation for the `pigpio`_ library. See :class:`PiGPIOFactory` + for more information. + + .. _pigpio: http://abyz.co.uk/rpi/pigpio/ + """ _CONNECTIONS = {} # maps (host, port) to (connection, pi_info) GPIO_FUNCTIONS = { 'input': pigpio.INPUT, @@ -290,6 +296,12 @@ class PiGPIOPin(PiPin): class PiGPIOHardwareSPI(SPI, Device): + """ + Hardware SPI implementation for the `pigpio`_ library. Uses the ``spi_*`` + functions from the pigpio API. + + .. _pigpio: http://abyz.co.uk/rpi/pigpio/ + """ def __init__(self, factory, port, device): self._port = port self._device = device @@ -386,6 +398,12 @@ class PiGPIOHardwareSPI(SPI, Device): class PiGPIOSoftwareSPI(SPI, Device): + """ + Software SPI implementation for the `pigpio`_ library. Uses the ``bb_spi_*`` + functions from the pigpio API. + + .. _pigpio: http://abyz.co.uk/rpi/pigpio/ + """ def __init__(self, factory, clock_pin, mosi_pin, miso_pin, select_pin): self._closed = True self._select_pin = select_pin diff --git a/gpiozero/pins/rpigpio.py b/gpiozero/pins/rpigpio.py index 332c1c2..6a35727 100644 --- a/gpiozero/pins/rpigpio.py +++ b/gpiozero/pins/rpigpio.py @@ -58,6 +58,12 @@ class RPiGPIOFactory(LocalPiFactory): class RPiGPIOPin(LocalPiPin): + """ + Pin implementation for the `RPi.GPIO`_ library. See :class:`RPiGPIOFactory` + for more information. + + .. _RPi.GPIO: https://pypi.python.org/pypi/RPi.GPIO + """ GPIO_FUNCTIONS = { 'input': GPIO.IN, 'output': GPIO.OUT, diff --git a/gpiozero/pins/rpio.py b/gpiozero/pins/rpio.py index 553916b..c97c56d 100644 --- a/gpiozero/pins/rpio.py +++ b/gpiozero/pins/rpio.py @@ -64,6 +64,12 @@ class RPIOFactory(LocalPiFactory): class RPIOPin(LocalPiPin): + """ + Pin implementation for the `RPIO`_ library. See :class:`RPIOFactory` for + more information. + + .. _RPIO: https://pythonhosted.org/RPIO/ + """ GPIO_FUNCTIONS = { 'input': RPIO.IN, 'output': RPIO.OUT, From b462b5f84ac2b4247296f455f70cbbb14cb9418f Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Fri, 14 Jul 2017 16:11:40 +0100 Subject: [PATCH 9/9] Remove all the remaining pin.address stuff ... again? --- gpiozero/pins/pi.py | 4 ++-- gpiozero/pins/rpigpio.py | 6 +++--- gpiozero/pins/rpio.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/gpiozero/pins/pi.py b/gpiozero/pins/pi.py index f2c02a8..071d959 100644 --- a/gpiozero/pins/pi.py +++ b/gpiozero/pins/pi.py @@ -238,11 +238,11 @@ class PiPin(Pin): self._when_changed = None self._number = number try: - factory.pi_info.physical_pin('GPIO%d' % self.number) + factory.pi_info.physical_pin(repr(self)) except PinNoPins: warnings.warn( PinNonPhysical( - 'no physical pins exist for GPIO%d' % self.number)) + 'no physical pins exist for %s' % repr(self))) @property def number(self): diff --git a/gpiozero/pins/rpigpio.py b/gpiozero/pins/rpigpio.py index 6a35727..300ac65 100644 --- a/gpiozero/pins/rpigpio.py +++ b/gpiozero/pins/rpigpio.py @@ -92,7 +92,7 @@ class RPiGPIOPin(LocalPiPin): def __init__(self, factory, number): super(RPiGPIOPin, self).__init__(factory, number) - self._pull = 'up' if self.factory.pi_info.pulled_up(self.address[-1]) else 'floating' + self._pull = 'up' if self.factory.pi_info.pulled_up(repr(self)) else 'floating' self._pwm = None self._frequency = None self._duty_cycle = None @@ -110,7 +110,7 @@ class RPiGPIOPin(LocalPiPin): GPIO.setup(self.number, GPIO.OUT, initial=state) def input_with_pull(self, pull): - if pull != 'up' and self.factory.pi_info.pulled_up(self.address[-1]): + if pull != 'up' and self.factory.pi_info.pulled_up(repr(self)): raise PinFixedPull('%r has a physical pull-up resistor' % self) try: GPIO.setup(self.number, GPIO.IN, self.GPIO_PULL_UPS[pull]) @@ -156,7 +156,7 @@ class RPiGPIOPin(LocalPiPin): def _set_pull(self, value): if self.function != 'input': raise PinFixedPull('cannot set pull on non-input pin %r' % self) - if value != 'up' and self.factory.pi_info.pulled_up(self.address[-1]): + if value != 'up' and self.factory.pi_info.pulled_up(repr(self)): raise PinFixedPull('%r has a physical pull-up resistor' % self) try: GPIO.setup(self.number, GPIO.IN, self.GPIO_PULL_UPS[value]) diff --git a/gpiozero/pins/rpio.py b/gpiozero/pins/rpio.py index c97c56d..ab9eecf 100644 --- a/gpiozero/pins/rpio.py +++ b/gpiozero/pins/rpio.py @@ -87,7 +87,7 @@ class RPIOPin(LocalPiPin): def __init__(self, factory, number): super(RPIOPin, self).__init__(factory, number) - self._pull = 'up' if self.factory.pi_info.pulled_up(self.address[-1]) else 'floating' + self._pull = 'up' if self.factory.pi_info.pulled_up(repr(self)) else 'floating' self._pwm = False self._duty_cycle = None self._bounce = None @@ -145,7 +145,7 @@ class RPIOPin(LocalPiPin): def _set_pull(self, value): if self.function != 'input': raise PinFixedPull('cannot set pull on non-input pin %r' % self) - if value != 'up' and self.factory.pi_info.pulled_up(self.address[-1]): + if value != 'up' and self.factory.pi_info.pulled_up(repr(self)): raise PinFixedPull('%r has a physical pull-up resistor' % self) try: RPIO.setup(self.number, RPIO.IN, self.GPIO_PULL_UPS[value])