Protokół urządzenia interfejsu (HID) ś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 udostępnienie go platformie Android oraz aplikacjom za pomocą platformy sensorów. Ten protokół służy do sterowania efektem 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 śledzenia głowy. Więcej informacji o konfiguracji znajdziesz w pliku README Dynamic Sensors (w języku angielskim).
Na tej stronie zakładamy, ż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 wspierany:
- 1: lista 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 niestandardowych, aby uniknąć kolizji.
Właściwość: trwały 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. 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ą te schematy:
Samodzielny lokalizator ruchów głowy
Jeśli właściwość Persistent Unique ID (0x0302
) nie istnieje lub jest ustawiona na wszystkie zera, oznacza to, że urządzenie śledzące ruch głowy nie jest trwale połączone z urządzeniem audio i może być używane osobno, na przykład przez umożliwienie użytkownikowi ręcznego powiązania urządzenia śledzącego ruch głowy 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 B
i T
, 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. Adres ten musi być adresem tożsamości, nawet jeśli urządzenie używa losowego adresu MAC do nawiązywania połączeń. Urządzenia w trybie podwójnym, które łączą 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 dwutrybowe z oddzielnymi urządzeniami lewym i prawym muszą udostępniać interfejs Bluetooth LE HID za pomocą głównego urządzenia dwutrybowego, a nie dodatkowego urządzenia obsługującego tylko Bluetooth LE.
Odwoływanie się za pomocą UUID
Gdy najbardziej znaczący bit (MSB) oktetu 8 jest ustawiony (≥0x80
), pole jest interpretowane jako identyfikator UUID zgodnie z specyfikacją 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.
Właściwość: 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. Host używa tej właściwości, aby wskazać urządzeniu, które zdarzenia ma zgłaszać. 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ść tylko do odczytu lub do odczytu i zapisu, która ma standardową semantykę zgodnie ze specyfikacją 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 jest określana przez urządzenie i nigdy nie może być modyfikowana przez urządzenie, tylko przez hosta.
Właściwość: Odstęp czasu między raportami (0x030E
)
Właściwość Odstęp między raportami (0x030E
) to właściwość tylko do odczytu lub zapisu, która ma standardową semantykę zdefiniowaną w specyfikacji HID. Host używa tej właściwości, aby wskazać urządzeniu, jak często ma raportować odczyty danych.
Jednostką są sekundy. Prawidłowy zakres tej wartości jest określany przez urządzenie i opisywany za pomocą mechanizmu Minimalna/Maksymalna wartość fizyczna. 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ć równy co najwyżej 20 ms, a zalecany interwał to co najmniej 10 ms.
Właściwość: transport LE zarezerwowany przez dostawcę (0xF410
)
Właściwość zastrzeżona przez dostawcę LE Transport (0xF410
) to własność z możliwością odczytu/zapisu, która ma standardową semantykę zgodnie ze specyfikacją HID. Użytkownik hostujący używa tej właściwości do wskazania wybranego transportu (ACL lub ISO). Używane są tylko wartości ACL (0xF800
) i ISO (0xF801
), które muszą być uwzględnione w zbiorze logicznym.
Ta usługa jest skonfigurowana przed stanem zasilania lub raportowania.
Pole danych: Wartość niestandardowa 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ą w radianach.
Elementy są interpretowane jako: [rx, ry, rz]
, tak aby [rx, ry, rz]
był wektorem obrotu, reprezentującym transformację z układu odniesienia do układu głowy.
Wielkość musi mieścić się w zakresie [0..π].
Ćwiczenie jest dowolne, ale jest zazwyczaj ustalone i musi być prawostronne. Dopuszczalny jest niewielki dryf. Osie głowy:
- X od lewego ucha do prawego
- Y od tyłu głowy do nosa (z tyłu do przodu)
- Z szyi do czubka 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 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 jako: [vx, vy, vz]
, tak aby [vx, vy, vz]
był wektorem obrotu, który reprezentuje prędkość kątową ramki głowicy (względem siebie).
Pole danych: Wartość niestandardowa 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 (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ść 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 wejściu, gdy są spełnione wszystkie te warunki:
- Właściwość Stan zasilania ma wartość Pełna moc.
- Właściwość Stan raportowania ma wartość Wszystkie zdarzenia.
- Właściwość IntervalReporting jest różna od 0.
Właściwość Interval Reporting określa częstotliwość wysyłania raportów. Jeśli którekolwiek z tych warunków nie jest spełnione, urządzenie nie może wysyłać żadnych raportów.
Zgodność wsteczna i wsteczna
Protokół HID lokalizatora 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ść z wersją podrzędną
Zmiany w wersji podrzędnej są zgodne wstecznie z wcześniejszymi wersjami podrzędnymi opartymi 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 takim przypadku host może wyliczyć wszystkie kolekcje aplikacji reklamowane przez urządzenie, sprawdzić ich atrybuty Sensor Description, aby określić wersje protokołów, które są przez nie implementowane, a następnie wybrać najnowszą wersję protokołu obsługiwaną przez hosta. Gdy ta opcja jest wybrana, host działa z jednym protokołem wybranym na czas trwania połączenia z urządzeniem.
Załącznik: przykład deskryptora HID
Poniższy przykład ilustruje 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 opisu 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,
};