Top

rstem.gpio module

This module provides interfaces to GPIOs - useful for the lighting LEDs in the I/O Ready Set STEM Cell, and other Ready Set STEM Cells.

#!/usr/bin/env python3
#
# Copyright (c) 2014, Scott Silver Labs, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
'''
This module provides interfaces to GPIOs - useful for the lighting LEDs in the I/O Ready Set STEM
Cell, and other Ready Set STEM Cells.
'''

import os
import time
from functools import partial
import re

PINS = range(2, 28)

PULLUP_CMD = '/usr/local/bin/pullup.sbin'
GPIO_EXPORT_FILE = '/sys/class/gpio/export'
GPIO_UNEXPORT_FILE = '/sys/class/gpio/unexport'
GPIO_PIN_FORMAT_STRING = '/sys/class/gpio/gpio%d'

PULL_DISABLE = 0
PULL_DOWN = 1
PULL_UP = 2

def _global_pin_init():
    # Disable all GPIOs to start.  Note that unexporting a GPIO if it hasn't
    # been exported will fail - but we will ignore it.
    for pin in PINS:
        try:
            with open(GPIO_UNEXPORT_FILE, 'w') as f:
                f.write('%d' % pin)
        except IOError:
            pass

def retry_func_on_error(func, tries=10, sleep=0.01, exc=IOError):
    for i in range(tries):
        try:
            ret = func()
        except exc as err:
            time.sleep(sleep)
            sleep *= 2
        else:
            break
    else:
        raise err
    return ret

class Pin(object):
    _global_pin_init()

    def __init__(self, pin):
        self._board_rev = self.board_rev()
        self.gpio_dir = GPIO_PIN_FORMAT_STRING % pin
        self.pin = pin
        if pin in PINS:
            if os.path.exists(self.gpio_dir):
                raise IOError('GPIO pin already in use')

            with open(GPIO_EXPORT_FILE, 'w') as f:
                f.write('%d\n' % pin)

            # Repeat trying to configure GPIO as input (default).  Repeats
            # required because this can fail when run right after the pin
            # is exported in /sys.  Once it passes, we know the pin is
            # ready to use.
            retry_func_on_error(partial(self._set_output, False))
        else:
            raise ValueError('Invalid GPIO pin')
        self._write_gpio_file('direction', 'in')
        self._write_gpio_file('active_low', '0')
        self._write_gpio_file('edge', 'none')
        self._set_pull(PULL_DISABLE)

    @staticmethod
    def board_rev():
        with open('/proc/cpuinfo') as f:
            cpuinfo = f.read()
        matches = re.findall(
            '^Revision.*([0-9A-F]{4})$',
            cpuinfo,
            flags=(re.MULTILINE|re.IGNORECASE)
            )
        board_rev = int(matches[0], 16)
        return board_rev

    def _write_gpio_file(self, filename, value):
        def write_val(filename, value):
            with open(self.gpio_dir + '/' + filename, 'w') as f:
                f.write(value)
        retry_func_on_error(partial(write_val, filename, value))

    def _set_output(self, output, starts_off=True):
        if output:
            is_low = self._active_low and not starts_off or not self._active_low and starts_off
            direction = 'low' if is_low else 'high'
        else:
            direction = 'in'
        self._write_gpio_file('direction', direction)

    def _set_pull(self, pull):
        if not pull in [PULL_UP, PULL_DOWN, PULL_DISABLE]:
            raise ValueError('Invalid pull type')
        os.system('%s %d %d %d' % (PULLUP_CMD, self.pin, pull, self._board_rev))

    def disable(self):
        '''Disable the GPIO pin.'''
        with open(GPIO_UNEXPORT_FILE, 'w') as f:
            f.write('%d\n' % self.pin)


