beginner kit nunchuck di Michele

Il venerdì spesso trovi articoli dedicati a progetti realizzati da appassionati come Michele che ha realizzato il “beginner kit nunchuck” unendo le sue conoscenze sul beginner kit ed il nunchuck.

beginner kit nunchuck collegamenti

Michele è un appassionato arduino e dopo aver frequentato i miei corsi arduino in aula è impegnato a sperimentare nuovi sketch con la passione per la condivisione delle scoperte e delle conoscenze acquisite.

Il progetto che ha realizzato è il beginner kit nunchuck, un progetto che unisce beginenr kit e nunchuck.

Michele non è il primo progetto che condivide sul blog, ha già realizzato e presentato:

Descrizione del beginner kit nunchuck scritta da Michele

In questo articolo vedremo come connettere un nunchuck della Nintendo ad una scheda Arduino e come utilizzare i dati letti dal nunchuck per pilotare un robot.

beginner kit nunchuck

Innanzitutto cerchiamo di capire quali dati possiamo leggere e con quale metodo.

Il nunchuck ha un accelerometro a 3 assi che permette di stabilire quando il nunchuck stesso subisce un’accelerazione. Possiede inoltre un piccolo joystick e 2 pulsanti. Quello che può essere letto è:

  1. La pressione del pulsante A
  2. La pressione del pulsante B
  3. Il valore del joystick sull’asse X
  4. Il valore del joystick sull’asse Y
  5. L’accelerazione del nunchuck sull’asse X
  6. L’accelerazione del nunchuck sull’asse Y
  7. L’accelerazione del nunchuck sull’asse Z

Per comunicare con il nunchuck si deve utilizzare il protocollo di comunicazione I2C.

Detto cosa si può leggere e qual è il metodo iniziamo dai collegamenti elettrici.

Nell’immagine vedete il connettore del nunchuck:

beginner kit nunchuck connettore

Il nunchuck va connesso alla scheda Arduino in questo modo:

beginner kit nunchuck tabella

Per realizzarlo io utilizzo un connettore come quello mostrato nella figura:

beginner kit nunchuck adattatore

connesso al connettore del nunchuck:

beginner kit nunchuck adattatore montato

Il connettore può essere usato in un solo modo visto che il numero di tracce sui 2 lati è diverso.

Una volta effettuati i collegamenti elettrici con la scheda Arduino possiamo iniziare a cercare di capire come si leggono i valori.

Il nunchuck dialoga con Arduino tramite protocollo I2C. Il primo problema è che gli accelerometri restituiscono valori a 10 bit (da 0 a 1024) mentre il protocollo I2C può trasmettere solo byte. Ogni volta che si effettua una lettura dal nunchuck questo deve spedire 10 bit per i 3 accelerometri, 8 bit per i 2 assi del joystick e 2 bit per i 2 pulsanti per un totale di 48 bit (10+10+10+8+8+2).

Pertanto ogni volta che si chiede una lettura al nunchuck, questo restituirà 6 byte di valori da interpretare nel seguente modo:

beginner kit nunchuck tabella byte

Byte 0: asse X del joystick

Byte 1: asse Y del joystick

Byte 2: 8 bit alti del valore letto dall’accelerometro X

Byte 3: 8 bit alti del valore letto dall’accelerometro Y

Byte 4: 8 bit alti del valore letto dall’accelerometro Z

Byte6:

        primo bit: pulsante Z (0 se premuto)

        secondo bit: pulsante C (0 se premuto)

        terzo e quarto bit: bit bassi del valore letto dall’accelerometro X

        quinto e sesto bit: bit bassi del valore letto dall’accelerometro Y

        settimo e ottavo bit: bit bassi del valore letto dall’accelerometro Z

