Ciao a tutti
è un po’ che ero fermo, ma si sa, le vacanze servono proprio a quello… a prendersi una meritata pausa.
Come al solito avevo ordinato un pezzo interessante ma di cui non sapevo cosa fare.
Si tratta di un display OLED con connessione I2C, che meriterebbe una trattazione a parte, e me ne occuperò di sicuro…
Avevo anche da parte un telaio con due motori per una automobile a due ruote motrici.

Nunchuck Wii
L’idea è stata istantanea.
Il solito Nunchuck utilizza, guarda caso, la stessa interfaccia I2C per la comunicazione con Arduino. Proprio per questo ho deciso di utilizzarlo per comandare la macchina e farla andare un po’ dove voglio…
L’interfaccia I2C è molto potente. Si tratta di una interfaccia seriale che con solo 4 pin vi permette di collegare parecchi dispositivi in parallelo.
Quindi ricapitolando la mia auto avrebbe avuto alcuni sistemi indipendenti e correlati tra loro tramite Arduino.
- il Nunchuck collegato tramite I2C
- un chip L293D come interfaccia verso i due motori
- i due motori
- un display OLED da 0.96″ con interfaccia I2C, con funzioni di debug
Di seguito un video di presentazione del funzionamento del progetto:
Il Nunchuck e il bus I2C
Per delucidazioni sul Nunchuck e sul bus I2C fate riferimento a questo articolo.
Il chip L293D e i due motori
Il chip L293D è un integrato molto utile per controllare motori e separare le alimentazioni usate per la logica di controllo da quella utilizzata per i motori.
L293 pinout
Il funzionamento di questo chip è molto semplice, a prescidere da quelle che potrebbe apparire a prima vista.
Ho collegato uno dei due motori al lato sinistro del chip (pin 3 e 6) e l’altro al lato destro (pin 11 e 12).
Arduino controlla lo stato di quei pin in due modi.
- Può abilitare o disabilitare uno dei due lati fornendo un ingresso alto o basso al pin 1 (lato sinistro) o al pin 2 (lato destro)
- fa passare la corrente ai pin 3, 6, 11, 14 a seconda dello stato rispettivamente dei pin 2, 7, 10, 15.
L’alimentazione Vcc1 è il riferimento per i motori, Vcc2 è il riferimento per la logica di controllo.
La messa a terra è comune ai due circuiti.
La tabella della verità per ciascuno dei due lati è quella qui di seguito

Tabella della verità per ciascuno dei due motori.
Nella tabella le colonne A e B rappresentano rispettivamente il valore logico sui pin 2 e 7 (motore 1, lato sinistro) o sui pin 10 e 15 (motore 2, lato destro). In questo modo si può quindi definire direzione e azionamento dei motori e di conseguenza se il rover si debba muovere o meno e in che direzione.
Per decidere invece la velocità dei motori, si deve agire sui pin di ENABLE (pin 1 per il lato sinistro e pin 9 per il lato destro) con un ingresso PWM. In questo modo i motori non andranno a velocità massima, ma avranno una velocità proporzionale al duty-cycle della PWM.
Il display OLED
Il display OLED mi ha procurato parecchi mal di testa, almeno fino a che non ho capito di che display si trattava (sí, si trattava di un acquisto compulsivo! 😉 ). È più o meno come quello in foto.

Il display ha queste caratteristiche:
- 0.96″
- bicolore (giallo nella parte superiore, blu in quella inferiore). Sì proprio bicolore, con due zone di colori diversi.
- risoluzione 128×64 (di cui 16 righe nella zona gialla e le restanti 48 nella zona blu)
- Interfaccia I2C
Quando ho capito che era basato sul controller SSD1306, ho capito anche come comandarlo.
Ho scoperto la libreria U8G, una libreria molto potente e che supporta un sacco di display. La potete trovare a questo link.
Questa libreria consente di utilizzare delle funzioni grafiche primitive molto potenti con estrema semplicità. Alcuni esempi di comandi: drawBox
disegna un rettangolo, drawCircle
disegna un cerchio, drawLine
disegna una linea, setFont
imposta il font, ecc.).
Lo schema elettrico
Di seguito riporto lo schema del progetto se per caso voleste cimentarvi nel costruirlo.

