Tablettes graphiques pour Wii

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:

Tablette uDraw

Tablette uDraw

Tablette Drawsome

Tablette Drawsome




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):

Crayon uDraw

Crayon uDraw



Mais la version Drawsome du crayon n'offre aucun boutons:
Crayon Drawsome

Crayon Drawsome




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:

Événemenet bruts

Événemenet bruts



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.

OctetSignification
0X (Les 8 bits les moins significatifs)
1Y (Les 8 bits les moins significatifs)
2Bits 7-4: Y (4 bits les plus significatifs)
Bits 3-0: X (les 4 bits les plus significatifs.
3Pression 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:
  1. Écrire 0x01 à l'adresse 0xFB
  2. É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:

Événemenet bruts

Événemenet bruts



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.

OctetSignification
0X (Les 8 bits les moins significatifs)
1X (Les 8 bits les plus significatifs)
2Y (Les 8 bits les moins significatifs)
3Y (Les 8 bits les plus significatifs)
4Pression (Les 8 bits les moins significatifs)
5Bits 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.




Références

Attention: En date du 5 mars 2019, l'information sur wiibrew est erronée [5 mars 2019]. Le format exposé est en fait celui de la tablette uDraw... Mais d'ici peu, j'ai l'intention de créer un compte et d'ajouter tout ce que je connais sur les tablettes Wii.