Per capire meglio come interpretare i dati degli accelerometri facciamo l’esempio con l’accelerometro X. Per avere il valore esatto tra 0 e 1024 dobbiamo prendere il valore del byte 3 ed aggiungerci a destra gli ultimi 2 bit dati dal bit 2 e 3 del byte 6. Per farlo, moltiplichiamo per 4 il valore del byte 3 (sono i bit alti cioè i primi 8 bit) ed aggiungiamo i valori del numero espresso in binario dal terzo e quarto bit del byte 6 (11=3, 10=2, 01=1, 00=0). Quindi se per esempio nel byte 3 leggiamo 120 e i bit 2 e 3 del byte 6 valgono 1 e 0 il valore finale sarà: 120*4+2=482.

In realtà tale precisione non è necessaria e possiamo limitarci ad ignorare i bit in più del byte 6 considerando il valore degli accelerometri compreso tra 0 e 255 (solo gli 8 bit dei byte 3, 4 e 5) invece che tra 0 e 1024. In questo caso il valore intermedio corrispondente alla posizione in cui l’accelerazione è nulla sarà, circa, 128.

Come detto la comunicazione avviene mediante protocollo I2C.

Schema dei collegamenti

Michele mi ha inviato lo schema dei collegamenti per il nunchuck al beginner robot kit:

beginner kit nunchuck schema collegamenti

anche se è di certo più comodo un adattatore come quello che Michele ha utilizzato, con pochi euro risolvi facilmente il problema dei collegamenti:

beginner kit nunchuck collegamenti connettore

Primo test del nunchuck

Prima di pilotare il robot con il nuchuck vediamo lo sketch per la lettura dei dati da Arduino per comprendere come funziona la comunicazione:

#include <Wire.h>
#include <stdio.h>
uint8_t valori[6];
int contatore = 0;
int aspetta=1000;
void setup()
{
    Serial.begin (9600);
    Wire.begin ();
    inizializza ();
    Serial.print ("Finished setup\n");
}
void inizializza()
{
    Wire.beginTransmission (0x52);
    Wire.write (0x40);
    Wire.write (0x00);  
    Wire.endTransmission ();
}
void send_zero()
{
    Wire.beginTransmission (0x52);
    Wire.write (0x00);
    Wire.endTransmission ();
}
void loop()
{
    Wire.requestFrom (0x52, 6);
    while (Wire.available ()) {
            valori[contatore] = decodifica (Wire.read ());
            contatore++;
     }
    contatore = 0;
    send_zero();
    mostra_valori();
    delay(aspetta);
}
void mostra_valori()
{
    int joy_x_axis = valori[0];
    int joy_y_axis = valori[1];
    int accel_x_axis = valori[2];
    int accel_y_axis = valori[3];
    int accel_z_axis = valori[4];
    int z_button = 0;
    int c_button = 0;
    z_button = (valori[5] & 1);
    c_button =  (valori[5] & 2)/2;


    Serial.print ("X: ");
    Serial.print (joy_x_axis, DEC);
    Serial.print ("\t");
    Serial.print ("Y: ");
    Serial.print (joy_y_axis, DEC);
    Serial.print ("\t");
    Serial.print ("AccX: ");
    Serial.print (accel_x_axis, DEC);
    Serial.print ("\t");
    Serial.print ("AccY: ");
    Serial.print (accel_y_axis, DEC);
    Serial.print ("\t");
    Serial.print ("AccZ: ");
    Serial.print (accel_z_axis, DEC);
    Serial.print ("\t");
    Serial.print (z_button, DEC);
    Serial.print (" ");
    Serial.print (c_button, DEC);
    Serial.print ("\r\n");
}
char decodifica (char x)
{
    x = (x ^ 0x17) + 0x17;
    return x;
}

Esaminiamo il codice.

Innanzitutto va detto che deve essere utilizzata la libreria Wire.h (linea 1) per gestire la comunicazione I2C.

I valori vengono restituiti in un array di 6 byte definiti come interi senza segno ad 8 bit nel codice (linea 3).

Linee 6 – 12: inizializzazione. Inizializza la seriale, aggiunge Arduino al bus di comunicazione I2C come Master (linea 9) ed inizializza il nunchuck (linea 10)

