This is HID over GATT Profile

Intro

Reports are HID communication. They could be input report (sent from device to host, such as key presses), output reports (sent from host to device, such as caps lock key on), feature report (read, write data, no clue what theyre for?> battery?).

They support boot vs report protocols. Boot protocols are meant to be simple and usable in BIOS, logins, etc, BEFORE AN OS LOADS. Meant to be super simple but lacks features.

Services

HID Service

UUID: 0x1812
Can be exposed multiple times (keyboard / mouse combo, but oftentimes its just one complex report descriptor)

This profile contains the following characteristics:

CharacteristicPurposeValueNotes
HID Information (0x2A4A)Basic info about HID (which version, coutnry code)4-bytes:

bcdHid (2 bytes): HID release number
bCountryCode (1 byte): Hardware country code
flags (1 byte): Bit 0 is set to 1 if device can send wake signal to host, Bit 1 is set to 1 if device will initiate bonding with a new host
Read
Report Map Characteristic (0x2A4B)Contains HID Report descriptor !!!Raw byte array of report descriptorRead

Contains the HID Report Descriptor defining device capabilities. Optionally uses External Report Reference Descriptor (0x2907).
HID Control Point Characteristic (0x2A4C)Host to send commands to HID0x00: Suspend
0x01: exit suspend
WriteWithoutResponse
Report Characteristic (0x2A4D)Used to exchange the input, output, feature reports ^220e7fRequires CCCD

Can have multiple instances. Idk maybe to split up reports across bandwidths?

Something about an option ” Report Reference Descriptor” (0x2908)
Protocol Mode Characteristic (0x2A4E)Used to switch from boot protocol to report protocol0x00 - boot (simlified)
0x01 report (idk)
Boot Keyboard Input Report Characteristic (0x2A22)
Boot Keyboard Output Report Characteristic (UUID: 0x2A32)
Boot Mouse Input Report Characteristic (UUID: 0x2A33)

Report Descriptors

Sequence of bytes that describe a device’s input and output capabilities.

Built off items. Items can be short or long. Short items have a 1-byte prefix (4 bits tag, 2 bits type, 2 bits size), and 0-4 data bytes. Long items are a bit more complex, used maybe only 0.01% of the time.

Three types of items: Main (for report fields or groups of report fields), Global (configuration that affects until subsequent changes), Local (affects only the next main item)

Main Items

The type is 00
For input, output, feature, consult section 6.2.2. of https://www.usb.org/sites/default/files/hid1_11.pdf. The type tells the parser how to interpret the data based on usage min / max, etc

TypePrefix bitsDescription
Input1000 00 xxDefines data fields sent from the HID device to the host
Output1001 00 xxDefines data fields sent from the host to the HID device
Feature1011 00 xxDefines data fields for config status
Collection1010 00 xxGroups related items into a unit. Essentially a “folder” to group data, and apply scopes (so local items can affect the entire collection and its children)
End Collection1100 00 xxMarks the end of a collection

Global Items

“Scoped” to collections, will be reverted. Sticky

TypePrefix bitsDescription
Usage page1000 00 xxSets the context for Usage items (Keyboard, keypad, generic desktop, etc)
Logical Min / Max0001 01 xx

0010 01 xx
Defines range of possible values. THe “value” of the data. Tells the host to accept this numerical range to expect
Report Size1001 01 xxDefines the size of each field (in bits)
Report COunt1001 01 xxSpecifies the number of fields described by the current item
Report ID1000 01 xxAssigns an ID to a report, allowing multiple reports of the same type (Input, output feature). This separates different report formats (a standard key that uses 8 bytes, a media key that uses only 2?)

Local Items

TypePrefix bitsDescription
Usage0000 10 xxDefines the SPECIFIC function of the next item within the current usage page (keys)
Usage Min / Max0001 10 xx

0010 10 xx
Defines range of possible values. The “name of the control”. Tells the host what the control is?

Usage Minimum and Usage Maximum assigns a series of Usage IDs to the data in the array or bitmap. Usage Minimum defines the starting ID and Usage Maximum defines the ending ID.
Example:
For a 3 button, mouse ID’s are assigned to Button1 (0x01), Button2 (0x02), Button3 (0x03)

Usage Minimum and Usage Maximum entries allow a series of similar items to be defined together when the usages are assigned to continuous numbers. Instead of defining 101 Usages separately for each key on the standard keyboard, a single usage can be defined with the maximum and minimum set to the maximum and minimum IDs.