class Output(Pin):
    '''A GPIO output.

    An `rstem.gpio.Output` configures a GPIO pin as an output.  The pin can then used as a
    programmable switch to drive LEDs, motor drivers, relays and other devices.
    '''
    def __init__(self, pin, active_low=True):
        '''Create a new `Output`.

        `pin` is the number of the GPIO as labeled on the Ready Set STEM Lid
        connector.  It is the GPIO number used by the Broadcom processor on
        the Raspberry Pi.

        If `active_low=True` (the default), then the output will be set LOW
        (grounded, i.e. 0 volts) when the output is turned `on`.  If
        `active_low=False`, then the output will be set HIGH (the supply
        voltage, i.e. 3.3 volts) when the output is turned `on`.
        '''
        super().__init__(pin)
        self._active_low = active_low
        self._set_output(True)
        self._fvalue = retry_func_on_error(partial(open, self.gpio_dir + '/value', 'w'))

    def _set(self, level):
        self._fvalue.seek(0)
        self._fvalue.write('1' if level else '0')
        self._fvalue.flush()

    def on(self):
        '''Turn the GPIO output on (repects `active_low` setting).'''
        self._set(not self._active_low)

    def off(self):
        '''Turn the GPIO output off (repects `active_low` setting).'''
        self._set(self._active_low)

    def disable(self):
        '''Disable the GPIO pin.'''
        self._fvalue.close()
        super().disable()

class Input(Pin):
    '''A GPIO input.

    An `rstem.gpio.Input` configures a GPIO pin as an input.  The pin can then
    used as to read the logic level of the pin - useful for reading the state
    of switches, sensors, and other electronics.
    '''
    def __init__(self, pin, active_low=False, pull=PULL_DISABLE):
        '''Create a new `Input`.

        `pin` is the number of the GPIO as labeled on the Ready Set STEM Lid
        connector.  It is the GPIO number used by the Broadcom processor on
        the Raspberry Pi.

        If `active_low=True` (the default), then when the input is externally
        set LOW (grounded, i.e. 0 volts), it is considered `on`.  If
        `active_low=False`, then when the input is externally set HIGH (the
        supply voltage, i.e. 3.3 volts), it is considered `on`.

        `pull` is the state of the GPIO internal pullup/down.
        If `pull` is `PULL_DISABLE`, then the internal pullup is disabled.
        If `pull` is `PULL_UP`, then the internal pullup is enabled.
        If `pull` is `PULL_DOWN`, then the internal pulldown is enabled.
        '''
        super().__init__(pin)
        self._active_low = active_low
        self._set_output(False)
        self._set_pull(pull)
        self._fvalue = retry_func_on_error(partial(open, self.gpio_dir + '/value', 'r'))

    def configure(self, pull=None):
        '''Set pullup on `pin`.  See `__init__` for info on the `pull` argument.'''
        self._set_pull(pull)

    def _get(self):
        self._fvalue.seek(0)
        return 1 if self._fvalue.read().strip() == '1' else 0

    def is_on(self):
        '''Return the GPIO input state (repects `active_low` setting).'''
        return bool(not self._get() if self._active_low else self._get())

    def is_off(self):
        '''Return the GPIO input state (repects `active_low` setting).'''
        return not self.is_on()

    def disable(self):
        '''Disable the GPIO pin.'''
        self._fvalue.close()
        super().disable()

__all__ = ['Output', 'Input', PULL_DISABLE, PULL_UP, PULL_DOWN]

Classes

class Input

A GPIO input.

An Input configures a GPIO pin as an input. The pin can then used as to read the logic level of the pin - useful for reading the state of switches, sensors, and other electronics.

