[{"element_type":"alert","content":"Quick summary: There is a tool called Waratah that helps creating report descriptors from TOML files (https://github.com/microsoft/hidtools/tree/main)","metadata":{"class":"element row-fluid alert-element build-alert alert-info","alert_type":"info"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003e\u003cstrong\u003eWhy Do You Want A HID Report Descriptor?\u003c/strong\u003e\u003c/h2\u003e\n\u003cp\u003eThe USB specification includes a section on Human Interface Devices (HID). These devices range from keyboards, mice, joysticks, audio controls to medical controls, eye tracking and LED lighting. CircuitPython has support for USB HID with built-in defaults for a keyboard, mouse and consumer control and associated libraries. Dan Halbert \u003ca href=\"https://learn.adafruit.com/custom-hid-devices-in-circuitpython/overview\" target=\"_blank\"\u003ehas an excellent guide\u003c/a\u003e to get started with this.\u003c/p\u003e\n\u003cp\u003eA USB report descriptor tells the host machine the how to talk to your device. But what if you want to communicate to a device that no one else has written a report descriptor for? Perhaps you are building a new joystick, LED indicator or medical ultrasound device. Then you will have to write your own report descriptor.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch3\u003e\u003cstrong\u003eSimple Descriptor Example\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003eInitially when you look at report descriptors they seem complicated but it is best to think of them as a description of one or more reports that can be sent to or from your device. Each report gives the current state you wish to communicate. For example a joystick report may give the X and Y axis, a throttle value and if any of a dozen buttons are pressed. The joystick may also have a second report defined that allows the host machine to light up one or more indicator lights.\u003c/p\u003e\n\u003cp\u003eBelow is an example report descriptor for a joystick that has 5 buttons. This descriptor only defines one report with each button taking up 1 bit, and 3 padding bits. While this is readable it is hard to create. There is an easier way.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"JOYSTICK_REPORT_DESCRIPTOR = bytes((\n    0x05, 0x01,    # UsagePage(Generic Desktop[0x0001])\n    0x09, 0x04,    # UsageId(Joystick[0x0004])\n    0xA1, 0x01,    # Collection(Application)\n    0x85, 0x01,    #     ReportId(1)\n    0x05, 0x09,    #     UsagePage(Button[0x0009])\n    0x19, 0x01,    #     UsageIdMin(Button 1[0x0001])\n    0x29, 0x05,    #     UsageIdMax(Button 5[0x0005])\n    0x15, 0x00,    #     LogicalMinimum(0)\n    0x25, 0x01,    #     LogicalMaximum(1)\n    0x95, 0x05,    #     ReportCount(5)\n    0x75, 0x01,    #     ReportSize(1)\n    0x81, 0x02,    #     Input(Data, Variable, Absolute, NoWrap, Linear, PreferredState, NoNullPosition, BitField)\n    0x95, 0x01,    #     ReportCount(1)\n    0x75, 0x03,    #     ReportSize(3)\n    0x81, 0x03,    #     Input(Constant, Variable, Absolute, NoWrap, Linear, PreferredState, NoNullPosition, BitField)\n    0xC0,          # EndCollection()\n))","metadata":{"language":"python","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n        \u003ch2\u003e\u003cstrong\u003eUSB HID Specification\u003c/strong\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cspan\u003eThe \u003c/span\u003e\u003ca href=\"https://www.usb.org/sites/default/files/hid1_11.pdf%C2%A0\" target=\"_blank\"\u003eUSB HID\u003c/a\u003e\u003cspan\u003e specification is large, there are a lot of possible interface devices. The area we are most interested in is the \u003c/span\u003e\u003ca href=\"https://www.usb.org/sites/default/files/hut1_4.pdf\" target=\"_blank\"\u003eHID Usage Tables\u003c/a\u003e\u003cspan\u003e. HID usages cover the parts of your device from generic to the detailed elements and tell you the predefined usage IDs that are used.\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003eFollowing those documents you could make a descriptor by hand, but it is tedious. Luckily there is a collection of \u003ca href=\"https://github.com/microsoft/hidtools/tree/main\" target=\"_blank\"\u003eMicrosoft HID Tools\u003c/a\u003e that contains \u003cstrong\u003eWaratah\u003c/strong\u003e. This tool takes a TOML formatted report descriptor and converts it to the hex required.\u003c/p\u003e\n      \n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n        \u003ch2\u003e\u003cstrong\u003eWaratah\u003c/strong\u003e\u003c/h2\u003e\n\u003cp\u003eThe \u003ca href=\"https://github.com/microsoft/hidtools/tree/main/Waratah/Samples\" target=\"_blank\"\u003eWaratah repository\u003c/a\u003e has both samples and a wiki to help teach about creating descriptors, the following is just an overview.\u003c/p\u003e\n\u003ch3\u003e\u003cstrong\u003eReport Descriptor TOML Example\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003e[[applicationCollection]]\u003cbr\u003eusage = ['Generic Desktop', 'Joystick']\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eThe start of the file defines the type of device you are describing reports for. This is where the usage tables are your guide. The first element of usage defines the page while the second declares a device from that page. This guides the host operating system as to what type of device you are describing.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e[[applicationCollection.inputReport]]\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eNext you start declaring the reports your device can send or receive. In this case there is only one report.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e[[applicationCollection.inputReport.physicalCollection]]\u003c/code\u003e\u003cbr\u003e\u003ccode\u003eusage = ['Generic Desktop', 'Pointer']\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eFor a joystick you may have composite items, such as where the stick is pointing. So you declare a collection of items.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e[[applicationCollection.inputReport.physicalCollection.variableItem]]\u003c/code\u003e\u003cbr\u003e\u003ccode\u003eusage = ['Generic Desktop', 'X']\u003c/code\u003e\u003cbr\u003e\u003ccode\u003esizeInBits = 8\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e[[applicationCollection.inputReport.physicalCollection.variableItem]]\u003c/code\u003e\u003cbr\u003e\u003ccode\u003eusage = ['Generic Desktop', 'Y']\u003c/code\u003e\u003cbr\u003e\u003ccode\u003esizeInBits = 8\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eAfter declaring the collection you need to define the actual items. You will notice the same pattern for usage, defining the page and then the detailed item. The usage tables continue to be your guide. Here both the X and Y variables are declared as 8 bits.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e[[applicationCollection.inputReport.variableItem]]\u003c/code\u003e\u003cbr\u003e\u003ccode\u003eusageRange = ['Button', 'Button 1', 'Button 4']\u003c/code\u003e\u003cbr\u003e\u003ccode\u003elogicalValueRange = [0, 1]\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eA joystick needs buttons, so finally 4 buttons are added to the report. This is still part of the same report as the pointer. Each button takes 1 bit.\u003c/p\u003e\n\u003cp\u003eAfter the TOML file is finished you run it through waratah and it will produce a C .h file, but that is easily converted to python or any other language you require.\u003c/p\u003e\n      \n\n","metadata":{}},{"element_type":"code","content":"[[applicationCollection]]\nusage = ['Generic Desktop', 'Joystick']\n\n    [[applicationCollection.inputReport]]\n\t\n        [[applicationCollection.inputReport.physicalCollection]]\n        usage = ['Generic Desktop', 'Pointer']\n\n            [[applicationCollection.inputReport.physicalCollection.variableItem]]\n            usage = ['Generic Desktop', 'X']\n            sizeInBits = 8\n\n            [[applicationCollection.inputReport.physicalCollection.variableItem]]\n            usage = ['Generic Desktop', 'Y']\n            sizeInBits = 8\n\t\t\t\n        [[applicationCollection.inputReport.variableItem]]\n        usageRange = ['Button', 'Button 1', 'Button 5']\n        logicalValueRange = [0, 1]","metadata":{"language":"auto","linenums":false}},{"element_type":"code","content":"// AUTO-GENERATED by WaratahCmd.exe (https://github.com/microsoft/hidtools)\n\n// HID Usage Tables: 1.4.0\n// Descriptor size: 50 (bytes)\n// +----------+-------+-------------------+\n// | ReportId | Kind  | ReportSizeInBytes |\n// +----------+-------+-------------------+\n// |        1 | Input |                 3 |\n// +----------+-------+-------------------+\nstatic const uint8_t hidReportDescriptor[] = \n{\n    0x05, 0x01,    // UsagePage(Generic Desktop[0x0001])\n    0x09, 0x04,    // UsageId(Joystick[0x0004])\n    0xA1, 0x01,    // Collection(Application)\n    0x85, 0x01,    //     ReportId(1)\n    0x09, 0x01,    //     UsageId(Pointer[0x0001])\n    0xA1, 0x00,    //     Collection(Physical)\n    0x09, 0x30,    //         UsageId(X[0x0030])\n    0x09, 0x31,    //         UsageId(Y[0x0031])\n    0x15, 0x80,    //         LogicalMinimum(-128)\n    0x25, 0x7F,    //         LogicalMaximum(127)\n    0x95, 0x02,    //         ReportCount(2)\n    0x75, 0x08,    //         ReportSize(8)\n    0x81, 0x02,    //         Input(Data, Variable, Absolute, NoWrap, Linear, PreferredState, NoNullPosition, BitField)\n    0xC0,          //     EndCollection()\n    0x05, 0x09,    //     UsagePage(Button[0x0009])\n    0x19, 0x01,    //     UsageIdMin(Button 1[0x0001])\n    0x29, 0x05,    //     UsageIdMax(Button 5[0x0005])\n    0x15, 0x00,    //     LogicalMinimum(0)\n    0x25, 0x01,    //     LogicalMaximum(1)\n    0x95, 0x05,    //     ReportCount(5)\n    0x75, 0x01,    //     ReportSize(1)\n    0x81, 0x02,    //     Input(Data, Variable, Absolute, NoWrap, Linear, PreferredState, NoNullPosition, BitField)\n    0x95, 0x01,    //     ReportCount(1)\n    0x75, 0x03,    //     ReportSize(3)\n    0x81, 0x03,    //     Input(Constant, Variable, Absolute, NoWrap, Linear, PreferredState, NoNullPosition, BitField)\n    0xC0,          // EndCollection()\n};","metadata":{"language":"cpp","linenums":false}},{"element_type":"text","content":"\n  \n        \u003cp\u003eAnd that is it, you have a report descriptor. But what do you send for the report?\u003c/p\u003e\n\u003cp\u003eIf you look at the generated code you will see ReportSize and ReportCount lines. The size is in bits and the count is how many that size appears in a row. For example above you see a count of 2 with a size of 8. So 2 bytes (16 bits). Later you see a report a count of 5 with a size of 1 (5 bits) followed by a count of 1 with a size of 3 (3 bits).\u003c/p\u003e\n\u003cp\u003eFrom there you can extrapolate your report is:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e8 bits - X\u003c/li\u003e\n\u003cli\u003e8 bits - Y\u003c/li\u003e\n\u003cli\u003e5 bits - buttons\u003c/li\u003e\n\u003cli\u003e3 bits - padding\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eHow you create the structure to send the report will depend on the language you are using.\u003c/p\u003e\n\u003cp\u003e\u0026nbsp;\u003c/p\u003e\n      \n","metadata":{}},{"element_type":"text","content":"\n        \u003ch2\u003e\u003cstrong\u003eWhat's Next?\u003c/strong\u003e\u003c/h2\u003e\n\u003cp\u003eThis is a brief view into creating your own report. The references are long but worth flipping through to get an idea of what is possible. The above example only handles an input report allowing the device to communicate with the host computer. There are also output reports to control things like LEDs in a keyboard.\u003c/p\u003e\n\u003cp\u003eAs well the samples and wiki for Waratah provides valuable information to help create your own descriptors without having to build the hex by hand.\u003c/p\u003e\n      ","metadata":{}}]