Source code for philander.gpio

"""General-purpose I/O abstraction module.

Provide a convergence layer API to abstract from several different
GPIO implementing driver modules possibly installed on the target
system.
"""
__author__ = "Oliver Maye"
__version__ = "0.2"
__all__ = ["GPIO"]

import logging
import time

from philander.interruptable import Interruptable
from philander.module import Module
from philander.sysfactory import SysProvider
from philander.systypes import ErrorCode


[docs] class GPIO( Module, Interruptable ): """General-purpose I/O abstraction class. Provide access to and control over the underlying GPIO hardware. For that, an implementing driver module is used. As a convergence layer, this class is to hide specifics and level syntactic requirements of the implementing package. """ _POLL_TIMEOUT = 1 PINNUMBERING_BCM = "BCM" # Pin naming by GPIOx number PINNUMBERING_BOARD = "BOARD" # Pin naming by number on header DIRECTION_IN = 1 DIRECTION_OUT = 2 LEVEL_LOW = 0 LEVEL_HIGH = 1 PULL_DEFAULT = 0 # Don't touch, leave resistance as is PULL_NONE = 1 # Disable resistance PULL_UP = 2 PULL_DOWN = 3 TRIGGER_EDGE_RISING = 1 TRIGGER_EDGE_FALLING = 2 TRIGGER_EDGE_ANY = 3 TRIGGER_LEVEL_HIGH = 4 TRIGGER_LEVEL_LOW = 5 BOUNCE_NONE = 0 # Disable de-bouncing. BOUNCE_DEFAULT = 200 # Default de-bounce interval in ms. EVENT_DEFAULT = "gpioFired" # Specific event fired on interrupt. def __init__(self): """Initialize the instance with defaults. As part of the construction, the underlying implementation is determined. So, at this time, one of the supported gpio packages will be accessed. Still, note that just after construction, the instance is not operable, yet. Call open() to configure it and set it into a functional state. """ self._dictDirection = {} self._dictLevel = {} self._dictPull = {} self._dictTrigger = {} self._lastEventTime = 0 self._pin = None self._softDebounce = True self.bounce = GPIO.BOUNCE_NONE self.designator = None self.direction = GPIO.DIRECTION_OUT self.inverted = False self.isIntEnabled = False self.isOpen = False self.numScheme = GPIO.PINNUMBERING_BCM self.provider = SysProvider.NONE self.trigger = GPIO.TRIGGER_EDGE_RISING Interruptable.__init__(self) # Interrupt handling routine called by the underlying implementation # upon a gpio interrupt occurrence. # Inform registrants by firing an event. # # :param handin: Parameter as provided by the underlying implementation # :type handin: implementation-specific # :rtype: None def _callback(self, handin): if self._softDebounce and (self.bounce > 0): now = time.time() * 1000 if (now - self._lastEventTime) > self.bounce: self._lastEventTime = now self._fire(GPIO.EVENT_DEFAULT, handin) else: self._fire(GPIO.EVENT_DEFAULT, handin) return None
[docs] @classmethod def Params_init(cls, paramDict): """Initialize parameters with their defaults. The given dictionary should not be None, on entry. Options not present in the dictionary will be added and set to their defaults on return. The following options are supported. ================== ============================================== ========================= Key Range Default ================== ============================================== ========================= gpio.pinNumbering GPIO.PINNUMBERING_[BCM | BOARD] GPIO.PINNUMBERING_BCM gpio.pinDesignator pin name or number (e.g. 17 or "GPIO17") None gpio.direction GPIO.DIRECTION_[IN | OUT] GPIO.DIRECTION_OUT gpio.inverted [True | False] False gpio.level GPIO.LEVEL_[LOW | HIGH] GPIO.LEVEL_LOW gpio.pull GPIO.PULL_[DEFAULT | NONE | UP | DOWN] GPIO.PULL_DEFAULT (NONE) gpio.trigger GPIO.TRIGGER_EDGE_[RISING | FALLING | ANY] GPIO.TRIGGER_EDGE_RISING gpio.bounce integer number, delay in milliseconds [ms] GPIO.BOUNCE_DEFAULT gpio.feedback Arbitrary. Passed on to the interrupt handler. None gpio.handler Handling routine reference. None ================== ============================================== ========================= :param dict(str, object) paramDict: Configuration parameters as obtained from :meth:`Params_init`, possibly. :return: none :rtype: None """ if not ("gpio.pinNumbering" in paramDict): paramDict["gpio.pinNumbering"] = GPIO.PINNUMBERING_BCM if not ("gpio.direction" in paramDict): paramDict["gpio.direction"] = GPIO.DIRECTION_OUT if not ("gpio.inverted" in paramDict): paramDict["gpio.inverted"] = False if not ("gpio.level" in paramDict): paramDict["gpio.level"] = GPIO.LEVEL_LOW if not ("gpio.pull" in paramDict): paramDict["gpio.pull"] = GPIO.PULL_DEFAULT if not ("gpio.trigger" in paramDict): paramDict["gpio.trigger"] = GPIO.TRIGGER_EDGE_RISING if not ("gpio.bounce" in paramDict): paramDict["gpio.bounce"] = GPIO.BOUNCE_DEFAULT if not ("gpio.feedback" in paramDict): paramDict["gpio.feedback"] = None if not ("gpio.handler" in paramDict): paramDict["gpio.handler"] = None return None
[docs] def open(self, paramDict): """Opens the instance and sets it in a usable state. Allocate necessary hardware resources and configure user-adjustable parameters to meaningful defaults. This function must be called prior to any further usage of the instance. Involving it in the system ramp-up procedure could be a good choice. After usage of this instance is finished, the application should call :meth:`close`. :param dict(str, object) paramDict: Configuration parameters as obtained from :meth:`Params_init`, possibly. :return: An error code indicating either success or the reason of failure. :rtype: ErrorCode """ ret = ErrorCode.errOk # Retrieve defaults defaults = {} self.Params_init(defaults) # Scan parameters self.designator = paramDict.get("gpio.pinDesignator", None) if self.designator is None: ret = ErrorCode.errInvalidParameter else: if not isinstance( self.designator, int ): try: num = int(self.designator) self.designator = num except ValueError: pass self.numScheme = paramDict.get("gpio.pinNumbering", defaults["gpio.pinNumbering"]) self.direction = paramDict.get("gpio.direction", defaults["gpio.direction"]) self.inverted = paramDict.get("gpio.inverted", defaults["gpio.inverted"]) if self.inverted: # If inverted, simply swap the entries of the level-dictionary self._dictLevel[GPIO.LEVEL_LOW], self._dictLevel[GPIO.LEVEL_HIGH] = self._dictLevel[GPIO.LEVEL_HIGH], self._dictLevel[GPIO.LEVEL_LOW] if self.direction == GPIO.DIRECTION_IN: self.trigger = paramDict.get("gpio.trigger", defaults["gpio.trigger"]) self.bounce = paramDict.get("gpio.bounce", defaults["gpio.bounce"]) self.isOpen = True logging.debug("GPIO base> open <%s> returns %s.", self.designator, ret) return ret
[docs] def close(self): """Closes this instance and releases associated hardware resources. This is the counterpart of :meth:`open`. Upon return, further usage of this instance is prohibited and may lead to unexpected results. The instance can be re-activated by calling :meth:`open`, again. :return: An error code indicating either success or the reason of failure. :rtype: ErrorCode """ ret = ErrorCode.errOk if self.isOpen: if self.direction == GPIO.DIRECTION_IN: ret = self.registerInterruptHandler() self.isOpen = False else: ret = ErrorCode.errResourceConflict logging.debug("GPIO base> close <%s> returns %s.", self.designator, ret) return ret
[docs] def setRunLevel(self, level): """Select the power-saving operation mode. Switches the instance to one of the power-saving modes or recovers from these modes. Situation-aware deployment of these modes can greatly reduce the system's total power consumption. :param RunLevel level: The level to switch to. :return: An error code indicating either success or the reason of failure. :rtype: ErrorCode """ del level return ErrorCode.errNotImplemented
[docs] def enableInterrupt(self): """Enables the gpio interrupt for that pin. If the pin is configured for input, enables the interrupt for that pin. Depending on the trigger configured during :meth:`open`, an event will be fired the next time when the condition is satisfied. :return: An error code indicating either success or the reason of failure. :rtype: ErrorCode """ ret = ErrorCode.errOk if not self.isOpen: ret = ErrorCode.errResourceConflict elif self.isIntEnabled: ret = ErrorCode.errOk else: ret = ErrorCode.errNotImplemented logging.debug("GPIO base> enable int for <%s> returns %s.", self.designator, ret) return ret
[docs] def disableInterrupt(self): """Disables the gpio interrupt for that pin. Immediately disables the interrupt for that pin. It will not _fire an event anymore, unless :meth:`enableInterrupt` is called anew. :return: An error code indicating either success or the reason of failure. :rtype: ErrorCode """ ret = ErrorCode.errOk if not self.isOpen: ret = ErrorCode.errResourceConflict elif not self.isIntEnabled: ret = ErrorCode.errOk else: ret = ErrorCode.errNotImplemented logging.debug("GPIO base> disable int for <%s> returns %s.", self.designator, ret) return ret
[docs] def get(self): """Retrieve the pin level. Gives the pin level, independent of whether the pin direction is set to input or output. :return: GPIO.LEVEL_HIGH, if the pin is at high level. Otherwise, GPIO.LEVEL_LOW. :rtype: int """ level = GPIO.LEVEL_LOW return level
[docs] def set(self, newLevel): """Sets the pin to the given level. Outputs the given level at this pin. Does not work, if this pin is set to input direction. :param int newLevel: The new level to set this pin to. Must be one of GPIO.LEVEL_[HIGH | LOW]. :return: An error code indicating either success or the reason of failure. :rtype: ErrorCode """ ret = ErrorCode.errOk if not self.isOpen: ret = ErrorCode.errResourceConflict else: del newLevel ret = ErrorCode.errNotImplemented return ret