Le protocole HID (Human Interface Device) du capteur de suivi de la tête, disponible pour les appareils équipés d'Android 13 ou version ultérieure, permet de connecter un capteur de suivi de la tête à un appareil Android via USB ou Bluetooth, et de l'exposer au framework et aux applications Android via le framework sensors. Ce protocole est utilisé pour contrôler un effet de virtualisation audio (audio 3D). Sur cette page, les termes appareil et hôte sont utilisés dans leur sens Bluetooth, où appareil désigne le dispositif de suivi de la tête et hôte désigne l'hôte Android.
Les fabricants d'appareils doivent configurer leurs appareils Android pour qu'ils soient compatibles avec le protocole HID du capteur de mouvements de la tête. Pour en savoir plus sur la configuration, consultez le fichier README des capteurs dynamiques.
Sur cette page, nous partons du principe que vous connaissez bien les ressources suivantes :
- Définition de la classe d'appareil pour HID
- Tables d'utilisation HID pour USB
- Utilisations des capteurs HID
Structure de premier niveau
Le framework Android identifie le capteur de tête comme un périphérique HID.
Pour obtenir un exemple complet de descripteur HID valide, consultez l'annexe 1 : Exemple de descripteur HID.
Au niveau supérieur, le dispositif de suivi de la tête est une collection d'applications avec la page Sensors
(0x20
) et l'utilisation Other: Custom
(0xE1
). Cette collection contient plusieurs champs de données (entrées) et propriétés (fonctionnalités).
Propriétés et champs de données
Cette section décrit les propriétés et les champs de données d'une collection d'applications d'un appareil de suivi de la tête.
Propriété : Description du capteur (0x0308
)
La propriété de description du capteur (0x0308
) est une propriété de chaîne ASCII (8 bits) en lecture seule qui doit contenir les valeurs suivantes :
Version 1.0 du détecteur de mouvements de la tête :
#AndroidHeadTracker#1.0
Version 2.0 du suivi de la tête (disponible dans Android 15 ou version ultérieure), qui inclut la prise en charge de LE Audio :
#AndroidHeadTracker#2.0#x
x
est un entier (1
, 2
, 3
) indiquant le mode de transport pris en charge :
- 1 : LCA
- 2 : ISO
- 3 : ACL + ISO
Aucun caractère nul de fin n'est attendu, ce qui signifie que la taille totale de cette propriété est de 23 caractères de 8 bits pour la version 1.0.
Cette propriété sert de discriminant pour éviter les collisions avec d'autres capteurs personnalisés.
Propriété : ID unique persistant (0x0302
)
La propriété Persistent Unique ID (0x0302
) est un tableau en lecture seule de 16 éléments, chacun de 8 bits (128 bits au total). Aucun terminateur NULL n'est attendu. Cette propriété est facultative.
Cette propriété permet aux appareils de suivi de la tête intégrés aux appareils audio de faire référence à l'appareil audio auquel ils sont rattachés. Les schémas suivants sont acceptés.
Capteur de mouvements autonome
Si la propriété "ID unique persistant" (0x0302
) n'existe pas ou est définie sur zéro, cela signifie que le capteur de tête n'est pas associé de manière permanente à un appareil audio et peut être utilisé séparément, par exemple en permettant à l'utilisateur d'associer manuellement le capteur de tête à un appareil audio distinct.
Référence utilisant l'adresse MAC Bluetooth
Octet | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
Valeur | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | B | T | Adresse MAC Bluetooth |
Dans ce schéma, les huit premiers octets doivent être 0
, les octets 8 et 9 doivent contenir respectivement les valeurs ASCII B
et T
, et les six octets suivants sont interprétés comme une adresse MAC Bluetooth, en supposant que l'application de suivi de la tête s'applique à tout appareil audio ayant cette adresse MAC. Cette adresse doit être l'adresse d'identité, même si l'appareil utilise une adresse MAC aléatoire pour établir des connexions. Les appareils à double mode qui se connectent via Bluetooth classique (format HID v1.0) et Bluetooth LE (format HID v2.0) doivent exposer deux descripteurs HID avec la même adresse d'identité. Les appareils à double mode avec des appareils gauche et droit séparés doivent exposer le HID Bluetooth LE à l'aide de l'appareil à double mode principal au lieu de l'appareil secondaire LE uniquement.
Référence utilisant un UUID
Lorsque le bit de poids fort (MSB) de l'octet 8 est défini (≥0x80
), le champ est interprété comme un UUID, comme spécifié dans la RFC-4122. Le périphérique audio correspondant fournit le même UUID, qui est enregistré sur le framework Android, via un mécanisme non spécifié propre au type de transport utilisé.
Propriété : État des rapports (0x0316
)
La propriété "État du rapport" (0x0316
) est une propriété en lecture/écriture qui possède la sémantique standard définie dans la spécification HID. L'hôte utilise cette propriété pour indiquer à l'appareil les événements à signaler. Seules les valeurs Aucun événement (0x0840
) et Tous les événements (0x0841
) sont utilisées.
La valeur initiale de ce champ doit être "Aucun événement" et ne doit jamais être modifiée par l'appareil, mais uniquement par l'hôte.
Propriété : État de l'alimentation (0x0319
)
La propriété "État de l'alimentation" (0x0319
) est une propriété en lecture/écriture qui possède la sémantique standard définie dans la spécification HID. L'hôte utilise cette propriété pour indiquer à l'appareil l'état d'alimentation dans lequel il doit se trouver. Seules les valeurs "Pleine puissance" (0x0851
) et "Éteint" (0x0855
) sont utilisées.
La valeur initiale de ce champ est déterminée par l'appareil et ne doit jamais être modifiée par l'appareil, mais uniquement par l'hôte.
Propriété : intervalle du rapport (0x030E
)
La propriété "Intervalle du rapport" (0x030E
) est une propriété en lecture/écriture qui présente la sémantique standard définie dans la spécification HID. L'hôte utilise cette propriété pour indiquer à l'appareil la fréquence à laquelle il doit signaler ses relevés de données.
Cette valeur est exprimée en secondes. La plage de valeurs valides pour cette valeur est déterminée par l'appareil et décrite à l'aide du mécanisme de min/max physique. Une fréquence de rapport d'au moins 50 Hz doit être prise en charge, et la fréquence de rapport maximale recommandée est de 100 Hz. Par conséquent, l'intervalle de rapport minimal doit être inférieur ou égal à 20 ms, et il est recommandé qu'il soit supérieur ou égal à 10 ms.
Propriété : Transport LE réservé au fournisseur (0xF410
)
La propriété 0xF410
(transport LE réservé au fournisseur) est une propriété en lecture/écriture qui présente la sémantique standard définie dans la spécification HID. L'hôte utilise cette propriété pour indiquer le transport sélectionné (ACL ou ISO). Seules les valeurs ACL (0xF800
) et ISO (0xF801
) sont utilisées, et les deux doivent être incluses dans la collection logique.
Cette propriété est configurée avant les états d'alimentation ou de rapport.
Champ de données : Valeur personnalisée 1 (0x0544
)
Le champ "Valeur personnalisée 1" (0x0544
) est un champ de saisie utilisé pour indiquer les informations réelles de suivi de la tête. Il s'agit d'un tableau à trois éléments, interprété selon les règles HID normales pour les valeurs physiques, comme spécifié dans la section 6.2.2.7 de la spécification HID. La plage valide pour chaque élément est [-π, π] rad. Les unités sont toujours exprimées en radians.
Les éléments sont interprétés comme suit : [rx, ry, rz]
, où [rx, ry, rz]
est un vecteur de rotation représentant la transformation du repère de référence au repère de tête.
La magnitude doit être comprise dans la plage [0..π].
Le repère est arbitraire, mais il est généralement fixe et doit être orienté vers la droite. Une petite dérive est acceptable. Les axes de tête sont les suivants :
- X de l'oreille gauche à l'oreille droite
- Y : de l'arrière de la tête au nez (de l'arrière vers l'avant)
- Z du cou au sommet de la tête
Champ de données : Valeur personnalisée 2 (0x0545
)
Le champ "Valeur personnalisée 2" (0x0545
) est un champ de saisie utilisé pour indiquer les informations réelles sur le suivi de la tête. Il s'agit d'un tableau à virgule fixe de trois éléments, interprété selon les règles HID normales pour les valeurs physiques.
Les unités sont toujours en radians/seconde.
Les éléments sont interprétés comme suit : [vx, vy, vz]
, où [vx, vy, vz]
est un vecteur de rotation représentant la vitesse angulaire du cadre de tête (par rapport à lui-même).
Champ de données : Valeur personnalisée 3 (0x0546
)
Le champ "Valeur personnalisée 3" (0x0546
) est un champ de saisie utilisé pour suivre les discontinuités dans le repère. Il s'agit d'un entier scalaire de 8 bits. Il doit être incrémenté (avec retour à zéro) par l'appareil chaque fois que le repère est modifié, par exemple si l'état d'un algorithme de filtre d'orientation utilisé pour déterminer l'orientation a été réinitialisé. Cette valeur est interprétée selon les règles HID normales pour les valeurs physiques. Toutefois, la valeur physique et les unités n'ont pas d'importance. La seule information pertinente pour l'hôte est une valeur modifiée. Pour éviter les problèmes numériques liés à la perte de précision lors de la conversion d'unités logiques en unités physiques, il est recommandé de définir les valeurs de min physique, max physique et exposant d'unité sur zéro pour ce champ.
Structure du rapport
Le regroupement des propriétés dans les rapports (par attribution d'ID de rapport) est flexible. Pour plus d'efficacité, nous vous recommandons de séparer les propriétés en lecture seule de celles en lecture/écriture.
Pour les champs de données, les champs "Valeur personnalisée 1", "Valeur personnalisée 2" et "Valeur personnalisée 3" doivent figurer dans le même rapport et dans un seul rapport pour un appareil donné (collection d'applications).
Envoyer des rapports d'entrée
L'appareil doit envoyer des rapports d'entrée de manière périodique et asynchrone (via des messages HID INPUT) lorsque toutes les conditions suivantes sont remplies :
- La propriété "État de l'alimentation" est définie sur "Alimentation complète".
- La propriété "État des rapports" est définie sur "Tous les événements".
- La propriété "Intervalle de création des rapports" est différente de zéro.
La propriété "Intervalle de création des rapports" détermine la fréquence d'envoi des rapports. Si l'une des conditions ci-dessus n'est pas remplie, l'appareil ne doit envoyer aucun rapport.
Compatibilité ascendante et rétrocompatibilité
Le protocole HID du capteur de mouvements de tête utilise un système de gestion des versions qui permet les mises à jour tout en assurant l'interopérabilité entre un hôte et un appareil qui utilisent différentes versions du protocole. Les versions du protocole sont identifiées par deux numéros, un numéro de version majeure et un numéro de version mineure, qui ont une sémantique distincte, comme décrit dans les sections suivantes.
Les versions compatibles avec un appareil peuvent être déterminées en examinant sa propriété de description du capteur (0x0308
).
Compatibilité des versions mineures
Les modifications apportées à la version mineure sont rétrocompatibles avec les versions mineures antérieures basées sur la même version majeure. Lors des mises à jour de la version mineure, l'hôte ignore les champs et propriétés de données supplémentaires. Par exemple, un appareil utilisant la version 1.6 du protocole est compatible avec un hôte qui prend en charge la version 1.x du protocole, y compris la version 1.5.
Compatibilité des versions majeures
Les modifications non rétrocompatibles sont autorisées pour les modifications apportées aux versions majeures. Pour prendre en charge plusieurs versions majeures pour l'interopérabilité avec les anciens et les nouveaux hôtes, les appareils peuvent spécifier plusieurs collections d'applications dans leurs descripteurs de rapport. Exemple :
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,
};
Dans ce cas, l'hôte peut énumérer toutes les collections d'applications différentes annoncées par l'appareil, en examinant leur propriété "Description du capteur" pour déterminer les versions de protocole qu'elles implémentent chacune, puis en choisissant la dernière version de protocole prise en charge par l'hôte. Lorsqu'il est sélectionné, l'hôte fonctionne avec le protocole unique choisi pour la durée de vie de la connexion de l'appareil.
Annexe : Exemple de descripteur HID
L'exemple suivant illustre un descripteur HID valide typique. Il utilise les macros C courantes fournies dans HID Sensor Usages (section 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,
};
Annexe 2 : Exemple de descripteur HID v2.0
L'exemple suivant illustre un descripteur HID v2.0 pour un appareil ne prenant en charge que le 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,
};