Shopping Cart

Posts

Developing Arduino code for HID Joystick

This article focuses on how to use the existing USB code library and HID report descriptor info to implement joystick functionality.  Human readable HID report descriptor and report information can be easily obtained using USBHID_desc.pde sketch – see previous article for details. This information will help you getting field details such as size and count info. Also, if you don’t have Arduino Mega or 2560 to run USBHID_desc, report descriptor for your device can be obtained using one of many PC tools known as USB analyzers, or even the official usb.org device verification tool. This article is written by Alex Glushchenko – a developer behind second revision of USB Host Library as well as majority of device support code.

As you may already know report is a data structure used by HID device to return the information about the certain device parameters such as joystick coordinates or button events, or receive new settings such as switching on/off LEDs on keyboard.  

Report descriptor is a data structure which describes report or reports, if there are few in number, field sequence, sizes and counts.  Each report descriptor consists of several items. Each item describes some field property. I am not going too deep into details on items, explaining only the most important ones which are absolutely necessary in writing your own report parser.  

The items usually describe type of field (input/output/feature), minimum, maximum field values, units, value meaning (usage) etc.

The joystick I’m using can be seen on title picture (click on it to make it bigger). Here is how my joystick report descriptor looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
Usage Page Gen Desktop Ctrls(01)
Usage Game Pad
Collection Application
Collection Logical
Report Size(08)
Report Count(05)
Logical Min(00)
Logical Max(FF00)
Physical Min(00)
Physical Max(FF00)
Usage X
Usage Y
Usage Z
Usage Z
Usage Rz
Input(00000010)
Report Size(04)
Report Count(01)
Logical Max(07)
Physical Max(3B01)
Unit(14)
Usage Hat Switch
Input(01000010)
Unit(00)
Report Size(01)
Report Count(0C)
Logical Max(01)
Physical Max(01)
Usage Page Button(09)
Usage Min(01)
Usage Max(0C)
Input(00000010)
Usage Page Undef(00)
Report Size(01)
Report Count(08)
Logical Max(01)
Physical Max(01)
Usage
Input(00000010)
End Collection
Collection Logical
Report Size(08)
Report Count(07)
Physical Max(FF00)
Logical Max(FF00)
Usage
Output(00000010)
End Collection
End Collection

It may look puzzling at first sight, but soon it is going to look more clear to you. HID specification makes it possible for any  host to know exactly what the certain report field means by introducing usages and usage tables.

Usage table specifies all possible controls and device setting IDs for a given class of devices, such as game controllers.

So, analyzing report descriptor dump you have to pay attention to usage table, usage, report size, report count, input and output items.

Now let’s see what we have in the dump. First we have five (line 6) input (line 16) fields X, Y, Z, Z, Rz for Game Pad (lines 11-15), 8-bit in size each (line 5), followed by 4-bit Hat Switch field, 12 buttons, 1-bit each, 8-bit field with unknown usage. 64 bit in total which is 8 bytes.  You don’t have to calculate Output items unless you plan to make use of output report.

Now let me explain some details. Each report field or group of fields with the same properties is described by a number of items. Each field explanation ends with an Input, Output or Feature item. Take another look at the dump. Game Pad fields are five in number which is specified by Report Count(05), 8 bit in size Report Size(08) and individual usages for X, Y, Z, Z and Rz. Hat Switch is one field 4-bit in size, 12 buttons are described by Usage Page Button(09) and usage range specified by Usage Min(01) and Usage Max(0C).

Now you know how to interpret report descriptor dump. Let’s go down to code. There is a sample sketch called USBHIDJoystick.pde which we are going to look at. Just make sure you have the latest version of the USB Host library downloaded. With the current version of the USB Host library you do not have to write all the code from scratch, use HIDUniversal class to reuse the main functionality responsible for USB interaction. You have to inherit HIDReportParser class to implement report parser functionality and provide its address by  SetReportParser method to HIDUniversal class instance. Files hidjoystickrptparser.h and hidjoystickrptparser.cpp contain all the interesting code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct GamePadEventData
{
	uint8_t X, Y, Z1, Z2, Rz;
};
 
class JoystickEvents
{
public:
	virtual void OnGamePadChanged(const GamePadEventData *evt);
	virtual void OnHatSwitch(uint8_t hat);
	virtual void OnButtonUp(uint8_t but_id);
	virtual void OnButtonDn(uint8_t but_id);
};
 
#define RPT_GEMEPAD_LEN		5
 
class JoystickReportParser : public HIDReportParser
{
	JoystickEvents		*joyEvents;
 
	uint8_t				oldPad[RPT_GEMEPAD_LEN];
	uint8_t				oldHat;
	uint16_t			oldButtons;
 
public:
	JoystickReportParser(JoystickEvents *evt);
 
	virtual void Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
};

Now consider Parse() method functionality. Because the event buffer allocated for the report is pretty large and the size of my joystick report is only 8 byte long, Parse() is called only once per each report.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
void JoystickReportParser::Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf)
{
	bool match = true;
 
	// Checking if there are changes in report since the method was last called
	for (uint8_t i=0; i<RPT_GEMEPAD_LEN; i++)
		if (buf[i] != oldPad[i])
		{
			match = false;
			break;
		}
 
	// Calling Game Pad event handler
	if (!match && joyEvents)
	{
		joyEvents->OnGamePadChanged((const GamePadEventData*)buf);
 
		for (uint8_t i=0; i<RPT_GEMEPAD_LEN; i++) oldPad[i] = buf[i];
	}
 
	uint8_t hat = (buf[5] & 0xF);
 
	// Calling Hat Switch event handler
	if (hat != oldHat && joyEvents)
	{
		joyEvents->OnHatSwitch(hat);
		oldHat = hat;
	}
 
	uint16_t buttons = (0x0000 | buf[6]);
	buttons <<= 4;
	buttons |= (buf[5] >> 4);
	uint16_t changes = (buttons ^ oldButtons);
 
	// Calling Button Event Handler for every button changed
	if (changes)
	{
		for (uint8_t i=0; i<0x0C; i++)
		{
			uint16_t mask = (0x0001 << i);
 
			if (((mask & changes) > 0) && joyEvents)
				if ((buttons & mask) > 0)
					joyEvents->OnButtonDn(i+1);
				else
					joyEvents->OnButtonUp(i+1);
		}
		oldButtons = buttons;
	}
}

