Developing Arduino code for HID Joystick

By

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:

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.

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.

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.

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.

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 0x00, 0x80 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.

79 comments to Developing Arduino code for HID Joystick

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 !!!

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:

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)

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.

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(0x81 ,…. the rcode comes backs as DB which is USB_ERROR_EP_NOT_FOUND_IN_TBL.

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?

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:

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.

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.

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.

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 0x07 which is standard keyboard table. Anyone can shed some light into this? Greatly appreciated, thanks!!

oleg, appreciate your kind reply. I’m quite new to this Arduino programming and hope you can give me a bit more info/direction on how I should initialise it in report protocol mode. The only code that I used to initialise the keyboard is based off the keyboard example code in the USB Host Shield library as follows:

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

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!

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???

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

Thanks for the answer. Its 16 buttons = 0x10, 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.

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.

I’m nearly done with my project. But I have to change the USB Type of my Teensy3.0 to something that doesn’t have a Serial Port and my code does not compile since the host shield library seems to rely on a Serial output (printhex.h). I just commmented out every line where it is used, but this is not a very nice and clean solution.

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.

};
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.

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.

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.

> I don’t understand what you meant by “change this to zero”. Input reports can’t be changed, you can only interpret them.
when i press a key it outputs the pressure (0-240 with the original interface). here it outputs a value between -128 and 50 (Logical Min(80), Logical Max(7F)
). the default / not pressed value is 0x80 or uint8_t 128 because the zero is shifted i guess.
i wonder why its not 0 – 255 ?
my work around is now i just add 128 to get out of the negative: Serial.print( (((int8_t)evt->M2) + (int8_t)127) );

I have a Saitek X52 Pro Joy stick. I have tried to get this working and reading these oages but I can’t getting working. I have ran the HID Joystick example on both an mega 2560 and an Uno with USB Host Shield 2. All I get is Start and then nothing.

If I run the desc example on the mega I get start and then nothing on the UNo I get the folowing:

Hello, I’m trying to implement a hardware with an Arduino Board that needs to be recognized as a game controller. Does the USB Host Shield 2.0 libray allow Arduino Uno, Arduino Mega or Nano to be recognized as a game controller? Or will I have to use the Leonardo board?

Hello Oleg, thanks for all your work.
Can you give us a hint on how to proceed if we want to connect two joysticks(say a joystick and throttle or rudder pedals) via a hub and want to parse info from all?
Take care!

Hi, not sure if this would be helpful to anyone, but here is code for a device that sends multiple input reports. This is an old Spaceball 5000 that has a report for X,Y,Z, a second report for Rx,Ry,Rz, and a third report for the buttons–rather than wrapping them all in a single report as the devices I’ve seen discussed here. I took the le3dp code as a starting point, with changes as below.