Linee 13-19: inizializzazione nunchuck. Per prima cosa inizia la trasmissione verso il device con indirizzo (esadecimale) 52 che corrisponde al nunchuck. Trasmette poi i valori 40 (esadecimale) e 0 per inizializzare il nunchuck e chiude la trasmissione.

Linee 20-25: fine trasmissione. Inviando al nunchuck il valore 0 ferma l’invio dati dal nunchuck.

Linee 26-37: loop. Richiede 6 byte al device all’indirizzo 52 cioè il nunchuck (linea 28). Valorizza nell’array valori i dati letti (byte a byte) dal cunchuck e li decodifica (riga 30). Parleremo della decodifica in seguito. Azzera la variabile utilizzata per memorizzare i dati letti (riga 33) comunica al nunchuck il termine della lettura (riga 34) e richiama la funzione per stampare sul monitor seriale i valori letti (riga 35).

Linee 38-70: mostra valori. Vale la pena evidenziare come solo gli 8 bit più significativi degli accelerometri siano mostrati (quindi i valori varieranno tra 0 e 255 con intermedio intorno a 128). Inoltre le linee da 45 a 48 mostrano la pressione dei tasti C e Z del nunchuck. Quando non sono premuti questi valgono 1 mentre, quando sono premuti, il bit 0 e il bit 1 assumono valore 0. Per questo motivo, nella linea 47 il valore del pulsante è dato dal valore letto in AND con il valore 1 (se il bit vale 0 il risultato è 0, se il bit 0 vale 1 il risultato è 1) mentre alla riga 48 il valore letto viene messo in AND col valore 2 per cui, se il bit 1 è acceso, il risultato dell’AND è 2 altrimenti 0 e poi diviso per 2 per avere i valori 0, premuto, 1 non premuto.

Linee 71-75 decodifica. Sul tema ci sono in rete pareri contrastanti. Diciamo che la decodifica per il nunchuck significa prendere il valore di ogni singolo byte, metterlo in XOR con il valore 17 esadecimale (23 decimale) e sommargli il valore 17 (sempre esadecimale). Alcuni sostengono che sia inutile io ho riscontrato, con i miei 2 nunchuck, che per il joystick leggo valori alquanto strani se non li decodifico (non progressività dei valori nelle posizioni intermedie del joystick, valori che dal minimo passano al massimo se inclino il nunchuck pur mantenendo il joystick nella medesima posizione) quindi, almeno per i miei modelli di nunchuck, la decodifica è necessaria.

Il valore minimo per l’accelerometro X è dato dal nunchuck inclinato verso sx (o spostato rapidamente a sx) mentre il valore massimo è dato dal nunchuck inclinato verso dx o spostato rapidamente a dx.

Per l’accelerometro Y il valore minimo si ha inclinando il nunchuck verso l’alto mentre il valore massimo si ha inclinandolo verso il basso.

Per i joystick il valore minimo della X si ottiene spostandolo a sx mentre il valore minimo per la Y si ottiene tirando il joystick indietro.

Nota importante: inclinando il nunchuck varierà anche il valore dell’accelerometro Z il cui valore massimo si ha con il nunchuck “dritto” ed il minimo col nunchuck capovolto.

E’ arrivato il momento di collegare il nunchuck al robot costruito con il robot beginner kit.

Ho preferito pilotare il robot utilizzando il joystick e non inclinando il nunchuck in quanto più semplice da controllare considerando che il cavo ha una lunghezza limitata e che il robot diventa filoguidato e quindi complicato da pilotare inclinando il nunchuck.

Con pochissime modifiche è però possibile pilotarlo nell’altro modo.

Non mi dilungherò con i dettagli elettrici e di contorno che trovate negli altri articoli:

  • https://www.mauroalfieri.it/elettronica/robot-beginner-kit-inseguitore.html
  • https://www.mauroalfieri.it/elettronica/robot-beginner-kit-fotoresistenze.html

Lo sketch del beginner kit nunchuck

Riporto l’intero sketch ma commenterò solo le parti più interessanti:

#include <Wire.h>
#include <stdio.h>

