From ceec171c42bd79f1762b971569e20ead656f4fd0 Mon Sep 17 00:00:00 2001
From: Cody Hiar <codyfh@gmail.com>
Date: Sun, 27 May 2018 15:29:21 -0600
Subject: Moving libs into separate own directory

---
 lib/Adafruit_Python_GPIO/Adafruit_GPIO/FT232H.py   | 816 +++++++++++++++++++++
 lib/Adafruit_Python_GPIO/Adafruit_GPIO/GPIO.py     | 426 +++++++++++
 lib/Adafruit_Python_GPIO/Adafruit_GPIO/I2C.py      | 202 +++++
 lib/Adafruit_Python_GPIO/Adafruit_GPIO/MCP230xx.py | 165 +++++
 lib/Adafruit_Python_GPIO/Adafruit_GPIO/PCA95xx.py  | 121 +++
 lib/Adafruit_Python_GPIO/Adafruit_GPIO/PCF8574.py  |  94 +++
 lib/Adafruit_Python_GPIO/Adafruit_GPIO/PWM.py      | 128 ++++
 lib/Adafruit_Python_GPIO/Adafruit_GPIO/Platform.py | 110 +++
 lib/Adafruit_Python_GPIO/Adafruit_GPIO/SPI.py      | 328 +++++++++
 lib/Adafruit_Python_GPIO/Adafruit_GPIO/__init__.py |   3 +
 10 files changed, 2393 insertions(+)
 create mode 100644 lib/Adafruit_Python_GPIO/Adafruit_GPIO/FT232H.py
 create mode 100644 lib/Adafruit_Python_GPIO/Adafruit_GPIO/GPIO.py
 create mode 100644 lib/Adafruit_Python_GPIO/Adafruit_GPIO/I2C.py
 create mode 100644 lib/Adafruit_Python_GPIO/Adafruit_GPIO/MCP230xx.py
 create mode 100644 lib/Adafruit_Python_GPIO/Adafruit_GPIO/PCA95xx.py
 create mode 100644 lib/Adafruit_Python_GPIO/Adafruit_GPIO/PCF8574.py
 create mode 100644 lib/Adafruit_Python_GPIO/Adafruit_GPIO/PWM.py
 create mode 100644 lib/Adafruit_Python_GPIO/Adafruit_GPIO/Platform.py
 create mode 100644 lib/Adafruit_Python_GPIO/Adafruit_GPIO/SPI.py
 create mode 100644 lib/Adafruit_Python_GPIO/Adafruit_GPIO/__init__.py

(limited to 'lib/Adafruit_Python_GPIO/Adafruit_GPIO')