Il codice
Di seguito incollo il codice che ho scritto per il funzionamento del rover.
/* This code is based on the following code: - test code for the http://code.google.com/p/u8glib/ library */ // include the library code: #include <U8glib.h> #include <Wire.h> #include <string.h> #include <stdio.h> // initialize the library with the numbers of the interface pins U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NO_ACK); // Display which does not send ACK int disp_w = 128; int disp_h = 64; /* Connessione L293D ---- pin3 ----------------- ENABLE1 -| |- VSS ------- L293D power supply pin4 ----------------- INPUT1 -| |- INPUT4 ------------------ pin8 motor 1 + ------------ OUTPUT1 -| |- OUTPUT4 ------------- motor2 + GND ----------------- GND -| |- GND ---------------------- GND GND ----------------- GND -| |- GND ---------------------- GND motor 1 - ------------ OUTPUT2 -| |- OUTPUT3 ------------- motor2 - pin2 ----------------- INPUT2 -| |- INPUT3 ------------------ pin7 motor pwr supply ----- VS -| |- ENABLE2 ----------------- pin6 ---- */ // Porte Motori (L293D) int en1Pin=3; // pin 1 on L293 int in1Pin=4; // pin 2 on L293 int in2Pin=2; // pin 7 on L293 int en2Pin=6; // pin 9 on L293 int in3Pin=7; // pin 10 on L293 int in4Pin=8; // pin 15 on L293 // this comes from nunchuck init uint8_t outbuf[6]; int joy_x_axis; int joy_y_axis; int accel_x_axis; int accel_y_axis; int accel_z_axis; int z_button = 0; int c_button = 0; int t = 0; int i = 0; int cnt = 0; int dtime = 50; // Prepare U8G library regular font void u8g_prepare(void) { u8g.setFont(u8g_font_6x10); u8g.setFontRefHeightExtendedText(); u8g.setDefaultForegroundColor(); u8g.setFontPosTop(); } // Prepare U8G library Micro font void u8g_prepare_micro(void) { u8g.setFont(u8g_font_micro); u8g.setFontRefHeightExtendedText(); u8g.setDefaultForegroundColor(); u8g.setFontPosTop(); } void setup(){ // init serial port Serial.begin(9600); Serial.println( "Nunchuck + OLED + L293D + Car"); // init nunchuck interface and hardware Wire.begin (); nunchuck_init (); // init pins pinMode(en1Pin, OUTPUT); pinMode(in1Pin, OUTPUT); pinMode(in2Pin, OUTPUT); pinMode(en2Pin, OUTPUT); pinMode(in3Pin, OUTPUT); pinMode(in4Pin, OUTPUT); u8g_prepare(); u8g.firstPage(); do { u8g.setColorIndex(1); u8g.drawBox(0, 0, disp_w-1, (disp_h/4)-1); u8g.setColorIndex(0); u8g.drawStr( int((disp_w - u8g.getStrWidth("Nunchuck + OLED")) / 2), 4, "Nunchuck + OLED"); } while( u8g.nextPage() ); Serial.print ("Finished setup\n"); delay (1000); } /* void drawStrMid(int y, char words, int disp_w){ u8g.drawStr( int((disp_w - u8g.getStrWidth(words)) / 2), y, words); } */ void nunchuck_init(){ Wire.beginTransmission (0x52); Wire.write (0x40); Wire.write (0x00); Wire.endTransmission (); } void send_zero(){ Wire.beginTransmission (0x52); Wire.write (0x00); Wire.endTransmission (); } void decodeNunchuckData() { joy_x_axis = outbuf[0]; joy_y_axis = outbuf[1]; accel_x_axis = outbuf[2] * 2 * 2; accel_y_axis = outbuf[3] * 2 * 2; accel_z_axis = outbuf[4] * 2 * 2; z_button = 0; c_button = 0; if ((outbuf[5] >> 0) & 1) z_button = 1; if ((outbuf[5] >> 1) & 1) c_button = 1; if ((outbuf[5] >> 2) & 1) accel_x_axis += 2; if ((outbuf[5] >> 3) & 1) accel_x_axis += 1; if ((outbuf[5] >> 4) & 1) accel_y_axis += 2; if ((outbuf[5] >> 5) & 1) accel_y_axis += 1; if ((outbuf[5] >> 6) & 1) accel_z_axis += 2; if ((outbuf[5] >> 7) & 1) accel_z_axis += 1; } void loop(){ t++; long last = millis(); if( t == 1) { t = 0; Wire.requestFrom (0x52, 6); while (Wire.available ()) { outbuf[cnt] = nunchuk_decode_byte (Wire.read ()); cnt++; } if (cnt >= 5) { int z_button = 0; int c_button = 0; if ((outbuf[5] >> 0) & 1) z_button = 1; if ((outbuf[5] >> 1) & 1) c_button = 1; } cnt = 0; send_zero(); } // if(t==) decodeNunchuckData(); drawNunchuckData(); runMyCar(); delay(dtime); } void printNunchuckData() { Serial.print (i,DEC); Serial.print ("\t"); 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"); i++; } void drawNunchuckData() { int jx = map (joy_x_axis, 0, 256, 2, (disp_w/2)-3); int jy = map (joy_y_axis, 0, 256, disp_h-3, (disp_h/4)+2); int ax = map (accel_x_axis, 0, 1024, (disp_w/2)+1, disp_w-2); int ay = map (accel_y_axis, 0, 1024, disp_h-3, (disp_h/4)+2); int az = map (accel_z_axis, 0, 1024, disp_w/4, 3*(disp_w/4)); u8g.firstPage(); do { // draw debug area u8g_prepare_micro(); drawLabelValue(13, 1, "jX: ", joy_x_axis); drawLabelValue(49, 1, "jY: ", joy_y_axis); drawLabelValue(86, 1, "set: ", i); drawLabelValue(13, 7, "aX: ", accel_x_axis); drawLabelValue(49, 7, "aY: ", accel_y_axis); drawLabelValue(86, 7, "aZ: ", accel_z_axis); u8g.drawBox (0,0,10,14); u8g_prepare(); u8g.setColorIndex(0); u8g.drawStr(2, 3, "Z"); if(z_button) { u8g.drawBox(1, 1, 8, 12); } u8g.setColorIndex(1); u8g.drawBox (118,0,10,14); u8g.setColorIndex(0); u8g.drawStr(120, 3, "C"); if(c_button) { u8g.drawBox(119, 1, 8, 12); } //draw graphic area u8g.setColorIndex(1); u8g.drawVLine(31,16,46); u8g.drawHLine(1,40,62); u8g.drawDisc(jx,jy,2); u8g.drawVLine(95,16,46); u8g.drawHLine(65,40,62); u8g.drawDisc(ax,ay,2); if (az > 63){ u8g.drawBox(63,16,az-63,3); } else { u8g.drawBox(az,16,63-az,3); } } while( u8g.nextPage() ); i++; } char nunchuk_decode_byte (char x) { x = (x ^ 0x17) + 0x17; return x; } void drawLabelValue(int x, int y, char *label, int val) { u8g.drawStr (x, y, label); u8g.setPrintPos ( x + u8g.getStrWidth(label), y); u8g.print (val); } void runMyCar() { int motor; int brake; int fwd; int spd; int jx = map (joy_x_axis, 29, 225, 0, 10); int jy = (32 * map (joy_y_axis, 29, 225, -7, 7)) ; // remember that runMotor ( motor, brake, fwd, spd ) if (jx == 5 && jy == 0) { runMotor (1, HIGH, HIGH, 0); runMotor (2, HIGH, HIGH, 0); } else { if (jx == 5) { if (jy > 0) { runMotor (1, LOW, HIGH, abs(jy)); runMotor (2, LOW, HIGH, abs(jy)); } else { runMotor (1, LOW, LOW, abs(jy)); runMotor (2, LOW, LOW, abs(jy)); } } else { if (jy == 0) { if (jx < 5){ runMotor (1, LOW, HIGH, int((255*abs(5-jx))/5)); runMotor (2, LOW, LOW, int((255*abs(5-jx))/5)); } else { runMotor (1, LOW, LOW, int((255*abs(5-jx))/5)); runMotor (2, LOW, HIGH, int((255*abs(5-jx))/5)); } } else if (jy > 0) { if (jx < 5){ runMotor (1, LOW, HIGH, abs(jy)); runMotor (2, LOW, HIGH, int((jy*abs(5-jx))/5)); } else { runMotor (1, LOW, HIGH, int((jy*abs(5-jx))/5)); runMotor (2, LOW, HIGH, abs(jy)); } } else { // jy < 0 if (jx < 5){ runMotor (1, LOW, LOW, abs(jy)); runMotor (2, LOW, LOW, int((jy*abs(5-jx))/5)); } else { runMotor (1, LOW, LOW, int((jy*abs(5-jx))/5)); runMotor (2, LOW, LOW, abs(jy)); } } } } } void runMotor ( int motor, int brake, int fwd, int spd ) { int enPin = en1Pin; int fwdPin = in2Pin; int revPin = in1Pin; // motor 1 = motor A; motor 2 = motor B if ( motor == 2 ) { enPin = en2Pin; fwdPin = in3Pin; revPin = in4Pin; } if (brake == HIGH) { digitalWrite (fwdPin, HIGH); digitalWrite (revPin, HIGH); analogWrite (enPin, HIGH); } else if (fwd == LOW) { int tmp = fwdPin; fwdPin = revPin; revPin = tmp; } digitalWrite (fwdPin, HIGH); digitalWrite (revPin, LOW); analogWrite (enPin, spd); }
Arrivederci!!!
Pierluigi
Posso chiederti dove hai trovato lo schermo OLED per fritzing?
Io trovo soltanto le librerie di Adafruit, ma lo schermo ha 8 pin…
"Mi piace""Mi piace"
In realtà l’ho disegnato io
"Mi piace""Mi piace"
se dovesse servirti contattami pure direttamente, posso inviartelo.
"Mi piace""Mi piace"