Control your Marty with a GamePad using Python
What You'll Need
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.
MKDIR martypy-joystick CHDIR martypy-joystick python -m venv .\VENV .\VENV\Scripts\activate.bat
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
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")]
(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
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)
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
event.ev_type, which has come out as either
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
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…