A friend of mine recently asked if I could help out with a Raspberry Pi project he is working on. Pi was one of those things I had read about and always wanted to take a look at — and here was my excuse!
Moreover, Python, the language most Raspberry Pi tools are written in, is something else I had been meaning to take a look at.
Of course playing around with Raspberry Pi very quickly led to this:
On the left is a GoPiGo (affiliate link), while on the right is a Logitech F710 wireless gamepad. These 2 devices do not work together “out of the box.” The GoPiGo has tutorials and sample scripts for working with a keyboard or mouse, but not for a gamepad. Here’s a chance to learn some basics and have a little fun!
Much of what makes working with the Raspberry Pi easy is that it runs Linux. (It runs other operating systems, but we’ll stick with Linux here.) As a matter of fact, this first tutorial will work on Linux running on pretty much any hardware.
For this tutorial you need a Raspberry Pi (or a computer running Linux, but you will need a GoPiGo for the next article in this series) and a gamepad. I am using a Logitech Gamepad F710 (affiliate link,) which lists for $40 right now. I picked mine up at a local Target for $25. Other USB gamepads should work similarly to the F710, including wired models. (But wireless will work a lot better once you get your robot moving.)
You need to be comfortable with a Linux command line, as well as having as basic knowledge of Python. Nothing fancy: just the ability to enter a program in your favorite text editor and run it.
Reading from Device Files
In order to use the gamepad we need to get information from it into our program. Linux, like most UNIX-like operating systems, makes this easy via device files. When the gamepad (or it’s receiver in the case of a wireless device) is connected a file appears that can be read from.
Without the joystick or USB receiver connected, open a shell on your system and list the contents of /dev/input (click on the image for a larger version if you cannot read it):
On my system I see only a default file named mice, as I do not have an actual mouse or keyboard connected.
Now connect the gamepad or its USB receiver and wait a few seconds (especially if you are using a Pi B+ or Original) for your system to notice the device:
On my system we see 2 new files and 2 new folders. The event0 and js0 devices correspond the my gamepad. The by-id and by-path folders provide an alternative method to access the devices, as well as additional information about the device.
Since these devices are files, we can treat them as such. Let’s see what’s “in” them. Opening them in an editor will not work well, but we can use good old cat to have some fun.
Cat the event0 (or eventN if you had one before you connected the new gamepad) and then press a button on the gamepad:
The image above is for a single button press. That’s a lot of characters! And most of them seem garbled. But we do see that the gamepad device file is just like any other file that can be read.
Reading the Gamepad
We’re going to use the evdev package to interpret the input from the gamepad. I’ll explain what this package does as we go. The package has not been installed on any of the Pi distributions I have worked with, so let’s start by installing it with pip:
% sudo pip install evdev
Pip will do it’s thing and after a bit (it can take a while on a slower Pi) the package is installed.
Now let’s open the gamepad and print some information about it. For these exercises I am using idle but a command line session with Python or running these lines from a script will work too. (Again, click on the image for a larger version.)
The first line imports a few things from the evdev package: InputDevice, categorize, and ecodes.
The second line creates an InputDevice by passing the path to the gamepad device file.
And the we finally print this new device object to standard output and see some interesting information about it.
While the mechanics of reading from the gamepad device are as simple as any other file, this quick 3 line program demonstrates how evdev makes it even simpler. The package has a termendous of code built that for managing devices like a joystick or gamepad.
Let’s use evdev to read some buttons. Start with the same import statement and create an InputDevice like we did above, but now replace printing the device with the 2 lines below. Then press a button on your gamepad.
Instead of the garbled characters we saw earlier, we see something that is at least readable.
Before we discuss the events, let’s talk about this line:
for event in gamepad.read_loop()
This illustrates another feature of the evdev package. The package provides a simple for loop that reads the device and creates events for us. Without this feature our code would look something like this:
from evdev import InputDevice
from select import select
gamepad = InputDevice('/dev/input/event0')
r,w,x = select([gamepad], , )
for event in gamepad.read():
Evdev eliminates the outer While loop, as well as the call to select. In terms of lines of code, the difference is negligible, however the read_loop() version is a little easier to read and we’ll stick with it.
(In an application that needs to monitor more than one input device, such as a mouse and a keyboard, read_loop() won’t work, as it only lets us read one device. Select is excellent for reading and writing from multiple devices.)
Let’s identify what’s actually being pressed on the gamepad:
Now for every event we examine the type value and see if it is a button. finally we are using the ecodes we imported.
If it is a button, we use categorize to return a more specific type of event to us and print it. If it is not, we simply print a message.
Here we see where evdev saves us a lot of work. In addition to reading the events a gamepad sends to our app, it also categorizes them for us too.
key event at 1433298890.180640, 304 (['BTN_A', 'BTN_GAMEPAD']), down
Note two things about this event info: we see the button I pressed (‘A’) and we see “down.” After a mysterious “not a button” event we then see the same button going up.
We know a lot about how to read the gamepad at this point. Let’s write a script that we could use to control the robot using the A-B-X-Y buttons on the gamepad. Enter the following into a text editor and save it.
from evdev import InputDevice, categorize, ecodes, KeyEvent
gamepad = InputDevice('/dev/input/event0')
for event in gamepad.read_loop():
if event.type == ecodes.EV_KEY:
keyevent = categorize(event)
if keyevent.keystate == KeyEvent.key_down:
if keyevent.keycode == 'BTN_A':
elif keyevent.keycode == 'BTN_Y':
elif keyevent.keycode == 'BTN_B':
elif keyevent.keycode == 'BTN_X':
I saved this as gamepad.py.
chmod +x gamepad.py
Run the script and press a few buttons.
As we receive each event we first check to see if it is a button. If it is, we check to see that the button is down (it’s been pressed) and then finally we check to see if it is one of the four buttons we currently care about.
That’s it for this week. Next week we’ll add code to make the GoPiGo respond to the buttons, and we’ll also a some more controls.
See you then! And feel free to leave a comment below.