class Input(Pin):
    '''A GPIO input.

    An `rstem.gpio.Input` configures a GPIO pin as an input.  The pin can then
    used as to read the logic level of the pin - useful for reading the state
    of switches, sensors, and other electronics.
    '''
    def __init__(self, pin, active_low=False, pull=PULL_DISABLE):
        '''Create a new `Input`.

        `pin` is the number of the GPIO as labeled on the Ready Set STEM Lid
        connector.  It is the GPIO number used by the Broadcom processor on
        the Raspberry Pi.

        If `active_low=True` (the default), then when the input is externally
        set LOW (grounded, i.e. 0 volts), it is considered `on`.  If
        `active_low=False`, then when the input is externally set HIGH (the
        supply voltage, i.e. 3.3 volts), it is considered `on`.

        `pull` is the state of the GPIO internal pullup/down.
        If `pull` is `PULL_DISABLE`, then the internal pullup is disabled.
        If `pull` is `PULL_UP`, then the internal pullup is enabled.
        If `pull` is `PULL_DOWN`, then the internal pulldown is enabled.
        '''
        super().__init__(pin)
        self._active_low = active_low
        self._set_output(False)
        self._set_pull(pull)
        self._fvalue = retry_func_on_error(partial(open, self.gpio_dir + '/value', 'r'))

    def configure(self, pull=None):
        '''Set pullup on `pin`.  See `__init__` for info on the `pull` argument.'''
        self._set_pull(pull)

    def _get(self):
        self._fvalue.seek(0)
        return 1 if self._fvalue.read().strip() == '1' else 0

    def is_on(self):
        '''Return the GPIO input state (repects `active_low` setting).'''
        return bool(not self._get() if self._active_low else self._get())

    def is_off(self):
        '''Return the GPIO input state (repects `active_low` setting).'''
        return not self.is_on()

    def disable(self):
        '''Disable the GPIO pin.'''
        self._fvalue.close()
        super().disable()

Ancestors (in MRO)

  • Input
  • rstem.gpio.Pin
  • builtins.object

Static methods

def __init__(

self, pin, active_low=False, pull=0)

Create a new Input.

pin is the number of the GPIO as labeled on the Ready Set STEM Lid connector. It is the GPIO number used by the Broadcom processor on the Raspberry Pi.

If active_low=True (the default), then when the input is externally set LOW (grounded, i.e. 0 volts), it is considered on. If active_low=False, then when the input is externally set HIGH (the supply voltage, i.e. 3.3 volts), it is considered on.

pull is the state of the GPIO internal pullup/down. If pull is PULL_DISABLE, then the internal pullup is disabled. If pull is PULL_UP, then the internal pullup is enabled. If pull is PULL_DOWN, then the internal pulldown is enabled.

def __init__(self, pin, active_low=False, pull=PULL_DISABLE):
    '''Create a new `Input`.
    `pin` is the number of the GPIO as labeled on the Ready Set STEM Lid
    connector.  It is the GPIO number used by the Broadcom processor on
    the Raspberry Pi.
    If `active_low=True` (the default), then when the input is externally
    set LOW (grounded, i.e. 0 volts), it is considered `on`.  If
    `active_low=False`, then when the input is externally set HIGH (the
    supply voltage, i.e. 3.3 volts), it is considered `on`.
    `pull` is the state of the GPIO internal pullup/down.
    If `pull` is `PULL_DISABLE`, then the internal pullup is disabled.
    If `pull` is `PULL_UP`, then the internal pullup is enabled.
    If `pull` is `PULL_DOWN`, then the internal pulldown is enabled.
    '''
    super().__init__(pin)
    self._active_low = active_low
    self._set_output(False)
    self._set_pull(pull)
    self._fvalue = retry_func_on_error(partial(open, self.gpio_dir + '/value', 'r'))

def board_rev(

)

@staticmethod
def board_rev():
    with open('/proc/cpuinfo') as f:
        cpuinfo = f.read()
    matches = re.findall(
        '^Revision.*([0-9A-F]{4})$',
        cpuinfo,
        flags=(re.MULTILINE|re.IGNORECASE)
        )
    board_rev = int(matches[0], 16)
    return board_rev

def configure(

self, pull=None)

Set pullup on pin. See __init__ for info on the pull argument.

def configure(self, pull=None):
    '''Set pullup on `pin`.  See `__init__` for info on the `pull` argument.'''
    self._set_pull(pull)

def disable(

self)

Disable the GPIO pin.

def disable(self):
    '''Disable the GPIO pin.'''
    self._fvalue.close()
    super().disable()

def is_off(

self)

Return the GPIO input state (repects active_low setting).

