Adaptateur Nintendo NunChuk

Ce tutoriel explique comment mettre en oeuvre un adaptateur Grove pour manette Nintendo NunChuk.

Description

La NunChuk est une manette supplémentaire venant se connecter à la WiiMote (une manette destinée à être utilisée pour la console Nintendo Wii). Elle comporte :

  • Un joystick 2 axes (x, y)
  • Un accéléromètre 3 axes (x, y, z)
  • Des boutons poussoirs (C et Z)

Elle communique en liaison I2C avec la WiiMote ; un protocole de communication ne nécessitant que 4 fils (5V, GND, SDA, SCL).

Matériel requis

  1. Une carte d’extension de base Grove
  2. La carte NUCLEO-WB55
  3. Un module adaptateur Nintendo NunChuk Grove

L’adaptateur NunChuk Grove :

Grove - Nintendo NunChuk Adapter

Crédit image : Seeed Studio

Il faut ensuite brancher la manette à un connecteur I2C du Grove Base Shield.

Le code MicroPython

Les scripts présentés ci-après sont disponibles dans la zone de téléchargement.

Etape 1 : Pour faire fonctionner le programme nous devons dans un premier temps importer 3 bibliothèques au tout début de notre code de cette façon :

from machine import I2C, Pin
from time import sleep_ms
from wiichuck import WiiChuck

Etape 2 : Ensuite il faut les initialiser en rajoutant ces lignes de codes. La première ligne précise quel canal I2C nous utilisons (dans notre cas le canal 1). La deuxième ligne de code définit une variable wii qui récupèrera les informations retournées par la bibliothèque.

i2c = I2C(1)

# Pause d'une seconde pour laisser à l'I2C le temps de s'initialiser
time.sleep_ms(1000)

wii = WiiChuck(i2c)

Etape 3 : Enfin nous affichons les informations retournées par la manette sous forme de tableau. Celui-ci sera affiché dans notre terminal au sein d’une “boucle infinie” (CTRL+C pour l’interrompre).

while True:
	direction = ''
	if wii.joy_up:
		direction = 'Haut'
	elif wii.joy_down:
		direction = 'Bas'
	elif wii.joy_right:
		direction = 'Droite'
	elif wii.joy_left:
		direction = 'Gauche'
	else:
		direction = '-----'
	if(wii.c):
		Cbouton = 'C'
	else:
		Cbouton = '-'
	if(wii.z):
		Zbouton = 'Z'
	else:
		Zbouton = '-'

	print("Joystick: (%3d, %3d) %6s \t| Accelerometre XYZ: (%3d, %3d, %3d) \t| Boutons: %s %s" %(wii.joy_x, wii.joy_y, direction, wii.accel_x, wii.accel_y, wii.accel_z, Cbouton, Zbouton))

	wii.update()
	sleep_ms(100)

Résultat

Et voilà ! Nous pouvons à présent observer le résultat avec les données extraites du NunChuk sous cette forme :

Affichage des données du NunChuk


Faites un mouvement avec le NunChuk et vous devriez voir les valeurs de l’accéléromètre varier selon l’axe sur lequel vous l’avez déplacé. Vous pouvez également faire bouger le joystick et voir où celui-ci se situe. De plus vous avez un retour d’informations sur l’état des boutons poussoirs.

Pour aller plus loin

Dans cet exercice nous avons vu l’affichage des données du NunChuk. Pour aller plus loin nous pouvons, par exemple, récupérer ces données et les traiter pour activer des servomoteurs et moteurs en fonction de la direction du joystick.

Le but ici est de récupérer le déplacement du joystick sur l’axe horizontal (axe x) et faire tourner un servomoteur de façon très précise. Nous utiliserons également l’axe vertical (axe y) pour faire tourner un moteur dans un sens ou dans l’autre.

Ce code peut nous servir de base si on souhaite créer un véhicule capable de se déplacer avec les moteurs pour le faire avancer/reculer et le servomoteur pour controler sa direction.

Montage

On reprend le montage précédent avec la manette auquel ajoute un servomoteur et un moteur. Si vous n’avez pas encore abordée la partie sur le servomoteur et le moteur je vous invite à les consulter afin de mieux comprendre cet exercice.

Schema pour aller plus loin NunChuk


La correspondance entre le servomoteur et la carte NUCLEO-WB55 est la suivante :

Servomoteur ST Nucleo
Signal D6
GND GND
3.3V 3.3V

La correspondance entre le servomoteur et la carte NUCLEO-WB55 est la suivante :

Driver moteur ST Nucleo Moteur Alim externe
Enable 1 D1    
Input 1 D0    
Output 1   Oui  
GND GND   GND
GND      
Output 2   Oui  
Input 2 D2    
VS     5V

Afin de fournir la puissance adéquate aux moteurs on vient les alimenter grâce à une alimention externe avec, par exemple, une batterie ou une pile.

Le code MicroPython

Les scripts présentés ci-après sont disponibles dans la zone de téléchargement.

Etape 1 : Pour faire fonctionner le programme nous devons dans un premier temps importer 4 bibliothèques au tout début de notre code de cette façon :

import pyb
from machine import I2C
from time import sleep_ms
from wiichuck import WiiChuck
from pyb import Pin, Timer
import math

