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 terminów urządzenie i host w rozumieniu Bluetooth, gdzie urządzenie oznacza urządzenie do śledzenia ruchów głowy, a host to host 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 załączniku 1: Przykład deskryptora HID.
Na najwyższym poziomie urządzenie śledzące ruch głowy jest kolekcją aplikacji z poziomami Sensors
(0x20
) i Other: Custom
(0xE1
). W tej kolekcji znajduje się kilka pól danych (wejść) i właściwości (funkcji).
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
śledzenie ruchów głowy w wersji 2.0 (dostępne w Androidzie 15 lub nowszym), które obejmuje obsługę dźwięku LE:
#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.
Właściwość: trwały identyfikator unikalny (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 oczekuje się zakończenia o wartości null. Ta właściwość 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ą poniższe 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
Oktet | 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 zawierać ciąg 0
, oktety 8 i 9 muszą zawierać
wartości ASCII odpowiednio B
i T
, a następujących 6 oktetów jest
interpretowane jako adres MAC Bluetooth przy założeniu, że urządzenie śledzące
dotyczy wszystkich urządzeń audio z tym adresem 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 z 2 trybami łączące się przez klasyczny Bluetooth
(format HID v1.0) i Bluetooth LE (format HID v2.0) muszą udostępniać dwa HID
deskryptory 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łanie z użyciem identyfikatora UUID
Gdy ustawiony jest najbardziej znaczący bit (MSB) oktetu 8 (≥0x80
), pole
jest interpretowany jako identyfikator UUID, jak określono w
RFC-4122. Odpowiadające urządzenie audio udostępnia ten sam identyfikator UUID, który jest zarejestrowany w ramach Androida za pomocą nieokreślonego mechanizmu odpowiedniego do typu używanego transportu.
Usługa: stan raportowania (0x0316
)
Właściwość Stan raportowania (0x0316
) to właściwość tylko do odczytu lub do odczytu i zapisu, która ma standardową semantykę zgodnie ze specyfikacją 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
).
Wartość początkowa tego pola musi wynosić Brak zdarzeń i nie może być modyfikowana przez urządzenie, tylko przez hosta.
Właściwość: stan zasilania (0x0319
)
Właściwość Stan zasilania (0x0319
) to właściwość odczytu/zapisu o standardowej semantyce zgodnie ze specyfikacją HID. Używany przez gospodarza
jest właściwy, by wskazać urządzeniu, w jakim musi być jego stanie zasilania. Tylko
używane są wartości Pełne zasilanie (0x0851
) i Wyłącz (0x0855
).
Początkowa wartość tego pola jest określana przez urządzenie i nigdy nie może być modyfikowana przez urządzenie, tylko przez hosta.
Usługa: interwał raportów (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. Używany przez gospodarza
właściwość wskazującą urządzenie, jak często ma ono raportować odczyty danych.
Jednostką są sekundy. 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 usługa jest skonfigurowana 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 3-elementowa tablica interpretowana jako
do normalnych reguł HID dla wartości fizycznych, zgodnie z sekcją 6.2.2.7
ze specyfikacją HID. Prawidłowy zakres dla każdego elementu to [-π, π] rad. Jednostki zawsze są w radianach.
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..π].
Ćwiczenie jest dowolne, ale jest zazwyczaj ustalone i musi być prawostronne. Dopuszczalne jest niewielkie odchylenie. Osie głowy:
- X od lewego ucha do prawego
- Y od tyłu głowy do nosa (z 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 rzeczywistych informacji o śledzeniu ruchem 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 zostać zwiększona (z zawijaniem) za każdym razem, gdy
obszar odniesienia ulegnie zmianie, np. gdy algorytm filtra orientacji zostanie zmieniony.
służy do określenia, że stan 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 liczbowych związanych z utratą dokładności
przy konwertowaniu jednostek logicznych na fizyczne zalecamy ustawienie
dla fizycznego minimum, fizycznego maksimum i wykładnika jednostki do zera dla tego pola.
Struktura raportu
Grupowanie usług w raporty (przez przypisanie identyfikatorów raportów) to i elastyczności. Aby zwiększyć wydajność, najlepiej oddzielić właściwości tylko do odczytu z właściwości do odczytu/zapisu.
W przypadku pól danych pola Wartość własna 1, 2 i 3 muszą znajdować się w tym samym raporcie i w jednym raporcie dotyczącym danego urządzenia (zbioru aplikacji).
Wysyłanie raportów o błędach
Urządzenie musi okresowo i asymetrycznie (za pomocą komunikatów HID INPUT) przesyłać raporty o wprowadzaniu danych, gdy są spełnione wszystkie te warunki:
- Właściwość stanu zasilania ma wartość Pełne zasilanie.
- Właściwość Stan raportowania ma wartość Wszystkie zdarzenia.
- Właściwość Interwał raportowania ma wartość różną od zera.
Właściwość Interval Reporting określa częstotliwość wysyłania raportów. Kiedy żaden z powyższych warunków nie jest spełniony, 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. Zidentyfikowano wersje protokołu dzielone na dwie liczby, większe i mniejsze. Mają one odmienne semantyki: opisane w dalszej części tego artykułu.
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 z wcześniejszymi wersjami podrzędnymi, które są oparte na tej samej wersji nadrzędnej. 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ą
Zmiany niekompatybilne wstecznie są dozwolone w przypadku zmian w wersjach głównych. 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.
Załącznik: przykład opisu HID
Przykład poniżej pokazuje typowy prawidłowy deskryptor HID. Używa ona powszechnie stosowanych makr C, które są opisane w Zastosowaniach czujników HID (sekcja 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 deskryptora HID w wersji 2.0
Ten przykład pokazuje deskryptor HID w wersji 2.0 dla urządzenia obsługującego tylko transport ACL Bluetooth LE.
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,
};