#define PWMA 5
#define PWMB 6
#define AIN1 1
#define AIN2 2
#define BIN1 8
#define BIN2 9
#define STBY 0

uint8_t dati[6];
int cnt = 0;
int lettura = 0;
int correzione = 10;
int pausa=100;
int minx,miny,maxx,maxy;
int velx,vely;

void inizializza_nunchuck()
{
  Wire.beginTransmission (0x52);
  Wire.write (0x40);
  Wire.write (0x00);  
  Wire.endTransmission ();
}

void send_zero()
{
  Wire.beginTransmission (0x52);
  Wire.write (0x00);
  Wire.endTransmission ();
}

void setup()
{
  Wire.begin ();
  inizializza_nunchuck ();
  vely=100;
  pinMode( STBY, OUTPUT );
  pinMode( PWMA, OUTPUT );
  pinMode( PWMB, OUTPUT );
  pinMode( AIN1, OUTPUT );
  pinMode( AIN2, OUTPUT );
  pinMode( BIN1, OUTPUT );
  pinMode( BIN2, OUTPUT );
  digitalWrite( STBY, HIGH );
  analogWrite( PWMA, vely );
  analogWrite( PWMB, vely );
  minx=50;
  miny=50;
  maxx=200;
  maxy=200;
}

void loop()
{
  int avanza,destra;
  lettura++;
  Wire.requestFrom (0x52, 6);

  while (Wire.available ()) {
    dati[cnt] = decodifica (Wire.read ());
    cnt++;
  }

  if (cnt >= 5) {
    cnt = 0;
    send_zero();
   } 
  if (lettura > 5) aggiorna_estremi(dati[0],dati[1]);
  velx=map(dati[0],minx,maxx,-200,200);
  vely=map(dati[1],miny,maxy,-200,200);
  if (velx>200) velx=200;
  if (vely>200) vely=200;
  if (vely>0) avanza = 1;
  else   {
    vely=-vely;
    avanza=0;
  }
  if (velx>0) destra = 1;
  else   {
    velx=-velx;
    destra=0;
  }

  if (vely>velx)
  {
    if (vely<70) vely=0;
    if ((avanza==1)&&(vely>0)) avanti();
    else if (vely>0) indietro();
    else alt();
  } 
  else
  {
    if (velx<70) velx = 0;
    if ((destra==1)&&(velx>0)) giradx();
    else if (velx>0) girasx();
    else alt();
  }

  delay(pausa);
}
char aggiorna_estremi (int x, int y)
{
  if (x<minx) minx=x;
  if (y<miny) miny=y;
  if (x>maxx) maxx=x;
  if (y>maxy) maxy=y;

}

void avanti()
{
  int vel2;
  vel2=vely+vely/correzione;
  analogWrite( PWMA, vel2 );
  analogWrite( PWMB, vely );
  digitalWrite( AIN1, LOW );
  digitalWrite( AIN2, HIGH );
  digitalWrite( BIN1, LOW );
  digitalWrite( BIN2, HIGH );
}
void indietro()
{
  int vel2;
  vel2=vely+vely/correzione;
  analogWrite( PWMA, vel2 );
  analogWrite( PWMB, vely );
  digitalWrite( AIN1, HIGH );
  digitalWrite( AIN2, LOW );
  digitalWrite( BIN1, HIGH );
  digitalWrite( BIN2, LOW );
}

void alt()
{
  digitalWrite( AIN1, LOW );
  digitalWrite( AIN2, LOW );
  digitalWrite( BIN1, LOW );
  digitalWrite( BIN2, LOW );
}

void girasx() {
  analogWrite( PWMA, 100 );
  analogWrite( PWMB, 100 );
  digitalWrite( AIN1, LOW );
  digitalWrite( AIN2, HIGH );
  digitalWrite( BIN1, HIGH );
  digitalWrite( BIN2, LOW );
}

void giradx() 
{
  analogWrite( PWMA, 100 );
  analogWrite( PWMB, 100 );
  digitalWrite( AIN1, HIGH );
  digitalWrite( AIN2, LOW );
  digitalWrite( BIN1, LOW );
  digitalWrite( BIN2, HIGH );
}

