简体   繁体   中英

Use Python Asyncio to wait for GPIO interrupt

I am trying to convert a program from Tornado to Asyncio and the first step would be to use the actual asyncio eventloop as described here .

This application runs on an embedded Linux machine, I am using GPIOs through the sysfs/gpio subsystem and on some of these GPIOs I am waiting for interrupts. I was able to directly integrate this into the Tornado IOLoop by doing:

# Register with the queue
self.io_loop.add_handler(
    self.gpio._value_file, self._handle_interrupt, self.io_loop._EPOLLPRI | self.io_loop.ERROR
)

In the code piece, _value_file is the file handle to the file that the GPIO can be read from. The event EPOLLPRI is fired, whenever an interrupt on that GPIO is available. On Tornado, this works very well. It would call the _handle_interrupt function shortly after the interrupt comes.

My issue is, that I have not been able to translate this to the native asyncio event loop. In the documentation for watching file descriptors I only find functions to add readers and writers but nothing to watch for a generic event mask on a file descriptor. I can't dive into the code since that goes into C. However, looking at the Tornado layer to translate calls from the Tornado IOLoop into the asyncio IOLoop it seems like this is the case:

def add_handler(self, fd, handler, events):
    fd, fileobj = self.split_fd(fd)
    if fd in self.handlers:
        raise ValueError("fd %s added twice" % fd)
    self.handlers[fd] = (fileobj, stack_context.wrap(handler))
    if events & IOLoop.READ:
        self.asyncio_loop.add_reader(
            fd, self._handle_events, fd, IOLoop.READ)
        self.readers.add(fd)
    if events & IOLoop.WRITE:
        self.asyncio_loop.add_writer(
            fd, self._handle_events, fd, IOLoop.WRITE)
        self.writers.add(fd)

Only READ and WRITE flags are translated, all other flags are ignored.

Could someone confirm that it is currently not possible to use asyncio to watch for any events on file descriptors except READ and WRITE events? Or am I doing something wrong and there actually is a way?

I have found the solution for this myself now. My main source of information was this thread in the Python-tulip group and this piece of code which I had to adopt slightly.

The main insight is that an epoll that can be used to watch for POLLPRI events is itself a file descriptor. Whenever an event occurs on an FD that the epoll watches, the epoll file descriptor will generate a POLLIN event which can be watched using asyncio with add_reader . So instead of registering directly, we manually create an epoll structure and register it with the ioloop like so:

self.epoll = select.epoll()
self.io_loop.add_reader(self.epoll.fileno(), self._handle_interrupt)

The actual interrupt event is then registered to the epoll structure

self.epoll.register(self.gpio._value_file, select.POLLPRI)

At this point, interrupt events will be received in the _handle_interrupt function. Make sure to actually poll the epoll structure in the event handler, or it will continuously generate read events

def _handle_interrupt(self):
    self.epoll.poll(0)
    ...

It is important to use the low-level select functionality instead of the high-level selectors since they do a similar event flag filtering as in asyncio . The following code snipped is from selectors.EpollSelector :

def register(self, fileobj, events, data=None):
    key = super().register(fileobj, events, data)
    epoll_events = 0
    if events & EVENT_READ:
        epoll_events |= select.EPOLLIN
    if events & EVENT_WRITE:
        epoll_events |= select.EPOLLOUT
    try:
        self._epoll.register(key.fd, epoll_events)
    except BaseException:
        super().unregister(fileobj)
        raise
    return key

It can be seen, that all events except READ and WRITE are filtered. Hence, you can't use the high level interface to watch for POLLPRI events. Therefore, use the low-level interface.

I hope this helps people stumbling over this issue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM