diff --git a/docs/changelog.rst b/docs/changelog.rst index d926ba9..aa5d5a1 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,7 +5,7 @@ Changelog .. currentmodule:: gpiozero -Release 1.2.0 (2016-04-??) +Release 1.2.0 (2016-04-10) ========================== * Added :class:`Energenie` class for controlling Energenie plugs (`#69`_) @@ -32,7 +32,16 @@ Release 1.2.0 (2016-04-??) And I'll just add a note of thanks to the many people in the community who contributed to this release: we've had some great PRs, suggestions, and bug -reports in this version - keep 'em coming! +reports in this version. Of particular note: + +* Schelto van Doorn was instrumental in adding support for numerous ADC chips +* Alex Eames generously donated a RasPiO Analog board which was extremely + useful in developing the software SPI interface (and testing the ADC support) +* Andrew Scheller squashed several dozen bugs (usually a day or so after Dave + had introduced them ;) + +As always, many thanks to the whole community - we look forward to hearing from +you more in 1.3! .. _#69: https://github.com/RPi-Distro/python-gpiozero/issues/69 .. _#109: https://github.com/RPi-Distro/python-gpiozero/issues/109 diff --git a/gpiozero/__init__.py b/gpiozero/__init__.py index f615082..63191c1 100644 --- a/gpiozero/__init__.py +++ b/gpiozero/__init__.py @@ -51,6 +51,8 @@ from .exc import ( GPIOZeroWarning, SPIWarning, SPISoftwareFallback, + PinWarning, + PinNonPhysical, ) from .devices import ( Device, diff --git a/gpiozero/exc.py b/gpiozero/exc.py index 60e83f7..3e8ed50 100644 --- a/gpiozero/exc.py +++ b/gpiozero/exc.py @@ -121,3 +121,9 @@ class SPIWarning(GPIOZeroWarning): class SPISoftwareFallback(SPIWarning): "Warning raised when falling back to the software implementation" +class PinWarning(GPIOZeroWarning): + "Base class for warnings related to pin implementations" + +class PinNonPhysical(PinWarning): + "Warning raised when a non-physical pin is specified in a constructor" + diff --git a/gpiozero/mixins.py b/gpiozero/mixins.py index bbe656d..fd110ae 100644 --- a/gpiozero/mixins.py +++ b/gpiozero/mixins.py @@ -322,6 +322,7 @@ class HoldMixin(EventsMixin): ``True``) at internals defined by :attr:`hold_time`. """ def __init__(self, *args, **kwargs): + self._hold_thread = None super(HoldMixin, self).__init__(*args, **kwargs) self._when_held = None self._held_from = None diff --git a/gpiozero/pins/data.py b/gpiozero/pins/data.py index d15315e..b824d7f 100644 --- a/gpiozero/pins/data.py +++ b/gpiozero/pins/data.py @@ -261,11 +261,11 @@ PI_REVISIONS = { '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}, ), + '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}, ), } @@ -479,8 +479,12 @@ class PiBoardInfo(namedtuple('PiBoardInfo', ( The pin function you wish to determine pull-up for. Usually this is something like "GPIO9" for Broadcom GPIO pin 9. """ - header, number = self.physical_pin(function) - return self.headers[header][number].pull_up + try: + header, number = self.physical_pin(function) + except PinNoPins: + return False + else: + return self.headers[header][number].pull_up _PI_REVISION = None diff --git a/gpiozero/pins/native.py b/gpiozero/pins/native.py index 447aa5d..8244bac 100644 --- a/gpiozero/pins/native.py +++ b/gpiozero/pins/native.py @@ -12,6 +12,7 @@ import os import mmap import errno import struct +import warnings from time import sleep from threading import Thread, Event, Lock from collections import Counter @@ -24,6 +25,8 @@ from ..exc import ( PinInvalidFunction, PinFixedPull, PinSetInput, + PinNonPhysical, + PinNoPins, ) @@ -213,7 +216,12 @@ class NativePin(Pin): return cls._PINS[number] except KeyError: self = super(NativePin, cls).__new__(cls) - cls._PINS[number] = self + try: + cls.PI_INFO.physical_pin('GPIO%d' % number) + except PinNoPins: + warnings.warn( + PinNonPhysical( + 'no physical pins exist for GPIO%d' % number)) self._number = number self._func_offset = self._MEM.GPFSEL_OFFSET + (number // 10) self._func_shift = (number % 10) * 3 @@ -238,6 +246,7 @@ class NativePin(Pin): self.pull = 'up' if cls.PI_INFO.pulled_up('GPIO%d' % number) else 'floating' self.bounce = None self.edges = 'both' + cls._PINS[number] = self return self def __repr__(self): diff --git a/gpiozero/pins/pigpiod.py b/gpiozero/pins/pigpiod.py index 25b3270..3dafefc 100644 --- a/gpiozero/pins/pigpiod.py +++ b/gpiozero/pins/pigpiod.py @@ -6,6 +6,7 @@ from __future__ import ( ) str = type('') +import warnings import pigpio from . import Pin @@ -17,6 +18,8 @@ from ..exc import ( PinInvalidPull, PinInvalidBounce, PinInvalidState, + PinNonPhysical, + PinNoPins, ) @@ -107,12 +110,17 @@ class PiGPIOPin(Pin): return cls._PINS[(host, port, number)] except KeyError: self = super(PiGPIOPin, cls).__new__(cls) - cls._PINS[(host, port, number)] = self 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) + except PinNoPins: + warnings.warn( + PinNonPhysical( + 'no physical pins exist for GPIO%d' % number)) self._host = host self._port = port self._number = number @@ -125,11 +133,11 @@ class PiGPIOPin(Pin): try: self._connection.set_mode(self._number, pigpio.INPUT) except pigpio.error as e: - del cls._PINS[(host, port, number)] 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 def __repr__(self): diff --git a/gpiozero/pins/rpigpio.py b/gpiozero/pins/rpigpio.py index 99ca0d9..9fea142 100644 --- a/gpiozero/pins/rpigpio.py +++ b/gpiozero/pins/rpigpio.py @@ -6,6 +6,7 @@ from __future__ import ( ) str = type('') +import warnings from RPi import GPIO from . import Pin @@ -18,6 +19,8 @@ from ..exc import ( PinInvalidState, PinInvalidBounce, PinPWMFixedValue, + PinNonPhysical, + PinNoPins, ) @@ -84,7 +87,12 @@ class RPiGPIOPin(Pin): return cls._PINS[number] except KeyError: self = super(RPiGPIOPin, cls).__new__(cls) - cls._PINS[number] = self + try: + cls.PI_INFO.physical_pin('GPIO%d' % number) + except PinNoPins: + warnings.warn( + PinNonPhysical( + 'no physical pins exist for GPIO%d' % number)) self._number = number self._pull = 'up' if cls.PI_INFO.pulled_up('GPIO%d' % number) else 'floating' self._pwm = None @@ -94,6 +102,7 @@ class RPiGPIOPin(Pin): self._when_changed = None self._edges = GPIO.BOTH GPIO.setup(self._number, GPIO.IN, self.GPIO_PULL_UPS[self._pull]) + cls._PINS[number] = self return self def __repr__(self): diff --git a/gpiozero/pins/rpio.py b/gpiozero/pins/rpio.py index 845424c..5118150 100644 --- a/gpiozero/pins/rpio.py +++ b/gpiozero/pins/rpio.py @@ -7,6 +7,7 @@ from __future__ import ( str = type('') +import warnings import RPIO import RPIO.PWM from RPIO.Exceptions import InvalidChannelException @@ -21,6 +22,8 @@ from ..exc import ( PinInvalidBounce, PinInvalidState, PinPWMError, + PinNonPhysical, + PinNoPins, ) @@ -81,7 +84,12 @@ class RPIOPin(Pin): return cls._PINS[number] except KeyError: self = super(RPIOPin, cls).__new__(cls) - cls._PINS[number] = self + try: + cls.PI_INFO.physical_pin('GPIO%d' % number) + except PinNoPins: + warnings.warn( + PinNonPhysical( + 'no physical pins exist for GPIO%d' % number)) self._number = number self._pull = 'up' if cls.PI_INFO.pulled_up('GPIO%d' % number) else 'floating' self._pwm = False @@ -93,6 +101,7 @@ class RPIOPin(Pin): RPIO.setup(self._number, RPIO.IN, self.GPIO_PULL_UPS[self._pull]) except InvalidChannelException as e: raise ValueError(e) + cls._PINS[number] = self return self def __repr__(self): diff --git a/gpiozero/tools.py b/gpiozero/tools.py index 21cdfc0..11a079a 100644 --- a/gpiozero/tools.py +++ b/gpiozero/tools.py @@ -16,7 +16,7 @@ try: except ImportError: pass from itertools import cycle -from math import sin, cos, radians +from math import sin, cos, pi try: from statistics import mean except ImportError: @@ -285,12 +285,11 @@ def random_values(): yield random() -def sin_values(): +def sin_values(period=360): """ Provides an infinite source of values representing a sine wave (from -1 to - +1), calculated as the result of applying sign to a simple degrees counter - that increments by one for each requested value. For example, to produce a - "siren" effect with a couple of LEDs:: + +1) which repeats every *period* values. For example, to produce a "siren" + effect with a couple of LEDs that repeats once a second:: from gpiozero import PWMLED from gpiozero.tools import sin_values, scaled, inverted @@ -300,22 +299,22 @@ def sin_values(): blue = PWMLED(3) red.source_delay = 0.01 blue.source_delay = 0.01 - red.source = scaled(sin_values(), 0, 1, -1, 1) + red.source = scaled(sin_values(100), 0, 1, -1, 1) blue.source = inverted(red.values) pause() - If you require a wider range than 0 to 1, see :func:`scaled`. + If you require a different range than -1 to +1, see :func:`scaled`. """ - for d in cycle(range(360)): - yield sin(radians(d)) + angles = (2 * pi * i / period for i in range(period)) + for a in cycle(angles): + yield sin(a) -def cos_values(): +def cos_values(period=360): """ Provides an infinite source of values representing a cosine wave (from -1 - to +1), calculated as the result of applying sign to a simple degrees - counter that increments by one for each requested value. For example, to - produce a "siren" effect with a couple of LEDs:: + to +1) which repeats every *period* values. For example, to produce a + "siren" effect with a couple of LEDs that repeats once a second:: from gpiozero import PWMLED from gpiozero.tools import cos_values, scaled, inverted @@ -325,12 +324,13 @@ def cos_values(): blue = PWMLED(3) red.source_delay = 0.01 blue.source_delay = 0.01 - red.source = scaled(cos_values(), 0, 1, -1, 1) + red.source = scaled(cos_values(100), 0, 1, -1, 1) blue.source = inverted(red.values) pause() - If you require a wider range than 0 to 1, see :func:`scaled`. + If you require a different range than -1 to +1, see :func:`scaled`. """ - for d in cycle(range(360)): - yield cos(radians(d)) + angles = (2 * pi * i / period for i in range(period)) + for a in cycle(angles): + yield cos(a)