char decodifica (char x)
{
  x = (x ^ 0x17) + 0x17;
  return x;
}

Linee 35-54, setup: oltre alle operazioni “standard” inizializzo a 50 e 200 i valori minimi e massimi per i confronti con i valori letti dal nunchuck (minx, miny, maxx, maxy).

Linee 56-103, Loop: poiché per ogni nunchuck i valori minimo e massimo letti possono variare, ci sono 2 possibili strategie per determinare quanto il joystick è distante dalla posizione di riposo (in avanti, a sx, a dx o indietro): leggere prima i valori per quel modello ed impostarli nel codice oppure “tarare” i valori in corso di funzionamento. Per questo motivo ho impostato dei valori minimo e massimo fittizi che dovrebbero essere facilmente superati dalle letture reali e li aggiorno (funzione aggiorna estremi in seguito) ad ogni lettura se vengono letti dei nuovi minimi o massimi. La lettura dal nunchuck, però, può essere poco significativa appena si instaura la comunicazione quindi, prima di aggiornare i valori minimi e massimi, lascio trascorrere 5 cicli da 100 ms dopodiché inizierò a considerarli significativi. L’incremento della variabile lettura alla riga 59 unito al controllo alla riga 71 assolve questo scopo.

Alle righe 72 e 73 rimappo i valori letti nell’intervallo [-200, 200] cosi ché per posizioni centrali del joystick i valori rimappati saranno vicini allo 0, mentre se il joystick è:

        tutto a sinistra, velx assumerà un valore prossimo a -200

        tutto a dx, velx assumerà un valore prossimo a 200

        in avanti, vely assumerà un valore prossimo a 200

        indietro, vely assumerà un valore prossimo a -200

Nelle righe da 76 a 80, basandomi sul segno di vely, valorizzo avanza a 1 se il robot deve andare avanti (vely>0) mentre la valorizzo a 0 se deve andare indietro (vely<0 quindi provvedo anche a cambiare segno a vely).

Nelle righe da 81 a 85 eseguo controlli analoghi ma non dx e sx invece che con avanti e indietro.

Nelle righe da 87 a 100 controllo se lo scostamento del joystick è maggiore lungo l’asse X o quello Y. Se è maggiore lungo l’asse Y allora lo faccio avanzare o indietreggiare altrimenti lo faccio girare. Non ho combinato le 2 cose cambiando la velocità di uno solo dei motori per varie difficoltà tecniche dovute all’hardware utilizzato (i motori non girano sotto una certa velocità (vedi anche la riga 89: se la velocità < 70 la azzero del tutto proprio per questo motivo), se si impostano i motori alla stessa velocità il mio robot non va dritto (vedi correzioni alle righe 116 e 127), ecc.).

Righe 104-110: aggiorna gli estremi massimi e minimi dei valori letti dal nunchuck per eseguire la mappatura con valori reali rendendo non necessaria la fase di lettura dei valori alla scoperta dei range del particolare nunchuck utilizzato come indicato precedentemente.

Il video del beginner kit nunchuck

Michele ga realizzato un fantastico video dimostrativo con la collaborazione del figlio: il pilota

  • Questo sito ed i suoi contenuti è fornito "così com'è" e Mauro Alfieri non rilascia alcuna dichiarazione o garanzia di alcun tipo, esplicita o implicita, riguardo alla completezza, accuratezza, affidabilità, idoneità o disponibilità del sito o delle informazioni, prodotti, servizi o grafiche correlate contenute sul sito per qualsiasi scopo.
  • Ti chiedo di leggere e rispettare il regolamento del sito prima di utilizzarlo
  • Ti chiedo di leggere i Termini e Condizioni d'uso del sito prima di utilizzarlo
  • In qualità di Affiliato Amazon io ricevo un guadagno dagli acquisti idonei qualora siano presenti link al suddetto sito.

Permalink link a questo articolo: https://www.mauroalfieri.it/elettronica/beginner-kit-nunchuck-di-michele.html

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.