2 Much code from https://github.com/vsergeev/python-periphery/blob/master/periphery/gpio.py
 
   3 Copyright (c) 2015-2019 vsergeev / Ivan (Vanya) A. Sergeev
 
  11 # pylint: disable=unnecessary-pass
 
  12 class GPIOError(IOError):
 
  13     """Base class for GPIO errors."""
 
  18 # pylint: enable=unnecessary-pass
 
  22     """SysFS GPIO Pin Class"""
 
  24     # Number of retries to check for GPIO export or direction write on open
 
  25     GPIO_OPEN_RETRIES = 10
 
  26     # Delay between check for GPIO export or direction write on open (100ms)
 
  42     _sysfs_path = "/sys/class/gpio/"
 
  43     _channel_path = "gpiochip{}"
 
  46     _export_path = "export"
 
  47     _unexport_path = "unexport"
 
  50     def __init__(self, pin_id):
 
  51         """Instantiate a Pin object and open the sysfs GPIO with the specified
 
  55             pin_id (int): GPIO pin number.
 
  58             SysfsGPIO: GPIO object.
 
  61             GPIOError: if an I/O or OS error occurs.
 
  62             TypeError: if `line` or `direction`  types are invalid.
 
  63             ValueError: if `direction` value is invalid.
 
  64             TimeoutError: if waiting for GPIO export times out.
 
  68         if not isinstance(pin_id, int):
 
  69             raise TypeError("Invalid Pin ID, should be integer.")
 
  82     def __exit__(self, t, value, traceback):
 
  85     def init(self, mode=IN, pull=None):
 
  86         """Initialize the Pin"""
 
  91             elif mode == self.OUT:
 
  95                 raise RuntimeError("Invalid mode for pin: %s" % self.id)
 
  98                 if pull == self.PULL_UP:
 
  99                     raise NotImplementedError(
 
 100                         "Internal pullups not supported in periphery, "
 
 101                         "use physical resistor instead!"
 
 103                 if pull == self.PULL_DOWN:
 
 104                     raise NotImplementedError(
 
 105                         "Internal pulldowns not supported in periphery, "
 
 106                         "use physical resistor instead!"
 
 108                 raise RuntimeError("Invalid pull for pin: %s" % self.id)
 
 110     def value(self, val=None):
 
 111         """Set or return the Pin Value"""
 
 121             raise RuntimeError("Invalid value for pin")
 
 122         return self.HIGH if self._read() else self.LOW
 
 124     # pylint: disable=too-many-branches
 
 125     def _open(self, direction):
 
 126         if not isinstance(direction, str):
 
 127             raise TypeError("Invalid direction type, should be string.")
 
 128         if direction.lower() not in ["in", "out", "high", "low"]:
 
 129             raise ValueError('Invalid direction, can be: "in", "out", "high", "low".')
 
 130         gpio_path = "/sys/class/gpio/gpio{:d}".format(self.id)
 
 132         if not os.path.isdir(gpio_path):
 
 135                 with open("/sys/class/gpio/export", "w") as f_export:
 
 136                     f_export.write("{:d}\n".format(self.id))
 
 138                 raise GPIOError(e.errno, "Exporting GPIO: " + e.strerror) from IOError
 
 140             # Loop until GPIO is exported
 
 142             for i in range(self.GPIO_OPEN_RETRIES):
 
 143                 if os.path.isdir(gpio_path):
 
 147                 time.sleep(self.GPIO_OPEN_DELAY)
 
 151                     'Exporting GPIO: waiting for "{:s}" timed out'.format(gpio_path)
 
 154             # Write direction, looping in case of EACCES errors due to delayed udev
 
 155             # permission rule application after export
 
 156             for i in range(self.GPIO_OPEN_RETRIES):
 
 158                     with open(os.path.join(gpio_path, "direction"), "w") as f_direction:
 
 159                         f_direction.write(direction.lower() + "\n")
 
 162                     if e.errno != errno.EACCES or (
 
 163                         e.errno == errno.EACCES and i == self.GPIO_OPEN_RETRIES - 1
 
 166                             e.errno, "Setting GPIO direction: " + e.strerror
 
 169                 time.sleep(self.GPIO_OPEN_DELAY)
 
 173                 with open(os.path.join(gpio_path, "direction"), "w") as f_direction:
 
 174                     f_direction.write(direction.lower() + "\n")
 
 177                     e.errno, "Setting GPIO direction: " + e.strerror
 
 182             self._fd = os.open(os.path.join(gpio_path, "value"), os.O_RDWR)
 
 184             raise GPIOError(e.errno, "Opening GPIO: " + e.strerror) from OSError
 
 186         self._path = gpio_path
 
 188     # pylint: enable=too-many-branches
 
 197             raise GPIOError(e.errno, "Closing GPIO: " + e.strerror) from OSError
 
 203             unexport_fd = os.open("/sys/class/gpio/unexport", os.O_WRONLY)
 
 204             os.write(unexport_fd, "{:d}\n".format(self.id).encode())
 
 205             os.close(unexport_fd)
 
 207             raise GPIOError(e.errno, "Unexporting GPIO: " + e.strerror) from OSError
 
 212             buf = os.read(self._fd, 2)
 
 214             raise GPIOError(e.errno, "Reading GPIO: " + e.strerror) from OSError
 
 218             os.lseek(self._fd, 0, os.SEEK_SET)
 
 220             raise GPIOError(e.errno, "Rewinding GPIO: " + e.strerror) from OSError
 
 222         if buf[0] == b"0"[0]:
 
 224         if buf[0] == b"1"[0]:
 
 227         raise GPIOError(None, "Unknown GPIO value: {}".format(buf))
 
 229     def _write(self, value):
 
 230         if not isinstance(value, bool):
 
 231             raise TypeError("Invalid value type, should be bool.")
 
 236                 os.write(self._fd, b"1\n")
 
 238                 os.write(self._fd, b"0\n")
 
 240             raise GPIOError(e.errno, "Writing GPIO: " + e.strerror) from OSError
 
 244             os.lseek(self._fd, 0, os.SEEK_SET)
 
 246             raise GPIOError(e.errno, "Rewinding GPIO: " + e.strerror) from OSError
 
 250         """Return the Chip Name"""
 
 251         gpio_path = os.path.join(self._path, "device")
 
 253         gpiochip_path = os.readlink(gpio_path)
 
 255         if "/" not in gpiochip_path:
 
 258                 'Reading gpiochip name: invalid device symlink "{:s}"'.format(
 
 263         return gpiochip_path.split("/")[-1]
 
 266     def chip_label(self):
 
 267         """Return the Chip Label"""
 
 268         gpio_path = "/sys/class/gpio/{:s}/label".format(self.chip_name)
 
 271             with open(gpio_path, "r") as f_label:
 
 272                 label = f_label.read()
 
 273         except (GPIOError, IOError) as e:
 
 274             if isinstance(e, IOError):
 
 276                     e.errno, "Reading gpiochip label: " + e.strerror
 
 280                 None, "Reading gpiochip label: " + e.strerror
 
 287     def _get_direction(self):
 
 290             with open(os.path.join(self._path, "direction"), "r") as f_direction:
 
 291                 direction = f_direction.read()
 
 294                 e.errno, "Getting GPIO direction: " + e.strerror
 
 297         return direction.strip()
 
 299     def _set_direction(self, direction):
 
 300         if not isinstance(direction, str):
 
 301             raise TypeError("Invalid direction type, should be string.")
 
 302         if direction.lower() not in ["in", "out", "high", "low"]:
 
 303             raise ValueError('Invalid direction, can be: "in", "out", "high", "low".')
 
 307             with open(os.path.join(self._path, "direction"), "w") as f_direction:
 
 308                 f_direction.write(direction.lower() + "\n")
 
 311                 e.errno, "Setting GPIO direction: " + e.strerror
 
 314     direction = property(_get_direction, _set_direction)
 
 318             str_direction = self.direction
 
 320             str_direction = "<error>"
 
 323             str_chip_name = self.chip_name
 
 325             str_chip_name = "<error>"
 
 328             str_chip_label = self.chip_label
 
 330             str_chip_label = "<error>"
 
 332         output = "Pin {:d} (dev={:s}, fd={:d}, dir={:s}, chip_name='{:s}', chip_label='{:s}')"
 
 333         return output.format(
 
 334             self.id, self._path, self._fd, str_direction, str_chip_name, str_chip_label