Oggi aggiungiamo una lezione al nostro corso Programmare con Arduino e Protocoder per makers, nella quale impareremo ad usare matrici di LED per creare animazioni per il nostro robot. In questo caso, useremo la matrice per creare un occhio che controlleremo da remoto usando Protocoder.
Lista dei materiali
- Scheda ZUM BT-328
- Matrice di LED: Questa volta utilizzeremo una scheda basata sul chip MAX72xx.
- Comodoro: Scarica i files dei nostri robots che trovi alla fine del post.
Codice di programma per Arduino
Il codice di Arduino riceve i dati dal programma Protocoder e muove gli occhi in specifiche posizioni, che dipendono dai comandi ricevuti. Gli occhi lampeggeranno a caso per un certo periodo. Per controllare la matrice di LED useremo la libreria LedControl, che puoi scaricare qui oppure insieme al resto del codice alla fine di questo post. Se vuoi conoscere tutte le possibilità offerte da questa libreria, puoi trovare maggiori informazioni nella pagina Arduino Playground. Per iniziare a programmare con Arduino, dovremo includere la libreria, definire i pins e creare un oggetto LedControl, che abbiamo chiamato LM:
1 2 3 4 5 6 7 8 9 10 11 |
//Led Display #include "LedControl.h" #define MAX_DEVICES 1 #define CLK_PIN 13 #define DATA_PIN 11 #define CS_PIN 12 LedControl LM = LedControl(DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES); |
Con l’ultima linea di codice abbiamo creato l’oggetto LM, che useremo per controllare la matrice di LED. Creiamo anche la variabile inString che memorizzerà i dati ricevuti via Bluetooth. Le funzioni readFromAndroid e writeData, memorizzano i dati in inString per “scegliere” come devono “comportarsi” gli occhi. Se vuoi sapere di più su queste funzioni visita questo sito web.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
String inString = ""; // Stringa letta da Protocoder void readFromAndroid() { char inChar; while (Serial.available() > 0) { inChar = (char) Serial.read(); Serial.flush(); if (inChar == '='){ inString = ""; LM.clearDisplay(0); }else if (inChar != '+'){ inString += inChar; } } } void writeData(){ if (inString == "up") { lookUp(); } if (inString == "down") { lookDown(); } if (inString == "center") { lookCenter(); } if (inString == "left") { lookLeft(); } if (inString == "right") { lookRight(); } } |
In questo caso, appena il messaggio è stato interamente letto (dopo la ricezione del carattere ‘=’), cancelliamo lo schermo usando il comando LM.clearDisplay(0); per essere sicuri che è posizionato correttamente. Nel “setup”, avviamo la porta seriale, attiviamo la matrice di LED (che per default è impostata in modalità risparmio-energia), imposta la luminosità al massimo, “azzera”, e finalmente programma gli occhi per sembrare al centro.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
void setup() { Serial.begin(19200); /* The MAX72XX is in power-saving mode on startup, we have to do a wakeup call // Function to go to power-save mode: LM.shutdown(0,true); */ LM.shutdown(0, false); /* Set the brightness to max values value={0,15} */ LM.setIntensity(0, 15); /* and clear the display */ LM.clearDisplay(0); lookCenter(); // test routines //testPos(250); //test(); } |
Due routine di verifica sono state incluse, testPos(), che cambia la posizione degli occhi e test(), che accende i LED uno alla volta. Per generare differenti modalità di lampeggio, possiamo usare una matrice da un file nella sezione di download, dove contrassegnamo quali LEDs vogliamo accendere e “tradurlo” in programma . Useremo questa funzione per accendere un LED:
1 |
LM.setLed(0,row, column, status) |
Lo stato può essere vero o falso, per commutare il LED acceso o spento. Qui c’è la matrice per la funzione lookCenter() che muove gli occhi rivolti in avanti: Qui c’è il codice per Arduino:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
void lookCenter() { LM.setLed(0, 3, 3, false); LM.setLed(0, 3, 4, false); LM.setLed(0, 4, 3, false); LM.setLed(0, 4, 4, false); LM.setLed(0, 1, 3, true); LM.setLed(0, 1, 4, true); LM.setLed(0, 2, 2, true); LM.setLed(0, 2, 3, true); LM.setLed(0, 2, 4, true); LM.setLed(0, 2, 5, true); LM.setLed(0, 3, 1, true); LM.setLed(0, 3, 2, true); LM.setLed(0, 3, 5, true); LM.setLed(0, 3, 6, true); LM.setLed(0, 4, 1, true); LM.setLed(0, 4, 2, true); LM.setLed(0, 4, 5, true); LM.setLed(0, 4, 6, true); LM.setLed(0, 5, 2, true); LM.setLed(0, 5, 3, true); LM.setLed(0, 5, 4, true); LM.setLed(0, 5, 5, true); LM.setLed(0, 6, 3, true); LM.setLed(0, 6, 4, true); } |
Questo ci permette di generare differenti motivi con i LEDs. Nel nostro caso, le funzioni sono state generate per emulare le posizioni della pupilla (lookLeft, lookRight, lookUp, lookDown e lookCenter), come anche funzioni che emulano l’ammiccare degli occhi (blinkClose, blinkOpen e fullBlink). La variabile delayBlink cambia la velocità con cui varia l’apertura e la chiusura dell’occhio. La useremo per velocizzare o ridurre la velocità di ammiccamento. La funzione fullblink utilizza, come parametro, il tempo che intercorre tra l’apertura e la chiusura dell’occhio. La utilizzeremo per velocizzare o ridurre la frequenza con cui si aprono gli occhi.
1 2 3 4 5 6 7 |
int delayBlink = 10; void fullBlink(int timeClosed){ blinkClose(); delay(timeClosed); blinkOpen(); } |
Le funzioni blinkOpen e blinkClose aprono e chiudono gli occhi, e usano la variabile delayBlink per variane la velocità di esecuzione. Per ultimo, la funzione loop controlla la routine di lampeggio e le funzioni che ricevono i dati via Bluetooth, readFromAndroid e writeData.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
void loop() { if (millis() > nextTimeDouble){ fullBlink(60); delay(100); fullBlink(60); nextTimeDouble = millis() + random(30000, 60000); writeData(); } else if (millis() > nextTime){ fullBlink(60); nextTime = millis() + random(1500, 4000); writeData(); } if (Serial.available() > 0) { readFromAndroid(); } writeData(); } |
Per controllare il lampeggio degli occhi, useremo la prima parte del codice:
1 2 3 4 5 6 7 8 9 10 11 12 |
if (millis() > nextTimeDouble){ fullBlink(60); delay(100); fullBlink(60); nextTimeDouble = millis() + random(30000, 60000); writeData(); } else if (millis() > nextTime){ fullBlink(60); nextTime = millis() + random(1500, 4000); writeData(); } |
Per variare la velocità di lampeggio ogni volta che viene eseguita la routine, ci sono due funzioni (singola e doppia), che sono simili. Per prima cosa dobbiamo controllare se il tempo trascorso è maggiore del tempo a cui deve variare lo stato degli occhi. Se la condizione è vera, verrà eseguita la routine di lampeggio e re-impostato il prossimo evento, aumentando di una quantità variabile, random, il numero che determina l’evento successivo. Per leggere il testo inviato da Protocoder via Bluetooth, viene usato il seguente codice:
1 2 3 4 |
if (Serial.available() > 0) { readFromAndroid(); } writeData(); |
Codice per Protocoder
Il programma che sviluppiamo con Protocoder invierà i dati alla scheda Arduino via Bluetooth. Per iniziare il programma utilizzeremo la intestazione già utilizzata in altri programmi, aggiungendo la gestione della connessione/disconnessione Bluetooth. Se vuoi approfondire il funzionamento di questa parte di codice guarda questa parte di corso.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
ui.toolbar.title("Robot-Eyes"); ui.toolbar.bgColor(55, 155, 155, 255); ui.toolbar.show(true); //ui.screenMode("fullscreen"); ui.screenOrientation("portrait"); ui.allowScroll(false); device.screenAlwaysOn(true); var margin = 10; var w = ui.screenWidth - 2*margin; var h = 150 + margin; var y = h + margin; var btStatus=0; // Variabile che memorizza lo stato (connesso/non connesso o 1/0) del Bluetooth var btClient; // "oggetto" per far funzionare il Bluetooth var btnConectar = ui.addButton("Connect Bluetooth", margin, margin,w,h).onClick(function() { // Pulsante Bluetooth btClient = network.bluetooth.connectSerial(function(status) { console.log("connected " + status); if (status){ ui.toast("Connected"); btStatus = status; console.log("btStatus = " +btStatus); } }); }); var btnDesconectar = ui.addButton("Disconnect", margin, y,w,h).onClick(function() { // Pulsante disconnetti btClient.disconnect(); // Disconnetti btStatus= false; // Cambia lo stato di connessione (0) }); |
Per inviare dati alla scheda ZUM, useremo la funzione send che prende le stringhe da inviare come parametro. Per esempio, per far girare l’occhio a destra, la funzione deve inviare la parola “right”, cioè send(“right”);. Il codice della funzione send è:
1 2 3 4 5 6 |
function send(str) { // Funzione che invia una stringa (una catena di caratteri) if (btStatus) { // Se il Bluetooth è connesso, esegui le righe di programma seguenti btClient.send("="+str+"+"); // Invia la stringa inclusa nei simboli = e + console.log("="+str+"+"); // Visualizza la stringa di caratteri sul video. } } |
Ora creaimo il pulsante che determina la posizione dell’occhio. Per associare ad ogni pulsante un immagine, carichiamo i file immagine che vogliamo utilizzare con Protocoder, che poi utilizzeremo nella creazione del pulsante come di seguito:
1 2 3 |
ui.addImageButton(positionX,positionY,width,height,"file name",false).onClick(function(){ }); |
Per aggiustare la grandezza e la posizione dei pulsanti, useremo due variabili:
- WidthImg, usata per assegnare altezza e larghezza dell’immagine.
- posX, usata per determinare la posizione X dei pulsanti.
Nel nostro caso, includiamo la funzione send(); all’interno della funzione attivata quando viene premuto il pulsante, inviando l’ordine corrispondente alla posizione:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
ui.addImageButton(posX,3*y,widthImg,widthImg,"up.png",false).onClick(function(){ send("up"); }); ui.addImageButton(10*margin,3*y + widthImg,widthImg,widthImg,"left.png",false).onClick(function(){ send("left"); }); ui.addImageButton(posX,3*y + widthImg,widthImg,widthImg,"center.png",false).onClick(function(){ send("center"); }); ui.addImageButton(posX + widthImg,3*y + widthImg,widthImg,widthImg,"right.png",false).onClick(function(){ send("right"); }); ui.addImageButton(posX,3*y + 2*widthImg,widthImg,widthImg,"down.png",false).onClick(function(){ send("down"); }); |
Disegno 3D: Comodoro
Per creare il disegno del Comodoro, abbiamo utilizzato il programma Blender. Per stampare il disegno, utilizzeremo Cura con le seguenti impostazioni per generare i supporti:
Questi supporti sono facilmente asportabili e danno stabilità all’oggetto da stampare. In Cura, la scala è stata modificata a 40.79 per adattare il disegno alle dimensioni della nostra matrice di LED, ma puoi variare la scala in base alla matrice che hai.
-
Material_grafico-Animaciones_led
Immagini della matrice di LED e dei pulsanti | CC-BY-SA
-
Programacion-Animaciones_led
Codice per Arduino, progetto per Protocoder e libreria LedControl | CC-BY-SA
-
Comodoro
File stl e disegno del Comodoro per Blender | CC-BY-SA
-
LedControl-1.0.6
Libreria utilizzata | Derechos de autor