Communiquer avec un iPod, via le port Dock

Date de publication : — dernière modification :

En 2003, Apple a adopté sur ses iPod un connecteur propriétaire, nommé "dock". Ce connecteur possédant 30 broches permet de recharger et synchroniser votre iPod, mais aussi d'y connecter des accessoires, comme une station d'accueil par exemple.

Certaines stations d'accueil peuvent interagir avec votre iPod. Par exemple quand vous appuyez sur le bouton play de votre station d'accueil, la musique démarre sur l'iPod qui lui est connectée.

Dans cet article, nous allons comprendre comment on peut communiquer avec un iPod via son port Dock.

Anatomie d'un connecteur Dock

Comme indiqué ci-dessus, le connecteur Dock possède 30 broches. Sur ces 30 broches, l'iPod véhicule différents types de signaux :

  • De l'USB
  • Du FireWire (à partir de l'iPod Vidéo/5G, il n'est plus utilisé)
  • De la vidéo, soit par une sortie composite, soit en YPbPR
  • De l'audio, il y a une entrée et une sortie audio niveau ligne (les E/S sont stéréo)
  • De l'UART

Vous pouvez retrouver une fiche détaillée du pinout où la fonction de chaque broche est indiquée.

Pour pouvoir communiquer avec l'iPod, nous allons devoir communiquer avec l'interface UART de l'iPod, en générant des paquets respectant la norme Apple Accessory Protocol, qui spécifie le format de paquets à utiliser. La liaison série s'effectue au minimum à 9600 bauds, et peut aller jusqu'à 38400 bauds sans voir apparaître des erreurs.

Créer un paquet

Le protocole Apple Accessory Protocol nous indique que à la fois l'accessoire et l'iPod doivent émettre des packets de la forme suivante :

Champ Taille (octets) Valeur
Header 2 0xFF 0x55
Taille de la requête 1 0x03 + taille du paramètre en octets
Mode 1 Le mode concerné par la commande
Commande 2 La commande
Paramètres Variable Paramètre pour la commande, optionnel
Somme de contrôle 1 0x00 - ((somme de tous les bytes des autres champs excepté le header) & 0xFF)

Les commandes sont regroupées en modes. Il existe 5 modes :

  • 0x00 qui permet de changer de mode
  • 0x01 qui est relatif aux fonctions d'enregistreur vocal
  • 0x02 qui permet de commander l'iPod avec un nombre limité de commandes
  • 0x04 qui permet de commander l'iPod avec un nombre de commandes étendues (par contre il semblerait que l'affichage de l'iPod soit désactivé)

Pour connaître la liste des commandes du Apple Accessory Protocol, je vous invite à consulter cette page.

Afin de générer des paquets facilement, j'ai codé un petit utilitaire en Vala, nommé AAPPG :

AAPPG en action

Il permet de générer les packets à envoyer à l'iPod facilement, tout en se chargeant de calculer le checksum à la fin.

Trouver un connecteur

Nous avons rapidement abordé la partie logicielle, maintenant le matériel. Pour nous connecter à notr e iPod, nous ne pouvons pas simplement prendre un cable Dock vers USB du commerce et le bidouiller : seule les lignes USB sont cablées.

La solution la plus facile serait d'acheter une carte dîte "breakout", comme la PodBreakout, qui vous permet d'accéder facilement aux 30 broches du connecteur dock.

La carte PodBreakout

L'autre solution, c'est de récupérer le circuit imprimé du connecteur dock d'une station d'accueil type "iMini" (fabriqué par Ozaki) ; dans mon cas il s'agissait d'un iMini rainbow, parfois trouvable en vide-grenier.

La carte dock

On notera la présence dans un coin de la carte d'une paire de pont diviseurs de tension (encadrés en bleu), formés par 4 résistances. Ces deux ponts servent à fournir la tensions au bornes des lignes de données USB pour pouvoir charger l'appareil (pour plus d'informations à ce sujet il y a une vidéo explicative de LadyAda).

Il y a aussi une autre résistance marquée 12D (donc 130 kΩ) : il semblerait bien que cette résistance soit celle qui active l'UART.

Le dessous de la carte

Brancher l'iPod à notre carte de développement

J'utiliserai une carte de développement Ti Launchpad pourr ce projet car elle intègre deux boutons qu'on pourra assigner à deux commandes, et aussi car elle est très bon marché. On peut aussi bien utiliser un Arduino ou une autre plate-forme de développement électronique moyennant quelques modifications du code.

Le schéma simplifié suivant indique les connexions à faire (sur un Ti Launchpad équipé du micro-contrôleur MSP430G2553) :

Schéma du montage

Nous noterez la présence d'une résistance entre la broche 21 du connecteur Dock et la masse : elle est nécessaire pour que notre iPod écoute son interface UART.

Coder

Nous allons utiliser l'environnement Energia pour programmer ma Launchpad.

Je vais assigner au bouton RESET la commande "Play/pause" de l'iPod, et au bouton PUSH2 la commande "Chanson suivante".

On va commencer par créer une fonction pour envoyer une commande de changement de mode, pour être sûr qu'on est bien en mode "Simple remote" :

byte switch_simple_remote[] = {0xFF,0x55,0x03,0x00,0x01,0x02,0xFA};

void sendSwitchSimpleRemote() {
    for (int i = 0; i < sizeof(switch_simple_remote); i++) {
        Serial.write(switch_simple_remote[i]);
    }
}

A chaque fois qu'on relache un bouton appuyé, il faut envoyer un paquet pour indiquer que le bouton a été relaché. On va aussi coder une fonction pour ce faire :

byte button_released[] = {0xFF,0x55,0x03,0x02,0x00,0x00,0xFB};

void sendButtonReleased() {
    for (int i = 0; i < sizeof(button_released); i++) {
        Serial.write(button_released[i]);
    }
}

Nous allons ensuite faire deux fonctions pour envoyer les paquets pour dire à l'iPod qu'on veut mettre en pause la musique, ou la changer :

byte pause_song[] = {0xFF,0x55,0x03,0x02,0x00,0x01,0xFA};
byte next_song[] = {0xFF,0x55,0x03,0x02,0x00,0x08,0xF3};

void sendNextSong() {
  for (int i = 0; i < sizeof(next_song); i++) {
    Serial.write(next_song[i]);
  }
}

void sendPauseSong() {
  for (int i = 0; i < sizeof(pause_song); i++) {
    Serial.write(pause_song[i]);
  }
}

Presque tout est codé, il ne manque plus qu'à initialiser la liaison série et à faire en sorte que les deux boutons déclenchent l'envoi des commandes "play/pause" et "chanson suivante" :

void setup() { 
    Serial.begin(9600);
    pinMode(PUSH2,INPUT_PULLUP);

    sendSwitchSimpleRemote();
    sendPauseSong();
    sendButtonReleased();
}

void loop() {
    if (digitalRead(PUSH2) == LOW) {
        sendNextSong();
        sendButtonReleased();
        delay(1000);
    }
}

Démonstration