From 83aecd470e1192e500da928bb643d634afc98e0e Mon Sep 17 00:00:00 2001 From: Sylvan Butler Date: Sun, 22 Feb 2015 20:37:54 -0700 Subject: [PATCH 1/8] create input_pins() to read multiple pins with one call - MCP230xx --- Adafruit_GPIO/MCP230xx.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Adafruit_GPIO/MCP230xx.py b/Adafruit_GPIO/MCP230xx.py index 42ac1ed..b9d86e8 100755 --- a/Adafruit_GPIO/MCP230xx.py +++ b/Adafruit_GPIO/MCP230xx.py @@ -100,11 +100,18 @@ def input(self, pin): """Read the specified pin and return GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low. """ - self._validate_pin(pin) + return self.input_pins([pin])[0] + + def input_pins(self, pins): + """Read multiple pins specified in the given list and return list of pin values + GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low. + """ + for pin in pins: + self._validate_pin(pin) # Get GPIO state. gpio = self._device.readList(self.GPIO, self.gpio_bytes) # Return True if pin's bit is set. - return (gpio[int(pin/8)] & 1 << (int(pin%8))) > 0 + return [(gpio[int(pin/8)] & 1 << (int(pin%8))) > 0 for pin in pins] def pullup(self, pin, enabled): """Turn on the pull-up resistor for the specified pin if enabled is True, From 6f795d333ceeb87e93c611815b5a62c9a53538c6 Mon Sep 17 00:00:00 2001 From: Sylvan Butler Date: Sun, 22 Feb 2015 20:53:14 -0700 Subject: [PATCH 2/8] create input_pins() for FT232H and GPIO (none tested) --- Adafruit_GPIO/FT232H.py | 11 ++++++++--- Adafruit_GPIO/GPIO.py | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/Adafruit_GPIO/FT232H.py b/Adafruit_GPIO/FT232H.py index 7375923..065a9a9 100644 --- a/Adafruit_GPIO/FT232H.py +++ b/Adafruit_GPIO/FT232H.py @@ -380,10 +380,15 @@ def output_pins(self, pins, write=True): def input(self, pin): """Read the specified pin and return HIGH/true if the pin is pulled high, or LOW/false if pulled low.""" - if pin < 0 or pin > 15: + return self.input_pins([pin])[0] + + def input_pins(self, pins): + """Read multiple pins specified in the given list and return list of pin values + GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low.""" + if [pin for pin in pins if pin < 0 or pin > 15]: raise ValueError('Pin must be between 0 and 15 (inclusive).') - pins = self.mpsse_read_gpio() - return ((pins >> pin) & 0x0001) == 1 + _pins = self.mpsse_read_gpio() + return [((_pins >> pin) & 0x0001) == 1 for pin in pins] class SPI(object): diff --git a/Adafruit_GPIO/GPIO.py b/Adafruit_GPIO/GPIO.py index 0accc06..b5cb126 100644 --- a/Adafruit_GPIO/GPIO.py +++ b/Adafruit_GPIO/GPIO.py @@ -55,6 +55,12 @@ def input(self, pin): or LOW/false if pulled low.""" raise NotImplementedError + def input_pins(self, pins): + """Read multiple pins specified in the given list and return list of pin values + GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low. + """ + raise NotImplementedError + def set_high(self, pin): """Set the specified pin HIGH.""" self.output(pin, HIGH) @@ -171,6 +177,13 @@ def input(self, pin): """ return self.rpi_gpio.input(pin) + def input_pins(self, pins): + """Read multiple pins specified in the given list and return list of pin values + GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low. + """ + # maybe rpi has a mass read... it would be more efficient to use it if it exists + return [self.rpi_gpio.input(pin) for pin in pins] + def add_event_detect(self, pin, edge, callback=None, bouncetime=-1): """Enable edge detection events for a particular GPIO channel. Pin should be type IN. Edge must be RISING, FALLING or BOTH. Callback is a @@ -254,6 +267,13 @@ def input(self, pin): """ return self.bbio_gpio.input(pin) + def input_pins(self, pins): + """Read multiple pins specified in the given list and return list of pin values + GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low. + """ + # maybe bbb has a mass read... it would be more efficient to use it if it exists + return [self.bbio_gpio.input(pin) for pin in pins] + def add_event_detect(self, pin, edge, callback=None, bouncetime=-1): """Enable edge detection events for a particular GPIO channel. Pin should be type IN. Edge must be RISING, FALLING or BOTH. Callback is a From af4a24b390e3ce5059e5179a029bdc031ca59269 Mon Sep 17 00:00:00 2001 From: Sylvan Butler Date: Sat, 25 Apr 2015 00:38:26 -0600 Subject: [PATCH 3/8] remove redundant output() code, add validation to output_pins() --- Adafruit_GPIO/MCP230xx.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) mode change 100755 => 100644 Adafruit_GPIO/MCP230xx.py diff --git a/Adafruit_GPIO/MCP230xx.py b/Adafruit_GPIO/MCP230xx.py old mode 100755 new mode 100644 index b9d86e8..9e55a15 --- a/Adafruit_GPIO/MCP230xx.py +++ b/Adafruit_GPIO/MCP230xx.py @@ -71,22 +71,17 @@ def setup(self, pin, value): def output(self, pin, value): """Set the specified pin the provided high/low value. Value should be - either GPIO.HIGH/GPIO.LOW or a boolean (True = high). + either GPIO.HIGH/GPIO.LOW or a boolean (True = HIGH). """ - self._validate_pin(pin) - # Set bit on or off. - if value: - self.gpio[int(pin/8)] |= 1 << (int(pin%8)) - else: - self.gpio[int(pin/8)] &= ~(1 << (int(pin%8))) - # Write GPIO state. - self.write_gpio() + self.output_pins({pin: value}) def output_pins(self, pins): """Set multiple pins high or low at once. Pins should be a dict of pin name to pin value (HIGH/True for 1, LOW/False for 0). All provided pins will be set to the given values. """ + for pin in pins.keys(): + self._validate_pin(pin) # Set each changed pin's bit. for pin, value in pins.iteritems(): if value: From c56a9327a08cb2eb94ed5118d27584954da146a0 Mon Sep 17 00:00:00 2001 From: Sylvan Butler Date: Sat, 25 Apr 2015 01:36:08 -0600 Subject: [PATCH 4/8] basic PCF8574 functionality --- Adafruit_GPIO/PCF8574.py | 101 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 Adafruit_GPIO/PCF8574.py diff --git a/Adafruit_GPIO/PCF8574.py b/Adafruit_GPIO/PCF8574.py new file mode 100644 index 0000000..f4fa10a --- /dev/null +++ b/Adafruit_GPIO/PCF8574.py @@ -0,0 +1,101 @@ +''' +Adafruit compatible using BaseGPIO class to represent a PCF8574/A IO expander +Copyright (C) 2015 Sylvan Butler + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +''' + +import Adafruit_GPIO as GPIO +import Adafruit_GPIO.I2C as I2C + + + +IN = GPIO.IN +OUT = GPIO.OUT +HIGH = GPIO.HIGH +LOW = GPIO.LOW + + +class PCF8574(GPIO.BaseGPIO): + """Class to represent a PCF8574 or PCF8574A GPIO extender. Compatible + with the Adafruit_GPIO BaseGPIO class so it can be used as a custom GPIO + class for interacting with device. + """ + + NUM_GPIO = 8 + + def __init__(self, address, i2c=None, **kwargs): + address = int(address) + self.__name__ = \ + "PCF8574" if address in range(0x20, 0x28) else \ + "PCF8574A" if address in range(0x38, 0x40) else \ + "Bad address for PCF8574(A): 0x%02X not in range [0x20..0x27, 0x38..0x3F]" % address + if self.__name__[0] != 'P': + raise ValueError(self.__name__) + # Create I2C device. + if i2c is None: + i2c = I2C + self._device = i2c.get_i2c_device(address, **kwargs) + # Buffer register values so they can be changed without reading. + self.iodir = 0xFF # Default direction to all inputs is in + self.gpio = 0x00 + self._write_pins() + + + def _write_pins(self): + self._device.writeRaw8(self.gpio | self.iodir) + + def _read_pins(self): + return self._device.readRaw8() & self.iodir + + def _validate_pin(self, pin): + # Raise an exception if pin is outside the range of allowed values. + if pin < 0 or pin >= self.NUM_GPIO: + raise ValueError('Invalid GPIO value, must be between 0 and {0}.'.format(self.NUM_GPIO)) + + def _bit2(self, src, bit, val): + bit = 1 << bit + return (src | bit) if val else (src & ~bit) + + + def setup(self, pin, mode): + self.setup_pins({pin: mode}) + + def setup_pins(self, pins): + if False in [y for x,y in [(self._validate_pin(pin),mode in (IN,OUT)) for pin,mode in pins.iteritems()]]: + raise ValueError('Invalid MODE, IN or OUT') + for pin,mode in pins.iteritems(): + self.iodir = self._bit2(self.iodir, pin, mode) + self._write_pins() + + + def output(self, pin, value): + self.output_pins({pin: value}) + + def output_pins(self, pins): + [self._validate_pin(pin) for pin in pins.keys()] + for pin,value in pins.iteritems(): + self.gpio = self._bit2(self.gpio, pin, bool(value)) + self._write_pins() + + + def input(self, pin): + return self.input_pins([pin])[0] + + def input_pins(self, pins): + [self._validate_pin(pin) for pin in pins] + inp = self._read_pins() + return [bool(inp & pin) for pin in pins] From 4229645f8e2afbb896e6da612fe73f75d9a79341 Mon Sep 17 00:00:00 2001 From: Sylvan Butler Date: Sat, 25 Apr 2015 11:21:57 -0600 Subject: [PATCH 5/8] provide rudimentary input_pins(), pull in some more methods useful in derived classes --- Adafruit_GPIO/GPIO.py | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/Adafruit_GPIO/GPIO.py b/Adafruit_GPIO/GPIO.py index b5cb126..2ba1947 100644 --- a/Adafruit_GPIO/GPIO.py +++ b/Adafruit_GPIO/GPIO.py @@ -55,12 +55,6 @@ def input(self, pin): or LOW/false if pulled low.""" raise NotImplementedError - def input_pins(self, pins): - """Read multiple pins specified in the given list and return list of pin values - GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low. - """ - raise NotImplementedError - def set_high(self, pin): """Set the specified pin HIGH.""" self.output(pin, HIGH) @@ -77,15 +71,19 @@ def is_low(self, pin): """Return true if the specified pin is pulled low.""" return self.input(pin) == LOW + +# Basic implementation of multiple pin methods just loops through pins and +# processes each one individually. This is not optimal, but derived classes can +# provide a more optimal implementation that deals with groups of pins +# simultaneously. +# See MCP230xx or PCF8574 classes for examples of optimized implementations. + def output_pins(self, pins): """Set multiple pins high or low at once. Pins should be a dict of pin name to pin value (HIGH/True for 1, LOW/False for 0). All provided pins will be set to the given values. """ - # General implementation just loops through pins and writes them out - # manually. This is not optimized, but subclasses can choose to implement - # a more optimal batch output implementation. See the MCP230xx class for - # example of optimized implementation. + # General implementation that can be optimized by derived classes. for pin, value in pins.iteritems(): self.output(pin, value) @@ -93,10 +91,18 @@ def setup_pins(self, pins): """Setup multiple pins as inputs or outputs at once. Pins should be a dict of pin name to pin type (IN or OUT). """ - # General implementation that can be improved by subclasses. + # General implementation that can be optimized by derived classes. for pin, value in pins.iteritems(): self.setup(pin, value) + def input_pins(self, pins): + """Read multiple pins specified in the given list and return list of pin values + GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low. + """ + # General implementation that can be optimized by derived classes. + return [self.input(pin) for pin in pins] + + def add_event_detect(self, pin, edge): """Enable edge detection events for a particular GPIO channel. Pin should be type IN. Edge must be RISING, FALLING or BOTH. @@ -133,6 +139,19 @@ def cleanup(self, pin=None): """ raise NotImplementedError + +# helper functions useful to derived classes + + def _validate_pin(self, pin): + # Raise an exception if pin is outside the range of allowed values. + if pin < 0 or pin >= self.NUM_GPIO: + raise ValueError('Invalid GPIO value, must be between 0 and {0}.'.format(self.NUM_GPIO)) + + def _bit2(self, src, bit, val): + bit = 1 << bit + return (src | bit) if val else (src & ~bit) + + class RPiGPIOAdapter(BaseGPIO): """GPIO implementation for the Raspberry Pi using the RPi.GPIO library.""" From e07a2f60955cbc5df27b7778aa5e7415a4d35e11 Mon Sep 17 00:00:00 2001 From: Sylvan Butler Date: Sat, 25 Apr 2015 11:50:42 -0600 Subject: [PATCH 6/8] fix input_pins(), reduce required client knowledge of I/Os, use BaseGPIO helper methods --- Adafruit_GPIO/PCF8574.py | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/Adafruit_GPIO/PCF8574.py b/Adafruit_GPIO/PCF8574.py index f4fa10a..9ec8996 100644 --- a/Adafruit_GPIO/PCF8574.py +++ b/Adafruit_GPIO/PCF8574.py @@ -37,7 +37,7 @@ class for interacting with device. NUM_GPIO = 8 - def __init__(self, address, i2c=None, **kwargs): + def __init__(self, address=0x27, busnum=None, i2c=None, **kwargs): address = int(address) self.__name__ = \ "PCF8574" if address in range(0x20, 0x28) else \ @@ -46,9 +46,9 @@ def __init__(self, address, i2c=None, **kwargs): if self.__name__[0] != 'P': raise ValueError(self.__name__) # Create I2C device. - if i2c is None: - i2c = I2C - self._device = i2c.get_i2c_device(address, **kwargs) + i2c = i2c or I2C + busnum = busnum or i2c.get_default_bus() + self._device = i2c.get_i2c_device(address, busnum, **kwargs) # Buffer register values so they can be changed without reading. self.iodir = 0xFF # Default direction to all inputs is in self.gpio = 0x00 @@ -61,15 +61,6 @@ def _write_pins(self): def _read_pins(self): return self._device.readRaw8() & self.iodir - def _validate_pin(self, pin): - # Raise an exception if pin is outside the range of allowed values. - if pin < 0 or pin >= self.NUM_GPIO: - raise ValueError('Invalid GPIO value, must be between 0 and {0}.'.format(self.NUM_GPIO)) - - def _bit2(self, src, bit, val): - bit = 1 << bit - return (src | bit) if val else (src & ~bit) - def setup(self, pin, mode): self.setup_pins({pin: mode}) @@ -98,4 +89,4 @@ def input(self, pin): def input_pins(self, pins): [self._validate_pin(pin) for pin in pins] inp = self._read_pins() - return [bool(inp & pin) for pin in pins] + return [bool(inp & (1< Date: Sat, 25 Apr 2015 11:51:46 -0600 Subject: [PATCH 7/8] use BaseGPIO _validate_pin() --- Adafruit_GPIO/MCP230xx.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Adafruit_GPIO/MCP230xx.py b/Adafruit_GPIO/MCP230xx.py index 9e55a15..12b1e9b 100644 --- a/Adafruit_GPIO/MCP230xx.py +++ b/Adafruit_GPIO/MCP230xx.py @@ -50,10 +50,6 @@ def __init__(self, address, i2c=None, **kwargs): self.write_iodir() self.write_gppu() - def _validate_pin(self, pin): - # Raise an exception if pin is outside the range of allowed values. - if pin < 0 or pin >= self.NUM_GPIO: - raise ValueError('Invalid GPIO value, must be between 0 and {0}.'.format(self.NUM_GPIO)) def setup(self, pin, value): """Set the input or output mode for a specified pin. Mode should be @@ -69,6 +65,7 @@ def setup(self, pin, value): raise ValueError('Unexpected value. Must be GPIO.IN or GPIO.OUT.') self.write_iodir() + def output(self, pin, value): """Set the specified pin the provided high/low value. Value should be either GPIO.HIGH/GPIO.LOW or a boolean (True = HIGH). @@ -80,8 +77,7 @@ def output_pins(self, pins): name to pin value (HIGH/True for 1, LOW/False for 0). All provided pins will be set to the given values. """ - for pin in pins.keys(): - self._validate_pin(pin) + [self._validate_pin(pin) for pin in pins.keys()] # Set each changed pin's bit. for pin, value in pins.iteritems(): if value: @@ -91,6 +87,7 @@ def output_pins(self, pins): # Write GPIO state. self.write_gpio() + def input(self, pin): """Read the specified pin and return GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low. @@ -101,13 +98,13 @@ def input_pins(self, pins): """Read multiple pins specified in the given list and return list of pin values GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low. """ - for pin in pins: - self._validate_pin(pin) + [self._validate_pin(pin) for pin in pins] # Get GPIO state. gpio = self._device.readList(self.GPIO, self.gpio_bytes) # Return True if pin's bit is set. return [(gpio[int(pin/8)] & 1 << (int(pin%8))) > 0 for pin in pins] + def pullup(self, pin, enabled): """Turn on the pull-up resistor for the specified pin if enabled is True, otherwise turn off the pull-up resistor. From 8d5728374058406f44068feaf646a65fbd607629 Mon Sep 17 00:00:00 2001 From: Sylvan Butler Date: Sat, 25 Apr 2015 16:08:46 -0600 Subject: [PATCH 8/8] reflow license text --- Adafruit_GPIO/PCF8574.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/Adafruit_GPIO/PCF8574.py b/Adafruit_GPIO/PCF8574.py index 9ec8996..fe58e52 100644 --- a/Adafruit_GPIO/PCF8574.py +++ b/Adafruit_GPIO/PCF8574.py @@ -2,21 +2,23 @@ Adafruit compatible using BaseGPIO class to represent a PCF8574/A IO expander Copyright (C) 2015 Sylvan Butler -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, -sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial -portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT -LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -''' +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.''' import Adafruit_GPIO as GPIO import Adafruit_GPIO.I2C as I2C