La raspberry-pi est une jolie petite plateforme pour mettre une petite touche d'informatique dans un produit ou dans la maison. Elle est bon marché, facile à développer et à adapter mais elle n'a ni écran ni clavier permettant de la commander.
Nous allons voire dans cette série d'articles les différentes façons de faire communiquer une carte Raspberry pi et un téléphone Android via une connexion USB entre ces deux éléments.
Ce premier article présentera la gestion de l'USB sur android et vous expliquera comment rediriger le flux audio de votre téléphone vers votre carte Raspberry-pi.
Le deuxième article vous apprendra à rediriger un périphérique USB HID (souris ou clavier) branché sur votre raspberry-pi vers votre téléphone Android
Enfin, le dernier article vous montrera comment transmettre des données quelconques entre votre raspberry-pi et une application Android que nous développerons spécifiquement.
Android et l'USB
Maîtres et esclaves
L'USB est un protocole qui a été conçu pour connecter des périphériques à un ordinateur. Le protocole est donc prévue pour avoir un maître et des esclaves. Typiquement le PC est maître et les périphériques sont esclaves. Le maître décide qui parle sur le bus et les périphériques ne peuvent pas dialoguer entre eux. Le maître est également le seul à pouvoir fournir de la puissance électrique sur le bus USB. A l'exceptions des téléphones les plus récents, les téléphones Android ne peuvent être qu'esclaves. C'est assez logique puisqu'il est souhaitable de pouvoir fournir de la puissance au téléphone (pour le charger par exemple) et de pouvoir communiquer avec un PC (le téléphone se faisant passer pour une clé USB ). Notre Raspberry sera donc le maître USB et le téléphone l'esclave USB.
La connexion d'un périphérique Android
Si vous branchez un téléphone android sur votre PC linux et que vous utilisez la commande suivante :
lsusb
vous remarquerez que votre téléphone n'indique d'aucune manière qu'il est équipé du système Android. Typiquement il vous donnera un nom de fabriquant et un nom de modèle différent pour chaque télépone
Bus 002 Device 126: ID 0bb4:0cac HTC (High Tech Computer Corp.)
Nous verrons lors de l'intégration sur la raspberry pi comment reconnaître le périphérique. Les grandes étapes de la connexion sont les suivantes :
- Interroger le téléphone pour connaitre la version du protocole AOA (android open accessory) à utiliser.
- Envoyer au téléphone des identifiants et des informations de configuration.
- Demander au téléphone de passer en mode connecté USB.
- Le téléphone se déconnecte du bus USB puis se reconnecte avec de nouveaux identifiants.
- Utiliser les identifiants pour vérifier les capacités du téléphone.
- Utiliser les capacités du téléphone.
Pour communiquer avec un périphérique Android nous utilisons simplement libusb. Vous trouverez à la fin de cet article le code source complet des applications de configuration et de communication avec le périphérique android.
Le premier programme à étudier est le programme usbAccConfig. Ce programme est appelé par udev à la connexion du téléphone et enverra les paramètres de configuration avant de demander au téléphone de basculer en mode accessoire.
Pour tester usbAccConfig il suffit de le compiler puis de le lancer en lui passant les identifiants du télépone:
sudo ./usbAccConfig s 0x0bb4 0x0cac
Le programme peut être testé sur votre PC de bureau. Il n'y a rien de spécifique à la raspberry-pi. Les identifiants à utiliser sont ceux de votre téléphone tels qu'ils sont indiqués par lsusb
Nous ne commenterons ici que les grandes lignes du code source. La gestion d'erreur et la mise en place détaillée peut être trouvée dans le code source.
Tout d'abord ouvrons le périphérique
handle = libusb_open_device_with_vid_pid(NULL, vid, pid))
le VendorID et le ProductID sont obtenu depuis la ligne de commande.
Ensuite il faut envoyer un paquet spécial permettant de vérifier la version du protocole à utiliser ( les valeurs transmise viennent de la description du protocole AOA)
response = libusb_control_transfer( handle, 0xC0, 51, 0, 0, ioBuffer, 2, 0);
La réponse arrive sous forme de deux octets que nous remettons dans le bon ordre
devVersion = ioBuffer[1] << 8 | ioBuffer[0];
devVersion doit être égal à 1 ou 2. Une autre valeur indique un périphérique qui n'est pas un périphérique Android.
Nous devons ensuite envoyer au téléphone une série de commandes pour lui dire quel type de périphérique l'hôte souhaite voir émulé par le téléphone.
- Dock audio (les flux sonores du téléphone sont redirigés vers l'hôte).
- Périphérique spécifique (le téléphone cherche une application capable de gérer le périphérique et laisse ensuite celle-ci gérer les communications).
Les détails des commandes de configuration à envoyer seront détaillés dans les différents articles de cette série.
Enfin, La dernière commande demande au téléphone de passer en mode gestion de périphérique.
response = libusb_control_transfer(handle,0x40,53,0,0,NULL,0,0);
Le téléphone se déconnectera du bus USB puis se reconnectera avec des identifiants fixés par le protocole AOA. (Le vendorID sera celui de google, quel que soit le constructeur du téléphone et le productID dépendra de la configuration demandée.)
La commande lsusb devrait désormais afficher quelque chose de similaire à ceci :
Bus 002 Device 006: ID 18d1:2d00 Google Inc. Android-powered device in accessory mode
notez le 18d1 qui est le vendorId de google. La seconde valeur dépend de la configuration exacte qui a été demandée au téléphone.
Intégration dans buildroot
Nous voulons que tout périphérique USB branché sur la raspberry soit testé par notre programme. Pour cela le programme usbAccConfig commence par vérifier une liste d'identifiants correspondant aux périphériques USB embarqués sur la raspberry pi. Pour tout autre identifiant il enverra la séquence de commandes décrite plus haut.
Pour que usbAccConfig soit lancé automatiquement à chaque connexion USB nous devons ajouter une règle udev. Pour cela nous ajoutons un fichier /etc/udev/rules.d/dock.rules avec le contenu suivant
ACTION=="add",SUBSYSTEM=="usb",RUN+="/usbAccConfig s 0x%s{idVendor} 0x%s{idProduct}"
Le premier paramètre (le s) indique que nous voulons utiliser notre device en mode audio. Lorsque vous voudrez tester le mode accessoire, il faudra le remplacer par un a.
Udev appellera ainsi le programme à chaque connexion USB. Nous pouvons vérifier que tout cela fonctionne correctement en branchant un téléphone sur la raspberry pi et en observant la déconnexion et reconnexion du téléphone.
Transformer notre Raspberry en dock Audio
Cette partie n'est possible que si votre téléphone gère la version 2.0 du protocole AOA, c'est à dire qu'il intègre Android 4.1 (i.e API 16) ou une version plus récente.
Nous avons vu dans le paragraphe précédant que le téléphone nous indique la version du protocole AOA qu'il utilise. Si notre téléphone utilise la version 2.0 nous pouvons lui passer une commande de configuration pour lui indiquer que nous souhaitons gérer les flux audio et que le téléphone doit se configurer en tant que périphérique Audio (le téléphone se présentera sur le bus comme une carte son USB avec une source sonore correspondante aux sons du téléphone). La ligne suivante sert à indiquer au téléphone qu'il doit passer en mode dock :
response = libusb_control_transfer(handle,0x40,58,1,0,NULL,0,0);
Après la configuration le téléphone devrait apparaître comme une carte son sur le bus USB et être reconnu comme tel par ALSA. un simple ls /proc/asound nous permet de vérifier que tout cela fonctionne... Notre raspberry a donc une nouvelle entrée son qui corresponds au périphérique Android, mais nous devons encore rediriger le son vers la véritable carte son de la raspberry pi. Pour cela il faut utiliser pulseaudio.
La configuration par défaut de pulseaudio est presque parfaite pour notre utilisation. Il lui manque juste quelques options. Dans le ficher /etc/pulse/daemon.conf il faut changer la methode de resampling vers la méthode trivial, la méthode par défaut étant trop gourmande en CPU. Dans l'exemple buildroot fourni à la fin de l'article nous utilisons les overlay pour configurer le fichier. Ensuite, il faut lancer la commande suivante après que pulseaudio ait détecté le téléphone Android
pactl load-module module-loopback source=`pactl list sources short | grep alsa_input.usb | cut -f 1
Une fois cette commande executée vous pouvez lancer n'importe quelle application audio sur le téléphone et le son sera redirigé vers le port jack de la carte raspberry pi.
Sur une véritable application l'api de la librairie pulseaudio permet de détecter l'ajout et de réagir mais, dans le cadre de cet article, nous nous contenterons de lancer la commande manuellement.
Code source, exemples et limitation
Vous trouverez le code source ci-dessous
usbAccConfig
Cette application prend trois paramètres, un action (a ou s), le vendor-id et product-id d'un périphérique USB. elle enverra les commandes de configuration au périphérique pour le mettre en mode dock audio ou en mode accessoire. Ce programme doit être lancé en tant que root pour avoir les droits d'accès sur le périphérique USB.
Le mode accessoire sera utilisé dans un des futurs articles de cette série.
buildroot Bundle
Cette archive contient les bases pour construire votre propre projet buildroot
- un defconfig pour la raspberry pi
- un répertoire overlay contenant les fichiers modifiés
Notez que ce tutoriel utilise buildroot comme exemple mais que les techniques et logiciels utilisés sont communs sur toutes les distributions linux et qu'il est très facile d'adapter cela à d'autres distributions que ce soient des distributions embarquées ou bureautiques...
Lors de l'écriture de cet article nous avons rencontré quelques limitations du système et des applications qu'il est utile de mentionner :
- Un bug Android empêche d'utiliser simultanément le mode Audio et le mode Accessoire. Si on tente de le faire, Alsa ne reconnaîtra pas correctement le périphérique Audio. Il s'agit d'un bug Android que vous pouvez suivre ici une analyse du bug sur la mailing list Alsa contient un patch kernel permettant de contourner le bug (le thread commence ici) Ce patch a été intégré dans le kernel 3.9 ainsi que dans les dernières mises à jour des kernels 3.0 et 3.4.
- Les applications de test ne gèrent pas pulseaudio (il faut lancer la commande manuellement). L'intégration de pulseaudio est possible dans une application réelle mais n'a pas été traitée ici.
Ca serait pas plutot
sudo ./usbAccConfig s 0x0bb4 0x0cac
à la place de :
sudo ./usbAccConfig s 0x0bb4 0xcaac