The logic is the same for all report fields. First the report buffer is checked against the previously saved one for the data changes. If changes found the appropriate event handler is invoked with the current state data passed as a function argument. In case of Game Pad the current state data is passed as structure pointer. In case of the Hat Switch the current state is passed as integer value.

Handling buttons is a bit more complicated. Lines 30-33 are responsible for finding out which buttons have changed their states since the last Parse() call. Cycling through each bit of the mask we find out the current state of the changed button. If the button is pressed, OnButtonDn() event handler is called. If the button released, OnButtonUp() event handler is called.

If your joystick report has different structure, all you have to do is modify the Parse method to change fields offsets and sizes. Pretty simple, isn’t it?

The last component of the code is actual event handlers. In this example they simply output game pad, hat switch and button states to the serial console. Again, the body of each function can easily be modified to perform something more useful.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void JoystickEvents::OnGamePadChanged(const GamePadEventData *evt)
{
	Serial.print("X: ");
	PrintHex<uint8_t>(evt->X);
	Serial.print("\tY: ");
	PrintHex<uint8_t>(evt->Y);
	Serial.print("\tZ: ");
	PrintHex<uint8_t>(evt->Z1);
	Serial.print("\tZ: ");
	PrintHex<uint8_t>(evt->Z2);
	Serial.print("\tRz: ");
	PrintHex<uint8_t>(evt->Rz);
	Serial.println("");
}
 
void JoystickEvents::OnHatSwitch(uint8_t hat)
{
	Serial.print("Hat Switch: ");
	PrintHex<uint8_t>(hat);
	Serial.println("");
}
 
void JoystickEvents::OnButtonUp(uint8_t but_id)
{
	Serial.print("Up: ");
	Serial.println(but_id, DEC);
}
 
void JoystickEvents::OnButtonDn(uint8_t but_id)
{
	Serial.print("Dn: ");
	Serial.println(but_id, DEC);
}

The full text of Arduino sketch plus parsers/callbacks is available on GitHub. The code works with USB Host Shield 2.0, it has been tested on Arduino UNO, Mega, as well as Mega 2560. Unlike USBHID_desc this code takes just a little more than 16K of program space. The joystick I use is a random eBay unit (note from Oleg: I have a wireless HID joystick which looks quite differently from this one, however, the report descriptor is the same. If you have similar one, try it – it may work with this code without any modifications). If you’re shopping for a joystick, make sure it has analog game pads, otherwise you will only see values 0×00, 0×80 and 0xff from them. Also, all HID devices are picky about power, if you see strange behaviour while running your Arduino from USB the first thing to do is switch to external supply.

Try this code and if you see any issues or errors, please leave a comment. Also, if you have other HID devices you’d like me to cover, please let me know.

No related posts.

