# Raspberry Pi GPIO access using file descriptors. '''The sysfs interface for user control of GPIO pins is deprecated, and may ultimately be removed. It is replaced by a file descriptor interface that uses ioctl calls. This module provides access to this newer GPIO facility. GPIO device files, such as /dev/gpiochip0, by default allow access only by root. Execution of Python programs by root is possible, but carries unnecesary risk. A better plan is to change permissions for the device file to allow all users (or only users in a particular group) to access the device. For example, when executed by root, this command: chmod a+rw /dev/gpiochip0 allows any user access to this GPIO device, without super-user privilege. To allow access to users in a particular group: chmod g+rw /dev/gpiochip0 chgrp dialout /dev/gpiochip0 will allow any user in the dialout group to access /dev/gpiochip0. See the man pages for newgrp (change the group of the current login session) and usermod (to add a user to a suplementary group so newgrp can change to that group.) This example used the "dialout" group because it already exists in typical linux systems. A new group, such as "gpio", could be defined if "dialout" seems inappropriate. udev rules can be written to set the default permissions for a device file such as /dev/gpiochip0 during boot. Example: ________________________________________________________________________________ # File: /etc/udev/rules.d/20_gpio.rules SUBSYSTEM=="gpio", DEVPATH=="/devices/platform/soc/3f200000.gpio/gpiochip0", ACTION=="add", GROUP="dialout", MODE="0660" ________________________________________________________________________________ or MODE="0666" would allow access to every user without limitation to one group. GPIO data access. To reduce overhead, this module provides the pieces that allow a user to call to read or write GPIO data using the file descripter obtained from get_gpiohandle. FD = gpiofd.get_gpiohandle(...) DATA = gpiofd.get_data_arg() To read: gpiofd.f_cntrl(FD, gpiofd.DATA_READ, DATA) To write: gpiofd.f_cntrl(FD, gpiofd.DATA_WRITE, DATA) Data values can be read/written from DATA (this has length gpiofd.GPIOHANDLES_MAX, but only the first num_lines [from the call to gpiofd.get_gpiohandle] are used.) Example: DATA[0] = 1 sets an output pin on (off if GPIOLINE_FLAG_ACTIVE_LOW was specified.) DATA[0] = b'\x00' if input is low (high if GPIOLINE_FLAG_ACTIVE_LOW was specified.) Complete example: a blink program. ________________________________________________________________________________ #!/usr/bin/python3 import gpiofd, time count = 30 # Number of output changes (blinks will be half this number.) pin = 23 # The GPIO output pin. # Could add optional argument to the following: label = b'blink' g = gpiofd.get_gpiohandle('/dev/gpiochip0', (pin,), gpiofd.GPIOHANDLE_REQUEST_OUTPUT) data = gpiofd.get_data_arg() func = gpiofd.f_ioctl x = 0 data[0] = 1 while x < count: x += 1 func(g, gpiofd.DATA_WRITE, data) if data[0] == b'\00': data[0] = 1 else: data[0] = 0 time.sleep(1) ________________________________________________________________________________ Another example: monitor input signal on a GPIO pin and display state changes: ________________________________________________________________________________ #!/bin/python3 # Monitor GPIO pin 24. import sys, select import gpiofd device = '/dev/gpiochip0' pin = 24 event_names = ['Zero! (error)', 'Rising edge', 'Falling edge'] event_fd = gpiofd.get_event(device, pin, 0, gpiofd.GPIOEVENT_REQUEST_RISING_EDGE | gpiofd.GPIOEVENT_REQUEST_FALLING_EDGE, label=b'gpio_monitor.py') ep = select.epoll() ep.register(event_fd, select.EPOLLIN) sys.stdout.write('Waiting for events on GPIO pin {}\n'.format(pin)) while True: ep.poll() (timestamp, event) = gpiofd.get_event_data(event_fd) sys.stdout.write('{:24.9f} {:d} {}\n'.format(timestamp, event, event_names[event])) ________________________________________________________________________________ Written by: Richard Ryniker April 10, 2017 ''' import sys, os, ctypes, ctypes.util from utils import dump_memory class gpiofd_error(IOError): def __init__(self, errno): self.errno = errno libc = ctypes.CDLL(ctypes.util.find_library('c')) f_ioctl = libc.ioctl f_read = libc.read ################################################# # ioctl construction functions. # Derived from asm-generic/ioctl.h sizebits = 14 dirbits = 2 nrbits = 8 typebits = 8 nrshift = 0 typeshift = nrshift + nrbits sizeshift = typeshift + typebits dirshift = sizeshift + sizebits ioc_read = 2 ioc_write = 1 def IOC(dir, t, nr, size): x = dir<