Saturday 11 June 2011

Connecting a PS3 Dualshock/Sixaxis Controller to your PC (and the basics of Programming with it)

Finally! Something a bit more technical - so yeah as the title describes I'm gonna go through the basics of installing your PS3 Controller on your PC and then accessing its input programmatically through DirectInput in C++.



Prerequisites

MotionInJoy Gamepad Tool (Programming and Gaming)
http://www.motioninjoy.com/download
MotionInJoy contains the drivers for the PS3 Controller allowing it to connect as a DirectInput device for gaming or programming. I found 
Version 0.6.0001 more stable than 

Version 0.6.0003 which was the latest version at time of writing.


DirectX SDK (Programming Only)
http://msdn.microsoft.com/en-us/directx/aa937788
A recent version of the DirectX SDK is needed, this will give you the librarys and include's for using DirectInput when programming with your newly attached PS3 Controller.


Let's begin


1. All going well you should now have MotionInJoy installed successfully so open up the MotionInJoy Gamepad Tool and click on the "Driver Manager" tab.

2. Plug in the PS3 Controller via the normal USB charging cable

2.2 IMPORTANT: Remove or disable any attached Bluetooth hardware.

3. Press the "Load Driver" or "Install All" button

4. After a few bleeps as the computer reconfigures the hardware you should see a Success message, the controller is now successfully attached however only in Playstation 1 mode (No Analog Sticks)

5. Next go to the "Profiles" tab and select the "Playstation 3" button then the "Enable" button

And thats it - the Playstation 3 Dualshock or Sixaxis Controller is now linked to your PC any game that supports Game Controllers should now automatically detect and you'll just need to map the buttons to the actions of your choice.

Leading me on to
Step 2 - Programming use of the Playstation 3 Controller
Note I'm using Visual Studio 2010 for this however VS2003+ and any other compiler compatible with DirectX should work fine.


First Step: Including Libraries and Header files
So first things first get your Project open, any Win32 application should work after some moderated reconfiguration - so open Project > [Project Name] Properties.

First go to Configuration Properties > VC++ Directories and add the Include Path and Libraries Path for the DirectX SDK both are probably in:
C:\Program Files\Microsoft DirectX SDK ([SDK Month and Year])\Include
and
C:\Program Files\Microsoft DirectX SDK ([SDK Month and Year])\Lib\x86 (or x64 on a 64bit processor)

Now, go to Configuration Properties > Linker > Input in the same dialog and add dxguid.lib;dinput8.lib under additional dependencies.

With that done its time to start coding, start with the following includes:

#include <d3d9.h>
#include <d3dx9.h>
#include <dinput.h>
Next we're gonna need a DirectInput interface and a handle for our PS3 Controller so declare these global/member variables:
LPDIRECTINPUT8 DIObject;
LPDIRECTINPUTDEVICE8 DIPS3ControllerDevice;
Second, initialising the device
We now have variables that will allow us to communicate with DirectInput, next we need to initialise them, to do this we use the DirectInput8Create() function thusly:
if(FAILED(DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&DIObject, NULL)))
{
exit(0);
}

Next, we need to set the Device variable to reference the PS3 Controller (note we don't reference the PS3 Controller explicitly just any game controllers attached to the PC so if you have others remove them)

So we take a rather roundabout route to achieving this and step one is to enumerate all of the devices of the right type (Game Controllers) and the EnumDevices() function does just that.
DIObject->EnumDevices(DI8DEVCLASS_GAMECTRL,(LPDIENUMDEVICESCALLBACKA)(&(AttainDevice)), NULL, DIEDFL_ATTACHEDONLY);
The parameters for this are simpler than they may look, the first simply sets the class of device you wish to connect with they are all listed on MSDN so I'll stick to what we need in this a Game Controller so we use the aptly named constant DI8_DEVCLASS_GAMECTRL, next we supply a callback function, that is the function to be called for each device allowing us to decide whether or not to use that device and then attempt to connect to it. It looks really awkward but we just reference the function we are calling (more on that in a sec) and cast as a LPDIENUMDEVICESCALLBACKA, LP is the standard notation for a pointer within the API then DI for DirectInput of course and EnumDevices Callback A, next we can pass in a void* (LPVOID)to pass to the Callback function, finally we specify the scope of the enumeration in this case we only really care about devices that are currently attached and so we use the constant "DIEDFL_ATTACHEDONLY (DI EnumDevices FLag)

So now for the aforementioned callback function, code speaks louder than words so here it is:
BOOL _PlayerManager::AttainDevice(DIDEVICEINSTANCE& lpddi,VOID * pvRef){
    if(FAILED(DIObject->CreateDevice(lpddi.guidInstance, &DIPS3ControllerDevice,NULL)))
    {
        return DIENUM_CONTINUE;
    }
    
    // Set the Data format (c_dfDIJoystick is standard Dirx Global)
    if(FAILED(DIPS3ControllerDevice->SetDataFormat(&c_dfDIJoystick)))
    {
        if(DIPS3ControllerDevice!= NULL)
        {
           DIPS3ControllerDevice->Unacquire(); 
           DIPS3ControllerDevice->Release();   
           DIPS3ControllerDevice= NULL;  
        }
        return DIENUM_CONTINUE;
    }
    
    // How the app handles control of keyboard when switching window etc. 
    if(FAILED(DIPS3ControllerDevice->SetCooperativeLevel(NULL,
                                                   DISCL_BACKGROUND | 
                                                   DISCL_NONEXCLUSIVE)))
    { 
        if(DIPS3ControllerDevice!= NULL)
        {
           DIPS3ControllerDevice->Unacquire(); 
           DIPS3ControllerDevice->Release();   
           DIPS3ControllerDevice = NULL;  
        }
        return DIENUM_CONTINUE;
    }
    
    // Aquiring the Controller now everything is set up.
    if(FAILED(DIPS3ControllerDevice->Acquire()))
    { 
        if(DIPS3ControllerDevice!= NULL)
        {
           DIPS3ControllerDevice->Unacquire(); 
           DIPS3ControllerDevice->Release();   
           DIPS3ControllerDevice = NULL;  
        }
        return DIENUM_CONTINUE;
    }
    return DIENUM_STOP;
}
We handle the device in the most basic sense, we assume it is the PS3 Controller and then run through acquiring use of it, if it any point we fail then we immediately make attempts to Unacquire it and release its use and reset the pointer to NULL to keep everything clean before ending the function with DIENUM_CONTINUE which tells DirectInput to continue enumerating, if it runs through all setup commands successfully then we send DIENUM_STOP, device acquired time to move on to using it. The setup commands themselves are simple enough, simply creating an instance of the Device class using the Controllers GUID, setting it up as a Joystick/Game Controller, defining how we're using it and finally acquiring the device.

Third Step: Get Using!
DIJOYSTATE Data;
if(DIPS3ControllerDevice != NULL)
{
 DIPS3ControllerDevice->GetDeviceState(sizeof(DIJOYSTATE), &Data);
}
So this snippet simply creates a structure to hold the data passed out by the controller and assuming the Controller has been initialised (remember we set it back to NULL if we fail) it'll fill the Data structure with the info from the controller, so we run this every frame and we have the controller data ready to tap from the Data structure

So, thats it. I hope you get as much thrill from all this as I do, any questions shoot them down below!

Brian

2 comments:

  1. hey Man. I need to know if this works on Visual Basic (Visual Studio 2005). ¿Cant i write this code on VB instead of C? Thanks

    ReplyDelete
  2. Technically speaking it should be possible, just find a good tutorial for implementing DirectInput in Visual Basic.

    ReplyDelete