Protokół HID śledzenia ruchów głowy

Protokół urządzenia HID (Human Interface Device) śledzącego ruch głowy, dostępny na urządzeniach z Androidem 13 lub nowszym, umożliwia podłączenie urządzenia śledzącego ruch głowy do urządzenia z Androidem przez USB lub Bluetooth i ujawnienie go w ramach frameworku Androida i aplikacji za pomocą frameworku sensorów. Protokół ten jest używany do sterowanie efekt wirtualizacji dźwięku (dźwięk 3D). Na tej stronie używamy haseł urządzenie i hosta w technologii Bluetooth, gdzie urządzenie oznacza urządzenie śledzące ruch głowy a host oznacza hosta Androida.

Producenci urządzeń muszą skonfigurować swoje urządzenia z Androidem, aby umożliwić obsługę protokołu HID lokalizatora głowy. Więcej informacji o konfiguracji znajdziesz w pliku README Dynamic Sensors (w języku angielskim).

Ta strona zakłada, że znasz te zasoby:

Struktura najwyższego poziomu

Platforma Android rozpoznaje urządzenie śledzące ruch głowy jako urządzenie HID.

Pełny przykład prawidłowego deskryptora HID znajdziesz w sekcji Załącznik 1. Przykład deskryptora HID

Najwyższy poziom trackera to kolekcja aplikacji z Sensors (0x20) i użycie usługi Other: Custom (0xE1). Wewnątrz zbiór danych obejmuje różne pola danych (dane wejściowe) i właściwości (funkcje).

Właściwości i pola danych

W tej sekcji opisano właściwości i pola danych w zbiorze aplikacji urządzenia śledzącego ruch głowy.

Właściwość: Opis czujnika (0x0308)

Właściwość Opis czujnika (0x0308) to tylko do odczytu ciąg znaków ASCII (8-bitowy), który musi zawierać te wartości:

Śledzik w wersji 1.0:

#AndroidHeadTracker#1.0

