I've a RaspberryPi 3 which will be used headless, and am using https://github.com/adafruit/Adafruit_DotStar_Pi for controlling LEDs, which constrains me to using Python 2. The LEDs are pretty much the only output of my device, and a single key (specifically a 'w' from a Bluefruit EZ-Key Bluetooth HID Keyboard Controller) is the only input - basically activating or deactivating the lights.
I want to have a callback function be called when a keypress event is detected, to change some state, but all available methods I've found so far (ie reading from /dev/input/event0) seem to be blocking. The python-evdev module looks useful, but all the async options appear to be Python 3.
In the GPIO python library (which works fine in Python 2) there are callback functions for when a pin changes state, so I guess I'm hoping that there is a similar API for detecting keypress events.
The code will be running in a systemd process, and it seems reading from /dev/input/event0 is more likely to get me useful key events than stdin (which doesn't seem to be hooked up to detect keypresses in systemd processes).
(This question was off-topic for https://raspberrypi.stackexchange.com/ btw )
1 Answer 1
An input device isn't substantially different from any other file. You can use select() and poll() (et al) on it to test if input is available. That is, given:
import select
dev = open('/dev/input/event4')
p = select.poll()
p.register(dev, select.POLLIN)
I can check and see if input is available by running:
events = p.poll(0)
This will either return an empty list, or it will return a list of
(file descriptor, event) tuples; in either case it will not block.
Depending on your code, you may actually want this to block a
little; the 0 in the above call is actually a timeout (in
milliseconds).
Having received a POLLIN event on this file descriptor, I know that
I can read some data from it without blocking. A complete example
might look like:
import os
import select
p = select.poll()
dev = open('/dev/input/event4')
p.register(dev, select.POLLIN)
while True:
events = p.poll(500)
if events:
print 'events:', events
data = os.read(dev.fileno(), 1024)
print 'read: %s' % (repr(data))
else:
print 'no events...'
This calls poll in a loop, sleeping up to 500 milliseconds each time
unless there is input, in which case it reacts immediately.
I like select.poll because the api is easier the the older select
call, but I note that that the
python-evdev module includes
an example using
select.
Taking a closer look at that example, you can actually just pass an
InputDevice to poll, so we can rewrite the above like:
import os
import select
from evdev import InputDevice
p = select.poll()
dev = InputDevice('/dev/input/event4')
p.register(dev, select.POLLIN)
while True:
events = p.poll(500)
if events:
print 'events:', events
data = list(dev.read())
print 'read: %s' % data
And see on output something like:
events: [(3, 1)]
read: [InputEvent(1461702764L, 854531L, 4, 4, 458789L), InputEvent(1461702764L, 854531L, 1, 9, 1L), InputEvent(1461702764L, 854531L, 0, 0, 0L)]
See the documentation on the select module for more
details.
Hopefully that is enough to get you pointed in the right direction.
Comments
Explore related questions
See similar questions with these tags.