Merge branch 'master' of github.com:RPi-Distro/python-gpiozero
Damn, forgot to push the debian/changelog changes after the last release.
3
.gitignore
vendored
@@ -27,3 +27,6 @@ coverage
|
||||
.coverage
|
||||
.tox
|
||||
.cache
|
||||
|
||||
# Generated documentation
|
||||
docs/_build
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
# Contributing
|
||||
|
||||
This module was designed for use in education; particularly for young children.
|
||||
It is intended to provide a simple interface to everyday components.
|
||||
|
||||
If a proposed change added an advanced feature but made basic usage more
|
||||
complex, it is unlikely to be added.
|
||||
|
||||
## Suggestions
|
||||
|
||||
Please make suggestions for additional components or enhancements to the
|
||||
codebase by opening an
|
||||
[issue](https://github.com/RPi-Distro/python-gpiozero/issues) explaining your
|
||||
reasoning clearly.
|
||||
|
||||
## Bugs
|
||||
|
||||
Please submit bug reports by opening an
|
||||
[issue](https://github.com/RPi-Distro/python-gpiozero/issues) explaining the
|
||||
problem clearly using code examples.
|
||||
|
||||
## Documentation
|
||||
|
||||
The documentation source lives in the
|
||||
[docs](https://github.com/RPi-Distro/python-gpiozero/tree/master/docs) folder.
|
||||
Contributions to the documentation are welcome but should be easy to read and
|
||||
understand.
|
||||
|
||||
## Commit messages and pull requests
|
||||
|
||||
Commit messages should be concise but descriptive, and in the form of a patch
|
||||
description, i.e. instructional not past tense ("Add LED example" not "Added
|
||||
LED example"). Commits that close (or intend to close) an issue should use the
|
||||
phrase "fix #123" where `#123` is the issue number.
|
||||
|
||||
## Backwards compatibility
|
||||
|
||||
Since this library reached v1.0 we aim to maintain backwards-compatability
|
||||
thereafter. Changes which break backwards-compatability will not be accepted.
|
||||
|
||||
## Python
|
||||
|
||||
- Python 2/3 compatibility
|
||||
- PEP8-compliance (with exceptions)
|
||||
@@ -1 +1,2 @@
|
||||
include README.rst
|
||||
recursive-include tests *.py
|
||||
|
||||
24
README.rst
@@ -2,13 +2,19 @@
|
||||
gpiozero
|
||||
========
|
||||
|
||||
.. image:: https://badge.fury.io/py/gpiozero.svg
|
||||
:target: https://badge.fury.io/py/gpiozero
|
||||
:alt: Latest Version
|
||||
.. ifconfig:: html_theme == 'sphinx_rtd_theme'
|
||||
|
||||
.. image:: https://travis-ci.org/RPi-Distro/python-gpiozero.svg?branch=master
|
||||
:target: https://travis-ci.org/RPi-Distro/python-gpiozero
|
||||
:alt: Build Tests
|
||||
.. image:: https://badge.fury.io/py/gpiozero.svg
|
||||
:target: https://badge.fury.io/py/gpiozero
|
||||
:alt: Latest Version
|
||||
|
||||
.. image:: https://travis-ci.org/RPi-Distro/python-gpiozero.svg?branch=master
|
||||
:target: https://travis-ci.org/RPi-Distro/python-gpiozero
|
||||
:alt: Build Tests
|
||||
|
||||
.. image:: https://img.shields.io/codecov/c/github/RPi-Distro/python-gpiozero/master.svg?maxAge=2592000
|
||||
:target: https://codecov.io/github/RPi-Distro/python-gpiozero
|
||||
:alt: Code Coverage
|
||||
|
||||
A simple interface to everyday GPIO components used with Raspberry Pi.
|
||||
|
||||
@@ -69,7 +75,7 @@ or::
|
||||
Documentation
|
||||
=============
|
||||
|
||||
Comprehensive documentation is available at https://gpiozero.readthedocs.org/.
|
||||
Comprehensive documentation is available at https://gpiozero.readthedocs.io/.
|
||||
|
||||
Development
|
||||
===========
|
||||
@@ -95,8 +101,8 @@ Contributors
|
||||
.. _Raspberry Pi Foundation: https://www.raspberrypi.org/
|
||||
.. _GitHub: https://github.com/RPi-Distro/python-gpiozero
|
||||
.. _issues: https://github.com/RPi-Distro/python-gpiozero/issues
|
||||
.. _recipes: http://gpiozero.readthedocs.org/en/latest/recipes.html
|
||||
.. _Contribute: CONTRIBUTING.md
|
||||
.. _recipes: http://gpiozero.readthedocs.io/en/latest/recipes.html
|
||||
.. _contribute: http://gpiozero.readthedocs.io/en/latest/contributing.html
|
||||
.. _Ben Nuttall: https://github.com/bennuttall
|
||||
.. _Dave Jones: https://github.com/waveform80
|
||||
.. _Martin O'Hanlon: https://github.com/martinohanlon
|
||||
|
||||
10
debian/control
vendored
@@ -11,8 +11,9 @@ X-Python3-Version: >= 3.2
|
||||
Package: python-gpiozero
|
||||
Architecture: all
|
||||
Section: python
|
||||
Depends: ${misc:Depends}, ${python:Depends}, python-rpi.gpio
|
||||
Suggests: python-spidev, python-gpiozero-docs
|
||||
Depends: ${misc:Depends}, ${python:Depends}
|
||||
Recommends: python-rpi.gpio, python-spidev
|
||||
Suggests: python-gpiozero-docs
|
||||
Description: Simple API for controlling devices attached to the GPIO pins.
|
||||
gpiozero builds on RPi.GPIO to provide a set of classes designed to simplify
|
||||
interaction with devices connected to the GPIO pins, from simple buttons and
|
||||
@@ -24,8 +25,9 @@ Description: Simple API for controlling devices attached to the GPIO pins.
|
||||
Package: python3-gpiozero
|
||||
Architecture: all
|
||||
Section: python
|
||||
Depends: ${misc:Depends}, ${python3:Depends}, python3-rpi.gpio
|
||||
Suggests: python3-spidev, python-gpiozero-docs
|
||||
Depends: ${misc:Depends}, ${python3:Depends}
|
||||
Recommends: python3-rpi.gpio, python3-spidev
|
||||
Suggests: python-gpiozero-docs
|
||||
Description: Simple API for controlling devices attached to the GPIO pins.
|
||||
gpiozero builds on RPi.GPIO to provide a set of classes designed to simplify
|
||||
interaction with devices connected to the GPIO pins, from simple buttons and
|
||||
|
||||
@@ -29,6 +29,13 @@ LEDBarGraph
|
||||
:inherited-members:
|
||||
:members:
|
||||
|
||||
ButtonBoard
|
||||
===========
|
||||
|
||||
.. autoclass:: ButtonBoard(\*pins, pull_up=True, bounce_time=None, hold_time=1, hold_repeat=False, \*\*named_pins)
|
||||
:inherited-members:
|
||||
:members:
|
||||
|
||||
TrafficLights
|
||||
=============
|
||||
|
||||
@@ -36,6 +43,13 @@ TrafficLights
|
||||
:inherited-members:
|
||||
:members:
|
||||
|
||||
LedBorg
|
||||
=======
|
||||
|
||||
.. autoclass:: LedBorg
|
||||
:inherited-members:
|
||||
:members:
|
||||
|
||||
PiLITEr
|
||||
=======
|
||||
|
||||
@@ -106,6 +120,13 @@ Energenie
|
||||
:inherited-members:
|
||||
:members:
|
||||
|
||||
SnowPi
|
||||
======
|
||||
|
||||
.. autoclass:: SnowPi
|
||||
:inherited-members:
|
||||
:members:
|
||||
|
||||
Base Classes
|
||||
============
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ Button
|
||||
======
|
||||
|
||||
.. autoclass:: Button(pin, pull_up=True, bounce_time=None)
|
||||
:members: wait_for_press, wait_for_release, pin, is_pressed, pull_up, when_pressed, when_released
|
||||
:members: wait_for_press, wait_for_release, pin, is_pressed, is_held, hold_time, held_time, hold_repeat, pull_up, when_pressed, when_released, when_held
|
||||
|
||||
|
||||
Line Sensor (TRCT5000)
|
||||
|
||||
@@ -25,6 +25,11 @@ PingServer
|
||||
|
||||
.. autoclass:: PingServer
|
||||
|
||||
CPUTemperature
|
||||
==============
|
||||
|
||||
.. autoclass:: CPUTemperature
|
||||
|
||||
Base Classes
|
||||
============
|
||||
|
||||
|
||||
@@ -23,13 +23,13 @@ PWMLED
|
||||
======
|
||||
|
||||
.. autoclass:: PWMLED(pin, active_high=True, initial_value=0, frequency=100)
|
||||
:members: on, off, toggle, blink, pin, is_lit, value
|
||||
:members: on, off, toggle, blink, pulse, pin, is_lit, value
|
||||
|
||||
RGBLED
|
||||
======
|
||||
|
||||
.. autoclass:: RGBLED(red, green, blue, active_high=True, initial_value=(0, 0, 0))
|
||||
:members: on, off, toggle, blink, red, green, blue, is_lit, color
|
||||
.. autoclass:: RGBLED(red, green, blue, active_high=True, initial_value=(0, 0, 0), pwm=True)
|
||||
:members: on, off, toggle, blink, pulse, red, green, blue, is_lit, color
|
||||
|
||||
Buzzer
|
||||
======
|
||||
@@ -40,9 +40,23 @@ Buzzer
|
||||
Motor
|
||||
=====
|
||||
|
||||
.. autoclass:: Motor(forward, backward)
|
||||
.. autoclass:: Motor(forward, backward, pwm=True)
|
||||
:members: forward, backward, stop
|
||||
|
||||
Servo
|
||||
=====
|
||||
|
||||
.. autoclass:: Servo(pin, initial_value=0, min_pulse_width=1/1000, max_pulse_width=2/1000, frame_width=20/1000)
|
||||
:inherited-members:
|
||||
:members:
|
||||
|
||||
AngularServo
|
||||
============
|
||||
|
||||
.. autoclass:: AngularServo(pin, initial_angle=0, min_angle=-90, max_angle=90, min_pulse_width=1/1000, max_pulse_width=2/1000, frame_width=20/1000)
|
||||
:inherited-members:
|
||||
:members:
|
||||
|
||||
Base Classes
|
||||
============
|
||||
|
||||
|
||||
@@ -25,35 +25,39 @@ integer number instead, it uses one of the following classes to provide the
|
||||
4. :class:`gpiozero.pins.native.NativePin`
|
||||
|
||||
You can change the default pin implementation by over-writing the
|
||||
``DefaultPin`` global in the ``devices`` module like so::
|
||||
``pin_factory`` global in the ``devices`` module like so::
|
||||
|
||||
from gpiozero.pins.native import NativePin
|
||||
import gpiozero.devices
|
||||
# Force the default pin implementation to be NativePin
|
||||
gpiozero.devices.DefaultPin = NativePin
|
||||
gpiozero.devices.pin_factory = NativePin
|
||||
|
||||
from gpiozero import LED
|
||||
|
||||
# This will now use NativePin instead of RPiGPIOPin
|
||||
led = LED(16)
|
||||
|
||||
Alternatively, instead of passing an integer to the device constructor, you
|
||||
can pass a :class:`Pin` object itself::
|
||||
``pin_factory`` is a concrete descendent of the abstract :class:`Pin` class.
|
||||
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
|
||||
:class:`gpiozero.pins.pigpiod.PiGPIOPin` on a remote pi called ``remote-pi``
|
||||
you can set the :envvar:`PIGPIO_ADDR` environment variable when running your
|
||||
script::
|
||||
|
||||
$ PIGPIO_ADDR=remote-pi python my_script.py
|
||||
|
||||
It is worth noting that instead of passing an integer to device constructors,
|
||||
you can pass an object derived from :class:`Pin` itself::
|
||||
|
||||
from gpiozero.pins.native import NativePin
|
||||
from gpiozero import LED
|
||||
|
||||
led = LED(NativePin(16))
|
||||
|
||||
This is particularly useful with implementations that can take extra parameters
|
||||
such as :class:`~gpiozero.pins.pigpiod.PiGPIOPin` which can address pins on
|
||||
remote machines::
|
||||
|
||||
from gpiozero.pins.pigpiod import PiGPIOPin
|
||||
from gpiozero import LED
|
||||
|
||||
led = LED(PiGPIOPin(16, host='my_other_pi'))
|
||||
|
||||
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::
|
||||
|
||||
@@ -110,6 +114,13 @@ Abstract Pin
|
||||
:members:
|
||||
|
||||
|
||||
Local Pin
|
||||
=========
|
||||
|
||||
.. autoclass:: LocalPin
|
||||
:members:
|
||||
|
||||
|
||||
Utilities
|
||||
=========
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ the :attr:`~gpiozero.SourceMixin.source` and
|
||||
library. These utility routines are in the ``tools`` module of GPIO Zero and
|
||||
are typically imported as follows::
|
||||
|
||||
from gpiozero.tools import scaled, negated, conjunction
|
||||
from gpiozero.tools import scaled, negated, all_values
|
||||
|
||||
Given that :attr:`~gpiozero.SourceMixin.source` and
|
||||
:attr:`~gpiozero.ValuesMixin.values` deal with infinite iterators, another
|
||||
@@ -29,6 +29,8 @@ Single source conversions
|
||||
|
||||
.. autofunction:: absoluted
|
||||
|
||||
.. autofunction:: booleanized
|
||||
|
||||
.. autofunction:: clamped
|
||||
|
||||
.. autofunction:: inverted
|
||||
@@ -37,12 +39,18 @@ Single source conversions
|
||||
|
||||
.. autofunction:: post_delayed
|
||||
|
||||
.. autofunction:: post_periodic_filtered
|
||||
|
||||
.. autofunction:: pre_delayed
|
||||
|
||||
.. autofunction:: pre_periodic_filtered
|
||||
|
||||
.. autofunction:: quantized
|
||||
|
||||
.. autofunction:: queued
|
||||
|
||||
.. autofunction:: smoothed
|
||||
|
||||
.. autofunction:: scaled
|
||||
|
||||
Combining sources
|
||||
@@ -54,8 +62,12 @@ Combining sources
|
||||
|
||||
.. autofunction:: averaged
|
||||
|
||||
Artifical sources
|
||||
=================
|
||||
.. autofunction:: multiplied
|
||||
|
||||
.. autofunction:: summed
|
||||
|
||||
Artificial sources
|
||||
==================
|
||||
|
||||
.. autofunction:: cos_values
|
||||
|
||||
|
||||
@@ -46,7 +46,12 @@ sys.modules['spidev'] = Mock()
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.intersphinx']
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc', # support for automethod, autoclass, etc.
|
||||
'sphinx.ext.viewcode', # support for "Source" links in output
|
||||
'sphinx.ext.intersphinx', # support links to Python library docs etc.
|
||||
'sphinx.ext.ifconfig', # support for ifconfig conditional includes
|
||||
]
|
||||
templates_path = ['_templates']
|
||||
source_suffix = '.rst'
|
||||
#source_encoding = 'utf-8-sig'
|
||||
@@ -74,6 +79,7 @@ autodoc_member_order = 'groupwise'
|
||||
|
||||
intersphinx_mapping = {
|
||||
'python': ('http://docs.python.org/3.4', None),
|
||||
'picamera': ('http://picamera.readthedocs.io/en/latest', None),
|
||||
}
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
52
docs/contributing.rst
Normal file
@@ -0,0 +1,52 @@
|
||||
.. _contributing:
|
||||
|
||||
============
|
||||
Contributing
|
||||
============
|
||||
|
||||
This module was designed for use in education; particularly for young children.
|
||||
It is intended to provide a simple interface to everyday components.
|
||||
|
||||
If a proposed change added an advanced feature but made basic usage more
|
||||
complex, it is unlikely to be added.
|
||||
|
||||
Suggestions
|
||||
===========
|
||||
|
||||
Please make suggestions for additional components or enhancements to the
|
||||
codebase by opening an `issue`_ explaining your reasoning clearly.
|
||||
|
||||
Bugs
|
||||
====
|
||||
|
||||
Please submit bug reports by opening an `issue`_ explaining the problem clearly
|
||||
using code examples.
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
||||
The documentation source lives in the `docs`_ folder. Contributions to the
|
||||
documentation are welcome but should be easy to read and understand.
|
||||
|
||||
Commit messages and pull requests
|
||||
=================================
|
||||
|
||||
Commit messages should be concise but descriptive, and in the form of a patch
|
||||
description, i.e. instructional not past tense ("Add LED example" not "Added
|
||||
LED example"). Commits that close (or intend to close) an issue should use the
|
||||
phrase "fix #123" where ``#123`` is the issue number.
|
||||
|
||||
Backwards compatibility
|
||||
=======================
|
||||
|
||||
Since this library reached v1.0 we aim to maintain backwards-compatibility
|
||||
thereafter. Changes which break backwards-compatibility will not be accepted.
|
||||
|
||||
Python
|
||||
======
|
||||
|
||||
* Python 2/3 compatibility
|
||||
* PEP8-compliance (with exceptions)
|
||||
|
||||
.. _docs: https://github.com/RPi-Distro/python-gpiozero/tree/master/docs
|
||||
.. _issue: https://github.com/RPi-Distro/python-gpiozero/issues
|
||||
9
docs/examples/all_on_1.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from gpiozero import FishDish
|
||||
from signal import pause
|
||||
|
||||
fish = FishDish()
|
||||
|
||||
fish.button.when_pressed = fish.on
|
||||
fish.button.when_released = fish.off
|
||||
|
||||
pause()
|
||||
9
docs/examples/all_on_2.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from gpiozero import TrafficHat
|
||||
from signal import pause
|
||||
|
||||
th = TrafficHat()
|
||||
|
||||
th.button.when_pressed = th.on
|
||||
th.button.when_released = th.off
|
||||
|
||||
pause()
|
||||
23
docs/examples/all_on_3.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from gpiozero import LED, Buzzer, Button
|
||||
from signal import pause
|
||||
|
||||
button = Button(2)
|
||||
buzzer = Buzzer(3)
|
||||
red = LED(4)
|
||||
amber = LED(5)
|
||||
green = LED(6)
|
||||
|
||||
things = [red, amber, green, buzzer]
|
||||
|
||||
def things_on():
|
||||
for thing in things:
|
||||
thing.on()
|
||||
|
||||
def things_off():
|
||||
for thing in things:
|
||||
thing.off()
|
||||
|
||||
button.when_pressed = things_on
|
||||
button.when_released = things_off
|
||||
|
||||
pause()
|
||||
9
docs/examples/button_1.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from gpiozero import Button
|
||||
|
||||
button = Button(2)
|
||||
|
||||
while True:
|
||||
if button.is_pressed:
|
||||
print("Button is pressed")
|
||||
else:
|
||||
print("Button is not pressed")
|
||||
6
docs/examples/button_2.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from gpiozero import Button
|
||||
|
||||
button = Button(2)
|
||||
|
||||
button.wait_for_press()
|
||||
print("Button was pressed")
|
||||
11
docs/examples/button_3.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from gpiozero import Button
|
||||
from signal import pause
|
||||
|
||||
def say_hello():
|
||||
print("Hello!")
|
||||
|
||||
button = Button(2)
|
||||
|
||||
button.when_pressed = say_hello
|
||||
|
||||
pause()
|
||||
15
docs/examples/button_4.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from gpiozero import Button
|
||||
from signal import pause
|
||||
|
||||
def say_hello():
|
||||
print("Hello!")
|
||||
|
||||
def say_goodbye():
|
||||
print("Goodbye!")
|
||||
|
||||
button = Button(2)
|
||||
|
||||
button.when_pressed = say_hello
|
||||
button.when_released = say_goodbye
|
||||
|
||||
pause()
|
||||
15
docs/examples/button_camera_1.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from gpiozero import Button
|
||||
from picamera import PiCamera
|
||||
from datetime import datetime
|
||||
from signal import pause
|
||||
|
||||
button = Button(2)
|
||||
camera = PiCamera()
|
||||
|
||||
def capture():
|
||||
datetime = datetime.now().isoformat()
|
||||
camera.capture('/home/pi/%s.jpg' % datetime)
|
||||
|
||||
button.when_pressed = capture
|
||||
|
||||
pause()
|
||||
18
docs/examples/button_camera_2.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from gpiozero import Button
|
||||
from picamera import PiCamera
|
||||
from datetime import datetime
|
||||
from signal import pause
|
||||
|
||||
left_button = Button(2)
|
||||
right_button = Button(3)
|
||||
camera = PiCamera()
|
||||
|
||||
def capture():
|
||||
datetime = datetime.now().isoformat()
|
||||
camera.capture('/home/pi/%s.jpg' % datetime)
|
||||
|
||||
left_button.when_pressed = camera.start_preview
|
||||
left_button.when_released = camera.stop_preview
|
||||
right_button.when_pressed = capture
|
||||
|
||||
pause()
|
||||
10
docs/examples/button_led_1.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from gpiozero import LED, Button
|
||||
from signal import pause
|
||||
|
||||
led = LED(17)
|
||||
button = Button(2)
|
||||
|
||||
button.when_pressed = led.on
|
||||
button.when_released = led.off
|
||||
|
||||
pause()
|
||||
9
docs/examples/button_led_2.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from gpiozero import LED, Button
|
||||
from signal import pause
|
||||
|
||||
led = LED(17)
|
||||
button = Button(2)
|
||||
|
||||
led.source = button.values
|
||||
|
||||
pause()
|
||||
11
docs/examples/button_shutdown.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from gpiozero import Button
|
||||
from subprocess import check_call
|
||||
from signal import pause
|
||||
|
||||
def shutdown():
|
||||
check_call(['sudo', 'poweroff'])
|
||||
|
||||
shutdown_btn = Button(17, hold_time=2)
|
||||
shutdown_btn.when_held = shutdown
|
||||
|
||||
pause()
|
||||
12
docs/examples/button_stop_motion.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from gpiozero import Button
|
||||
from picamera import PiCamera
|
||||
|
||||
button = Button(2)
|
||||
camera = PiCamera()
|
||||
|
||||
camera.start_preview()
|
||||
frame = 1
|
||||
while True:
|
||||
button.wait_for_press()
|
||||
camera.capture('/home/pi/frame%03d.jpg' % frame)
|
||||
frame += 1
|
||||
8
docs/examples/distance_sensor_1.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from gpiozero import DistanceSensor
|
||||
from time import sleep
|
||||
|
||||
sensor = DistanceSensor(23, 24)
|
||||
|
||||
while True:
|
||||
print('Distance to nearest object is', sensor.distance, 'm')
|
||||
sleep(1)
|
||||
10
docs/examples/distance_sensor_2.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from gpiozero import DistanceSensor, LED
|
||||
from signal import pause
|
||||
|
||||
sensor = DistanceSensor(23, 24, max_distance=1, threshold_distance=0.2)
|
||||
led = LED(16)
|
||||
|
||||
sensor.when_in_range = led.on
|
||||
sensor.when_out_of_range = led.off
|
||||
|
||||
pause()
|
||||
10
docs/examples/led_1.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from gpiozero import LED
|
||||
from time import sleep
|
||||
|
||||
red = LED(17)
|
||||
|
||||
while True:
|
||||
red.on()
|
||||
sleep(1)
|
||||
red.off()
|
||||
sleep(1)
|
||||
8
docs/examples/led_2.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from gpiozero import LED
|
||||
from signal import pause
|
||||
|
||||
red = LED(17)
|
||||
|
||||
red.blink()
|
||||
|
||||
pause()
|
||||
15
docs/examples/led_bargraph_1.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from gpiozero import LEDBarGraph
|
||||
from time import sleep
|
||||
|
||||
graph = LEDBarGraph(5, 6, 13, 19, 26, 20)
|
||||
|
||||
graph.value = 1 # (1, 1, 1, 1, 1, 1)
|
||||
sleep(1)
|
||||
graph.value = 1/2 # (1, 1, 1, 0, 0, 0)
|
||||
sleep(1)
|
||||
graph.value = -1/2 # (0, 0, 0, 1, 1, 1)
|
||||
sleep(1)
|
||||
graph.value = 1/4 # (1, 0, 0, 0, 0, 0)
|
||||
sleep(1)
|
||||
graph.value = -1 # (1, 1, 1, 1, 1, 1)
|
||||
sleep(1)
|
||||
15
docs/examples/led_bargraph_2.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from gpiozero import LEDBarGraph
|
||||
from time import sleep
|
||||
|
||||
graph = LEDBarGraph(5, 6, 13, 19, 26, pwm=True)
|
||||
|
||||
graph.value = 1/10 # (0.5, 0, 0, 0, 0)
|
||||
sleep(1)
|
||||
graph.value = 3/10 # (1, 0.5, 0, 0, 0)
|
||||
sleep(1)
|
||||
graph.value = -3/10 # (0, 0, 0, 0.5, 1)
|
||||
sleep(1)
|
||||
graph.value = 9/10 # (1, 1, 1, 1, 0.5)
|
||||
sleep(1)
|
||||
graph.value = 95/100 # (1, 1, 1, 1, 0.75)
|
||||
sleep(1)
|
||||
15
docs/examples/led_board_1.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from gpiozero import LEDBoard
|
||||
from time import sleep
|
||||
from signal import pause
|
||||
|
||||
leds = LEDBoard(5, 6, 13, 19, 26)
|
||||
|
||||
leds.on()
|
||||
sleep(1)
|
||||
leds.off()
|
||||
sleep(1)
|
||||
leds.value = (1, 0, 1, 0, 1)
|
||||
sleep(1)
|
||||
leds.blink()
|
||||
|
||||
pause()
|
||||
5
docs/examples/led_board_2.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from gpiozero import LEDBoard
|
||||
|
||||
leds = LEDBoard(5, 6, 13, 19, 26, pwm=True)
|
||||
|
||||
leds.value = (0.2, 0.4, 0.6, 0.8, 1.0)
|
||||
9
docs/examples/led_builtin.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from gpiozero import LED
|
||||
from signal import pause
|
||||
|
||||
power = LED(35) # /sys/class/leds/led1
|
||||
activity = LED(47) # /sys/class/leds/led0
|
||||
|
||||
activity.blink()
|
||||
power.blink()
|
||||
pause()
|
||||
8
docs/examples/led_pulse.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from gpiozero import PWMLED
|
||||
from signal import pause
|
||||
|
||||
led = PWMLED(17)
|
||||
|
||||
led.pulse()
|
||||
|
||||
pause()
|
||||
19
docs/examples/led_travis.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from travispy import TravisPy
|
||||
from gpiozero import LED
|
||||
from gpiozero.tools import negated
|
||||
from time import sleep
|
||||
from signal import pause
|
||||
|
||||
def build_passed(repo='RPi-Distro/python-gpiozero', delay=3600):
|
||||
t = TravisPy()
|
||||
r = t.repo(repo)
|
||||
while True:
|
||||
yield r.last_build_state == 'passed'
|
||||
sleep(delay) # Sleep an hour before hitting travis again
|
||||
|
||||
red = LED(12)
|
||||
green = LED(16)
|
||||
|
||||
red.source = negated(green.values)
|
||||
green.source = build_passed()
|
||||
pause()
|
||||
12
docs/examples/led_variable_brightness.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from gpiozero import PWMLED
|
||||
from time import sleep
|
||||
|
||||
led = PWMLED(17)
|
||||
|
||||
while True:
|
||||
led.value = 0 # off
|
||||
sleep(1)
|
||||
led.value = 0.5 # half brightness
|
||||
sleep(1)
|
||||
led.value = 1 # full brightness
|
||||
sleep(1)
|
||||
9
docs/examples/light_sensor_1.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from gpiozero import LightSensor
|
||||
|
||||
sensor = LightSensor(18)
|
||||
|
||||
while True:
|
||||
sensor.wait_for_light()
|
||||
print("It's light! :)")
|
||||
sensor.wait_for_dark()
|
||||
print("It's dark :(")
|
||||
10
docs/examples/light_sensor_2.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from gpiozero import LightSensor, LED
|
||||
from signal import pause
|
||||
|
||||
sensor = LightSensor(18)
|
||||
led = LED(16)
|
||||
|
||||
sensor.when_dark = led.on
|
||||
sensor.when_light = led.off
|
||||
|
||||
pause()
|
||||
9
docs/examples/light_sensor_3.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from gpiozero import LightSensor, PWMLED
|
||||
from signal import pause
|
||||
|
||||
sensor = LightSensor(18)
|
||||
led = PWMLED(16)
|
||||
|
||||
led.source = sensor.values
|
||||
|
||||
pause()
|
||||
10
docs/examples/motion_sensor.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from gpiozero import MotionSensor, LED
|
||||
from signal import pause
|
||||
|
||||
pir = MotionSensor(4)
|
||||
led = LED(16)
|
||||
|
||||
pir.when_motion = led.on
|
||||
pir.when_no_motion = led.off
|
||||
|
||||
pause()
|
||||
10
docs/examples/motor.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from gpiozero import Motor
|
||||
from time import sleep
|
||||
|
||||
motor = Motor(forward=4, backward=14)
|
||||
|
||||
while True:
|
||||
motor.forward()
|
||||
sleep(5)
|
||||
motor.backward()
|
||||
sleep(5)
|
||||
18
docs/examples/music_box.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from gpiozero import Button
|
||||
import pygame.mixer
|
||||
from pygame.mixer import Sound
|
||||
from signal import pause
|
||||
|
||||
pygame.mixer.init()
|
||||
|
||||
sound_pins = {
|
||||
2: Sound("samples/drum_tom_mid_hard.wav"),
|
||||
3: Sound("samples/drum_cymbal_open.wav"),
|
||||
}
|
||||
|
||||
buttons = [Button(pin) for pin in sound_pins]
|
||||
for button in buttons:
|
||||
sound = sound_pins[button.pin.number]
|
||||
button.when_pressed = sound.play
|
||||
|
||||
pause()
|
||||
6
docs/examples/pot_1.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from gpiozero import MCP3008
|
||||
|
||||
pot = MCP3008(channel=0)
|
||||
|
||||
while True:
|
||||
print(pot.value)
|
||||
7
docs/examples/pot_2.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from gpiozero import LEDBarGraph, MCP3008
|
||||
from signal import pause
|
||||
|
||||
graph = LEDBarGraph(5, 6, 13, 19, 26, pwm=True)
|
||||
pot = MCP3008(channel=0)
|
||||
graph.source = pot.values
|
||||
pause()
|
||||
22
docs/examples/reaction_game.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from gpiozero import Button, LED
|
||||
from time import sleep
|
||||
import random
|
||||
|
||||
led = LED(17)
|
||||
|
||||
player_1 = Button(2)
|
||||
player_2 = Button(3)
|
||||
|
||||
time = random.uniform(5, 10)
|
||||
sleep(time)
|
||||
led.on()
|
||||
|
||||
while True:
|
||||
if player_1.is_pressed:
|
||||
print("Player 1 wins!")
|
||||
break
|
||||
if player_2.is_pressed:
|
||||
print("Player 2 wins!")
|
||||
break
|
||||
|
||||
led.off()
|
||||
28
docs/examples/rgbled.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from gpiozero import RGBLED
|
||||
from time import sleep
|
||||
|
||||
led = RGBLED(red=9, green=10, blue=11)
|
||||
|
||||
led.red = 1 # full red
|
||||
sleep(1)
|
||||
led.red = 0.5 # half red
|
||||
sleep(1)
|
||||
|
||||
led.color = (0, 1, 0) # full green
|
||||
sleep(1)
|
||||
led.color = (1, 0, 1) # magenta
|
||||
sleep(1)
|
||||
led.color = (1, 1, 0) # yellow
|
||||
sleep(1)
|
||||
led.color = (0, 1, 1) # cyan
|
||||
sleep(1)
|
||||
led.color = (1, 1, 1) # white
|
||||
sleep(1)
|
||||
|
||||
led.color = (0, 0, 0) # off
|
||||
sleep(1)
|
||||
|
||||
# slowly increase intensity of blue
|
||||
for n in range(100):
|
||||
led.blue = n/100
|
||||
sleep(0.1)
|
||||
11
docs/examples/rgbled_pot_1.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from gpiozero import RGBLED, MCP3008
|
||||
|
||||
led = RGBLED(red=2, green=3, blue=4)
|
||||
red_pot = MCP3008(channel=0)
|
||||
green_pot = MCP3008(channel=1)
|
||||
blue_pot = MCP3008(channel=2)
|
||||
|
||||
while True:
|
||||
led.red = red_pot.value
|
||||
led.green = green_pot.value
|
||||
led.blue = blue_pot.value
|
||||
11
docs/examples/rgbled_pot_2.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from gpiozero import RGBLED, MCP3008
|
||||
from signal import pause
|
||||
|
||||
led = RGBLED(2, 3, 4)
|
||||
red_pot = MCP3008(0)
|
||||
green_pot = MCP3008(1)
|
||||
blue_pot = MCP3008(2)
|
||||
|
||||
led.source = zip(red_pot.values, green_pot.values, blue_pot.values)
|
||||
|
||||
pause()
|
||||
10
docs/examples/robot_1.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from gpiozero import Robot
|
||||
from time import sleep
|
||||
|
||||
robot = Robot(left=(4, 14), right=(17, 18))
|
||||
|
||||
for i in range(4):
|
||||
robot.forward()
|
||||
sleep(10)
|
||||
robot.right()
|
||||
sleep(1)
|
||||
9
docs/examples/robot_2.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from gpiozero import Robot, DistanceSensor
|
||||
from signal import pause
|
||||
|
||||
sensor = DistanceSensor(23, 24, max_distance=1, threshold_distance=0.2)
|
||||
robot = Robot(left=(4, 14), right=(17, 18))
|
||||
|
||||
sensor.when_in_range = robot.backward
|
||||
sensor.when_out_of_range = robot.stop
|
||||
pause()
|
||||
23
docs/examples/robot_buttons.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from gpiozero import Robot, Button
|
||||
from signal import pause
|
||||
|
||||
robot = Robot(left=(4, 14), right=(17, 18))
|
||||
|
||||
left = Button(26)
|
||||
right = Button(16)
|
||||
fw = Button(21)
|
||||
bw = Button(20)
|
||||
|
||||
fw.when_pressed = robot.forward
|
||||
fw.when_released = robot.stop
|
||||
|
||||
left.when_pressed = robot.left
|
||||
left.when_released = robot.stop
|
||||
|
||||
right.when_pressed = robot.right
|
||||
right.when_released = robot.stop
|
||||
|
||||
bw.when_pressed = robot.backward
|
||||
bw.when_released = robot.stop
|
||||
|
||||
pause()
|
||||
34
docs/examples/robot_keyboard_1.py
Normal file
@@ -0,0 +1,34 @@
|
||||
import curses
|
||||
from gpiozero import Robot
|
||||
|
||||
robot = Robot(left=(4, 14), right=(17, 18))
|
||||
|
||||
actions = {
|
||||
curses.KEY_UP: robot.forward,
|
||||
curses.KEY_DOWN: robot.backward,
|
||||
curses.KEY_LEFT: robot.left,
|
||||
curses.KEY_RIGHT: robot.right,
|
||||
}
|
||||
|
||||
def main(window):
|
||||
next_key = None
|
||||
while True:
|
||||
curses.halfdelay(1)
|
||||
if next_key is None:
|
||||
key = window.getch()
|
||||
else:
|
||||
key = next_key
|
||||
next_key = None
|
||||
if key != -1:
|
||||
# KEY DOWN
|
||||
curses.halfdelay(3)
|
||||
action = actions.get(key)
|
||||
if action is not None:
|
||||
action()
|
||||
next_key = key
|
||||
while next_key == key:
|
||||
next_key = window.getch()
|
||||
# KEY UP
|
||||
robot.stop()
|
||||
|
||||
curses.wrapper(main)
|
||||
36
docs/examples/robot_keyboard_2.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from gpiozero import Robot
|
||||
from evdev import InputDevice, list_devices, ecodes
|
||||
|
||||
robot = Robot(left=(4, 14), right=(17, 18))
|
||||
|
||||
# Get the list of available input devices
|
||||
devices = [InputDevice(device) for device in list_devices()]
|
||||
# Filter out everything that's not a keyboard. Keyboards are defined as any
|
||||
# device which has keys, and which specifically has keys 1..31 (roughly Esc,
|
||||
# the numeric keys, the first row of QWERTY plus a few more) and which does
|
||||
# *not* have key 0 (reserved)
|
||||
must_have = {i for i in range(1, 32)}
|
||||
must_not_have = {0}
|
||||
devices = [
|
||||
dev
|
||||
for dev in devices
|
||||
for keys in (set(dev.capabilities().get(ecodes.EV_KEY, [])),)
|
||||
if must_have.issubset(keys)
|
||||
and must_not_have.isdisjoint(keys)
|
||||
]
|
||||
# Pick the first keyboard
|
||||
keyboard = devices[0]
|
||||
|
||||
keypress_actions = {
|
||||
ecodes.KEY_UP: robot.forward,
|
||||
ecodes.KEY_DOWN: robot.backward,
|
||||
ecodes.KEY_LEFT: robot.left,
|
||||
ecodes.KEY_RIGHT: robot.right,
|
||||
}
|
||||
|
||||
for event in keyboard.read_loop():
|
||||
if event.type == ecodes.EV_KEY and event.code in keypress_actions:
|
||||
if event.value == 1: # key down
|
||||
keypress_actions[event.code]()
|
||||
if event.value == 0: # key up
|
||||
robot.stop()
|
||||
10
docs/examples/robot_motion_1.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from gpiozero import Robot, MotionSensor
|
||||
from signal import pause
|
||||
|
||||
robot = Robot(left=(4, 14), right=(17, 18))
|
||||
pir = MotionSensor(5)
|
||||
|
||||
pir.when_motion = robot.forward
|
||||
pir.when_no_motion = robot.stop
|
||||
|
||||
pause()
|
||||
9
docs/examples/robot_motion_2.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from gpiozero import Robot, MotionSensor
|
||||
from signal import pause
|
||||
|
||||
robot = Robot(left=(4, 14), right=(17, 18))
|
||||
pir = MotionSensor(5)
|
||||
|
||||
robot.source = zip(pir.values, pir.values)
|
||||
|
||||
pause()
|
||||
12
docs/examples/thermometer.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from gpiozero import MCP3008
|
||||
from time import sleep
|
||||
|
||||
def convert_temp(gen):
|
||||
for value in gen:
|
||||
yield (value * 3.3 - 0.5) * 100
|
||||
|
||||
adc = MCP3008(channel=0)
|
||||
|
||||
for temp in convert_temp(adc.values):
|
||||
print('The temperature is', temp, 'C')
|
||||
sleep(1)
|
||||
20
docs/examples/traffic_lights_1.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from gpiozero import TrafficLights
|
||||
from time import sleep
|
||||
|
||||
lights = TrafficLights(2, 3, 4)
|
||||
|
||||
lights.green.on()
|
||||
|
||||
while True:
|
||||
sleep(10)
|
||||
lights.green.off()
|
||||
lights.amber.on()
|
||||
sleep(1)
|
||||
lights.amber.off()
|
||||
lights.red.on()
|
||||
sleep(10)
|
||||
lights.amber.on()
|
||||
sleep(1)
|
||||
lights.green.on()
|
||||
lights.amber.off()
|
||||
lights.red.off()
|
||||
20
docs/examples/traffic_lights_2.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from gpiozero import TrafficLights
|
||||
from time import sleep
|
||||
from signal import pause
|
||||
|
||||
lights = TrafficLights(2, 3, 4)
|
||||
|
||||
def traffic_light_sequence():
|
||||
while True:
|
||||
yield (0, 0, 1) # green
|
||||
sleep(10)
|
||||
yield (0, 1, 0) # amber
|
||||
sleep(1)
|
||||
yield (1, 0, 0) # red
|
||||
sleep(10)
|
||||
yield (1, 1, 0) # red+amber
|
||||
sleep(1)
|
||||
|
||||
lights.source = traffic_light_sequence()
|
||||
|
||||
pause()
|
||||
24
docs/examples/traffic_lights_3.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from gpiozero import LED
|
||||
from time import sleep
|
||||
|
||||
red = LED(2)
|
||||
amber = LED(3)
|
||||
green = LED(4)
|
||||
|
||||
green.on()
|
||||
amber.off()
|
||||
red.off()
|
||||
|
||||
while True:
|
||||
sleep(10)
|
||||
green.off()
|
||||
amber.on()
|
||||
sleep(1)
|
||||
amber.off()
|
||||
red.on()
|
||||
sleep(10)
|
||||
amber.on()
|
||||
sleep(1)
|
||||
green.on()
|
||||
amber.off()
|
||||
red.off()
|
||||
@@ -5,15 +5,20 @@ digraph classes {
|
||||
node [shape=rect, style=filled, color="#298029", fontname=Sans, fontcolor="#ffffff", fontsize=10];
|
||||
edge [arrowhead=onormal, style=dashed];
|
||||
|
||||
RGBLED->LED;
|
||||
RGBLED->PWMLED;
|
||||
LEDBoard->LED;
|
||||
LEDBoard->PWMLED;
|
||||
LEDBarGraph->LED;
|
||||
LEDBarGraph->PWMLED;
|
||||
|
||||
ButtonBoard->Button;
|
||||
|
||||
TrafficLightsBuzzer->TrafficLights;
|
||||
TrafficLightsBuzzer->Buzzer;
|
||||
TrafficLightsBuzzer->Button;
|
||||
|
||||
Robot->Motor;
|
||||
Motor->DigitalOutputDevice;
|
||||
Motor->PWMOutputDevice;
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 32 KiB |
@@ -1,113 +1,148 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.36.0 (20140111.2315)
|
||||
<!-- Generated by graphviz version 2.38.0 (20140413.2041)
|
||||
-->
|
||||
<!-- Title: classes Pages: 1 -->
|
||||
<svg width="538pt" height="116pt"
|
||||
viewBox="0.00 0.00 538.00 116.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 112)">
|
||||
<svg width="672pt" height="188pt"
|
||||
viewBox="0.00 0.00 672.00 188.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 184)">
|
||||
<title>classes</title>
|
||||
<polygon fill="white" stroke="none" points="-4,4 -4,-112 534,-112 534,4 -4,4"/>
|
||||
<polygon fill="white" stroke="none" points="-4,4 -4,-184 668,-184 668,4 -4,4"/>
|
||||
<!-- RGBLED -->
|
||||
<g id="node1" class="node"><title>RGBLED</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="55.5,-108 0.5,-108 0.5,-72 55.5,-72 55.5,-108"/>
|
||||
<text text-anchor="middle" x="28" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">RGBLED</text>
|
||||
</g>
|
||||
<!-- PWMLED -->
|
||||
<g id="node2" class="node"><title>PWMLED</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="143.5,-36 86.5,-36 86.5,-0 143.5,-0 143.5,-36"/>
|
||||
<text text-anchor="middle" x="115" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">PWMLED</text>
|
||||
</g>
|
||||
<!-- RGBLED->PWMLED -->
|
||||
<g id="edge1" class="edge"><title>RGBLED->PWMLED</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M49.5056,-71.6966C60.4625,-62.8807 73.9459,-52.0321 85.8249,-42.4742"/>
|
||||
<polygon fill="none" stroke="black" points="88.1446,-45.1001 93.7418,-36.1043 83.7565,-39.6462 88.1446,-45.1001"/>
|
||||
</g>
|
||||
<!-- LEDBoard -->
|
||||
<g id="node3" class="node"><title>LEDBoard</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="238,-108 174,-108 174,-72 238,-72 238,-108"/>
|
||||
<text text-anchor="middle" x="206" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">LEDBoard</text>
|
||||
</g>
|
||||
<!-- LEDBoard->PWMLED -->
|
||||
<g id="edge3" class="edge"><title>LEDBoard->PWMLED</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M183.506,-71.6966C172.045,-62.8807 157.942,-52.0321 145.516,-42.4742"/>
|
||||
<polygon fill="none" stroke="black" points="147.296,-39.4273 137.236,-36.1043 143.028,-44.9757 147.296,-39.4273"/>
|
||||
<polygon fill="#298029" stroke="#298029" points="56,-180 0,-180 0,-144 56,-144 56,-180"/>
|
||||
<text text-anchor="middle" x="28" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">RGBLED</text>
|
||||
</g>
|
||||
<!-- LED -->
|
||||
<g id="node4" class="node"><title>LED</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="224,-36 170,-36 170,-0 224,-0 224,-36"/>
|
||||
<text text-anchor="middle" x="197" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">LED</text>
|
||||
<g id="node2" class="node"><title>LED</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="96,-108 42,-108 42,-72 96,-72 96,-108"/>
|
||||
<text text-anchor="middle" x="69" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">LED</text>
|
||||
</g>
|
||||
<!-- RGBLED->LED -->
|
||||
<g id="edge1" class="edge"><title>RGBLED->LED</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M38.1348,-143.697C42.8516,-135.644 48.5618,-125.894 53.7817,-116.982"/>
|
||||
<polygon fill="none" stroke="black" points="56.9478,-118.502 58.9817,-108.104 50.9076,-114.964 56.9478,-118.502"/>
|
||||
</g>
|
||||
<!-- PWMLED -->
|
||||
<g id="node3" class="node"><title>PWMLED</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="172,-108 114,-108 114,-72 172,-72 172,-108"/>
|
||||
<text text-anchor="middle" x="143" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">PWMLED</text>
|
||||
</g>
|
||||
<!-- RGBLED->PWMLED -->
|
||||
<g id="edge2" class="edge"><title>RGBLED->PWMLED</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M56.1322,-143.876C71.1684,-134.724 89.8671,-123.342 106.06,-113.485"/>
|
||||
<polygon fill="none" stroke="black" points="108.038,-116.379 114.76,-108.19 104.398,-110.4 108.038,-116.379"/>
|
||||
</g>
|
||||
<!-- LEDBoard -->
|
||||
<g id="node4" class="node"><title>LEDBoard</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="138,-180 74,-180 74,-144 138,-144 138,-180"/>
|
||||
<text text-anchor="middle" x="106" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">LEDBoard</text>
|
||||
</g>
|
||||
<!-- LEDBoard->LED -->
|
||||
<g id="edge2" class="edge"><title>LEDBoard->LED</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M203.775,-71.6966C202.783,-63.9827 201.592,-54.7125 200.486,-46.1124"/>
|
||||
<polygon fill="none" stroke="black" points="203.946,-45.5763 199.199,-36.1043 197.003,-46.469 203.946,-45.5763"/>
|
||||
<g id="edge3" class="edge"><title>LEDBoard->LED</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M96.8539,-143.697C92.5974,-135.644 87.4442,-125.894 82.7336,-116.982"/>
|
||||
<polygon fill="none" stroke="black" points="85.8083,-115.31 78.0409,-108.104 79.6197,-118.581 85.8083,-115.31"/>
|
||||
</g>
|
||||
<!-- LEDBoard->PWMLED -->
|
||||
<g id="edge4" class="edge"><title>LEDBoard->PWMLED</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M115.146,-143.697C119.403,-135.644 124.556,-125.894 129.266,-116.982"/>
|
||||
<polygon fill="none" stroke="black" points="132.38,-118.581 133.959,-108.104 126.192,-115.31 132.38,-118.581"/>
|
||||
</g>
|
||||
<!-- LEDBarGraph -->
|
||||
<g id="node5" class="node"><title>LEDBarGraph</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="155.25,-108 74.75,-108 74.75,-72 155.25,-72 155.25,-108"/>
|
||||
<text text-anchor="middle" x="115" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">LEDBarGraph</text>
|
||||
</g>
|
||||
<!-- LEDBarGraph->PWMLED -->
|
||||
<g id="edge5" class="edge"><title>LEDBarGraph->PWMLED</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M115,-71.6966C115,-63.9827 115,-54.7125 115,-46.1124"/>
|
||||
<polygon fill="none" stroke="black" points="118.5,-46.1043 115,-36.1043 111.5,-46.1044 118.5,-46.1043"/>
|
||||
<polygon fill="#298029" stroke="#298029" points="237.5,-180 156.5,-180 156.5,-144 237.5,-144 237.5,-180"/>
|
||||
<text text-anchor="middle" x="197" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">LEDBarGraph</text>
|
||||
</g>
|
||||
<!-- LEDBarGraph->LED -->
|
||||
<g id="edge4" class="edge"><title>LEDBarGraph->LED</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M135.27,-71.6966C145.498,-62.9655 158.061,-52.2405 169.178,-42.7503"/>
|
||||
<polygon fill="none" stroke="black" points="171.63,-45.259 176.963,-36.1043 167.085,-39.935 171.63,-45.259"/>
|
||||
<g id="edge5" class="edge"><title>LEDBarGraph->LED</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M165.688,-143.876C147.37,-133.858 124.168,-121.17 105.085,-110.734"/>
|
||||
<polygon fill="none" stroke="black" points="106.54,-107.54 96.0868,-105.813 103.181,-113.682 106.54,-107.54"/>
|
||||
</g>
|
||||
<!-- TrafficLightsBuzzer -->
|
||||
<g id="node6" class="node"><title>TrafficLightsBuzzer</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="411.25,-108 306.75,-108 306.75,-72 411.25,-72 411.25,-108"/>
|
||||
<text text-anchor="middle" x="359" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">TrafficLightsBuzzer</text>
|
||||
<!-- LEDBarGraph->PWMLED -->
|
||||
<g id="edge6" class="edge"><title>LEDBarGraph->PWMLED</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M183.652,-143.697C177.243,-135.389 169.442,-125.277 162.394,-116.141"/>
|
||||
<polygon fill="none" stroke="black" points="165.074,-113.884 156.195,-108.104 159.532,-118.16 165.074,-113.884"/>
|
||||
</g>
|
||||
<!-- TrafficLights -->
|
||||
<g id="node7" class="node"><title>TrafficLights</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="314,-36 242,-36 242,-0 314,-0 314,-36"/>
|
||||
<text text-anchor="middle" x="278" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">TrafficLights</text>
|
||||
</g>
|
||||
<!-- TrafficLightsBuzzer->TrafficLights -->
|
||||
<g id="edge6" class="edge"><title>TrafficLightsBuzzer->TrafficLights</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M338.978,-71.6966C328.874,-62.9655 316.464,-52.2405 305.482,-42.7503"/>
|
||||
<polygon fill="none" stroke="black" points="307.647,-39.9948 297.792,-36.1043 303.07,-45.2912 307.647,-39.9948"/>
|
||||
</g>
|
||||
<!-- Buzzer -->
|
||||
<g id="node8" class="node"><title>Buzzer</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="386,-36 332,-36 332,-0 386,-0 386,-36"/>
|
||||
<text text-anchor="middle" x="359" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">Buzzer</text>
|
||||
</g>
|
||||
<!-- TrafficLightsBuzzer->Buzzer -->
|
||||
<g id="edge7" class="edge"><title>TrafficLightsBuzzer->Buzzer</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M359,-71.6966C359,-63.9827 359,-54.7125 359,-46.1124"/>
|
||||
<polygon fill="none" stroke="black" points="362.5,-46.1043 359,-36.1043 355.5,-46.1044 362.5,-46.1043"/>
|
||||
<!-- ButtonBoard -->
|
||||
<g id="node6" class="node"><title>ButtonBoard</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="332.5,-180 255.5,-180 255.5,-144 332.5,-144 332.5,-180"/>
|
||||
<text text-anchor="middle" x="294" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">ButtonBoard</text>
|
||||
</g>
|
||||
<!-- Button -->
|
||||
<g id="node9" class="node"><title>Button</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="458,-36 404,-36 404,-0 458,-0 458,-36"/>
|
||||
<text text-anchor="middle" x="431" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">Button</text>
|
||||
<g id="node7" class="node"><title>Button</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="328,-108 274,-108 274,-72 328,-72 328,-108"/>
|
||||
<text text-anchor="middle" x="301" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">Button</text>
|
||||
</g>
|
||||
<!-- ButtonBoard->Button -->
|
||||
<g id="edge7" class="edge"><title>ButtonBoard->Button</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M295.73,-143.697C296.502,-135.983 297.429,-126.712 298.289,-118.112"/>
|
||||
<polygon fill="none" stroke="black" points="301.777,-118.403 299.29,-108.104 294.812,-117.706 301.777,-118.403"/>
|
||||
</g>
|
||||
<!-- TrafficLightsBuzzer -->
|
||||
<g id="node8" class="node"><title>TrafficLightsBuzzer</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="455.5,-180 350.5,-180 350.5,-144 455.5,-144 455.5,-180"/>
|
||||
<text text-anchor="middle" x="403" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">TrafficLightsBuzzer</text>
|
||||
</g>
|
||||
<!-- TrafficLightsBuzzer->Button -->
|
||||
<g id="edge8" class="edge"><title>TrafficLightsBuzzer->Button</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M376.798,-71.6966C385.604,-63.135 396.382,-52.6562 406.001,-43.3045"/>
|
||||
<polygon fill="none" stroke="black" points="408.677,-45.5846 413.407,-36.1043 403.797,-40.5657 408.677,-45.5846"/>
|
||||
<g id="edge10" class="edge"><title>TrafficLightsBuzzer->Button</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M378.048,-143.876C364.958,-134.893 348.74,-123.763 334.564,-114.034"/>
|
||||
<polygon fill="none" stroke="black" points="336.274,-110.962 326.048,-108.19 332.313,-116.734 336.274,-110.962"/>
|
||||
</g>
|
||||
<!-- TrafficLights -->
|
||||
<g id="node9" class="node"><title>TrafficLights</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="432,-108 360,-108 360,-72 432,-72 432,-108"/>
|
||||
<text text-anchor="middle" x="396" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">TrafficLights</text>
|
||||
</g>
|
||||
<!-- TrafficLightsBuzzer->TrafficLights -->
|
||||
<g id="edge8" class="edge"><title>TrafficLightsBuzzer->TrafficLights</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M401.27,-143.697C400.498,-135.983 399.571,-126.712 398.711,-118.112"/>
|
||||
<polygon fill="none" stroke="black" points="402.188,-117.706 397.71,-108.104 395.223,-118.403 402.188,-117.706"/>
|
||||
</g>
|
||||
<!-- Buzzer -->
|
||||
<g id="node10" class="node"><title>Buzzer</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="504,-108 450,-108 450,-72 504,-72 504,-108"/>
|
||||
<text text-anchor="middle" x="477" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">Buzzer</text>
|
||||
</g>
|
||||
<!-- TrafficLightsBuzzer->Buzzer -->
|
||||
<g id="edge9" class="edge"><title>TrafficLightsBuzzer->Buzzer</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M421.292,-143.697C430.433,-135.05 441.64,-124.449 451.6,-115.027"/>
|
||||
<polygon fill="none" stroke="black" points="454.059,-117.519 458.918,-108.104 449.248,-112.434 454.059,-117.519"/>
|
||||
</g>
|
||||
<!-- Robot -->
|
||||
<g id="node10" class="node"><title>Robot</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="530,-108 476,-108 476,-72 530,-72 530,-108"/>
|
||||
<text text-anchor="middle" x="503" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">Robot</text>
|
||||
<g id="node11" class="node"><title>Robot</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="576,-180 522,-180 522,-144 576,-144 576,-180"/>
|
||||
<text text-anchor="middle" x="549" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">Robot</text>
|
||||
</g>
|
||||
<!-- Motor -->
|
||||
<g id="node11" class="node"><title>Motor</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="530,-36 476,-36 476,-0 530,-0 530,-36"/>
|
||||
<text text-anchor="middle" x="503" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">Motor</text>
|
||||
<g id="node12" class="node"><title>Motor</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="576,-108 522,-108 522,-72 576,-72 576,-108"/>
|
||||
<text text-anchor="middle" x="549" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">Motor</text>
|
||||
</g>
|
||||
<!-- Robot->Motor -->
|
||||
<g id="edge9" class="edge"><title>Robot->Motor</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M503,-71.6966C503,-63.9827 503,-54.7125 503,-46.1124"/>
|
||||
<polygon fill="none" stroke="black" points="506.5,-46.1043 503,-36.1043 499.5,-46.1044 506.5,-46.1043"/>
|
||||
<g id="edge11" class="edge"><title>Robot->Motor</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M549,-143.697C549,-135.983 549,-126.712 549,-118.112"/>
|
||||
<polygon fill="none" stroke="black" points="552.5,-118.104 549,-108.104 545.5,-118.104 552.5,-118.104"/>
|
||||
</g>
|
||||
<!-- DigitalOutputDevice -->
|
||||
<g id="node13" class="node"><title>DigitalOutputDevice</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="542,-36 430,-36 430,-0 542,-0 542,-36"/>
|
||||
<text text-anchor="middle" x="486" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">DigitalOutputDevice</text>
|
||||
</g>
|
||||
<!-- Motor->DigitalOutputDevice -->
|
||||
<g id="edge12" class="edge"><title>Motor->DigitalOutputDevice</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M533.427,-71.6966C525.798,-63.2198 516.477,-52.8636 508.124,-43.5826"/>
|
||||
<polygon fill="none" stroke="black" points="510.685,-41.1959 501.394,-36.1043 505.482,-45.8787 510.685,-41.1959"/>
|
||||
</g>
|
||||
<!-- PWMOutputDevice -->
|
||||
<g id="node14" class="node"><title>PWMOutputDevice</title>
|
||||
<polygon fill="#298029" stroke="#298029" points="664,-36 560,-36 560,-0 664,-0 664,-36"/>
|
||||
<text text-anchor="middle" x="612" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">PWMOutputDevice</text>
|
||||
</g>
|
||||
<!-- Motor->PWMOutputDevice -->
|
||||
<g id="edge13" class="edge"><title>Motor->PWMOutputDevice</title>
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M564.573,-71.6966C572.202,-63.2198 581.523,-52.8636 589.876,-43.5826"/>
|
||||
<polygon fill="none" stroke="black" points="592.518,-45.8787 596.606,-36.1043 587.315,-41.1959 592.518,-45.8787"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 9.0 KiB |
@@ -32,5 +32,8 @@ digraph classes {
|
||||
RyanteckRobot->Robot;
|
||||
CamJamKitRobot->Robot;
|
||||
Motor->CompositeDevice;
|
||||
Servo->CompositeDevice;
|
||||
AngularServo->Servo;
|
||||
Energenie->Device;
|
||||
ButtonBoard->CompositeDevice;
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 56 KiB |
@@ -4,25 +4,25 @@
|
||||
<!-- Generated by graphviz version 2.38.0 (20140413.2041)
|
||||
-->
|
||||
<!-- Title: classes Pages: 1 -->
|
||||
<svg width="587pt" height="476pt"
|
||||
viewBox="0.00 0.00 587.00 476.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<svg width="733pt" height="476pt"
|
||||
viewBox="0.00 0.00 732.50 476.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 472)">
|
||||
<title>classes</title>
|
||||
<polygon fill="white" stroke="none" points="-4,4 -4,-472 583,-472 583,4 -4,4"/>
|
||||
<polygon fill="white" stroke="none" points="-4,4 -4,-472 728.5,-472 728.5,4 -4,4"/>
|
||||
<!-- Device -->
|
||||
<g id="node1" class="node"><title>Device</title>
|
||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="499,-468 445,-468 445,-432 499,-432 499,-468"/>
|
||||
<text text-anchor="middle" x="472" y="-447.5" font-family="Sans" font-size="10.00" fill="#000000">Device</text>
|
||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="607,-468 553,-468 553,-432 607,-432 607,-468"/>
|
||||
<text text-anchor="middle" x="580" y="-447.5" font-family="Sans" font-size="10.00" fill="#000000">Device</text>
|
||||
</g>
|
||||
<!-- CompositeDevice -->
|
||||
<g id="node2" class="node"><title>CompositeDevice</title>
|
||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="472.5,-396 371.5,-396 371.5,-360 472.5,-360 472.5,-396"/>
|
||||
<text text-anchor="middle" x="422" y="-375.5" font-family="Sans" font-size="10.00" fill="#000000">CompositeDevice</text>
|
||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="580.5,-396 479.5,-396 479.5,-360 580.5,-360 580.5,-396"/>
|
||||
<text text-anchor="middle" x="530" y="-375.5" font-family="Sans" font-size="10.00" fill="#000000">CompositeDevice</text>
|
||||
</g>
|
||||
<!-- CompositeDevice->Device -->
|
||||
<g id="edge1" class="edge"><title>CompositeDevice->Device</title>
|
||||
<path fill="none" stroke="black" d="M434.36,-396.303C440.233,-404.526 447.369,-414.517 453.842,-423.579"/>
|
||||
<polygon fill="black" stroke="black" points="451.122,-425.793 459.783,-431.896 456.818,-421.724 451.122,-425.793"/>
|
||||
<path fill="none" stroke="black" d="M542.36,-396.303C548.233,-404.526 555.369,-414.517 561.842,-423.579"/>
|
||||
<polygon fill="black" stroke="black" points="559.122,-425.793 567.783,-431.896 564.818,-421.724 559.122,-425.793"/>
|
||||
</g>
|
||||
<!-- CompositeOutputDevice -->
|
||||
<g id="node3" class="node"><title>CompositeOutputDevice</title>
|
||||
@@ -31,8 +31,8 @@
|
||||
</g>
|
||||
<!-- CompositeOutputDevice->CompositeDevice -->
|
||||
<g id="edge2" class="edge"><title>CompositeOutputDevice->CompositeDevice</title>
|
||||
<path fill="none" stroke="black" d="M335.888,-324.124C350.793,-333.276 369.329,-344.658 385.382,-354.515"/>
|
||||
<polygon fill="black" stroke="black" points="383.652,-357.56 394.005,-359.81 387.315,-351.595 383.652,-357.56"/>
|
||||
<path fill="none" stroke="black" d="M362.023,-324.034C394.498,-334.274 435.899,-347.328 469.472,-357.915"/>
|
||||
<polygon fill="black" stroke="black" points="468.58,-361.303 479.17,-360.972 470.685,-354.627 468.58,-361.303"/>
|
||||
</g>
|
||||
<!-- LEDCollection -->
|
||||
<g id="node4" class="node"><title>LEDCollection</title>
|
||||
@@ -136,13 +136,13 @@
|
||||
</g>
|
||||
<!-- Robot -->
|
||||
<g id="node14" class="node"><title>Robot</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="449,-324 395,-324 395,-288 449,-288 449,-324"/>
|
||||
<text text-anchor="middle" x="422" y="-303.5" font-family="Sans" font-size="10.00" fill="#ffffff">Robot</text>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="485,-324 431,-324 431,-288 485,-288 485,-324"/>
|
||||
<text text-anchor="middle" x="458" y="-303.5" font-family="Sans" font-size="10.00" fill="#ffffff">Robot</text>
|
||||
</g>
|
||||
<!-- Robot->CompositeDevice -->
|
||||
<g id="edge13" class="edge"><title>Robot->CompositeDevice</title>
|
||||
<path fill="none" stroke="black" d="M422,-324.303C422,-332.017 422,-341.288 422,-349.888"/>
|
||||
<polygon fill="black" stroke="black" points="418.5,-349.896 422,-359.896 425.5,-349.896 418.5,-349.896"/>
|
||||
<path fill="none" stroke="black" d="M475.798,-324.303C484.604,-332.865 495.382,-343.344 505.001,-352.696"/>
|
||||
<polygon fill="black" stroke="black" points="502.797,-355.434 512.407,-359.896 507.677,-350.415 502.797,-355.434"/>
|
||||
</g>
|
||||
<!-- RyanteckRobot -->
|
||||
<g id="node15" class="node"><title>RyanteckRobot</title>
|
||||
@@ -151,8 +151,8 @@
|
||||
</g>
|
||||
<!-- RyanteckRobot->Robot -->
|
||||
<g id="edge14" class="edge"><title>RyanteckRobot->Robot</title>
|
||||
<path fill="none" stroke="black" d="M422,-252.303C422,-260.017 422,-269.288 422,-277.888"/>
|
||||
<polygon fill="black" stroke="black" points="418.5,-277.896 422,-287.896 425.5,-277.896 418.5,-277.896"/>
|
||||
<path fill="none" stroke="black" d="M430.899,-252.303C434.997,-260.272 439.949,-269.9 444.493,-278.736"/>
|
||||
<polygon fill="black" stroke="black" points="441.517,-280.604 449.203,-287.896 447.742,-277.402 441.517,-280.604"/>
|
||||
</g>
|
||||
<!-- CamJamKitRobot -->
|
||||
<g id="node16" class="node"><title>CamJamKitRobot</title>
|
||||
@@ -161,28 +161,58 @@
|
||||
</g>
|
||||
<!-- CamJamKitRobot->Robot -->
|
||||
<g id="edge15" class="edge"><title>CamJamKitRobot->Robot</title>
|
||||
<path fill="none" stroke="black" d="M504.336,-252.124C490.216,-261.192 472.689,-272.448 457.439,-282.241"/>
|
||||
<polygon fill="black" stroke="black" points="455.29,-279.462 448.767,-287.81 459.073,-285.352 455.29,-279.462"/>
|
||||
<path fill="none" stroke="black" d="M512.955,-252.303C503.938,-260.95 492.882,-271.551 483.057,-280.973"/>
|
||||
<polygon fill="black" stroke="black" points="480.633,-278.448 475.837,-287.896 485.478,-283.501 480.633,-278.448"/>
|
||||
</g>
|
||||
<!-- Motor -->
|
||||
<g id="node17" class="node"><title>Motor</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="521,-324 467,-324 467,-288 521,-288 521,-324"/>
|
||||
<text text-anchor="middle" x="494" y="-303.5" font-family="Sans" font-size="10.00" fill="#ffffff">Motor</text>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="557,-324 503,-324 503,-288 557,-288 557,-324"/>
|
||||
<text text-anchor="middle" x="530" y="-303.5" font-family="Sans" font-size="10.00" fill="#ffffff">Motor</text>
|
||||
</g>
|
||||
<!-- Motor->CompositeDevice -->
|
||||
<g id="edge16" class="edge"><title>Motor->CompositeDevice</title>
|
||||
<path fill="none" stroke="black" d="M476.202,-324.303C467.396,-332.865 456.618,-343.344 446.999,-352.696"/>
|
||||
<polygon fill="black" stroke="black" points="444.323,-350.415 439.593,-359.896 449.203,-355.434 444.323,-350.415"/>
|
||||
<path fill="none" stroke="black" d="M530,-324.303C530,-332.017 530,-341.288 530,-349.888"/>
|
||||
<polygon fill="black" stroke="black" points="526.5,-349.896 530,-359.896 533.5,-349.896 526.5,-349.896"/>
|
||||
</g>
|
||||
<!-- Servo -->
|
||||
<g id="node18" class="node"><title>Servo</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="629,-324 575,-324 575,-288 629,-288 629,-324"/>
|
||||
<text text-anchor="middle" x="602" y="-303.5" font-family="Sans" font-size="10.00" fill="#ffffff">Servo</text>
|
||||
</g>
|
||||
<!-- Servo->CompositeDevice -->
|
||||
<g id="edge17" class="edge"><title>Servo->CompositeDevice</title>
|
||||
<path fill="none" stroke="black" d="M584.202,-324.303C575.396,-332.865 564.618,-343.344 554.999,-352.696"/>
|
||||
<polygon fill="black" stroke="black" points="552.323,-350.415 547.593,-359.896 557.203,-355.434 552.323,-350.415"/>
|
||||
</g>
|
||||
<!-- AngularServo -->
|
||||
<g id="node19" class="node"><title>AngularServo</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="678.5,-252 597.5,-252 597.5,-216 678.5,-216 678.5,-252"/>
|
||||
<text text-anchor="middle" x="638" y="-231.5" font-family="Sans" font-size="10.00" fill="#ffffff">AngularServo</text>
|
||||
</g>
|
||||
<!-- AngularServo->Servo -->
|
||||
<g id="edge18" class="edge"><title>AngularServo->Servo</title>
|
||||
<path fill="none" stroke="black" d="M629.101,-252.303C625.003,-260.272 620.051,-269.9 615.507,-278.736"/>
|
||||
<polygon fill="black" stroke="black" points="612.258,-277.402 610.797,-287.896 618.483,-280.604 612.258,-277.402"/>
|
||||
</g>
|
||||
<!-- Energenie -->
|
||||
<g id="node18" class="node"><title>Energenie</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="555.5,-396 490.5,-396 490.5,-360 555.5,-360 555.5,-396"/>
|
||||
<text text-anchor="middle" x="523" y="-375.5" font-family="Sans" font-size="10.00" fill="#ffffff">Energenie</text>
|
||||
<g id="node20" class="node"><title>Energenie</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="663.5,-396 598.5,-396 598.5,-360 663.5,-360 663.5,-396"/>
|
||||
<text text-anchor="middle" x="631" y="-375.5" font-family="Sans" font-size="10.00" fill="#ffffff">Energenie</text>
|
||||
</g>
|
||||
<!-- Energenie->Device -->
|
||||
<g id="edge17" class="edge"><title>Energenie->Device</title>
|
||||
<path fill="none" stroke="black" d="M510.393,-396.303C504.403,-404.526 497.124,-414.517 490.521,-423.579"/>
|
||||
<polygon fill="black" stroke="black" points="487.522,-421.752 484.462,-431.896 493.179,-425.874 487.522,-421.752"/>
|
||||
<g id="edge19" class="edge"><title>Energenie->Device</title>
|
||||
<path fill="none" stroke="black" d="M618.393,-396.303C612.403,-404.526 605.124,-414.517 598.521,-423.579"/>
|
||||
<polygon fill="black" stroke="black" points="595.522,-421.752 592.462,-431.896 601.179,-425.874 595.522,-421.752"/>
|
||||
</g>
|
||||
<!-- ButtonBoard -->
|
||||
<g id="node21" class="node"><title>ButtonBoard</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="724.5,-324 647.5,-324 647.5,-288 724.5,-288 724.5,-324"/>
|
||||
<text text-anchor="middle" x="686" y="-303.5" font-family="Sans" font-size="10.00" fill="#ffffff">ButtonBoard</text>
|
||||
</g>
|
||||
<!-- ButtonBoard->CompositeDevice -->
|
||||
<g id="edge20" class="edge"><title>ButtonBoard->CompositeDevice</title>
|
||||
<path fill="none" stroke="black" d="M647.838,-324.124C626.607,-333.651 599.992,-345.593 577.431,-355.717"/>
|
||||
<polygon fill="black" stroke="black" points="575.729,-352.644 568.038,-359.932 578.594,-359.031 575.729,-352.644"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 13 KiB |
@@ -21,5 +21,6 @@ digraph classes {
|
||||
PWMOutputDevice->OutputDevice;
|
||||
PWMLED->PWMOutputDevice;
|
||||
RGBLED->Device;
|
||||
LedBorg->RGBLED;
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 22 KiB |
@@ -11,18 +11,18 @@
|
||||
<polygon fill="white" stroke="none" points="-4,4 -4,-328 255,-328 255,4 -4,4"/>
|
||||
<!-- Device -->
|
||||
<g id="node1" class="node"><title>Device</title>
|
||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="204,-324 150,-324 150,-288 204,-288 204,-324"/>
|
||||
<text text-anchor="middle" x="177" y="-303.5" font-family="Sans" font-size="10.00" fill="#000000">Device</text>
|
||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="206,-324 152,-324 152,-288 206,-288 206,-324"/>
|
||||
<text text-anchor="middle" x="179" y="-303.5" font-family="Sans" font-size="10.00" fill="#000000">Device</text>
|
||||
</g>
|
||||
<!-- GPIODevice -->
|
||||
<g id="node2" class="node"><title>GPIODevice</title>
|
||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="172,-252 100,-252 100,-216 172,-216 172,-252"/>
|
||||
<text text-anchor="middle" x="136" y="-231.5" font-family="Sans" font-size="10.00" fill="#000000">GPIODevice</text>
|
||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="173,-252 101,-252 101,-216 173,-216 173,-252"/>
|
||||
<text text-anchor="middle" x="137" y="-231.5" font-family="Sans" font-size="10.00" fill="#000000">GPIODevice</text>
|
||||
</g>
|
||||
<!-- GPIODevice->Device -->
|
||||
<g id="edge1" class="edge"><title>GPIODevice->Device</title>
|
||||
<path fill="none" stroke="black" d="M146.135,-252.303C150.852,-260.356 156.562,-270.106 161.782,-279.018"/>
|
||||
<polygon fill="black" stroke="black" points="158.908,-281.036 166.982,-287.896 164.948,-277.498 158.908,-281.036"/>
|
||||
<path fill="none" stroke="black" d="M147.382,-252.303C152.265,-260.441 158.187,-270.311 163.579,-279.299"/>
|
||||
<polygon fill="black" stroke="black" points="160.591,-281.121 168.737,-287.896 166.594,-277.52 160.591,-281.121"/>
|
||||
</g>
|
||||
<!-- OutputDevice -->
|
||||
<g id="node3" class="node"><title>OutputDevice</title>
|
||||
@@ -31,8 +31,8 @@
|
||||
</g>
|
||||
<!-- OutputDevice->GPIODevice -->
|
||||
<g id="edge2" class="edge"><title>OutputDevice->GPIODevice</title>
|
||||
<path fill="none" stroke="black" d="M136,-180.303C136,-188.017 136,-197.288 136,-205.888"/>
|
||||
<polygon fill="black" stroke="black" points="132.5,-205.896 136,-215.896 139.5,-205.896 132.5,-205.896"/>
|
||||
<path fill="none" stroke="black" d="M136.247,-180.303C136.357,-188.017 136.49,-197.288 136.613,-205.888"/>
|
||||
<polygon fill="black" stroke="black" points="133.113,-205.947 136.756,-215.896 140.112,-205.847 133.113,-205.947"/>
|
||||
</g>
|
||||
<!-- DigitalOutputDevice -->
|
||||
<g id="node4" class="node"><title>DigitalOutputDevice</title>
|
||||
@@ -86,13 +86,23 @@
|
||||
</g>
|
||||
<!-- RGBLED -->
|
||||
<g id="node9" class="node"><title>RGBLED</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="246,-252 190,-252 190,-216 246,-216 246,-252"/>
|
||||
<text text-anchor="middle" x="218" y="-231.5" font-family="Sans" font-size="10.00" fill="#ffffff">RGBLED</text>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="249,-252 193,-252 193,-216 249,-216 249,-252"/>
|
||||
<text text-anchor="middle" x="221" y="-231.5" font-family="Sans" font-size="10.00" fill="#ffffff">RGBLED</text>
|
||||
</g>
|
||||
<!-- RGBLED->Device -->
|
||||
<g id="edge8" class="edge"><title>RGBLED->Device</title>
|
||||
<path fill="none" stroke="black" d="M207.865,-252.303C203.148,-260.356 197.438,-270.106 192.218,-279.018"/>
|
||||
<polygon fill="black" stroke="black" points="189.052,-277.498 187.018,-287.896 195.092,-281.036 189.052,-277.498"/>
|
||||
<path fill="none" stroke="black" d="M210.618,-252.303C205.735,-260.441 199.813,-270.311 194.421,-279.299"/>
|
||||
<polygon fill="black" stroke="black" points="191.406,-277.52 189.263,-287.896 197.409,-281.121 191.406,-277.52"/>
|
||||
</g>
|
||||
<!-- LedBorg -->
|
||||
<g id="node10" class="node"><title>LedBorg</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="251,-180 195,-180 195,-144 251,-144 251,-180"/>
|
||||
<text text-anchor="middle" x="223" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">LedBorg</text>
|
||||
</g>
|
||||
<!-- LedBorg->RGBLED -->
|
||||
<g id="edge9" class="edge"><title>LedBorg->RGBLED</title>
|
||||
<path fill="none" stroke="black" d="M222.506,-180.303C222.285,-188.017 222.02,-197.288 221.775,-205.888"/>
|
||||
<polygon fill="black" stroke="black" points="218.276,-205.8 221.489,-215.896 225.273,-206 218.276,-205.8"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 6.3 KiB |
@@ -8,6 +8,7 @@ Table of Contents
|
||||
|
||||
recipes
|
||||
notes
|
||||
contributing
|
||||
api_input
|
||||
api_output
|
||||
api_spi
|
||||
@@ -19,3 +20,4 @@ Table of Contents
|
||||
api_exc
|
||||
changelog
|
||||
license
|
||||
|
||||
|
||||
@@ -70,3 +70,26 @@ In this case, all references to items within GPIO Zero must be prefixed::
|
||||
|
||||
button = gpiozero.Button(2)
|
||||
|
||||
How can I tell what version of gpiozero I have installed?
|
||||
=========================================================
|
||||
|
||||
The gpiozero library relies on the setuptools package for installation
|
||||
services. You can use the setuptools ``pkg_resources`` API to query which
|
||||
version of gpiozero is available in your Python environment like so::
|
||||
|
||||
>>> from pkg_resources import require
|
||||
>>> require('gpiozero')
|
||||
[gpiozero 1.2.0 (/usr/local/lib/python2.7/dist-packages)]
|
||||
>>> require('gpiozero')[0].version
|
||||
'1.2.0'
|
||||
|
||||
If you have multiple versions installed (e.g. from ``pip`` and ``apt-get``)
|
||||
they will not show up in the list returned by the ``require`` method. However,
|
||||
the first entry in the list will be the version that ``import gpiozero`` will
|
||||
import.
|
||||
|
||||
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::
|
||||
|
||||
$ sudo apt-get install python-pip
|
||||
|
||||
|
||||
690
docs/recipes.rst
@@ -9,6 +9,8 @@ library. Please note that all recipes are written assuming Python 3. Recipes
|
||||
*may* work under Python 2, but no guarantees!
|
||||
|
||||
|
||||
.. _pin_numbering:
|
||||
|
||||
Pin Numbering
|
||||
=============
|
||||
|
||||
@@ -18,39 +20,25 @@ configurable.
|
||||
|
||||
.. _RPi.GPIO: https://pypi.python.org/pypi/RPi.GPIO
|
||||
|
||||
Any pin marked ``GPIO`` in the diagram below can be used for generic
|
||||
components:
|
||||
Any pin marked "GPIO" in the diagram below can be used as a pin number. For
|
||||
example, if an LED was attached to "GPIO17" you would specify the pin number as
|
||||
17 rather than 11:
|
||||
|
||||
.. image:: images/pin_layout.*
|
||||
|
||||
|
||||
LED
|
||||
===
|
||||
|
||||
.. image:: images/led.*
|
||||
|
||||
Turn an :class:`LED` on and off repeatedly::
|
||||
Turn an :class:`LED` on and off repeatedly:
|
||||
|
||||
from gpiozero import LED
|
||||
from time import sleep
|
||||
.. literalinclude:: examples/led_1.py
|
||||
|
||||
red = LED(17)
|
||||
Alternatively:
|
||||
|
||||
while True:
|
||||
red.on()
|
||||
sleep(1)
|
||||
red.off()
|
||||
sleep(1)
|
||||
|
||||
Alternatively::
|
||||
|
||||
from gpiozero import LED
|
||||
from signal import pause
|
||||
|
||||
red = LED(17)
|
||||
|
||||
red.blink()
|
||||
|
||||
pause()
|
||||
.. literalinclude:: examples/led_2.py
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -59,45 +47,51 @@ Alternatively::
|
||||
:ref:`keep-your-script-running` for more information.
|
||||
|
||||
|
||||
LED with variable brightness
|
||||
============================
|
||||
|
||||
Any regular LED can have its brightness value set using PWM
|
||||
(pulse-width-modulation). In GPIO Zero, this can be achieved using
|
||||
:class:`PWMLED` using values between 0 and 1:
|
||||
|
||||
.. literalinclude:: examples/led_variable_brightness.py
|
||||
|
||||
Similarly to blinking on and off continuously, a PWMLED can pulse (fade in and
|
||||
out continuously):
|
||||
|
||||
.. literalinclude:: examples/led_pulse.py
|
||||
|
||||
|
||||
Button
|
||||
======
|
||||
|
||||
.. image:: images/button.*
|
||||
|
||||
Check if a :class:`Button` is pressed::
|
||||
Check if a :class:`Button` is pressed:
|
||||
|
||||
from gpiozero import Button
|
||||
.. literalinclude:: examples/button_1.py
|
||||
|
||||
button = Button(2)
|
||||
Wait for a button to be pressed before continuing:
|
||||
|
||||
while True:
|
||||
if button.is_pressed:
|
||||
print("Button is pressed")
|
||||
else:
|
||||
print("Button is not pressed")
|
||||
.. literalinclude:: examples/button_2.py
|
||||
|
||||
Wait for a button to be pressed before continuing::
|
||||
Run a function every time the button is pressed:
|
||||
|
||||
from gpiozero import Button
|
||||
.. literalinclude:: examples/button_3.py
|
||||
:emphasize-lines: 9
|
||||
|
||||
button = Button(2)
|
||||
.. note::
|
||||
|
||||
button.wait_for_press()
|
||||
print("Button was pressed")
|
||||
Note that the line ``button.when_pressed = say_hello`` does not run the
|
||||
function ``say_hello``, rather it creates a reference to the function to
|
||||
be called when the button is pressed. Accidental use of
|
||||
``button.when_pressed = say_hello()`` would set the ``when_pressed`` action
|
||||
to ``None`` (the return value of this function) which would mean nothing
|
||||
happens when the button is pressed.
|
||||
|
||||
Run a function every time the button is pressed::
|
||||
Similarly, functions can be attached to button releases:
|
||||
|
||||
from gpiozero import Button
|
||||
from signal import pause
|
||||
|
||||
def say_hello():
|
||||
print("Hello!")
|
||||
|
||||
button = Button(2)
|
||||
|
||||
button.when_pressed = say_hello
|
||||
|
||||
pause()
|
||||
.. literalinclude:: examples/button_4.py
|
||||
|
||||
|
||||
Button controlled LED
|
||||
@@ -105,30 +99,71 @@ Button controlled LED
|
||||
|
||||
.. image:: images/led_button_bb.*
|
||||
|
||||
Turn on an :class:`LED` when a :class:`Button` is pressed::
|
||||
Turn on an :class:`LED` when a :class:`Button` is pressed:
|
||||
|
||||
from gpiozero import LED, Button
|
||||
from signal import pause
|
||||
.. literalinclude:: examples/button_led_1.py
|
||||
|
||||
led = LED(17)
|
||||
button = Button(2)
|
||||
Alternatively:
|
||||
|
||||
button.when_pressed = led.on
|
||||
button.when_released = led.off
|
||||
.. literalinclude:: examples/button_led_2.py
|
||||
|
||||
pause()
|
||||
|
||||
Alternatively::
|
||||
Button controlled camera
|
||||
========================
|
||||
|
||||
from gpiozero import LED, Button
|
||||
from signal import pause
|
||||
Using the button press to trigger :class:`~picamera.PiCamera` to take a picture
|
||||
using ``button.when_pressed = camera.capture`` would not work because the
|
||||
:meth:`~picamera.PiCamera.capture` method requires an ``output`` parameter.
|
||||
However, this can be achieved using a custom function which requires no
|
||||
parameters:
|
||||
|
||||
led = LED(17)
|
||||
button = Button(2)
|
||||
.. literalinclude:: examples/button_camera_1.py
|
||||
:emphasize-lines: 9-11
|
||||
|
||||
led.source = button.values
|
||||
Another example could use one button to start and stop the camera preview, and
|
||||
another to capture:
|
||||
|
||||
pause()
|
||||
.. literalinclude:: examples/button_camera_2.py
|
||||
|
||||
|
||||
Shutdown button
|
||||
===============
|
||||
|
||||
The :class:`Button` class also provides the ability to run a function when the
|
||||
button has been held for a given length of time. This example will shut down
|
||||
the Raspberry Pi when the button is held for 2 seconds:
|
||||
|
||||
.. literalinclude:: examples/button_shutdown.py
|
||||
|
||||
|
||||
LEDBoard
|
||||
========
|
||||
|
||||
A collection of LEDs can be accessed using :class:`LEDBoard`:
|
||||
|
||||
.. literalinclude:: examples/led_board_1.py
|
||||
|
||||
Using :class:`LEDBoard` with ``pwm=True`` allows each LED's brightness to be
|
||||
controlled:
|
||||
|
||||
.. literalinclude:: examples/led_board_2.py
|
||||
|
||||
|
||||
LEDBarGraph
|
||||
===========
|
||||
|
||||
A collection of LEDs can be treated like a bar graph using
|
||||
:class:`LEDBarGraph`:
|
||||
|
||||
.. literalinclude:: examples/led_bargraph_2.py
|
||||
|
||||
Note values are essentially rounded to account for the fact LEDs can only be on
|
||||
or off when ``pwm=False`` (the default).
|
||||
|
||||
However, using :class:`LEDBarGraph` with ``pwm=True`` allows more precise
|
||||
values using LED brightness:
|
||||
|
||||
.. literalinclude:: examples/led_bargraph_2.py
|
||||
|
||||
|
||||
Traffic Lights
|
||||
@@ -138,97 +173,37 @@ Traffic Lights
|
||||
|
||||
A full traffic lights system.
|
||||
|
||||
Using a :class:`TrafficLights` kit like Pi-Stop::
|
||||
Using a :class:`TrafficLights` kit like Pi-Stop:
|
||||
|
||||
from gpiozero import TrafficLights
|
||||
from time import sleep
|
||||
.. literalinclude:: examples/traffic_lights_1.py
|
||||
|
||||
lights = TrafficLights(2, 3, 4)
|
||||
Alternatively:
|
||||
|
||||
lights.green.on()
|
||||
.. literalinclude:: examples/traffic_lights_2.py
|
||||
|
||||
while True:
|
||||
sleep(10)
|
||||
lights.green.off()
|
||||
lights.amber.on()
|
||||
sleep(1)
|
||||
lights.amber.off()
|
||||
lights.red.on()
|
||||
sleep(10)
|
||||
lights.amber.on()
|
||||
sleep(1)
|
||||
lights.green.on()
|
||||
lights.amber.off()
|
||||
lights.red.off()
|
||||
Using :class:`LED` components:
|
||||
|
||||
Alternatively::
|
||||
.. literalinclude:: examples/traffic_lights_3.py
|
||||
|
||||
from gpiozero import TrafficLights
|
||||
from time import sleep
|
||||
from signal import pause
|
||||
|
||||
lights = TrafficLights(2, 3, 4)
|
||||
Travis build LED indicator
|
||||
==========================
|
||||
|
||||
def traffic_light_sequence():
|
||||
while True:
|
||||
yield (0, 0, 1) # green
|
||||
sleep(10)
|
||||
yield (0, 1, 0) # amber
|
||||
sleep(1)
|
||||
yield (1, 0, 0) # red
|
||||
sleep(10)
|
||||
yield (1, 1, 0) # red+amber
|
||||
sleep(1)
|
||||
Use LEDs to indicate the status of a Travis build. A green light means the
|
||||
tests are passing, a red light means the build is broken:
|
||||
|
||||
lights.source = traffic_light_sequence()
|
||||
.. literalinclude:: examples/led_travis.py
|
||||
|
||||
pause()
|
||||
|
||||
Using :class:`LED` components::
|
||||
|
||||
from gpiozero import LED
|
||||
from time import sleep
|
||||
|
||||
red = LED(2)
|
||||
amber = LED(3)
|
||||
green = LED(4)
|
||||
|
||||
green.on()
|
||||
amber.off()
|
||||
red.off()
|
||||
|
||||
while True:
|
||||
sleep(10)
|
||||
green.off()
|
||||
amber.on()
|
||||
sleep(1)
|
||||
amber.off()
|
||||
red.on()
|
||||
sleep(10)
|
||||
amber.on()
|
||||
sleep(1)
|
||||
green.on()
|
||||
amber.off()
|
||||
red.off()
|
||||
Note this recipe requires `travispy`_. Install with ``sudo pip3 install
|
||||
travispy``.
|
||||
|
||||
|
||||
Push button stop motion
|
||||
=======================
|
||||
|
||||
Capture a picture with the camera module every time a button is pressed::
|
||||
Capture a picture with the camera module every time a button is pressed:
|
||||
|
||||
from gpiozero import Button
|
||||
from picamera import PiCamera
|
||||
|
||||
button = Button(2)
|
||||
|
||||
with PiCamera() as camera:
|
||||
camera.start_preview()
|
||||
frame = 1
|
||||
while True:
|
||||
button.wait_for_press()
|
||||
camera.capture('/home/pi/frame%03d.jpg' % frame)
|
||||
frame += 1
|
||||
.. literalinclude:: examples/button_stop_motion.py
|
||||
|
||||
See `Push Button Stop Motion`_ for a full resource.
|
||||
|
||||
@@ -240,30 +215,7 @@ Reaction Game
|
||||
|
||||
When you see the light come on, the first person to press their button wins!
|
||||
|
||||
::
|
||||
|
||||
from gpiozero import Button, LED
|
||||
from time import sleep
|
||||
import random
|
||||
|
||||
led = LED(17)
|
||||
|
||||
player_1 = Button(2)
|
||||
player_2 = Button(3)
|
||||
|
||||
time = random.uniform(5, 10)
|
||||
sleep(time)
|
||||
led.on()
|
||||
|
||||
while True:
|
||||
if player_1.is_pressed:
|
||||
print("Player 1 wins!")
|
||||
break
|
||||
if player_2.is_pressed:
|
||||
print("Player 2 wins!")
|
||||
break
|
||||
|
||||
led.off()
|
||||
.. literalinclude:: examples/reaction_game.py
|
||||
|
||||
See `Quick Reaction Game`_ for a full resource.
|
||||
|
||||
@@ -273,26 +225,7 @@ GPIO Music Box
|
||||
|
||||
Each button plays a different sound!
|
||||
|
||||
::
|
||||
|
||||
from gpiozero import Button
|
||||
import pygame.mixer
|
||||
from pygame.mixer import Sound
|
||||
from signal import pause
|
||||
|
||||
pygame.mixer.init()
|
||||
|
||||
sound_pins = {
|
||||
2: Sound("samples/drum_tom_mid_hard.wav"),
|
||||
3: Sound("samples/drum_cymbal_open.wav"),
|
||||
}
|
||||
|
||||
buttons = [Button(pin) for pin in sound_pins]
|
||||
for button in buttons:
|
||||
sound = sound_pins[button.pin.number]
|
||||
button.when_pressed = sound.play
|
||||
|
||||
pause()
|
||||
.. literalinclude:: examples/music_box.py
|
||||
|
||||
See `GPIO Music Box`_ for a full resource.
|
||||
|
||||
@@ -302,92 +235,27 @@ All on when pressed
|
||||
|
||||
While the button is pressed down, the buzzer and all the lights come on.
|
||||
|
||||
:class:`FishDish`::
|
||||
:class:`FishDish`:
|
||||
|
||||
from gpiozero import FishDish
|
||||
from signal import pause
|
||||
.. literalinclude:: examples/all_on_1.py
|
||||
|
||||
fish = FishDish()
|
||||
Ryanteck :class:`TrafficHat`:
|
||||
|
||||
fish.button.when_pressed = fish.on
|
||||
fish.button.when_released = fish.off
|
||||
.. literalinclude:: examples/all_on_2.py
|
||||
|
||||
pause()
|
||||
Using :class:`LED`, :class:`Buzzer`, and :class:`Button` components:
|
||||
|
||||
Ryanteck :class:`TrafficHat`::
|
||||
|
||||
from gpiozero import TrafficHat
|
||||
from signal import pause
|
||||
|
||||
th = TrafficHat()
|
||||
|
||||
th.button.when_pressed = th.on
|
||||
th.button.when_released = th.off
|
||||
|
||||
pause()
|
||||
|
||||
Using :class:`LED`, :class:`Buzzer`, and :class:`Button` components::
|
||||
|
||||
from gpiozero import LED, Buzzer, Button
|
||||
from signal import pause
|
||||
|
||||
button = Button(2)
|
||||
buzzer = Buzzer(3)
|
||||
red = LED(4)
|
||||
amber = LED(5)
|
||||
green = LED(6)
|
||||
|
||||
things = [red, amber, green, buzzer]
|
||||
|
||||
def things_on():
|
||||
for thing in things:
|
||||
thing.on()
|
||||
|
||||
def things_off():
|
||||
for thing in things:
|
||||
thing.off()
|
||||
|
||||
button.when_pressed = things_on
|
||||
button.when_released = things_off
|
||||
|
||||
pause()
|
||||
.. literalinclude:: examples/all_on_3.py
|
||||
|
||||
|
||||
RGB LED
|
||||
=======
|
||||
Full color LED
|
||||
==============
|
||||
|
||||
.. image:: images/rgb_led_bb.*
|
||||
|
||||
Making colours with an :class:`RGBLED`::
|
||||
Making colours with an :class:`RGBLED`:
|
||||
|
||||
from gpiozero import RGBLED
|
||||
from time import sleep
|
||||
|
||||
led = RGBLED(red=9, green=10, blue=11)
|
||||
|
||||
led.red = 1 # full red
|
||||
sleep(1)
|
||||
led.red = 0.5 # half red
|
||||
sleep(1)
|
||||
|
||||
led.color = (0, 1, 0) # full green
|
||||
sleep(1)
|
||||
led.color = (1, 0, 1) # magenta
|
||||
sleep(1)
|
||||
led.color = (1, 1, 0) # yellow
|
||||
sleep(1)
|
||||
led.color = (0, 1, 1) # cyan
|
||||
sleep(1)
|
||||
led.color = (1, 1, 1) # white
|
||||
sleep(1)
|
||||
|
||||
led.color = (0, 0, 0) # off
|
||||
sleep(1)
|
||||
|
||||
# slowly increase intensity of blue
|
||||
for n in range(100):
|
||||
led.blue = n/100
|
||||
sleep(0.1)
|
||||
.. literalinclude:: examples/rgbled.py
|
||||
|
||||
|
||||
Motion sensor
|
||||
@@ -395,18 +263,9 @@ Motion sensor
|
||||
|
||||
.. image:: images/motion_sensor_bb.*
|
||||
|
||||
Light an :class:`LED` when a :class:`MotionSensor` detects motion::
|
||||
Light an :class:`LED` when a :class:`MotionSensor` detects motion:
|
||||
|
||||
from gpiozero import MotionSensor, LED
|
||||
from signal import pause
|
||||
|
||||
pir = MotionSensor(4)
|
||||
led = LED(16)
|
||||
|
||||
pir.when_motion = led.on
|
||||
pir.when_no_motion = led.off
|
||||
|
||||
pause()
|
||||
.. literalinclude:: examples/motion_sensor.py
|
||||
|
||||
|
||||
Light sensor
|
||||
@@ -414,43 +273,18 @@ Light sensor
|
||||
|
||||
.. image:: images/light_sensor_bb.*
|
||||
|
||||
Have a :class:`LightSensor` detect light and dark::
|
||||
Have a :class:`LightSensor` detect light and dark:
|
||||
|
||||
from gpiozero import LightSensor
|
||||
.. literalinclude:: examples/light_sensor_1.py
|
||||
|
||||
sensor = LightSensor(18)
|
||||
Run a function when the light changes:
|
||||
|
||||
while True:
|
||||
sensor.wait_for_light()
|
||||
print("It's light! :)")
|
||||
sensor.wait_for_dark()
|
||||
print("It's dark :(")
|
||||
|
||||
Run a function when the light changes::
|
||||
|
||||
from gpiozero import LightSensor, LED
|
||||
from signal import pause
|
||||
|
||||
sensor = LightSensor(18)
|
||||
led = LED(16)
|
||||
|
||||
sensor.when_dark = led.on
|
||||
sensor.when_light = led.off
|
||||
|
||||
pause()
|
||||
.. literalinclude:: examples/light_sensor_2.py
|
||||
|
||||
Or make a :class:`PWMLED` change brightness according to the detected light
|
||||
level::
|
||||
level:
|
||||
|
||||
from gpiozero import LightSensor, LED
|
||||
from signal import pause
|
||||
|
||||
sensor = LightSensor(18)
|
||||
led = PWMLED(16)
|
||||
|
||||
led.source = sensor.values
|
||||
|
||||
pause()
|
||||
.. literalinclude:: examples/light_sensor_3.py
|
||||
|
||||
|
||||
Distance sensor
|
||||
@@ -458,29 +292,13 @@ Distance sensor
|
||||
|
||||
.. IMAGE TBD
|
||||
|
||||
Have a :class:`DistanceSensor` detect the distance to the nearest object::
|
||||
Have a :class:`DistanceSensor` detect the distance to the nearest object:
|
||||
|
||||
from gpiozero import DistanceSensor
|
||||
from time import sleep
|
||||
.. literalinclude:: examples/distance_sensor_1.py
|
||||
|
||||
sensor = DistanceSensor(23, 24)
|
||||
Run a function when something gets near the sensor:
|
||||
|
||||
while True:
|
||||
print('Distance to nearest object is', sensor.distance, 'm')
|
||||
sleep(1)
|
||||
|
||||
Run a function when something gets near the sensor::
|
||||
|
||||
from gpiozero import DistanceSensor, LED
|
||||
from signal import pause
|
||||
|
||||
sensor = DistanceSensor(23, 24, max_distance=1, threshold_distance=0.2)
|
||||
led = LED(16)
|
||||
|
||||
sensor.when_in_range = led.on
|
||||
sensor.when_out_of_range = led.off
|
||||
|
||||
pause()
|
||||
.. literalinclude:: examples/distance_sensor_2.py
|
||||
|
||||
|
||||
Motors
|
||||
@@ -488,18 +306,9 @@ Motors
|
||||
|
||||
.. image:: images/motor_bb.*
|
||||
|
||||
Spin a :class:`Motor` around forwards and backwards::
|
||||
Spin a :class:`Motor` around forwards and backwards:
|
||||
|
||||
from gpiozero import Motor
|
||||
from time import sleep
|
||||
|
||||
motor = Motor(forward=4, back=14)
|
||||
|
||||
while True:
|
||||
motor.forward()
|
||||
sleep(5)
|
||||
motor.backward()
|
||||
sleep(5)
|
||||
.. literalinclude:: examples/motor.py
|
||||
|
||||
|
||||
Robot
|
||||
@@ -507,163 +316,59 @@ Robot
|
||||
|
||||
.. IMAGE TBD
|
||||
|
||||
Make a :class:`Robot` drive around in (roughly) a square::
|
||||
Make a :class:`Robot` drive around in (roughly) a square:
|
||||
|
||||
from gpiozero import Robot
|
||||
from time import sleep
|
||||
|
||||
robot = Robot(left=(4, 14), right=(17, 18))
|
||||
|
||||
for i in range(4):
|
||||
robot.forward()
|
||||
sleep(10)
|
||||
robot.right()
|
||||
sleep(1)
|
||||
.. literalinclude:: examples/robot_1.py
|
||||
|
||||
Make a robot with a distance sensor that runs away when things get within
|
||||
20cm of it::
|
||||
20cm of it:
|
||||
|
||||
from gpiozero import Robot, DistanceSensor
|
||||
from signal import pause
|
||||
|
||||
sensor = DistanceSensor(23, 24, max_distance=1, threshold_distance=0.2)
|
||||
robot = Robot(left=(4, 14), right=(17, 18))
|
||||
|
||||
sensor.when_in_range = robot.backward
|
||||
sensor.when_out_of_range = robot.stop
|
||||
pause()
|
||||
.. literalinclude:: examples/robot_2.py
|
||||
|
||||
|
||||
Button controlled robot
|
||||
=======================
|
||||
|
||||
Use four GPIO buttons as forward/back/left/right controls for a robot::
|
||||
Use four GPIO buttons as forward/back/left/right controls for a robot:
|
||||
|
||||
from gpiozero import RyanteckRobot, Button
|
||||
from signal import pause
|
||||
|
||||
robot = RyanteckRobot()
|
||||
|
||||
left = Button(26)
|
||||
right = Button(16)
|
||||
fw = Button(21)
|
||||
bw = Button(20)
|
||||
|
||||
fw.when_pressed = robot.forward
|
||||
fw.when_released = robot.stop
|
||||
|
||||
left.when_pressed = robot.left
|
||||
left.when_released = robot.stop
|
||||
|
||||
right.when_pressed = robot.right
|
||||
right.when_released = robot.stop
|
||||
|
||||
bw.when_pressed = robot.backward
|
||||
bw.when_released = robot.stop
|
||||
|
||||
pause()
|
||||
.. literalinclude:: examples/robot_buttons.py
|
||||
|
||||
|
||||
Keyboard controlled robot
|
||||
=========================
|
||||
|
||||
Use up/down/left/right keys to control a robot::
|
||||
Use up/down/left/right keys to control a robot:
|
||||
|
||||
import curses
|
||||
from gpiozero import RyanteckRobot
|
||||
|
||||
robot = RyanteckRobot()
|
||||
|
||||
actions = {
|
||||
curses.KEY_UP: robot.forward,
|
||||
curses.KEY_DOWN: robot.backward,
|
||||
curses.KEY_LEFT: robot.left,
|
||||
curses.KEY_RIGHT: robot.right,
|
||||
}
|
||||
|
||||
def main(window):
|
||||
next_key = None
|
||||
while True:
|
||||
curses.halfdelay(1)
|
||||
if next_key is None:
|
||||
key = window.getch()
|
||||
else:
|
||||
key = next_key
|
||||
next_key = None
|
||||
if key != -1:
|
||||
# KEY DOWN
|
||||
curses.halfdelay(3)
|
||||
action = actions.get(key)
|
||||
if action is not None:
|
||||
action()
|
||||
next_key = key
|
||||
while next_key == key:
|
||||
next_key = window.getch()
|
||||
# KEY UP
|
||||
robot.stop()
|
||||
|
||||
curses.wrapper(main)
|
||||
.. literalinclude:: examples/robot_keyboard_1.py
|
||||
|
||||
.. note::
|
||||
|
||||
This recipe uses the ``curses`` module. This module requires that Python is
|
||||
running in a terminal in order to work correctly, hence this recipe will
|
||||
*not* work in environments like IDLE.
|
||||
This recipe uses the standard :mod:`curses` module. This module requires
|
||||
that Python is running in a terminal in order to work correctly, hence this
|
||||
recipe will *not* work in environments like IDLE.
|
||||
|
||||
If you prefer a version that works under IDLE, the following recipe should
|
||||
suffice, but will require that you install the evdev library with ``sudo pip
|
||||
install evdev`` first::
|
||||
suffice:
|
||||
|
||||
from gpiozero import RyanteckRobot
|
||||
from evdev import InputDevice, list_devices, ecodes
|
||||
.. literalinclude:: examples/robot_keyboard_2.py
|
||||
|
||||
robot = RyanteckRobot()
|
||||
.. note::
|
||||
|
||||
devices = [InputDevice(device) for device in list_devices()]
|
||||
keyboard = devices[0] # this may vary
|
||||
|
||||
keypress_actions = {
|
||||
ecodes.KEY_UP: robot.forward,
|
||||
ecodes.KEY_DOWN: robot.backward,
|
||||
ecodes.KEY_LEFT: robot.left,
|
||||
ecodes.KEY_RIGHT: robot.right,
|
||||
}
|
||||
|
||||
for event in keyboard.read_loop():
|
||||
if event.type == ecodes.EV_KEY:
|
||||
if event.value == 1: # key down
|
||||
keypress_actions[event.code]()
|
||||
if event.value == 0: # key up
|
||||
robot.stop()
|
||||
This recipe uses the third-party ``evdev`` module. Install this library
|
||||
with ``sudo pip3 install evdev`` first. Be aware that ``evdev`` will only
|
||||
work with local input devices; this recipe will *not* work over SSH.
|
||||
|
||||
|
||||
Motion sensor robot
|
||||
===================
|
||||
|
||||
Make a robot drive forward when it detects motion::
|
||||
Make a robot drive forward when it detects motion:
|
||||
|
||||
from gpiozero import Robot, MotionSensor
|
||||
from signal import pause
|
||||
.. literalinclude:: examples/robot_motion_1.py
|
||||
|
||||
robot = Robot(left=(4, 14), right=(17, 18))
|
||||
pir = MotionSensor(5)
|
||||
Alternatively:
|
||||
|
||||
pir.when_motion = robot.forward
|
||||
pir.when_no_motion = robot.stop
|
||||
|
||||
pause()
|
||||
|
||||
Alternatively::
|
||||
|
||||
from gpiozero import Robot, MotionSensor
|
||||
from signal import pause
|
||||
|
||||
robot = Robot(left=(4, 14), right=(17, 18))
|
||||
pir = MotionSensor(5)
|
||||
|
||||
robot.source = zip(pir.values, pir.values)
|
||||
|
||||
pause()
|
||||
.. literalinclude:: examples/robot_motion_2.py
|
||||
|
||||
|
||||
Potentiometer
|
||||
@@ -672,24 +377,14 @@ Potentiometer
|
||||
.. image:: images/potentiometer_bb.*
|
||||
|
||||
Continually print the value of a potentiometer (values between 0 and 1)
|
||||
connected to a :class:`MCP3008` analog to digital converter::
|
||||
connected to a :class:`MCP3008` analog to digital converter:
|
||||
|
||||
from gpiozero import MCP3008
|
||||
|
||||
while True:
|
||||
with MCP3008(channel=0) as pot:
|
||||
print(pot.value)
|
||||
.. literalinclude:: examples/pot_1.py
|
||||
|
||||
Present the value of a potentiometer on an LED bar graph using PWM to represent
|
||||
states that won't "fill" an LED::
|
||||
states that won't "fill" an LED:
|
||||
|
||||
from gpiozero import LEDBarGraph, MCP3008
|
||||
from signal import pause
|
||||
|
||||
graph = LEDBarGraph(5, 6, 13, 19, 26, pwm=True)
|
||||
pot = MCP3008(channel=0)
|
||||
graph.source = pot.values
|
||||
pause()
|
||||
.. literalinclude:: examples/pot_2.py
|
||||
|
||||
|
||||
Measure temperature with an ADC
|
||||
@@ -698,54 +393,24 @@ Measure temperature with an ADC
|
||||
.. IMAGE TBD
|
||||
|
||||
Wire a TMP36 temperature sensor to the first channel of an :class:`MCP3008`
|
||||
analog to digital converter::
|
||||
analog to digital converter:
|
||||
|
||||
from gpiozero import MCP3008
|
||||
from time import sleep
|
||||
|
||||
def convert_temp(gen):
|
||||
for value in gen:
|
||||
yield (value * 3.3 - 0.5) * 100
|
||||
|
||||
adc = MCP3008(channel=0)
|
||||
|
||||
for temp in convert_temp(adc.values):
|
||||
print('The temperature is', temp, 'C')
|
||||
sleep(1)
|
||||
.. literalinclude:: examples/thermometer.py
|
||||
|
||||
|
||||
Full color LED controlled by 3 potentiometers
|
||||
=============================================
|
||||
|
||||
Wire up three potentiometers (for red, green and blue) and use each of their
|
||||
values to make up the colour of the LED::
|
||||
values to make up the colour of the LED:
|
||||
|
||||
from gpiozero import RGBLED, MCP3008
|
||||
|
||||
led = RGBLED(red=2, green=3, blue=4)
|
||||
red_pot = MCP3008(channel=0)
|
||||
green_pot = MCP3008(channel=1)
|
||||
blue_pot = MCP3008(channel=2)
|
||||
|
||||
while True:
|
||||
led.red = red_pot.value
|
||||
led.green = green_pot.value
|
||||
led.blue = blue_pot.value
|
||||
.. literalinclude:: examples/rgbled_pot_1.py
|
||||
|
||||
Alternatively, the following example is identical, but uses the
|
||||
:attr:`~SourceMixin.source` property rather than a :keyword:`while` loop::
|
||||
:attr:`~SourceMixin.source` property rather than a :keyword:`while` loop:
|
||||
|
||||
from gpiozero import RGBLED, MCP3008
|
||||
from signal import pause
|
||||
|
||||
led = RGBLED(2, 3, 4)
|
||||
red_pot = MCP3008(0)
|
||||
green_pot = MCP3008(1)
|
||||
blue_pot = MCP3008(2)
|
||||
|
||||
led.source = zip(red_pot.values, green_pot.values, blue_pot.values)
|
||||
|
||||
pause()
|
||||
.. literalinclude:: examples/rgbled_pot_2.py
|
||||
:emphasize-lines: 8
|
||||
|
||||
Please note the example above requires Python 3. In Python 2, :func:`zip`
|
||||
doesn't support lazy evaluation so the script will simply hang.
|
||||
@@ -765,17 +430,9 @@ be done from the terminal with the following commands::
|
||||
$ echo none | sudo tee /sys/class/leds/led0/trigger
|
||||
$ echo gpio | sudo tee /sys/class/leds/led1/trigger
|
||||
|
||||
Now you can control the LEDs with gpiozero like so::
|
||||
Now you can control the LEDs with gpiozero like so:
|
||||
|
||||
from gpiozero import LED
|
||||
from signal import pause
|
||||
|
||||
power = LED(35)
|
||||
activity = LED(47)
|
||||
|
||||
activity.blink()
|
||||
power.blink()
|
||||
pause()
|
||||
.. literalinclude:: examples/led_builtin.py
|
||||
|
||||
To revert the LEDs to their usual purpose you can either reboot your Pi or
|
||||
run the following commands::
|
||||
@@ -788,7 +445,7 @@ run the following commands::
|
||||
On the Pi Zero you can control the activity LED with this recipe, but
|
||||
there's no separate power LED to control (it's also worth noting the
|
||||
activity LED is active low, so set ``active_high=False`` when constructing
|
||||
your LED component.
|
||||
your LED component).
|
||||
|
||||
On the original Pi 1 (model A or B), the activity LED can be controlled
|
||||
with GPIO16 (after disabling its trigger as above) but the power LED is
|
||||
@@ -798,6 +455,7 @@ run the following commands::
|
||||
accessible from gpiozero (yet).
|
||||
|
||||
|
||||
.. _travispy: https://travispy.readthedocs.io/
|
||||
.. _Push Button Stop Motion: https://www.raspberrypi.org/learning/quick-reaction-game/
|
||||
.. _Quick Reaction Game: https://www.raspberrypi.org/learning/quick-reaction-game/
|
||||
.. _GPIO Music Box: https://www.raspberrypi.org/learning/gpio-music-box/
|
||||
|
||||
@@ -7,6 +7,7 @@ from __future__ import (
|
||||
|
||||
from .pins import (
|
||||
Pin,
|
||||
LocalPin,
|
||||
)
|
||||
from .pins.data import (
|
||||
PiBoardInfo,
|
||||
@@ -99,13 +100,17 @@ from .output_devices import (
|
||||
LED,
|
||||
Buzzer,
|
||||
Motor,
|
||||
Servo,
|
||||
AngularServo,
|
||||
RGBLED,
|
||||
)
|
||||
from .boards import (
|
||||
CompositeOutputDevice,
|
||||
ButtonBoard,
|
||||
LEDCollection,
|
||||
LEDBoard,
|
||||
LEDBarGraph,
|
||||
LedBorg,
|
||||
PiLiter,
|
||||
PiLiterBarGraph,
|
||||
TrafficLights,
|
||||
@@ -122,5 +127,6 @@ from .boards import (
|
||||
from .other_devices import (
|
||||
InternalDevice,
|
||||
PingServer,
|
||||
CPUTemperature,
|
||||
TimeOfDay,
|
||||
)
|
||||
|
||||
@@ -12,18 +12,27 @@ except ImportError:
|
||||
from time import sleep
|
||||
from itertools import repeat, cycle, chain
|
||||
from threading import Lock
|
||||
from collections import OrderedDict
|
||||
|
||||
from .exc import (
|
||||
DeviceClosed,
|
||||
GPIOPinMissing,
|
||||
EnergenieSocketMissing,
|
||||
EnergenieBadSocket,
|
||||
OutputDeviceBadValue,
|
||||
)
|
||||
from .input_devices import Button
|
||||
from .output_devices import OutputDevice, LED, PWMLED, Buzzer, Motor
|
||||
from .output_devices import (
|
||||
OutputDevice,
|
||||
LED,
|
||||
PWMLED,
|
||||
RGBLED,
|
||||
Buzzer,
|
||||
Motor,
|
||||
)
|
||||
from .threads import GPIOThread
|
||||
from .devices import Device, CompositeDevice
|
||||
from .mixins import SharedMixin, SourceMixin
|
||||
from .mixins import SharedMixin, SourceMixin, HoldMixin
|
||||
|
||||
|
||||
class CompositeOutputDevice(SourceMixin, CompositeDevice):
|
||||
@@ -44,7 +53,7 @@ class CompositeOutputDevice(SourceMixin, CompositeDevice):
|
||||
"""
|
||||
Turn all the output devices on.
|
||||
"""
|
||||
for device in self.all:
|
||||
for device in self:
|
||||
if isinstance(device, (OutputDevice, CompositeOutputDevice)):
|
||||
device.on()
|
||||
|
||||
@@ -52,7 +61,7 @@ class CompositeOutputDevice(SourceMixin, CompositeDevice):
|
||||
"""
|
||||
Turn all the output devices off.
|
||||
"""
|
||||
for device in self.all:
|
||||
for device in self:
|
||||
if isinstance(device, (OutputDevice, CompositeOutputDevice)):
|
||||
device.off()
|
||||
|
||||
@@ -61,7 +70,7 @@ class CompositeOutputDevice(SourceMixin, CompositeDevice):
|
||||
Toggle all the output devices. For each device, if it's on, turn it
|
||||
off; if it's off, turn it on.
|
||||
"""
|
||||
for device in self.all:
|
||||
for device in self:
|
||||
if isinstance(device, (OutputDevice, CompositeOutputDevice)):
|
||||
device.toggle()
|
||||
|
||||
@@ -75,18 +84,126 @@ class CompositeOutputDevice(SourceMixin, CompositeDevice):
|
||||
|
||||
@value.setter
|
||||
def value(self, value):
|
||||
for device, v in zip(self.all, value):
|
||||
for device, v in zip(self, value):
|
||||
if isinstance(device, (OutputDevice, CompositeOutputDevice)):
|
||||
device.value = v
|
||||
# Simply ignore values for non-output devices
|
||||
|
||||
|
||||
class ButtonBoard(HoldMixin, CompositeDevice):
|
||||
"""
|
||||
Extends :class:`CompositeDevice` and represents a generic button board or
|
||||
collection of buttons.
|
||||
|
||||
:param int \*pins:
|
||||
Specify the GPIO pins that the buttons of the board are attached to.
|
||||
You can designate as many pins as necessary.
|
||||
|
||||
:param bool pull_up:
|
||||
If ``True`` (the default), the GPIO pins will be pulled high by
|
||||
default. In this case, connect the other side of the buttons to
|
||||
ground. If ``False``, the GPIO pins will be pulled low by default. In
|
||||
this case, connect the other side of the buttons to 3V3. This
|
||||
parameter can only be specified as a keyword parameter.
|
||||
|
||||
:param float bounce_time:
|
||||
If ``None`` (the default), no software bounce compensation will be
|
||||
performed. Otherwise, this is the length of time (in seconds) that the
|
||||
buttons will ignore changes in state after an initial change. This
|
||||
parameter can only be specified as a keyword parameter.
|
||||
|
||||
:param float hold_time:
|
||||
The length of time (in seconds) to wait after any button is pushed,
|
||||
until executing the :attr:`when_held` handler. Defaults to ``1``. This
|
||||
parameter can only be specified as a keyword parameter.
|
||||
|
||||
:param bool hold_repeat:
|
||||
If ``True``, the :attr:`when_held` handler will be repeatedly executed
|
||||
as long as any buttons remain held, every *hold_time* seconds. If
|
||||
``False`` (the default) the :attr:`when_held` handler will be only be
|
||||
executed once per hold. This parameter can only be specified as a
|
||||
keyword parameter.
|
||||
|
||||
:param \*\*named_pins:
|
||||
Specify GPIO pins that buttons of the board are attached to,
|
||||
associating each button with a property name. You can designate as
|
||||
many pins as necessary and use any names, provided they're not already
|
||||
in use by something else.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
pull_up = kwargs.pop('pull_up', True)
|
||||
bounce_time = kwargs.pop('bounce_time', None)
|
||||
hold_time = kwargs.pop('hold_time', 1)
|
||||
hold_repeat = kwargs.pop('hold_repeat', False)
|
||||
order = kwargs.pop('_order', None)
|
||||
super(ButtonBoard, self).__init__(
|
||||
*(
|
||||
Button(pin, pull_up, bounce_time, hold_time, hold_repeat)
|
||||
for pin in args
|
||||
),
|
||||
_order=order,
|
||||
**{
|
||||
name: Button(pin, pull_up, bounce_time, hold_time, hold_repeat)
|
||||
for name, pin in kwargs.items()
|
||||
})
|
||||
def get_new_handler(device):
|
||||
def fire_both_events():
|
||||
device._fire_events()
|
||||
self._fire_events()
|
||||
return fire_both_events
|
||||
for button in self:
|
||||
button.pin.when_changed = get_new_handler(button)
|
||||
self._when_changed = None
|
||||
self._last_value = None
|
||||
# Call _fire_events once to set initial state of events
|
||||
self._fire_events()
|
||||
self.hold_time = hold_time
|
||||
self.hold_repeat = hold_repeat
|
||||
|
||||
@property
|
||||
def pull_up(self):
|
||||
"""
|
||||
If ``True``, the device uses a pull-up resistor to set the GPIO pin
|
||||
"high" by default.
|
||||
"""
|
||||
return self[0].pull_up
|
||||
|
||||
@property
|
||||
def when_changed(self):
|
||||
return self._when_changed
|
||||
|
||||
@when_changed.setter
|
||||
def when_changed(self, value):
|
||||
self._when_changed = self._wrap_callback(value)
|
||||
|
||||
def _fire_changed(self):
|
||||
if self.when_changed:
|
||||
self.when_changed()
|
||||
|
||||
def _fire_events(self):
|
||||
super(ButtonBoard, self)._fire_events()
|
||||
old_value = self._last_value
|
||||
new_value = self._last_value = self.value
|
||||
if old_value is None:
|
||||
# Initial "indeterminate" value; don't do anything
|
||||
pass
|
||||
elif old_value != new_value:
|
||||
self._fire_changed()
|
||||
|
||||
|
||||
ButtonBoard.is_pressed = ButtonBoard.is_active
|
||||
ButtonBoard.pressed_time = ButtonBoard.active_time
|
||||
ButtonBoard.when_pressed = ButtonBoard.when_activated
|
||||
ButtonBoard.when_released = ButtonBoard.when_deactivated
|
||||
ButtonBoard.wait_for_press = ButtonBoard.wait_for_active
|
||||
ButtonBoard.wait_for_release = ButtonBoard.wait_for_inactive
|
||||
|
||||
|
||||
class LEDCollection(CompositeOutputDevice):
|
||||
"""
|
||||
Extends :class:`CompositeOutputDevice`. Abstract base class for
|
||||
:class:`LEDBoard` and :class:`LEDBarGraph`.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._blink_thread = None
|
||||
pwm = kwargs.pop('pwm', False)
|
||||
@@ -108,19 +225,26 @@ class LEDCollection(CompositeOutputDevice):
|
||||
LEDClass(pin_or_collection, active_high, initial_value)
|
||||
for name, pin_or_collection in kwargs.items()
|
||||
})
|
||||
leds = []
|
||||
for item in self:
|
||||
if isinstance(item, LEDCollection):
|
||||
for subitem in item.leds:
|
||||
leds.append(subitem)
|
||||
else:
|
||||
leds.append(item)
|
||||
self._leds = tuple(leds)
|
||||
|
||||
@property
|
||||
def leds(self):
|
||||
"""
|
||||
A flat iterator over all LEDs contained in this collection (and all
|
||||
A flat tuple of all LEDs contained in this collection (and all
|
||||
sub-collections).
|
||||
"""
|
||||
for item in self:
|
||||
if isinstance(item, LEDCollection):
|
||||
for subitem in item.leds:
|
||||
yield subitem
|
||||
else:
|
||||
yield item
|
||||
return self._leds
|
||||
|
||||
@property
|
||||
def active_high(self):
|
||||
return self[0].active_high
|
||||
|
||||
|
||||
class LEDBoard(LEDCollection):
|
||||
@@ -148,21 +272,23 @@ class LEDBoard(LEDCollection):
|
||||
|
||||
:param bool active_high:
|
||||
If ``True`` (the default), the :meth:`on` method will set all the
|
||||
associates pins to HIGH. If ``False``, the :meth:`on` method will set
|
||||
all pins to LOW (the :meth:`off` method always does the opposite).
|
||||
associated pins to HIGH. If ``False``, the :meth:`on` method will set
|
||||
all pins to LOW (the :meth:`off` method always does the opposite). This
|
||||
parameter can only be specified as a keyword parameter.
|
||||
|
||||
:param bool initial_value:
|
||||
If ``False`` (the default), all LEDs will be off initially. If
|
||||
``None``, each device will be left in whatever state the pin is found
|
||||
in when configured for output (warning: this can be on). If ``True``,
|
||||
the device will be switched on initially.
|
||||
the device will be switched on initially. This parameter can only be
|
||||
specified as a keyword parameter.
|
||||
|
||||
:param \*\*named_pins:
|
||||
Sepcify GPIO pins that LEDs of the board are attached to, associated
|
||||
Specify GPIO pins that LEDs of the board are attached to, associating
|
||||
each LED with a property name. You can designate as many pins as
|
||||
necessary and any name provided it's not already in use by something
|
||||
else. You can also specify :class:`LEDBoard` instances to create
|
||||
trees of LEDs.
|
||||
necessary and use any names, provided they're not already in use by
|
||||
something else. You can also specify :class:`LEDBoard` instances to
|
||||
create trees of LEDs.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._blink_leds = []
|
||||
@@ -345,15 +471,21 @@ class LEDBarGraph(LEDCollection):
|
||||
Specify the GPIO pins that the LEDs of the bar graph are attached to.
|
||||
You can designate as many pins as necessary.
|
||||
|
||||
:param float initial_value:
|
||||
The initial :attr:`value` of the graph given as a float between -1 and
|
||||
+1. Defaults to 0.0. This parameter can only be specified as a keyword
|
||||
parameter.
|
||||
|
||||
:param bool pwm:
|
||||
If ``True``, construct :class:`PWMLED` instances for each pin. If
|
||||
``False`` (the default), construct regular :class:`LED` instances. This
|
||||
parameter can only be specified as a keyword parameter.
|
||||
|
||||
:param bool active_high:
|
||||
If ``True`` (the default), the :meth:`on` method will set all the
|
||||
associated pins to HIGH. If ``False``, the :meth:`on` method will set
|
||||
all pins to LOW (the :meth:`off` method always does the opposite). This
|
||||
parameter can only be specified as a keyword parameter.
|
||||
|
||||
:param float initial_value:
|
||||
The initial :attr:`value` of the graph given as a float between -1 and
|
||||
+1. Defaults to ``0.0``. This parameter can only be specified as a
|
||||
keyword parameter.
|
||||
"""
|
||||
|
||||
def __init__(self, *pins, **kwargs):
|
||||
@@ -361,10 +493,11 @@ class LEDBarGraph(LEDCollection):
|
||||
for pin in pins:
|
||||
assert not isinstance(pin, LEDCollection)
|
||||
pwm = kwargs.pop('pwm', False)
|
||||
initial_value = kwargs.pop('initial_value', 0)
|
||||
active_high = kwargs.pop('active_high', True)
|
||||
initial_value = kwargs.pop('initial_value', 0.0)
|
||||
if kwargs:
|
||||
raise TypeError('unexpected keyword argument: %s' % kwargs.popitem()[0])
|
||||
super(LEDBarGraph, self).__init__(*pins, pwm=pwm)
|
||||
super(LEDBarGraph, self).__init__(*pins, pwm=pwm, active_high=active_high)
|
||||
try:
|
||||
self.value = initial_value
|
||||
except:
|
||||
@@ -402,6 +535,8 @@ class LEDBarGraph(LEDCollection):
|
||||
|
||||
@value.setter
|
||||
def value(self, value):
|
||||
if not -1 <= value <= 1:
|
||||
raise OutputDeviceBadValue('LEDBarGraph value must be between -1 and 1')
|
||||
count = len(self)
|
||||
leds = self
|
||||
if value < 0:
|
||||
@@ -415,6 +550,36 @@ class LEDBarGraph(LEDCollection):
|
||||
led.value = calc_value(index)
|
||||
|
||||
|
||||
class LedBorg(RGBLED):
|
||||
"""
|
||||
Extends :class:`RGBLED` for the `PiBorg LedBorg`_: an add-on board
|
||||
containing a very bright RGB LED.
|
||||
|
||||
The LedBorg pins are fixed and therefore there's no need to specify them
|
||||
when constructing this class. The following example turns the LedBorg
|
||||
purple::
|
||||
|
||||
from gpiozero import LedBorg
|
||||
|
||||
led = LedBorg()
|
||||
led.color = (1, 0, 1)
|
||||
|
||||
:param tuple initial_value:
|
||||
The initial color for the LedBorg. Defaults to black ``(0, 0, 0)``.
|
||||
|
||||
:param bool pwm:
|
||||
If ``True`` (the default), construct :class:`PWMLED` instances for
|
||||
each component of the LedBorg. If ``False``, construct regular
|
||||
:class:`LED` instances, which prevents smooth color graduations.
|
||||
|
||||
.. _PiBorg LedBorg: https://www.piborg.org/ledborg
|
||||
"""
|
||||
|
||||
def __init__(self, initial_value=(0, 0, 0), pwm=True):
|
||||
super(LedBorg, self).__init__(red=17, green=27, blue=22,
|
||||
pwm=pwm, initial_value=initial_value)
|
||||
|
||||
|
||||
class PiLiter(LEDBoard):
|
||||
"""
|
||||
Extends :class:`LEDBoard` for the `Ciseco Pi-LITEr`_: a strip of 8 very bright
|
||||
@@ -431,14 +596,20 @@ class PiLiter(LEDBoard):
|
||||
|
||||
:param bool pwm:
|
||||
If ``True``, construct :class:`PWMLED` instances for each pin. If
|
||||
``False`` (the default), construct regular :class:`LED` instances. This
|
||||
parameter can only be specified as a keyword parameter.
|
||||
``False`` (the default), construct regular :class:`LED` instances.
|
||||
|
||||
:param bool initial_value:
|
||||
If ``False`` (the default), all LEDs will be off initially. If
|
||||
``None``, each device will be left in whatever state the pin is found
|
||||
in when configured for output (warning: this can be on). If ``True``,
|
||||
the device will be switched on initially.
|
||||
|
||||
.. _Ciseco Pi-LITEr: http://shop.ciseco.co.uk/pi-liter-8-led-strip-for-the-raspberry-pi/
|
||||
"""
|
||||
|
||||
def __init__(self, pwm=False):
|
||||
super(PiLiter, self).__init__(4, 17, 27, 18, 22, 23, 24, 25, pwm=pwm)
|
||||
def __init__(self, pwm=False, initial_value=False):
|
||||
super(PiLiter, self).__init__(4, 17, 27, 18, 22, 23, 24, 25,
|
||||
pwm=pwm, initial_value=initial_value)
|
||||
|
||||
|
||||
class PiLiterBarGraph(LEDBarGraph):
|
||||
@@ -455,25 +626,30 @@ class PiLiterBarGraph(LEDBarGraph):
|
||||
graph = PiLiterBarGraph()
|
||||
graph.value = 0.5
|
||||
|
||||
:param bool initial_value:
|
||||
The initial value of the graph given as a float between -1 and +1.
|
||||
Defaults to 0.0.
|
||||
:param bool pwm:
|
||||
If ``True``, construct :class:`PWMLED` instances for each pin. If
|
||||
``False`` (the default), construct regular :class:`LED` instances.
|
||||
|
||||
:param float initial_value:
|
||||
The initial :attr:`value` of the graph given as a float between -1 and
|
||||
+1. Defaults to ``0.0``.
|
||||
|
||||
.. _Ciseco Pi-LITEr: http://shop.ciseco.co.uk/pi-liter-8-led-strip-for-the-raspberry-pi/
|
||||
"""
|
||||
|
||||
def __init__(self, initial_value=0):
|
||||
super(PiLiterBarGraph, self).__init__(
|
||||
4, 17, 27, 18, 22, 23, 24, 25, initial_value=initial_value)
|
||||
def __init__(self, pwm=False, initial_value=0.0):
|
||||
pins = (4, 17, 27, 18, 22, 23, 24, 25)
|
||||
super(PiLiterBarGraph, self).__init__(*pins,
|
||||
pwm=pwm, initial_value=initial_value)
|
||||
|
||||
|
||||
class TrafficLights(LEDBoard):
|
||||
"""
|
||||
Extends :class:`LEDBoard` for devices containing red, amber, and green
|
||||
Extends :class:`LEDBoard` for devices containing red, yellow, and green
|
||||
LEDs.
|
||||
|
||||
The following example initializes a device connected to GPIO pins 2, 3,
|
||||
and 4, then lights the amber LED attached to GPIO 3::
|
||||
and 4, then lights the amber (yellow) LED attached to GPIO 3::
|
||||
|
||||
from gpiozero import TrafficLights
|
||||
|
||||
@@ -493,15 +669,53 @@ class TrafficLights(LEDBoard):
|
||||
If ``True``, construct :class:`PWMLED` instances to represent each
|
||||
LED. If ``False`` (the default), construct regular :class:`LED`
|
||||
instances.
|
||||
|
||||
:param bool initial_value:
|
||||
If ``False`` (the default), all LEDs will be off initially. If
|
||||
``None``, each device will be left in whatever state the pin is found
|
||||
in when configured for output (warning: this can be on). If ``True``,
|
||||
the device will be switched on initially.
|
||||
|
||||
:param int yellow:
|
||||
The GPIO pin that the yellow LED is attached to. This is merely an
|
||||
alias for the ``amber`` parameter - you can't specify both ``amber``
|
||||
and ``yellow``.
|
||||
"""
|
||||
def __init__(self, red=None, amber=None, green=None, pwm=False):
|
||||
if not all([red, amber, green]):
|
||||
def __init__(self, red=None, amber=None, green=None,
|
||||
pwm=False, initial_value=False, yellow=None):
|
||||
if amber is not None and yellow is not None:
|
||||
raise OutputDeviceBadValue(
|
||||
'Only one of amber or yellow can be specified'
|
||||
)
|
||||
devices = OrderedDict((('red', red), ))
|
||||
self._display_yellow = amber is None and yellow is not None
|
||||
if self._display_yellow:
|
||||
devices['yellow'] = yellow
|
||||
else:
|
||||
devices['amber'] = amber
|
||||
devices['green'] = green
|
||||
if not all(p is not None for p in devices.values()):
|
||||
raise GPIOPinMissing(
|
||||
'red, amber and green pins must be provided'
|
||||
', '.join(devices.keys())+' pins must be provided'
|
||||
)
|
||||
super(TrafficLights, self).__init__(
|
||||
red=red, amber=amber, green=green, pwm=pwm,
|
||||
_order=('red', 'amber', 'green'))
|
||||
pwm=pwm, initial_value=initial_value,
|
||||
_order=devices.keys(),
|
||||
**devices)
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name == 'amber' and self._display_yellow:
|
||||
name = 'yellow'
|
||||
elif name == 'yellow' and not self._display_yellow:
|
||||
name = 'amber'
|
||||
return super(TrafficLights, self).__getattr__(name)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name == 'amber' and self._display_yellow:
|
||||
name = 'yellow'
|
||||
elif name == 'yellow' and not self._display_yellow:
|
||||
name = 'amber'
|
||||
return super(TrafficLights, self).__setattr__(name, value)
|
||||
|
||||
|
||||
class PiTraffic(TrafficLights):
|
||||
@@ -521,11 +735,22 @@ class PiTraffic(TrafficLights):
|
||||
To use the PI-TRAFFIC board when attached to a non-standard set of pins,
|
||||
simply use the parent class, :class:`TrafficLights`.
|
||||
|
||||
:param bool pwm:
|
||||
If ``True``, construct :class:`PWMLED` instances to represent each
|
||||
LED. If ``False`` (the default), construct regular :class:`LED`
|
||||
instances.
|
||||
|
||||
:param bool initial_value:
|
||||
If ``False`` (the default), all LEDs will be off initially. If
|
||||
``None``, each device will be left in whatever state the pin is found
|
||||
in when configured for output (warning: this can be on). If ``True``,
|
||||
the device will be switched on initially.
|
||||
|
||||
.. _Low Voltage Labs PI-TRAFFIC: http://lowvoltagelabs.com/products/pi-traffic/
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(PiTraffic, self).__init__(9, 10, 11)
|
||||
def __init__(self, pwm=False, initial_value=False):
|
||||
super(PiTraffic, self).__init__(9, 10, 11,
|
||||
pwm=pwm, initial_value=initial_value)
|
||||
|
||||
|
||||
class SnowPi(LEDBoard):
|
||||
@@ -548,31 +773,41 @@ class SnowPi(LEDBoard):
|
||||
LED. If ``False`` (the default), construct regular :class:`LED`
|
||||
instances.
|
||||
|
||||
:param bool initial_value:
|
||||
If ``False`` (the default), all LEDs will be off initially. If
|
||||
``None``, each device will be left in whatever state the pin is found
|
||||
in when configured for output (warning: this can be on). If ``True``,
|
||||
the device will be switched on initially.
|
||||
|
||||
.. _Ryanteck SnowPi: https://ryanteck.uk/raspberry-pi/114-snowpi-the-gpio-snowman-for-raspberry-pi-0635648608303.html
|
||||
"""
|
||||
def __init__(self, pwm=False):
|
||||
def __init__(self, pwm=False, initial_value=False):
|
||||
super(SnowPi, self).__init__(
|
||||
arms=LEDBoard(
|
||||
left=LEDBoard(
|
||||
top=17, middle=18, bottom=22, pwm=pwm,
|
||||
top=17, middle=18, bottom=22,
|
||||
pwm=pwm, initial_value=initial_value,
|
||||
_order=('top', 'middle', 'bottom')),
|
||||
right=LEDBoard(
|
||||
top=7, middle=8, bottom=9, pwm=pwm,
|
||||
top=7, middle=8, bottom=9,
|
||||
pwm=pwm, initial_value=initial_value,
|
||||
_order=('top', 'middle', 'bottom')),
|
||||
_order=('left', 'right')
|
||||
),
|
||||
eyes=LEDBoard(
|
||||
left=23, right=24, pwm=pwm,
|
||||
left=23, right=24,
|
||||
pwm=pwm, initial_value=initial_value,
|
||||
_order=('left', 'right')
|
||||
),
|
||||
nose=25, pwm=pwm,
|
||||
nose=25,
|
||||
pwm=pwm, initial_value=initial_value,
|
||||
_order=('eyes', 'nose', 'arms')
|
||||
)
|
||||
|
||||
|
||||
class TrafficLightsBuzzer(CompositeOutputDevice):
|
||||
"""
|
||||
Extends :class:`CompositeDevice` and is a generic class for HATs with
|
||||
Extends :class:`CompositeOutputDevice` and is a generic class for HATs with
|
||||
traffic lights, a button and a buzzer.
|
||||
|
||||
:param TrafficLights lights:
|
||||
@@ -594,7 +829,7 @@ class TrafficLightsBuzzer(CompositeOutputDevice):
|
||||
|
||||
class FishDish(TrafficLightsBuzzer):
|
||||
"""
|
||||
Extends :class:`TrafficLightsBuzzer` for the Pi Supply FishDish: traffic
|
||||
Extends :class:`TrafficLightsBuzzer` for the `Pi Supply FishDish`_: traffic
|
||||
light LEDs, a button and a buzzer.
|
||||
|
||||
The FishDish pins are fixed and therefore there's no need to specify them
|
||||
@@ -611,6 +846,8 @@ class FishDish(TrafficLightsBuzzer):
|
||||
If ``True``, construct :class:`PWMLED` instances to represent each
|
||||
LED. If ``False`` (the default), construct regular :class:`LED`
|
||||
instances.
|
||||
|
||||
.. _Pi Supply FishDish: https://www.pi-supply.com/product/fish-dish-raspberry-pi-led-buzzer-board/
|
||||
"""
|
||||
|
||||
def __init__(self, pwm=False):
|
||||
@@ -623,7 +860,7 @@ class FishDish(TrafficLightsBuzzer):
|
||||
|
||||
class TrafficHat(TrafficLightsBuzzer):
|
||||
"""
|
||||
Extends :class:`TrafficLightsBuzzer` for the Ryanteck Traffic HAT: traffic
|
||||
Extends :class:`TrafficLightsBuzzer` for the `Ryanteck Traffic HAT`_: traffic
|
||||
light LEDs, a button and a buzzer.
|
||||
|
||||
The Traffic HAT pins are fixed and therefore there's no need to specify
|
||||
@@ -640,6 +877,8 @@ class TrafficHat(TrafficLightsBuzzer):
|
||||
If ``True``, construct :class:`PWMLED` instances to represent each
|
||||
LED. If ``False`` (the default), construct regular :class:`LED`
|
||||
instances.
|
||||
|
||||
.. _Ryanteck Traffic HAT: https://ryanteck.uk/hats/1-traffichat-0635648607122.html
|
||||
"""
|
||||
|
||||
def __init__(self, pwm=False):
|
||||
@@ -658,12 +897,12 @@ class Robot(SourceMixin, CompositeDevice):
|
||||
backward pins of the left and right controllers respectively. For example,
|
||||
if the left motor's controller is connected to GPIOs 4 and 14, while the
|
||||
right motor's controller is connected to GPIOs 17 and 18 then the following
|
||||
example will turn the robot left::
|
||||
example will drive the robot forward::
|
||||
|
||||
from gpiozero import Robot
|
||||
|
||||
robot = Robot(left=(4, 14), right=(17, 18))
|
||||
robot.left()
|
||||
robot.forward()
|
||||
|
||||
:param tuple left:
|
||||
A tuple of two GPIO pins representing the forward and backward inputs
|
||||
@@ -680,6 +919,20 @@ class Robot(SourceMixin, CompositeDevice):
|
||||
right_motor=Motor(*right),
|
||||
_order=('left_motor', 'right_motor'))
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
"""
|
||||
Represents the motion of the robot as a tuple of (left_motor_speed,
|
||||
right_motor_speed) with ``(-1, -1)`` representing full speed backwards,
|
||||
``(1, 1)`` representing full speed forwards, and ``(0, 0)``
|
||||
representing stopped.
|
||||
"""
|
||||
return super(Robot, self).value
|
||||
|
||||
@value.setter
|
||||
def value(self, value):
|
||||
self.left_motor.value, self.right_motor.value = value
|
||||
|
||||
def forward(self, speed=1):
|
||||
"""
|
||||
Drive the robot forward by running both motors forward.
|
||||
@@ -746,16 +999,18 @@ class Robot(SourceMixin, CompositeDevice):
|
||||
|
||||
class RyanteckRobot(Robot):
|
||||
"""
|
||||
Extends :class:`Robot` for the Ryanteck MCB robot.
|
||||
Extends :class:`Robot` for the `Ryanteck MCB`_ robot.
|
||||
|
||||
The Ryanteck MCB pins are fixed and therefore there's no need to specify
|
||||
them when constructing this class. The following example turns the robot
|
||||
left::
|
||||
them when constructing this class. The following example drives the robot
|
||||
forward::
|
||||
|
||||
from gpiozero import RyanteckRobot
|
||||
|
||||
robot = RyanteckRobot()
|
||||
robot.left()
|
||||
robot.forward()
|
||||
|
||||
.. _Ryanteck MCB: https://ryanteck.uk/add-ons/6-ryanteck-rpi-motor-controller-board-0635648607160.html
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
@@ -767,13 +1022,13 @@ class CamJamKitRobot(Robot):
|
||||
Extends :class:`Robot` for the `CamJam #3 EduKit`_ robot controller.
|
||||
|
||||
The CamJam robot controller pins are fixed and therefore there's no need
|
||||
to specify them when constructing this class. The following example turns
|
||||
the robot left::
|
||||
to specify them when constructing this class. The following example drives
|
||||
the robot forward::
|
||||
|
||||
from gpiozero import CamJamKitRobot
|
||||
|
||||
robot = CamJamKitRobot()
|
||||
robot.left()
|
||||
robot.forward()
|
||||
|
||||
.. _CamJam #3 EduKit: http://camjam.me/?page_id=1035
|
||||
"""
|
||||
@@ -805,7 +1060,7 @@ class _EnergenieMaster(SharedMixin, CompositeOutputDevice):
|
||||
with self._lock:
|
||||
try:
|
||||
code = (8 * bool(enable)) + (8 - socket)
|
||||
for bit in self.all[:4]:
|
||||
for bit in self[:4]:
|
||||
bit.value = (code & 1)
|
||||
code >>= 1
|
||||
sleep(0.1)
|
||||
@@ -846,6 +1101,7 @@ class Energenie(SourceMixin, Device):
|
||||
raise EnergenieSocketMissing('socket number must be provided')
|
||||
if not (1 <= socket <= 4):
|
||||
raise EnergenieBadSocket('socket number must be between 1 and 4')
|
||||
self._value = None
|
||||
super(Energenie, self).__init__()
|
||||
self._socket = socket
|
||||
self._master = _EnergenieMaster()
|
||||
@@ -877,8 +1133,9 @@ class Energenie(SourceMixin, Device):
|
||||
|
||||
@value.setter
|
||||
def value(self, value):
|
||||
self._master.transmit(self._socket, bool(value))
|
||||
self._value = bool(value)
|
||||
value = bool(value)
|
||||
self._master.transmit(self._socket, value)
|
||||
self._value = value
|
||||
|
||||
def on(self):
|
||||
self.value = True
|
||||
|
||||
@@ -9,6 +9,9 @@ from __future__ import (
|
||||
str = type('')
|
||||
|
||||
import cmath
|
||||
import collections
|
||||
import operator
|
||||
import functools
|
||||
|
||||
|
||||
# Back-ported from python 3.5; see
|
||||
@@ -51,3 +54,30 @@ def median(data):
|
||||
i = n // 2
|
||||
return (data[i - 1] + data[i]) / 2
|
||||
|
||||
|
||||
# Copied from the MIT-licensed https://github.com/slezica/python-frozendict
|
||||
class frozendict(collections.Mapping):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.__dict = dict(*args, **kwargs)
|
||||
self.__hash = None
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.__dict[key]
|
||||
|
||||
def copy(self, **add_or_replace):
|
||||
return frozendict(self, **add_or_replace)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.__dict)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.__dict)
|
||||
|
||||
def __repr__(self):
|
||||
return '<frozendict %s>' % repr(self.__dict)
|
||||
|
||||
def __hash__(self):
|
||||
if self.__hash is None:
|
||||
hashes = map(hash, self.items())
|
||||
self.__hash = functools.reduce(operator.xor, hashes, 0)
|
||||
return self.__hash
|
||||
|
||||
@@ -7,6 +7,7 @@ from __future__ import (
|
||||
nstr = str
|
||||
str = type('')
|
||||
|
||||
import os
|
||||
import atexit
|
||||
import weakref
|
||||
from collections import namedtuple
|
||||
@@ -14,12 +15,16 @@ from itertools import chain
|
||||
from types import FunctionType
|
||||
from threading import RLock
|
||||
|
||||
import pkg_resources
|
||||
|
||||
from .threads import _threads_shutdown
|
||||
from .pins import _pins_shutdown
|
||||
from .mixins import (
|
||||
ValuesMixin,
|
||||
SharedMixin,
|
||||
)
|
||||
from .exc import (
|
||||
BadPinFactory,
|
||||
DeviceClosed,
|
||||
CompositeDeviceBadName,
|
||||
CompositeDeviceBadOrder,
|
||||
@@ -28,26 +33,34 @@ from .exc import (
|
||||
GPIOPinInUse,
|
||||
GPIODeviceClosed,
|
||||
)
|
||||
from .compat import frozendict
|
||||
|
||||
# Get a pin implementation to use as the default; we prefer RPi.GPIO's 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
|
||||
from .pins import _pins_shutdown
|
||||
try:
|
||||
from .pins.rpigpio import RPiGPIOPin
|
||||
DefaultPin = RPiGPIOPin
|
||||
except ImportError:
|
||||
try:
|
||||
from .pins.rpio import RPIOPin
|
||||
DefaultPin = RPIOPin
|
||||
except ImportError:
|
||||
try:
|
||||
from .pins.pigipod import PiGPIOPin
|
||||
DefaultPin = PiGPIOPin
|
||||
except ImportError:
|
||||
from .pins.native import NativePin
|
||||
DefaultPin = NativePin
|
||||
|
||||
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()
|
||||
@@ -263,14 +276,15 @@ class CompositeDevice(Device):
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._all = ()
|
||||
self._named = {}
|
||||
self._named = frozendict({})
|
||||
self._namedtuple = None
|
||||
self._order = kwargs.pop('_order', None)
|
||||
if self._order is None:
|
||||
self._order = sorted(kwargs.keys())
|
||||
else:
|
||||
for missing_name in set(kwargs.keys()) - set(self._order):
|
||||
raise CompositeDeviceBadOrder('%s missing from _order' % missing_name)
|
||||
self._order = tuple(self._order)
|
||||
for missing_name in set(kwargs.keys()) - set(self._order):
|
||||
raise CompositeDeviceBadOrder('%s missing from _order' % missing_name)
|
||||
super(CompositeDevice, self).__init__()
|
||||
for name in set(self._order) & set(dir(self)):
|
||||
raise CompositeDeviceBadName('%s is a reserved name' % name)
|
||||
@@ -278,7 +292,7 @@ class CompositeDevice(Device):
|
||||
for dev in self._all:
|
||||
if not isinstance(dev, Device):
|
||||
raise CompositeDeviceBadDevice("%s doesn't inherit from Device" % dev)
|
||||
self._named = kwargs
|
||||
self._named = frozendict(kwargs)
|
||||
self._namedtuple = namedtuple('%sValue' % self.__class__.__name__, chain(
|
||||
(str(i) for i in range(len(args))), self._order),
|
||||
rename=True)
|
||||
@@ -286,7 +300,7 @@ class CompositeDevice(Device):
|
||||
def __getattr__(self, name):
|
||||
# if _named doesn't exist yet, pretend it's an empty dict
|
||||
if name == '_named':
|
||||
return {}
|
||||
return frozendict({})
|
||||
try:
|
||||
return self._named[name]
|
||||
except KeyError:
|
||||
@@ -303,7 +317,7 @@ class CompositeDevice(Device):
|
||||
self._check_open()
|
||||
return "<gpiozero.%s object containing %d devices: %s and %d unnamed>" % (
|
||||
self.__class__.__name__,
|
||||
len(self), ','.join(self._named),
|
||||
len(self), ','.join(self._order),
|
||||
len(self) - len(self._named)
|
||||
)
|
||||
except DeviceClosed:
|
||||
@@ -365,7 +379,7 @@ class GPIODevice(Device):
|
||||
if pin is None:
|
||||
raise GPIOPinMissing('No pin given')
|
||||
if isinstance(pin, int):
|
||||
pin = DefaultPin(pin)
|
||||
pin = pin_factory(pin)
|
||||
with _PINS_LOCK:
|
||||
if pin in _PINS:
|
||||
raise GPIOPinInUse(
|
||||
@@ -376,9 +390,12 @@ class GPIODevice(Device):
|
||||
self._active_state = True
|
||||
self._inactive_state = False
|
||||
|
||||
def _state_to_value(self, state):
|
||||
return bool(state == self._active_state)
|
||||
|
||||
def _read(self):
|
||||
try:
|
||||
return self.pin.state == self._active_state
|
||||
return self._state_to_value(self.pin.state)
|
||||
except (AttributeError, TypeError):
|
||||
self._check_open()
|
||||
raise
|
||||
|
||||
@@ -22,6 +22,9 @@ class BadWaitTime(GPIOZeroError, ValueError):
|
||||
class BadQueueLen(GPIOZeroError, ValueError):
|
||||
"Error raised when non-positive queue length is specified"
|
||||
|
||||
class BadPinFactory(GPIOZeroError, ImportError):
|
||||
"Error raised when an unknown pin factory name is specified"
|
||||
|
||||
class CompositeDeviceError(GPIOZeroError):
|
||||
"Base class for errors specific to the CompositeDevice hierarchy"
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class InputDevice(GPIODevice):
|
||||
def pull_up(self):
|
||||
"""
|
||||
If ``True``, the device uses a pull-up resistor to set the GPIO pin
|
||||
"high" by default. Defaults to ``False``.
|
||||
"high" by default.
|
||||
"""
|
||||
return self.pin.pull == 'up'
|
||||
|
||||
@@ -71,7 +71,7 @@ class DigitalInputDevice(EventsMixin, InputDevice):
|
||||
straight forward on / off states with (reasonably) clean transitions
|
||||
between the two.
|
||||
|
||||
:param float bouncetime:
|
||||
:param float bounce_time:
|
||||
Specifies the length of time (in seconds) that the component will
|
||||
ignore changes in state after an initial change. This defaults to
|
||||
``None`` which indicates that no bounce compensation will be performed.
|
||||
@@ -162,10 +162,10 @@ class SmoothedInputDevice(EventsMixin, InputDevice):
|
||||
except DeviceClosed:
|
||||
return super(SmoothedInputDevice, self).__repr__()
|
||||
else:
|
||||
if self.partial or self._queue.full.wait(0):
|
||||
if self.partial or self._queue.full.is_set():
|
||||
return super(SmoothedInputDevice, self).__repr__()
|
||||
else:
|
||||
return "<gpiozero.%s object on pin=%d, pull_up=%s>" % (
|
||||
return "<gpiozero.%s object on pin=%r, pull_up=%s>" % (
|
||||
self.__class__.__name__, self.pin, self.pull_up)
|
||||
|
||||
@property
|
||||
@@ -240,8 +240,8 @@ class Button(HoldMixin, DigitalInputDevice):
|
||||
print("The button was pressed!")
|
||||
|
||||
:param int pin:
|
||||
The GPIO pin which the button is attached to. See :doc:`notes` for
|
||||
valid pin numbers.
|
||||
The GPIO pin which the button is attached to. See :ref:`pin_numbering`
|
||||
for valid pin numbers.
|
||||
|
||||
:param bool pull_up:
|
||||
If ``True`` (the default), the GPIO pin will be pulled high by default.
|
||||
@@ -251,16 +251,18 @@ class Button(HoldMixin, DigitalInputDevice):
|
||||
|
||||
:param float bounce_time:
|
||||
If ``None`` (the default), no software bounce compensation will be
|
||||
performed. Otherwise, this is the length in time (in seconds) that the
|
||||
performed. Otherwise, this is the length of time (in seconds) that the
|
||||
component will ignore changes in state after an initial change.
|
||||
|
||||
:param float hold_time:
|
||||
The length of time (in seconds) to wait after the button is pushed,
|
||||
until executing the :attr:`when_held` handler.
|
||||
until executing the :attr:`when_held` handler. Defaults to ``1``.
|
||||
|
||||
:param bool hold_repeat:
|
||||
If ``True``, the :attr:`when_held` handler will be repeatedly executed
|
||||
as long as the device remains active, every *hold_time* seconds.
|
||||
as long as the device remains active, every *hold_time* seconds. If
|
||||
``False`` (the default) the :attr:`when_held` handler will be only be
|
||||
executed once per hold.
|
||||
"""
|
||||
def __init__(
|
||||
self, pin=None, pull_up=True, bounce_time=None,
|
||||
@@ -279,7 +281,7 @@ Button.wait_for_release = Button.wait_for_inactive
|
||||
|
||||
class LineSensor(SmoothedInputDevice):
|
||||
"""
|
||||
Extends :class:`DigitalInputDevice` and represents a single pin line sensor
|
||||
Extends :class:`SmoothedInputDevice` and represents a single pin line sensor
|
||||
like the TCRT5000 infra-red proximity sensor found in the `CamJam #3
|
||||
EduKit`_.
|
||||
|
||||
@@ -300,8 +302,8 @@ class LineSensor(SmoothedInputDevice):
|
||||
pause()
|
||||
|
||||
:param int pin:
|
||||
The GPIO pin which the button is attached to. See :doc:`notes` for
|
||||
valid pin numbers.
|
||||
The GPIO pin which the sensor is attached to. See :ref:`pin_numbering`
|
||||
for valid pin numbers.
|
||||
|
||||
:param int queue_len:
|
||||
The length of the queue used to store values read from the sensor. This
|
||||
@@ -369,8 +371,8 @@ class MotionSensor(SmoothedInputDevice):
|
||||
print("Motion detected!")
|
||||
|
||||
:param int pin:
|
||||
The GPIO pin which the button is attached to. See :doc:`notes` for
|
||||
valid pin numbers.
|
||||
The GPIO pin which the sensor is attached to. See :ref:`pin_numbering`
|
||||
for valid pin numbers.
|
||||
|
||||
:param int queue_len:
|
||||
The length of the queue used to store values read from the sensor. This
|
||||
@@ -418,7 +420,7 @@ class LightSensor(SmoothedInputDevice):
|
||||
Extends :class:`SmoothedInputDevice` and represents a light dependent
|
||||
resistor (LDR).
|
||||
|
||||
Connect one leg of the LDR to the 3V3 pin; connect one leg of a 1µf
|
||||
Connect one leg of the LDR to the 3V3 pin; connect one leg of a 1µF
|
||||
capacitor to a ground pin; connect the other leg of the LDR and the other
|
||||
leg of the capacitor to the same GPIO pin. This class repeatedly discharges
|
||||
the capacitor, then times the duration it takes to charge (which will vary
|
||||
@@ -433,8 +435,8 @@ class LightSensor(SmoothedInputDevice):
|
||||
print("Light detected!")
|
||||
|
||||
:param int pin:
|
||||
The GPIO pin which the button is attached to. See :doc:`notes` for
|
||||
valid pin numbers.
|
||||
The GPIO pin which the sensor is attached to. See :ref:`pin_numbering`
|
||||
for valid pin numbers.
|
||||
|
||||
:param int queue_len:
|
||||
The length of the queue used to store values read from the circuit.
|
||||
@@ -443,7 +445,7 @@ class LightSensor(SmoothedInputDevice):
|
||||
:param float charge_time_limit:
|
||||
If the capacitor in the circuit takes longer than this length of time
|
||||
to charge, it is assumed to be dark. The default (0.01 seconds) is
|
||||
appropriate for a 0.01µf capacitor coupled with the LDR from the
|
||||
appropriate for a 1µF capacitor coupled with the LDR from the
|
||||
`CamJam #2 EduKit`_. You may need to adjust this value for different
|
||||
valued capacitors or LDRs.
|
||||
|
||||
@@ -534,18 +536,18 @@ class DistanceSensor(SmoothedInputDevice):
|
||||
from gpiozero import DistanceSensor
|
||||
from time import sleep
|
||||
|
||||
sensor = DistanceSensor(18, 17)
|
||||
sensor = DistanceSensor(echo=18, trigger=17)
|
||||
while True:
|
||||
print('Distance: ', sensor.distance * 100)
|
||||
sleep(1)
|
||||
|
||||
:param int echo:
|
||||
The GPIO pin which the ECHO pin is attached to. See :doc:`notes` for
|
||||
valid pin numbers.
|
||||
The GPIO pin which the ECHO pin is attached to. See
|
||||
:ref:`pin_numbering` for valid pin numbers.
|
||||
|
||||
:param int trigger:
|
||||
The GPIO pin which the TRIG pin is attached to. See :doc:`notes` for
|
||||
valid pin numbers.
|
||||
The GPIO pin which the TRIG pin is attached to. See
|
||||
:ref:`pin_numbering` for valid pin numbers.
|
||||
|
||||
:param int queue_len:
|
||||
The length of the queue used to store values read from the sensor.
|
||||
@@ -583,11 +585,19 @@ class DistanceSensor(SmoothedInputDevice):
|
||||
self._max_distance = max_distance
|
||||
self._trigger = GPIODevice(trigger)
|
||||
self._echo = Event()
|
||||
self._echo_rise = None
|
||||
self._echo_fall = None
|
||||
self._trigger.pin.function = 'output'
|
||||
self._trigger.pin.state = False
|
||||
self.pin.edges = 'both'
|
||||
self.pin.bounce = None
|
||||
self.pin.when_changed = self._echo.set
|
||||
def callback():
|
||||
if self._echo_rise is None:
|
||||
self._echo_rise = time()
|
||||
else:
|
||||
self._echo_fall = time()
|
||||
self._echo.set()
|
||||
self.pin.when_changed = callback
|
||||
self._queue.start()
|
||||
except:
|
||||
self.close()
|
||||
@@ -615,7 +625,7 @@ class DistanceSensor(SmoothedInputDevice):
|
||||
|
||||
@max_distance.setter
|
||||
def max_distance(self, value):
|
||||
if not (value > 0):
|
||||
if value <= 0:
|
||||
raise ValueError('invalid maximum distance (must be positive)')
|
||||
t = self.threshold_distance
|
||||
self._max_distance = value
|
||||
@@ -670,14 +680,15 @@ class DistanceSensor(SmoothedInputDevice):
|
||||
self._trigger.pin.state = False
|
||||
# Wait up to 1 second for the echo pin to rise
|
||||
if self._echo.wait(1):
|
||||
start = time()
|
||||
self._echo.clear()
|
||||
# Wait up to 40ms for the echo pin to fall (35ms is maximum pulse
|
||||
# time so any longer means something's gone wrong). Calculate
|
||||
# distance as time for echo multiplied by speed of sound divided by
|
||||
# two to compensate for travel to and from the reflector
|
||||
if self._echo.wait(0.04):
|
||||
distance = (time() - start) * self.speed_of_sound / 2.0
|
||||
if self._echo.wait(0.04) and self._echo_fall is not None and self._echo_rise is not None:
|
||||
distance = (self._echo_fall - self._echo_rise) * self.speed_of_sound / 2.0
|
||||
self._echo_fall = None
|
||||
self._echo_rise = None
|
||||
return min(1.0, distance / self._max_distance)
|
||||
else:
|
||||
# If we only saw one edge it means we missed the echo because
|
||||
|
||||
@@ -234,7 +234,7 @@ class EventsMixin(object):
|
||||
The length of time (in seconds) that the device has been active for.
|
||||
When the device is inactive, this is ``None``.
|
||||
"""
|
||||
if self._active_event.wait(0):
|
||||
if self._active_event.is_set():
|
||||
return time() - self._last_changed
|
||||
else:
|
||||
return None
|
||||
@@ -245,7 +245,7 @@ class EventsMixin(object):
|
||||
The length of time (in seconds) that the device has been inactive for.
|
||||
When the device is active, this is ``None``.
|
||||
"""
|
||||
if self._inactive_event.wait(0):
|
||||
if self._inactive_event.is_set():
|
||||
return time() - self._last_changed
|
||||
else:
|
||||
return None
|
||||
@@ -434,11 +434,11 @@ class HoldThread(GPIOThread):
|
||||
self.start()
|
||||
|
||||
def held(self, parent):
|
||||
while not self.stopping.wait(0):
|
||||
while not self.stopping.is_set():
|
||||
if self.holding.wait(0.1):
|
||||
self.holding.clear()
|
||||
while not (
|
||||
self.stopping.wait(0) or
|
||||
self.stopping.is_set() or
|
||||
parent._inactive_event.wait(parent.hold_time)
|
||||
):
|
||||
if parent._held_from is None:
|
||||
|
||||
@@ -73,6 +73,81 @@ class PingServer(InternalDevice):
|
||||
return True
|
||||
|
||||
|
||||
class CPUTemperature(InternalDevice):
|
||||
"""
|
||||
Extends :class:`InternalDevice` to provide a device which is active when
|
||||
the CPU temperature exceeds the *threshold* value.
|
||||
|
||||
The following example plots the CPU's temperature on an LED bar graph::
|
||||
|
||||
from gpiozero import LEDBarGraph, CPUTemperature
|
||||
from signal import pause
|
||||
|
||||
# Use minimums and maximums that are closer to "normal" usage so the
|
||||
# bar graph is a bit more "lively"
|
||||
temp = CPUTemperature(min_temp=50, max_temp=90)
|
||||
graph = LEDBarGraph(5, 6, 13, 19, 25, pwm=True)
|
||||
graph.source = temp.values
|
||||
pause()
|
||||
|
||||
:param str sensor_file:
|
||||
The file from which to read the temperature. This defaults to the
|
||||
sysfs file :file:`/sys/class/thermal/thermal_zone0/temp`. Whatever
|
||||
file is specified is expected to contain a single line containing the
|
||||
temperature in milli-degrees celsius.
|
||||
|
||||
:param float min_temp:
|
||||
The temperature at which :attr:`value` will read 0.0. This defaults to
|
||||
0.0.
|
||||
|
||||
:param float max_temp:
|
||||
The temperature at which :attr:`value` will read 1.0. This defaults to
|
||||
100.0.
|
||||
|
||||
:param float threshold:
|
||||
The temperature above which the device will be considered "active".
|
||||
This defaults to 80.0.
|
||||
"""
|
||||
def __init__(self, sensor_file='/sys/class/thermal/thermal_zone0/temp',
|
||||
min_temp=0.0, max_temp=100.0, threshold=80.0):
|
||||
self.sensor_file = sensor_file
|
||||
super(CPUTemperature, self).__init__()
|
||||
self.min_temp = min_temp
|
||||
self.max_temp = max_temp
|
||||
self.threshold = threshold
|
||||
self._fire_events()
|
||||
|
||||
def __repr__(self):
|
||||
return '<gpiozero.CPUTemperature temperature=%.2f>' % self.temperature
|
||||
|
||||
@property
|
||||
def temperature(self):
|
||||
"""
|
||||
Returns the current CPU temperature in degrees celsius.
|
||||
"""
|
||||
with io.open(self.sensor_file, 'r') as f:
|
||||
return float(f.readline().strip()) / 1000
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
"""
|
||||
Returns the current CPU temperature as a value between 0.0
|
||||
(representing the *min_temp* value) and 1.0 (representing the
|
||||
*max_temp* value). These default to 0.0 and 100.0 respectively, hence
|
||||
:attr:`value` is :attr:`temperature` divided by 100 by default.
|
||||
"""
|
||||
temp_range = self.max_temp - self.min_temp
|
||||
return (self.temperature - self.min_temp) / temp_range
|
||||
|
||||
@property
|
||||
def is_active(self):
|
||||
"""
|
||||
Returns ``True`` when the CPU :attr:`temperature` exceeds the
|
||||
:attr:`threshold`.
|
||||
"""
|
||||
return self.temperature > self.threshold
|
||||
|
||||
|
||||
class TimeOfDay(InternalDevice):
|
||||
"""
|
||||
Extends :class:`InternalDevice` to provide a device which is active when
|
||||
|
||||
@@ -43,16 +43,15 @@ class OutputDevice(SourceMixin, GPIODevice):
|
||||
self.active_high = active_high
|
||||
if initial_value is None:
|
||||
self.pin.function = 'output'
|
||||
elif initial_value:
|
||||
self.pin.output_with_state(self._active_state)
|
||||
else:
|
||||
self.pin.output_with_state(self._inactive_state)
|
||||
self.pin.output_with_state(self._value_to_state(initial_value))
|
||||
|
||||
def _value_to_state(self, value):
|
||||
return bool(self._active_state if value else self._inactive_state)
|
||||
|
||||
def _write(self, value):
|
||||
if not self.active_high:
|
||||
value = not value
|
||||
try:
|
||||
self.pin.state = bool(value)
|
||||
self.pin.state = self._value_to_state(value)
|
||||
except AttributeError:
|
||||
self._check_open()
|
||||
raise
|
||||
@@ -218,8 +217,8 @@ class LED(DigitalOutputDevice):
|
||||
led.on()
|
||||
|
||||
:param int pin:
|
||||
The GPIO pin which the LED is attached to. See :doc:`notes` for valid
|
||||
pin numbers.
|
||||
The GPIO pin which the LED is attached to. See :ref:`pin_numbering` for
|
||||
valid pin numbers.
|
||||
|
||||
:param bool active_high:
|
||||
If ``True`` (the default), the LED will operate normally with the
|
||||
@@ -253,8 +252,8 @@ class Buzzer(DigitalOutputDevice):
|
||||
bz.on()
|
||||
|
||||
:param int pin:
|
||||
The GPIO pin which the buzzer is attached to. See :doc:`notes` for
|
||||
valid pin numbers.
|
||||
The GPIO pin which the buzzer is attached to. See :ref:`pin_numbering`
|
||||
for valid pin numbers.
|
||||
|
||||
:param bool active_high:
|
||||
If ``True`` (the default), the buzzer will operate normally with the
|
||||
@@ -277,15 +276,15 @@ class PWMOutputDevice(OutputDevice):
|
||||
Generic output device configured for pulse-width modulation (PWM).
|
||||
|
||||
:param int pin:
|
||||
The GPIO pin which the device is attached to. See :doc:`notes` for
|
||||
valid pin numbers.
|
||||
The GPIO pin which the device is attached to. See :ref:`pin_numbering`
|
||||
for valid pin numbers.
|
||||
|
||||
:param bool active_high:
|
||||
If ``True`` (the default), the :meth:`on` method will set the GPIO to
|
||||
HIGH. If ``False``, the :meth:`on` method will set the GPIO to LOW (the
|
||||
:meth:`off` method always does the opposite).
|
||||
|
||||
:param bool initial_value:
|
||||
:param float initial_value:
|
||||
If ``0`` (the default), the device's duty cycle will be 0 initially.
|
||||
Other values between 0 and 1 can be specified as an initial duty cycle.
|
||||
Note that ``None`` cannot be specified (unlike the parent class) as
|
||||
@@ -300,7 +299,7 @@ class PWMOutputDevice(OutputDevice):
|
||||
self._controller = None
|
||||
if not 0 <= initial_value <= 1:
|
||||
raise OutputDeviceBadValue("initial_value must be between 0 and 1")
|
||||
super(PWMOutputDevice, self).__init__(pin, active_high)
|
||||
super(PWMOutputDevice, self).__init__(pin, active_high, initial_value=None)
|
||||
try:
|
||||
# XXX need a way of setting these together
|
||||
self.pin.frequency = frequency
|
||||
@@ -318,23 +317,16 @@ class PWMOutputDevice(OutputDevice):
|
||||
pass
|
||||
super(PWMOutputDevice, self).close()
|
||||
|
||||
def _read(self):
|
||||
self._check_open()
|
||||
if self.active_high:
|
||||
return self.pin.state
|
||||
else:
|
||||
return 1 - self.pin.state
|
||||
def _state_to_value(self, state):
|
||||
return float(state if self.active_high else 1 - state)
|
||||
|
||||
def _value_to_state(self, value):
|
||||
return float(value if self.active_high else 1 - value)
|
||||
|
||||
def _write(self, value):
|
||||
if not self.active_high:
|
||||
value = 1 - value
|
||||
if not 0 <= value <= 1:
|
||||
raise OutputDeviceBadValue("PWM value must be between 0 and 1")
|
||||
try:
|
||||
self.pin.state = value
|
||||
except AttributeError:
|
||||
self._check_open()
|
||||
raise
|
||||
super(PWMOutputDevice, self)._write(value)
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
@@ -435,12 +427,12 @@ class PWMOutputDevice(OutputDevice):
|
||||
Number of seconds to spend fading out. Defaults to 1.
|
||||
|
||||
:param int n:
|
||||
Number of times to blink; ``None`` (the default) means forever.
|
||||
Number of times to pulse; ``None`` (the default) means forever.
|
||||
|
||||
:param bool background:
|
||||
If ``True`` (the default), start a background thread to continue
|
||||
blinking and return immediately. If ``False``, only return when the
|
||||
blink is finished (warning: the default value of *n* will result in
|
||||
pulsing and return immediately. If ``False``, only return when the
|
||||
pulse is finished (warning: the default value of *n* will result in
|
||||
this method never returning).
|
||||
"""
|
||||
on_time = off_time = 0
|
||||
@@ -491,7 +483,7 @@ class PWMLED(PWMOutputDevice):
|
||||
an optional resistor to prevent the LED from burning out.
|
||||
|
||||
:param int pin:
|
||||
The GPIO pin which the LED is attached to. See :doc:`notes` for
|
||||
The GPIO pin which the LED is attached to. See :ref:`pin_numbering` for
|
||||
valid pin numbers.
|
||||
|
||||
:param bool active_high:
|
||||
@@ -499,7 +491,7 @@ class PWMLED(PWMOutputDevice):
|
||||
HIGH. If ``False``, the :meth:`on` method will set the GPIO to LOW (the
|
||||
:meth:`off` method always does the opposite).
|
||||
|
||||
:param bool initial_value:
|
||||
:param float initial_value:
|
||||
If ``0`` (the default), the LED will be off initially. Other values
|
||||
between 0 and 1 can be specified as an initial brightness for the LED.
|
||||
Note that ``None`` cannot be specified (unlike the parent class) as
|
||||
@@ -553,18 +545,24 @@ class RGBLED(SourceMixin, Device):
|
||||
Set to ``True`` (the default) for common cathode RGB LEDs. If you are
|
||||
using a common anode RGB LED, set this to ``False``.
|
||||
|
||||
:param bool initial_value:
|
||||
The initial color for the LED. Defaults to black ``(0, 0, 0)``.
|
||||
:param tuple initial_value:
|
||||
The initial color for the RGB LED. Defaults to black ``(0, 0, 0)``.
|
||||
|
||||
:param bool pwm:
|
||||
If ``True`` (the default), construct :class:`PWMLED` instances for
|
||||
each component of the RGBLED. If ``False``, construct regular
|
||||
:class:`LED` instances, which prevents smooth color graduations.
|
||||
"""
|
||||
def __init__(
|
||||
self, red=None, green=None, blue=None, active_high=True,
|
||||
initial_value=(0, 0, 0)):
|
||||
initial_value=(0, 0, 0), pwm=True):
|
||||
self._leds = ()
|
||||
self._blink_thread = None
|
||||
if not all([red, green, blue]):
|
||||
if not all(p is not None for p in [red, green, blue]):
|
||||
raise GPIOPinMissing('red, green, and blue pins must be provided')
|
||||
LEDClass = PWMLED if pwm else LED
|
||||
super(RGBLED, self).__init__()
|
||||
self._leds = tuple(PWMLED(pin, active_high) for pin in (red, green, blue))
|
||||
self._leds = tuple(LEDClass(pin, active_high) for pin in (red, green, blue))
|
||||
self.value = initial_value
|
||||
|
||||
red = _led_property(0)
|
||||
@@ -581,13 +579,14 @@ class RGBLED(SourceMixin, Device):
|
||||
|
||||
@property
|
||||
def closed(self):
|
||||
return bool(self._leds)
|
||||
return len(self._leds) == 0
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
"""
|
||||
Represents the color of the LED as an RGB 3-tuple of ``(red, green,
|
||||
blue)`` where each value is between 0 and 1.
|
||||
blue)`` where each value is between 0 and 1 if ``pwm`` was ``True``
|
||||
when the class was constructed (and only 0 or 1 if not).
|
||||
|
||||
For example, purple would be ``(1, 0, 1)`` and yellow would be ``(1, 1,
|
||||
0)``, while orange would be ``(1, 0.5, 0)``.
|
||||
@@ -596,6 +595,12 @@ class RGBLED(SourceMixin, Device):
|
||||
|
||||
@value.setter
|
||||
def value(self, value):
|
||||
for component in value:
|
||||
if not 0 <= component <= 1:
|
||||
raise OutputDeviceBadValue('each RGB color component must be between 0 and 1')
|
||||
if isinstance(self._leds[0], LED):
|
||||
if component not in (0, 1):
|
||||
raise OutputDeviceBadValue('each RGB color component must be 0 or 1 with non-PWM RGBLEDs')
|
||||
self._stop_blink()
|
||||
self.red, self.green, self.blue = value
|
||||
|
||||
@@ -647,10 +652,14 @@ class RGBLED(SourceMixin, Device):
|
||||
Number of seconds off. Defaults to 1 second.
|
||||
|
||||
:param float fade_in_time:
|
||||
Number of seconds to spend fading in. Defaults to 0.
|
||||
Number of seconds to spend fading in. Defaults to 0. Must be 0 if
|
||||
``pwm`` was ``False`` when the class was constructed
|
||||
(:exc:`ValueError` will be raised if not).
|
||||
|
||||
:param float fade_out_time:
|
||||
Number of seconds to spend fading out. Defaults to 0.
|
||||
Number of seconds to spend fading out. Defaults to 0. Must be 0 if
|
||||
``pwm`` was ``False`` when the class was constructed
|
||||
(:exc:`ValueError` will be raised if not).
|
||||
|
||||
:param tuple on_color:
|
||||
The color to use when the LED is "on". Defaults to white.
|
||||
@@ -667,16 +676,57 @@ class RGBLED(SourceMixin, Device):
|
||||
blink is finished (warning: the default value of *n* will result in
|
||||
this method never returning).
|
||||
"""
|
||||
if isinstance(self._leds[0], LED):
|
||||
if fade_in_time:
|
||||
raise ValueError('fade_in_time must be 0 with non-PWM RGBLEDs')
|
||||
if fade_out_time:
|
||||
raise ValueError('fade_out_time must be 0 with non-PWM RGBLEDs')
|
||||
self._stop_blink()
|
||||
self._blink_thread = GPIOThread(
|
||||
target=self._blink_device,
|
||||
args=(on_time, off_time, fade_in_time, fade_out_time, on_color, off_color, n)
|
||||
args=(
|
||||
on_time, off_time, fade_in_time, fade_out_time,
|
||||
on_color, off_color, n
|
||||
)
|
||||
)
|
||||
self._blink_thread.start()
|
||||
if not background:
|
||||
self._blink_thread.join()
|
||||
self._blink_thread = None
|
||||
|
||||
def pulse(
|
||||
self, fade_in_time=1, fade_out_time=1,
|
||||
on_color=(1, 1, 1), off_color=(0, 0, 0), n=None, background=True):
|
||||
"""
|
||||
Make the device fade in and out repeatedly.
|
||||
|
||||
:param float fade_in_time:
|
||||
Number of seconds to spend fading in. Defaults to 1.
|
||||
|
||||
:param float fade_out_time:
|
||||
Number of seconds to spend fading out. Defaults to 1.
|
||||
|
||||
:param tuple on_color:
|
||||
The color to use when the LED is "on". Defaults to white.
|
||||
|
||||
:param tuple off_color:
|
||||
The color to use when the LED is "off". Defaults to black.
|
||||
|
||||
:param int n:
|
||||
Number of times to pulse; ``None`` (the default) means forever.
|
||||
|
||||
:param bool background:
|
||||
If ``True`` (the default), start a background thread to continue
|
||||
pulsing and return immediately. If ``False``, only return when the
|
||||
pulse is finished (warning: the default value of *n* will result in
|
||||
this method never returning).
|
||||
"""
|
||||
on_time = off_time = 0
|
||||
self.blink(
|
||||
on_time, off_time, fade_in_time, fade_out_time,
|
||||
on_color, off_color, n, background
|
||||
)
|
||||
|
||||
def _stop_blink(self, led=None):
|
||||
# If this is called with a single led, we stop all blinking anyway
|
||||
if self._blink_thread:
|
||||
@@ -746,22 +796,31 @@ class Motor(SourceMixin, CompositeDevice):
|
||||
:param int backward:
|
||||
The GPIO pin that the backward input of the motor driver chip is
|
||||
connected to.
|
||||
|
||||
:param bool pwm:
|
||||
If ``True`` (the default), construct :class:`PWMOutputDevice`
|
||||
instances for the motor controller pins, allowing both direction and
|
||||
variable speed control. If ``False``, construct
|
||||
:class:`DigitalOutputDevice` instances, allowing only direction
|
||||
control.
|
||||
"""
|
||||
def __init__(self, forward=None, backward=None):
|
||||
if not all([forward, backward]):
|
||||
def __init__(self, forward=None, backward=None, pwm=True):
|
||||
if not all(p is not None for p in [forward, backward]):
|
||||
raise GPIOPinMissing(
|
||||
'forward and backward pins must be provided'
|
||||
)
|
||||
PinClass = PWMOutputDevice if pwm else DigitalOutputDevice
|
||||
super(Motor, self).__init__(
|
||||
forward_device=PWMOutputDevice(forward),
|
||||
backward_device=PWMOutputDevice(backward),
|
||||
forward_device=PinClass(forward),
|
||||
backward_device=PinClass(backward),
|
||||
_order=('forward_device', 'backward_device'))
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
"""
|
||||
Represents the speed of the motor as a floating point value between -1
|
||||
(full speed backward) and 1 (full speed forward).
|
||||
(full speed backward) and 1 (full speed forward), with 0 representing
|
||||
stopped.
|
||||
"""
|
||||
return self.forward_device.value - self.backward_device.value
|
||||
|
||||
@@ -770,9 +829,15 @@ class Motor(SourceMixin, CompositeDevice):
|
||||
if not -1 <= value <= 1:
|
||||
raise OutputDeviceBadValue("Motor value must be between -1 and 1")
|
||||
if value > 0:
|
||||
self.forward(value)
|
||||
try:
|
||||
self.forward(value)
|
||||
except ValueError as e:
|
||||
raise OutputDeviceBadValue(e)
|
||||
elif value < 0:
|
||||
self.backward(-value)
|
||||
try:
|
||||
self.backward(-value)
|
||||
except ValueError as e:
|
||||
raise OutputDeviceBadValue(e)
|
||||
else:
|
||||
self.stop()
|
||||
|
||||
@@ -790,8 +855,14 @@ class Motor(SourceMixin, CompositeDevice):
|
||||
|
||||
:param float speed:
|
||||
The speed at which the motor should turn. Can be any value between
|
||||
0 (stopped) and the default 1 (maximum speed).
|
||||
0 (stopped) and the default 1 (maximum speed) if ``pwm`` was
|
||||
``True`` when the class was constructed (and only 0 or 1 if not).
|
||||
"""
|
||||
if not 0 <= speed <= 1:
|
||||
raise ValueError('forward speed must be between 0 and 1')
|
||||
if isinstance(self.forward_device, DigitalOutputDevice):
|
||||
if speed not in (0, 1):
|
||||
raise ValueError('forward speed must be 0 or 1 with non-PWM Motors')
|
||||
self.backward_device.off()
|
||||
self.forward_device.value = speed
|
||||
|
||||
@@ -801,8 +872,14 @@ class Motor(SourceMixin, CompositeDevice):
|
||||
|
||||
:param float speed:
|
||||
The speed at which the motor should turn. Can be any value between
|
||||
0 (stopped) and the default 1 (maximum speed).
|
||||
0 (stopped) and the default 1 (maximum speed) if ``pwm`` was
|
||||
``True`` when the class was constructed (and only 0 or 1 if not).
|
||||
"""
|
||||
if not 0 <= speed <= 1:
|
||||
raise ValueError('backward speed must be between 0 and 1')
|
||||
if isinstance(self.backward_device, DigitalOutputDevice):
|
||||
if speed not in (0, 1):
|
||||
raise ValueError('backward speed must be 0 or 1 with non-PWM Motors')
|
||||
self.forward_device.off()
|
||||
self.backward_device.value = speed
|
||||
|
||||
@@ -820,3 +897,314 @@ class Motor(SourceMixin, CompositeDevice):
|
||||
"""
|
||||
self.forward_device.off()
|
||||
self.backward_device.off()
|
||||
|
||||
|
||||
class Servo(SourceMixin, CompositeDevice):
|
||||
"""
|
||||
Extends :class:`CompositeDevice` and represents a PWM-controlled servo
|
||||
motor connected to a GPIO pin.
|
||||
|
||||
Connect a power source (e.g. a battery pack or the 5V pin) to the power
|
||||
cable of the servo (this is typically colored red); connect the ground
|
||||
cable of the servo (typically colored black or brown) to the negative of
|
||||
your battery pack, or a GND pin; connect the final cable (typically colored
|
||||
white or orange) to the GPIO pin you wish to use for controlling the servo.
|
||||
|
||||
The following code will make the servo move between its minimum, maximum,
|
||||
and mid-point positions with a pause between each::
|
||||
|
||||
from gpiozero import Servo
|
||||
from time import sleep
|
||||
|
||||
servo = Servo(17)
|
||||
while True:
|
||||
servo.min()
|
||||
sleep(1)
|
||||
servo.mid()
|
||||
sleep(1)
|
||||
servo.max()
|
||||
sleep(1)
|
||||
|
||||
:param int pin:
|
||||
The GPIO pin which the device is attached to. See :ref:`pin_numbering`
|
||||
for valid pin numbers.
|
||||
|
||||
:param float initial_value:
|
||||
If ``0`` (the default), the device's mid-point will be set
|
||||
initially. Other values between -1 and +1 can be specified as an
|
||||
initial position. ``None`` means to start the servo un-controlled (see
|
||||
:attr:`value`).
|
||||
|
||||
:param float min_pulse_width:
|
||||
The pulse width corresponding to the servo's minimum position. This
|
||||
defaults to 1ms.
|
||||
|
||||
:param float max_pulse_width:
|
||||
The pulse width corresponding to the servo's maximum position. This
|
||||
defaults to 2ms.
|
||||
|
||||
:param float frame_width:
|
||||
The length of time between servo control pulses measured in seconds.
|
||||
This defaults to 20ms which is a common value for servos.
|
||||
"""
|
||||
def __init__(
|
||||
self, pin=None, initial_value=0.0,
|
||||
min_pulse_width=1/1000, max_pulse_width=2/1000,
|
||||
frame_width=20/1000):
|
||||
if min_pulse_width >= max_pulse_width:
|
||||
raise ValueError('min_pulse_width must be less than max_pulse_width')
|
||||
if max_pulse_width >= frame_width:
|
||||
raise ValueError('max_pulse_width must be less than frame_width')
|
||||
self._frame_width = frame_width
|
||||
self._min_dc = min_pulse_width / frame_width
|
||||
self._dc_range = (max_pulse_width - min_pulse_width) / frame_width
|
||||
self._min_value = -1
|
||||
self._value_range = 2
|
||||
super(Servo, self).__init__(
|
||||
pwm_device=PWMOutputDevice(pin, frequency=int(1 / frame_width)))
|
||||
try:
|
||||
self.value = initial_value
|
||||
except:
|
||||
self.close()
|
||||
raise
|
||||
|
||||
@property
|
||||
def frame_width(self):
|
||||
"""
|
||||
The time between control pulses, measured in seconds.
|
||||
"""
|
||||
return self._frame_width
|
||||
|
||||
@property
|
||||
def min_pulse_width(self):
|
||||
"""
|
||||
The control pulse width corresponding to the servo's minimum position,
|
||||
measured in seconds.
|
||||
"""
|
||||
return self._min_dc * self.frame_width
|
||||
|
||||
@property
|
||||
def max_pulse_width(self):
|
||||
"""
|
||||
The control pulse width corresponding to the servo's maximum position,
|
||||
measured in seconds.
|
||||
"""
|
||||
return (self._dc_range * self.frame_width) + self.min_pulse_width
|
||||
|
||||
@property
|
||||
def pulse_width(self):
|
||||
"""
|
||||
Returns the current pulse width controlling the servo.
|
||||
"""
|
||||
if self.pwm_device.pin.frequency is None:
|
||||
return None
|
||||
else:
|
||||
return self.pwm_device.pin.state * self.frame_width
|
||||
|
||||
def min(self):
|
||||
"""
|
||||
Set the servo to its minimum position.
|
||||
"""
|
||||
self.value = -1
|
||||
|
||||
def mid(self):
|
||||
"""
|
||||
Set the servo to its mid-point position.
|
||||
"""
|
||||
self.value = 0
|
||||
|
||||
def max(self):
|
||||
"""
|
||||
Set the servo to its maximum position.
|
||||
"""
|
||||
self.value = 1
|
||||
|
||||
def detach(self):
|
||||
"""
|
||||
Temporarily disable control of the servo. This is equivalent to
|
||||
setting :attr:`value` to ``None``.
|
||||
"""
|
||||
self.value = None
|
||||
|
||||
def _get_value(self):
|
||||
if self.pwm_device.pin.frequency is None:
|
||||
return None
|
||||
else:
|
||||
return (
|
||||
((self.pwm_device.pin.state - self._min_dc) / self._dc_range) *
|
||||
self._value_range + self._min_value)
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
"""
|
||||
Represents the position of the servo as a value between -1 (the minimum
|
||||
position) and +1 (the maximum position). This can also be the special
|
||||
value ``None`` indicating that the servo is currently "uncontrolled",
|
||||
i.e. that no control signal is being sent. Typically this means the
|
||||
servo's position remains unchanged, but that it can be moved by hand.
|
||||
"""
|
||||
result = self._get_value()
|
||||
if result is None:
|
||||
return result
|
||||
else:
|
||||
# NOTE: This round() only exists to ensure we don't confuse people
|
||||
# by returning 2.220446049250313e-16 as the default initial value
|
||||
# instead of 0. The reason _get_value and _set_value are split
|
||||
# out is for descendents that require the un-rounded values for
|
||||
# accuracy
|
||||
return round(result, 14)
|
||||
|
||||
@value.setter
|
||||
def value(self, value):
|
||||
if value is None:
|
||||
self.pwm_device.pin.frequency = None
|
||||
elif -1 <= value <= 1:
|
||||
self.pwm_device.pin.frequency = int(1 / self.frame_width)
|
||||
self.pwm_device.pin.state = (
|
||||
self._min_dc + self._dc_range *
|
||||
((value - self._min_value) / self._value_range)
|
||||
)
|
||||
else:
|
||||
raise OutputDeviceBadValue(
|
||||
"Servo value must be between -1 and 1, or None")
|
||||
|
||||
@property
|
||||
def is_active(self):
|
||||
return self.value is not None
|
||||
|
||||
|
||||
class AngularServo(Servo):
|
||||
"""
|
||||
Extends :class:`Servo` and represents a rotational PWM-controlled servo
|
||||
motor which can be set to particular angles (assuming valid minimum and
|
||||
maximum angles are provided to the constructor).
|
||||
|
||||
Connect a power source (e.g. a battery pack or the 5V pin) to the power
|
||||
cable of the servo (this is typically colored red); connect the ground
|
||||
cable of the servo (typically colored black or brown) to the negative of
|
||||
your battery pack, or a GND pin; connect the final cable (typically colored
|
||||
white or orange) to the GPIO pin you wish to use for controlling the servo.
|
||||
|
||||
Next, calibrate the angles that the servo can rotate to. In an interactive
|
||||
Python session, construct a :class:`Servo` instance. The servo should move
|
||||
to its mid-point by default. Set the servo to its minimum value, and
|
||||
measure the angle from the mid-point. Set the servo to its maximum value,
|
||||
and again measure the angle::
|
||||
|
||||
>>> from gpiozero import Servo
|
||||
>>> s = Servo(17)
|
||||
>>> s.min() # measure the angle
|
||||
>>> s.max() # measure the angle
|
||||
|
||||
You should now be able to construct an :class:`AngularServo` instance
|
||||
with the correct bounds::
|
||||
|
||||
>>> from gpiozero import AngularServo
|
||||
>>> s = AngularServo(17, min_angle=-42, max_angle=44)
|
||||
>>> s.angle = 0.0
|
||||
>>> s.angle
|
||||
0.0
|
||||
>>> s.angle = 15
|
||||
>>> s.angle
|
||||
15.0
|
||||
|
||||
.. note::
|
||||
|
||||
You can set *min_angle* greater than *max_angle* if you wish to reverse
|
||||
the sense of the angles (e.g. ``min_angle=45, max_angle=-45``). This
|
||||
can be useful with servos that rotate in the opposite direction to your
|
||||
expectations of minimum and maximum.
|
||||
|
||||
:param int pin:
|
||||
The GPIO pin which the device is attached to. See :ref:`pin_numbering`
|
||||
for valid pin numbers.
|
||||
|
||||
:param float initial_angle:
|
||||
Sets the servo's initial angle to the specified value. The default is
|
||||
0. The value specified must be between *min_angle* and *max_angle*
|
||||
inclusive. ``None`` means to start the servo un-controlled (see
|
||||
:attr:`value`).
|
||||
|
||||
:param float min_angle:
|
||||
Sets the minimum angle that the servo can rotate to. This defaults to
|
||||
-90, but should be set to whatever you measure from your servo during
|
||||
calibration.
|
||||
|
||||
:param float max_angle:
|
||||
Sets the maximum angle that the servo can rotate to. This defaults to
|
||||
90, but should be set to whatever you measure from your servo during
|
||||
calibration.
|
||||
|
||||
:param float min_pulse_width:
|
||||
The pulse width corresponding to the servo's minimum position. This
|
||||
defaults to 1ms.
|
||||
|
||||
:param float max_pulse_width:
|
||||
The pulse width corresponding to the servo's maximum position. This
|
||||
defaults to 2ms.
|
||||
|
||||
:param float frame_width:
|
||||
The length of time between servo control pulses measured in seconds.
|
||||
This defaults to 20ms which is a common value for servos.
|
||||
"""
|
||||
def __init__(
|
||||
self, pin=None, initial_angle=0.0,
|
||||
min_angle=-90, max_angle=90,
|
||||
min_pulse_width=1/1000, max_pulse_width=2/1000,
|
||||
frame_width=20/1000):
|
||||
self._min_angle = min_angle
|
||||
self._angular_range = max_angle - min_angle
|
||||
initial_value = 2 * ((initial_angle - min_angle) / self._angular_range) - 1
|
||||
super(AngularServo, self).__init__(
|
||||
pin, initial_value, min_pulse_width, max_pulse_width, frame_width)
|
||||
|
||||
@property
|
||||
def min_angle(self):
|
||||
"""
|
||||
The minimum angle that the servo will rotate to when :meth:`min` is
|
||||
called.
|
||||
"""
|
||||
return self._min_angle
|
||||
|
||||
@property
|
||||
def max_angle(self):
|
||||
"""
|
||||
The maximum angle that the servo will rotate to when :meth:`max` is
|
||||
called.
|
||||
"""
|
||||
return self._min_angle + self._angular_range
|
||||
|
||||
@property
|
||||
def angle(self):
|
||||
"""
|
||||
The position of the servo as an angle measured in degrees. This will
|
||||
only be accurate if *min_angle* and *max_angle* have been set
|
||||
appropriately in the constructor.
|
||||
|
||||
This can also be the special value ``None`` indicating that the servo
|
||||
is currently "uncontrolled", i.e. that no control signal is being sent.
|
||||
Typically this means the servo's position remains unchanged, but that
|
||||
it can be moved by hand.
|
||||
"""
|
||||
result = self._get_value()
|
||||
if result is None:
|
||||
return None
|
||||
else:
|
||||
# NOTE: Why round(n, 12) here instead of 14? Angle ranges can be
|
||||
# much larger than -1..1 so we need a little more rounding to
|
||||
# smooth off the rough corners!
|
||||
return round(
|
||||
self._angular_range *
|
||||
((result - self._min_value) / self._value_range) +
|
||||
self._min_angle, 12)
|
||||
|
||||
@angle.setter
|
||||
def angle(self, value):
|
||||
if value is None:
|
||||
self.value = None
|
||||
else:
|
||||
self.value = (
|
||||
self._value_range *
|
||||
((value - self._min_angle) / self._angular_range) +
|
||||
self._min_value)
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@ from __future__ import (
|
||||
)
|
||||
str = type('')
|
||||
|
||||
import io
|
||||
|
||||
from .data import pi_info
|
||||
from ..exc import (
|
||||
PinInvalidFunction,
|
||||
PinSetInput,
|
||||
@@ -47,6 +50,7 @@ class Pin(object):
|
||||
* :meth:`_set_edges`
|
||||
* :meth:`_get_when_changed`
|
||||
* :meth:`_set_when_changed`
|
||||
* :meth:`pi_info`
|
||||
* :meth:`output_with_state`
|
||||
* :meth:`input_with_pull`
|
||||
|
||||
@@ -243,3 +247,48 @@ class Pin(object):
|
||||
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
|
||||
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
|
||||
the base class for local-only pin interfaces (:class:`RPiGPIOPin`,
|
||||
:class:`RPIOPin`, and :class:`NativePin`).
|
||||
"""
|
||||
_PI_REVISION = None
|
||||
|
||||
@classmethod
|
||||
def pi_info(cls):
|
||||
"""
|
||||
Returns a :class:`PiBoardInfo` instance representing the local Pi.
|
||||
The Pi's revision is determined by reading :file:`/proc/cpuinfo`. If
|
||||
no valid revision is found, returns ``None``.
|
||||
"""
|
||||
# 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)
|
||||
if cls._PI_REVISION is None:
|
||||
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:]
|
||||
cls._PI_REVISION = revision
|
||||
break
|
||||
if cls._PI_REVISION is None:
|
||||
return None # something weird going on
|
||||
return pi_info(cls._PI_REVISION)
|
||||
|
||||
|
||||
@@ -243,29 +243,29 @@ CM_SODIMM = {
|
||||
|
||||
PI_REVISIONS = {
|
||||
# rev model pcb_rev released soc manufacturer ram storage usb eth wifi bt csi dsi headers
|
||||
'beta': ('B', '?', '2012Q1', 'BCM2835', '?', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV1_P1}, ),
|
||||
'0002': ('B', '1.0', '2012Q1', 'BCM2835', 'Egoman', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV1_P1}, ),
|
||||
'0003': ('B', '1.0', '2012Q3', 'BCM2835', 'Egoman', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV1_P1}, ),
|
||||
'0004': ('B', '2.0', '2012Q3', 'BCM2835', 'Sony', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
|
||||
'0005': ('B', '2.0', '2012Q4', 'BCM2835', 'Qisda', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
|
||||
'0006': ('B', '2.0', '2012Q4', 'BCM2835', 'Egoman', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
|
||||
'0007': ('A', '2.0', '2013Q1', 'BCM2835', 'Egoman', 256, 'SD', 1, 0, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
|
||||
'0008': ('A', '2.0', '2013Q1', 'BCM2835', 'Sony', 256, 'SD', 1, 0, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
|
||||
'0009': ('A', '2.0', '2013Q1', 'BCM2835', 'Qisda', 256, 'SD', 1, 0, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
|
||||
'000d': ('B', '2.0', '2012Q4', 'BCM2835', 'Egoman', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
|
||||
'000e': ('B', '2.0', '2012Q4', 'BCM2835', 'Sony', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
|
||||
'000f': ('B', '2.0', '2012Q4', 'BCM2835', 'Egoman', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
|
||||
'0010': ('B+', '1.2', '2014Q3', 'BCM2835', 'Sony', 512, 'MicroSD', 4, 1, False, False, 1, 1, {'P1': PLUS_P1}, ),
|
||||
'0011': ('CM', '1.2', '2014Q2', 'BCM2835', 'Sony', 512, 'eMMC', 0, 0, False, False, 2, 2, {'SODIMM': CM_SODIMM}, ),
|
||||
'0012': ('A+', '1.2', '2014Q4', 'BCM2835', 'Sony', 256, 'MicroSD', 1, 0, False, False, 1, 1, {'P1': PLUS_P1}, ),
|
||||
'0013': ('B+', '1.2', '2015Q1', 'BCM2835', 'Egoman', 512, 'MicroSD', 4, 1, False, False, 1, 1, {'P1': PLUS_P1}, ),
|
||||
'0014': ('CM', '1.1', '2014Q2', 'BCM2835', 'Sony', 512, 'eMMC', 0, 0, False, False, 2, 2, {'SODIMM': CM_SODIMM}, ),
|
||||
'0015': ('A+', '1.1', '2014Q4', 'BCM2835', 'Sony', 256, 'MicroSD', 1, 0, False, False, 1, 1, {'P1': PLUS_P1}, ),
|
||||
'a01041': ('2B', '1.1', '2015Q1', 'BCM2836', 'Sony', 1024, 'MicroSD', 4, 1, False, False, 1, 1, {'P1': PLUS_P1}, ),
|
||||
'a21041': ('2B', '1.1', '2015Q1', 'BCM2836', 'Embest', 1024, 'MicroSD', 4, 1, False, False, 1, 1, {'P1': PLUS_P1}, ),
|
||||
'900092': ('Zero', '1.2', '2015Q4', 'BCM2835', 'Sony', 512, 'MicroSD', 1, 0, False, False, 0, 0, {'P1': PLUS_P1}, ),
|
||||
'a02082': ('3B', '1.2', '2016Q1', 'BCM2837', 'Sony', 1024, 'MicroSD', 4, 1, True, True, 1, 1, {'P1': PLUS_P1}, ),
|
||||
'a22082': ('3B', '1.2', '2016Q1', 'BCM2837', 'Embest', 1024, 'MicroSD', 4, 1, True, True, 1, 1, {'P1': PLUS_P1}, ),
|
||||
0x2: ('B', '1.0', '2012Q1', 'BCM2835', 'Egoman', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV1_P1}, ),
|
||||
0x3: ('B', '1.0', '2012Q3', 'BCM2835', 'Egoman', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV1_P1}, ),
|
||||
0x4: ('B', '2.0', '2012Q3', 'BCM2835', 'Sony', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
|
||||
0x5: ('B', '2.0', '2012Q4', 'BCM2835', 'Qisda', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
|
||||
0x6: ('B', '2.0', '2012Q4', 'BCM2835', 'Egoman', 256, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
|
||||
0x7: ('A', '2.0', '2013Q1', 'BCM2835', 'Egoman', 256, 'SD', 1, 0, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
|
||||
0x8: ('A', '2.0', '2013Q1', 'BCM2835', 'Sony', 256, 'SD', 1, 0, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
|
||||
0x9: ('A', '2.0', '2013Q1', 'BCM2835', 'Qisda', 256, 'SD', 1, 0, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
|
||||
0xd: ('B', '2.0', '2012Q4', 'BCM2835', 'Egoman', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
|
||||
0xe: ('B', '2.0', '2012Q4', 'BCM2835', 'Sony', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
|
||||
0xf: ('B', '2.0', '2012Q4', 'BCM2835', 'Egoman', 512, 'SD', 2, 1, False, False, 1, 1, {'P1': REV2_P1, 'P5': REV2_P5},),
|
||||
0x10: ('B+', '1.2', '2014Q3', 'BCM2835', 'Sony', 512, 'MicroSD', 4, 1, False, False, 1, 1, {'P1': PLUS_P1}, ),
|
||||
0x11: ('CM', '1.2', '2014Q2', 'BCM2835', 'Sony', 512, 'eMMC', 1, 0, False, False, 2, 2, {'SODIMM': CM_SODIMM}, ),
|
||||
0x12: ('A+', '1.2', '2014Q4', 'BCM2835', 'Sony', 256, 'MicroSD', 1, 0, False, False, 1, 1, {'P1': PLUS_P1}, ),
|
||||
0x13: ('B+', '1.2', '2015Q1', 'BCM2835', 'Egoman', 512, 'MicroSD', 4, 1, False, False, 1, 1, {'P1': PLUS_P1}, ),
|
||||
0x14: ('CM', '1.1', '2014Q2', 'BCM2835', 'Embest', 512, 'eMMC', 1, 0, False, False, 2, 2, {'SODIMM': CM_SODIMM}, ),
|
||||
0x15: ('A+', '1.1', '2014Q4', 'BCM2835', 'Sony', 256, 'MicroSD', 1, 0, False, False, 1, 1, {'P1': PLUS_P1}, ),
|
||||
0xa01041: ('2B', '1.1', '2015Q1', 'BCM2836', 'Sony', 1024, 'MicroSD', 4, 1, False, False, 1, 1, {'P1': PLUS_P1}, ),
|
||||
0xa21041: ('2B', '1.1', '2015Q1', 'BCM2836', 'Embest', 1024, 'MicroSD', 4, 1, False, False, 1, 1, {'P1': PLUS_P1}, ),
|
||||
0x900092: ('Zero', '1.2', '2015Q4', 'BCM2835', 'Sony', 512, 'MicroSD', 1, 0, False, False, 0, 0, {'P1': PLUS_P1}, ),
|
||||
0xa02082: ('3B', '1.2', '2016Q1', 'BCM2837', 'Sony', 1024, 'MicroSD', 4, 1, True, True, 1, 1, {'P1': PLUS_P1}, ),
|
||||
0xa22082: ('3B', '1.2', '2016Q1', 'BCM2837', 'Embest', 1024, 'MicroSD', 4, 1, True, True, 1, 1, {'P1': PLUS_P1}, ),
|
||||
0x900093: ('Zero', '1.3', '2016Q2', 'BCM2835', 'Sony', 512, 'MicroSD', 1, 0, False, False, 1, 0, {'P1': PLUS_P1}, ),
|
||||
}
|
||||
|
||||
|
||||
@@ -324,6 +324,12 @@ class PiBoardInfo(namedtuple('PiBoardInfo', (
|
||||
a tuple, it is strongly recommended that you use the following named
|
||||
attributes to access the data contained within.
|
||||
|
||||
.. automethod:: physical_pin
|
||||
|
||||
.. automethod:: physical_pins
|
||||
|
||||
.. automethod:: pulled_up
|
||||
|
||||
.. attribute:: revision
|
||||
|
||||
A string indicating the revision of the Pi. This is unique to each
|
||||
@@ -389,10 +395,6 @@ class PiBoardInfo(namedtuple('PiBoardInfo', (
|
||||
.. note::
|
||||
|
||||
This does *not* include the micro-USB port used to power the Pi.
|
||||
On the Compute Module this is listed as 0 as the compute module
|
||||
itself doesn't have any physical USB headers, despite providing one
|
||||
on the I/O development board and having the pins for one on the
|
||||
module itself.
|
||||
|
||||
.. attribute:: ethernet
|
||||
|
||||
@@ -430,7 +432,7 @@ class PiBoardInfo(namedtuple('PiBoardInfo', (
|
||||
|
||||
def physical_pins(self, function):
|
||||
"""
|
||||
Return the physical pins supporting the specified *function* as a tuple
|
||||
Return the physical pins supporting the specified *function* as tuples
|
||||
of ``(header, pin_number)`` where *header* is a string specifying the
|
||||
header containing the *pin_number*. Note that the return value is a
|
||||
:class:`set` which is not indexable. Use :func:`physical_pin` if you
|
||||
@@ -487,19 +489,6 @@ class PiBoardInfo(namedtuple('PiBoardInfo', (
|
||||
return self.headers[header][number].pull_up
|
||||
|
||||
|
||||
_PI_REVISION = None
|
||||
def _get_pi_revision():
|
||||
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('1000')
|
||||
if overvolted:
|
||||
revision = revision[4:]
|
||||
return revision
|
||||
raise IOError('unable to locate Pi revision in /proc/cpuinfo')
|
||||
|
||||
|
||||
def _parse_pi_revision(revision):
|
||||
# For new-style revisions the value's bit pattern is as follows:
|
||||
#
|
||||
@@ -512,10 +501,9 @@ def _parse_pi_revision(revision):
|
||||
# CCCC - Manufacturer (0=Sony, 1=Egoman, 2=Embest)
|
||||
# PPPP - Processor (0=2835, 1=2836, 2=2837)
|
||||
# TTTTTTTT - Type (0=A, 1=B, 2=A+, 3=B+, 4=2B, 5=Alpha (??), 6=CM, 8=3B, 9=Zero)
|
||||
# RRR - Revision (0, 1, or 2)
|
||||
i = int(revision, base=16)
|
||||
if not (i & 0x800000):
|
||||
raise ValueError('cannot parse "%s"; this is not a new-style revision' % revision)
|
||||
# RRRR - Revision (0, 1, or 2)
|
||||
if not (revision & 0x800000):
|
||||
raise PinUnknownPi('cannot parse "%x"; this is not a new-style revision' % revision)
|
||||
try:
|
||||
model = {
|
||||
0: 'A',
|
||||
@@ -526,15 +514,15 @@ def _parse_pi_revision(revision):
|
||||
6: 'CM',
|
||||
8: '3B',
|
||||
9: 'Zero',
|
||||
}[(i & 0xff0) >> 4]
|
||||
}[(revision & 0xff0) >> 4]
|
||||
if model in ('A', 'B'):
|
||||
pcb_revision = {
|
||||
0: '1.0', # is this right?
|
||||
1: '1.0',
|
||||
2: '2.0',
|
||||
}[i & 0x0f]
|
||||
}[revision & 0x0f]
|
||||
else:
|
||||
pcb_revision = '1.%d' % (i & 0x0f)
|
||||
pcb_revision = '1.%d' % (revision & 0x0f)
|
||||
released = {
|
||||
'A': '2013Q1',
|
||||
'B': '2012Q1' if pcb_revision == '1.0' else '2012Q4',
|
||||
@@ -543,23 +531,23 @@ def _parse_pi_revision(revision):
|
||||
'2B': '2015Q1',
|
||||
'CM': '2014Q2',
|
||||
'3B': '2016Q1',
|
||||
'Zero': '2015Q4',
|
||||
'Zero': '2015Q4' if pcb_revision == '1.0' else '2016Q2',
|
||||
}[model]
|
||||
soc = {
|
||||
0: 'BCM2835',
|
||||
1: 'BCM2836',
|
||||
2: 'BCM2837',
|
||||
}[(i & 0xf000) >> 12]
|
||||
}[(revision & 0xf000) >> 12]
|
||||
manufacturer = {
|
||||
0: 'Sony',
|
||||
1: 'Egoman',
|
||||
2: 'Embest',
|
||||
}[(i & 0xf0000) >> 16]
|
||||
}[(revision & 0xf0000) >> 16]
|
||||
memory = {
|
||||
0: 256,
|
||||
1: 512,
|
||||
2: 1024,
|
||||
}[(i & 0x700000) >> 20]
|
||||
}[(revision & 0x700000) >> 20]
|
||||
storage = {
|
||||
'A': 'SD',
|
||||
'B': 'SD',
|
||||
@@ -585,17 +573,19 @@ def _parse_pi_revision(revision):
|
||||
'3B': True,
|
||||
}.get(model, False)
|
||||
csi = {
|
||||
'Zero': 0,
|
||||
'Zero': 0 if pcb_revision == '1.0' else 1,
|
||||
'CM': 2,
|
||||
}.get(model, 1)
|
||||
dsi = csi
|
||||
dsi = {
|
||||
'Zero': 0,
|
||||
}.get(model, csi)
|
||||
headers = {
|
||||
'A': {'P1': REV2_P1, 'P5': REV2_P5},
|
||||
'B': {'P1': REV2_P1, 'P5': REV2_P5} if pcb_revision == '2.0' else {'P1': REV1_P1},
|
||||
'CM': {'SODIMM': CM_SODIMM},
|
||||
}.get(model, {'P1': PLUS_P1})
|
||||
except KeyError:
|
||||
raise ValueError('unable to parse new-style revision "%s"' % revision)
|
||||
raise PinUnknownPi('unable to parse new-style revision "%x"' % revision)
|
||||
else:
|
||||
return (
|
||||
model,
|
||||
@@ -625,16 +615,26 @@ def pi_info(revision=None):
|
||||
or ``None`` (the default), then the library will attempt to determine
|
||||
the model of Pi it is running on and return information about that.
|
||||
"""
|
||||
# cache the result as we can reasonably assume the revision of the Pi isn't
|
||||
# going to change at runtime...
|
||||
if revision is None:
|
||||
global _PI_REVISION
|
||||
if _PI_REVISION is None:
|
||||
try:
|
||||
_PI_REVISION = _get_pi_revision()
|
||||
except IOError:
|
||||
_PI_REVISION = 'unknown'
|
||||
revision = _PI_REVISION
|
||||
# NOTE: This import is declared locally for two reasons. Firstly it
|
||||
# avoids a circular dependency (devices->pins->pins.data->devices).
|
||||
# Secondly, pin_factory is one global which might potentially be
|
||||
# re-written by a user's script at runtime hence we should re-import
|
||||
# here in case it's changed since initialization
|
||||
from ..devices import pin_factory
|
||||
result = pin_factory.pi_info()
|
||||
if result is None:
|
||||
raise PinUnknownPi('The default pin_factory is not attached to a Pi')
|
||||
else:
|
||||
return result
|
||||
else:
|
||||
if isinstance(revision, bytes):
|
||||
revision = revision.decode('ascii')
|
||||
if isinstance(revision, str):
|
||||
revision = int(revision, base=16)
|
||||
else:
|
||||
# be nice to people passing an int (or something numeric anyway)
|
||||
revision = int(revision)
|
||||
try:
|
||||
(
|
||||
model,
|
||||
@@ -653,25 +653,22 @@ def pi_info(revision=None):
|
||||
headers,
|
||||
) = PI_REVISIONS[revision]
|
||||
except KeyError:
|
||||
try:
|
||||
(
|
||||
model,
|
||||
pcb_revision,
|
||||
released,
|
||||
soc,
|
||||
manufacturer,
|
||||
memory,
|
||||
storage,
|
||||
usb,
|
||||
ethernet,
|
||||
wifi,
|
||||
bluetooth,
|
||||
csi,
|
||||
dsi,
|
||||
headers,
|
||||
) = _parse_pi_revision(revision)
|
||||
except ValueError:
|
||||
raise PinUnknownPi('unknown RPi revision "%s"' % revision)
|
||||
(
|
||||
model,
|
||||
pcb_revision,
|
||||
released,
|
||||
soc,
|
||||
manufacturer,
|
||||
memory,
|
||||
storage,
|
||||
usb,
|
||||
ethernet,
|
||||
wifi,
|
||||
bluetooth,
|
||||
csi,
|
||||
dsi,
|
||||
headers,
|
||||
) = _parse_pi_revision(revision)
|
||||
headers = {
|
||||
header: {
|
||||
number: PinInfo(number, function, pull_up)
|
||||
@@ -680,7 +677,7 @@ def pi_info(revision=None):
|
||||
for header, header_data in headers.items()
|
||||
}
|
||||
return PiBoardInfo(
|
||||
revision,
|
||||
'%04x' % revision,
|
||||
model,
|
||||
pcb_revision,
|
||||
released,
|
||||
|
||||
@@ -16,6 +16,7 @@ except ImportError:
|
||||
from ..compat import isclose
|
||||
|
||||
from . import Pin
|
||||
from .data import pi_info
|
||||
from ..exc import PinSetInput, PinPWMUnsupported, PinFixedPull
|
||||
|
||||
|
||||
@@ -32,6 +33,10 @@ class MockPin(Pin):
|
||||
def clear_pins(cls):
|
||||
cls._PINS.clear()
|
||||
|
||||
@classmethod
|
||||
def pi_info(cls):
|
||||
return pi_info('a21041') # Pretend we're a Pi 2B
|
||||
|
||||
def __new__(cls, number):
|
||||
if not (0 <= number < 54):
|
||||
raise ValueError('invalid pin %d specified (must be 0..53)' % number)
|
||||
|
||||
@@ -17,7 +17,7 @@ from time import sleep
|
||||
from threading import Thread, Event, Lock
|
||||
from collections import Counter
|
||||
|
||||
from . import Pin, PINS_CLEANUP
|
||||
from . import LocalPin, PINS_CLEANUP
|
||||
from .data import pi_info
|
||||
from ..exc import (
|
||||
PinInvalidPull,
|
||||
@@ -149,7 +149,7 @@ class GPIOFS(object):
|
||||
f.write(str(pin).encode('ascii'))
|
||||
|
||||
|
||||
class NativePin(Pin):
|
||||
class NativePin(LocalPin):
|
||||
"""
|
||||
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
|
||||
|
||||
@@ -8,6 +8,7 @@ str = type('')
|
||||
|
||||
import warnings
|
||||
import pigpio
|
||||
import os
|
||||
|
||||
from . import Pin
|
||||
from .data import pi_info
|
||||
@@ -68,7 +69,7 @@ class PiGPIOPin(Pin):
|
||||
.. _pigpio: http://abyz.co.uk/rpi/pigpio/
|
||||
"""
|
||||
|
||||
_CONNECTIONS = {}
|
||||
_CONNECTIONS = {} # maps (host, port) to (connection, pi_info)
|
||||
_PINS = {}
|
||||
|
||||
GPIO_FUNCTIONS = {
|
||||
@@ -98,25 +99,17 @@ class PiGPIOPin(Pin):
|
||||
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()}
|
||||
|
||||
PI_INFO = None
|
||||
|
||||
def __new__(cls, number, host='localhost', port=8888):
|
||||
# XXX What about remote pins? This should probably be instance
|
||||
# specific rather than class specific for pigpio. Need to check how
|
||||
# to query remote info though...
|
||||
if cls.PI_INFO is None:
|
||||
cls.PI_INFO = pi_info()
|
||||
def __new__(
|
||||
cls, number, host=os.getenv('PIGPIO_ADDR', 'localhost'),
|
||||
port=int(os.getenv('PIGPIO_PORT', 8888))):
|
||||
try:
|
||||
return cls._PINS[(host, port, number)]
|
||||
except KeyError:
|
||||
self = super(PiGPIOPin, cls).__new__(cls)
|
||||
cls.pi_info(host, port) # implicitly creates connection
|
||||
self._connection, self._pi_info = cls._CONNECTIONS[(host, port)]
|
||||
try:
|
||||
self._connection = cls._CONNECTIONS[(host, port)]
|
||||
except KeyError:
|
||||
self._connection = pigpio.pi(host, port)
|
||||
cls._CONNECTIONS[(host, port)] = self._connection
|
||||
try:
|
||||
cls.PI_INFO.physical_pin('GPIO%d' % number)
|
||||
self._pi_info.physical_pin('GPIO%d' % number)
|
||||
except PinNoPins:
|
||||
warnings.warn(
|
||||
PinNonPhysical(
|
||||
@@ -124,7 +117,7 @@ class PiGPIOPin(Pin):
|
||||
self._host = host
|
||||
self._port = port
|
||||
self._number = number
|
||||
self._pull = 'up' if cls.PI_INFO.pulled_up('GPIO%d' % number) else 'floating'
|
||||
self._pull = 'up' if self._pi_info.pulled_up('GPIO%d' % number) else 'floating'
|
||||
self._pwm = False
|
||||
self._bounce = None
|
||||
self._when_changed = None
|
||||
@@ -136,7 +129,6 @@ class PiGPIOPin(Pin):
|
||||
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)
|
||||
self._connection.set_PWM_range(self._number, 255)
|
||||
cls._PINS[(host, port, number)] = self
|
||||
return self
|
||||
|
||||
@@ -167,7 +159,7 @@ class PiGPIOPin(Pin):
|
||||
self.frequency = None
|
||||
self.when_changed = None
|
||||
self.function = 'input'
|
||||
self.pull = 'up' if self.PI_INFO.pulled_up('GPIO%d' % self.number) else 'floating'
|
||||
self.pull = 'up' if self._pi_info.pulled_up('GPIO%d' % self.number) else 'floating'
|
||||
|
||||
def _get_function(self):
|
||||
return self.GPIO_FUNCTION_NAMES[self._connection.get_mode(self._number)]
|
||||
@@ -182,14 +174,19 @@ class PiGPIOPin(Pin):
|
||||
|
||||
def _get_state(self):
|
||||
if self._pwm:
|
||||
return self._connection.get_PWM_dutycycle(self._number) / 255
|
||||
return (
|
||||
self._connection.get_PWM_dutycycle(self._number) /
|
||||
self._connection.get_PWM_range(self._number)
|
||||
)
|
||||
else:
|
||||
return bool(self._connection.read(self._number))
|
||||
|
||||
def _set_state(self, value):
|
||||
if self._pwm:
|
||||
try:
|
||||
self._connection.set_PWM_dutycycle(self._number, int(value * 255))
|
||||
value = int(value * self._connection.get_PWM_range(self._number))
|
||||
if value != self._connection.get_PWM_dutycycle(self._number):
|
||||
self._connection.set_PWM_dutycycle(self._number, value)
|
||||
except pigpio.error:
|
||||
raise PinInvalidState('invalid state "%s" for pin %r' % (value, self))
|
||||
elif self.function == 'input':
|
||||
@@ -204,7 +201,7 @@ class PiGPIOPin(Pin):
|
||||
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.PI_INFO.pulled_up('GPIO%d' % self._number):
|
||||
if value != 'up' and self._pi_info.pulled_up('GPIO%d' % self._number):
|
||||
raise PinFixedPull('%r has a physical pull-up resistor' % self)
|
||||
try:
|
||||
self._connection.set_pull_up_down(self._number, self.GPIO_PULL_UPS[value])
|
||||
@@ -220,12 +217,15 @@ class PiGPIOPin(Pin):
|
||||
def _set_frequency(self, value):
|
||||
if not self._pwm and value is not None:
|
||||
self._connection.set_PWM_frequency(self._number, value)
|
||||
self._connection.set_PWM_range(self._number, 10000)
|
||||
self._connection.set_PWM_dutycycle(self._number, 0)
|
||||
self._pwm = True
|
||||
elif self._pwm and value is not None:
|
||||
self._connection.set_PWM_frequency(self._number, value)
|
||||
if value != self._connection.get_PWM_frequency(self._number):
|
||||
self._connection.set_PWM_frequency(self._number, value)
|
||||
self._connection.set_PWM_range(self._number, 10000)
|
||||
elif self._pwm and value is None:
|
||||
self._connection.set_PWM_dutycycle(self._number, 0)
|
||||
self._connection.write(self._number, 0)
|
||||
self._pwm = False
|
||||
|
||||
def _get_bounce(self):
|
||||
@@ -263,3 +263,16 @@ class PiGPIOPin(Pin):
|
||||
self._number, self._edges,
|
||||
lambda gpio, level, tick: value())
|
||||
|
||||
@classmethod
|
||||
def pi_info(
|
||||
cls, host=os.getenv('PIGPIO_ADDR', 'localhost'),
|
||||
port=int(os.getenv('PIGPIO_PORT', 8888))):
|
||||
try:
|
||||
connection, info = cls._CONNECTIONS[(host, port)]
|
||||
except KeyError:
|
||||
connection = pigpio.pi(host, port)
|
||||
revision = '%04x' % connection.get_hardware_revision()
|
||||
info = pi_info(revision)
|
||||
cls._CONNECTIONS[(host, port)] = (connection, info)
|
||||
return info
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ str = type('')
|
||||
import warnings
|
||||
from RPi import GPIO
|
||||
|
||||
from . import Pin
|
||||
from . import LocalPin
|
||||
from .data import pi_info
|
||||
from ..exc import (
|
||||
PinInvalidFunction,
|
||||
@@ -24,7 +24,7 @@ from ..exc import (
|
||||
)
|
||||
|
||||
|
||||
class RPiGPIOPin(Pin):
|
||||
class RPiGPIOPin(LocalPin):
|
||||
"""
|
||||
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.
|
||||
|
||||
@@ -12,7 +12,7 @@ import RPIO
|
||||
import RPIO.PWM
|
||||
from RPIO.Exceptions import InvalidChannelException
|
||||
|
||||
from . import Pin, PINS_CLEANUP
|
||||
from . import LocalPin, PINS_CLEANUP
|
||||
from .data import pi_info
|
||||
from ..exc import (
|
||||
PinInvalidFunction,
|
||||
@@ -27,7 +27,7 @@ from ..exc import (
|
||||
)
|
||||
|
||||
|
||||
class RPIOPin(Pin):
|
||||
class RPIOPin(LocalPin):
|
||||
"""
|
||||
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,
|
||||
|
||||
@@ -163,7 +163,7 @@ class SPISoftwareBus(SharedMixin, Device):
|
||||
return self.lock is None
|
||||
|
||||
@classmethod
|
||||
def _shared_key(self, clock_pin, mosi_pin, miso_pin):
|
||||
def _shared_key(cls, clock_pin, mosi_pin, miso_pin):
|
||||
return (clock_pin, mosi_pin, miso_pin)
|
||||
|
||||
def read(self, n):
|
||||
@@ -396,24 +396,30 @@ def SPI(**spi_args):
|
||||
raise SPIBadArgs(
|
||||
'unrecognized keyword argument %s' % kwargs.popitem()[0])
|
||||
if all((
|
||||
SpiDev is not None,
|
||||
spi_args['clock_pin'] == 11,
|
||||
spi_args['mosi_pin'] == 10,
|
||||
spi_args['miso_pin'] == 9,
|
||||
spi_args['select_pin'] in (7, 8),
|
||||
)):
|
||||
try:
|
||||
if shared:
|
||||
return SharedSPIHardwareInterface(
|
||||
port=0, device={8: 0, 7: 1}[spi_args['select_pin']])
|
||||
else:
|
||||
return SPIHardwareInterface(
|
||||
port=0, device={8: 0, 7: 1}[spi_args['select_pin']])
|
||||
except Exception as e:
|
||||
if SpiDev is None:
|
||||
warnings.warn(
|
||||
SPISoftwareFallback(
|
||||
'failed to initialize hardware SPI, falling back to '
|
||||
'software (error was: %s)' % str(e)))
|
||||
'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:
|
||||
|
||||
@@ -112,7 +112,6 @@ class MCP3xxx(AnalogInputDevice):
|
||||
|
||||
def __init__(self, channel=0, bits=10, differential=False, **spi_args):
|
||||
self._channel = channel
|
||||
self._bits = bits
|
||||
self._differential = bool(differential)
|
||||
super(MCP3xxx, self).__init__(bits, **spi_args)
|
||||
|
||||
|
||||
@@ -41,10 +41,13 @@ def negated(values):
|
||||
yield not v
|
||||
|
||||
|
||||
def inverted(values):
|
||||
def inverted(values, input_min=0, input_max=1):
|
||||
"""
|
||||
Returns the inversion of the supplied values (1 becomes 0, 0 becomes 1,
|
||||
0.1 becomes 0.9, etc.). For example::
|
||||
Returns the inversion of the supplied values (*input_min* becomes
|
||||
*input_max*, *input_max* becomes *input_min*, `input_min + 0.1` becomes
|
||||
`input_max - 0.1`, etc.). All items in *values* are assumed to be between
|
||||
*input_min* and *input_max* (which default to 0 and 1 respectively), and
|
||||
the output will be in the same range. For example::
|
||||
|
||||
from gpiozero import MCP3008, PWMLED
|
||||
from gpiozero.tools import inverted
|
||||
@@ -55,8 +58,10 @@ def inverted(values):
|
||||
led.source = inverted(pot.values)
|
||||
pause()
|
||||
"""
|
||||
if input_min >= input_max:
|
||||
raise ValueError('input_min must be smaller than input_max')
|
||||
for v in values:
|
||||
yield 1 - v
|
||||
yield input_min + input_max - v
|
||||
|
||||
|
||||
def scaled(values, output_min, output_max, input_min=0, input_max=1):
|
||||
@@ -82,6 +87,8 @@ def scaled(values, output_min, output_max, input_min=0, input_max=1):
|
||||
*input_max* (inclusive) then the function will not produce values that
|
||||
lie within *output_min* to *output_max* (inclusive).
|
||||
"""
|
||||
if input_min >= input_max:
|
||||
raise ValueError('input_min must be smaller than input_max')
|
||||
input_size = input_max - input_min
|
||||
output_size = output_max - output_min
|
||||
for v in values:
|
||||
@@ -104,6 +111,8 @@ def clamped(values, output_min=0, output_max=1):
|
||||
led.source = clamped(pot.values, 0.5, 1.0)
|
||||
pause()
|
||||
"""
|
||||
if output_min >= output_max:
|
||||
raise ValueError('output_min must be smaller than output_max')
|
||||
for v in values:
|
||||
yield min(max(v, output_min), output_max)
|
||||
|
||||
@@ -128,11 +137,11 @@ def absoluted(values):
|
||||
yield abs(v)
|
||||
|
||||
|
||||
def quantized(values, steps, output_min=0, output_max=1):
|
||||
def quantized(values, steps, input_min=0, input_max=1):
|
||||
"""
|
||||
Returns *values* quantized to *steps* increments. All items in *values* are
|
||||
assumed to be between *output_min* and *output_max* (use :func:`scaled` to
|
||||
ensure this if necessary).
|
||||
assumed to be between *input_min* and *input_max* (which default to 0 and
|
||||
1 respectively), and the output will be in the same range.
|
||||
|
||||
For example, to quantize values between 0 and 1 to 5 "steps" (0.0, 0.25,
|
||||
0.5, 0.75, 1.0)::
|
||||
@@ -146,9 +155,72 @@ def quantized(values, steps, output_min=0, output_max=1):
|
||||
led.source = quantized(pot.values, 4)
|
||||
pause()
|
||||
"""
|
||||
output_size = output_max - output_min
|
||||
for v in scaled(values, 0, 1, output_min, output_max):
|
||||
yield ((int(v * steps) / steps) * output_size) + output_min
|
||||
if steps < 1:
|
||||
raise ValueError("steps must be 1 or larger")
|
||||
if input_min >= input_max:
|
||||
raise ValueError('input_min must be smaller than input_max')
|
||||
input_size = input_max - input_min
|
||||
for v in scaled(values, 0, 1, input_min, input_max):
|
||||
yield ((int(v * steps) / steps) * input_size) + input_min
|
||||
|
||||
|
||||
def booleanized(values, min_value, max_value, hysteresis=0):
|
||||
"""
|
||||
Returns True for each item in *values* between *min_value* and
|
||||
*max_value*, and False otherwise. *hysteresis* can optionally be used to
|
||||
add `hysteresis`_ which prevents the output value rapidly flipping when
|
||||
the input value is fluctuating near the *min_value* or *max_value*
|
||||
thresholds. For example, to light an LED only when a potentiometer is
|
||||
between 1/4 and 3/4 of its full range::
|
||||
|
||||
from gpiozero import LED, MCP3008
|
||||
from gpiozero.tools import booleanized
|
||||
from signal import pause
|
||||
|
||||
led = LED(4)
|
||||
pot = MCP3008(channel=0)
|
||||
led.source = booleanized(pot.values, 0.25, 0.75)
|
||||
pause()
|
||||
|
||||
.. _hysteresis: https://en.wikipedia.org/wiki/Hysteresis
|
||||
"""
|
||||
if min_value >= max_value:
|
||||
raise ValueError('min_value must be smaller than max_value')
|
||||
min_value = float(min_value)
|
||||
max_value = float(max_value)
|
||||
if hysteresis < 0:
|
||||
raise ValueError("hysteresis must be 0 or larger")
|
||||
else:
|
||||
hysteresis = float(hysteresis)
|
||||
if (max_value - min_value) <= hysteresis:
|
||||
raise ValueError('The gap between min_value and max_value must be larger than hysteresis')
|
||||
last_state = None
|
||||
for v in values:
|
||||
if v < min_value:
|
||||
new_state = 'below'
|
||||
elif v > max_value:
|
||||
new_state = 'above'
|
||||
else:
|
||||
new_state = 'in'
|
||||
switch = False
|
||||
if last_state == None or not hysteresis:
|
||||
switch = True
|
||||
elif new_state == last_state:
|
||||
pass
|
||||
else: # new_state != last_state
|
||||
if last_state == 'below' and new_state == 'in':
|
||||
switch = v >= min_value + hysteresis
|
||||
elif last_state == 'in' and new_state == 'below':
|
||||
switch = v < min_value - hysteresis
|
||||
elif last_state == 'in' and new_state == 'above':
|
||||
switch = v > max_value + hysteresis
|
||||
elif last_state == 'above' and new_state == 'in':
|
||||
switch = v <= max_value - hysteresis
|
||||
else: # above->below or below->above
|
||||
switch = True
|
||||
if switch:
|
||||
last_state = new_state
|
||||
yield last_state == 'in'
|
||||
|
||||
|
||||
def all_values(*values):
|
||||
@@ -218,6 +290,54 @@ def averaged(*values):
|
||||
yield mean(v)
|
||||
|
||||
|
||||
def summed(*values):
|
||||
"""
|
||||
Returns the sum of all supplied values. One or more *values* can be
|
||||
specified. For example, to light a :class:`PWMLED` as the (scaled) sum of
|
||||
several potentiometers connected to an :class:`MCP3008` ADC::
|
||||
|
||||
from gpiozero import MCP3008, PWMLED
|
||||
from gpiozero.tools import summed, scaled
|
||||
from signal import pause
|
||||
|
||||
pot1 = MCP3008(channel=0)
|
||||
pot2 = MCP3008(channel=1)
|
||||
pot3 = MCP3008(channel=2)
|
||||
led = PWMLED(4)
|
||||
led.source = scaled(summed(pot1.values, pot2.values, pot3.values), 0, 1, 0, 3)
|
||||
pause()
|
||||
"""
|
||||
for v in zip(*values):
|
||||
yield sum(v)
|
||||
|
||||
|
||||
def multiplied(*values):
|
||||
"""
|
||||
Returns the product of all supplied values. One or more *values* can be
|
||||
specified. For example, to light a :class:`PWMLED` as the product (i.e.
|
||||
multiplication) of several potentiometers connected to an :class:`MCP3008`
|
||||
ADC::
|
||||
|
||||
from gpiozero import MCP3008, PWMLED
|
||||
from gpiozero.tools import multiplied
|
||||
from signal import pause
|
||||
|
||||
pot1 = MCP3008(channel=0)
|
||||
pot2 = MCP3008(channel=1)
|
||||
pot3 = MCP3008(channel=2)
|
||||
led = PWMLED(4)
|
||||
led.source = multiplied(pot1.values, pot2.values, pot3.values)
|
||||
pause()
|
||||
"""
|
||||
def _product(it):
|
||||
p = 1
|
||||
for n in it:
|
||||
p *= n
|
||||
return p
|
||||
for v in zip(*values):
|
||||
yield _product(v)
|
||||
|
||||
|
||||
def queued(values, qsize):
|
||||
"""
|
||||
Queues up readings from *values* (the number of readings queued is
|
||||
@@ -236,6 +356,8 @@ def queued(values, qsize):
|
||||
leds[4].source = btn.values
|
||||
pause()
|
||||
"""
|
||||
if qsize < 1:
|
||||
raise ValueError("qsize must be 1 or larger")
|
||||
q = []
|
||||
it = iter(values)
|
||||
for i in range(qsize):
|
||||
@@ -248,10 +370,41 @@ def queued(values, qsize):
|
||||
break
|
||||
|
||||
|
||||
def smoothed(values, qsize, average=mean):
|
||||
"""
|
||||
Queues up readings from *values* (the number of readings queued is
|
||||
determined by *qsize*) and begins yielding the *average* of the last
|
||||
*qsize* values when the queue is full. The larger the *qsize*, the more the
|
||||
values are smoothed. For example, to smooth the analog values read from an
|
||||
ADC::
|
||||
|
||||
from gpiozero import MCP3008
|
||||
from gpiozero.tools import smoothed
|
||||
|
||||
with MCP3008(channel=0) as adc:
|
||||
for value in smoothed(adc.values, 5):
|
||||
print value
|
||||
"""
|
||||
if qsize < 1:
|
||||
raise ValueError("qsize must be 1 or larger")
|
||||
q = []
|
||||
it = iter(values)
|
||||
for i in range(qsize):
|
||||
q.append(next(it))
|
||||
for i in cycle(range(qsize)):
|
||||
yield average(q)
|
||||
try:
|
||||
q[i] = next(it)
|
||||
except StopIteration:
|
||||
break
|
||||
|
||||
|
||||
def pre_delayed(values, delay):
|
||||
"""
|
||||
Waits for *delay* seconds before returning each item from *values*.
|
||||
"""
|
||||
if delay < 0:
|
||||
raise ValueError("delay must be 0 or larger")
|
||||
for v in values:
|
||||
sleep(delay)
|
||||
yield v
|
||||
@@ -261,11 +414,78 @@ def post_delayed(values, delay):
|
||||
"""
|
||||
Waits for *delay* seconds after returning each item from *values*.
|
||||
"""
|
||||
if delay < 0:
|
||||
raise ValueError("delay must be 0 or larger")
|
||||
for v in values:
|
||||
yield v
|
||||
sleep(delay)
|
||||
|
||||
|
||||
def pre_periodic_filtered(values, block, repeat_after):
|
||||
"""
|
||||
Blocks the first *block* items from *values*, repeating the block after
|
||||
every *repeat_after* items, if *repeat_after* is non-zero. For example, to
|
||||
discard the first 50 values read from an ADC::
|
||||
|
||||
from gpiozero import MCP3008
|
||||
from gpiozero.tools import pre_periodic_filtered
|
||||
|
||||
with MCP3008(channel=0) as adc:
|
||||
for value in pre_periodic_filtered(adc.values, 50, 0):
|
||||
print value
|
||||
|
||||
Or to only display every even item read from an ADC::
|
||||
|
||||
from gpiozero import MCP3008
|
||||
from gpiozero.tools import pre_periodic_filtered
|
||||
|
||||
with MCP3008(channel=0) as adc:
|
||||
for value in pre_periodic_filtered(adc.values, 1, 1):
|
||||
print value
|
||||
"""
|
||||
if block < 1:
|
||||
raise ValueError("block must be 1 or larger")
|
||||
if repeat_after < 0:
|
||||
raise ValueError("repeat_after must be 0 or larger")
|
||||
it = iter(values)
|
||||
if repeat_after == 0:
|
||||
for _ in range(block):
|
||||
next(it)
|
||||
while True:
|
||||
yield next(it)
|
||||
else:
|
||||
while True:
|
||||
for _ in range(block):
|
||||
next(it)
|
||||
for _ in range(repeat_after):
|
||||
yield next(it)
|
||||
|
||||
|
||||
def post_periodic_filtered(values, repeat_after, block):
|
||||
"""
|
||||
After every *repeat_after* items, blocks the next *block* items from
|
||||
*values*. Note that unlike :func:`pre_periodic_filtered`, *repeat_after*
|
||||
can't be 0. For example, to block every tenth item read from an ADC::
|
||||
|
||||
from gpiozero import MCP3008
|
||||
from gpiozero.tools import post_periodic_filtered
|
||||
|
||||
with MCP3008(channel=0) as adc:
|
||||
for value in post_periodic_filtered(adc.values, 9, 1):
|
||||
print value
|
||||
"""
|
||||
if repeat_after < 1:
|
||||
raise ValueError("repeat_after must be 1 or larger")
|
||||
if block < 1:
|
||||
raise ValueError("block must be 1 or larger")
|
||||
it = iter(values)
|
||||
while True:
|
||||
for _ in range(repeat_after):
|
||||
yield next(it)
|
||||
for _ in range(block):
|
||||
next(it)
|
||||
|
||||
|
||||
def random_values():
|
||||
"""
|
||||
Provides an infinite source of random values between 0 and 1. For example,
|
||||
@@ -298,7 +518,7 @@ def sin_values(period=360):
|
||||
red = PWMLED(2)
|
||||
blue = PWMLED(3)
|
||||
red.source_delay = 0.01
|
||||
blue.source_delay = 0.01
|
||||
blue.source_delay = red.source_delay
|
||||
red.source = scaled(sin_values(100), 0, 1, -1, 1)
|
||||
blue.source = inverted(red.values)
|
||||
pause()
|
||||
@@ -323,7 +543,7 @@ def cos_values(period=360):
|
||||
red = PWMLED(2)
|
||||
blue = PWMLED(3)
|
||||
red.source_delay = 0.01
|
||||
blue.source_delay = 0.01
|
||||
blue.source_delay = red.source_delay
|
||||
red.source = scaled(cos_values(100), 0, 1, -1, 1)
|
||||
blue.source = inverted(red.values)
|
||||
pause()
|
||||
|
||||
8
setup.py
@@ -61,6 +61,14 @@ if sys.version_info[:2] == (3, 2):
|
||||
__extra_requires__['test'][1] = 'coverage<4.0dev'
|
||||
|
||||
__entry_points__ = {
|
||||
'gpiozero_pin_factories': [
|
||||
'PiGPIOPin = gpiozero.pins.pigpiod:PiGPIOPin',
|
||||
'RPiGPIOPin = gpiozero.pins.rpigpio:RPiGPIOPin',
|
||||
'RPIOPin = gpiozero.pins.rpio:RPIOPin',
|
||||
'NativePin = gpiozero.pins.native:NativePin',
|
||||
'MockPin = gpiozero.pins.mock:MockPin',
|
||||
'MockPWMPin = gpiozero.pins.mock:MockPWMPin',
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -18,10 +18,10 @@ from gpiozero import *
|
||||
def setup_function(function):
|
||||
import gpiozero.devices
|
||||
# dirty, but it does the job
|
||||
if function.__name__ in ('test_robot', 'test_ryanteck_robot', 'test_camjam_kit_robot'):
|
||||
gpiozero.devices.DefaultPin = MockPWMPin
|
||||
if function.__name__ in ('test_robot', 'test_ryanteck_robot', 'test_camjam_kit_robot', 'test_led_borg', 'test_snow_pi_initial_value_pwm'):
|
||||
gpiozero.devices.pin_factory = MockPWMPin
|
||||
else:
|
||||
gpiozero.devices.DefaultPin = MockPin
|
||||
gpiozero.devices.pin_factory = MockPin
|
||||
|
||||
def teardown_function(function):
|
||||
MockPin.clear_pins()
|
||||
@@ -71,6 +71,10 @@ def test_led_board_on_off():
|
||||
assert isinstance(board[0], LED)
|
||||
assert isinstance(board[1], LED)
|
||||
assert isinstance(board[2], LED)
|
||||
assert board.active_high
|
||||
assert board[0].active_high
|
||||
assert board[1].active_high
|
||||
assert board[2].active_high
|
||||
board.on()
|
||||
assert all((pin1.state, pin2.state, pin3.state))
|
||||
board.off()
|
||||
@@ -85,6 +89,121 @@ def test_led_board_on_off():
|
||||
assert not pin1.state
|
||||
assert pin2.state
|
||||
assert pin3.state
|
||||
board.toggle(0,1)
|
||||
assert board.value == (1, 0, 1)
|
||||
assert pin1.state
|
||||
assert not pin2.state
|
||||
assert pin3.state
|
||||
board.off(2)
|
||||
assert board.value == (1, 0, 0)
|
||||
assert pin1.state
|
||||
assert not pin2.state
|
||||
assert not pin3.state
|
||||
board.on(1)
|
||||
assert board.value == (1, 1, 0)
|
||||
assert pin1.state
|
||||
assert pin2.state
|
||||
assert not pin3.state
|
||||
board.off(0,1)
|
||||
assert board.value == (0, 0, 0)
|
||||
assert not pin1.state
|
||||
assert not pin2.state
|
||||
assert not pin3.state
|
||||
board.on(1,2)
|
||||
assert board.value == (0, 1, 1)
|
||||
assert not pin1.state
|
||||
assert pin2.state
|
||||
assert pin3.state
|
||||
board.toggle(0)
|
||||
assert board.value == (1, 1, 1)
|
||||
assert pin1.state
|
||||
assert pin2.state
|
||||
assert pin3.state
|
||||
|
||||
def test_led_board_active_low():
|
||||
pin1 = MockPin(2)
|
||||
pin2 = MockPin(3)
|
||||
pin3 = MockPin(4)
|
||||
with LEDBoard(pin1, pin2, foo=pin3, active_high=False) as board:
|
||||
assert not board.active_high
|
||||
assert not board[0].active_high
|
||||
assert not board[1].active_high
|
||||
assert not board[2].active_high
|
||||
board.on()
|
||||
assert not any ((pin1.state, pin2.state, pin3.state))
|
||||
board.off()
|
||||
assert all((pin1.state, pin2.state, pin3.state))
|
||||
board[0].on()
|
||||
assert board.value == (1, 0, 0)
|
||||
assert not pin1.state
|
||||
assert pin2.state
|
||||
assert pin3.state
|
||||
board.toggle()
|
||||
assert board.value == (0, 1, 1)
|
||||
assert pin1.state
|
||||
assert not pin2.state
|
||||
assert not pin3.state
|
||||
|
||||
def test_led_board_value():
|
||||
pin1 = MockPin(2)
|
||||
pin2 = MockPin(3)
|
||||
pin3 = MockPin(4)
|
||||
with LEDBoard(pin1, pin2, foo=pin3) as board:
|
||||
assert board.value == (0, 0, 0)
|
||||
board.value = (0, 1, 0)
|
||||
assert board.value == (0, 1, 0)
|
||||
board.value = (1, 0, 1)
|
||||
assert board.value == (1, 0, 1)
|
||||
|
||||
def test_led_board_pwm_value():
|
||||
pin1 = MockPWMPin(2)
|
||||
pin2 = MockPWMPin(3)
|
||||
pin3 = MockPWMPin(4)
|
||||
with LEDBoard(pin1, pin2, foo=pin3, pwm=True) as board:
|
||||
assert board.value == (0, 0, 0)
|
||||
board.value = (0, 1, 0)
|
||||
assert board.value == (0, 1, 0)
|
||||
board.value = (0.5, 0, 0.75)
|
||||
assert board.value == (0.5, 0, 0.75)
|
||||
|
||||
def test_led_board_pwm_bad_value():
|
||||
pin1 = MockPWMPin(2)
|
||||
pin2 = MockPWMPin(3)
|
||||
pin3 = MockPWMPin(4)
|
||||
with LEDBoard(pin1, pin2, foo=pin3, pwm=True) as board:
|
||||
with pytest.raises(ValueError):
|
||||
board.value = (-1, 0, 0)
|
||||
with pytest.raises(ValueError):
|
||||
board.value = (0, 2, 0)
|
||||
|
||||
def test_led_board_initial_value():
|
||||
pin1 = MockPin(2)
|
||||
pin2 = MockPin(3)
|
||||
pin3 = MockPin(4)
|
||||
with LEDBoard(pin1, pin2, foo=pin3, initial_value=0) as board:
|
||||
assert board.value == (0, 0, 0)
|
||||
with LEDBoard(pin1, pin2, foo=pin3, initial_value=1) as board:
|
||||
assert board.value == (1, 1, 1)
|
||||
|
||||
def test_led_board_pwm_initial_value():
|
||||
pin1 = MockPWMPin(2)
|
||||
pin2 = MockPWMPin(3)
|
||||
pin3 = MockPWMPin(4)
|
||||
with LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=0) as board:
|
||||
assert board.value == (0, 0, 0)
|
||||
with LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=1) as board:
|
||||
assert board.value == (1, 1, 1)
|
||||
with LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=0.5) as board:
|
||||
assert board.value == (0.5, 0.5, 0.5)
|
||||
|
||||
def test_led_board_pwm_bad_initial_value():
|
||||
pin1 = MockPWMPin(2)
|
||||
pin2 = MockPWMPin(3)
|
||||
pin3 = MockPWMPin(4)
|
||||
with pytest.raises(ValueError):
|
||||
LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=-1)
|
||||
with pytest.raises(ValueError):
|
||||
LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=2)
|
||||
|
||||
def test_led_board_nested():
|
||||
pin1 = MockPin(2)
|
||||
@@ -159,7 +278,7 @@ def test_led_board_blink_control():
|
||||
board.blink(0.1, 0.1, n=2)
|
||||
# make sure the blink thread's started
|
||||
while not board._blink_leds:
|
||||
sleep(0.00001)
|
||||
sleep(0.00001) # pragma: no cover
|
||||
board[1][0].off() # immediately take over the second LED
|
||||
board._blink_thread.join() # naughty, but ensures no arbitrary waits in the test
|
||||
test = [
|
||||
@@ -206,7 +325,7 @@ def test_led_board_blink_control_all():
|
||||
board.blink(0.1, 0.1, n=2)
|
||||
# make sure the blink thread's started
|
||||
while not board._blink_leds:
|
||||
sleep(0.00001)
|
||||
sleep(0.00001) # pragma: no cover
|
||||
board[0].off() # immediately take over all LEDs
|
||||
board[1][0].off()
|
||||
board[1][1].off()
|
||||
@@ -285,13 +404,24 @@ def test_led_bar_graph_value():
|
||||
pin2 = MockPin(3)
|
||||
pin3 = MockPin(4)
|
||||
with LEDBarGraph(pin1, pin2, pin3) as graph:
|
||||
assert isinstance(graph[0], LED)
|
||||
assert isinstance(graph[1], LED)
|
||||
assert isinstance(graph[2], LED)
|
||||
assert graph.active_high
|
||||
assert graph[0].active_high
|
||||
assert graph[1].active_high
|
||||
assert graph[2].active_high
|
||||
graph.value = 0
|
||||
assert graph.value == 0
|
||||
assert not any((pin1.state, pin2.state, pin3.state))
|
||||
graph.value = 1
|
||||
assert graph.value == 1
|
||||
assert all((pin1.state, pin2.state, pin3.state))
|
||||
graph.value = 1/3
|
||||
assert graph.value == 1/3
|
||||
assert pin1.state and not (pin2.state or pin3.state)
|
||||
graph.value = -1/3
|
||||
assert graph.value == -1/3
|
||||
assert pin3.state and not (pin1.state or pin2.state)
|
||||
pin1.state = True
|
||||
pin2.state = True
|
||||
@@ -302,31 +432,102 @@ def test_led_bar_graph_value():
|
||||
pin1.state = False
|
||||
assert graph.value == -2/3
|
||||
|
||||
def test_led_bar_graph_active_low():
|
||||
pin1 = MockPin(2)
|
||||
pin2 = MockPin(3)
|
||||
pin3 = MockPin(4)
|
||||
with LEDBarGraph(pin1, pin2, pin3, active_high=False) as graph:
|
||||
assert not graph.active_high
|
||||
assert not graph[0].active_high
|
||||
assert not graph[1].active_high
|
||||
assert not graph[2].active_high
|
||||
graph.value = 0
|
||||
assert graph.value == 0
|
||||
assert all((pin1.state, pin2.state, pin3.state))
|
||||
graph.value = 1
|
||||
assert graph.value == 1
|
||||
assert not any((pin1.state, pin2.state, pin3.state))
|
||||
graph.value = 1/3
|
||||
assert graph.value == 1/3
|
||||
assert not pin1.state and pin2.state and pin3.state
|
||||
graph.value = -1/3
|
||||
assert graph.value == -1/3
|
||||
assert not pin3.state and pin1.state and pin2.state
|
||||
|
||||
def test_led_bar_graph_pwm_value():
|
||||
pin1 = MockPWMPin(2)
|
||||
pin2 = MockPWMPin(3)
|
||||
pin3 = MockPWMPin(4)
|
||||
with LEDBarGraph(pin1, pin2, pin3, pwm=True) as graph:
|
||||
assert isinstance(graph[0], PWMLED)
|
||||
assert isinstance(graph[1], PWMLED)
|
||||
assert isinstance(graph[2], PWMLED)
|
||||
graph.value = 0
|
||||
assert graph.value == 0
|
||||
assert not any((pin1.state, pin2.state, pin3.state))
|
||||
graph.value = 1
|
||||
assert graph.value == 1
|
||||
assert all((pin1.state, pin2.state, pin3.state))
|
||||
graph.value = 1/3
|
||||
assert graph.value == 1/3
|
||||
assert pin1.state and not (pin2.state or pin3.state)
|
||||
graph.value = -1/3
|
||||
assert graph.value == -1/3
|
||||
assert pin3.state and not (pin1.state or pin2.state)
|
||||
graph.value = 1/2
|
||||
assert graph.value == 1/2
|
||||
assert (pin1.state, pin2.state, pin3.state) == (1, 0.5, 0)
|
||||
pin1.state = 0
|
||||
pin3.state = 1
|
||||
assert graph.value == -1/2
|
||||
|
||||
def test_led_bar_graph_bad_value():
|
||||
pin1 = MockPin(2)
|
||||
pin2 = MockPin(3)
|
||||
pin3 = MockPin(4)
|
||||
with LEDBarGraph(pin1, pin2, pin3) as graph:
|
||||
with pytest.raises(ValueError):
|
||||
graph.value = -2
|
||||
with pytest.raises(ValueError):
|
||||
graph.value = 2
|
||||
|
||||
def test_led_bar_graph_bad_init():
|
||||
pin1 = MockPin(2)
|
||||
pin2 = MockPin(3)
|
||||
pin3 = MockPin(4)
|
||||
with pytest.raises(TypeError):
|
||||
LEDBarGraph(pin1, pin2, foo=pin3)
|
||||
with pytest.raises(ValueError):
|
||||
LEDBarGraph(pin1, pin2, pin3, initial_value=-2)
|
||||
with pytest.raises(ValueError):
|
||||
LEDBarGraph(pin1, pin2, pin3, initial_value=2)
|
||||
|
||||
def test_led_bar_graph_initial_value():
|
||||
pin1 = MockPin(2)
|
||||
pin2 = MockPin(3)
|
||||
pin3 = MockPin(4)
|
||||
with LEDBarGraph(pin1, pin2, pin3, initial_value=1/3) as graph:
|
||||
assert graph.value == 1/3
|
||||
assert pin1.state and not (pin2.state or pin3.state)
|
||||
with LEDBarGraph(pin1, pin2, pin3, initial_value=-1/3) as graph:
|
||||
assert graph.value == -1/3
|
||||
assert pin3.state and not (pin1.state or pin2.state)
|
||||
|
||||
def test_led_bar_graph_pwm_initial_value():
|
||||
pin1 = MockPWMPin(2)
|
||||
pin2 = MockPWMPin(3)
|
||||
pin3 = MockPWMPin(4)
|
||||
with LEDBarGraph(pin1, pin2, pin3, pwm=True, initial_value=0.5) as graph:
|
||||
assert graph.value == 0.5
|
||||
assert (pin1.state, pin2.state, pin3.state) == (1, 0.5, 0)
|
||||
with LEDBarGraph(pin1, pin2, pin3, pwm=True, initial_value=-0.5) as graph:
|
||||
assert graph.value == -0.5
|
||||
assert (pin1.state, pin2.state, pin3.state) == (0, 0.5, 1)
|
||||
|
||||
def test_led_borg():
|
||||
pins = [MockPWMPin(n) for n in (17, 27, 22)]
|
||||
with LedBorg() as board:
|
||||
assert [device.pin for device in board._leds] == pins
|
||||
|
||||
def test_pi_liter():
|
||||
pins = [MockPin(n) for n in (4, 17, 27, 18, 22, 23, 24, 25)]
|
||||
@@ -347,13 +548,32 @@ def test_traffic_lights():
|
||||
green_pin = MockPin(4)
|
||||
with TrafficLights(red_pin, amber_pin, green_pin) as board:
|
||||
board.red.on()
|
||||
assert board.red.value
|
||||
assert not board.amber.value
|
||||
assert not board.yellow.value
|
||||
assert not board.green.value
|
||||
assert red_pin.state
|
||||
assert not amber_pin.state
|
||||
assert not green_pin.state
|
||||
with TrafficLights(red=red_pin, yellow=amber_pin, green=green_pin) as board:
|
||||
board.yellow.on()
|
||||
assert not board.red.value
|
||||
assert board.amber.value
|
||||
assert board.yellow.value
|
||||
assert not board.green.value
|
||||
assert not red_pin.state
|
||||
assert amber_pin.state
|
||||
assert not green_pin.state
|
||||
|
||||
def test_traffic_lights_bad_init():
|
||||
with pytest.raises(ValueError):
|
||||
TrafficLights()
|
||||
red_pin = MockPin(2)
|
||||
amber_pin = MockPin(3)
|
||||
green_pin = MockPin(4)
|
||||
yellow_pin = MockPin(5)
|
||||
with pytest.raises(ValueError):
|
||||
TrafficLights(red=red_pin, amber=amber_pin, yellow=yellow_pin, green=green_pin)
|
||||
|
||||
def test_pi_traffic():
|
||||
pins = [MockPin(n) for n in (9, 10, 11)]
|
||||
@@ -365,6 +585,22 @@ def test_snow_pi():
|
||||
with SnowPi() as board:
|
||||
assert [device.pin for device in board.leds] == pins
|
||||
|
||||
def test_snow_pi_initial_value():
|
||||
with SnowPi() as board:
|
||||
assert all(device.pin.state == False for device in board.leds)
|
||||
with SnowPi(initial_value=False) as board:
|
||||
assert all(device.pin.state == False for device in board.leds)
|
||||
with SnowPi(initial_value=True) as board:
|
||||
assert all(device.pin.state == True for device in board.leds)
|
||||
with SnowPi(initial_value=0.5) as board:
|
||||
assert all(device.pin.state == True for device in board.leds)
|
||||
|
||||
def test_snow_pi_initial_value_pwm():
|
||||
pins = [MockPWMPin(n) for n in (23, 24, 25, 17, 18, 22, 7, 8, 9)]
|
||||
with SnowPi(pwm=True, initial_value=0.5) as board:
|
||||
assert [device.pin for device in board.leds] == pins
|
||||
assert all(device.pin.state == 0.5 for device in board.leds)
|
||||
|
||||
def test_traffic_lights_buzzer():
|
||||
red_pin = MockPin(2)
|
||||
amber_pin = MockPin(3)
|
||||
@@ -400,20 +636,34 @@ def test_robot():
|
||||
assert (
|
||||
[device.pin for device in robot.left_motor] +
|
||||
[device.pin for device in robot.right_motor]) == pins
|
||||
assert robot.value == (0, 0)
|
||||
robot.forward()
|
||||
assert [pin.state for pin in pins] == [1, 0, 1, 0]
|
||||
assert robot.value == (1, 1)
|
||||
robot.backward()
|
||||
assert [pin.state for pin in pins] == [0, 1, 0, 1]
|
||||
assert robot.value == (-1, -1)
|
||||
robot.forward(0.5)
|
||||
assert [pin.state for pin in pins] == [0.5, 0, 0.5, 0]
|
||||
assert robot.value == (0.5, 0.5)
|
||||
robot.left()
|
||||
assert [pin.state for pin in pins] == [0, 1, 1, 0]
|
||||
assert robot.value == (-1, 1)
|
||||
robot.right()
|
||||
assert [pin.state for pin in pins] == [1, 0, 0, 1]
|
||||
assert robot.value == (1, -1)
|
||||
robot.reverse()
|
||||
assert [pin.state for pin in pins] == [0, 1, 1, 0]
|
||||
assert robot.value == (-1, 1)
|
||||
robot.stop()
|
||||
assert [pin.state for pin in pins] == [0, 0, 0, 0]
|
||||
assert robot.value == (0, 0)
|
||||
robot.value = (-1, -1)
|
||||
assert robot.value == (-1, -1)
|
||||
robot.value = (0.5, 1)
|
||||
assert robot.value == (0.5, 1)
|
||||
robot.value = (0, -0.5)
|
||||
assert robot.value == (0, -0.5)
|
||||
|
||||
def test_ryanteck_robot():
|
||||
pins = [MockPWMPin(n) for n in (17, 18, 22, 23)]
|
||||
@@ -430,11 +680,15 @@ def test_energenie_bad_init():
|
||||
Energenie()
|
||||
with pytest.raises(ValueError):
|
||||
Energenie(0)
|
||||
with pytest.raises(ValueError):
|
||||
Energenie(5)
|
||||
|
||||
def test_energenie():
|
||||
pins = [MockPin(n) for n in (17, 22, 23, 27, 24, 25)]
|
||||
with Energenie(1, initial_value=True) as device1, \
|
||||
Energenie(2, initial_value=False) as device2:
|
||||
assert repr(device1) == '<gpiozero.Energenie object on socket 1>'
|
||||
assert repr(device2) == '<gpiozero.Energenie object on socket 2>'
|
||||
assert device1.value
|
||||
assert not device2.value
|
||||
[pin.clear_states() for pin in pins]
|
||||
@@ -455,4 +709,5 @@ def test_energenie():
|
||||
pins[3].assert_states_and_times([(0.0, True), (0.0, True)])
|
||||
pins[4].assert_states_and_times([(0.0, False)])
|
||||
pins[5].assert_states_and_times([(0.0, False), (0.1, True), (0.25, False)])
|
||||
|
||||
device1.close()
|
||||
assert repr(device1) == '<gpiozero.Energenie object closed>'
|
||||
|
||||
@@ -120,6 +120,7 @@ def test_mean():
|
||||
values = list(values)
|
||||
random.shuffle(values)
|
||||
assert mean(values) == result
|
||||
assert mean(iter(values)) == result
|
||||
|
||||
def test_mean_big_data():
|
||||
c = 1e9
|
||||
|
||||
@@ -34,13 +34,25 @@ def test_input_initial_values():
|
||||
assert pin.pull == 'down'
|
||||
assert not device.pull_up
|
||||
|
||||
def test_input_is_active():
|
||||
def test_input_is_active_low():
|
||||
pin = MockPin(2)
|
||||
with InputDevice(pin, pull_up=True) as device:
|
||||
pin.drive_high()
|
||||
assert not device.is_active
|
||||
assert repr(device) == '<gpiozero.InputDevice object on pin MOCK2, pull_up=True, is_active=False>'
|
||||
pin.drive_low()
|
||||
assert device.is_active
|
||||
assert repr(device) == '<gpiozero.InputDevice object on pin MOCK2, pull_up=True, is_active=True>'
|
||||
|
||||
def test_input_is_active_high():
|
||||
pin = MockPin(2)
|
||||
with InputDevice(pin, pull_up=False) as device:
|
||||
pin.drive_high()
|
||||
assert device.is_active
|
||||
assert repr(device) == '<gpiozero.InputDevice object on pin MOCK2, pull_up=False, is_active=True>'
|
||||
pin.drive_low()
|
||||
assert not device.is_active
|
||||
assert repr(device) == '<gpiozero.InputDevice object on pin MOCK2, pull_up=False, is_active=False>'
|
||||
|
||||
def test_input_pulled_up():
|
||||
pin = MockPulledUpPin(2)
|
||||
@@ -52,20 +64,20 @@ def test_input_event_activated():
|
||||
pin = MockPin(2)
|
||||
with DigitalInputDevice(pin) as device:
|
||||
device.when_activated = lambda: event.set()
|
||||
assert not event.wait(0)
|
||||
assert not event.is_set()
|
||||
pin.drive_high()
|
||||
assert event.wait(0)
|
||||
assert event.is_set()
|
||||
|
||||
def test_input_event_deactivated():
|
||||
event = Event()
|
||||
pin = MockPin(2)
|
||||
with DigitalInputDevice(pin) as device:
|
||||
device.when_deactivated = lambda: event.set()
|
||||
assert not event.wait(0)
|
||||
assert not event.is_set()
|
||||
pin.drive_high()
|
||||
assert not event.wait(0)
|
||||
assert not event.is_set()
|
||||
pin.drive_low()
|
||||
assert event.wait(0)
|
||||
assert event.is_set()
|
||||
|
||||
def test_input_wait_active():
|
||||
pin = MockPin(2)
|
||||
@@ -83,11 +95,14 @@ def test_input_wait_inactive():
|
||||
def test_input_smoothed_attrib():
|
||||
pin = MockPin(2)
|
||||
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 device.threshold == 0.5
|
||||
assert device.queue_len == 5
|
||||
assert not device.partial
|
||||
device._queue.start()
|
||||
assert not device.is_active
|
||||
with pytest.raises(InputDeviceError):
|
||||
device.threshold = 1
|
||||
|
||||
def test_input_smoothed_values():
|
||||
pin = MockPin(2)
|
||||
|
||||
@@ -160,15 +160,15 @@ def test_mock_pin_edges():
|
||||
pin.when_changed = changed
|
||||
pin.drive_high()
|
||||
assert pin.state
|
||||
assert fired.wait(0)
|
||||
assert fired.is_set()
|
||||
fired.clear()
|
||||
pin.edges = 'falling'
|
||||
pin.drive_low()
|
||||
assert not pin.state
|
||||
assert fired.wait(0)
|
||||
assert fired.is_set()
|
||||
fired.clear()
|
||||
pin.drive_high()
|
||||
assert pin.state
|
||||
assert not fired.wait(0)
|
||||
assert not fired.is_set()
|
||||
assert pin.edges == 'falling'
|
||||
|
||||
|
||||