Lokalizator w wersji 2.0 (dostępny na urządzeniach z Androidem 15 lub , która obejmuje obsługę LE Audio:

#AndroidHeadTracker#2.0#x

Wartość x jest liczbą całkowitą (1, 2, 3) wskazującą transport wspierający:

  • 1: Lista kontroli dostępu (ACL)
  • 2: ISO
  • 3: ACL + ISO

Nie jest wymagany żaden terminator null, co oznacza, że łączny rozmiar tej właściwości w wersji 1.0 wynosi 23 8-bitowe znaki.

Ta właściwość służy do rozróżniania czujników, aby uniknąć kolizji z innymi czujnikami niestandardowymi.

Usługa: stały unikalny identyfikator (0x0302)

Właściwość trwały identyfikator unikalny (0x0302) to tablica tylko do odczytu zawierająca 16 elementów, z których każdy ma 8 bitów (razem 128 bitów). Nie jest wymagany terminator null. Ten jest opcjonalna.

Ta właściwość umożliwia urządzeniom śledzącym ruch głowy, które są zintegrowane z urządzeniami audio, odwoływanie się do urządzenia audio, do którego są podłączone. Obsługiwane są te schematy:

Samodzielny lokalizator ruchów głowy

Jeśli właściwość Persistent Unique ID (0x0302) nie istnieje lub została ustawiona na wszystkie oznacza to, że tracker nie jest na stałe podłączony do urządzenia audio i można jej używać oddzielnie, np. umożliwiając ręcznie powiązać go z osobnym urządzeniem audio.

Odwoływanie się do adresu MAC Bluetooth

Octet 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Wartość 0 0 0 0 0 0 0 0 B T Adres MAC Bluetooth

W tym schemacie pierwsze 8 oktetów musi być 0, oktety 8 i 9 muszą zawierać odpowiednio wartości ASCII BT, a kolejne 6 oktetów są interpretowane jako adres MAC Bluetooth, przy założeniu, że urządzenie śledzące ruchy głowy dotyczy dowolnego urządzenia audio mającego ten adres MAC. Ten adres musi być adresu tożsamości, nawet jeśli urządzenie używa losowego adresu MAC do ustalania połączeń. Urządzenia w trybie podwójnym łączące się przez klasyczny Bluetooth (w formacie HID w wersji 1.0) i Bluetooth LE (w formacie HID w wersji 2.0) muszą udostępniać 2 deskryptory HID z tym samym adresem tożsamości. Urządzenia z 2 trybami oddzielone urządzenia lewe i prawe muszą udostępniać Bluetooth LE HID za pomocą podstawowego urządzenia w trybie online, a nie dodatkowego urządzenia obsługującego tylko LE.

Odwoływanie się za pomocą UUID

Gdy ustawiony jest najbardziej znaczący bit (MSB) oktetu 8 (≥0x80), pole jest interpretowany jako identyfikator UUID, jak określono w RFC-4122. odpowiednie urządzenie audio udostępnia ten sam identyfikator UUID zarejestrowany na platformy Androida za pomocą nieokreślonego mechanizmu charakterystycznego dla z jakiegoś rodzaju transportu.

Usługa: stan raportowania (0x0316)

Właściwość Stan raportowania (0x0316) to właściwość do odczytu/zapisu, w której występuje wartość standardowej semantyki zdefiniowanej w specyfikacji HID. Używany przez gospodarza aby wskazać urządzeniu, które zdarzenia mają być zgłaszane. Używane są tylko wartości Brak zdarzeń (0x0840) i Wszystkie zdarzenia (0x0841).

Początkowa wartość tego pola musi wynosić Brak zdarzeń i nigdy nie może modyfikowane przez urządzenie, tylko przez gospodarza.

Właściwość: stan zasilania (0x0319)

Właściwość Power State (0x0319) to właściwość do odczytu/zapisu, która zawiera atrybut standardowej semantyki zdefiniowanej w specyfikacji HID. Host używa tej właściwości, aby wskazać urządzeniu, w jakim stanie zasilania ma się ono znajdować. Używane są tylko wartości Pełna moc (0x0851) i Wyłącz zasilanie (0x0855).

Początkowa wartość tego pola zależy od urządzenia i nie można jej nigdy modyfikowane przez urządzenie, tylko przez gospodarza.

Właściwość: Interwał raportowania (0x030E)

Właściwość Interwał raportów (0x030E) to właściwość do odczytu/zapisu, w której parametr standardowej semantyki zdefiniowanej w specyfikacji HID. Host używa tej właściwości, aby wskazać urządzeniu, jak często ma raportować odczyty danych. Jednostki są wyrażone w sekundach. Prawidłowy zakres tej wartości jest określany przez urządzenie i opisane z użyciem mechanizmu minimalne/maksymalnej wartości fizycznej. Musi być obsługiwana częstotliwość raportowania co najmniej 50 Hz, a zalecana maksymalna częstotliwość raportowania to 100 Hz. Dlatego minimalny interwał raportowania musi być krótszy lub równy 20 ms, a zalecany interwał powinien być dłuższy lub równy 10 ms.

Usługa: zarezerwowane przez dostawcę LE Transport (0xF410)

Właściwość LE Transport (0xF410) zarezerwowana przez dostawcę to właściwość do odczytu/zapisu. o standardowej semantyce zdefiniowanej w specyfikacji HID. Gospodarz używa tej właściwości do wskazania wybranego transportu (ACL lub ISO). Tylko używane są wartości ACL (0xF800) i ISO (0xF801), które trzeba uwzględnić w zbiorze logicznym.

Ta właściwość jest konfigurowana przed stanem zasilania lub raportowania.

Pole danych: niestandardowa wartość 1 (0x0544)

Pole Wartość niestandardowa 1 (0x0544) to pole danych służące do raportowania rzeczywistych informacji o śledzeniu ruchem głowy. Jest to tablica o 3 elementach interpretowana zgodnie ze zwykłymi regułami HID dotyczącymi wartości fizycznych określonymi w sekcji 6.2.2.7 specyfikacji HID. Prawidłowy zakres dla każdego elementu to [-π, π] rad. Jednostki zawsze są radiany.

Elementy są interpretowane w taki sposób: [rx, ry, rz], więc [rx, ry, rz] to wektor obrotu, reprezentujący przekształcenie z ramki referencyjnej w ramkę nagłówka. Wielkość musi mieścić się w zakresie [0..π].

Układ odniesienia jest dowolny, ale zwykle jest stały i musi być dla prawej ręki. Dopuszczalna jest niewielka wartość dryfu. Osie głowy:

  • X od lewego ucha do prawego
  • Y od tylnej części głowy do nosa (od tyłu do przodu)
  • Z od szyi do góry głowy

Pole danych: wartość niestandardowa 2 (0x0545)

Pole Wartość niestandardowa 2 (0x0545) to pole do wprowadzania danych służące do raportowania wartości prawdziwych danych na temat śledzenia ruchów głowy. Jest to 3-elementowy tablica o stałym punkcie, interpretowana zgodnie ze zwykłymi regułami HID dla wartości fizycznych. Jednostkami są zawsze radiany na sekundę.

Elementy są interpretowane w taki sposób: [vx, vy, vz], więc [vx, vy, vz] to wektor obrotu, reprezentujący prędkość kątową ramy głowy (względem siebie).

Pole danych: niestandardowa wartość 3 (0x0546)

Pole Wartość niestandardowa 3 (0x0546) to pole wejściowe służące do śledzenia przerw w ramce odniesienia. Jest to liczba całkowita skalarna o długości 8 bitów. Musi być zwiększany (z przewijaniem) przez urządzenie za każdym razem, gdy zmienia się układ odniesienia, na przykład gdy stan algorytmu filtra orientacji używanego do określania orientacji został zresetowany. Ta wartość jest interpretowana zgodnie ze zwykłymi regułami HID dotyczącymi wartości fizycznych. Wartość fizyczna i jednostki nie mają jednak znaczenia. Jedynymi informacjami istotnymi dla hosta jest zmieniona wartość. Aby uniknąć problemów związanych z utratą dokładności podczas konwertowania z jednostek logicznych na fizyczne, zalecamy ustawienie w tym polu wartości minimalnej, maksymalnej i wykładniczej jednostki na zero.

Struktura raportu

Grupowanie usług w raporty (poprzez przypisywanie identyfikatorów raportów) jest elastyczne. Ze względu na wydajność zalecamy oddzielenie właściwości tylko do odczytu od właściwości tylko do odczytu/zapisu.

W przypadku pól danych pola Wartość niestandardowa 1, 2 i 3 muszą być w tym samym widoku i wyświetlać tylko jeden raport dla danego urządzenia (kolekcja aplikacji).

Wysyłanie raportów o błędach

Urządzenie musi okresowo i asynchronicznie (za pomocą komunikatów HID INPUT) wysyłaj raporty wejściowe, gdy są spełnione wszystkie poniższe warunki:

  • Właściwość Stan zasilania ma wartość Pełna moc.
  • Właściwość Stan raportowania jest ustawiona na Wszystkie zdarzenia.
  • Właściwość Interwał raportowania ma wartość różną od zera.

Właściwość Interwał raportowania określa, jak często mają być wysyłane raporty. Jeśli którekolwiek z tych warunków nie jest spełnione, urządzenie nie może wysyłać żadnych raportów.

Zgodność do przodu i do tyłu

Protokół HID śledzącego ruch głowy używa schematu wersji, który umożliwia aktualizacje, a także interoperacyjność między hostem a urządzeniem, które używają różnych wersji protokołu. Wersje protokołu są identyfikowane za pomocą 2 liczb: głównej i podrzędnej, które mają odmienną semantykę, jak opisano w poniższych sekcjach.

Wersje obsługiwane przez urządzenie można określić, analizując jego właściwość „Opis czujnika” (0x0308).

Zgodność wersji podrzędnych

Zmiany w wersji podrzędnej są zgodne wstecznie z wcześniejszymi wersjami podrzędnymi opartych na tej samej wersji głównej. W aktualizacjach do wersji podrzędnej host ignoruje dodatkowe pola danych i właściwości. Na przykład urządzenie korzystające z wersji 1.6 protokołu jest zgodne z hostem obsługującym wersję 1.x protokołu, w tym wersję 1.5.

Zgodność z wersją główną

W przypadku zmian w głównych wersjach dozwolone są zmiany niezgodne ze starszymi wersjami. Aby obsługiwać wiele głównych wersji w celu zapewnienia interoperacyjności ze starymi i nowymi hostami, urządzenia mogą określać w swoich deskryptorach raportów wiele kolekcji aplikacji. Na przykład:

const unsigned char ReportDescriptor[] = {
    HID_USAGE_PAGE_SENSOR,
    HID_USAGE_SENSOR_TYPE_OTHER_CUSTOM,

    HID_COLLECTION(HID_APPLICATION),
        // Feature report 2 (read-only).
        HID_REPORT_ID(2),

        // Magic value: "#AndroidHeadTracker#1.5"
        HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(23),
        HID_FEATURE(HID_CONST_VAR_ABS),

      ...

    HID_END_COLLECTION,

    HID_COLLECTION(HID_APPLICATION),
        // Feature report 12 (read-only).
        HID_REPORT_ID(12),

        // Magic value: "#AndroidHeadTracker#2.4"
        HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(23),
        HID_FEATURE(HID_CONST_VAR_ABS),

      ...

    HID_END_COLLECTION,
};

W tym przypadku host może wyliczyć wszystkie różne kolekcje aplikacji które są reklamowane przez urządzenie, sprawdzając właściwość „Opis czujnika”, aby określić wersje protokołów wdrażane przez każdy z nich, a następnie wybrać Najnowsza wersja protokołu obsługiwana przez hosta. Po jej wybraniu host będzie działać korzystając z pojedynczego protokołu wybranego przez cały okres użytkowania urządzenia połączenia.

Dodatek: przykład deskryptora HID

Przykład poniżej pokazuje typowy prawidłowy deskryptor HID. Wykorzystuje często używane makra C, udostępniane w Wykorzystanie czujników HID (art. 4.1).

const unsigned char ReportDescriptor[] = {
    HID_USAGE_PAGE_SENSOR,
    HID_USAGE_SENSOR_TYPE_OTHER_CUSTOM,
    HID_COLLECTION(HID_APPLICATION),
        // Feature report 2 (read-only).
        HID_REPORT_ID(2),

        // Magic value: "#AndroidHeadTracker#1.0"
        HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(23),
        HID_FEATURE(HID_CONST_VAR_ABS),

        // UUID.
        HID_USAGE_SENSOR_PROPERTY_PERSISTENT_UNIQUE_ID,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(16),
        HID_FEATURE(HID_CONST_VAR_ABS),

        // Feature report 1 (read/write).
        HID_REPORT_ID(1),

        // 1-bit on/off reporting state.
        HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(1),
        HID_REPORT_SIZE(1),
        HID_REPORT_COUNT(1),
        HID_COLLECTION(HID_LOGICAL),
            HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE_NO_EVENTS,
            HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE_ALL_EVENTS,
            HID_FEATURE(HID_DATA_ARR_ABS),
        HID_END_COLLECTION,

        // 1-bit on/off power state.
        HID_USAGE_SENSOR_PROPERTY_POWER_STATE,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(1),
        HID_REPORT_SIZE(1),
        HID_REPORT_COUNT(1),
        HID_COLLECTION(HID_LOGICAL),
            HID_USAGE_SENSOR_PROPERTY_POWER_STATE_D4_POWER_OFF,
            HID_USAGE_SENSOR_PROPERTY_POWER_STATE_D0_FULL_POWER,
            HID_FEATURE(HID_DATA_ARR_ABS),
        HID_END_COLLECTION,

        // 6-bit reporting interval, with values [0x00..0x3F] corresponding to [10ms..100ms].
        HID_USAGE_SENSOR_PROPERTY_REPORT_INTERVAL,
        HID_LOGICAL_MIN_8(0x00),
        HID_LOGICAL_MAX_8(0x3F),
        HID_PHYSICAL_MIN_8(10),
        HID_PHYSICAL_MAX_8(100),
        HID_REPORT_SIZE(6),
        HID_REPORT_COUNT(1),
        HID_USAGE_SENSOR_UNITS_SECOND,
        HID_UNIT_EXPONENT(0xD),  // 10^-3
        HID_FEATURE(HID_DATA_VAR_ABS),

        // Input report 1

        // Orientation as rotation vector (scaled to [-pi..pi] rad).
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_1,
        HID_LOGICAL_MIN_16(0x01, 0x80), // LOGICAL_MINIMUM (-32767)
        HID_LOGICAL_MAX_16(0xFF, 0x7F), // LOGICAL_MAXIMUM (32767)
        HID_PHYSICAL_MIN_32(0x60, 0x4F, 0x46, 0xED),  // -314159265
        HID_PHYSICAL_MAX_32(0xA1, 0xB0, 0xB9, 0x12),  // 314159265
        HID_UNIT_EXPONENT(0x08),  // 10^-8
        HID_REPORT_SIZE(16),
        HID_REPORT_COUNT(3),
        HID_INPUT(HID_DATA_VAR_ABS),

        // Angular velocity as rotation vector (scaled to [-32..32] rad/sec).
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_2,
        HID_LOGICAL_MIN_16(0x01, 0x80), // LOGICAL_MINIMUM (-32767)
        HID_LOGICAL_MAX_16(0xFF, 0x7F), // LOGICAL_MAXIMUM (32767)
        HID_PHYSICAL_MIN_8(0xE0),
        HID_PHYSICAL_MAX_8(0x20),
        HID_UNIT_EXPONENT(0x00),  // 10^0
        HID_REPORT_SIZE(16),
        HID_REPORT_COUNT(3),
        HID_INPUT(HID_DATA_VAR_ABS),

        // Reference frame reset counter.
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_3,
        HID_LOGICAL_MIN_16(0x00, 0x00), // LOGICAL_MINIMUM (0)
        HID_LOGICAL_MAX_16(0xFF, 0x00), // LOGICAL_MAXIMUM (255)
        HID_PHYSICAL_MIN_8(0x00),
        HID_PHYSICAL_MAX_8(0x00),
        HID_UNIT_EXPONENT(0x00),  // 10^0
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(1),
        HID_INPUT(HID_DATA_VAR_ABS),

    HID_END_COLLECTION,
};

Załącznik 2. Przykład opisu HID w wersji 2.0

Przykład poniżej pokazuje deskryptor HID w wersji 2.0 dla urządzenia obsługującego tylko transport Bluetooth LE ACL.

const unsigned char ReportDescriptor[] = {
    HID_USAGE_PAGE_SENSOR,
    HID_USAGE_SENSOR_TYPE_OTHER_CUSTOM,
    HID_COLLECTION(HID_APPLICATION),
        // Feature report 2 (read-only).
        HID_REPORT_ID(2),

        // Magic value: "#AndroidHeadTracker#2.0#1"
        HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(25),
        HID_FEATURE(HID_CONST_VAR_ABS),

        // UUID.
        HID_USAGE_SENSOR_PROPERTY_PERSISTENT_UNIQUE_ID,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(16),
        HID_FEATURE(HID_CONST_VAR_ABS),

        // Feature report 1 (read/write).
        HID_REPORT_ID(1),

        // 1-bit on/off reporting state.
        HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(1),
        HID_REPORT_SIZE(1),
        HID_REPORT_COUNT(1),
        HID_COLLECTION(HID_LOGICAL),
            HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE_NO_EVENTS,
            HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE_ALL_EVENTS,
            HID_FEATURE(HID_DATA_ARR_ABS),
        HID_END_COLLECTION,

        // 1-bit on/off power state.
        HID_USAGE_SENSOR_PROPERTY_POWER_STATE,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(1),
        HID_REPORT_SIZE(1),
        HID_REPORT_COUNT(1),
        HID_COLLECTION(HID_LOGICAL),
            HID_USAGE_SENSOR_PROPERTY_POWER_STATE_D4_POWER_OFF,
            HID_USAGE_SENSOR_PROPERTY_POWER_STATE_D0_FULL_POWER,
            HID_FEATURE(HID_DATA_ARR_ABS),
        HID_END_COLLECTION,

        // 6-bit reporting interval, with values [0x00..0x3F] corresponding to [10ms..100ms].
        HID_USAGE_SENSOR_PROPERTY_REPORT_INTERVAL,
        HID_LOGICAL_MIN_8(0x00),
        HID_LOGICAL_MAX_8(0x3F),
        HID_PHYSICAL_MIN_8(10),
        HID_PHYSICAL_MAX_8(100),
        HID_REPORT_SIZE(6),
        HID_REPORT_COUNT(1),
        HID_USAGE_SENSOR_UNITS_SECOND,
        HID_UNIT_EXPONENT(0xD),  // 10^-3
        HID_FEATURE(HID_DATA_VAR_ABS),

        // 1-bit transport selection
        HID_USAGE_SENSOR_PROPERTY_VENDOR_LE_TRANSPORT,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(1),
        HID_REPORT_SIZE(1),
        HID_REPORT_COUNT(1),
        HID_COLLECTION(HID_LOGICAL),
            HID_USAGE_SENSOR_PROPERTY_VENDOR_LE_TRANSPORT_ACL,
            HID_USAGE_SENSOR_PROPERTY_VENDOR_LE_TRANSPORT_ISO,
            HID_FEATURE(HID_DATA_ARR_ABS),
        HID_END_COLLECTION,

        // Input report 1

        // Orientation as rotation vector (scaled to [-pi..pi] rad).
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_1,
        HID_LOGICAL_MIN_16(0x01, 0x80), // LOGICAL_MINIMUM (-32767)
        HID_LOGICAL_MAX_16(0xFF, 0x7F), // LOGICAL_MAXIMUM (32767)
        HID_PHYSICAL_MIN_32(0x60, 0x4F, 0x46, 0xED),  // -314159265
        HID_PHYSICAL_MAX_32(0xA1, 0xB0, 0xB9, 0x12),  // 314159265
        HID_UNIT_EXPONENT(0x08),  // 10^-8
        HID_REPORT_SIZE(16),
        HID_REPORT_COUNT(3),
        HID_INPUT(HID_DATA_VAR_ABS),

        // Angular velocity as rotation vector (scaled to [-32..32] rad/sec).
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_2,
        HID_LOGICAL_MIN_16(0x01, 0x80), // LOGICAL_MINIMUM (-32767)
        HID_LOGICAL_MAX_16(0xFF, 0x7F), // LOGICAL_MAXIMUM (32767)
        HID_PHYSICAL_MIN_8(0xE0),
        HID_PHYSICAL_MAX_8(0x20),
        HID_UNIT_EXPONENT(0x00),  // 10^0
        HID_REPORT_SIZE(16),
        HID_REPORT_COUNT(3),
        HID_INPUT(HID_DATA_VAR_ABS),

        // Reference frame reset counter.
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_3,
        HID_LOGICAL_MIN_16(0x00, 0x00), // LOGICAL_MINIMUM (0)
        HID_LOGICAL_MAX_16(0xFF, 0x00), // LOGICAL_MAXIMUM (255)
        HID_PHYSICAL_MIN_8(0x00),
        HID_PHYSICAL_MAX_8(0x00),
        HID_UNIT_EXPONENT(0x00),  // 10^0
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(1),
        HID_INPUT(HID_DATA_VAR_ABS),

    HID_END_COLLECTION,
};