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 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.
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 !!!
sorry i mean usbhidbootmouse examples
Works like charm… nice work.
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?
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);
}
This will work; the better way to do it is to use callbacks directly.
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)
Maybe it doesn’t support boot protocol? Can you post configuration descriptor as well?
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.
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 (0x00) | Class (0x20) | Interface (0x01), Set Report (0x09), Report Type (Feature 0x03) – Report ID (0xF4), Host to device (0x00) – Endpoint 0 (0x00), data, dataoffset, datalength
raw.SendSetupTransfer(0x21, 0x09, 0x03F4, 0x0000, buf, 0x00, 0x04);
}
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
hi,
I have to transmit data from host –> device to control a device
for that we have to pass following things in “send report function”,
Report ID
Report type
Report length
What are these and how to find it.
I am very new in USBH.
Thanks for help..
Krunal
Check out the PS3USB library at this line: https://github.com/felis/USB_Host_Shield_2.0/blob/master/PS3USB.cpp#L422.
All this information can be found in report descriptor.
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
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] = { 0x42, 0x0c, 0x00, 0x00 };
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
Thanks for sharing!
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(0x81 ,…. the rcode comes backs as DB which is USB_ERROR_EP_NOT_FOUND_IN_TBL.
Any clues?
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
I found the secret setReport command documented here:
http://www.circuitsathome.com/mcu/programming/ps3-and-wiimote-game-controllers-on-the-arduino-host-shield-part-2
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.
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
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.
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
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.
Thank you, it works!
Can you tell me, what this if for which seems not to be required in my case?
buttons <> 4);
buttons <> 4);
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.
Can you tell me, how to manage this?
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!!
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
For media keys, you need to use report protocol. If you init you keyboard as boot device media keys won’t be visible in the descriptor.
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:
delay( 200 );
next_time = millis() + 5000;
Keyboard2.SetReportParser(0, (HIDReportParser*)&Prs);
Note that I initialised the keyboard object as Keyboard2 becoz Keyboard is a reserved object in the Leonardo due to its built-in HID capabilities.
Thanks in advanced!
Have you tried this sketch already? -> https://github.com/felis/USB_Host_Shield_2.0/blob/master/examples/HID/USBHID_desc/USBHID_desc.pde
Yes I did. The output is as per what I posted up there. The media keys did not generate a “report” but the other keys worked fine.
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!
Make sure you read report correctly. Joysticks are all different, a sketch reading report of one particular joystick won’t necessarily work with another.
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!
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
If you know your report just change the parser. Here’s another article on the topic -> https://www.circuitsathome.com/mcu/using-logitech-extreme-3d-pro-joystick-with-arduino-hid-library
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!
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
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.
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.
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.
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.
The callbacks are not called from the
loop()
. They are called fromPoll()
, which is called fromUsb.Task()
.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.
Are you sure that you are running the newest code from Github: https://github.com/felis/USB_Host_Shield_2.0? This error should have been fixed several month ago.
Please post your code somewhere if it still doesn’t work, then I will take a look at it.
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.
Please post your code and the full error output.
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?
Look at boot keyboard example. Output report is written using
SetReport()
.The only reference to
SetReport()
that I can find in the current version of the USB Host Shield library is inhidboot.cpp
:return (hid->SetReport(0, 0/*hid->GetIface()*/, 2, 0, 1, &kbdLockingKeys.bLeds));
When I use the function in my own code it does return 0 (as expected) but the LED does not turn on. Some more info on
SetReport()
could be helpful.You may want to start with USBHIDBootKbd example -> https://github.com/felis/USB_Host_Shield_2.0/tree/master/examples/HID/USBHIDBootKbd to get the working example. What you’re passing to the function is important too. If your device is not a boot keyboard then you also need to know report number and the format of the report.
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.
Start with report count and report size. You have one 4-bit field, then four one bit fields. This is the first byte. Then you have 2 fields 10 bits each and 6 fields one bit each. 10 bit field are axes, one bit fields are buttons. Output your report in hex, operate the joystick and see what changes in the report. Also, since you have fields crossing byte boundary, look at this article -> https://www.circuitsathome.com/mcu/using-logitech-extreme-3d-pro-joystick-with-arduino-hid-library
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.
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.
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 scan
Buf: 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
Take a look at this article for the alternative approach to report parsing -> https://www.circuitsathome.com/mcu/using-logitech-extreme-3d-pro-joystick-with-arduino-hid-library
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.
hello,
i am trying to use a snyderphonic manta capacitive keyboard (http://www.snyderphonics.com/products.htm) with your shield.
i outputs a large collection of key values (only when a key is pressured). each key also has a remote controllable led.
with a modified USBHIDJoystick patch i already managed to grab the key values with
struct GamePadEventData {
int8_t M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11; // … more to come
};
and
void JoystickEvents::OnGamePadChanged(const GamePadEventData *evt) {
Serial.print(“”);
Serial.print(evt->M1);
Serial.print(“\t “);
Serial.print(evt->M2);
Serial.print(“\t “);
Serial.print(evt->M3);
Serial.print(“\t “);
Serial.print(evt->M4);
Serial.print(“\t “);
Serial.print(evt->M5);
Serial.print(“\t “);
Serial.print(evt->M6);
Serial.print(“\t “);
Serial.print(evt->M7);
//…
}
the values are 8bit signed with a range of -127 – 127
if i unsign them to 0-255 the default value is 127.
any idea where to change this to zero ?
and any idea how to set the leds ?
this is the descriptor:
Start
0000: 06 00 FF 09 01 A1 01 09 02 A1 00 09 03 15 80 25
0010: 7F 75 50 95 01 91 02 09 04 15 80 25 7F 95 40 75
0020: 08 81 02 C0 C0
Usage Page Undef(00)
Usage
Collection Application
Usage
Collection Physical
Usage
Logical Min(80)
Logical Max(7F)
Report Size(50)
Report Count(01)
Output(00000010)
Usage
Logical Min(80)
Logical Max(7F)
Report Count(40)
Report Size(08)
Input(00000010)
End Collection
End Collection
(00)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(7F)(7F)(7F)(7F)(00)(00)(00)(00)(00)(00)(00)
thanks a lot in advance for helping
This article explains HID reports -> http://www.circuitsathome.com/mcu/usb/visualizing-hid-device-reports-and-report-descriptors
I don’t understand what you meant by “change this to zero”. Input reports can’t be changed, you can only interpret them.
thanks for your very fast reply.
> 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 can’t answer this, you need to ask the manufacturer as to why they decided to format the output this way.
(00)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(80)(7F)(7F)(7F)(7F)(00)(00)(00)(00)(00)(00)(00)
all the 80s are keys. the 7F values are two 10bit sliders. the 00s i dont know.
how could / should i parse this array (?) of values ?
hi oleg,
thanks a lot for your help.
receiving parcing the values of my usb device works very well !
now i would like to set some leds of the device.
i sniffed the 10byte code i need to send to the device with “Device Monitoring Studio” but i am puzzeld how to send the code (0xa) ?
it looks like this:
Send 0xa bytes to the device
00 00 00 00 00 00 00 01 01 20
thanks for helping again !
Hi Oleg,
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:
Start
0000: 05 01 09 04 A1 01 09 01 A1 00 09 30 09 31 15 00
0010: 26 FF 03 75 0A 95 02 81 02 75 02 95 01 81 01 09
0020: 35 15 00 26 FF 03 75 0A 95 01 81 02 09 32 09 33
0030: 09 34 09 36 15 00 26 FF 00 75 08 95 04 81 02 05
0040: 09 19 01 29 27 15 00 25 01 95 27 75 01 81 02 75
0050: 05 95 01 81 01 05 01 09 39 15 01 25 08 35 00 46
0060: 3B 01 66 14 00 75 04 95 01 81 42 05 05 09 24 09
0070: 26 15 00 25 0F 75 04 95 02 81 02 C0 C0
Usage Page Gen Desktop Ctrls(01)
Usage Game Pad
Collection Application
Usage Pointer
Collection Physical
Usage X
Usage Y
Logical Min(00)
Logical Max(FF03)
Report Size(0A)
Report Count(02)
Input(00000010)
Report Size(02)
Report Count(01)
Input(00000001)
Usage Rz
Logical Min(00)
Logical Max(FF03)
Report Size(0A)
Report Count(01)
Input(00000010)
Usage Z
Usage Rx
Usage Ry
Usage Slider
Logical Min(00)
Logical Max(FF00)
Report Size(08)
Report Count(04)
Input(00000010)
Usage Page Button(09)
Usage Min(01)
Usage Max(27)
Logical Min(00)
Logical Max(01)
Report Count(27)
Report Size(01)
Input(00000010)
Report Size(05)
Report Count(01)
Input(00000001)
Usage Page Gen Desktop Ctrls(01)
Usage Hat Switch
Logical Min(01)
Logical Max(08)
Physical Min(00)
Physical Max(3B01)
Unit(1400)
Report Size(04)
Report Count(01)
Input(01000010)
Usage Page Game Ctrls(05)
Usage Move Right/Left
Usage Move Up/Down
Logical Min(00)
Logical Max(0F)
Report Size(04)
Report Count(02)
Input(00000010)
End Collection
End CollectionStart
and after pressingt the reset button i get:
0000: 05 01 09 04 A1 01 09 01 A1 00 09 30 09 31 15 00
0010: 26 FF 03 75 0A 95 02 81 02 75 02 95 01 81 01 09
0020: 35 15 00 26 FF 03 75 0A 95 01 81 02 09 32 09 33
0030: 09 34 09 36 15 00 26 FF 00 75 08 95 04 81 02 05
0040: 09 19 01 29 27 15 00 25 01 95 27 75 01 81 02 75
0050: 05 95 01 81 01 05 01 09 39 15 01 25 08 35 00 46
0060: 3B 01 66 14 00 75 04 95 01 81 42 05 05 09 24 09
0070: 26 15 00 25 0F 75 04 95 02 81 02 C0 C0
Usage Page Gen Desktop Ctrls(01)
Usage Game Pad
Collection Application
Usage Pointer
Collection Physical
Usage X
Usage Y
Logical Min(00)
Logical Max(FF03)
Report Size(0A)
Report Count(02)
Input(00000010)
Report Size(02)
Report Count(01)
Input(00000001)
Usage Rz
Logical Min(00)
Logical Max(FF03)
Report Size(0A)
Report Count(01)
Input(00000010)
Usage Z
Usage Rx
Usage Ry
Usage Slider
Logical Min(00)
Logical Max(FF00)
Report Size(08)
Report Count(04)
Input(00000010)
Usage Page Button(09)
Usage Min(01)
Usage Max(27)
Logical Min(00)
Logical Max(01)
Report Count(27)
Report Size(01)
Input(00000010)
Report Size(05)
Report Count(01)
Input(00000001)
Usage Page Gen Desktop Ctrls(01)
Usage Hat Switch
Logical Min(01)
Logical Max(08)
Physical Min(00)
Physical Max(3B01)
Unit(1400)
Report Size(04)
Report Count(01)
Input(01000010)
Usage Page Game Ctrls(05)
Usage Move Right/Left
Usage Move Up/Down
Logical Min(00)
Logical Max(0F)
Report Size(04)
Report Count(02)
Input(00000010)
End Collection
End Collection Game Pad Pointer X Y(02)(D7)
(00)
Rz(00)
Z Rx Ry Slider(3E)(54)(FF)(9E)
Btn0001
(00) Btn0002
(00) Btn0003
(00) Btn0004
(00) Btn0005
(00) Btn0006
(00) Btn0007
(00) Btn0008
(00) Btn0009
(00) Btn000A
(01) Btn000B
(01) Btn000C
(00) Btn000D
(01) Btn000E
(01) Btn000F
(01) 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
(01) Btn0021
(00) Btn0022
(01) Btn0023
(01) Btn0024
(01) Btn0025
(01) Btn0026
(01) Btn0027
(00)
(00)
Hat Switch(05)
Move Right/Left Move Up/Down(0F)(0F)
If I read the items about the report parser I don’t realy undertsnad what’s happening.
Do you know someone who succeeded in getting the x52 running and is there a sketch available?
Regards,
Jarco
What you see is the report descriptor, it will always be printed. In order to see data you need to generate it. Have you tried to move the joystick?
Thanks for your quick response.
Indeed after seeing start on the screen I moved the joystick in many directions and pushed many buttons unfortunately nothing appears on the screen.
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?
Thanks!!
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!
Multi-report parsing
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.
HID Report Descriptor
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x08, // Usage (Multi-axis Controller)
0xA1, 0x01, // Collection (Application)
0xA1, 0x00, // Collection (Physical)
0x85, 0x01, // Report ID (1)
0x16, 0x0C, 0xFE, // Logical Minimum (65036)
0x26, 0xF4, 0x01, // Logical Maximum (500)
0x36, 0x00, 0x80, // Physical Minimum (32768)
0x46, 0xFF, 0x7F, // Physical Maximum (32767)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x32, // Usage (Z)
0x75, 0x10, // Report Size (16)
0x95, 0x03, // Report Count (3)
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0xA1, 0x00, // Collection (Physical)
0x85, 0x02, // Report ID (2)
0x09, 0x33, // Usage (Rx)
0x09, 0x34, // Usage (Ry)
0x09, 0x35, // Usage (Rz)
0x75, 0x10, // Report Size (16)
0x95, 0x03, // Report Count (3)
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0xA1, 0x02, // Collection (Logical)
0x85, 0x03, // Report ID (3)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x0D, // Usage Maximum (0x0D)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x35, 0x00, // Physical Minimum (0)
0x45, 0x01, // Physical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x0D, // Report Count (13)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x03, // Report Count (3)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
report struct
Note: multi-report buffers start with a uint8 report number, and then data. Since data are int16, need to pack the struct for the alignment to work out properly.
// use a single struct to interpret all report types -- must force packed, else int16 will be on 16-bit boundaries
struct __attribute__ ((packed)) SBEventData
{
uint8_t id;
union {
struct { //report 01, 6 bytes of data
int16_t x; // returns +/- 500. To scale to full 16 bit range: ((x + 500) x 13107 / 200 - 32768)
int16_t y;
int16_t z;
};
struct { //report 02, 6 bytes of data
int16_t rx;
int16_t ry;
int16_t rz;
};
struct { //report 03, only 2 bytes of data
uint16_t buttons; //bits 1-13 reserved for buttons (though there are only 12 buttons)
uint32_t notused; //undefined
};
};
};
Finally, I stored the state for each report type:
uint8_t oldPad[N_REPT][RPT_GAMEPAD_LEN]; //holds state for each of the three report types
And here are the parsing functions
void JoystickReportParser::Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf)
{
bool match = true;
uint8_t reportNo = buf[0];
// Checking if there are changes in report since the method was last called
for (uint8_t i=1; i<len; i++) {
if( buf[i] != oldPad[reportNo-1][i] ) {
match = false;
break;
}
}
/*
//display raw report data -----------------
if (!match && reportNo==2) {
Serial.print(" Len: ");
PrintHex(len, 0x80);
Serial.print(": ");
for (uint8_t i=1; i<len; i++)
{
Serial.print(" ");
PrintHex(buf[i],0x80);
}
Serial.println("");
}
// --------------------------------
*/
// Calling Game Pad event handler
if (!match && joyEvents) {
joyEvents->OnGamePadChanged((const SBEventData*)buf);
for (uint8_t i=1; iid) {
case 1: {
Serial.print(" X: ");
Serial.print(evt->x);
Serial.print(" Y: ");
Serial.print(evt->y);
Serial.print(" Z: ");
Serial.print(evt->z);
Serial.println("");
break;
}
case 2: {
Serial.print("Rx: ");
Serial.print(evt->rx);
Serial.print(" Ry: ");
Serial.print(evt->ry);
Serial.print(" Rz: ");
Serial.print(evt->rz);
Serial.println("");
break;
}
case 3: {
Serial.print(" Buttons: ");
Serial.print(evt->buttons,BIN);
Serial.println("");
break;
}
}
}
Thanks again for all of the excellent code and examples!
The formatting omitted a few lines:
...
// Calling Game Pad event handler
if (!match && joyEvents) {
joyEvents->OnGamePadChanged((const SBEventData*)buf);
* for (uint8_t i=1; i<len; i++) oldPad[reportNo-1][i] = buf[i];
* }
*}
*
*void JoystickEvents::OnGamePadChanged(const SBEventData *evt)
*{
*
* switch (evt->id) {
case 01: {
Serial.print(" X: ");
...