Etape 2 : On initialise ensuite la connection avec le servomoteur sur le port D6 auquel on applique une fréquence de 50Hz (soit une période de 20ms) provenant du timer 1 sur le canal 3. On configure les pattes du driver moteur et applique une fréquence de 1000Hz provenant du timer 2 canal 3. On initialise la liaison I2C avec la manette en précisant quel canal nous utilisons (dans notre cas le canal 1). Enfin on définit une variable wii qui récupèrera les informations retournées par la bibliothèque.

#Initilisation servomoteur (Timer 1 channel 3, fréquence = 50 Hz)
servo = pyb.Pin('D6')
tim_servo = pyb.Timer(1, freq=50)

#Driver moteur ("enable" + "input 1" et "input 2" en sorties)
moteur_enable = pyb.Pin('D1')
moteur_pin1 = pyb.Pin('D0', Pin.OUT_PP)
moteur_pin2 = pyb.Pin('D2', Pin.OUT_PP)
moteur_pin1.low()
moteur_pin2.low()

#PWM pour le moteur (Timer 2 channel 3, fréquence = 1kHz et sur "enable")
tim2 = Timer(2, freq=1000)
ch3 = tim2.channel(3, Timer.PWM, pin=moteur_enable)

#PWM pour régler la tension du moteur
ch3.pulse_width_percent(0)

#Initialisation Nunchuk en I2C
i2c = I2C(1)
sleep_ms(1000)
wii = WiiChuck(i2c)

Etape 3 : Nous utilisons une boucle infinie qui nous permettra de contrôler le servomoteur en continu. L’axe X du joystick peut avoir des valeurs comprises entre -100 (orienté à gauche) et 100 (orienté à droite) tandis que le pulse_width_percent du servomoteur n’accepte que des valeurs comprises entre 2,5 et 12,5. Il faut donc trouver un rapport entre les deux. Pour un angle total plus petit que 180° on augmente la valeur du Joystick de +200 afin que toutes les valeurs deviennent positives et on divise le tout par 27. De cette façon si on reprend les calculs avec la formule suivante :

pulse_width_percent = (wii.joy_x + offset) / x

Avec nos valeurs on obtient :

  • Minimum : (-100 + 200) / 27 = 3,7% –> soit proche de 2,5% qui correspond à un angle de -90°
  • Medium : (0 + 200) / 27 = 7,4% –> soit proche de 7,5% qui correspond à un angle de 0°
  • Maximum : (100 + 200) / 27 = 11,1% –> soit proche de 12,5% qui correspond à un angle de 90°

Enfin vous l’avez compris, modifier ce calcul permettra de modifier les angles d’inclinaison du servo-moteur.

Dans notre cas nous avons fait exprès d’avoir des extrema moins importants que les vrais extrema car, avec la précision du calcul, le servomoteur peut se retrouver en butée d’un côté mais pas de l’autre. Cependant voici la formule si vous voulez essayer un angle (presque parfait) de 180° :

(wii.joy_x + 150) / 20

Et si on reprend les calculs on obtient :

  • Minimum : (-100 + 150) / 20 = 2,5%
  • Medium : (0 + 150) / 20 = 7,5%
  • Maximum : (100 + 150) / 20 = 12,5%

Pour revenir à notre code :

while True:
    #Si mouvement sur l'axe X alors fait tourner le servo
    joystick_x = (wii.joy_x + 200)/26
    tim_servo.channel(1, pyb.Timer.PWM, pin=servo, pulse_width_percent=joystick_x)

Etape 4 : Toujours dans la boucle while on vient cette fois contrôler le moteur en vérifiant l’axe y du joystick. Pour rappel l’axe renvoie une valeur comprise en -100 au minimum, 100 au maximum et environ 0 au repos. Si celui ci est poussé vers le bas alors on active la sortie input 1 et on lit la valeur de l’axe y. Dans ce cas là cette valeur est comprise entre 0 et -100. Afin de la ramener de 0 à 100 on prend sa valeur absolue. On procède de même si on pousse le joystick vers le haut ; on active la sortie input 2 sans besoin cette fois-ci de valeur absolue. Enfin, au repos, on arrête tout.

    #Si mouvement sur l'axe Y 
    if(wii.joy_y < -5):             #Vers le bas
        moteur_pin1.high()
        moteur_pin2.low()
        ch3.pulse_width_percent(abs(wii.joy_y))
    elif(wii.joy_y > 5):            #Vers le haut
        moteur_pin1.low()
        moteur_pin2.high()
        ch3.pulse_width_percent(wii.joy_y)
    else:                           #Au repos
        moteur_pin1.low()
        moteur_pin2.low()
        ch3.pulse_width_percent(0)

    wii.update()

Résultat

Vous êtes maintenant en mesure de contrôler un servomoteur et un moteur à l’aide du joystick d’une manette Wii de façon assez simple. A partir de cette base vous pouvez même concevoir un mini-véhicule capable de se déplacer à droite ou à gauche par le joystick en utilisant le servomoteur relié à un axe de direction des roues. Si vous souhaitez continuer l’exercice vous pouvez par exemple câbler un deuxième moteur et le programmer de la même manière que nous venons de l’expliquer.