The Gamepad library provides a simple way of getting inputs from joysticks, gamepads, and other game controllers.
Both buttons and axes / joysticks can be referenced by names as well as their raw index numbers. These can be easily customised and new controllers can easily be added as well.
It is designed and tested for the Raspberry Pi, but it should work with an Linux install and any device which shows up as a /dev/input/js?
device.
Gamepad only uses imports which are part of the standard Python install and works with both Python 2 and 3.
Multiple controllers are also supported, just create more than one Gamepad
instance with different joystick IDs.
See our other projects on GitHub here :)
cd ~
git clone https://github.com/piborg/Gamepad
That's it. The library does not need installing, it just needs to be downloaded.
The library provides three basic modes for getting updates from your controller:
See the examples further down for an explanation of how each mode is used.
The library itself is contained in just two scripts:
Gamepad.py
The main library, this script contains all of the code which reads input from the controller. It contains the Gamepad
class which has all of the logic used to decode the joystick events and store the current state.
It works with both the raw axis / joystick and button numbers and easy to read names. It also contains the threading code needed to handle the background updating used in the asynchronous and event modes.
This script can be run directly using ./Gamepad.py
to check your controller mappings are correct or work out the mapping numbers for your own controller.
Controllers.py
This script contains the layouts for the different controllers. Each controller is represented by its own class inheriting from the main Gamepad
class.
If the mapping is not right for you the layout for both the axis / joystick names and the button names by editing these classes. Adding your own controller is also simple, just make your own class based on the example at the bottom :)
Any button or axis without a name can still be used by the raw number if needed. This also means the Gamepad
class can be used directly if you are only using the raw numbers.
This script is not run directly, instead it is read by Gamepad.py
so that all of the devices are available when Gamepad is imported.
PollingExample.py
The polling mode is probably the simplest and uses the Gamepad library to decode controller events one at a time.
It works by repeatedly calling the getNextEvent()
function to get the next update from the controller in the order they occurred. Each call returns three things:
'BUTTON'
for button presses or 'AXIS'
for axis / joystick movements.True
or False
, for axis / joystick movement it is a position between -1.0 and +1.0.For example if the circle button on your controller was just pressed you would get 'BUTTON', 'CIRCLE', True
as the result.
Polling mode cannot be used at the same time as the asynchronous or event modes as they read the controller events for you.
AsyncExample.py
Asynchronous mode works by reading the controller events in a background thread and updating the objects state to match the controller.
It is started by calling the startBackgroundUpdates()
function and should be stopped at the end of your script by calling the disconnect()
function.
The current controller state can be queried using the following functions:
isConnected()
- check if the controller is still connected.isPressed(X)
- check if a button is currently pressed.beenPressed(X)
- see if a button was pressed since the last check. Only returns True
once per press.beenReleased(X)
- see if a button was released since the last check. Only returns True
once per release.axis(X)
- read the latest axis / joystick position. This is a float number between -1.0 and +1.0.In all cases X
can either be the string based name or the raw index number (e.g. 'CIRCLE'
or 1
).
Asynchronous mode cannot be used at the same time as the polling mode as it reads the controller events for you, but it can be used with event mode.
EventExample.py
Event mode works by reading the controller events in a background thread and calling functions in your script when changes occur.
It is started by calling the startBackgroundUpdates()
function and should be stopped at the end of your script by calling the disconnect()
function. The isConnected()
function will return False
if the controller is disconnected while running.
Once started you can register your functions to controller events using these calls:
addButtonPressedHandler(X, F)
- called when a button is pressed, no values passed.addButtonReleasedHandler(X, F)
- called when a button is released, no values passed.addButtonChangedHandler(X, F)
- called when a button changes state, boolean passed with True
for pressed or False
for released.addAxisMovedHandler(X, F)
- called when an axis / joystick is moved, a float number between -1.0 and +1.0 is passed.In all cases X
can either be the string based name or the raw index number (e.g. 'CIRCLE'
or 1
). F
is the function which gets called when the event occurs.
The same function can be registered with multiple events. You can also register multiple functions with the same event.
You can also remove an already registered event using these calls if needed:
removeButtonPressedHandler(X, F)
- removes a callback added by addButtonPressedHandler
.removeButtonReleasedHandler(X, F)
- removes a callback added by addButtonReleasedHandler
.removeButtonChangedHandler(X, F)
- removes a callback added by addButtonChangedHandler
.removeAxisMovedHandler(X, F)
- removes a callback added by addAxisMovedHandler
.removeAllEventHandlers()
- removes all added callbacks for this controller.Event mode cannot be used at the same time as the polling mode as it reads the controller events for you, but it can be used with asynchronous mode.
AsyncAndEventExample.py
This is not really a mode, but an example of how the asynchronous and event modes can be used at the same time. This is generally my preferred option as event mode is often easier to understand for button presses and asynchronous mode works well with axis / joystick movements.
The example script here is really a hybrid between the AsyncExample.py
and EventExample.py
examples. Button callbacks are registered in the event style, then the loop only checks one button and the joystick positions.
In this style you are free to mix and match what you see as events and what you read the state of directly.
ListNames.py
This example is just a helpful utility to print out all of the axis and button names for a controller type. You can change the controller type by looking for this line:
gamepad = Gamepad.PS4()
and changing PS4
to any available device name.
The list of device names is shown when you run ./Gamepad.py
directly.
CustomGamepadExample.py
This example shows how you can create a controller mapping in your own script without changing Controllers.py
. This can be useful if you need to use different names in just one script, or if you want to keep all of your changes in your own code.
In this case you make your own class inheriting from Gamepad.Gamepad
in the same way as they are written in Controllers.py
. You do not have to set the fullName
value.
rockyJoy.py
Here we have an actual use of the Gamepad library, controlling a RockyBorg robot :)
It works exactly like the old Pygame version of the rbJoystick.py
example script, but with the addition of a button to end the script.
The controller and button layout is all specified towards the top of the script and the standard RockyBorg library is used to control the motors and servo.
monsterJoy.py
Here we have another real-world use of the Gamepad library, controlling a MonsterBorg robot :)
This provides the same control scheme we usually use for our robots, plus buttons to toggle the LEDs and end the script.
The controller and button layout is all specified towards the top of the script and the standard ThunderBorg library is used to control the motors.
If you are using Gamepad
in your own script it will need access to both the Gamepad.py
and Controllers.py
scripts. This can be done in a few ways:
import sys
and sys.path.insert(0, "/home/pi/Gamepad")
, where /home/pi/Gamepad
is the Gamepad directory.For troubleshooting and further help please post questions on our forum.
If you find a bug with the RockyBorg code, please feel free to report it as an issue. When reporting issues with this library please fill in the issue report with as much information as you can to help us help you.
Please supply as much information as you can so we can replicate the bug locally. :)