Présentation
À ma connaissance, il y a eu deux modèles de tablettes graphiques pour
la console Wii. Pour un projet sur lequel je travaille, j'avais besoin
de communiquer avec ces tablettes. Je me les suis donc procurées pour,
suite à quelques expériences, en déterminer le protocole de communication.
Je documente le tout sur cette page.
Voici une rapide comparaison externe des deux tablettes:
Elles sont très similaires. Dans les deux cas, la tablette est normalement
raccordée à une télécommande wii.
Le crayon de la tablette uDraw comporte
cependant deux boutons latéraux (chaque boût de la petite barre fonctionne
en tant que bouton, comme sur un crayon Wacom):
Mais la version Drawsome du crayon n'offre aucun boutons:
Analyse de la tablette uDraw
J'ai branché la tablette sur un
Adaptateur manette Classique à USB
(raphnet-tech) de ma conception. Cet adaptateur comporte des fonctions avancés qui permettent notamment de communiquer directement
avec l'acessoire (manette, tablette ou autre). Dans le cas des périphériques pour Wiimote, il s'agit du protocole I2C.
Autrement dit: J'utilise l'adaptateur en tant que passerelle USB à I2C.
Si cela est nouveau pour vous, voici une excellente page pour en apprendre sur le fonctionnement des périphériques pour Wiimote. Ce
document fait justement référence à la tablette Drawsome, mais pas à la tablette uDraw.
https://wiibrew.org/wiki/Wiimote/Extension_Controllers
Le gestionnaire d'adaptateur que j'ai conçu pour les produits raphnet-tech contient un outil en ligne de commande
capable d'examiner la mémoire des périphériques pour Wiimote en communiquant par I2C. Au lancement, un
dump complet
de la mémoire (256 octets) est affiché:
Les 6 derniers octets permettent d'identifier le type de périphérique. La documentation sur wiibrew.org mentionne que la tablette Drawsome utilise
les octets suivants:
00 A4 20 00 13. Pour la tablette uDraw, c'est donc différent:
00 A4 20 01 12.
Tous les périphériques Wii avec lesquels j'ai travaillé indique leur état (statut des boutons, position des axes, etc) avec une
série d'octet à partir de l'adresse zéro. Mon outil lit continuellement les 16 premiers octets, et chaque fois qu'un
d'eux change, affiche le tout sur une nouvelle ligne.
Il m'a donc suffit de jouer avec le crayon tout en observant les octets à l'écran pour déduire leur signification:
La position X et Y est codée sous 12 bits et la pression sur la pointe sous 8 bits. Et un bit correspond
à l'état de chaque bouton.
Octet | Signification |
0 | X (Les 8 bits les moins significatifs) |
1 | Y (Les 8 bits les moins significatifs) |
2 | Bits 7-4: Y (4 bits les plus significatifs) Bits 3-0: X (les 4 bits les plus significatifs. |
3 | Pression sur la pointe du crayon. Variation entre 0x08 (pas de pression) à 0xFB (forte pression) |
4 | ??? (Toujours 0xFF) |
5 | État des boutons:- Pointe: Bit 3 (1 quand appuyé)
- Bit 1: Bouton 1 (0 quand appuyé)
- Bit 0: Bouton 2 (0 quand appuyé)
|
Bien que 12 bits permettent des valeurs entre 0 et 4095, en pratique la variation est plus limitée:
- X: Varie environ de 95 à 1960
- Y: Varie environ de 95 à 1440
Lorsque le crayon n'est plus dans la zone active, la tablette retourne 4095,4095.
Pour vérifier si tout était exact, j'ai écrit du code pour extraire les informations et les afficher de manière plus
lisible:
void wusbmotelib_bufferToUdrawData(const uint8_t *buf, udraw_tablet_data *dst)
{
dst->x = ((buf[2] & 0x0f) << 8) | buf[0];
dst->y = ((buf[2] & 0xf0) << 4) | buf[1];
dst->pressure = buf[3];
dst->buttons = buf[5] ^ 0xfb;
}
...
case ID_UDRAW:
wusbmotelib_bufferToUdrawData(status, &udraw_data);
printf("X: %6d, Y: %6d, P=%3d, Buttons: 0x%02x (%3d %3d)\n",
udraw_data.x,
udraw_data.y,
udraw_data.pressure,
udraw_data.buttons,
(udraw_data.x - 90 - (1440-90)/2),
(udraw_data.y - 90 - (1920-90)/2));
...
Voici ce que génère l'extrait ci-dessus à l'exécution. Tout semble être en ordre!
Grâce à ces informations, le firmware version 2.2 pour l'
Adaptateur manette Classique à USB (raphnet-tech) permettra d'utiliser la tablette comme une souris.
Analyse de la tablette Drawsome
Pour la tablette Drawsome, j'ai d'abord suivi les même étapes que précédemment. Voici le contenu
de la mémoire:
Les 6 derniers octets qui permettent d'identifier le type de périphérique sont, tel que documenté
sur
wiibrew.org sont bien les suivants:
00 A4 20 00 13.
Et c'est ici que ça se corse. Contrairement à la tablette uDraw, rien ne se produit lorsque je déplace
le stylo. Les octets restent tels qu'ils sont ci-dessus:
00 00 00 00 00 c0.
J'ai d'abord craint que la tablette soit défectueuse, alors je l'ai essayée sur une Wii avec le jeu fourni. Mais elle
fonctionnait à merveille. J'en ai donc conclu donc qu'il fallait probablement « activer » la tablette en lui envoyant
un message. Mais lequel?
Le plus facile aurait été de capturer les échanges entre la télécommande Wiimote et la tablette, mais je n'avais
pas mon analyseur de logique avec moi. J'ai donc pris une approche « force brute » qui consiste à écrire toutes les valeurs
possibles à toutes les adresses, en vérifiant continuellement si les données aux adresses 0 à 6 se mettent à changer.
Voici mon pseudo-code:
for (value=0; value <= 255; value++) {
for (address=0; address <= 0xFF; address++) {
write_byte(address, value);
printf("0x%02x: %d\n", address, value);
readbytes(0, 6, buffer);
print_bytes_if_changed(buffer);
}
}
Après quelques minutes à regarder l'écran défiler pendant que je déplaçais continuellement le
stylo sur la surface de la tablette, tout à coup des données sont apparues!
Cela s'est mis à fonctionner après que 85 (0x55) ait été écrit à l'adresse 0xF0. Ne serait-ce que cela? Eh bien non.
Uniquement écrire 0x55 à l'adresse 0xF0 ne fonctionne pas. C'est donc qu'une autre écriture doit être
faite au préalable.
J'ai ensuite réduit la plage d'adresses de la recherche à 0xF0 à 0xFF (16 adresses).
for (value=0; value <= 255; value++) {
for (address=0xF0; address <= 0xFF; address++) {
write_byte(address, value);
printf("0x%02x: %d\n", address, value);
readbytes(0, 6, buffer);
print_bytes_if_changed(buffer);
}
}
Ceci était beaucoup plus rapide, et fonctionnait encore. J'ai ensuite expérimenté avec les valeurs
qui sont écrites. Après quelques essais j'ai compris qu'il fallait absolument écrire 0x01
quelque part avant l'écriture du 0x55 final.
// Step 1
value = 0x01;
for (address=0xF0; address <= 0xFF; address++) {
write_byte(address, value);
}
// Step 2
value = 0x55;
address=0xF0;
write_byte(address, value);
Le code ci-dessus écrit 0x01 à toutes les adresses de 0xF0 à 0xFF puis écrit 0x55 à l'adresse 0xF0. Et cela
active la tablette.
Mais faut-il vraiment écrire 0x01 à 16 endroits différents? J'ai procédé par élimination en réduisant
progressivement la plage d'adresses utilisée:
- 0xF8 - 0xFF : Fonctionne encore
- 0xFC - 0xFF : Ne fonctionne pas
- 0xF8 - 0xFC : Fonctionne
- 0xF8 - 0xFA : Ne fonctionne pas
- 0xFB seul : Fonctionne!
La recette magique pour activer la tablette est donc:
- Écrire 0x01 à l'adresse 0xFB
- Écrire 0x55 à l'adresse 0xF0
Bon! Ce petit obstacle hors de ma route, j'ai procédé à l'observation du mouvement
des valeurs à l'écran pendant le déplacement du crayon:
La position X et Y est codée sous 16 bits et la pression sur la pointe sous 12 bits. Et un bit de statut
indique si le crayon est à l'intérieur ou à l'extérieur de la zone.
Octet | Signification |
0 | X (Les 8 bits les moins significatifs) |
1 | X (Les 8 bits les plus significatifs) |
2 | Y (Les 8 bits les moins significatifs) |
3 | Y (Les 8 bits les plus significatifs) |
4 | Pression (Les 8 bits les moins significatifs) |
5 | Bits 7-4: Bits d'état (0x4 lorsque le crayon est détecté, sinon 0xC) Bits 3-0: Pression (les 4 bits les plus significatifs) |
- X varie entre 0 et 10206 (de gauche à droite)
- Y varie entre 0 et 7422 (de haut en bas)
- La pression varie de 0 (pas de pression) à 2047
J'ai écrit du code qui extrait et affiche les informations de manière plus lisible:
void wusbmotelib_bufferToDrawsomeData(const uint8_t *buf, drawsome_tablet_data *dst)
{
dst->x = buf[0] | buf[1]<<8;
dst->y = buf[2] | buf[3]<<8;
dst->pressure = buf[4] | (buf[5]&0x0f)<<8;
dst->status = buf[5]>>4;
}
...
case ID_DRAWSOME:
wusbmotelib_bufferToDrawsomeData(status, &drawsome_data);
printf("X: %6d, Y: %6d, P=%3d, status: 0x%02x (%s)\n",
drawsome_data.x,
drawsome_data.y,
drawsome_data.pressure,
drawsome_data.status,
drawsome_data.status & 0x08 ? "Pen out of range":"Pen in range"
);
...
Et tout semble être au bon endroit. Succès!
Grâce à ces informations, le firmware version 2.2 pour l'
Adaptateur manette Classique à USB (raphnet-tech) permettra d'utiliser la tablette comme une souris.