Logical Minimum and Logical Maximum specify the range of values that the report can contain.
Example:

  1. Mouse that reports position value of -127 to 127 will have a logical maximum of 127 and logical minimum of -127
  2. Single button state that is either asserted or released will have a logical minimum of 0 and logical maximum of 1

Reports are “concated” together. So if i say i have a Report count(8), and a report count (16), then my final report will be 8 bits + 16 bits. Report IDs seperate that

https://usb.org/sites/default/files/hut1_5.pdf

Examples

Heres my boot keyboard:

0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
0x09, 0x06,        // Usage (Keyboard)
0xA1, 0x01,        // Collection (Application)
0x05, 0x07,        //   Usage Page (Kbrd/Keypad)
0x19, 0xE0,        //   Usage Minimum (0xE0)
0x29, 0xE7,        //   Usage Maximum (0xE7)
0x15, 0x00,        //   Logical Minimum (0)
0x25, 0x01,        //   Logical Maximum (1)
0x95, 0x08,        //   Report Count (8)
0x75, 0x01,        //   Report Size (1)
0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01,        //   Report Count (1)
0x75, 0x08,        //   Report Size (8)
0x81, 0x03,        //   Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x06,        //   Report Count (6)
0x75, 0x08,        //   Report Size (8)
0x15, 0x00,        //   Logical Minimum (0)
0x26, 0xFF, 0x00,  //   Logical Maximum (255)
0x05, 0x07,        //   Usage Page (Kbrd/Keypad)
0x19, 0x00,        //   Usage Minimum (0x00)
0x2A, 0xFF, 0x00,  //   Usage Maximum (0xFF)
0x81, 0x00,        //   Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x25, 0x01,        //   Logical Maximum (1)
0x95, 0x05,        //   Report Count (5)
0x75, 0x01,        //   Report Size (1)
0x05, 0x08,        //   Usage Page (LEDs)
0x19, 0x01,        //   Usage Minimum (Num Lock)
0x29, 0x05,        //   Usage Maximum (Kana)
0x91, 0x02,        //   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x95, 0x01,        //   Report Count (1)
0x75, 0x03,        //   Report Size (3)
0x91, 0x03,        //   Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0,              // End Collection

// 67 bytes

Heres my QMK, NKRO HID:

