In version 2.0 of this library, the API has been simplified and made more Python-like. Rather than having to fetch nested objects containing axis and button information, all the most common operations are now available as methods and attributes of the controller class itself. Read on…
Connecting to a controller¶
To connect to a controller you do the following (including error handling):
from approxeng.input.selectbinder import ControllerResource while True: try: with ControllerResource() as joystick: print('Found a joystick and connected') while joystick.connected: # Do stuff with your joystick here! # .... # .... # Joystick disconnected... print('Connection to joystick lost') except IOError: # No joystick found, wait for a bit before trying again print('Unable to find any joysticks') sleep(1.0)
This example will loop forever. It will attempt to connect to any supported joystick - the ControllerResource will raise IOError if it can’t find one, otherwise it will do all the necessary background work, create a controller object and, in this case, bind it to the ‘joystick’ variable which can then be used to read axes, buttons etc.
The connected property on the joystick object indicates whether the underlying device is connected or not - if, for example, you have a controller that goes out of range, runs out of batteries, or is turned off while in use this will be set to False and you can handle the case correctly (if using this code in a robot, this would be an excellent time to turn of all your motors, for instance!)
approxeng.input.selectbinder.ControllerResource class accepts a number of, optional, arguments. These can be
used to tell it which controller type to use (the default is to connect to the first controller it can understand, but
if you have multiple controllers connected for some reason you might want to tell it to use your XBox1 rather than your
PS4 controller). They can also be used to configure the default settings for axes, and there’s an option to print out
debug information. In general though you’ll only ever need the simple form shown here.
Once you have a controller object you can use it to read axes and buttons, both of which are referenced by a standard name (see Standard Names) - this allows you to not worry too much about exactly what controller’s going to be present. Rather than having to know that the XBox controller has ‘A’,’B’… and so on, whereas the Playstation controllers have ‘cross’, ‘circle’ etc the API defines a standard set of names for buttons and axes (they’re based on the PS3 controller as it happens, mostly because that’s the one I first used when I started writing the library!).
Connecting to multiple controllers¶
To connect to multiple controllers, or to be more specific about the types and capabilities of controllers you need, see Controller Discovery - approxeng.input.controllers. The example below shows binding to two controllers, the first of which must be a PS3 controller, the second of which must have a left X and Y axis:
from approxeng.input.selectbinder import ControllerResource from approxeng.input.dualshock3 import DualShock3 from approxeng.input.controllers import ControllerRequirement while True: try: with ControllerResource(ControllerRequirement(require_class=DualShock3), ControllerRequirement(require_snames=['lx','ly'])) as ds3, joystick: print('Found two joysticks and connected') while joystick.connected and ds3.connected: # Do stuff with your joystick here! # .... # .... # One or both joystick(s) disconnected... print('Connection to joystick(s) lost') except IOError: # No matching joystick found, wait for a bit before trying again print('Unable to find matching joysticks') sleep(1.0)
Reading Analogue Axes¶
Analogue axes are those which vary continuously, allowing for fine control of motion. Unlike buttons, which are either on or off, an analogue axis has a floating point value associated with its current position.
- Centred axes have a value ranging from -1.0 to 1.0
- Trigger axes have a value ranging from 0.0 to 1.0
Axis values are read as properties of the joystick object (in this and other examples we’re not showing the exception handling from the first example, but you should still do it!):
from approxeng.input.selectbinder import ControllerResource # Get a joystick with ControllerResource() as joystick: # Loop until disconnected while joystick.connected: # Get a corrected value for the left stick x-axis left_x = joystick['lx'] # We can also get values as attributes: left_y = joystick.ly
…and that’s it! You might have used other libraries which require you to do event handling and similar, but in this case all that stuff is taken care of in the background and you just have to read the information you want from the joystick object.
All the controllers supported by this library are fairly similar - they have two analogue joysticks, a bunch of buttons, some triggers etc. It would be helpful therefore to be able to make use of one controller type but make it as easy as possible to use others without substantial code changes in your own code.
To do this the library assigns a standard name, or sname to each button and axis on every controller. These are based loosely on the buttons found on a PS3 controller, at the cost of minor confusion for the XBox users (where, for example, the X button is referred to by the name square). As long as you use controls which are common to all three controllers you should be able to transparently make use of whichever of them is available at the time. You can also choose to make use of facilities which are only available on specific hardware (such as the analogue triggers on the PS4 and XBoxOne controllers) but you should bear in mind that this will preclude use of a less well equipped controller. Up to you.
A look at the source for each of the controller subclasses should make it obvious what names are available, but the standard ones are as follows:
With a new kernel (4.13 upwards, tested with 4.15) the Sony controllers expose their motion events in a way we can handle, so I’ve added pitch and roll for both controllers, and yaw rate for the DS4. There is no absolute yaw value available, you’d have to calculate this from the rates (tricky to do with any accuracy). Roll is positive clockwise when holding the controller, pitch is positive aiming the front of the controller towards the ceiling. Available in 2.1.0 of this library onwards.
|Standard name||PS3||PS4||XBoxOne||Rock Candy||Steam||Wii Pro|
|lx||Left X||Left X||Left X||Left X||Left X||Left X|
|ly||Left Y||Left Y||Left Y||Left Y||Left Y||Left Y|
|rx||Right X||Right X||Right X||Right X||Right X||Right X|
|ry||Right Y||Right Y||Right Y||Right Y||Right Y||Right Y|
|lt||L2 Trigger||L2 Trigger||LT Trigger||—||Left Trigger||—|
|rt||R2 Trigger||R2 Trigger||RT Trigger||—||Right Trigger||—|
|Standard name||PiHut||SF30 Pro|
|lx||Left X||Left X|
|ly||Left Y||Left Y|
|rx||Right X||Right X|
|ry||Right Y||Right Y|
|lt||L Trigger||L Trigger|
|rt||R Trigger||R Trigger|