66 comments to Developing Arduino code for HID Joystick

  • Yazan

    Can you add Arm_mouse project to the examples of USBHIDBootKbd examples ?!!!!!
    every day opening the site and waiting … i swear that i tried to make that work o rev 2.0 but i cant i a beginner in this …. why you don’t want to do that !!!

  • yazan

    sorry i mean usbhidbootmouse examples

  • Zheffie

    Works like charm… nice work.

  • Josh

    I’m comfortable within arduino’s processing environment, but I get lost within C++. Is there any way to access the button states from within arduino’s loop() function?

    I realize I can go in and change the code within the event handlers, and I’ve been playing around with it, but don’t completely grok it yet. Is there any way to access the button states from within my pde file as the code is written now? I just want to do some simple state checking, and I’m having a hard time understanding the C++ code, ie:

    void loop(){

    Serial.print("X: ");
    PrintHex(JoystickEvents::X);

    }

    How far off am I?

  • Josh

    I figured it out. It took me a few hours, but I was able to do it. I would still appreciate your feedback. I’m sure there’s a way to do this better, but this works.

    In case anyone else is interested, this is how I did it:

    In the header file, I gave the JoystickEvents class a few more members – one for each of the buttons:

    class JoystickEvents
    {
    public:
    virtual void OnGamePadChanged(const GamePadEventData *evt);
    virtual void OnHatSwitch(uint8_t hat);
    virtual void OnButtonUp(uint8_t but_id);
    virtual void OnButtonDn(uint8_t but_id);
    uint8_t X;
    uint8_t Y;
    // ... etc.

    };

    Then, I changed the event handler to populate those variables instead of printing:

    void JoystickEvents::OnGamePadChanged(const GamePadEventData *evt)
    {
    X = evt->X;
    Y = evt->Y;
    }

    Now, in my PDE file, I can access the member variables:

    void loop(){

    Usb.Task();

    float freq = map(JoyEvents.X, 0, 0xFF, 30.0f, 250.0f);

    }

  • trabin

    I’m not sure where to post this, but I have a USB HID magentic card swiper that is USB – shows up the following:
    0000: 05 01 09 06 A1 01 05 07 19 E0 29 E7 15 00 25 01
    0010: 75 01 95 08 81 02 95 01 75 08 81 01 95 05 75 01
    0020: 05 08 19 01 29 05 91 02 95 01 75 03 91 01 95 06
    0030: 75 08 15 00 25 68 05 07 19 00 29 68 81 00 C0
    Usage Page Gen Desktop Ctrls(01)
    Usage Keypad
    Collection Application
    Usage Page Kbrd/Keypad(07)
    Usage Min(E0)
    Usage Max(E7)
    Logical Min(00)
    Logical Max(01)
    Report Size(01)
    Report Count(08)
    Input(00000010)
    Report Count(01)
    Report Size(08)
    Input(00000001)
    Report Count(05)
    Report Size(01)
    Usage Page LEDs(08)
    Usage Min(01)
    Usage Max(05)
    Output(00000010)
    Report Count(01)
    Report Size(03)
    Output(00000001)
    Report Count(06)
    Report Size(08)
    Logical Min(00)
    Logical Max(68)
    Usage Page Kbrd/Keypad(07)
    Usage Min(00)
    Usage Max(68)
    Input(00000000)
    End Collection

    but when I attempt to use it as a boot keyboard – it returns 0x0D (hrJERR) – I was wondering if you had any direction that I could look at…it does work under windows/mac (but only after a reboot actually)

  • Alan

    I’m trying to get my Arduino Mega to communicate over USB (not over BT) with my Sony DS3 USB/BT game controller using your USB Host 2.0 shield.

    I can successfully build and run the USBHID_desc sketch which yields the device report far below.

    But when I build and run the USBHIDJoystick example, the code prints out the following:

    Start
    HU Init
    Addr:1
    NC:1

    Cnf:1
    HU configured

    and never prints anything else.

    I tweaked the parser code based on my understanding of the buf[] layout for my controller, but still nothing gets printed no matter what button I push or joystick I bend.

    After fiddling around with it all day, I’ve determined that the parser never gets called. I put a Serial.println(“In Parser”) at the very beginning of the parser code but the line never gets printed.

    I added a Serial.println(“In parser constructor”) just after the oldPad is filled with 0xD, and I DO see that get printed at the beginning so something is working.

    Any ideas what I’m doing wrong?

    Device Report
    ————-
    Start
    HU Init
    Addr:1
    NC:1

    Cnf:1
    HU configured
    0000: 05 01 09 04 A1 01 A1 02 85 01 75 08 95 01 15 00
    0010: 26 FF 00 81 03 75 01 95 13 15 00 25 01 35 00 45
    0020: 01 05 09 19 01 29 13 81 02 75 01 95 0D 06 00 FF
    0030: 81 03 15 00 26 FF 00 05 01 09 01 A1 00 75 08 95
    0040: 04 35 00 46 FF 00 09 30 09 31 09 32 09 35 81 02
    0050: C0 05 01 75 08 95 27 09 01 81 02 75 08 95 30 09
    0060: 01 91 02 75 08 95 30 09 01 B1 02 C0 A1 02 85 02
    0070: 75 08 95 30 09 01 B1 02 C0 A1 02 85 EE 75 08 95

    Usage Page Gen Desktop Ctrls(01)
    Usage Game Pad
    Collection Application
    Collection Logical
    Report Id(01)
    Report Size(08)
    Report Count(01)
    Logical Min(00)
    Logical Max(FF00)
    Input(00000011)
    Report Size(01)
    Report Count(13)
    Logical Min(00)
    Logical Max(01)
    Physical Min(00)
    Physical Max(01)
    Usage Page Button(09)
    Usage Min(01)
    Usage Max(13)
    Input(00000010)
    Report Size(01)
    Report Count(0D)
    Usage Page Undef(00)
    Input(00000011)
    Logical Min(00)
    Logical Max(FF00)
    Usage Page Gen Desktop Ctrls(01)
    Usage Pointer
    Collection Physical
    Report Size(08)
    Report Count(04)
    Physical Min(00)
    Physical Max(FF00)
    Usage X
    Usage Y
    Usage Z
    Usage Rz
    Input(00000010)
    End Collection
    Usage Page Gen Desktop Ctrls(01)
    Report Size(08)
    Report Count(27)
    Usage Pointer
    Input(00000010)
    Report Size(08)
    Report Count(30)
    Usage Pointer
    Output(00000010)
    Report Size(08)
    Report Count(30)
    Usage Pointer
    Feature(00000010)
    End Collection
    Collection Logical
    Report Id(02)
    Report Size(08)
    Report Count(30)
    Usage Pointer
    Feature(00000010)
    End Collection
    Collection Logical
    Report Id(EE)
    Report Size(08)
    Report Count

    • I played with DS3 connected to a PC. It looks like this device won’t generate reports by itself like other HID devices but needs some command sent to it first.

  • Alan

    Thanks Oleg!

    I think I found the command that you reference:

    try
    {
    //request the PS3 controller to send button presses etc back
    //Host to device (0×00) | Class (0×20) | Interface (0×01), Set Report (0×09), Report Type (Feature 0×03) – Report ID (0xF4), Host to device (0×00) – Endpoint 0 (0×00), data, dataoffset, datalength
    raw.SendSetupTransfer(0×21, 0×09, 0x03F4, 0×0000, buf, 0×00, 0×04);
    }

    But I’m not sure where/when/how to send this command. I’m thinking I’d just tack it into my loop() function just after the call to Usb.Task(). But I don’t know what command to use…

    I’ll keep digging, but if you have a simple answer, that would be FANTASTIC!

    Alan

  • Alan

    I added this to loop():

    void loop()
    {
    Usb.Task();

    if (nextPoll <= millis()) {
    nextPoll = millis() + 5000;
    Hid.SetReport(0, 1, 3, 0xf4, 0, 0);
    Serial.println("SetReport sent");
    Serial.println(Usb.getUsbTaskState());
    }
    }

    I see the "SetReport sent" followed by "144" (USB_STATE_RUNNING) print 6 times and then the output stops.

    No change in behavior. My parser never gets called.

    Alan

  • Alan

    I managed to get it going finally.

    I was on the right track with the previous code. But I wasn’t formatting the SetReport message properly. And you only have to send it once.

    Here is what I ended up with:

    uint8_t feature_F4_report[4] = { 0×42, 0x0c, 0×00, 0×00 };
    uint8_t startDelay = millis() + 5000;

    void loop()
    {
    Usb.Task();

    if ((Usb.getUsbTaskState() == USB_STATE_RUNNING) && startDelay && (startDelay <= millis())) {
    Hid.SetReport(0, 0, 3, 0xf4, 4, feature_F4_report);
    startDelay = 0;
    }
    }

    Because of continuous equivocation on the LSB values of the joysticks, as well as the accelerometer values, a new buffer of changed data is sent by the controller almost all the time. Consequently I had to comment out the code in the HIDUniversal::Poll() routine that does a hexdump of the buffer.

    As I mentioned before, I had to do some morphing of the buffer parser you provided to match the format of the buffer coming from the DS3.

    Things are working well now!

    Thanks for you help.

    Alan

  • Hid.SetReport(0, 0, 3, 0xf4, 4, feature_F4_report);

    Alan, how did you discover parameters for the SetReport function call?

    I am trying to read data from a weather station receiver WS3081, and I can do it from the PC if I send 8 bytes
    I need to send $A1 $00 $20 $20 $A1 $00 $20 $20 and i am not sure of the function call parameters.
    Also the USB_desc program tells me the EndPoint is $81 as does some working C code.
    Yet this endpont is not set in the table of valid endpoints and when is try to do a SetReport(0×81 ,…. the rcode comes backs as DB which is USB_ERROR_EP_NOT_FOUND_IN_TBL.

    Any clues?

    • Wexican

      Hi Whereswally,
      I’m doing something similar to yourself, I want to read the data from a WH1081 weather station & then format & publish this data to wunderground.com without the use of a pc. I’ve just started with mine & have got a list of the memory map & instruction list(similar to yours, same instructions). Only starting the Arduino part now, got the USB_desc program to work & report the interface description but from here I have no idea how to actually send the request to the WH1081 & receive back data from it! Did you get yours working?

      Thanks,

      Wexican

  • Alan

    Looks like PS3 Controller USB support has been officially pulled into the library codebase:

    http://www.circuitsathome.com/mcu/arduino/interfacing-ps3-controllers-via-usb

    This is very good news.

  • François

    Hello,

    Before i user the first version of USB HOST SHIELD board(sparkfun) and a SAITEK X45 all was ok.

    Now i use ADK Board and try tou get data from joystick.

    I gt this

    Read : 8
    90E008FFAF00AD00
    Read : 3
    220000

    Datas were split in two parts 8 + 3 which make my 11 Bytes struct
    struct GamePadEventData
    {
    byte Part1;
    byte Part2;
    byte Part3;
    byte Throlle;
    byte Dial;
    byte RZ;
    byte RX;
    byte Buttons;
    byte Reserved;
    byte Hat1;
    byte Hat2;
    };

    How can i threat all in one loop?

    Thanks

  • sisco

    I understand the maxPktSize < Reportlength

    maxPktSize = 8 and reportlength = 11

    How can i do?

    Thanks

    François

  • Ok i hardly code for waiting 11 Bytes in array before send it in the Poll method!

    An idea?

    thanks.

  • Kai

    Hi guys,

    I am playing around with the USB Host shield 2.0. Thanks for development. Its very cool!

    HID_test and HID_desc works good, but now I am trying to do my own sketch based on USBHIDJoystick.

    As I am not familiar with bitshift etc I am not shure how to adapt the code. I have a 3 Axis, 6 Buttons Loogitech Wingman Attack 2.
    The HIDdesc output looks like this:

    Usage Page Gen Desktop Ctrls(01)
    Usage Game Pad
    Collection Application
    Collection Logical
    Logical Min(00)
    Logical Max(FF00)
    Physical Min(00)
    Physical Max(FF00)
    Report Size(08)
    Report Count(03)
    Usage X
    Usage Y
    Usage Z
    Input(00000010)
    Logical Max(01)
    Physical Max(01)
    Report Size(01)
    Report Count(06)
    Usage Page Button(09)
    Usage Min(01)
    Usage Max(06)
    Input(00000010)
    Report Count(02)
    Input(00000001)
    End Collection
    Collection Logical
    Logical Max(FF00)
    Physical Max(FF00)
    Report Size(08)
    Report Count(04)
    Usage Page Undef(00)
    Usage
    Feature(00000010)
    End Collection
    End Collection

    I changed struct GamePadEventData to X, Y, Z and RPT_GEMEPAD_LEN to 3, but I think I have to change something in the button parsing…

    Can you help me?

    Thanks
    Kai

  • Alex

    Hi, Kai,

    According to the dump you’ve sent you have three fields for X,Y,Z coordinates and one-byte field for 6 buttons (the last two bits, if I am not mistaken, is an alignment). Here is how you should change the code:


    void JoystickReportParser::Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf)
    {
    bool match = true;

    // Checking if there are changes in report since the method was last called
    for (uint8_t i=0; iOnGamePadChanged((const GamePadEventData*)buf);

    for (uint8_t i=0; i<RPT_GEMEPAD_LEN; i++) oldPad[i] = buf[i];
    }

    uint8_t buttons = (0x00 | buf[3]);
    uint8_t changes = (buttons ^ oldButtons);

    // Calling Button Event Handler for every button changed
    if (changes)
    {
    for (uint8_t i=0; i<0x06; i++)
    {
    uint16_t mask = (0x01 < 0) && joyEvents)
    if ((buttons & mask) > 0)
    joyEvents->OnButtonDn(i+1);
    else
    joyEvents->OnButtonUp(i+1);
    }
    oldButtons = buttons;
    }
    }

    I hope it’s going to work with your joystick.

  • Kai

    Thank you, it works!

    Can you tell me, what this if for which seems not to be required in my case?

    buttons <> 4);

    • Alex

      If you mean code from method JoystickReportParser::Parse numbered from 30 through 33 up in the article, these are bit shift operations done to extract one-bit button fields from two separate bytes. If you look at the dump at the beginning of the article you will see that button data is following five eight-bit coordinate fields and one four-bit hat switch field, so out of 12 (0x0C) one-bit button fields six are coming in sixth byte of the report buffer and the rest in seventh byte. To put all of them together into two-byte (uint16_t) variable I had to put the high byte first (line 30), then shift all its bits four bits left (line 31), then put lower four bits using OR operation from sixth byte previously shifted so that the high four button bits become low ones(line 31). At the end of the day we have the variable buttons where the first twelve bits correspond to button states.
      In your case you have three one-byte coordinate fields followed by six one-byte button fields which are fit in one byte, so the above mention code is not needed for your joystick.

  • Kai

    Just another thing: When I am trying to call a function defined below the main loop within the JoystickEvents::OnGamePadChanged or JoystickEvents::OnButtonUp etc the complier says, that the function isnt declared in the scope.

    Can you tell me, how to manage this?

    • Alex

      JoystickEvent class is defined in hidjoystickrptparser.h and implemented in hidjoystickrptparser.cpp, so when parsing those two files the compiler knows nothing about other files which are not included by #include statement. To get your code up and running you have to put your function either into hidjoystickrptparser.cpp, or put it into separate yourfile.h and yourfile.cpp files and then add #include “yourfile.h” in hidjoystickrptparser.cpp.

  • weigee

    Can someone help me with a perplexing problem? I tried the USBHID_desc sketch on my USB keyboard and all normal keys were captured fine. However, all of the media keys (e.g., volumne, mute, play, FF, etc.) are not being detected by the host shield. However, plugging the keyboard directly into the computer works fine. I checked the usage table and it corresponds to 0×07 which is standard keyboard table. Anyone can shed some light into this? Greatly appreciated, thanks!!

    NC:1

    Cnf:1
    HU configured
    0000: 05 01 09 06 A1 01 05 07 19 E0 29 E7 15 00 25 01
    0010: 75 01 95 08 81 02 05 08 09 4B 81 02 95 05 05 08
    0020: 19 01 29 05 91 02 95 01 75 03 91 01 95 06 75 08
    0030: 15 00 26 A4 00 05 07 19 00 2A A4 00 81 00 C0
    Usage Page Gen Desktop Ctrls(01)
    Usage Keypad
    Collection Application
    Usage Page Kbrd/Keypad(07)
    Usage Min(E0)
    Usage Max(E7)
    Logical Min(00)
    Logical Max(01)
    Report Size(01)
    Report Count(08)
    Input(00000010)
    Usage Page LEDs(08)
    Usage Gen Ind
    Input(00000010)
    Report Count(05)
    Usage Page LEDs(08)
    Usage Min(01)
    Usage Max(05)
    Output(00000010)
    Report Count(01)
    Report Size(03)
    Output(00000001)
    Report Count(06)
    Report Size(08)
    Logical Min(00)
    Logical Max(A400)
    Usage Page Kbrd/Keypad(07)
    Usage Min(00)
    Usage Max(A400)
    Input(00000000)
    End Collection

  • Zangetsu

    I was wondering if there was a code that can be used to get the Logitech extreme 3d pro to get it to move autonomously. I have this project that is racking my brain and I can not find anything to help me

  • Gocht

    Hi Oleg!
    I’ve implemented the example in the USB Host Shield lib (HID/Joystick) but I don’t hace any result for the axis or the joystick in my usb controller… when I press the buttons I get some action from the arduino, but when I move the JOystick nothing happens, I’d like you reply me with some advices about what am I doing wrong?.. TY!

    • Make sure you read report correctly. Joysticks are all different, a sketch reading report of one particular joystick won’t necessarily work with another.

  • Gocht

    Hello everyone!

    I am trying to do something like this, I found a library to use the USB Joystick with Arduino but it did not work.. however..

    I try this, but I get error compiling, I wonder if one of you could explain how to use this library. I couldn’t make it run.
    Thanks!

  • Josh

    Hello Oleg! thanks for all this, I am learning a lot! However I am kinda confuse…
    Just one question, I am playing with the Saitek x52, I got the HID report with the Arduino Mega and I know how to interpret… but after that, should I just go ahead and re-do the code that you are analyzing here, load it into the AMega and run it? Hope you have an advice for this HID report:
    Start
    0000: 05 01 09 04 A1 01 09 30 16 00 00 26 FF 07 75 0B
    0010: 95 01 81 02 09 31 16 00 00 26 FF 07 75 0B 95 01
    0020: 81 02 09 35 16 00 00 26 FF 03 75 0A 95 01 81 02
    0030: 09 32 15 00 26 FF 00 75 08 95 01 81 02 09 33 15
    0040: 00 26 FF 00 75 08 95 01 81 02 09 34 15 00 26 FF
    0050: 00 75 08 95 01 81 02 09 36 15 00 26 FF 00 75 08
    0060: 95 01 81 02 05 09 15 00 25 01 19 01 29 22 75 01
    0070: 95 22 81 02 75 02 95 01 81 01 05 01 09 39 15 01

    Usage Page Gen Desktop Ctrls(01)
    Usage Game Pad
    Collection Application
    Usage X
    Logical Min(0000)
    Logical Max(FF07)
    Report Size(0B)
    Report Count(01)
    Input(00000010)
    Usage Y
    Logical Min(0000)
    Logical Max(FF07)
    Report Size(0B)
    Report Count(01)
    Input(00000010)
    Usage Rz
    Logical Min(0000)
    Logical Max(FF03)
    Report Size(0A)
    Report Count(01)
    Input(00000010)
    Usage Z
    Logical Min(00)
    Logical Max(FF00)
    Report Size(08)
    Report Count(01)
    Input(00000010)
    Usage Rx
    Logical Min(00)
    Logical Max(FF00)
    Report Size(08)
    Report Count(01)
    Input(00000010)
    Usage Ry
    Logical Min(00)
    Logical Max(FF00)
    Report Size(08)
    Report Count(01)
    Input(00000010)
    Usage Slider
    Logical Min(00)
    Logical Max(FF00)
    Report Size(08)
    Report Count(01)
    Input(00000010)
    Usage Page Button(09)
    Logical Min(00)
    Logical Max(01)
    Usage Min(01)
    Usage Max(22)
    Report Size(01)
    Report Count(22)
    Input(00000010)
    Report Size(02)
    Report Count(01)
    Input(00000001)
    Usage Page Gen Desktop Ctrls(01)
    Usage Hat Switch
    Logical Min(01) Game Pad X(04)
    Y(00)
    Rz(00)
    Z(00)
    Rx(00)
    Ry(00)
    Slider(00)
    Btn0001
    (00) Btn0002
    (00) Btn0003
    (00) Btn0004
    (00) Btn0005
    (00) Btn0006
    (00) Btn0007
    (00) Btn0008
    (01) Btn0009
    (00) Btn000A
    (00) Btn000B
    (00) Btn000C
    (00) Btn000D
    (00) Btn000E
    (00) Btn000F
    (00) Btn0010
    (00) Btn0011
    (00) Btn0012
    (00) Btn0013
    (00) Btn0014
    (00) Btn0015
    (00) Btn0016
    (00) Btn0017
    (00) Btn0018
    (00) Btn0019
    (00) Btn001A
    (00) Btn001B
    (00) Btn001C
    (00) Btn001D
    (00) Btn001E
    (00) Btn001F
    (00) Btn0020
    (00) Btn0021
    (01) Btn0022
    (01)
    (03)
    Hat Switch Game Pad X(00)
    Y(00)
    Rz(00)
    Z(00)
    Rx(00)
    Ry(00)
    Slider(00)
    Btn0001
    (00) Btn0002
    (00) Btn0003
    (00) Btn0004
    (00) Btn0005
    (00) Btn0006
    (00) Btn0007
    (00) Btn0008
    (00) Btn0009
    (00) Btn000A
    (00) Btn000B
    (00) Btn000C
    (00) Btn000D
    (00) Btn000E
    (00) Btn000F
    (00) Btn0010
    (00) Btn0011
    (00) Btn0012
    (00) Btn0013
    (00) Btn0014
    (01) Btn0015
    (00) Btn0016
    (00) Btn0017
    (00) Btn0018
    (01) Btn0019
    (00) Btn001A
    (00) Btn001B
    (00) Btn001C
    (00) Btn001D
    (00) Btn001E
    (00) Btn001F
    (00) Btn0020
    (00) Btn0021
    (00) Btn0022
    (00)
    (00)
    Hat Switch

  • Josh

    OK Oleg!… I just have an unusual problem and I hope you have an idea to solve it..

    All my axes’s sum is 64 bits, when I tried to do the buttons my computer sends me right to the beginning, it prints the button’s value ok but modifies my X axe. Is that because of the Computer memory?? How would you solve that???

    Thank You Very Much!

  • Wurstwasser

    Hi,
    I have a Thrustmaster T16000M Joystick and tried to modify the parser but failed because the descriptor looks pretty different from the example here.
    The descriptor is this:

    Start
    0000: 05 01 09 04 A1 01 05 09 19 01 29 10 15 00 25 01
    0010: 35 00 45 01 75 01 95 10 81 02 05 01 09 39 15 00
    0020: 25 07 35 00 46 3B 01 65 14 75 04 95 01 81 42 81
    0030: 01 05 01 09 30 15 00 26 FF 3F 35 00 46 FF 3F 65
    0040: 00 75 0E 95 01 81 02 75 02 81 01 09 31 75 0E 81
    0050: 02 75 02 81 01 09 35 09 36 15 00 26 FF 00 35 00
    0060: 46 FF 00 65 00 75 08 95 02 81 02 06 00 FF 27 FF
    0070: FF 00 00 09 01 75 08 95 04 B1 A2 C0
    Usage Page Gen Desktop Ctrls(01)
    Usage Game Pad
    Collection Application
    Usage Page Button(09)
    Usage Min(01)
    Usage Max(10)
    Logical Min(00)
    Logical Max(01)
    Physical Min(00)
    Physical Max(01)
    Report Size(01)
    Report Count(10)
    Input(00000010)
    Usage Page Gen Desktop Ctrls(01)
    Usage Hat Switch
    Logical Min(00)
    Logical Max(07)
    Physical Min(00)
    Physical Max(3B01)
    Unit(14)
    Report Size(04)
    Report Count(01)
    Input(01000010)
    Input(00000001)
    Usage Page Gen Desktop Ctrls(01)
    Usage X
    Logical Min(00)
    Logical Max(FF3F)
    Physical Min(00)
    Physical Max(FF3F)
    Unit(00)
    Report Size(0E)
    Report Count(01)
    Input(00000010)
    Report Size(02)
    Input(00000001)
    Usage Y
    Report Size(0E)
    Input(00000010)
    Report Size(02)
    Input(00000001)
    Usage Rz
    Usage Slider
    Logical Min(00)
    Logical Max(FF00)
    Physical Min(00)
    Physical Max(FF00)
    Unit(00)
    Report Size(08)
    Report Count(02)
    Input(00000010)
    Usage Page Undef(00)
    Logical Max(FFFF0000)
    Usage
    Report Size(08)
    Report Count(04)
    Feature(10100010)
    End Collection

    I tried to start with just the 32(?) buttons which should be in buf[0], right? Is there a chance to get through this line by line in the code? I totally have no idea how to use that mask and the shift operations to get all the buttons.

    • First 10 bits are buttons, next 4 bits is a hat switch. Look at Report Size/Report Count pair – first is number of bits for the value of the control, second is the number of controls. Buf[0] must contain first 8 buttons. I don’t see 32 buttons on your joystick

  • Wurstwasser

    Thanks for the answer. Its 16 buttons = 0×10, my mistake. I got the buttons and the pov hat working now. But the axes are giving me problems. The x-axis for example should be 14 bit (0x0E). I just coded this to test it:
    uint16_t x_axis = buf[4];
    x_axis <<= 8;
    x_axis |= buf[3];
    x_axis <> 4);
    x_axis <>= 2;
    I then printed x_axis via Serial, but the values seem to not match the x axis position. Am i doing something wrong? So bit 1-16 are the buttons, 17-20 is the pov hat. Shouldn’t the x-axis start at bit 21 and stop at bit 34? Also there is a report size(02) after Usage X, which seems to be padding?

    • You should be able to output your report in hex using USB HID Descriptor sketch. It will also try to parse it but most likely will do it incorrectly. You can then operate your joystick and see the changes in the report.

      • Wurstwasser

        I just found out that the x-axis has 262144 values = 18bit, although the descriptor looks like it has 14bit. I moved the x-axis from most left = 0 to most right = 262144.

        • Wurstwasser

          OK I think i shifted a bit too much. Both x and y seem to have 16 bits which also makes sense with the physical and logical max values. It just seems that after the 4 bit pov hat there are 2 useless bits and the x-axis starts at bit 23-38 and the y axis starts at bit 39-56.

  • Wurstwasser

    So another thing i didn’t find is how and where is an example of callbacks used to access the GamePadEventData from within the loop() function.

  • Wurstwasser

    Yeah, i was using the newest version from github (download as -zip; master-branch). The error comes from the SerialPrintHex(T) method from printhex.h.

  • Gerrit

    Hi there,

    I got the code above working to parse the input report, but how would I go about writing an output report, e.g. to change an LED on the device?

  • Mike Broich

    Hello
    I am trying to use a Microsoft Sidewinder Precision Pro Joystick in a project, and I need some help parsing it.
    In addition to the joystick, it has twist, rudder slider, 8-way hat, and 9 or 10 buttons.

    Here is the descriptor:
    Start
    0000: 05 01 09 04 A1 01 09 01 A1 00 95 01 75 04 15 00
    0010: 25 07 35 00 46 3B 01 66 14 00 09 39 81 42 95 04
    0020: 75 01 15 00 25 01 35 00 45 01 66 00 00 05 09 19
    0030: 01 29 04 A4 81 02 95 02 75 0A 16 00 FE 26 FF 01
    0040: 35 00 46 FF 03 05 01 09 30 09 31 81 02 75 06 95
    0050: 01 15 E0 25 1F 35 00 45 2E 66 14 00 05 01 09 35
    0060: 81 02 C0 B4 05 09 95 05 19 05 29 09 81 02 95 01
    0070: 81 01 75 07 15 C0 25 3F 35 00 45 7F 05 01 09 36

    Usage Page Gen Desktop Ctrls(01)
    Usage Game Pad
    Collection Application
    Usage Pointer
    Collection Physical
    Report Count(01)
    Report Size(04)
    Logical Min(00)
    Logical Max(07)
    Physical Min(00)
    Physical Max(3B01)
    Unit(1400)
    Usage Hat Switch
    Input(01000010)
    Report Count(04)
    Report Size(01)
    Logical Min(00)
    Logical Max(01)
    Physical Min(00)
    Physical Max(01)
    Unit(0000)
    Usage Page Button(09)
    Usage Min(01)
    Usage Max(04)
    Push
    Input(00000010)
    Report Count(02)
    Report Size(0A)
    Logical Min(00FE)
    Logical Max(FF01)
    Physical Min(00)
    Physical Max(FF03)
    Usage Page Gen Desktop Ctrls(01)
    Usage X
    Usage Y
    Input(00000010)
    Report Size(06)
    Report Count(01)
    Logical Min(E0)
    Logical Max(1F)
    Physical Min(00)
    Physical Max(2E)
    Unit(1400)
    Usage Page Gen Desktop Ctrls(01)
    Usage Rz
    Input(00000010)
    End Collection

    Thank you for any help you can provide.

  • Mike Broich

    Thank you for the lead.
    I modified the le3dp_rptparser.h like so:

    struct GamePadEventData
    {
    union { //axes and hut switch
    uint32_t axes;
    struct {
    uint8_t hat : 4;
    uint8_t buttons_a : 4;
    uint32_t x : 10;
    uint32_t y : 10;
    uint8_t twist : 6;
    uint8_t buttons_b : 5;
    uint8_t slider : 7;
    };
    };

    };
    I also changed the le3dp_rptparser.cpp to print integer values instead of hex.
    I am seeing data on the serial monitor.
    When I move the joystick to the far left, the X value is 512….then it increases to 1023 as it returns to center, then drops to 0 just right of center and increases to 511 as I move the joystick to the far right.
    The Y, twist, and slider values behave in a similar way.
    I’m guessing I’m losing bits along the way….what should I do to get them back?
    Thank you again….this is as close as I have come to completing a project I’ve been trying to build for over 10 years.

  • Mike Broich

    OK, figured it out….had to change some data types to get it to make sense.

    Here is a parser that works for the Microsoft Sidewinder Precision Pro joystick:

    struct GamePadEventData
    {
    uint8_t hat : 4; //hat switch
    uint8_t buttons_a : 4; //stalk buttons
    union {
    uint32_t axes;
    struct {
    int x : 10; //X axis value range -512 to +511
    int y : 10; //Y axis value range -512 to +511
    int twist : 6; //Twist rudder range -32 to +31
    uint8_t buttons_b : 5; //buttons on base
    int filler : 1; //throwaway
    int slider : 7; //throttle slider value range -64 to +63
    };
    };
    };
    Hope this helps someone else that tries to use one of these.

  • shane stevens

    this library has baffled me for days nw
    i am trying to use a thrust master steering wheel/pedal set just trying to add the wheel break and accelerate buttons to use external with servos.for the life of me i don know how to call it or ADD it in the .parser files under hid joystick.

    this is my scanBuf: 00C04F2FFFFF000000000000000000000000

    STEERING WHEEL
    X: 0000 Y: 03F0 Hat Switch: 04 Twist: 2F Slider: FF Buttons A: FF Buttons B: 00
    Buf: 00C04F30FFFF000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 04 Twist: 30 Slider: FF Buttons A: FF Buttons B: 00

    Buf: 00C0BF30FFFF000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0B Twist: 30 Slider: FF Buttons A: FF Buttons B: 00

    Buf: 00C00F31FFFF000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 00 Twist: 31 Slider: FF Buttons A: FF Buttons B: 00

    Buf: 00C01F31FFFF000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 01 Twist: 31 Slider: FF Buttons A: FF Buttons B: 00

    Buf: 00C0FF30FFFF000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0F Twist: 30 Slider: FF Buttons A: FF Buttons B: 00

    Buf: 00C0EF30FFFF000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0E Twist: 30 Slider: FF Buttons A: FF Buttons B: 00

    Buf: 00C0FF30FFFF000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0F Twist: 30 Slider: FF Buttons A: FF Buttons B: 00

    Buf: 00C09F32FFFF000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 09 Twist: 32 Slider: FF Buttons A: FF Buttons B: 00

    Buf: 00C04F35FFFF000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 04 Twist: 35 Slider: FF Buttons A: FF Buttons B: 00

    Buf: 00C0FF38FFFF000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0F Twist: 38 Slider: FF Buttons A: FF Buttons B: 00

    RIGHT PEDDAL
    uf: 00C0EF58FF00000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0E Twist: 58 Slider: 00 Buttons A: FF Buttons B: 00

    Buf: 00C0EF58FF01000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0E Twist: 58 Slider: 01 Buttons A: FF Buttons B: 00

    Buf: 00C0EF58FF2E000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0E Twist: 58 Slider: 2E Buttons A: FF Buttons B: 00

    Buf: 00C0EF58FF5B000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0E Twist: 58 Slider: 5B Buttons A: FF Buttons B: 00

    Buf: 00C0EF58FF86000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0E Twist: 58 Slider: 86 Buttons A: FF Buttons B: 00

    Buf: 00C0EF58FFB1000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0E Twist: 58 Slider: B1 Buttons A: FF Buttons B: 00

    Buf: 00C0EF58FFDC000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0E Twist: 58 Slider: DC Buttons A: FF Buttons B: 00

    Buf: 00C0EF58FFFF000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0E Twist: 58 Slider: FF Buttons A: FF Buttons B: 00

    Buf: 00C0FF58FFFF000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0F Twist: 58 Slider: FF Buttons A: FF Buttons B: 00

    LEFT PEDDAL
    : 0000 Y: 03F0 Hat Switch: 0F Twist: 58 Slider: 31 Buttons A: FF Buttons B: 00

    Buf: 00C0FF58FF1F000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0F Twist: 58 Slider: 1F Buttons A: FF Buttons B: 00

    Buf: 00C0FF58FF11000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0F Twist: 58 Slider: 11 Buttons A: FF Buttons B: 00

    Buf: 00C0FF58FF06000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0F Twist: 58 Slider: 06 Buttons A: FF Buttons B: 00

    Buf: 00C0FF58FF00000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0F Twist: 58 Slider: 00 Buttons A: FF Buttons B: 00

    Buf: 00C0FF58FF08000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0F Twist: 58 Slider: 08 Buttons A: FF Buttons B: 00

    Buf: 00C0FF58FF3D000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0F Twist: 58 Slider: 3D Buttons A: FF Buttons B: 00

    Buf: 00C0FF58FF76000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0F Twist: 58 Slider: 76 Buttons A: FF Buttons B: 00

    Buf: 00C0FF58FFB0000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0F Twist: 58 Slider: B0 Buttons A: FF Buttons B: 00

    Buf: 00C0FF58FFE6000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0F Twist: 58 Slider: E6 Buttons A: FF Buttons B: 00

    Buf: 00C0FF58FFFF000000000000000000000000
    X: 0000 Y: 03F0 Hat Switch: 0F Twist: 58 Slider: FF Buttons A: FF Buttons B: 00

  • Noor Hasan

    I am very much noob in USB Sheild using. I want to control a servo motor with Logitech extream 3d pro Joystick. I would be very glad if i found a simple sample code to control a servo motor with USB Shield 2.0 and Arduino. Please…. Help me.

Leave a Reply

  

  

  



You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">