Control your Marty with a GamePad using Python

What You'll Need

The Logitech F710 Gamepad
Just as a side-note, if you're running Marty with a Raspberry Pi and ROS installed, then there's a ROS-based Joystick controller for Marty here you can just download and use

 

We're using a Logitech F710 wireless Gamepad here, but most USB gamepads should work. Drivers and supported features might be different between different controllers, and some bluetooth controllers might work too. Something that looks like a 'normal' console controller with plenty of buttons, joysticks and triggers is ideal!

And you'll also need a Marty running with our current hardware (i.e. a Rick) - Though if you're running the way-old I2C Servo Board + Raspberry Pi combination, it is possible to adapt this example to work there too.

Lastly, you need to have a working Python install (preferably Python 3) on your Computer/Raspberry Pi. If you've never used Python before then we'd recommend first running through a basic Python tutorial to get you well acquainted with Python.

First Step - Set up a new Python Project

The first thing we'll need to do is create a project folder to work in, something like martypy-joystick. Now we should install a few things in the folder; the commands below are for doing this in a Terminal (RPi/Linux/Mac) or Command Prompt/cmd (Windows).

We'll create an environment for our project to run in, called a virtual environment, or venv for short. This lets us choose exactly what version of Python and which libraries we want to use, separately from the ones that already come installed on your system.

 
Windows
MKDIR martypy-joystick
CHDIR martypy-joystick
python -m venv .\VENV
.\VENV\Scripts\activate.bat
Raspberry Pi, Mac and Linux
mkdir martypy-joystick
cd martypy-joystick
python3 -m venv ./VENV
source ./VENV/bin/activate

Next, we need to install some Libraries that contain some Python code we can use to help us work with the Game controller and Marty.

As with the MartyPy Introduction, we then need to install the latest version of martypy, Robotical's Python library for Marty, and inputs, an aptly named library for using Input Devices like Gamepads, Mice and Keyboards:

(VENV) $ pip install martypy
(VENV) $ pip install inputs

Note: If you use IDLE, Python's built-in IDE, you can start it in your venv like so, again from the command line:

(VENV) $ python -m idlelib.idle

Next Step - Getting the controller working

Our first task is going to be getting python to respond and do stuff when we press buttons and move controls on the Gamepad. We'll make a Python file, called joystick.py in the Project folder we made earlier. Open it in a code editor like IDLE or Atom

Import the inputs library we just installed, then, we'll ask it to tell us what Gamepads it can see plugged in to our computer:

import inputs

print(inputs.devices.gamepads)

Run the Python file (press F5 in IDLE), which will print something that looks like this to the terminal:

(VENV) $ python ./joystick.py
[inputs.GamePad("/dev/input/by-id/usb-Logitech_Wireless_Gamepad_F710_4C089F2D-event-joystick")]

or

(VENV) >python .\joystick.py
[inputs.GamePad("/dev/input/by_id/usb-Microsoft_Corporation_Controller_0-event-joystick")]

If instead you just see [], then check you've got the Gamepad plugged in and turned-on correctly.

We can also ask inputs about all the USB devices currently plugged in with inputs.devices.all_devices, but here we're only interested in Gamepads.

So, when we start our program it'll need to start using a Gamepad, so to check that there is one available, replace the print line with this:

pads = inputs.devices.gamepads

if len(pads) == 0:
    raise Exception("Couldn't find any Gamepads!")

This is just checking that there is at least one Gamepad available, and will exit the Python program if one can't be found. You can check that it works by unplugging the Gamepad from your computer, which will cause the Exception we just created. If the Gamepad is plugged in correctly, the program should run fine without error.

Getting events from the Gamepad

The inputs library can give us information about buttons being pressed and triggers and thumb-sticks being moved in things called events. To see what these look like, we can add a bit of code:

while True:
    events = inputs.get_gamepad()
    for event in events:
        print(event.ev_type, event.code, event.state)

The while True: makes sure this code will run forever, or at least until you manually stop the program. Within the while loop, we get the latest events from the Gamepad and print them to the terminal so we can see them.

If you then run the joystick.py file again and press a couple of buttons on your controller, you should see a load of events stream in as you mess with the controller, looking something like this:

Key BTN_SOUTH 1
Sync SYN_REPORT 0
Key BTN_SOUTH 0
Sync SYN_REPORT 0
Absolute ABS_RZ 8
Sync SYN_REPORT 0
Absolute ABS_RZ 26
Sync SYN_REPORT 0
Absolute ABS_RZ 93
Sync SYN_REPORT 0
Absolute ABS_RZ 12
Sync SYN_REPORT 0
Absolute ABS_RZ 0
Sync SYN_REPORT 0
Absolute ABS_HAT0Y -1
Sync SYN_REPORT 0
Absolute ABS_HAT0Y 0
Sync SYN_REPORT 0
...

This might look a bit scary, but look at the print statement from our code -- the first bit is event.ev_type, which has come out as either Absolute or Sync for this Gamepad, and the second bit is event.code which tells us which button or joystick was touched - ABS_HAT0Y just means that the Hat #0 (i.e. the first, and only Hat on this pad) moved to -1 in the Y direction and then back to 0 when the button was released. You can have a play with all the buttons at this stage to see what name they are given by inputs and the range of values you can expect from them.

Make Marty do something!

We'll start with something simple, making Marty dance when we press a button. We'll use the same loop we had before, but now check for the button press we want - on this controller, the A button shows up as BTN_SOUTH.

We'll need to establish a connection with Marty, so first we need to add some Python code to get that going:

# Add these lines at the top of the file:
import martypy

#...

# Add this line just above the while loop: 
marty = martypy.Marty(url='socket://192.168.0.42') # <<--- REPLACE with the correct URL

You can find out what the IP address (the 192.168.0.42 bit) for your Marty is using a scanning tool, like our Simple Scanner or Angry IP

The above bit of code tries to connect to a Marty using a URL, like socket://192.168.0.42 to connect to Marty. If martypy can't connect toy your Marty, it will raise an Exception.

Remember, with Python and most programming languages, it's good practice to always keep your imports at the top of your file, so add those there and then add all your code below.

Right, so now we can add the following in the the for loop to make Marty celebrate when the A button on the controller is hit!

if event.code == 'BTN_SOUTH' and event.state == 1:
    print('Celebrate!')
    marty.celebrate()

So, to recap, our whole program should now look like this:

import inputs
import martypy

pads = inputs.devices.gamepads

if len(pads) == 0:
    raise Exception("Couldn't find any Gamepads!")

marty = martypy.Marty(url='socket://192.168.0.42') # <<--- REPLACE with the correct URL

while True:
    events = inputs.get_gamepad()
    for event in events:
        print(event.ev_type, event.code, event.state)
        if event.code == 'BTN_SOUTH' and event.state == 1:
            print('Celebrate!')
            marty.celebrate()

From here, you can check for more Gamepad events and make Marty do practically whatever you want!

So, I'll leave you to it -- but if you want, here's a program we made earlier that has functions for most of the buttons and joysticks that you can download from a GitHub Gist. It also has a few added features, such as needing to give the URL to Marty as a command line argument.

You can run this version like so:

(VENV) $ python joystick.py socket://192.168.0.42

Let us know how you got on!

Post in the Forum or Send us a tweet at @RoboticalLtd!

Got stuck or need a hand? Start a chat with Support…