0x06, 0x01, 0x00,  // Usage Page (Generic Desktop Ctrls)
0x09, 0x80,        // Usage (Sys Control)
0xA1, 0x01,        // Collection (Application)
0x85, 0x01,        //   Report ID (1)
0x19, 0x81,        //   Usage Minimum (Sys Power Down)
0x29, 0x83,        //   Usage Maximum (Sys Wake Up)
0x15, 0x00,        //   Logical Minimum (0)
0x25, 0x01,        //   Logical Maximum (1)
0x95, 0x03,        //   Report Count (3)
0x75, 0x01,        //   Report Size (1)
0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01,        //   Report Count (1)
0x75, 0x05,        //   Report Size (5)
0x81, 0x01,        //   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              // End Collection
0x05, 0x0C,        // Usage Page (Consumer)
0x09, 0x01,        // Usage (Consumer Control)
0xA1, 0x01,        // Collection (Application)
0x85, 0x02,        //   Report ID (2)
0x19, 0x00,        //   Usage Minimum (Unassigned)
0x2A, 0xFF, 0x02,  //   Usage Maximum (0x02FF)
0x15, 0x00,        //   Logical Minimum (0)
0x26, 0xFF, 0x7F,  //   Logical Maximum (32767)
0x95, 0x01,        //   Report Count (1)
0x75, 0x10,        //   Report Size (16)
0x81, 0x00,        //   Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              // End Collection
0x06, 0x00, 0xFF,  // Usage Page (Vendor Defined 0xFF00)
0x09, 0x01,        // Usage (0x01)
0xA1, 0x01,        // Collection (Application)
0x85, 0x03,        //   Report ID (3)
0x15, 0x00,        //   Logical Minimum (0)
0x26, 0xFF, 0x00,  //   Logical Maximum (255)
0x09, 0x2F,        //   Usage (0x2F)
0x75, 0x08,        //   Report Size (8)
0x95, 0x03,        //   Report Count (3)
0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              // End Collection
0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
0x09, 0x06,        // Usage (Keyboard)
0xA1, 0x01,        // Collection (Application)
0x85, 0x04,        //   Report ID (4)
0x05, 0x07,        //   Usage Page (Kbrd/Keypad)
0x19, 0x04,        //   Usage Minimum (0x04)
0x29, 0x70,        //   Usage Maximum (0x70)
0x15, 0x00,        //   Logical Minimum (0)
0x25, 0x01,        //   Logical Maximum (1)
0x75, 0x01,        //   Report Size (1)
0x95, 0x78,        //   Report Count (120)
0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              // End Collection
0x06, 0x00, 0xFF,  // Usage Page (Vendor Defined 0xFF00)
0x09, 0x01,        // Usage (0x01)
0xA1, 0x01,        // Collection (Application)
0x85, 0x05,        //   Report ID (5)
0x15, 0x00,        //   Logical Minimum (0)
0x26, 0xFF, 0x00,  //   Logical Maximum (255)
0x19, 0x01,        //   Usage Minimum (0x01)
0x29, 0x02,        //   Usage Maximum (0x02)
0x75, 0x08,        //   Report Size (8)
0x95, 0x05,        //   Report Count (5)
0xB1, 0x02,        //   Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0,              // End Collection
0x06, 0x00, 0xFF,  // Usage Page (Vendor Defined 0xFF00)
0x09, 0x01,        // Usage (0x01)
0xA1, 0x01,        // Collection (Application)
0x85, 0x06,        //   Report ID (6)
0x15, 0x00,        //   Logical Minimum (0)
0x26, 0xFF, 0x00,  //   Logical Maximum (255)
0x19, 0x01,        //   Usage Minimum (0x01)
0x29, 0x02,        //   Usage Maximum (0x02)
0x75, 0x08,        //   Report Size (8)
0x96, 0x07, 0x04,  //   Report Count (1031)
0xB1, 0x02,        //   Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0,              // End Collection
0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
0x09, 0x02,        // Usage (Mouse)
0xA1, 0x01,        // Collection (Application)
0x85, 0x07,        //   Report ID (7)
0x09, 0x01,        //   Usage (Pointer)
0xA1, 0x00,        //   Collection (Physical)
0x05, 0x09,        //     Usage Page (Button)
0x15, 0x00,        //     Logical Minimum (0)
0x25, 0x01,        //     Logical Maximum (1)
0x19, 0x01,        //     Usage Minimum (0x01)
0x29, 0x05,        //     Usage Maximum (0x05)
0x75, 0x01,        //     Report Size (1)
0x95, 0x05,        //     Report Count (5)
0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x03,        //     Report Count (3)
0x81, 0x01,        //     Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01,        //     Usage Page (Generic Desktop Ctrls)
0x16, 0x00, 0x80,  //     Logical Minimum (-32768)
0x26, 0xFF, 0x7F,  //     Logical Maximum (32767)
0x09, 0x30,        //     Usage (X)
0x09, 0x31,        //     Usage (Y)
0x75, 0x10,        //     Report Size (16)
0x95, 0x02,        //     Report Count (2)
0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0x15, 0x81,        //     Logical Minimum (-127)
0x25, 0x7F,        //     Logical Maximum (127)
0x09, 0x38,        //     Usage (Wheel)
0x75, 0x08,        //     Report Size (8)
0x95, 0x01,        //     Report Count (1)
0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x0C,        //     Usage Page (Consumer)
0x0A, 0x38, 0x02,  //     Usage (AC Pan)
0x95, 0x01,        //     Report Count (1)
0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              //   End Collection
0xC0,              // End Collection
0x06, 0x00, 0xFF,  // Usage Page (Vendor Defined 0xFF00)
0x09, 0x01,        // Usage (0x01)
0xA1, 0x01,        // Collection (Application)
0x85, 0x08,        //   Report ID (8)
0x15, 0x00,        //   Logical Minimum (0)
0x26, 0xFF, 0x00,  //   Logical Maximum (255)
0x09, 0x00,        //   Usage (0x00)
0x75, 0x08,        //   Report Size (8)
0x96, 0x7D, 0x01,  //   Report Count (381)
0xB1, 0x02,        //   Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0,              // End Collection

// 251 bytes

Resources

https://github.com/esp32beans/BLE_HID_Client

https://github.com/olegos76/nimble_kbdhid_example

https://www.ti.com/lit/an/swra715/swra715.pdf?ts=1765644181805

https://circuitlabs.net/ble-hid-device-implementation/

https://docs.kernel.org/hid/hidintro.html