Human Interface Device class of USB devices has a unique property – a report descriptor which contains information about data that device is sending to the host as well as data that can be sent to the device. This property allows for variety of devices – keyboards, mice, joysticks, digital scales, uninterruptible power sources, GPS receivers, and even toy missile launchers to belong to a single class – HID. A vendor just needs to pick a usage table which contains controls similar to vendor’s device – every OS has a generic support for HID devices so in most cases specific device driver is not necessary. The report descriptor again makes this possible – it contains definitions or report fields therefore a generic parser can process reports from any arbitrary HID device. However, this generic parser will take too much space on small microcontroller systems such as Arduino due to the amount of constants that needs to be present in the program code.
It shall be noted that a HID report itself is a simple structure of fixed fields and when this structure is known a very lightweight parser can easily be developed. HID development in legacy USB library has stopped at this point; I thought people will just take a look at the spec and write report parser for their device. It soon became evident that very few are actually willing to do this and in rev.2.0 of the library the HID report parsing is semi-automatic – a report descriptor has to be analyzed first using USBHID_desc utility presented in this article and then actual reports for the device can be parsed using library facilities (an article about coding report parsing is here).
Even though report descriptor parsing is moved to a separate utility, it is still a heavy load for Arduino. The code, which at present only contains strings for keyboards, mice and joysticks, won’t fit into 32K Arduino – a Mega or 2560 is necessary. A screenshot of descriptor report output of a typical mouse can be seen on title picture. The explanation follows.
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
Usage Page Gen Desktop Ctrls(01) Usage Mouse Collection Application Usage Pointer Collection Physical Usage Page Button(09) Usage Min(01) Usage Max(03) Logical Min(00) Logical Max(01) Report Size(01) Report Count(03) Input(00000010) Report Size(05) Report Count(01) Input(00000001) Usage Page Gen Desktop Ctrls(01) Usage X Usage Y Usage Wheel Logical Min(81) Logical Max(7F) Report Size(08) Report Count(03) Input(00000110) End Collection End Collection
Mouse buttons are defined first. We can see that it is input item (line 13). They are buttons (line 6), which is also confirmed by logical min and max (line 9,10) – only two values, 0 and 1, are possible. The report size is one bit (line 11) and number of buttons is 3 (line 12).
The next item (line 16) is a filler for the rest of the first byte. It specifies one (line 15) 5-bit (line 14) report with no minimums or maximums.
The last report (line 25) specifies X-axis, Y-axis and wheel (lines 18-20). 3 reports (line 24) has been specified, one byte each (line 23).
That’s all we need to know about the device in order to communicate to it. For the curious, detailed explanation of these and all other fields in this report can be found in USB specifications listed on usb.org HID page. Device Class Definition and HID Usage Tables are two most important docs.
USBHID_desc also can parse HID reports on the fly. In order to generate some reports I’m going to move my mouse and observe the results. Here’s what happens when I move it vertically:
1 2 3 4
Buf: 00000100 Mouse Pointer Btn1(00) Btn2(00) Btn3(00) (00) X Y Wheel(00)(01)(00)
Line 1 shows the raw report, 4 bytes in length. Line 2 shows state of the buttons, none of them pressed. Line 3 shows state of the “filler” report; since it doesn’t have usage page assosciated with it, it doesn’t have a name. It never changes, either. Line 4 shows state of X,Y and wheel, the Y axis having moved 1 step. We can also find it easily in the raw report in the third byte.
Next output shows how the report looks like when mouse is moved diagonally. We can see that both X and Y are changed and can check that value for X-axis is output in the second byte of the report.
Buf: 00FDAB00 Mouse Pointer Btn1(00) Btn2(00) Btn3(00) (00) X Y Wheel(FD)(AB)(00)
The next output demonstrates reports for wheel and button presses. It can be seen that buttons are stored in 3 low bits of the first byte of the report.
Buf: 00000002 Mouse Pointer Btn1(00) Btn2(00) Btn3(00) (00) X Y Wheel(00)(00)(02) Buf: 01000000 Mouse Pointer Btn1(01) Btn2(00) Btn3(00) (00) X Y Wheel(00)(00)(00) Buf: 00000000 Mouse Pointer Btn1(00) Btn2(00) Btn3(00) (00) X Y Wheel(00)(00)(00) Buf: 04000000 Mouse Pointer Btn1(00) Btn2(00) Btn3(01) (00) X Y Wheel(00)(00)(00) Buf: 00000000 Mouse Pointer Btn1(00) Btn2(00) Btn3(00) (00) X Y Wheel(00)(00)(00) Buf: 02000000 Mouse Pointer Btn1(00) Btn2(01) Btn3(00) (00) X Y Wheel(00)(00)(00) Buf: 00000000 Mouse Pointer Btn1(00) Btn2(00) Btn3(00) (00) X Y Wheel(00)(00)(00)
The last output demonstrates the behaviour of USBHID_desc encountering unsupported device (digital scale). It tries to parse the report descriptor but doesn’t have enough information for human-readable representation of the report. As a result, only raw reports are output. The first report shows empty scale, the second – scale loaded with Arduino Mega/USB Host Shield combo on which USBHID_desc is running (~68g or 2.4oz).
Usage Page Scale(8D) Usage Collection Application Usage Collection Logical Report Id(01) Report Size(08) Report Count(01) Logical Min(01) Logical Max(0C) Usage Collection Logical Usage Min(21) Usage Max(2A) Feature(00000000) End Collection Usage Collection Logical Usage Min(51) Usage Max(5C) Feature(00000000) End Collection End Collection Usage Collection Logical Report Id(02) Report Size(01) Report Count(02) Logical Min(00) Logical Max(01) Usage Min(80) Usage Max(81) Output(00000010) Report Size(06) Report Count(01) Output(00000011) End Collection Usage Collection Logical Report Id(03) Report Size(08) Logical Min(01) Usage Collection Logical Logical Max(08) Usage Min(71) Usage Max(78) Input(00000000) End Collection Usage Collection Logical Logical Max(0C) Usage Min(51) Usage Max(5C) Input(00000000) End Collection Logical Min(81) Logical Max(7F) Usage Input(00000010) Report Size(10) Logical Min(00) Logical Max(FFFF0000) Usage Input(00000010) End Collection Buf: 03040BFF0000 Buf: 03040BFF2100
USBHID_desc is a useful program designed to help with analyzing reports and report descriptors of HID devices using Arduino Mega or Mega 2560 board and USB Host Shield 2.0. By the end of this week I’m hoping to post an article demonstrating how to use the information – stay tuned!
No related posts.