def is_off(self):
    '''Return the GPIO input state (repects `active_low` setting).'''
    return not self.is_on()

def is_on(

self)

Return the GPIO input state (repects active_low setting).

def is_on(self):
    '''Return the GPIO input state (repects `active_low` setting).'''
    return bool(not self._get() if self._active_low else self._get())

class Output

A GPIO output.

An Output configures a GPIO pin as an output. The pin can then used as a programmable switch to drive LEDs, motor drivers, relays and other devices.

class Output(Pin):
    '''A GPIO output.

    An `rstem.gpio.Output` configures a GPIO pin as an output.  The pin can then used as a
    programmable switch to drive LEDs, motor drivers, relays and other devices.
    '''
    def __init__(self, pin, active_low=True):
        '''Create a new `Output`.

        `pin` is the number of the GPIO as labeled on the Ready Set STEM Lid
        connector.  It is the GPIO number used by the Broadcom processor on
        the Raspberry Pi.

        If `active_low=True` (the default), then the output will be set LOW
        (grounded, i.e. 0 volts) when the output is turned `on`.  If
        `active_low=False`, then the output will be set HIGH (the supply
        voltage, i.e. 3.3 volts) when the output is turned `on`.
        '''
        super().__init__(pin)
        self._active_low = active_low
        self._set_output(True)
        self._fvalue = retry_func_on_error(partial(open, self.gpio_dir + '/value', 'w'))

    def _set(self, level):
        self._fvalue.seek(0)
        self._fvalue.write('1' if level else '0')
        self._fvalue.flush()

    def on(self):
        '''Turn the GPIO output on (repects `active_low` setting).'''
        self._set(not self._active_low)

    def off(self):
        '''Turn the GPIO output off (repects `active_low` setting).'''
        self._set(self._active_low)

    def disable(self):
        '''Disable the GPIO pin.'''
        self._fvalue.close()
        super().disable()

Ancestors (in MRO)

  • Output
  • rstem.gpio.Pin
  • builtins.object

Static methods

def __init__(

self, pin, active_low=True)

Create a new Output.

pin is the number of the GPIO as labeled on the Ready Set STEM Lid connector. It is the GPIO number used by the Broadcom processor on the Raspberry Pi.

If active_low=True (the default), then the output will be set LOW (grounded, i.e. 0 volts) when the output is turned on. If active_low=False, then the output will be set HIGH (the supply voltage, i.e. 3.3 volts) when the output is turned on.

def __init__(self, pin, active_low=True):
    '''Create a new `Output`.
    `pin` is the number of the GPIO as labeled on the Ready Set STEM Lid
    connector.  It is the GPIO number used by the Broadcom processor on
    the Raspberry Pi.
    If `active_low=True` (the default), then the output will be set LOW
    (grounded, i.e. 0 volts) when the output is turned `on`.  If
    `active_low=False`, then the output will be set HIGH (the supply
    voltage, i.e. 3.3 volts) when the output is turned `on`.
    '''
    super().__init__(pin)
    self._active_low = active_low
    self._set_output(True)
    self._fvalue = retry_func_on_error(partial(open, self.gpio_dir + '/value', 'w'))

def board_rev(

)

@staticmethod
def board_rev():
    with open('/proc/cpuinfo') as f:
        cpuinfo = f.read()
    matches = re.findall(
        '^Revision.*([0-9A-F]{4})$',
        cpuinfo,
        flags=(re.MULTILINE|re.IGNORECASE)
        )
    board_rev = int(matches[0], 16)
    return board_rev

def disable(

self)

Disable the GPIO pin.

def disable(self):
    '''Disable the GPIO pin.'''
    self._fvalue.close()
    super().disable()

def off(

self)

Turn the GPIO output off (repects active_low setting).

def off(self):
    '''Turn the GPIO output off (repects `active_low` setting).'''
    self._set(self._active_low)

def on(

self)

Turn the GPIO output on (repects active_low setting).

def on(self):
    '''Turn the GPIO output on (repects `active_low` setting).'''
    self._set(not self._active_low)