GithubHelp home page GithubHelp logo

gamepad's Introduction

PiBorg Gamepad Library

Polling flowchart

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 :)

Installing the Library

cd ~
git clone https://github.com/piborg/Gamepad

That's it. The library does not need installing, it just needs to be downloaded.

The Gamepad Library

The library provides three basic modes for getting updates from your controller:

  1. Polling - we ask the library for the next controller update one at a time.
  2. Asynchronous - the controller state is updated in the background.
  3. Event - callbacks are made when the controller state changes.

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.

Examples

Polling mode - PollingExample.py

The polling mode is probably the simplest and uses the Gamepad library to decode controller events one at a time.

Polling flowchart

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:

  1. The event type. This is either 'BUTTON' for button presses or 'AXIS' for axis / joystick movements.
  2. The control which changed. This is a string with the name if available, or the index number if no name was available.
  3. The new value for the control. For buttons this is 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.

Asynchronous mode - AsyncExample.py

Asynchronous mode works by reading the controller events in a background thread and updating the objects state to match the controller.

Asynchronous flowchart

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.

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.

Event flowchart

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.

Asynchronous and event 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.

Asynchronous and event flowchart

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.

Getting the available names - 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.

Custom controller in your own script - 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.

RockyBorg example - 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.

MonsterBorg example - 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.

Using Gamepad in your own project

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:

  1. Write your script in the Gamepad directory.
  2. Copy the two scripts into your project's directory.
  3. Add the Gamepad directory to your PYTHONPATH environment variable.
  4. Add these lines to your script before importing the library: import sys and sys.path.insert(0, "/home/pi/Gamepad"), where /home/pi/Gamepad is the Gamepad directory.

Troubleshooting

For troubleshooting and further help please post questions on our forum.

Reporting Issues

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. :)

gamepad's People

Contributors

arronchurchill avatar poohl avatar spoonieau avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

gamepad's Issues

Button index not found

I've been getting this error when I run any script, including EventExample.py

Gamepad connected
Traceback (most recent call last):
File "/mnt/raid1/Code/Useful/Gamepad/Gamepad.py", line 421, in addButtonPressedHandler
if callback not in self.pressedEventMap[buttonIndex]:
KeyError: 0

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/mnt/raid1/Code/Useful/Gamepad/EventExample.py", line 64, in
gamepad.addButtonPressedHandler(buttonHappy, happyButtonPressed)
File "/mnt/raid1/Code/Useful/Gamepad/Gamepad.py", line 424, in addButtonPressedHandler
raise ValueError('Button %i was not found' % buttonIndex)
ValueError: Button 0 was not found

The only thing changed in EventExample.py is this:

Wait for a connection

if not Gamepad.available(1):
print('Please connect your gamepad...')
while not Gamepad.available(1):
time.sleep(1.0)
gamepad = gamepadType(1)
print('Gamepad connected')

I put "1" in the parantheses instead of default value 0.

It says "GamePad Connected" even if the GamePad is not connected.

D-Pad Button Vs Axis Support

I am using the XBox adaptive controller to make a robot for someone who has limited motion. This controller has two buttons and a D-Pad. The d-pad shows up as an axis, but the value always remains 0 regardless of if it is pressed or not. Is there a way to configure the D-pad to work like a button press rather than an analog axis?

The script I started with is the unmodified AsyncExample.py.

Thank you!

.disconnect() / stopBackgroundUpdates() blocks forever if no gamepad events occur.

Hi,

I think the background thread never terminates because stopBackgroundUpdates() just sets the running flag that is checked in a while loop. But _getNexEventRaw() blocks until there is an event. So, after stopBackgroundUpdates() is called, there needs to be one last event (i.e. a button press) until the flag is checked and the thread actually terminates.

Does anybody have a good idea on how to gracefully stop the update thread? (Ctrl-C works to just terminate the process, but that is not very nice.)

Xbox One wireless gamepad is not detected

this library doesn't seem to detect wireless gamepads connected using BT.
When I run:
python3 PollingExample.py
script hangs on message:
Please connect your gamepad...

My hardware/software setup:
Xbox One wireless gamepad
Mac OS 12.5 Monterey
Python 3.8.9

The controller is working fine when tested on https://gamepad-tester.com/

Is there a chance to add support for wireless controllers?

Key Error

Hello!
I tried using the Gamepad.py but kept running into a Key error, any idea how to solve it?

We have a Logitech F710 Gamepad, that we'd like to use for event based callbacks, so thought of using this library.

I tried running the Gamepad.pyon python3 with our controller to find the mappings to make my own class, so started with the PS4 class, ran into a key error, thought it was because I used the wrong class for my gamepad.

But after a few trials, found out that the problem wasn't with the Gamepad class. The Gamepad.py generates an output for only 2 buttons (R1 and B) and 2 axes, for every other button or axis event, it throws a Key error. I haven't changed anything in the Gamepad.py. Would be great if I could receive some insight into how to solve this issue.

Screenshots

Available device names:
    Gamepad - Generic (numbers only)
    Logitech - Logitech Rumblepad
    MMP1251 - ModMyPi Raspberry Pi Wireless USB Gamepad
    PS3 - PlayStation 3 controller
    PS4 - PlayStation 4 controller
    Xbox360 - Xbox 360 controller
    example - Enter the human readable name of the device here

What device name are you using (leave blank if not in the list)
? Logitech

Gamepad connected
Logitech Rumblepad
BUTTON,	  R1,	True
BUTTON,	  R1,	False
BUTTON,	  R1,	True
BUTTON,	  R1,	False
BUTTON,	  R1,	True
BUTTON,	  R1,	False
BUTTON,	  R1,	True
BUTTON,	  B,	False
BUTTON,	  B,	True
BUTTON,	  B,	False
BUTTON,	  B,	True
BUTTON,	  B,	False
BUTTON,	  B,	True
BUTTON,	  B,	False
BUTTON,	  B,	True
BUTTON,	  B,	False
BUTTON,	  B,	True
BUTTON,	  B,	False
Traceback (most recent call last):
  File "./Gamepad.py", line 613, in <module>
    eventType, index, value = gamepad.getNextEvent()
  File "./Gamepad.py", line 244, in getNextEvent
    return self.getNextEvent()
  File "./Gamepad.py", line 211, in getNextEvent
    for callback in self.movedEventMap[index]:
KeyError: 3

Hardware setup

  • Ubuntu 18.04 Desktop
  • Logitech Cordless RumblePad 2

Thanks in advance

Not working on RPI3 with Ubuntu 16.04

Describe the bug
PS4 gamepad is detected and connected. However the reported axis and buttons are wrong. Async doesn't update the values and events are not fired.

To Reproduce
Run any example from the folder

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.