Se Arduino non è un nome che vi suona del tutto nuovo e avete già mosso i primi passi con lo studio di questa scheda, potreste cominciare a cimentarvi in progetti un po’ più complessi del semplice controllo dei LED a scopo didattico. Dopotutto, la scheda di prototipazione permette di mettere in pratica con un numero ragionevole di righe di codice, anche idee apparentemente complicate. Ad esempio, è possibile far accendere una lampada con il semplice battito delle mani o realizzare dei robot che si muovono autonomamente. In questo articolo, dunque, scopriremo come realizzare questi due progetti, senza tralasciare delle nozioni teoriche fondamentali: spiegheremo ad esempio come controllare un relay grazie al quale usare Arduino per accendere e spegnere elettrodomestici di vario tipo, come lampade o stufe elettriche che normalmente lavorano a 220 Volt.
Per comodità abbiamo caricato il codice completo di entrambi i progetti su Google Drive, scaricabile da qui .
Da sapere prima di iniziare
Quando lavoriamo con Arduino, lavoriamo con l’elettronica e dunque con della corrente. Ma si tratta di corrente continua a basso voltaggio. Quando aggiungiamo un relay le cose cambiano, perché stiamo andando a utilizzare anche la normale corrente alternata a 220V delle prese di casa. Ed è molto pericoloso. Le saldature devono essere ben fatte per evitare possibili cortocircuiti e non si devono mai toccare contatti scoperti finché la corrente è in circolo. Non dovrebbe mai essere permesso a minorenni di toccare cavi preposti alla conduzione della corrente ad alto voltaggio, anche quando tali cavi siano scollegati dalla presa a muro. Comunque, è bene assicurarsi di lavorare dietro un salvavita, così un’eventuale scarica di corrente verrebbe interrotta prima di fulminare il malcapitato.
Il primo esempio che vediamo è molto semplice, ma anche elegante e utile. Proveremo infatti ad accendere un relay: i relay (o relé) sono dei semplici interruttori che possono accendere (o spegnere) dispositivi alimentati con un alto voltaggio, come la normale 220V delle prese elettriche di casa controllabili con Arduino.
Tipico collegamento di microfono e relay ad Arduino
Ogni relay ha due pin da collegare ad Arduino (uno al GND e l’altro a un pin digitale, come un LED, ed eventualmente un ulteriore pin ai 5V di Arduino), e due pin cui collegare il cavo della corrente (per esempio quello di una lampada, al posto di un normale interruttore). Con Arduino possiamo accendere il relay come se fosse un normale LED, impostando semplicemente il valore HIGH sul suo pin digitale. Quindi possiamo decidere di accendere il relay in qualsiasi momento: possiamo ad esempio collegare un microfono ad Arduino (noi ci siamo basati sul semplice ed economico KY-038 ) e accendere o spegnere il relay quando viene misurato un valore abbastanza forte (come quando qualcuno batte le mani vicino al microfono).
Il microfono KY-038 per Arduino
Il codice del programma da scrivere nell’ Arduino IDE e caricare sulla scheda comincia con la classica dichiarazione delle variabili:
int sensorPin = A0;
int relay = 13;
Prima di tutto indichiamo i pin che stiamo utilizzando: la variabile relay conterrà il numero del pin digitale cui abbiamo collegato il relay, mentre sensorPin contiene il numero del pin analogico cui abbiamo collegato il segnale del microfono. I microfoni, infatti, sono sensori analogici.
int sensorValue = 0;
Nella variabile sensorValue inseriremo il valore letto dal sensore, che quindi sarà rappresentato da un numero compreso tra 0 e 1023 perché questo è l’intervallo dei sensori analogici.
bool on = false;
Definiamo poi una variabile di tipo bool , ovvero booleano. Una variabile booleana può avere due soli valori: vero o falso ( true o false in inglese. La utilizzeremo per memorizzare l’attuale stato del relay e infatti la chiamiamo on . Se la variabile on è true vuol dire che il relay è acceso, altrimenti è spento.
void setup ()
{
pinMode (relay, OUTPUT);
digitalWrite (relay, LOW);
Serial.begin (9600);
}
La funzione setup , eseguita una sola volta all’avvio di Arduino, predispone il pin digitale cui è collegato il relay in modalità di OUPUT .
Sfruttando la funzione digitalWrite si può quindi scrivere il valore iniziale del relay, che sarà LOW , ovvero spento. Poi abilitiamo anche la porta seriale, così sarà possibile scrivere un messaggio al computer eventualmente collegato ad Arduino per fargli sapere cosa stiamo misurando con il microfono.
void loop ()
{
sensorValue = analogRead (sensorPin);
La funzione loop viene ripetuta all’infinito finché Arduino è acceso, quindi è quella che utilizziamo per realizzare le attività del nostro progetto. Per cominciare, ad ogni ciclo provvediamo a leggere l’attuale valore del microfono, che ovviamente è un numero compreso tra 0 e 1023 a seconda del volume percepito dal microfono, memorizzandolo nella variabile sensorValue .
Serial.println (sensorValue);
Ora possiamo scrivere il numero ottenuto sulla porta seriale, così possiamo controllarlo con un PC. Leggere il valore può essere utile per capire se il microfono debba essere regolato (di solito c’è un’apposita rotella) in modo da non ottenere numeri troppi alti o troppo bassi.
Distinguere un suono dal rumore di fondo
Ora dobbiamo decidere la soglia oltre la quale consideriamo il suono registrato dal microfono adeguato a causare l’accensione o lo spegnimento del relay.
if (sensorValue > 500) {
on = !on;
}
Un semplice if ci permette di risolvere il problema e possiamo scegliere qualsiasi valore come soglia: noi abbiamo scelto 500 , ma potete alzarlo o abbassarlo per vedere cosa funziona meglio con il vostro microfono. Se la soglia è stata superata, dobbiamo agire sul relay. Ma come? Semplice: se il relay è acceso lo vogliamo spegnere, se invece è spento lo vogliamo accendere. In poche parole, dobbiamo invertire il valore della variabile on , che indica l’attuale stato di accensione del relay e che dovrà passare da false a true e viceversa. Possiamo farlo con l’operatore logico NOT , ovvero il punto esclamativo. In poche parole, se on è false , !on sarà true e viceversa. Quindi scrivendo on = !on abbiamo semplicemente detto ad Arduino di invertire il valore della attuale variabile on, a prescindere da quale esso sia.
digitalWrite (relay, on);
Ora possiamo scrivere l’attuale valore della variabile on , sia esso true o false , sul pin digitale del relay. Infatti, in Arduino il valore true corrisponde al valore HIGH , mentre il valore false corrisponde al valore LOW . Quindi, avendo una variabile di tipo bool , possiamo assegnare direttamente il suo valore ad un pin digitale.
delay(100);
}
Prima di concludere la funzione loop , e il programma, aggiungiamo la funzione delay, che si occupa solo di attendere un certo numero di millisecondi prima che la funzione loop possa essere ripetuta.
Abbiamo scelto di attendere 100 millisecondi, vale a dire 0,1 secondi, perché è il tempo minimo per assicurarsi che un rumore rapido come il battito di due mani sia effettivamente terminato e non venga contato erroneamente due volte. Per essere più sicuri di non commettere errori, possiamo aumentare questo tempo fino a 1000 millisecondi. Il programma è ora completo.
Per spiegare in modo più preciso il funzionamento dell’operatore logico ! , chiariamo che la riga di codice on = !on equivale al seguente ciclo if-else:
if (on == true) {
on = false;
} else {
on = true;
}
La singola riga di codice che abbiamo utilizzato rende il programma più semplice ed elegante, ma ha esattamente lo stesso significato e lo stesso risultato.
Costruire un robot è meno complicato di quanto si possa immaginare. Se non si hanno particolari pretese, lo si può costruire come un triciclo o come un’automobile utilizzando due servomotori a rotazione continua ( continuous rotation servo ) per le ruote motrici, e poi aggiungendo una o due ruote “libere” soltanto per tenere in equilibrio il robot. Naturalmente, si deve poi sviluppare un sistema per controllare lo spostamento del robot: il line following basato sugli infrarossi è uno dei metodi più semplici, ma volendo si può anche realizzare un robot simile a quelli che oggi si trovano in commercio come aspirapolveri (i Roomba): basta montare un sensore di distanza a ultrasuoni come (l’ HC-SR04 ) per far muovere avanti il robot finché non trova un ostacolo e, a quel punto, farlo ruotare fino a trovare una via libera.
Collegare due servomotori ad Arduino è molto semplice
Più complicato può essere inventarsi un sistema per controllare lo spostamento del robot, ma esiste sempre il meccanismo del line following , ovvero del seguire le linee. L’idea è semplice: basta disegnare sul pavimento una linea con un buon contrasto (per esempio con un pennarello nero su un foglio di carta bianco). Esiste un metodo piuttosto semplice per consentire ad una scheda Arduino di riconoscere la linea nera: dei sensori infrarossi. Banalmente, basta accoppiare un LED a infrarossi con una fotoresistenza a infrarossi, montandoli sotto al robot. In questo modo, quando la luce infrarossa del LED colpisce una zona bianca, la fotoresistenza riceverà un riflesso molto luminoso, mentre quando la luce del led colpisce un punto nero la fotoresistenza riceverà un riflesso molto debole o addirittura nullo (perché il bianco riflette la luce e il nero la assorbe). Naturalmente il meccanismo funzionerebbe anche con dei normali LED colorati. Però, per non avere interferenze dovremmo far correre il robot in una stanza buia, perché la luce del sole o quella di una lampada si sovrapporrebbe a quella del LED. Utilizzando dei LED a infrarossi il problema è risolto. Esistono addirittura dei set già pronti con LED infrarossi e fotoresistenze, come il QTR-8A , che abbiamo preso come base per il nostro esempio.
Un semplice robot realizzato con Arduino, due servomotori, ed il set di sensori QTR-8A
Il QTR-8A è molto semplice da utilizzare: basta fissarlo sotto al robot, in mezzo ai due servomotori. Il pin Vcc va collegato al pin 5V di Arduino, mentre il pin GND va connesso al GND di Arduino. Poi, sono disponibili ben otto pin di segnale: infatti il QTR-8A dispone di 8 coppie di LED e fotoresistenze infrarosse, e ogni fotoresistenza ha un pin che offre ad Arduino il proprio segnale analogico, cioè la lettura della luminosità del pavimento. Però, Arduino Uno ha soltanto 6 pin analogici: poco male, collegheremo ad Arduino soltanto 6 dei pin del QTR-8A.
I contatti della scheda QTR-8A prevedono il Vcc (5V), il GND, e gli input analogici di Arduino
Per non fare confusione li collegheremo in ordine: il pin 1 del QTR-8A andrà connesso al pin analogico 0 di Arduino, il pin 2 del QTR-8A andrà collegato al pin analogico 1 di Arduino, e così via. Se abbiamo una scheda più grande, come Arduino Mega, possiamo utilizzare tutti i pin del QTR-8A, perché un Arduino Mega ha ben 16 pin analogici. In realtà, però, per la maggioranza delle applicazioni 6 coppie di LED e fotoresistenze infrarosse sono più che sufficienti. L’idea di base è molto semplice: abbiamo una fila di 6 sensori sotto al robot, e in teoria vorremmo che la riga nera si trovasse sempre in corrispondenza della metà dei sensori (ovvero tra il terzo e il quarto). Ciò significa che se facciamo una media della luminosità rilevata dai primi tre sensori e una media di quella rilevata dagli ultimi tre sensori, è ovvio che dovrebbero essere più o meno uguali.
Se la media dei primi tre sensori (che possono essere quelli di sinistra) è più alta significa che la linea nera si trova dalla loro parte, e quindi dovremo far ruotare il robot nella direzione opposta (per esempio destra) per far tornare la linea nera al centro. Infatti, i sensori del QTR-8A funzionano al contrario rispetto a una normale fotoresistenza: offrono il valore massimo quando la luminosità ricevuta è bassa, e viceversa. Naturalmente, destra e sinistra dipendono dal verso in cui si monta il QTR-8A sul robot: se il robot non si comporta come dovrebbe, basta ruotare di 180 gradi la scheda con tutti i sensori infrarossi.
Il robot posizionato sopra la linea nera disegnata su un pavimento bianco
Il codice per il line following
Il codice che fa funzionare questo programma può sembrare abbastanza complesso, ma in realtà è semplice da capire:
#include
Prima di tutto si include nel programma la libreria necessaria per il funzionamento dei servomotori.
Servo left;
Servo right;
Dichiariamo poi due variabili speciali, degli oggetti, che rappresenteranno i due servomotori. Il nostro robot ha un totale di due ruote motrici, ciascuna mossa da un servomotore: una a destra ( right ) e l’altra a sinistra ( left ). Possiamo poi aggiungere una terza ruota centrale solo per tenere il robot in equilibrio (una sorta di triciclo al contrario, visto che le ruote motrici sono due e non una).
int mid = 0;
int mn = 1023;
int mx = 0;
Per poter eseguire i nostri calcoli, dovremo tarare i sensori: dovremo capire quale sia il valore massimo ( mx ), minimo ( mn ), e medio ( mid ) di luminosità rilevabile dai vari sensori. Quindi dichiariamo delle variabili che ci serviranno per memorizzare questi valori.
void setup()
{
Cominciamo ora a scrivere la funzione setup , che viene eseguita all’avvio di Arduino.
left.attach(9, 800, 2200);
right.attach(10, 800, 2200);
Dobbiamo assegnare i due oggetti di tipo servo, left e right , ai pin digitali di Arduino: abbiamo deciso di collegare il pin 9 al servomotore sinistro e il pin 10 al servomotore destro. Indichiamo anche i due valori di minimo e massimo per gli impulsi che faranno muovere il servomotore: di solito non è necessario specificarli (lo fa automaticamente Arduino), ma può essere utile per evitare problemi quando si usano servomotori a rotazione continua.
Serial.begin(9600);
Attiviamo anche la comunicazione sulla porta seriale, così da poter inviare messaggi ad un computer per capire se c’è qualcosa nel robot non stia funzionando a dovere.
digitalWrite(13, LOW);
right.write(90);
left.write(90);
Visto che siamo all’inizio, spegniamo il LED collegato al pin digitale numero 13 di Arduino (è un LED di segnalazione saldato sulla scheda Arduino). Spegniamo anche i due servomotori, così il robot resterà fermo. Con dei servomotori a rotazione continua, lo spegnimento si esegue dando il valore 90 alla funzione write di ciascun oggetto left e right .
Calibrare i sensori infrarossi
Attenderemo 5 secondi (ovvero 5000 millisecondi) eseguendo una misura della luminosità di ogni sensore ogni millisecondo, grazie a un semplice ciclo for che viene ripetuto per 5000 volte.
for(int i=0; i<5000; i++)
{
digitalWrite(13, HIGH);
Accendiamo il LED connesso al pin 13, quello saldato su Arduino, per avvisare che la calibrazione dei sensori è in atto.
for(int j=0; j<=5; j++)
{
int val = analogRead(j);
if(val >= mx) mx = val;
if(val <= mn) mn = val;
}
Dobbiamo ora capire quali siano i valori massimo e minimo che si possano misurare con i nostri sensori: per farlo scorriamo tutti i sensori, dal pin analogico 0 al pin analogico 5 di Arduino, leggendo con analogRead il valore misurato al momento (si suppone che il robot si trovi già sopra alla linea e che almeno alcuni dei sensori siano proprio sopra alla linea nera mentre altri siano sopra al pavimento bianco).
Il valore di ogni sensore viene inserito nella variabile val . Se tale variabile è maggiore dell’attuale valore massimo ( mx ), allora essa viene considerata il nuovo massimo, assegnando il suo valore ad mx . Allo stesso modo, se val è minore del valore più basso finora registrato mn , alla variabile mn viene assegnato al valore della variabile val .
delay(1);
}
Prima di terminare il ciclo for da 0 a 5000, inseriamo la funzione delay chiedendole di attendere 1 millisecondo. Così siamo sicuri che il ciclo durerà in totale 5000 millisecondi, ovvero 5 secondi.
mid = ((mx + mn)/2);
Ottenuti i valori massimi e minimi che i sensori possono fornire, possiamo calcolare il valore medio, da memorizzare nella variabile mid .
digitalWrite(13, LOW);
}
Per segnalare che la calibrazione dei sensori è terminata, spegniamo il LED connesso al pin digitale 13 di Arduino.
void loop()
{
Finora abbiamo solo calibrato i sensori, ma il robot è ancora fermo. Cominciamo ora la funzione di loop, quella che farà muovere il nostro robot.
int s0 = analogRead(0);
int s1 = analogRead(1);
int s2 = analogRead(2);
int s3 = analogRead(3);
int s4 = analogRead(4);
int s5 = analogRead(5);
Per cominciare leggiamo i valori di tutti i sensori, inserendoli in apposite variabili chiamate s0, s1, s2, ecc.
right.write(180);
left.write(0);
Iniziamo a muovere il robot: per far girare un servomotore a rotazione continua si può indicare un numero da 0 a 90 oppure da 90 a 180.
Il valore 180 rappresenta la massima velocità in una direzione, 0 rappresenta la massima velocità nell’altra direzione, e 90 rappresenta la posizione di stallo (quindi il servomotore è fermo). Poiché i nostri servomotori sono montati in modo da essere uno speculare all’altro, è ovvio che per far andare il robot avanti uno dei servomotori girerà in una direzione e l’altro nell’altra, così alla fine le due ruote gireranno all’unisono.
Delay(1);
Attendiamo un millisecondo, soltanto per essere sicuri che il comando di movimento dei servomotori sia stato applicato.
int averageLeft = (s0+s1+s2)/3;
int averageRight = (s3+s4+s5)/3;
Abbiamo memorizzato nella variabili s0 , s1 , ecc. i valori dei vari sensori. Però, come abbiamo detto prima di cominciare a scrivere il programma, noi vogliamo semplicemente comparare la media dei sensori di sinistra con quella dei sensori di destra. Abbiamo deciso che i sensori di sinistra siano quelli collegati ai pin analogici 0 , 1 e 2 di Arduino, mentre quelli di destra siano i sensori connessi ai pin analogici 3 , 4 , e 5 (ovviamente dipende da come montiamo il QTR-8A sotto al robot). Le due medie si calcolano banalmente con al classica formula matematica: si fa la somma e si divide per 3. Ovviamente, la media potrebbe essere un numero con decimali (con la virgola), ma a noi basta un numero intero: siccome abbiamo definito le due variabili come tipo int , Arduino arrotonderà automaticamente i decimali al numero intero più vicino.
La linea è a destra o a sinistra?
Normalmente, il robot continua a muoversi in avanti. Però, se la media dei sensori di sinistra è maggiore di quelli dei sensori di destra significa che la linea nera sul pavimento si trova dalla parte sinistra del robot.
if(((averageLeft)>((averageRight)+240)))
{
Abbiamo indicato anche un fattore correttivo, pari a 240 , per avere un certo lasco: se avessimo scritto soltanto averageLeft>averageRight il ciclo if verrebbe attivato anche per variazioni minime dei sensori infrarossi tra la parte destra e quella sinistra del robot. Ma vi sarà sempre qualche piccola variazione, anche solo per minime interferenze o oscillazioni nella corrente. Inserendo un fattore correttivo ci assicuriamo che il ciclo if venga attivato soltanto se la differenza tra la parte destra e sinistra del robot è notevole.
right.write(130);
left.write(90);
Ovviamente, se la linea nera è alla sinistra del robot, dovremo ruotare il robot verso sinistra in modo da riportarlo in una posizione in cui la linea nera sia esattamente al centro del robot stesso. E per far ruotare il robot verso sinistra dobbiamo tenere fermo il servomotore di sinistra, dandogli il valore 90 , in modo che faccia da perno, e muovere il servomotore di destra con un valore vicino a 180.
Scegliamo un valore inferiore a 180 perché vogliamo che il servomotore di destra si muova, ma non alla sua massima velocità, così il movimento è più lento e più facile da controllare (se si esagera si rischia di finire fuori dalla linea nera).
delay(abs(((averageLeft)-(averageRight))/2));
}
Prima di concludere il ciclo if attendiamo una certa quantità di millisecondi, ottenuta come la metà della differenza delle due medie. L’idea è che maggiore è la differenza tra i sensori di destra e quelli di sinistra, maggiore è la dimensione della linea nera, e quindi maggiore è il tempo necessario durante lo spostamento del robot per riuscire ad avere la linea nera al centro del robot stesso.
Siccome la differenza dei due valori potrebbe risultare un numero negativo, utilizziamo la funzione abs per ottenere il valore assoluto, cioè ottenere la differenza senza segno negativo (in pratica, un eventuale valore -500 diventerebbe semplicemente 500).
if(((averageLeft)<((averageRight)-240)))
{
right.write(90);
left.write(40);
delay(abs(((averageLeft)-(averageRight))/2));
}
Se la media dei sensori di sinistra è inferiore a quella dei sensori di destra (tenuto sempre conto del solito fattore correttivo), allora significa che la linea nera è posizionata dalla parte destra del robot. Quindi faremo esattamente l’opposto del precedente ciclo if : terremo fermo il servomotore destro e muoveremo quello sinistro (anche in questo caso non imposteremo la sua velocità al valore massimo, cioè 0, ma un po’ meno, ossia 40). Prima di concludere questo ciclo if , attenderemo di nuovo una manciata di millisecondi con lo stesso calcolo precedente.
I percorsi disegnati con nastro adesivo nero su un pavimento chiaro possono anche essere molto lunghi ed elaborati
La fine del percorso
Abbiamo detto al robot cosa fare se la linea nera si trova a destra o a sinistra del robot stesso. E se invece la linea nera coprisse tutto il pavimento? Significherebbe che il percorso è terminato. Infatti, quando disegniamo il percorso sul pavimento, a meno che non sia un circuito chiuso come quelli automobilistici, possiamo indicarne la fine dipingendo di colore nero un bel rettangolo perpendicolare all’ultima parte della linea.
if((s0 > mid)&&(s5 > mid))
{
In questo modo quando il robot ci arriva sopra si accorgerà che tutti i suoi sensori, in particolare il sensore s0 e il sensore s5 che sono il primo e l’ultimo, hanno un valore che è superiore alla media. Ciò significa che tutti i sensori sono contemporaneamente sopra alla linea nera e quindi si è raggiunto il termine del percorso.
right.write(90);
left.write(90);
In questo caso dobbiamo fermare il robot dando il valore 90 ad entrambe i servomotori (questo valore provoca l’arresto immediato dei servomotori a rotazione continua).
for(int k=0; k<50; k++)
{
digitalWrite(13, HIGH);
delay(100);
digitalWrite(13, LOW);
delay(100);
}
Per segnalare di avere raggiunto quello che riteniamo essere il termine del percorso (e che quindi il robot non si è fermato per un errore), facciamo lampeggiare rapidamente il LED collegato al pin digitale 13 di Arduino, quello saldato sulla scheda.
Per farlo lampeggiare 50 volte basta un ciclo for che si ripete per l’appunto 50 volte, e ad ogni iterazione non fa altro che accendere il LED portando il suo pin al valore HIGH , attendere 100 millesimi di secondo, spegnere il LED scrivendo il valore LOW sul suo pin, e poi attendere altri 100 millisecondi prima di passare all’iterazione successiva.
delay(5000);
}
Ora attendiamo 5 secondi per essere sicuri che tutte le operazioni necessarie allo spegnimento dei servomotori siano state portate a termine. Durante questi 5 secondi si può tranquillamente spostare il robot, magari posizionandolo di nuovo all’inizio del percorso per farlo ripartire.
}
La funzione loop si conclude qui, e con essa il programma. Naturalmente, ricordiamo che la funzione loop viene ripetuta di continuo, quindi il robot continuerà a muoversi lungo la linea nera tracciata sul pavimento bianco finché non lo fermiamo facendolo passare sopra a un rettangolo nero largo tanto quanto l’intera scheda QTR-8A, in modo che tutti i sensori infrarossi si trovino contemporaneamente sopra al colore nero.
Utilizzare un radiocomando
Un altro metodo per controllare un robot realizzato con Arduino è utilizzare un radiocomando: i radiocomandi non sono altro che un insieme di potenziometri (le varie leve presenti sul radiocomando sono potenziometri) i cui valori vengono trasmessi a distanza tramite onde radio. Quindi basta collegare il ricevitore del radiocomando ad Arduino, alimentandolo con i pin 5V e GND, e connettendo tutti i vari pin di segnale (ce n’è uno per ciascuna leva del radiocomando) ai pin analogici di Arduino. Arduino può poi leggere i valori dei potenziometri, e dunque capire se sia stata spostata una levetta in tempo reale e reagire di conseguenza (per esempio spegnendo o accendendo uno dei due servomotori). Maggiori informazioni sono disponibili su questa pagina Web.