diff --git a/lib/Adafruit_Python_GPIO/Adafruit_GPIO/FT232H.py b/lib/Adafruit_Python_GPIO/Adafruit_GPIO/FT232H.py
new file mode 100644
index 0000000..1874272
--- /dev/null
+++ b/lib/Adafruit_Python_GPIO/Adafruit_GPIO/FT232H.py
@@ -0,0 +1,816 @@
+# Copyright (c) 2014 Adafruit Industries
+# Author: Tony DiCola
+#
+# 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 atexit
+import logging
+import math
+import os
+import subprocess
+import sys
+import time
+
+import ftdi1 as ftdi
+
+import Adafruit_GPIO.GPIO as GPIO
+
+
+logger = logging.getLogger(__name__)
+
+FT232H_VID = 0x0403   # Default FTDI FT232H vendor ID
+FT232H_PID = 0x6014   # Default FTDI FT232H product ID
+
+MSBFIRST = 0
+LSBFIRST = 1
+
+_REPEAT_DELAY = 4
+
+
+def _check_running_as_root():
+    # NOTE: Checking for root with user ID 0 isn't very portable, perhaps
+    # there's a better alternative?
+    if os.geteuid() != 0:
+        raise RuntimeError('Expected to be run by root user! Try running with sudo.')
+
+def disable_FTDI_driver():
+    """Disable the FTDI drivers for the current platform.  This is necessary
+    because they will conflict with libftdi and accessing the FT232H.  Note you
+    can enable the FTDI drivers again by calling enable_FTDI_driver.
+    """
+    logger.debug('Disabling FTDI driver.')
+    if sys.platform == 'darwin':
+        logger.debug('Detected Mac OSX')
+        # Mac OS commands to disable FTDI driver.
+        _check_running_as_root()
+        subprocess.call('kextunload -b com.apple.driver.AppleUSBFTDI', shell=True)
+        subprocess.call('kextunload /System/Library/Extensions/FTDIUSBSerialDriver.kext', shell=True)
+    elif sys.platform.startswith('linux'):
+        logger.debug('Detected Linux')
+        # Linux commands to disable FTDI driver.
+        _check_running_as_root()
+        subprocess.call('modprobe -r -q ftdi_sio', shell=True)
+        subprocess.call('modprobe -r -q usbserial', shell=True)
+    # Note there is no need to disable FTDI drivers on Windows!
+
+def enable_FTDI_driver():
+    """Re-enable the FTDI drivers for the current platform."""
+    logger.debug('Enabling FTDI driver.')
+    if sys.platform == 'darwin':
+        logger.debug('Detected Mac OSX')
+        # Mac OS commands to enable FTDI driver.
+        _check_running_as_root()
+        subprocess.check_call('kextload -b com.apple.driver.AppleUSBFTDI', shell=True)
+        subprocess.check_call('kextload /System/Library/Extensions/FTDIUSBSerialDriver.kext', shell=True)
+    elif sys.platform.startswith('linux'):
+        logger.debug('Detected Linux')
+        # Linux commands to enable FTDI driver.
+        _check_running_as_root()
+        subprocess.check_call('modprobe -q ftdi_sio', shell=True)
+        subprocess.check_call('modprobe -q usbserial', shell=True)
+
+def use_FT232H():
+    """Disable any built in FTDI drivers which will conflict and cause problems
+    with libftdi (which is used to communicate with the FT232H).  Will register
+    an exit function so the drivers are re-enabled on program exit.
+    """
+    disable_FTDI_driver()
+    atexit.register(enable_FTDI_driver)
+
+def enumerate_device_serials(vid=FT232H_VID, pid=FT232H_PID):
+    """Return a list of all FT232H device serial numbers connected to the
+    machine.  You can use these serial numbers to open a specific FT232H device
+    by passing it to the FT232H initializer's serial parameter.
+    """
+    try:
+        # Create a libftdi context.
+        ctx = None
+        ctx = ftdi.new()
+        # Enumerate FTDI devices.
+        device_list = None
+        count, device_list = ftdi.usb_find_all(ctx, vid, pid)
+        if count < 0:
+            raise RuntimeError('ftdi_usb_find_all returned error {0}: {1}'.format(count, ftdi.get_error_string(self._ctx)))
+        # Walk through list of devices and assemble list of serial numbers.
+        devices = []
+        while device_list is not None:
+            # Get USB device strings and add serial to list of devices.
+            ret, manufacturer, description, serial = ftdi.usb_get_strings(ctx, device_list.dev, 256, 256, 256)
+            if serial is not None:
+                devices.append(serial)
+            device_list = device_list.next
+        return devices
+    finally:
+        # Make sure to clean up list and context when done.
+        if device_list is not None:
+            ftdi.list_free(device_list)
+        if ctx is not None:
+            ftdi.free(ctx)
+
+
+class FT232H(GPIO.BaseGPIO):
+    # Make GPIO constants that match main GPIO class for compatibility.
+    HIGH = GPIO.HIGH
+    LOW  = GPIO.LOW
+    IN   = GPIO.IN
+    OUT  = GPIO.OUT
+
+    def __init__(self, vid=FT232H_VID, pid=FT232H_PID, serial=None):
+        """Create a FT232H object.  Will search for the first available FT232H
+        device with the specified USB vendor ID and product ID (defaults to
+        FT232H default VID & PID).  Can also specify an optional serial number
+        string to open an explicit FT232H device given its serial number.  See
+        the FT232H.enumerate_device_serials() function to see how to list all
+        connected device serial numbers.
+        """
+        # Initialize FTDI device connection.
+        self._ctx = ftdi.new()
+        if self._ctx == 0:
+            raise RuntimeError('ftdi_new failed! Is libftdi1 installed?')
+        # Register handler to close and cleanup FTDI context on program exit.
+        atexit.register(self.close)
+        if serial is None:
+            # Open USB connection for specified VID and PID if no serial is specified.
+            self._check(ftdi.usb_open, vid, pid)
+        else:
+            # Open USB connection for VID, PID, serial.
+            self._check(ftdi.usb_open_string, 's:{0}:{1}:{2}'.format(vid, pid, serial))
+        # Reset device.
+        self._check(ftdi.usb_reset)
+        # Disable flow control. Commented out because it is unclear if this is necessary.
+        #self._check(ftdi.setflowctrl, ftdi.SIO_DISABLE_FLOW_CTRL)
+        # Change read & write buffers to maximum size, 65535 bytes.
+        self._check(ftdi.read_data_set_chunksize, 65535)
+        self._check(ftdi.write_data_set_chunksize, 65535)
+        # Clear pending read data & write buffers.
+        self._check(ftdi.usb_purge_buffers)
+        # Enable MPSSE and syncronize communication with device.
+        self._mpsse_enable()
+        self._mpsse_sync()
+        # Initialize all GPIO as inputs.
+        self._write('\x80\x00\x00\x82\x00\x00')
+        self._direction = 0x0000
+        self._level = 0x0000
+
+    def close(self):
+        """Close the FTDI device.  Will be automatically called when the program ends."""
+        if self._ctx is not None:
+            ftdi.free(self._ctx)
+        self._ctx = None
+
+    def _write(self, string):
+        """Helper function to call write_data on the provided FTDI device and
+        verify it succeeds.
+        """
+        # Get modem status. Useful to enable for debugging.
+        #ret, status = ftdi.poll_modem_status(self._ctx)
+        #if ret == 0:
+        #	logger.debug('Modem status {0:02X}'.format(status))
+        #else:
+        #	logger.debug('Modem status error {0}'.format(ret))
+        length = len(string)
+        ret = ftdi.write_data(self._ctx, string, length)
+        # Log the string that was written in a python hex string format using a very
+        # ugly one-liner list comprehension for brevity.
+        #logger.debug('Wrote {0}'.format(''.join(['\\x{0:02X}'.format(ord(x)) for x in string])))
+        if ret < 0:
+            raise RuntimeError('ftdi_write_data failed with error {0}: {1}'.format(ret, ftdi.get_error_string(self._ctx)))
+        if ret != length:
+            raise RuntimeError('ftdi_write_data expected to write {0} bytes but actually wrote {1}!'.format(length, ret))
+
+    def _check(self, command, *args):
+        """Helper function to call the provided command on the FTDI device and
+        verify the response matches the expected value.
+        """
+        ret = command(self._ctx, *args)
+        logger.debug('Called ftdi_{0} and got response {1}.'.format(command.__name__, ret))
+        if ret != 0:
+            raise RuntimeError('ftdi_{0} failed with error {1}: {2}'.format(command.__name__, ret, ftdi.get_error_string(self._ctx)))
+
+    def _poll_read(self, expected, timeout_s=5.0):
+        """Helper function to continuously poll reads on the FTDI device until an
+        expected number of bytes are returned.  Will throw a timeout error if no
+        data is received within the specified number of timeout seconds.  Returns
+        the read data as a string if successful, otherwise raises an execption.
+        """
+        start = time.time()
+        # Start with an empty response buffer.
+        response = bytearray(expected)
+        index = 0
+        # Loop calling read until the response buffer is full or a timeout occurs.
+        while time.time() - start <= timeout_s:
+            ret, data = ftdi.read_data(self._ctx, expected - index)
+            # Fail if there was an error reading data.
+            if ret < 0:
+                raise RuntimeError('ftdi_read_data failed with error code {0}.'.format(ret))
+            # Add returned data to the buffer.
+            response[index:index+ret] = data[:ret]
+            index += ret
+            # Buffer is full, return the result data.
+            if index >= expected:
+                return str(response)
+            time.sleep(0.01)
+        raise RuntimeError('Timeout while polling ftdi_read_data for {0} bytes!'.format(expected))
+
+    def _mpsse_enable(self):
+        """Enable MPSSE mode on the FTDI device."""
+        # Reset MPSSE by sending mask = 0 and mode = 0
+        self._check(ftdi.set_bitmode, 0, 0)
+        # Enable MPSSE by sending mask = 0 and mode = 2
+        self._check(ftdi.set_bitmode, 0, 2)
+
+    def _mpsse_sync(self, max_retries=10):
+        """Synchronize buffers with MPSSE by sending bad opcode and reading expected
+        error response.  Should be called once after enabling MPSSE."""
+        # Send a bad/unknown command (0xAB), then read buffer until bad command
+        # response is found.
+        self._write('\xAB')
+        # Keep reading until bad command response (0xFA 0xAB) is returned.
+        # Fail if too many read attempts are made to prevent sticking in a loop.
+        tries = 0
+        sync = False
+        while not sync:
+            data = self._poll_read(2)
+            if data == '\xFA\xAB':
+                sync = True
+            tries += 1
+            if tries >= max_retries:
+                raise RuntimeError('Could not synchronize with FT232H!')
+
+    def mpsse_set_clock(self, clock_hz, adaptive=False, three_phase=False):
+        """Set the clock speed of the MPSSE engine.  Can be any value from 450hz
+        to 30mhz and will pick that speed or the closest speed below it.
+        """
+        # Disable clock divisor by 5 to enable faster speeds on FT232H.
+        self._write('\x8A')
+        # Turn on/off adaptive clocking.
+        if adaptive:
+            self._write('\x96')
+        else:
+            self._write('\x97')
+        # Turn on/off three phase clock (needed for I2C).
+        # Also adjust the frequency for three-phase clocking as specified in section 2.2.4
+        # of this document:
+        #   http://www.ftdichip.com/Support/Documents/AppNotes/AN_255_USB%20to%20I2C%20Example%20using%20the%20FT232H%20and%20FT201X%20devices.pdf
+        if three_phase:
+            self._write('\x8C')
+        else:
+            self._write('\x8D')
+        # Compute divisor for requested clock.
+        # Use equation from section 3.8.1 of:
+        #  http://www.ftdichip.com/Support/Documents/AppNotes/AN_108_Command_Processor_for_MPSSE_and_MCU_Host_Bus_Emulation_Modes.pdf
+        # Note equation is using 60mhz master clock instead of 12mhz.
+        divisor = int(math.ceil((30000000.0-float(clock_hz))/float(clock_hz))) & 0xFFFF
+        if three_phase:
+            divisor = int(divisor*(2.0/3.0))
+        logger.debug('Setting clockspeed with divisor value {0}'.format(divisor))
+        # Send command to set divisor from low and high byte values.
+        self._write(str(bytearray((0x86, divisor & 0xFF, (divisor >> 8) & 0xFF))))
+
+    def mpsse_read_gpio(self):
+        """Read both GPIO bus states and return a 16 bit value with their state.
+        D0-D7 are the lower 8 bits and C0-C7 are the upper 8 bits.
+        """
+        # Send command to read low byte and high byte.
+        self._write('\x81\x83')
+        # Wait for 2 byte response.
+        data = self._poll_read(2)
+        # Assemble response into 16 bit value.
+        low_byte = ord(data[0])
+        high_byte = ord(data[1])
+        logger.debug('Read MPSSE GPIO low byte = {0:02X} and high byte = {1:02X}'.format(low_byte, high_byte))
+        return (high_byte << 8) | low_byte
+
+    def mpsse_gpio(self):
+        """Return command to update the MPSSE GPIO state to the current direction
+        and level.
+        """
+        level_low  = chr(self._level & 0xFF)
+        level_high = chr((self._level >> 8) & 0xFF)
+        dir_low  = chr(self._direction & 0xFF)
+        dir_high = chr((self._direction >> 8) & 0xFF)
+        return str(bytearray((0x80, level_low, dir_low, 0x82, level_high, dir_high)))
+
+    def mpsse_write_gpio(self):
+        """Write the current MPSSE GPIO state to the FT232H chip."""
+        self._write(self.mpsse_gpio())
+
+    def get_i2c_device(self, address, **kwargs):
+        """Return an I2CDevice instance using this FT232H object and the provided
+        I2C address.  Meant to be passed as the i2c_provider parameter to objects
+        which use the Adafruit_Python_GPIO library for I2C.
+        """
+        return I2CDevice(self, address, **kwargs)
+
+    # GPIO functions below:
+
+    def _setup_pin(self, pin, mode):
+        if pin < 0 or pin > 15:
+            raise ValueError('Pin must be between 0 and 15 (inclusive).')
+        if mode not in (GPIO.IN, GPIO.OUT):
+            raise ValueError('Mode must be GPIO.IN or GPIO.OUT.')
+        if mode == GPIO.IN:
+            # Set the direction and level of the pin to 0.
+            self._direction &= ~(1 << pin) & 0xFFFF
+            self._level     &= ~(1 << pin) & 0xFFFF
+        else:
+            # Set the direction of the pin to 1.
+            self._direction |= (1 << pin) & 0xFFFF
+
+    def setup(self, pin, mode):
+        """Set the input or output mode for a specified pin.  Mode should be
+        either OUT or IN."""
+        self._setup_pin(pin, mode)
+        self.mpsse_write_gpio()
+
+    def setup_pins(self, pins, values={}, write=True):
+        """Setup multiple pins as inputs or outputs at once.  Pins should be a
+        dict of pin name to pin mode (IN or OUT).  Optional starting values of
+        pins can be provided in the values dict (with pin name to pin value).
+        """
+        # General implementation that can be improved by subclasses.
+        for pin, mode in iter(pins.items()):
+            self._setup_pin(pin, mode)
+        for pin, value in iter(values.items()):
+            self._output_pin(pin, value)
+        if write:
+            self.mpsse_write_gpio()
+
+    def _output_pin(self, pin, value):
+        if value:
+            self._level |= (1 << pin) & 0xFFFF
+        else:
+            self._level &= ~(1 << pin) & 0xFFFF
+
+    def output(self, pin, value):
+        """Set the specified pin the provided high/low value.  Value should be
+        either HIGH/LOW or a boolean (true = high)."""
+        if pin < 0 or pin > 15:
+            raise ValueError('Pin must be between 0 and 15 (inclusive).')
+        self._output_pin(pin, value)
+        self.mpsse_write_gpio()
+
+    def output_pins(self, pins, write=True):
+        """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, value in iter(pins.items()):
+            self._output_pin(pin, value)
+        if write:
+            self.mpsse_write_gpio()
+
+    def input(self, pin):
+        """Read the specified pin and return HIGH/true if the pin is pulled high,
+        or LOW/false if pulled low."""
+        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 for pin in pins]
+
+
+class SPI(object):
+    def __init__(self, ft232h, cs=None, max_speed_hz=1000000, mode=0, bitorder=MSBFIRST):
+        self._ft232h = ft232h
+        # Initialize chip select pin if provided to output high.
+        if cs is not None:
+            ft232h.setup(cs, GPIO.OUT)
+            ft232h.set_high(cs)
+        self._cs = cs
+        # Initialize clock, mode, and bit order.
+        self.set_clock_hz(max_speed_hz)
+        self.set_mode(mode)
+        self.set_bit_order(bitorder)
+
+    def _assert_cs(self):
+        if self._cs is not None:
+            self._ft232h.set_low(self._cs)
+
+    def _deassert_cs(self):
+        if self._cs is not None:
+            self._ft232h.set_high(self._cs)
+
+    def set_clock_hz(self, hz):
+        """Set the speed of the SPI clock in hertz.  Note that not all speeds
+        are supported and a lower speed might be chosen by the hardware.
+        """
+        self._ft232h.mpsse_set_clock(hz)
+
+    def set_mode(self, mode):
+        """Set SPI mode which controls clock polarity and phase.  Should be a
+        numeric value 0, 1, 2, or 3.  See wikipedia page for details on meaning:
+        http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
+        """
+        if mode < 0 or mode > 3:
+            raise ValueError('Mode must be a value 0, 1, 2, or 3.')
+        if mode == 0:
+            # Mode 0 captures on rising clock, propagates on falling clock
+            self.write_clock_ve = 1
+            self.read_clock_ve  = 0
+            # Clock base is low.
+            clock_base = GPIO.LOW
+        elif mode == 1:
+            # Mode 1 capture of falling edge, propagate on rising clock
+            self.write_clock_ve = 0
+            self.read_clock_ve  = 1
+            # Clock base is low.
+            clock_base = GPIO.LOW
+        elif mode == 2:
+            # Mode 2 capture on rising clock, propagate on falling clock
+            self.write_clock_ve = 1
+            self.read_clock_ve  = 0
+            # Clock base is high.
+            clock_base = GPIO.HIGH
+        elif mode == 3:
+            # Mode 3 capture on falling edge, propagage on rising clock
+            self.write_clock_ve = 0
+            self.read_clock_ve  = 1
+            # Clock base is high.
+            clock_base = GPIO.HIGH
+        # Set clock and DO as output, DI as input.  Also start clock at its base value.
+        self._ft232h.setup_pins({0: GPIO.OUT, 1: GPIO.OUT, 2: GPIO.IN}, {0: clock_base})
+
+    def set_bit_order(self, order):
+        """Set order of bits to be read/written over serial lines.  Should be
+        either MSBFIRST for most-significant first, or LSBFIRST for
+        least-signifcant first.
+        """
+        if order == MSBFIRST:
+            self.lsbfirst = 0
+        elif order == LSBFIRST:
+            self.lsbfirst = 1
+        else:
+            raise ValueError('Order must be MSBFIRST or LSBFIRST.')
+
+    def write(self, data):
+        """Half-duplex SPI write.  The specified array of bytes will be clocked
+        out the MOSI line.
+        """
+        # Build command to write SPI data.
+        command = 0x10 | (self.lsbfirst << 3) | self.write_clock_ve
+        logger.debug('SPI write with command {0:2X}.'.format(command))
+        # Compute length low and high bytes.
+        # NOTE: Must actually send length minus one because the MPSSE engine
+        # considers 0 a length of 1 and FFFF a length of 65536
+        length = len(data)-1
+        len_low  = length & 0xFF
+        len_high = (length >> 8) & 0xFF
+        self._assert_cs()
+        # Send command and length.
+        self._ft232h._write(str(bytearray((command, len_low, len_high))))
+        # Send data.
+        self._ft232h._write(str(bytearray(data)))
+        self._deassert_cs()
+
+    def read(self, length):
+        """Half-duplex SPI read.  The specified length of bytes will be clocked
+        in the MISO line and returned as a bytearray object.
+        """
+        # Build command to read SPI data.
+        command = 0x20 | (self.lsbfirst << 3) | (self.read_clock_ve << 2)
+        logger.debug('SPI read with command {0:2X}.'.format(command))
+        # Compute length low and high bytes.
+        # NOTE: Must actually send length minus one because the MPSSE engine
+        # considers 0 a length of 1 and FFFF a length of 65536
+        len_low  = (length-1) & 0xFF
+        len_high = ((length-1) >> 8) & 0xFF
+        self._assert_cs()
+        # Send command and length.
+        self._ft232h._write(str(bytearray((command, len_low, len_high, 0x87))))
+        self._deassert_cs()
+        # Read response bytes.
+        return bytearray(self._ft232h._poll_read(length))
+
+    def transfer(self, data):
+        """Full-duplex SPI read and write.  The specified array of bytes will be
+        clocked out the MOSI line, while simultaneously bytes will be read from
+        the MISO line.  Read bytes will be returned as a bytearray object.
+        """
+        # Build command to read and write SPI data.
+        command = 0x30 | (self.lsbfirst << 3) | (self.read_clock_ve << 2) | self.write_clock_ve
+        logger.debug('SPI transfer with command {0:2X}.'.format(command))
+        # Compute length low and high bytes.
+        # NOTE: Must actually send length minus one because the MPSSE engine
+        # considers 0 a length of 1 and FFFF a length of 65536
+        length = len(data)
+        len_low  = (length-1) & 0xFF
+        len_high = ((length-1) >> 8) & 0xFF
+        # Send command and length.
+        self._assert_cs()
+        self._ft232h._write(str(bytearray((command, len_low, len_high))))
+        self._ft232h._write(str(bytearray(data)))
+        self._ft232h._write('\x87')
+        self._deassert_cs()
+        # Read response bytes.
+        return bytearray(self._ft232h._poll_read(length))
+
+
+class I2CDevice(object):
+    """Class for communicating with an I2C device using the smbus library.
+    Allows reading and writing 8-bit, 16-bit, and byte array values to registers
+    on the device."""
+    # Note that most of the functions in this code are adapted from this app note:
+    #  http://www.ftdichip.com/Support/Documents/AppNotes/AN_255_USB%20to%20I2C%20Example%20using%20the%20FT232H%20and%20FT201X%20devices.pdf
+    def __init__(self, ft232h, address, clock_hz=100000):
+        """Create an instance of the I2C device at the specified address on the
+        specified I2C bus number."""
+        self._address = address
+        self._ft232h = ft232h
+        # Enable clock with three phases for I2C.
+        self._ft232h.mpsse_set_clock(clock_hz, three_phase=True)
+        # Enable drive-zero mode to drive outputs low on 0 and tri-state on 1.
+        # This matches the protocol for I2C communication so multiple devices can
+        # share the I2C bus.
+        self._ft232h._write('\x9E\x07\x00')
+        self._idle()
+
+    def _idle(self):
+        """Put I2C lines into idle state."""
+        # Put the I2C lines into an idle state with SCL and SDA high.
+        self._ft232h.setup_pins({0: GPIO.OUT, 1: GPIO.OUT, 2: GPIO.IN},
+                                {0: GPIO.HIGH, 1: GPIO.HIGH})
+
+    def _transaction_start(self):
+        """Start I2C transaction."""
+        # Clear command buffer and expected response bytes.
+        self._command = []
+        self._expected = 0
+
+    def _transaction_end(self):
+        """End I2C transaction and get response bytes, including ACKs."""
+        # Ask to return response bytes immediately.
+        self._command.append('\x87')
+        # Send the entire command to the MPSSE.
+        self._ft232h._write(''.join(self._command))
+        # Read response bytes and return them.
+        return bytearray(self._ft232h._poll_read(self._expected))
+
+    def _i2c_start(self):
+        """Send I2C start signal. Must be called within a transaction start/end.
+        """
+        # Set SCL high and SDA low, repeat 4 times to stay in this state for a
+        # short period of time.
+        self._ft232h.output_pins({0: GPIO.HIGH, 1: GPIO.LOW}, write=False)
+        self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY)
+        # Now drop SCL to low (again repeat 4 times for short delay).
+        self._ft232h.output_pins({0: GPIO.LOW, 1: GPIO.LOW}, write=False)
+        self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY)
+
+    def _i2c_idle(self):
+        """Set I2C signals to idle state with SCL and SDA at a high value. Must
+        be called within a transaction start/end.
+        """
+        self._ft232h.output_pins({0: GPIO.HIGH, 1: GPIO.HIGH}, write=False)
+        self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY)
+
+    def _i2c_stop(self):
+        """Send I2C stop signal. Must be called within a transaction start/end.
+        """
+        # Set SCL low and SDA low for a short period.
+        self._ft232h.output_pins({0: GPIO.LOW, 1: GPIO.LOW}, write=False)
+        self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY)
+        # Set SCL high and SDA low for a short period.
+        self._ft232h.output_pins({0: GPIO.HIGH, 1: GPIO.LOW}, write=False)
+        self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY)
+        # Finally set SCL high and SDA high for a short period.
+        self._ft232h.output_pins({0: GPIO.HIGH, 1: GPIO.HIGH}, write=False)
+        self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY)
+
+    def _i2c_read_bytes(self, length=1):
+        """Read the specified number of bytes from the I2C bus.  Length is the
+        number of bytes to read (must be 1 or more).
+        """
+        for i in range(length-1):
+            # Read a byte and send ACK.
+            self._command.append('\x20\x00\x00\x13\x00\x00')
+            # Make sure pins are back in idle state with clock low and data high.
+            self._ft232h.output_pins({0: GPIO.LOW, 1: GPIO.HIGH}, write=False)
+            self._command.append(self._ft232h.mpsse_gpio())
+        # Read last byte and send NAK.
+        self._command.append('\x20\x00\x00\x13\x00\xFF')
+        # Make sure pins are back in idle state with clock low and data high.
+        self._ft232h.output_pins({0: GPIO.LOW, 1: GPIO.HIGH}, write=False)
+        self._command.append(self._ft232h.mpsse_gpio())
+        # Increase expected number of bytes.
+        self._expected += length
+
+    def _i2c_write_bytes(self, data):
+        """Write the specified number of bytes to the chip."""
+        for byte in data:
+            # Write byte.
+            self._command.append(str(bytearray((0x11, 0x00, 0x00, byte))))
+            # Make sure pins are back in idle state with clock low and data high.
+            self._ft232h.output_pins({0: GPIO.LOW, 1: GPIO.HIGH}, write=False)
+            self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY)
+            # Read bit for ACK/NAK.
+            self._command.append('\x22\x00')
+        # Increase expected response bytes.
+        self._expected += len(data)
+
+    def _address_byte(self, read=True):
+        """Return the address byte with the specified R/W bit set.  If read is
+        True the R/W bit will be 1, otherwise the R/W bit will be 0.
+        """
+        if read:
+            return (self._address << 1) | 0x01
+        else:
+            return self._address << 1
+
+    def _verify_acks(self, response):
+        """Check all the specified bytes have the ACK bit set.  Throws a
+        RuntimeError exception if not all the ACKs are set.
+        """
+        for byte in response:
+            if byte & 0x01 != 0x00:
+                raise RuntimeError('Failed to find expected I2C ACK!')
+
+    def ping(self):
+        """Attempt to detect if a device at this address is present on the I2C
+        bus.  Will send out the device's address for writing and verify an ACK
+        is received.  Returns true if the ACK is received, and false if not.
+        """
+        self._idle()
+        self._transaction_start()
+        self._i2c_start()
+        self._i2c_write_bytes([self._address_byte(False)])
+        self._i2c_stop()
+        response = self._transaction_end()
+        if len(response) != 1:
+            raise RuntimeError('Expected 1 response byte but received {0} byte(s).'.format(len(response)))
+        return ((response[0] & 0x01) == 0x00)
+
+    def writeRaw8(self, value):
+        """Write an 8-bit value on the bus (without register)."""
+        value = value & 0xFF
+        self._idle()
+        self._transaction_start()
+        self._i2c_start()
+        self._i2c_write_bytes([self._address_byte(False), value])
+        self._i2c_stop()
+        response = self._transaction_end()
+        self._verify_acks(response)
+
+    def write8(self, register, value):
+        """Write an 8-bit value to the specified register."""
+        value = value & 0xFF
+        self._idle()
+        self._transaction_start()
+        self._i2c_start()
+        self._i2c_write_bytes([self._address_byte(False), register, value])
+        self._i2c_stop()
+        response = self._transaction_end()
+        self._verify_acks(response)
+
+    def write16(self, register, value, little_endian=True):
+        """Write a 16-bit value to the specified register."""
+        value = value & 0xFFFF
+        value_low  = value & 0xFF
+        value_high = (value >> 8) & 0xFF
+        if not little_endian:
+            value_low, value_high = value_high, value_low
+        self._idle()
+        self._transaction_start()
+        self._i2c_start()
+        self._i2c_write_bytes([self._address_byte(False), register, value_low,
+                                value_high])
+        self._i2c_stop()
+        response = self._transaction_end()
+        self._verify_acks(response)
+
+    def writeList(self, register, data):
+        """Write bytes to the specified register."""
+        self._idle()
+        self._transaction_start()
+        self._i2c_start()
+        self._i2c_write_bytes([self._address_byte(False), register] + data)
+        self._i2c_stop()
+        response = self._transaction_end()
+        self._verify_acks(response)
+
+    def readList(self, register, length):
+        """Read a length number of bytes from the specified register.  Results
+        will be returned as a bytearray."""
+        if length <= 0:
+            raise ValueError("Length must be at least 1 byte.")
+        self._idle()
+        self._transaction_start()
+        self._i2c_start()
+        self._i2c_write_bytes([self._address_byte(True), register])
+        self._i2c_stop()
+        self._i2c_idle()
+        self._i2c_start()
+        self._i2c_read_bytes(length)
+        self._i2c_stop()
+        response = self._transaction_end()
+        self._verify_acks(response[:-length])
+        return response[-length:]
+
+    def readRaw8(self):
+        """Read an 8-bit value on the bus (without register)."""
+        self._idle()
+        self._transaction_start()
+        self._i2c_start()
+        self._i2c_write_bytes([self._address_byte(False)])
+        self._i2c_stop()
+        self._i2c_idle()
+        self._i2c_start()
+        self._i2c_write_bytes([self._address_byte(True)])
+        self._i2c_read_bytes(1)
+        self._i2c_stop()
+        response = self._transaction_end()
+        self._verify_acks(response[:-1])
+        return response[-1]
+
+    def readU8(self, register):
+        """Read an unsigned byte from the specified register."""
+        self._idle()
+        self._transaction_start()
+        self._i2c_start()
+        self._i2c_write_bytes([self._address_byte(False), register])
+        self._i2c_stop()
+        self._i2c_idle()
+        self._i2c_start()
+        self._i2c_write_bytes([self._address_byte(True)])
+        self._i2c_read_bytes(1)
+        self._i2c_stop()
+        response = self._transaction_end()
+        self._verify_acks(response[:-1])
+        return response[-1]
+
+    def readS8(self, register):
+        """Read a signed byte from the specified register."""
+        result = self.readU8(register)
+        if result > 127:
+            result -= 256
+        return result
+
+    def readU16(self, register, little_endian=True):
+        """Read an unsigned 16-bit value from the specified register, with the
+        specified endianness (default little endian, or least significant byte
+        first)."""
+        self._idle()
+        self._transaction_start()
+        self._i2c_start()
+        self._i2c_write_bytes([self._address_byte(False), register])
+        self._i2c_stop()
+        self._i2c_idle()
+        self._i2c_start()
+        self._i2c_write_bytes([self._address_byte(True)])
+        self._i2c_read_bytes(2)
+        self._i2c_stop()
+        response = self._transaction_end()
+        self._verify_acks(response[:-2])
+        if little_endian:
+            return (response[-1] << 8) | response[-2]
+        else:
+            return (response[-2] << 8) | response[-1]
+
+    def readS16(self, register, little_endian=True):
+        """Read a signed 16-bit value from the specified register, with the
+        specified endianness (default little endian, or least significant byte
+        first)."""
+        result = self.readU16(register, little_endian)
+        if result > 32767:
+            result -= 65536
+        return result
+
+    def readU16LE(self, register):
+        """Read an unsigned 16-bit value from the specified register, in little
+        endian byte order."""
+        return self.readU16(register, little_endian=True)
+
+    def readU16BE(self, register):
+        """Read an unsigned 16-bit value from the specified register, in big
+        endian byte order."""
+        return self.readU16(register, little_endian=False)
+
+    def readS16LE(self, register):
+        """Read a signed 16-bit value from the specified register, in little
+        endian byte order."""
+        return self.readS16(register, little_endian=True)
+
+    def readS16BE(self, register):
+        """Read a signed 16-bit value from the specified register, in big
+        endian byte order."""
+        return self.readS16(register, little_endian=False)
diff --git a/lib/Adafruit_Python_GPIO/Adafruit_GPIO/GPIO.py b/lib/Adafruit_Python_GPIO/Adafruit_GPIO/GPIO.py
new file mode 100644
index 0000000..08e99c6
--- /dev/null
+++ b/lib/Adafruit_Python_GPIO/Adafruit_GPIO/GPIO.py
@@ -0,0 +1,426 @@
+# Copyright (c) 2014 Adafruit Industries
+# Author: Tony DiCola
+#
+# 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.Platform as Platform
+
+
+OUT     = 0
+IN      = 1
+HIGH    = True
+LOW     = False
+
+RISING      = 1
+FALLING     = 2
+BOTH        = 3
+
+PUD_OFF  = 0
+PUD_DOWN = 1
+PUD_UP   = 2
+
+class BaseGPIO(object):
+    """Base class for implementing simple digital IO for a platform.
+    Implementors are expected to subclass from this and provide an implementation
+    of the setup, output, and input functions."""
+
+    def setup(self, pin, mode, pull_up_down=PUD_OFF):
+        """Set the input or output mode for a specified pin.  Mode should be
+        either OUT or IN."""
+        raise NotImplementedError
+
+    def output(self, pin, value):
+        """Set the specified pin the provided high/low value.  Value should be
+        either HIGH/LOW or a boolean (true = high)."""
+        raise NotImplementedError
+
+    def input(self, pin):
+        """Read the specified pin and return HIGH/true if the pin is pulled high,
+        or LOW/false if pulled low."""
+        raise NotImplementedError
+
+    def set_high(self, pin):
+        """Set the specified pin HIGH."""
+        self.output(pin, HIGH)
+
+    def set_low(self, pin):
+        """Set the specified pin LOW."""
+        self.output(pin, LOW)
+
+    def is_high(self, pin):
+        """Return true if the specified pin is pulled high."""
+        return self.input(pin) == HIGH
+
+    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.
+        for pin, value in iter(pins.items()):
+            self.output(pin, value)
+
+    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 optimized by derived classes.
+        for pin, value in iter(pins.items()):
+            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.
+        """
+        raise NotImplementedError
+   
+    def remove_event_detect(self, pin):
+        """Remove edge detection for a particular GPIO channel.  Pin should be
+        type IN.
+        """
+        raise NotImplementedError
+  
+    def add_event_callback(self, pin, callback):
+        """Add a callback for an event already defined using add_event_detect().
+        Pin should be type IN.
+        """
+        raise NotImplementedError
+
+    def event_detected(self, pin):
+        """Returns True if an edge has occured on a given GPIO.  You need to 
+        enable edge detection using add_event_detect() first.   Pin should be 
+        type IN.
+        """
+        raise NotImplementedError
+
+    def wait_for_edge(self, pin, edge):
+        """Wait for an edge.   Pin should be type IN.  Edge must be RISING, 
+        FALLING or BOTH."""
+        raise NotImplementedError
+
+    def cleanup(self, pin=None):
+        """Clean up GPIO event detection for specific pin, or all pins if none 
+        is specified.
+        """
+        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."""
+
+    def __init__(self, rpi_gpio, mode=None):
+        self.rpi_gpio = rpi_gpio
+        # Suppress warnings about GPIO in use.
+        rpi_gpio.setwarnings(False)
+        # Setup board pin mode.
+        if mode == rpi_gpio.BOARD or mode == rpi_gpio.BCM:
+            rpi_gpio.setmode(mode)
+        elif mode is not None:
+            raise ValueError('Unexpected value for mode.  Must be BOARD or BCM.')
+        else:
+            # Default to BCM numbering if not told otherwise.
+            rpi_gpio.setmode(rpi_gpio.BCM)
+        # Define mapping of Adafruit GPIO library constants to RPi.GPIO constants.
+        self._dir_mapping = { OUT:      rpi_gpio.OUT,
+                              IN:       rpi_gpio.IN }
+        self._pud_mapping = { PUD_OFF:  rpi_gpio.PUD_OFF,
+                              PUD_DOWN: rpi_gpio.PUD_DOWN,
+                              PUD_UP:   rpi_gpio.PUD_UP }
+        self._edge_mapping = { RISING:  rpi_gpio.RISING,
+                               FALLING: rpi_gpio.FALLING,
+                               BOTH:    rpi_gpio.BOTH }
+
+    def setup(self, pin, mode, pull_up_down=PUD_OFF):
+        """Set the input or output mode for a specified pin.  Mode should be
+        either OUTPUT or INPUT.
+        """
+        self.rpi_gpio.setup(pin, self._dir_mapping[mode],
+                            pull_up_down=self._pud_mapping[pull_up_down])
+
+    def output(self, pin, value):
+        """Set the specified pin the provided high/low value.  Value should be
+        either HIGH/LOW or a boolean (true = high).
+        """
+        self.rpi_gpio.output(pin, value)
+
+    def input(self, pin):
+        """Read the specified pin and return HIGH/true if the pin is pulled high,
+        or LOW/false if pulled low.
+        """
+        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
+        function for the event.  Bouncetime is switch bounce timeout in ms for
+        callback
+        """
+        kwargs = {}
+        if callback:
+            kwargs['callback']=callback
+        if bouncetime > 0:
+            kwargs['bouncetime']=bouncetime
+        self.rpi_gpio.add_event_detect(pin, self._edge_mapping[edge], **kwargs)
+
+    def remove_event_detect(self, pin):
+        """Remove edge detection for a particular GPIO channel.  Pin should be
+        type IN.
+        """
+        self.rpi_gpio.remove_event_detect(pin)
+
+    def add_event_callback(self, pin, callback):
+        """Add a callback for an event already defined using add_event_detect().
+        Pin should be type IN.
+        """
+        self.rpi_gpio.add_event_callback(pin, callback)
+
+    def event_detected(self, pin):
+        """Returns True if an edge has occured on a given GPIO.  You need to 
+        enable edge detection using add_event_detect() first.   Pin should be
+        type IN.
+        """
+        return self.rpi_gpio.event_detected(pin)
+
+    def wait_for_edge(self, pin, edge):
+        """Wait for an edge.   Pin should be type IN.  Edge must be RISING,
+        FALLING or BOTH.
+        """
+        self.rpi_gpio.wait_for_edge(pin, self._edge_mapping[edge])
+
+    def cleanup(self, pin=None):
+        """Clean up GPIO event detection for specific pin, or all pins if none 
+        is specified.
+        """
+        if pin is None:
+            self.rpi_gpio.cleanup()
+        else:
+            self.rpi_gpio.cleanup(pin)
+
+class AdafruitBBIOAdapter(BaseGPIO):
+    """GPIO implementation for the Beaglebone Black using the Adafruit_BBIO
+    library.
+    """
+
+    def __init__(self, bbio_gpio):
+        self.bbio_gpio = bbio_gpio
+        # Define mapping of Adafruit GPIO library constants to RPi.GPIO constants.
+        self._dir_mapping = { OUT:      bbio_gpio.OUT,
+                              IN:       bbio_gpio.IN }
+        self._pud_mapping = { PUD_OFF:  bbio_gpio.PUD_OFF,
+                              PUD_DOWN: bbio_gpio.PUD_DOWN,
+                              PUD_UP:   bbio_gpio.PUD_UP }
+        self._edge_mapping = { RISING:  bbio_gpio.RISING,
+                               FALLING: bbio_gpio.FALLING,
+                               BOTH:    bbio_gpio.BOTH }
+
+    def setup(self, pin, mode, pull_up_down=PUD_OFF):
+        """Set the input or output mode for a specified pin.  Mode should be
+        either OUTPUT or INPUT.
+        """
+        self.bbio_gpio.setup(pin, self._dir_mapping[mode],
+                             pull_up_down=self._pud_mapping[pull_up_down])
+
+    def output(self, pin, value):
+        """Set the specified pin the provided high/low value.  Value should be
+        either HIGH/LOW or a boolean (true = high).
+        """
+        self.bbio_gpio.output(pin, value)
+
+    def input(self, pin):
+        """Read the specified pin and return HIGH/true if the pin is pulled high,
+        or LOW/false if pulled low.
+        """
+        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
+        function for the event.  Bouncetime is switch bounce timeout in ms for 
+        callback
+        """
+        kwargs = {}
+        if callback:
+            kwargs['callback']=callback
+        if bouncetime > 0:
+            kwargs['bouncetime']=bouncetime
+        self.bbio_gpio.add_event_detect(pin, self._edge_mapping[edge], **kwargs)
+
+    def remove_event_detect(self, pin):
+        """Remove edge detection for a particular GPIO channel.  Pin should be
+        type IN.
+        """
+        self.bbio_gpio.remove_event_detect(pin)
+
+    def add_event_callback(self, pin, callback, bouncetime=-1):
+        """Add a callback for an event already defined using add_event_detect().
+        Pin should be type IN.  Bouncetime is switch bounce timeout in ms for 
+        callback
+        """
+        kwargs = {}
+        if bouncetime > 0:
+            kwargs['bouncetime']=bouncetime
+        self.bbio_gpio.add_event_callback(pin, callback, **kwargs)
+
+    def event_detected(self, pin):
+        """Returns True if an edge has occured on a given GPIO.  You need to 
+        enable edge detection using add_event_detect() first.   Pin should be 
+        type IN.
+        """
+        return self.bbio_gpio.event_detected(pin)
+
+    def wait_for_edge(self, pin, edge):
+        """Wait for an edge.   Pin should be type IN.  Edge must be RISING, 
+        FALLING or BOTH.
+        """
+        self.bbio_gpio.wait_for_edge(pin, self._edge_mapping[edge])
+
+    def cleanup(self, pin=None):
+        """Clean up GPIO event detection for specific pin, or all pins if none 
+        is specified.
+        """
+        if pin is None:
+            self.bbio_gpio.cleanup()
+        else:
+            self.bbio_gpio.cleanup(pin)
+
+class AdafruitMinnowAdapter(BaseGPIO):
+    """GPIO implementation for the Minnowboard + MAX using the mraa library"""
+    
+    def __init__(self,mraa_gpio):
+        self.mraa_gpio = mraa_gpio
+        # Define mapping of Adafruit GPIO library constants to mraa constants
+        self._dir_mapping = { OUT:      self.mraa_gpio.DIR_OUT,
+                              IN:       self.mraa_gpio.DIR_IN }
+        self._pud_mapping = { PUD_OFF:  self.mraa_gpio.MODE_STRONG,
+                              PUD_UP:   self.mraa_gpio.MODE_HIZ,
+                              PUD_DOWN: self.mraa_gpio.MODE_PULLDOWN }
+        self._edge_mapping = { RISING:   self.mraa_gpio.EDGE_RISING,
+                              FALLING:  self.mraa_gpio.EDGE_FALLING,
+                              BOTH:     self.mraa_gpio.EDGE_BOTH }
+
+    def setup(self,pin,mode):
+        """Set the input or output mode for a specified pin.  Mode should be
+        either DIR_IN or DIR_OUT.
+        """
+        self.mraa_gpio.Gpio.dir(self.mraa_gpio.Gpio(pin),self._dir_mapping[mode])   
+
+    def output(self,pin,value):
+        """Set the specified pin the provided high/low value.  Value should be
+        either 1 (ON or HIGH), or 0 (OFF or LOW) or a boolean.
+        """
+        self.mraa_gpio.Gpio.write(self.mraa_gpio.Gpio(pin), value)
+    
+    def input(self,pin):
+        """Read the specified pin and return HIGH/true if the pin is pulled high,
+        or LOW/false if pulled low.
+        """
+        return self.mraa_gpio.Gpio.read(self.mraa_gpio.Gpio(pin))    
+    
+    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
+        function for the event.  Bouncetime is switch bounce timeout in ms for 
+        callback
+        """
+        kwargs = {}
+        if callback:
+            kwargs['callback']=callback
+        if bouncetime > 0:
+            kwargs['bouncetime']=bouncetime
+        self.mraa_gpio.Gpio.isr(self.mraa_gpio.Gpio(pin), self._edge_mapping[edge], **kwargs)
+
+    def remove_event_detect(self, pin):
+        """Remove edge detection for a particular GPIO channel.  Pin should be
+        type IN.
+        """
+        self.mraa_gpio.Gpio.isrExit(self.mraa_gpio.Gpio(pin))
+
+    def wait_for_edge(self, pin, edge):
+        """Wait for an edge.   Pin should be type IN.  Edge must be RISING, 
+        FALLING or BOTH.
+        """
+        self.bbio_gpio.wait_for_edge(self.mraa_gpio.Gpio(pin), self._edge_mapping[edge])
+
+def get_platform_gpio(**keywords):
+    """Attempt to return a GPIO instance for the platform which the code is being
+    executed on.  Currently supports only the Raspberry Pi using the RPi.GPIO
+    library and Beaglebone Black using the Adafruit_BBIO library.  Will throw an
+    exception if a GPIO instance can't be created for the current platform.  The
+    returned GPIO object is an instance of BaseGPIO.
+    """
+    plat = Platform.platform_detect()
+    if plat == Platform.RASPBERRY_PI:
+        import RPi.GPIO
+        return RPiGPIOAdapter(RPi.GPIO, **keywords)
+    elif plat == Platform.BEAGLEBONE_BLACK:
+        import Adafruit_BBIO.GPIO
+        return AdafruitBBIOAdapter(Adafruit_BBIO.GPIO, **keywords)
+    elif plat == Platform.MINNOWBOARD:
+        import mraa
+        return AdafruitMinnowAdapter(mraa, **keywords)
+    elif plat == Platform.UNKNOWN:
+        raise RuntimeError('Could not determine platform.')
diff --git a/lib/Adafruit_Python_GPIO/Adafruit_GPIO/I2C.py b/lib/Adafruit_Python_GPIO/Adafruit_GPIO/I2C.py
new file mode 100644
index 0000000..765ed82
--- /dev/null
+++ b/lib/Adafruit_Python_GPIO/Adafruit_GPIO/I2C.py
@@ -0,0 +1,202 @@
+# Copyright (c) 2014 Adafruit Industries
+# Author: Tony DiCola
+# Based on Adafruit_I2C.py created by Kevin Townsend.
+#
+# 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 logging
+import os
+import subprocess
+
+import Adafruit_GPIO.Platform as Platform
+
+
+def reverseByteOrder(data):
+    """DEPRECATED: See https://github.com/adafruit/Adafruit_Python_GPIO/issues/48"""
+    # # Courtesy Vishal Sapre
+    # byteCount = len(hex(data)[2:].replace('L','')[::2])
+    # val       = 0
+    # for i in range(byteCount):
+    #     val    = (val << 8) | (data & 0xff)
+    #     data >>= 8
+    # return val
+    raise RuntimeError('reverseByteOrder is deprecated! See: https://github.com/adafruit/Adafruit_Python_GPIO/issues/48')
+
+def get_default_bus():
+    """Return the default bus number based on the device platform.  For a
+    Raspberry Pi either bus 0 or 1 (based on the Pi revision) will be returned.
+    For a Beaglebone Black the first user accessible bus, 1, will be returned.
+    """
+    plat = Platform.platform_detect()
+    if plat == Platform.RASPBERRY_PI:
+        if Platform.pi_revision() == 1:
+            # Revision 1 Pi uses I2C bus 0.
+            return 0
+        else:
+            # Revision 2 Pi uses I2C bus 1.
+            return 1
+    elif plat == Platform.BEAGLEBONE_BLACK:
+        # Beaglebone Black has multiple I2C buses, default to 1 (P9_19 and P9_20).
+        return 1
+    else:
+        raise RuntimeError('Could not determine default I2C bus for platform.')
+
+def get_i2c_device(address, busnum=None, i2c_interface=None, **kwargs):
+    """Return an I2C device for the specified address and on the specified bus.
+    If busnum isn't specified, the default I2C bus for the platform will attempt
+    to be detected.
+    """
+    if busnum is None:
+        busnum = get_default_bus()
+    return Device(address, busnum, i2c_interface, **kwargs)
+
+def require_repeated_start():
+    """Enable repeated start conditions for I2C register reads.  This is the
+    normal behavior for I2C, however on some platforms like the Raspberry Pi
+    there are bugs which disable repeated starts unless explicitly enabled with
+    this function.  See this thread for more details:
+      http://www.raspberrypi.org/forums/viewtopic.php?f=44&t=15840
+    """
+    plat = Platform.platform_detect()
+    if plat == Platform.RASPBERRY_PI and os.path.exists('/sys/module/i2c_bcm2708/parameters/combined'):
+        # On the Raspberry Pi there is a bug where register reads don't send a
+        # repeated start condition like the kernel smbus I2C driver functions
+        # define.  As a workaround this bit in the BCM2708 driver sysfs tree can
+        # be changed to enable I2C repeated starts.
+        subprocess.check_call('chmod 666 /sys/module/i2c_bcm2708/parameters/combined', shell=True)
+        subprocess.check_call('echo -n 1 > /sys/module/i2c_bcm2708/parameters/combined', shell=True)
+    # Other platforms are a no-op because they (presumably) have the correct
+    # behavior and send repeated starts.
+
+
+class Device(object):
+    """Class for communicating with an I2C device using the adafruit-pureio pure
+    python smbus library, or other smbus compatible I2C interface. Allows reading
+    and writing 8-bit, 16-bit, and byte array values to registers
+    on the device."""
+    def __init__(self, address, busnum, i2c_interface=None):
+        """Create an instance of the I2C device at the specified address on the
+        specified I2C bus number."""
+        self._address = address
+        if i2c_interface is None:
+            # Use pure python I2C interface if none is specified.
+            import Adafruit_PureIO.smbus
+            self._bus = Adafruit_PureIO.smbus.SMBus(busnum)
+        else:
+            # Otherwise use the provided class to create an smbus interface.
+            self._bus = i2c_interface(busnum)
+        self._logger = logging.getLogger('Adafruit_I2C.Device.Bus.{0}.Address.{1:#0X}' \
+                                .format(busnum, address))
+
+    def writeRaw8(self, value):
+        """Write an 8-bit value on the bus (without register)."""
+        value = value & 0xFF
+        self._bus.write_byte(self._address, value)
+        self._logger.debug("Wrote 0x%02X",
+                     value)
+
+    def write8(self, register, value):
+        """Write an 8-bit value to the specified register."""
+        value = value & 0xFF
+        self._bus.write_byte_data(self._address, register, value)
+        self._logger.debug("Wrote 0x%02X to register 0x%02X",
+                     value, register)
+
+    def write16(self, register, value):
+        """Write a 16-bit value to the specified register."""
+        value = value & 0xFFFF
+        self._bus.write_word_data(self._address, register, value)
+        self._logger.debug("Wrote 0x%04X to register pair 0x%02X, 0x%02X",
+                     value, register, register+1)
+
+    def writeList(self, register, data):
+        """Write bytes to the specified register."""
+        self._bus.write_i2c_block_data(self._address, register, data)
+        self._logger.debug("Wrote to register 0x%02X: %s",
+                     register, data)
+
+    def readList(self, register, length):
+        """Read a length number of bytes from the specified register.  Results
+        will be returned as a bytearray."""
+        results = self._bus.read_i2c_block_data(self._address, register, length)
+        self._logger.debug("Read the following from register 0x%02X: %s",
+                     register, results)
+        return results
+
+    def readRaw8(self):
+        """Read an 8-bit value on the bus (without register)."""
+        result = self._bus.read_byte(self._address) & 0xFF
+        self._logger.debug("Read 0x%02X",
+                    result)
+        return result
+
+    def readU8(self, register):
+        """Read an unsigned byte from the specified register."""
+        result = self._bus.read_byte_data(self._address, register) & 0xFF
+        self._logger.debug("Read 0x%02X from register 0x%02X",
+                     result, register)
+        return result
+
+    def readS8(self, register):
+        """Read a signed byte from the specified register."""
+        result = self.readU8(register)
+        if result > 127:
+            result -= 256
+        return result
+
+    def readU16(self, register, little_endian=True):
+        """Read an unsigned 16-bit value from the specified register, with the
+        specified endianness (default little endian, or least significant byte
+        first)."""
+        result = self._bus.read_word_data(self._address,register) & 0xFFFF
+        self._logger.debug("Read 0x%04X from register pair 0x%02X, 0x%02X",
+                           result, register, register+1)
+        # Swap bytes if using big endian because read_word_data assumes little
+        # endian on ARM (little endian) systems.
+        if not little_endian:
+            result = ((result << 8) & 0xFF00) + (result >> 8)
+        return result
+
+    def readS16(self, register, little_endian=True):
+        """Read a signed 16-bit value from the specified register, with the
+        specified endianness (default little endian, or least significant byte
+        first)."""
+        result = self.readU16(register, little_endian)
+        if result > 32767:
+            result -= 65536
+        return result
+
+    def readU16LE(self, register):
+        """Read an unsigned 16-bit value from the specified register, in little
+        endian byte order."""
+        return self.readU16(register, little_endian=True)
+
+    def readU16BE(self, register):
+        """Read an unsigned 16-bit value from the specified register, in big
+        endian byte order."""
+        return self.readU16(register, little_endian=False)
+
+    def readS16LE(self, register):
+        """Read a signed 16-bit value from the specified register, in little
+        endian byte order."""
+        return self.readS16(register, little_endian=True)
+
+    def readS16BE(self, register):
+        """Read a signed 16-bit value from the specified register, in big
+        endian byte order."""
+        return self.readS16(register, little_endian=False)
diff --git a/lib/Adafruit_Python_GPIO/Adafruit_GPIO/MCP230xx.py b/lib/Adafruit_Python_GPIO/Adafruit_GPIO/MCP230xx.py
new file mode 100644
index 0000000..3ba8b5f
--- /dev/null
+++ b/lib/Adafruit_Python_GPIO/Adafruit_GPIO/MCP230xx.py
@@ -0,0 +1,165 @@
+# Copyright (c) 2014 Adafruit Industries
+# Author: Tony DiCola
+#
+# 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 math
+
+import Adafruit_GPIO as GPIO
+import Adafruit_GPIO.I2C as I2C
+
+
+class MCP230xxBase(GPIO.BaseGPIO):
+    """Base class to represent an MCP230xx series GPIO extender.  Is compatible
+    with the Adafruit_GPIO BaseGPIO class so it can be used as a custom GPIO
+    class for interacting with device.
+    """
+
+    def __init__(self, address, i2c=None, **kwargs):
+        """Initialize MCP230xx at specified I2C address and bus number.  If bus
+        is not specified it will default to the appropriate platform detected bus.
+        """
+        # Create I2C device.
+        if i2c is None:
+            import Adafruit_GPIO.I2C as I2C
+            i2c = I2C
+        self._device = i2c.get_i2c_device(address, **kwargs)
+        # Assume starting in ICON.BANK = 0 mode (sequential access).
+        # Compute how many bytes are needed to store count of GPIO.
+        self.gpio_bytes = int(math.ceil(self.NUM_GPIO/8.0))
+        # Buffer register values so they can be changed without reading.
+        self.iodir = [0xFF]*self.gpio_bytes  # Default direction to all inputs.
+        self.gppu = [0x00]*self.gpio_bytes  # Default to pullups disabled.
+        self.gpio = [0x00]*self.gpio_bytes
+        # Write current direction and pullup buffer state.
+        self.write_iodir()
+        self.write_gppu()
+
+
+    def setup(self, pin, value):
+        """Set the input or output mode for a specified pin.  Mode should be
+        either GPIO.OUT or GPIO.IN.
+        """
+        self._validate_pin(pin)
+        # Set bit to 1 for input or 0 for output.
+        if value == GPIO.IN:
+            self.iodir[int(pin/8)] |= 1 << (int(pin%8))
+        elif value == GPIO.OUT:
+            self.iodir[int(pin/8)] &= ~(1 << (int(pin%8)))
+        else:
+            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).
+        """
+        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.
+        """
+        [self._validate_pin(pin) for pin in pins.keys()]
+        # Set each changed pin's bit.
+        for pin, value in iter(pins.items()):
+            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()
+
+
+    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.
+        """
+        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.
+        """
+        [self._validate_pin(pin) for pin in pins]
+        # Get GPIO state.
+        self.gpio = self._device.readList(self.GPIO, self.gpio_bytes)
+        # Return True if pin's bit is set.
+        return [(self.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.
+        """
+        self._validate_pin(pin)
+        if enabled:
+            self.gppu[int(pin/8)] |= 1 << (int(pin%8))
+        else:
+            self.gppu[int(pin/8)] &= ~(1 << (int(pin%8)))
+        self.write_gppu()
+
+    def write_gpio(self, gpio=None):
+        """Write the specified byte value to the GPIO registor.  If no value
+        specified the current buffered value will be written.
+        """
+        if gpio is not None:
+            self.gpio = gpio
+        self._device.writeList(self.GPIO, self.gpio)
+
+    def write_iodir(self, iodir=None):
+        """Write the specified byte value to the IODIR registor.  If no value
+        specified the current buffered value will be written.
+        """
+        if iodir is not None:
+            self.iodir = iodir
+        self._device.writeList(self.IODIR, self.iodir)
+
+    def write_gppu(self, gppu=None):
+        """Write the specified byte value to the GPPU registor.  If no value
+        specified the current buffered value will be written.
+        """
+        if gppu is not None:
+            self.gppu = gppu
+        self._device.writeList(self.GPPU, self.gppu)
+
+
+class MCP23017(MCP230xxBase):
+    """MCP23017-based GPIO class with 16 GPIO pins."""
+    # Define number of pins and registor addresses.
+    NUM_GPIO = 16
+    IODIR    = 0x00
+    GPIO     = 0x12
+    GPPU     = 0x0C
+
+    def __init__(self, address=0x20, **kwargs):
+        super(MCP23017, self).__init__(address, **kwargs)
+
+
+class MCP23008(MCP230xxBase):
+    """MCP23008-based GPIO class with 8 GPIO pins."""
+    # Define number of pins and registor addresses.
+    NUM_GPIO = 8
+    IODIR    = 0x00
+    GPIO     = 0x09
+    GPPU     = 0x06
+
+    def __init__(self, address=0x20, **kwargs):
+        super(MCP23008, self).__init__(address, **kwargs)
diff --git a/lib/Adafruit_Python_GPIO/Adafruit_GPIO/PCA95xx.py b/lib/Adafruit_Python_GPIO/Adafruit_GPIO/PCA95xx.py
new file mode 100644
index 0000000..2a3b406
--- /dev/null
+++ b/lib/Adafruit_Python_GPIO/Adafruit_GPIO/PCA95xx.py
@@ -0,0 +1,121 @@
+'''
+Adafruit compatible using BaseGPIO class to represent a PCA9555 IO expander
+Copyright (C) 2016 Matias Vidal
+Ported from: https://github.com/dberlin/PCA95XX
+
+# Copyright 2012 Daniel Berlin
+
+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
+
+# For the PCA 953X and 955X series, the chips with 8 GPIO's have these port numbers
+# The chips with 16 GPIO's have the first port for each type at double these numbers
+# IE The first config port is 6
+
+INPUT_PORT = 0
+OUTPUT_PORT = 1
+POLARITY_PORT = 2
+CONFIG_PORT = 3
+
+IN = GPIO.IN
+OUT = GPIO.OUT
+HIGH = GPIO.HIGH
+LOW = GPIO.LOW
+
+
+class PCA9555(GPIO.BaseGPIO):
+    """Class to represent a PCA9555  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 = 16
+
+    def __init__(self, address=0x20, busnum=None, i2c=None, num_gpios=16, **kwargs):
+        address = int(address)
+        self.__name__ = "PCA955"
+        # Create I2C device.
+        i2c = i2c or I2C
+        busnum = busnum or i2c.get_default_bus()
+        self._device = i2c.get_i2c_device(address, busnum, **kwargs)
+        self.num_gpios = num_gpios
+        
+        if self.num_gpios <= 8:
+            self.iodir = self._device.readU8(CONFIG_PORT)
+            self.outputvalue = self._device.readU8(OUTPUT_PORT)
+
+        elif self.num_gpios > 8 and self.num_gpios <= 16:
+            self.iodir = self._device.readU16(CONFIG_PORT<< 1)
+            self.outputvalue = self._device.readU16(OUTPUT_PORT << 1)
+
+    def _changebit(self, bitmap, bit, value):
+        assert value == 1 or value == 0, "Value is %s must be 1 or 0" % value
+        if value == 0:
+            return bitmap & ~(1 << bit)
+        elif value == 1:
+            return bitmap | (1 << bit)
+
+    # Change the value of bit PIN on port PORT to VALUE.  If the
+    # current pin state for the port is passed in as PORTSTATE, we
+    # will avoid doing a read to get it.  The port pin state must be
+    # complete if passed in (IE it should not just be the value of the
+    # single pin we are trying to change)
+    def _readandchangepin(self, port, pin, value, portstate = None):
+        assert pin >= 0 and pin < self.num_gpios, "Pin number %s is invalid, only 0-%s are valid" % (pin, self.num_gpios)
+        if not portstate:
+          if self.num_gpios <= 8:
+             portstate = self._device.readU8(port)
+          elif self.num_gpios > 8 and self.num_gpios <= 16:
+             portstate = self._device.readU16(port << 1)
+        newstate = self._changebit(portstate, pin, value)
+        if self.num_gpios <= 8:
+            self._device.write8(port, newstate)
+        else:
+            self._device.write16(port << 1, newstate)
+        return newstate
+
+    # Polarity inversion
+    def polarity(self, pin, value):
+        return self._readandchangepin(POLARITY_PORT, pin, value)
+
+    # Pin direction
+    def config(self, pin, mode):
+        self.iodir = self._readandchangepin(CONFIG_PORT, pin, mode, self.iodir)
+        return self.iodir
+
+    def output(self, pin, value):
+        assert self.iodir & (1 << pin) == 0, "Pin %s not set to output" % pin
+        self.outputvalue = self._readandchangepin(OUTPUT_PORT, pin, value, self.outputvalue)
+        return self.outputvalue
+
+    def input(self, pin):
+        assert self.iodir & (1 << pin) != 0, "Pin %s not set to input" % pin
+        if self.num_gpios <= 8:
+            value = self._device.readU8(INPUT_PORT)
+        elif self.num_gpios > 8 and self.num_gpios <= 16:
+            value = self._device.readU16(INPUT_PORT << 1)
+        return value & (1 << pin)
+
+    def setup(self, pin, mode):
+        self.config(pin, mode)
+
+    def cleanup(self, pin=None):
+        # nothing to cleanup
+        pass
diff --git a/lib/Adafruit_Python_GPIO/Adafruit_GPIO/PCF8574.py b/lib/Adafruit_Python_GPIO/Adafruit_GPIO/PCF8574.py
new file mode 100644
index 0000000..02919d1
--- /dev/null
+++ b/lib/Adafruit_Python_GPIO/Adafruit_GPIO/PCF8574.py
@@ -0,0 +1,94 @@
+'''
+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=0x27, busnum=None, 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.
+        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
+        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 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.items()]]:
+            raise ValueError('Invalid MODE, IN or OUT')
+        for pin,mode in pins.items():
+            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.items():
+            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 & (1<<pin)) for pin in pins]
diff --git a/lib/Adafruit_Python_GPIO/Adafruit_GPIO/PWM.py b/lib/Adafruit_Python_GPIO/Adafruit_GPIO/PWM.py
new file mode 100644
index 0000000..1e1717a
--- /dev/null
+++ b/lib/Adafruit_Python_GPIO/Adafruit_GPIO/PWM.py
@@ -0,0 +1,128 @@
+# Copyright (c) 2014 Adafruit Industries
+# Author: Tony DiCola
+#
+# 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.Platform as Platform
+
+
+class RPi_PWM_Adapter(object):
+    """PWM implementation for the Raspberry Pi using the RPi.GPIO PWM library."""
+
+    def __init__(self, rpi_gpio, mode=None):
+        self.rpi_gpio = rpi_gpio
+        # Suppress warnings about GPIO in use.
+        rpi_gpio.setwarnings(False)
+        # Set board or BCM pin numbering.
+        if mode == rpi_gpio.BOARD or mode == rpi_gpio.BCM:
+            rpi_gpio.setmode(mode)
+        elif mode is not None:
+            raise ValueError('Unexpected value for mode.  Must be BOARD or BCM.')
+        else:
+            # Default to BCM numbering if not told otherwise.
+            rpi_gpio.setmode(rpi_gpio.BCM)
+        # Store reference to each created PWM instance.
+        self.pwm = {}
+
+    def start(self, pin, dutycycle, frequency_hz=2000):
+        """Enable PWM output on specified pin.  Set to intiial percent duty cycle
+        value (0.0 to 100.0) and frequency (in Hz).
+        """
+        if dutycycle < 0.0 or dutycycle > 100.0:
+            raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).')
+        # Make pin an output.
+        self.rpi_gpio.setup(pin, self.rpi_gpio.OUT)
+        # Create PWM instance and save a reference for later access.
+        self.pwm[pin] = self.rpi_gpio.PWM(pin, frequency_hz)
+        # Start the PWM at the specified duty cycle.
+        self.pwm[pin].start(dutycycle)
+
+    def set_duty_cycle(self, pin, dutycycle):
+        """Set percent duty cycle of PWM output on specified pin.  Duty cycle must
+        be a value 0.0 to 100.0 (inclusive).
+        """
+        if dutycycle < 0.0 or dutycycle > 100.0:
+            raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).')
+        if pin not in self.pwm:
+            raise ValueError('Pin {0} is not configured as a PWM.  Make sure to first call start for the pin.'.format(pin))
+        self.pwm[pin].ChangeDutyCycle(dutycycle)
+
+    def set_frequency(self, pin, frequency_hz):
+        """Set frequency (in Hz) of PWM output on specified pin."""
+        if pin not in self.pwm:
+            raise ValueError('Pin {0} is not configured as a PWM.  Make sure to first call start for the pin.'.format(pin))
+        self.pwm[pin].ChangeFrequency(frequency_hz)
+
+    def stop(self, pin):
+        """Stop PWM output on specified pin."""
+        if pin not in self.pwm:
+            raise ValueError('Pin {0} is not configured as a PWM.  Make sure to first call start for the pin.'.format(pin))
+        self.pwm[pin].stop()
+        del self.pwm[pin]
+
+
+class BBIO_PWM_Adapter(object):
+    """PWM implementation for the BeagleBone Black using the Adafruit_BBIO.PWM
+    library.
+    """
+
+    def __init__(self, bbio_pwm):
+        self.bbio_pwm = bbio_pwm
+
+    def start(self, pin, dutycycle, frequency_hz=2000):
+        """Enable PWM output on specified pin.  Set to intiial percent duty cycle
+        value (0.0 to 100.0) and frequency (in Hz).
+        """
+        if dutycycle < 0.0 or dutycycle > 100.0:
+            raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).')
+        self.bbio_pwm.start(pin, dutycycle, frequency_hz)
+
+    def set_duty_cycle(self, pin, dutycycle):
+        """Set percent duty cycle of PWM output on specified pin.  Duty cycle must
+        be a value 0.0 to 100.0 (inclusive).
+        """
+        if dutycycle < 0.0 or dutycycle > 100.0:
+            raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).')
+        self.bbio_pwm.set_duty_cycle(pin, dutycycle)
+
+    def set_frequency(self, pin, frequency_hz):
+        """Set frequency (in Hz) of PWM output on specified pin."""
+        self.bbio_pwm.set_frequency(pin, frequency_hz)
+
+    def stop(self, pin):
+        """Stop PWM output on specified pin."""
+        self.bbio_pwm.stop(pin)
+
+
+def get_platform_pwm(**keywords):
+    """Attempt to return a PWM instance for the platform which the code is being
+    executed on.  Currently supports only the Raspberry Pi using the RPi.GPIO
+    library and Beaglebone Black using the Adafruit_BBIO library.  Will throw an
+    exception if a PWM instance can't be created for the current platform.  The
+    returned PWM object has the same interface as the RPi_PWM_Adapter and
+    BBIO_PWM_Adapter classes.
+    """
+    plat = Platform.platform_detect()
+    if plat == Platform.RASPBERRY_PI:
+        import RPi.GPIO
+        return RPi_PWM_Adapter(RPi.GPIO, **keywords)
+    elif plat == Platform.BEAGLEBONE_BLACK:
+        import Adafruit_BBIO.PWM
+        return BBIO_PWM_Adapter(Adafruit_BBIO.PWM, **keywords)
+    elif plat == Platform.UNKNOWN:
+        raise RuntimeError('Could not determine platform.')
diff --git a/lib/Adafruit_Python_GPIO/Adafruit_GPIO/Platform.py b/lib/Adafruit_Python_GPIO/Adafruit_GPIO/Platform.py
new file mode 100644
index 0000000..2c041a8
--- /dev/null
+++ b/lib/Adafruit_Python_GPIO/Adafruit_GPIO/Platform.py
@@ -0,0 +1,110 @@
+# Copyright (c) 2014 Adafruit Industries
+# Author: Tony DiCola
+
+# 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 platform
+import re
+
+# Platform identification constants.
+UNKNOWN          = 0
+RASPBERRY_PI     = 1
+BEAGLEBONE_BLACK = 2
+MINNOWBOARD      = 3
+
+def platform_detect():
+    """Detect if running on the Raspberry Pi or Beaglebone Black and return the
+    platform type.  Will return RASPBERRY_PI, BEAGLEBONE_BLACK, or UNKNOWN."""
+    # Handle Raspberry Pi
+    pi = pi_version()
+    if pi is not None:
+        return RASPBERRY_PI
+
+    # Handle Beaglebone Black
+    # TODO: Check the Beaglebone Black /proc/cpuinfo value instead of reading
+    # the platform.
+    plat = platform.platform()
+    if plat.lower().find('armv7l-with-debian') > -1:
+        return BEAGLEBONE_BLACK
+    elif plat.lower().find('armv7l-with-ubuntu') > -1:
+        return BEAGLEBONE_BLACK
+    elif plat.lower().find('armv7l-with-glibc2.4') > -1:
+        return BEAGLEBONE_BLACK
+        
+    # Handle Minnowboard
+    # Assumption is that mraa is installed
+    try: 
+        import mraa 
+        if mraa.getPlatformName()=='MinnowBoard MAX':
+            return MINNOWBOARD
+    except ImportError:
+        pass
+    
+    # Couldn't figure out the platform, just return unknown.
+    return UNKNOWN
+
+
+def pi_revision():
+    """Detect the revision number of a Raspberry Pi, useful for changing
+    functionality like default I2C bus based on revision."""
+    # Revision list available at: http://elinux.org/RPi_HardwareHistory#Board_Revision_History
+    with open('/proc/cpuinfo', 'r') as infile:
+        for line in infile:
+            # Match a line of the form "Revision : 0002" while ignoring extra
+            # info in front of the revsion (like 1000 when the Pi was over-volted).
+            match = re.match('Revision\s+:\s+.*(\w{4})$', line, flags=re.IGNORECASE)
+            if match and match.group(1) in ['0000', '0002', '0003']:
+                # Return revision 1 if revision ends with 0000, 0002 or 0003.
+                return 1
+            elif match:
+                # Assume revision 2 if revision ends with any other 4 chars.
+                return 2
+        # Couldn't find the revision, throw an exception.
+        raise RuntimeError('Could not determine Raspberry Pi revision.')
+
+
+def pi_version():
+    """Detect the version of the Raspberry Pi.  Returns either 1, 2 or
+    None depending on if it's a Raspberry Pi 1 (model A, B, A+, B+),
+    Raspberry Pi 2 (model B+), or not a Raspberry Pi.
+    """
+    # Check /proc/cpuinfo for the Hardware field value.
+    # 2708 is pi 1
+    # 2709 is pi 2
+    # 2835 is pi 3 on 4.9.x kernel
+    # Anything else is not a pi.
+    with open('/proc/cpuinfo', 'r') as infile:
+        cpuinfo = infile.read()
+    # Match a line like 'Hardware   : BCM2709'
+    match = re.search('^Hardware\s+:\s+(\w+)$', cpuinfo,
+                      flags=re.MULTILINE | re.IGNORECASE)
+    if not match:
+        # Couldn't find the hardware, assume it isn't a pi.
+        return None
+    if match.group(1) == 'BCM2708':
+        # Pi 1
+        return 1
+    elif match.group(1) == 'BCM2709':
+        # Pi 2
+        return 2
+    elif match.group(1) == 'BCM2835':
+        # Pi 3 / Pi on 4.9.x kernel
+        return 3
+    else:
+        # Something else, not a pi.
+        return None
diff --git a/lib/Adafruit_Python_GPIO/Adafruit_GPIO/SPI.py b/lib/Adafruit_Python_GPIO/Adafruit_GPIO/SPI.py
new file mode 100644
index 0000000..c20a32c
--- /dev/null
+++ b/lib/Adafruit_Python_GPIO/Adafruit_GPIO/SPI.py
@@ -0,0 +1,328 @@
+# Copyright (c) 2014 Adafruit Industries
+# Author: Tony DiCola
+#
+# 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 operator
+import time
+
+import Adafruit_GPIO as GPIO
+
+
+MSBFIRST = 0
+LSBFIRST = 1
+
+
+class SpiDev(object):
+    """Hardware-based SPI implementation using the spidev interface."""
+
+    def __init__(self, port, device, max_speed_hz=500000):
+        """Initialize an SPI device using the SPIdev interface.  Port and device
+        identify the device, for example the device /dev/spidev1.0 would be port
+        1 and device 0.
+        """
+        import spidev
+        self._device = spidev.SpiDev()
+        self._device.open(port, device)
+        self._device.max_speed_hz=max_speed_hz
+        # Default to mode 0, and make sure CS is active low.
+        self._device.mode = 0
+        self._device.cshigh = False
+
+    def set_clock_hz(self, hz):
+        """Set the speed of the SPI clock in hertz.  Note that not all speeds
+        are supported and a lower speed might be chosen by the hardware.
+        """
+        self._device.max_speed_hz=hz
+
+    def set_mode(self, mode):
+        """Set SPI mode which controls clock polarity and phase.  Should be a
+        numeric value 0, 1, 2, or 3.  See wikipedia page for details on meaning:
+        http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
+        """
+        if mode < 0 or mode > 3:
+            raise ValueError('Mode must be a value 0, 1, 2, or 3.')
+        self._device.mode = mode
+
+    def set_bit_order(self, order):
+        """Set order of bits to be read/written over serial lines.  Should be
+        either MSBFIRST for most-significant first, or LSBFIRST for
+        least-signifcant first.
+        """
+        if order == MSBFIRST:
+            self._device.lsbfirst = False
+        elif order == LSBFIRST:
+            self._device.lsbfirst = True
+        else:
+            raise ValueError('Order must be MSBFIRST or LSBFIRST.')
+
+    def close(self):
+        """Close communication with the SPI device."""
+        self._device.close()
+
+    def write(self, data):
+        """Half-duplex SPI write.  The specified array of bytes will be clocked
+        out the MOSI line.
+        """
+        self._device.writebytes(data)
+
+    def read(self, length):
+        """Half-duplex SPI read.  The specified length of bytes will be clocked
+        in the MISO line and returned as a bytearray object.
+        """
+        return bytearray(self._device.readbytes(length))
+
+    def transfer(self, data):
+        """Full-duplex SPI read and write.  The specified array of bytes will be
+        clocked out the MOSI line, while simultaneously bytes will be read from
+        the MISO line.  Read bytes will be returned as a bytearray object.
+        """
+        return bytearray(self._device.xfer2(data))
+
+class SpiDevMraa(object):
+    """Hardware SPI implementation with the mraa library on Minnowboard"""
+    def __init__(self, port, device, max_speed_hz=500000):
+        import mraa
+        self._device = mraa.Spi(0)
+        self._device.mode(0)
+        
+    def set_clock_hz(self, hz):
+        """Set the speed of the SPI clock in hertz.  Note that not all speeds
+        are supported and a lower speed might be chosen by the hardware.
+        """
+        self._device.frequency(hz)
+
+    def set_mode(self,mode):
+        """Set SPI mode which controls clock polarity and phase.  Should be a
+        numeric value 0, 1, 2, or 3.  See wikipedia page for details on meaning:
+        http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
+        """
+        if mode < 0 or mode > 3:
+            raise ValueError('Mode must be a value 0, 1, 2, or 3.')
+        self._device.mode(mode)
+
+    def set_bit_order(self, order):
+        """Set order of bits to be read/written over serial lines.  Should be
+        either MSBFIRST for most-significant first, or LSBFIRST for
+        least-signifcant first.
+        """
+        if order == MSBFIRST:
+            self._device.lsbmode(False)
+        elif order == LSBFIRST:
+            self._device.lsbmode(True)
+        else:
+            raise ValueError('Order must be MSBFIRST or LSBFIRST.')
+        
+    def close(self):
+        """Close communication with the SPI device."""
+        self._device.Spi()
+
+    def write(self, data):
+        """Half-duplex SPI write.  The specified array of bytes will be clocked
+        out the MOSI line.
+        """
+        self._device.write(bytearray(data))
+
+class BitBang(object):
+    """Software-based implementation of the SPI protocol over GPIO pins."""
+
+    def __init__(self, gpio, sclk, mosi=None, miso=None, ss=None):
+        """Initialize bit bang (or software) based SPI.  Must provide a BaseGPIO
+        class, the SPI clock, and optionally MOSI, MISO, and SS (slave select)
+        pin numbers. If MOSI is set to None then writes will be disabled and fail
+        with an error, likewise for MISO reads will be disabled.  If SS is set to
+        None then SS will not be asserted high/low by the library when
+        transfering data.
+        """
+        self._gpio = gpio
+        self._sclk = sclk
+        self._mosi = mosi
+        self._miso = miso
+        self._ss = ss
+        # Set pins as outputs/inputs.
+        gpio.setup(sclk, GPIO.OUT)
+        if mosi is not None:
+            gpio.setup(mosi, GPIO.OUT)
+        if miso is not None:
+            gpio.setup(miso, GPIO.IN)
+        if ss is not None:
+            gpio.setup(ss, GPIO.OUT)
+            # Assert SS high to start with device communication off.
+            gpio.set_high(ss)
+        # Assume mode 0.
+        self.set_mode(0)
+        # Assume most significant bit first order.
+        self.set_bit_order(MSBFIRST)
+
+    def set_clock_hz(self, hz):
+        """Set the speed of the SPI clock.  This is unsupported with the bit
+        bang SPI class and will be ignored.
+        """
+        pass
+
+    def set_mode(self, mode):
+        """Set SPI mode which controls clock polarity and phase.  Should be a
+        numeric value 0, 1, 2, or 3.  See wikipedia page for details on meaning:
+        http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
+        """
+        if mode < 0 or mode > 3:
+            raise ValueError('Mode must be a value 0, 1, 2, or 3.')
+        if mode & 0x02:
+            # Clock is normally high in mode 2 and 3.
+            self._clock_base = GPIO.HIGH
+        else:
+            # Clock is normally low in mode 0 and 1.
+            self._clock_base = GPIO.LOW
+        if mode & 0x01:
+            # Read on trailing edge in mode 1 and 3.
+            self._read_leading = False
+        else:
+            # Read on leading edge in mode 0 and 2.
+            self._read_leading = True
+        # Put clock into its base state.
+        self._gpio.output(self._sclk, self._clock_base)
+
+    def set_bit_order(self, order):
+        """Set order of bits to be read/written over serial lines.  Should be
+        either MSBFIRST for most-significant first, or LSBFIRST for
+        least-signifcant first.
+        """
+        # Set self._mask to the bitmask which points at the appropriate bit to
+        # read or write, and appropriate left/right shift operator function for
+        # reading/writing.
+        if order == MSBFIRST:
+            self._mask = 0x80
+            self._write_shift = operator.lshift
+            self._read_shift = operator.rshift
+        elif order == LSBFIRST:
+            self._mask = 0x01
+            self._write_shift = operator.rshift
+            self._read_shift = operator.lshift
+        else:
+            raise ValueError('Order must be MSBFIRST or LSBFIRST.')
+
+    def close(self):
+        """Close the SPI connection.  Unused in the bit bang implementation."""
+        pass
+
+    def write(self, data, assert_ss=True, deassert_ss=True):
+        """Half-duplex SPI write.  If assert_ss is True, the SS line will be
+        asserted low, the specified bytes will be clocked out the MOSI line, and
+        if deassert_ss is True the SS line be put back high.
+        """
+        # Fail MOSI is not specified.
+        if self._mosi is None:
+            raise RuntimeError('Write attempted with no MOSI pin specified.')
+        if assert_ss and self._ss is not None:
+            self._gpio.set_low(self._ss)
+        for byte in data:
+            for i in range(8):
+                # Write bit to MOSI.
+                if self._write_shift(byte, i) & self._mask:
+                    self._gpio.set_high(self._mosi)
+                else:
+                    self._gpio.set_low(self._mosi)
+                # Flip clock off base.
+                self._gpio.output(self._sclk, not self._clock_base)
+                # Return clock to base.
+                self._gpio.output(self._sclk, self._clock_base)
+        if deassert_ss and self._ss is not None:
+            self._gpio.set_high(self._ss)
+
+    def read(self, length, assert_ss=True, deassert_ss=True):
+        """Half-duplex SPI read.  If assert_ss is true, the SS line will be
+        asserted low, the specified length of bytes will be clocked in the MISO
+        line, and if deassert_ss is true the SS line will be put back high.
+        Bytes which are read will be returned as a bytearray object.
+        """
+        if self._miso is None:
+            raise RuntimeError('Read attempted with no MISO pin specified.')
+        if assert_ss and self._ss is not None:
+            self._gpio.set_low(self._ss)
+        result = bytearray(length)
+        for i in range(length):
+            for j in range(8):
+                # Flip clock off base.
+                self._gpio.output(self._sclk, not self._clock_base)
+                # Handle read on leading edge of clock.
+                if self._read_leading:
+                    if self._gpio.is_high(self._miso):
+                        # Set bit to 1 at appropriate location.
+                        result[i] |= self._read_shift(self._mask, j)
+                    else:
+                        # Set bit to 0 at appropriate location.
+                        result[i] &= ~self._read_shift(self._mask, j)
+                # Return clock to base.
+                self._gpio.output(self._sclk, self._clock_base)
+                # Handle read on trailing edge of clock.
+                if not self._read_leading:
+                    if self._gpio.is_high(self._miso):
+                        # Set bit to 1 at appropriate location.
+                        result[i] |= self._read_shift(self._mask, j)
+                    else:
+                        # Set bit to 0 at appropriate location.
+                        result[i] &= ~self._read_shift(self._mask, j)
+        if deassert_ss and self._ss is not None:
+            self._gpio.set_high(self._ss)
+        return result
+
+    def transfer(self, data, assert_ss=True, deassert_ss=True):
+        """Full-duplex SPI read and write.  If assert_ss is true, the SS line
+        will be asserted low, the specified bytes will be clocked out the MOSI
+        line while bytes will also be read from the MISO line, and if
+        deassert_ss is true the SS line will be put back high.  Bytes which are
+        read will be returned as a bytearray object.
+        """
+        if self._mosi is None:
+            raise RuntimeError('Write attempted with no MOSI pin specified.')
+        if self._mosi is None:
+            raise RuntimeError('Read attempted with no MISO pin specified.')
+        if assert_ss and self._ss is not None:
+            self._gpio.set_low(self._ss)
+        result = bytearray(len(data))
+        for i in range(len(data)):
+            for j in range(8):
+                # Write bit to MOSI.
+                if self._write_shift(data[i], j) & self._mask:
+                    self._gpio.set_high(self._mosi)
+                else:
+                    self._gpio.set_low(self._mosi)
+                # Flip clock off base.
+                self._gpio.output(self._sclk, not self._clock_base)
+                # Handle read on leading edge of clock.
+                if self._read_leading:
+                    if self._gpio.is_high(self._miso):
+                        # Set bit to 1 at appropriate location.
+                        result[i] |= self._read_shift(self._mask, j)
+                    else:
+                        # Set bit to 0 at appropriate location.
+                        result[i] &= ~self._read_shift(self._mask, j)
+                # Return clock to base.
+                self._gpio.output(self._sclk, self._clock_base)
+                # Handle read on trailing edge of clock.
+                if not self._read_leading:
+                    if self._gpio.is_high(self._miso):
+                        # Set bit to 1 at appropriate location.
+                        result[i] |= self._read_shift(self._mask, j)
+                    else:
+                        # Set bit to 0 at appropriate location.
+                        result[i] &= ~self._read_shift(self._mask, j)
+        if deassert_ss and self._ss is not None:
+            self._gpio.set_high(self._ss)
+        return result
diff --git a/lib/Adafruit_Python_GPIO/Adafruit_GPIO/__init__.py b/lib/Adafruit_Python_GPIO/Adafruit_GPIO/__init__.py
new file mode 100644
index 0000000..7e99604
--- /dev/null
+++ b/lib/Adafruit_Python_GPIO/Adafruit_GPIO/__init__.py
@@ -0,0 +1,3 @@
+from __future__ import absolute_import
+
+from Adafruit_GPIO.GPIO import *
-- 
cgit v1.2.3