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)
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")
176 raise GPIOError(e.errno, "Setting GPIO direction: " + e.strerror)
180 self._fd = os.open(os.path.join(gpio_path, "value"), os.O_RDWR)
182 raise GPIOError(e.errno, "Opening GPIO: " + e.strerror)
184 self._path = gpio_path
186 # pylint: enable=too-many-branches
195 raise GPIOError(e.errno, "Closing GPIO: " + e.strerror)
201 unexport_fd = os.open("/sys/class/gpio/unexport", os.O_WRONLY)
202 os.write(unexport_fd, "{:d}\n".format(self.id).encode())
203 os.close(unexport_fd)
205 raise GPIOError(e.errno, "Unexporting GPIO: " + e.strerror)
210 buf = os.read(self._fd, 2)
212 raise GPIOError(e.errno, "Reading GPIO: " + e.strerror)
216 os.lseek(self._fd, 0, os.SEEK_SET)
218 raise GPIOError(e.errno, "Rewinding GPIO: " + e.strerror)
220 if buf[0] == b"0"[0]:
222 if buf[0] == b"1"[0]:
225 raise GPIOError(None, "Unknown GPIO value: {}".format(buf))
227 def _write(self, value):
228 if not isinstance(value, bool):
229 raise TypeError("Invalid value type, should be bool.")
234 os.write(self._fd, b"1\n")
236 os.write(self._fd, b"0\n")
238 raise GPIOError(e.errno, "Writing GPIO: " + e.strerror)
242 os.lseek(self._fd, 0, os.SEEK_SET)
244 raise GPIOError(e.errno, "Rewinding GPIO: " + e.strerror)
248 """Return the Chip Name"""
249 gpio_path = os.path.join(self._path, "device")
251 gpiochip_path = os.readlink(gpio_path)
253 if "/" not in gpiochip_path:
256 'Reading gpiochip name: invalid device symlink "{:s}"'.format(
261 return gpiochip_path.split("/")[-1]
264 def chip_label(self):
265 """Return the Chip Label"""
266 gpio_path = "/sys/class/gpio/{:s}/label".format(self.chip_name)
269 with open(gpio_path, "r") as f_label:
270 label = f_label.read()
271 except (GPIOError, IOError) as e:
272 if isinstance(e, IOError):
273 raise GPIOError(e.errno, "Reading gpiochip label: " + e.strerror)
275 raise GPIOError(None, "Reading gpiochip label: " + e.strerror)
281 def _get_direction(self):
284 with open(os.path.join(self._path, "direction"), "r") as f_direction:
285 direction = f_direction.read()
287 raise GPIOError(e.errno, "Getting GPIO direction: " + e.strerror)
289 return direction.strip()
291 def _set_direction(self, direction):
292 if not isinstance(direction, str):
293 raise TypeError("Invalid direction type, should be string.")
294 if direction.lower() not in ["in", "out", "high", "low"]:
295 raise ValueError('Invalid direction, can be: "in", "out", "high", "low".')
299 with open(os.path.join(self._path, "direction"), "w") as f_direction:
300 f_direction.write(direction.lower() + "\n")
302 raise GPIOError(e.errno, "Setting GPIO direction: " + e.strerror)
304 direction = property(_get_direction, _set_direction)
308 str_direction = self.direction
310 str_direction = "<error>"
313 str_chip_name = self.chip_name
315 str_chip_name = "<error>"
318 str_chip_label = self.chip_label
320 str_chip_label = "<error>"
322 output = "Pin {:d} (dev={:s}, fd={:d}, dir={:s}, chip_name='{:s}', chip_label='{:s}')"
323 return output.format(
324 self.id, self._path, self._fd, str_direction, str_chip_name, str_chip_label