In questa lezione, tratta dagli esempi del sito Arduino.cc verrà mostrato come effettuare i movimenti di base del robot impostando i movimenti mediante i pulsanti disposti sul robot, i pulsanti laterali saranno utilizzati per definire la direzione, mentre il pulsante centrale sarà utilizzato per memorizzare la sequenza impostata. Il numero massimo di istruzioni che possono essere impostate è 20. Per avviare il robot, una volta effettuato l’upload dello sketch, e posato per terra, accenderlo, i movimenti partiranno dopo un secondo.
Analizziamo il codice
/* Robot Logo traduzione e variazioni: 15 novembre 2013 Michele Maffucci tratto dallo sketch RO1_Logo: created 1 May 2013 by X. Yang modified 12 May 2013 by D. Cuartielles This example is in the public domain */ #include <ArduinoRobot.h> // inclusione della libreria robot int commands[20]; // array per la memorizzazione dei comandi void setup() { // inizializzazione del Robot, SD card e display Robot.begin(); Robot.beginTFT(); Robot.beginSD(); // la classe displayLogos() visualizza le immagini "lg0.bmp" e "lg1.bmp" sul display Robot.displayLogos(); } void loop() { Robot.drawBMP("intro.bmp", 0, 0); //visualizza l'immagine di sfondo iniCommands(); // funzione per rimuove i comandi dall'array addCommands(); // funzione per aggiungere i comandi nell'array delay(1000); // attende per un secondo executeCommands(); // esegue i comandi Robot.stroke(0,0,0); // imposta il colore del testo in nero Robot.text("Fatto!", 5, 103); // scrive il testo "Fatto!" sul display delay(1500); // attende per 1,5 secondi } // svuota l'array dei comandi // il ciclo for incrementa l'indice dell'array per valori che vanno da 0 a 19 // riempendo l'array di -1 void iniCommands() { for(int i=0; i<20; i++) commands[i]=-1; } // inserimento dei comandi nell'array void addCommands() { Robot.stroke(0,0,0); // imposta il colore del testo in nero // visualizza il testo sul display Robot.text("1. Premi i pulsanti per\n aggiungere comandi.\n\n 2. Centrale per finire.", 5, 5); // lettura dello stato dei pulsanti for(int i=0; i<20;) { // massimo 20 comandi int key = Robot.keyboardRead(); if(key == BUTTON_MIDDLE) { //fine input break; }else if(key == BUTTON_NONE) { //se nessun pulsante è premuto continue; } commands[i] = key; // salva il comando nell'array PrintCommandI(i, 46); // stampa il comando sul display delay(100); i++; } } // esegue i comandi memorizzati nell'array permettendo il movimento del robot void executeCommands() { // scrive su display lo stato in cui si trova il robot Robot.text("Esecuzione...",5,70); // esegue le istruzioni memorizzate nell'array for(int i=0; i<20; i++) { switch(commands[i]) { case BUTTON_LEFT: Robot.turn(-90); break; case BUTTON_RIGHT: Robot.turn(90); break; case BUTTON_UP: Robot.motorsWrite(255, 255); break; case BUTTON_DOWN: Robot.motorsWrite(-255, -255); break; case BUTTON_NONE: return; } // visualizza il comando che si sta eseguendo sul display con colore del carattere rosso Robot.stroke(255,0,0); PrintCommandI(i, 86); delay(1000); // interrompe il movimento per 1 secondo Robot.motorsStop(); delay(1000); } } // conversione della pressione del pulsante in un carattere char keyToChar(int key) { switch(key) { case BUTTON_LEFT: return '<'; case BUTTON_RIGHT: return '>'; case BUTTON_UP: return '^'; case BUTTON_DOWN: return 'v'; } } // visualizza il comando sul display void PrintCommandI(int i, int originY) { Robot.text(keyToChar(commands[i]), i%14*8+5, i/14*10+originY); }
Analizziamo la sezione di inserimento comandi:
// inserimento dei comandi nell'array void addCommands() { Robot.stroke(0,0,0); // imposta il colore del testo in nero // visualizza il testo sul display Robot.text("1. Premi i pulsanti per\n aggiungere comandi.\n\n 2. Centrale per finire.", 5, 5); // lettura dello stato dei pulsanti for(int i=0; i<20;) { // massimo 20 comandi int key = Robot.keyboardRead(); if(key == BUTTON_MIDDLE) { //fine input break; }else if(key == BUTTON_NONE) { //se nessun pulsante è premuto continue; } commands[i] = key; // salva il comando nell'array PrintCommandI(i, 46); // stampa il comando sul display delay(100); i++; } }
analizziamo riga per riga:
Robot.stroke(0,0,0); // imposta il colore del testo in nero
La funzione stroke imposta il colore delle line e dei bordi intorno alle figure sul display.
La sintassi è: screen.stroke(red, green, blue);
dove i parametri possono assumere i seguenti valori:
- red: intero da 0-255
- green: intero da 0-255
- blue: intero da 0-255
non restituisce nessun valore.
Passimo all’istruzione:
Robot.text("1. Premi il pulsante per\n aggiungere comandi.\n\n 2. Pulsante centrale per finire.", 5, 5);
La funzione text stampa un testo sul display alle coordinate indicate.
La sintassi è: screen.text(text, xPos, yPos);
Dove i parametri sono:
- text: array di caratteri, il testo che volete stampare sul display
- xPos: valore intero della coordinata x da cui far partire la scrittura del testo (valore 0,0 è in alto a sinistra).
- yPos: valore intero della coordinata y da cui far partire la scrittura del testo.
Tornano al nostro codice verrà stampato alle coordinate 5, 5 il testo:
- Premi il pulsante per aggiungere comandi.
- Pulsante centrale per finire.
Ricordo che il carattere escape “\n” indica il newline (a capo con aggiunta di una nuova linea).
Continuiamo con l’analisi:
// lettura dello stato dei pulsanti for(int i=0; i<20;) { // massimo 20 comandi int key = Robot.keyboardRead(); if(key == BUTTON_MIDDLE) { //fine input break; }else if(key == BUTTON_NONE) { //se nessun pulsante è premuto continue; } commands[i] = key; // salva il comando nell'array PrintCommandI(i, 46); // stampa il comando sul display delay(100); i++; }
All’interno del ciclo for è incluso tutto il codice che permette di:
- controllare se abbiamo raggiunto il numero massimo di comandi che possono essere inseriti (in questo caso 20);
- se abbiamo premuto il pulsante centrale per verificare se abbiamo concluso l’inserimento dei comandi;
- se non abbiamo premuto nessun pulsante.
Nel caso ci si trovi nella condizione 3 si riempie l’array commands[i] con il valore -1.
La funzione keyboardRead() rileva se uno dei 5 pulsanti viene premuto nel caso del nostro codice:
int key = Robot.keyboardRead();
rileva quale posante viene premuto e questo viene memorizzato nella variabile intera “key”
La sintassi è: Robot.keyboardRead()
Non ha nessun parametro
Valori che può restituire:
- BUTTON_NONE se non è premuto nessun pulsante
- BUTTON_LEFT se il pulsante sinistro è premuto
- BUTTON_DOWN se il pulsante giù è premuto
- BUTTON_UP se il pulsante su è premuto
- BUTTON_RIGHT se il pulsante destro è premuto
- BUTTON_MIDDLE se il pulsante centrale è premuto
A titolo esemplificativo provate ad eseguire il seguente sketch che visualizza sulla serial monitor il pulsante premuto:
#include void setup(){ Robot.begin(); Serial.begin(9600); } void loop(){ Serial.println(Robot.keyboardRead()); delay(100); }
Torniamo allo sketch logo
// lettura dello stato dei pulsanti for(int i=0; i<20;) { // massimo 20 comandi int key = Robot.keyboardRead(); if(key == BUTTON_MIDDLE) { //fine input break; }else if(key == BUTTON_NONE) { //se nessun pulsante è premuto continue; } commands[i] = key; // salva il comando nell'array PrintCommandI(i, 46); // stampa il comando sul display delay(100); i++; }
se il pulsante premuto è il centrale, allora la condizione dell’if:
key == BUTTON_MIDDLE
risulta vera, quindi viene eseguito il break, si esce dalla funzione addCommands() ed il controllo torna al loop dove troviamo:
delay(1000); // attende per un secondo
attesa che ci consente di distaccare o poggiare a terra il robot in quanto il passo successivo sarà quello dell’esecuzione dei comandi inseriti, ovvero il movimento del robot.
L’esecuzione dei comandi viene eseguita dalla funzione:
executeCommands(); // esegue i comandi
la funzione è così fatta:
// esegue i comandi memorizzati nell'array permettendo il movimento del robot void executeCommands() { // scrive su display lo stato in cui si trova il robot Robot.text("Esecuzione...",5,70); // esegue le istruzioni memorizzate nell'array for(int i=0; i<20; i++) { switch(commands[i]) { case BUTTON_LEFT: Robot.turn(-90); break; case BUTTON_RIGHT: Robot.turn(90); break; case BUTTON_UP: Robot.motorsWrite(255, 255); break; case BUTTON_DOWN: Robot.motorsWrite(-255, -255); break; case BUTTON_NONE: return; } // visualizza il comando che si sta eseguendo sul display Robot.stroke(255,0,0); PrintCommandI(i, 86); delay(1000); // interrompe il movimento per 1 secondo Robot.motorsStop(); delay(1000); } }
Per prima cosa viene eseguita è l’istruzione:
// scrive su display lo stato in cui si trova il robot Robot.text("Esecuzione...",5,70);
che scrive su display alle coordinate (5,70) il testo: Esecuzione…
Successivamente il controllo passa alle istruzioni incluse nel ciclo for, ciclo che itererà 20 volte, dimensione massima dell’array.
All’interno del for troviamo un case che effettuerà un controllo su ogni singola istruzione verificando quale istruzione è stata inserita.
nei primi due case:
case BUTTON_LEFT: Robot.turn(-90); break; case BUTTON_RIGHT: Robot.turn(90); break;
viene verificato se è stato premuto il pulsante sinistro o quello destro, se una delle due condizioni risulta vera, viene invocata la funzione turn(), funzione che permette di far girare di una certa quantità di gradi (nel nostro caso -90 oppure 90), rispetto alla posizione attuale. La posizione corrente è derivata dalla bussola di bordo.
La sintassi è: Robot.turn(degrees)
dove degrees può assumere qualsiasi valore nell’intervallo -180, 180.
Numeri positivi indicano una rotazione verso destra, numeri negativi indicano una rotazione verso sinistra. Poiché la posizione corrente del robot è derivata dalla bussola del robot è bene mantenere magneti (come ad esempio quelli degli altoparlanti) lontano dal robot.
Ricordo inoltre che una volta eseguite le istruzioni presenti nel relativo “case” il tutto si conclude con l’istruzione “break”, cioè termina l’esecuzione dello switch e l’esecuzione del programma prosegue dopo la graffa di chiusura dello switch.
Per approfondimenti sull’istruzione switch si segua il link.
Le successive due istruzioni:
case BUTTON_UP: Robot.motorsWrite(255, 255); break; case BUTTON_DOWN: Robot.motorsWrite(-255, -255); break;
Verificano se sono state premuti i pulsanti su o giù.
La funzione motorsWrite controlla la velocità e la direzione dei due motori collegati alle ruote del robot ed accetta due parametri numerici compresi tra -255 e 255.
Se il valore è maggiore di 0, il motore gira in avanti, se meno di 0, il motore ruota all’indietro.
La sintassi è:
Robot.motorsWrite(speedLeft, speedRight)
Parametri:
- speedLeft: velocità della ruota di sinistra
- speedRight: velocità della ruota di destra
non restituisce nessun parametro.
La successiva istruzione è:
case BUTTON_NONE: return;
nel caso non vi siano comandi viene restituito un valore nullo alla funzione ed il controllo passa dopo la parentesi graffa di chiusura dello switch, a questo punto vengono eseguite le seguenti istruzioni:
// visualizza il comando che si sta eseguendo sul display Robot.stroke(255,0,0); PrintCommandI(i, 86); delay(1000);
con l’istruzione:
Robot.stroke(255,0,0);
viene disegnata una cornice rossa sul display, successivamente viene invocata la funzione:
PrintCommandI(i, 86);
che permette di visualizzare, partendo dall’ascissa di valore 86, il comando in esecuzione. La PrintCommandI(i, 86) ha due parametri interi, il valore i-esimo dell’indice dell’array e l’ascissa di partenza da cui incominciare a scrivere i comandi in esecuzione.
All’interno della PrintCommandI troviamo la chiamata alla funzione keyToChar che permette la conversione del comando inserito nella posizione i-esima dell’array commands in un dei quattro caratteri: < > ^ v l’operazione viene eseguita con uno switch a cui è affidata la conversione.
Tornando alla PrintCommandI
// visualizza il comando sul display void PrintCommandI(int i, int originY) { Robot.text(keyToChar(commands[i]), i%14*8+5, i/14*10+originY); }
notiamo che gli incrementi di coordinate sono fatte utilizzando alcune operazioni che restituiscono le coordinate per la stampa del carattere.
Tonando alla executeCommands() l’istruzione successiva è un delay(1000) cioè pausa di 1 secondo e il successivo comando è Robot.motorsStop().
La funzione motorsStop() ferma entrambi i motori del robot e l’interruzione dura 1 secondo in quanto l’istruzione che segue è delay(1000).
Supponendo che l’iterazione del ciclo for sia conclusa, si esce dalla funzione executeCommands ed il controllo torna al loop alle istruzioni:
Robot.stroke(0,0,0); // imposta il colore del testo in nero Robot.text("Fatto!", 5, 103); // scrive il testo "Fatto!" sul display delay(1500); // attende per 1,5 secondi
- cioè stampa su display di una cornice nera
- stampa su display il testo “Fatto!” alle coordinate (5,103)
- persistenza di questo stato per 1,5 secondi
concluso questo tempo il robot si pone nuovamente in condizione di apprendimento, pronto per ricevere nuovi comandi.
molto interessante grazie
Grazie.
Spero di far di più con altre lezioni.
Saluti.
Sto cercando di utilizzare un encoders incrementale per controllare la posizione assunta da un nastro e chidevo se per cortesia potevi indicarmi dove trovare informarioni in merito
p.s. data la notevole velocita della rotella del encoder intendevo usare il contatore ardwer interno ad arduino mega pin 47 ,accumulare i valori interi del contatore in una altra variabile “tipo long”in modo di controllare un valore piu grande metricamente.
purtroppo non so come controllare i fleg del contatore e non sono molto sicuro di sperli gestire, PUOI AIUTARMI O INDICARMI DOVE CERCARE INFO GRAZIE