orso base su arduino Terza parte Gli ingressi analogici caratteristiche hardware Il microcontrollore di arduino Uno possiede al suo interno un convertitore analogico digitale con 10 bit di risoluzione. Questo convertitore, d ora in poi denominato D (analog to digital converter), riceve l ingresso tramite un multiplexer analogico che fa capo ai piedini da 0 ad 5 della scheda. La tensione di riferimento dello D è la tensione stessa di alimentazione della scheda: 5 V, però è possibile modificarla inserendo la tensione desiderata (da 0 a max 5V) sul piedino ref (nalog reference) e impartendo una speciale istruzione software. on la tensione di riferimento predefinita (5 V), gli ingressi analogici accettano tensioni comprese tra 0 V e +5 V e le convertono in un valore intero compreso tra 0 e 1023. La risoluzione effettiva dello D è quindi pari a 5V / 1024 = 4,88 mv (circa). Esiste anche la possibilità di usare una tensione di riferimento interna di 1,1 V; in tal caso la risoluzione raggiunge 1,074 mv (circa), ma la precisione dipende molto dalla tensione di alimentazione. Inoltre, non si possono dare in ingresso allo D tensioni superiori alla tensione di riferimento. on le funzioni predefinite di arduino, il tempo di conversione è pari a 108 µs, che corrisponde a 9259 conversioni al secondo. Si possono raggiungere valori più elevati di conversioni al secondo, ma bisogna ricorrere alla programmazione dei registri interni del micro e, comunque, si riduce la risoluzione. Inoltre, molti sensori analogici hanno tempi di risposta lenti (temperatura, pressione, umidità, fotoresistori, ecc ) pertanto il convertitore risulta adeguato per applicazioni che ne prevedono l uso. Gli ingressi analogici sono ad elevata impedenza, quindi occorre fare attenzione ad eventuali disturbi elettrici che possono alterare il valore acquisito. Gli ingressi analogici software Per leggere un dato da un ingresso analogico si usa la funzione analogread(piedino_analogico), dove piedino_analogico identifica uno degli ingressi da 0 ad 5. La funzione ritorna un valore intero compreso tra 0 e 1023, come detto in precedenza. Esempi: int valore = analogread(0); legge da 0 e ritorna un valore da 0 a 1023 nella var. valore int fotoresistore = 0; int luce; luce = analogread(fotoresistore); come prima, ritorna da 0 a 1023 nella variabile luce float tensione; int sensore = 1; tensione = analogread(sensore) / 1024.0 * 5.0; ritorna la tensione applicata ad 1 (0 5 V) ome fare per cambiare la tensione di riferimento dello D e quindi il fondoscala? Si usa la funzione analogreference(), con questi possibili parametri: 1
analogreference(internl); tensione di rif. e fondoscala a 1,1 V (generati internamente) analogreference(externl); tensione di rif. e fondoscala esterni, inseriti in ref analogreference(defult); tensione di rif. e fondoscala predefiniti a 5V (Vcc) Se si usa il riferimento a 5 V non serve chiamare la funzione analogreference() a meno che non si passi da una tensione di riferimento esterna / interna a quella standard di 5V. Dopo la chiamata alla funzione analogreference( ) è necessario effettuare una lettura a vuoto per dare tempo allo D di assestarsi sul nuovo valore (basta chiamare la funzione analogread() e scartarne il valore). lcune applicazioni laboratoriali ominciamo con un semplice esempio: la lettura di un potenziometro da 10 KΩ e la visualizzazione di un valore proporzionale alla posizione del cursore tramite monitor seriale. 2
int cursore = 0; void setup() pinmode(cursore, INPUT); dichiaro 0 come ingresso (facoltativo) Serial.begin(9600); inizializzo la seriale a 9600 baud void loop() int valore = analogread(cursore); leggo la posizione del cursore (0-1023) Serial.print( posizione = ); Serial.println(valore); mostro valore e vado a capo float volt = valore / 1024.0 * 5.0; converto la lettura in volt Serial.print( Tensione = ); Serial.println(volt); mostro la tensione corrispondente delay(1000); aspetto un secondo per dare tempo di leggere e riparto con la lettura successiva Variante possibile: invece di mandare i dati al monitor seriale, visualizzarli su display LD. Seconda esperienza: lettura dell intensità luminosa tramite fotoresistore e accensione di una fila di 10 LED in proporzione alla quantità di luce acquisita. 3
Per chi lo preferisce, ecco lo schema elettrico dei collegamenti: V 5.0V 0 R11 6.8kΩ R9 LED9 IO11 LED10 R8 LED8 IO10 R7 LED7 IO9 R6 LED6 R5 LED5 IO8 R4 IO7 LED4 IO6 R3 LED3 IO5 R2 LED2 IO4 R1 IO3 LED1 IO2 R10 ccende da uno a 10 LED proporzionalmente all' intensità luminosa che colpisce un fotoresistore. pin2 = LED -significativo, pin11 = LED +significativo Fotoresistore tra pin 0 e Vcc, R = 6k8 tra 0 e GND const int const int LED[] = 2,3,4,5,6,7,8,9,10,11; SENSORE = 0; void setup() for (int i = 0; i < 10; i++) pinmode(led[i], OUTPUT); pinmode(sensore, INPUT); void loop() int luce = analogread(sensore); if (luce > 1020) luce = 1020; luce = luce / 102; for (int j = 0; j < luce; j++) digitalwrite(led[j], HIGH); for (int k = luce; k < 10; k++) digitalwrite(led[k], LOW); delay(200); se luce tra 1021 e 1023 allora luce = 1020 = 102 * 10 porto il valore da 0-1023 a 0-10 accendo tutti i LED da 0 a luce-1 spengo gli altri LED il tempo per vederli accesi rduino ha anche la funzione map() che converte un intervallo di interi in un altro intervallo: 4
int valore; valore = map(valore, in_min, in_max, out_min, out_max); converte valore dal range (in_min, in_max) al range (out_min, out_max) llora il programma può essere riscritto in questo modo: const int const int LED[] = 2,3,4,5,6,7,8,9,10,11; SENSORE = 0; void setup() for (int i = 0; i < 10; i++) pinmode(led[i], OUTPUT); pinmode(sensore, INPUT); void loop() int luce = analogread(sensore); luce = map(luce, 0, 1023, 0, 10); for (int j = 0; j < luce; j++) accendo tutti i LED da 0 a luce-1 digitalwrite(led[j], HIGH); for (int k = luce; k < 10; k++) digitalwrite(led[k], LOW); spengo gli altri LED delay(200); il tempo per vederli accesi l posto del fotoresistore si può usare un NT, in tal caso il numero di LED accesi sarà proporzionale alla temperatura. Per vedere la variazione è sufficiente avvicinare la punta di un saldatore acceso allo NT (senza toccarlo!). Un ulteriore variante prevede la visualizzazione del valore di luminosità (da 0 a 1023) su un display LD. U1 V 5.0V D7 D6 D5 D4 D3 D2 D1 D0 FR E RS RW 5.0V V V GND V 0 R2 6.8kΩ IO5 IO4 IO3 IO2 R1 10kΩ 50 % Key= IO12 IO11 Notate che nello schema circuitale riportato qui sopra non è presente la retroilluminazione ed i piedini del display non sono ordinati in senso crescente (Vcc è il piedino 2, non l 1, ecc ). Lo schema di cablaggio riportato nella pagina seguente riporta la retroilluminazione ed i piedini del display sono nell ordine corretto. 5
#include <Liquidrystal.h> Liquidrystal lcd(12, 11, 5, 4, 3, 2); RS, E, D4, D5, D6, D7 int SENSORE = 0; void setup() lcd.begin(16, 2); display 16 colonne 2 righe pinmode(sensore, INPUT); void loop() int luce = analogread(sensore); lcd.clear(); pulisci display lcd.print("luce = "); lcd.print(luce); delay(2000); aggiorna la lettura ogni 2 secondi 6
Termometro digitale Viene utilizzato un sensore integrato LM35, caratterizzato da una sensibilità di 10 mv/, tensione di alimentazione da 4V a 30V, accuratezza 0,5 a 25. Se alimentato con tensioni positive, ha un range da 0 a +150. Gli esemplari che abbiamo in laboratorio hanno un contenitore plastico TO92 e la piedinatura, visto da sotto, è riportata nella figura seguente. Lo schema di cablaggio utilizzato è : 7
Schema elettrico: U1 V V GND 5.0V 1 5.0V D7 D6 D5 D4 D3 D2 D1 D0 V E RS RW V Vs 0 2 Vout U2 LM35 3 Gnd IO5 IO4 IO3 IO2 R1 10kΩ 50 % Key= IO12 IO11 lcune note riguardanti il programma. Data la sensibilità del sensore, alla temperatura di 100, l uscita è pari a 1V, pertanto viene usato solo 1/5 della scala dello D. onviene allora, usare come tensione di riferimento quella interna di 1,1V. La precisione di questa tensione è legata alla precisione della tensione di alimentazione, che nelle prese USB difficilmente è pari a 5,000 V. L ideale sarebbe di fornire al piedino ref di arduino una tensione stabile e precisa proveniente da un apposito integrato generatore di tensione di riferimento. La costante 9,31 si ricava tramite una proporzione. Dato che il fondo scala dello D è 1,1V, che corrisponde ad una temperatura di 110 (10mV/ ) e ad un valore in uscita dallo D di 1024, allora: X 110 = cioè val 1024 X = val 110 val = 1024 9.31 #include <Liquidrystal.h> Liquidrystal lcd(12, 11, 5, 4, 3, 2); RS, E, D4, D5, D6, D7 int lm35pin = 0; ingresso sensore temperatura void setup() lcd.begin(16, 2); pinmode(lm35pin, INPUT); analogreference(internl); analogread( lm35pin); lcd.clear(); void loop() lcd.home(); float temperatura = 0.0; int val = 0; float somma = 0.0; int num_letture = 5; 8 display 16 colonne 2 righe Vriferimento = 1,1 V lettura a vuoto per assestare adc pulisce il display singolo valore letto dal sensore (0-1023) viene fatta la media di 5 letture
for ( int i = 0; i < num_letture; i++) val = analogread( lm35pin); temperatura = val / 9.31; 9,31 unità per grado elsius somma += temperatura; temperatura = somma / num_letture; lcd.print( temp = ); lcd.print(temperatura); lcd.print( ); delay(5000); aggiorna ogni 5 secondi Termostato con regolazione manuale della temperatura ltro esempio proposto agli studenti delle mie classi. Il sensore è ancora uno LM35 e la regolazione della temperatura avviene tramite potenziometro i cui terminali estremi sono collegati rispettivamente a GND e ref, da cui esce una tensione di 1,1V (riferimento interno). Il cursore va collegato all ingresso analogico 1. i piedini 2 e 3 di arduino sono collegati 2 LED; il primo visualizza lo stato di caldaia in funzione, il secondo di caldaia spenta. l loro posto si può usare un relé, come nei termostati commerciali. Nel programma è inserita un isteresi di ±1 per non provocare un eccessivo numero di commutazioni dello stato della caldaia. ome al solito, prima propongo lo schema di cablaggio e poi il programma. U1 V V GND 5.0V 1 5.0V D7 D6 D5 D4 D3 D2 D1 D0 V E RS RW V Vs 2 0 Vout U2 LM35 Gnd IO5 IO4 IO3 IO2 R1 3 10kΩ Key= 50 % IO12 REF IO11 LED1 R2 10kΩ Key=B 50 % R3 1 LED2 R4 9 IO9 IO8
#include <Liquidrystal.h> #define ONPin 8 #define OFFPin 9 LED rosso = caldaia in funzione LED verde = caldaia in standby #define LM35 0 #define POT 1 sensore LM35 su pin 0 cursore potenziometro su pin 1 float readtemp(void); prototipo della funzione che legge dal sensore int acceso = 0; inizialmente, caldaia spenta Liquidrystal lcd(12, 11, 5, 4, 3, 2); void setup() pinmode(lm35,input); pinmode(pot,input); 10
analogreference(internl); per l'd usiamo il Vref interno da 1,1V (migliore risoluzione) analogread(lm35); Prima lettura "a vuoto" (serve per l'assestamento dell'd) pinmode(onpin,output); LED rosso in uscita pinmode(offpin,output); LED verde in uscita lcd.begin(16,2); lcd.setursor(3,0); su LD appare il titolo per 2 secondi lcd.print("termostato"); lcd.setursor(4,1); lcd.print("digitale"); delay(2000); void loop() float tamb = readtemp(); legge il valore della temperatura e la memorizza nella variabile temp. lcd.clear(); pulisce lo schermo LD lcd.print("tamb: "); lcd.print(tamb,1); Stampa la parte intera e un decimale della temp lcd.print(' '); Stampa uno spazio lcd.print(''); lcd.setursor(0,1); cursore in posizione: colonna 0, riga 1 lcd.print("tprog: "); int tprog = analogread(pot); tprog = map(tprog,0,1023,4,30); tmin=4, tmax=30 lcd.print(tprog); Stampa il valore di tprog sullo schermo /* accensione dei LED con isteresi */ if(tamb < tprog-1) digitalwrite(onpin, HIGH); digitalwrite(offpin, LOW); acceso = 1; if(tamb > tprog+1) digitalwrite(onpin, LOW); digitalwrite(offpin, HIGH); acceso = 0; if (acceso == 1) lcd.setursor(14,0); lcd.print('*'); quando la caldaia è accesa, appare un * su LD else lcd.setursor(14,0); lcd.print(' '); delay(1000); attende 1 secondo /* Funzione che legge la temperatura dal sensore */ float readtemp() float temp = 0.0; valore convertito in temperatura ( ) int val = 0; valore quantizzato dall'd [0..1023] int nread = 5; numero di letture (consigliabile da 5 a 8) float somma = 0.0; somma delle letture for (int i=0; i<nread; i++) 11
val = analogread(lm35); legge il dato della tensione sul pin 'LM35' temp = val / 9.31; converte la temperatura in somma += temp; aggiunge alla somma delle temperature lette return ( somma / nread ); calcola il valore medio Uno studente mi ha presentato una soluzione che prevedeva di regolare la temperatura desiderata mediante due pulsanti: con il primo si aumenta di 1 alla volta, con il secondo la si diminuisce della stessa quantità. Possibile variante: impostare la temperatura programmata e visualizzare la temperatura ambiente tramite smartphone, via Bluetooth. Materiale extra Sensore ad ultrasuoni H-SR04 Si tratta di un sensore che contiene due capsule ad ultrasuoni: una trasmittente e l altra ricevente. E particolarmente economico (costa qualche euro) e abbastanza preciso. Dato che invia un cono di ultrasuoni, funziona meglio con ostacoli piani e non eccessivamente piccoli (>0,5 mq). Ha una portata massima di 4 metri e lo si utilizza spesso come rilevatore di ostacoli in piccoli robot. Il sensore dispone di 4 terminali: GND e Vcc vanno collegati all alimentazione a 5V, su Trig si deve fornire un impulso di almeno 10 microsecondi che farà partire un treno di ultrasuoni (8 impulsi a 40 Khz), su Echo riceveremo un impulso a livello alto pari alla durata del tragitto andata e ritorno degli ultrasuoni. Lo schema seguente, tratto dal datasheet del componente, ne esemplifica il principio di funzionamento. 12
L esempio che propongo è la realizzazione di un metro ad ultrasuoni con visualizzazione sul monitor seriale. La velocità del suono nell aria alla temperatura di 20 è di circa 343,4 m/s, qui useremo la velocità approssimata di 340 m/s per semplicità; se è necessaria una maggior precisione si può usare la seguente legge in funzione della temperatura: V = 331,4 + 0,62*T. Nel programma si possono notare due nuove funzioni di arduino: delaymicroseconds(costante o variabile) che genera un ritardo specificato in microsecondi e pulsein(piedino, livello logico) che ritorna la durata di un impulso in microsecondi, al livello logico specificato (HIGH o LOW) sul piedino d ingresso specificato. lternativamente, invece del monitor seriale, si può usare un display LD per visualizzare la distanza. 13
H SR04 Sensore ultrasuoni int triggerport = 7; int echoport = 8; void setup() pinmode( triggerport, OUTPUT ); piedino trigger in uscita pinmode( echoport, INPUT ); piedino echo in ingresso Serial.begin( 9600 ); inizializza porta seriale a 9600 baud Serial.println( "Sensore ultrasuoni: "); scrivi titolo void loop() porta a livello basso l'uscita del trigger digitalwrite( triggerport, LOW ); invia un impulso di 10microsec su trigger digitalwrite( triggerport, HIGH ); delaymicroseconds( 10 ); digitalwrite( triggerport, LOW ); long duration = pulsein( echoport, HIGH );pulsein aspetta che echoport vada alto e poi ritorni basso e restituisce la durata dell'impulso in microsecondi long r = 0.034 * duration / 2; 340 m/s velocità del suono = 34000 cm, la durata è secondi x 10 alla -6 = 0,034 cm/us e viene divisa per 2 perché comprende il tempo di andata e di ritorno Serial.print( "durata: " ); 14
Serial.print( duration ); Serial.print( ", " ); Serial.print( "distanza: " ); dopo 38ms è sicuramente fuori dalla portata del sensore if( duration > 38000 ) Serial.println( "fuori portata"); else Serial.print( r ); Serial.println( "cm" ); aspetta 1.5 secondi delay( 1500 ); e ripeti la lettura Materiale extra La funzione millis( ) Quando s inizia a programmare arduino c è la tendenza a usare molto la funzione delay, ma dopo un po ci si scontra con un esigenza particolare: come fare per eseguire più attività contemporaneamente, o almeno che procedano indipendentemente una dall altra? d esempio, come far lampeggiare due LED con frequenze diverse? Dovremmo accendere il primo LED, dopo un certo tempo spegnerlo, attendere un altro po di tempo e riaccenderlo. E il secondo LED? E se i LED fossero 5? hiariamo subito che l ambiente di sviluppo di arduino non è multi tasking; è vero che esistono delle librerie, reperibili in rete, che implementano sistemi operativi real time, ma occupano parecchio spazio nella memoria flash lasciandone poca a disposizione del nostro programma. Una soluzione più avanzata consiste nell usare i 3 timer di cui è dotato il microcontrollore in unione con gli interrupt, ma risulta troppo difficile per un neofita perché bisogna agire sui registri interni. Il problema è che la funzione delay è bloccante: finché non è trascorso il tempo in millisecondi passato come argomento arduino non può fare nulla, ad esempio, non può leggere lo stato di un pulsante, il valore di un sensore o aggiornare un display LD. Fortunatamente, esiste la funzione millis( ) che ritorna, su una variabile unsigned long, il valore in millisecondi del tempo trascorso da quando la scheda è stata alimentata ed è aggiornato automaticamente da un timer interno del microcontrollore. Dato che una variabile unsigned long può contenere un valore che va da 0 a circa 4,3 miliardi, facendo gli opportuni calcoli, si vede che ritornerà a 0 dopo circa 49 giorni. Infatti, 4,3 miliardi diviso 1000 equivale a 4,3 milioni di secondi, diviso 60 fornisce 71666,7 minuti che, divisi a loro volta per 60, danno 1194,44 ore equivalenti a 49,77 giorni. Qualora la nostra applicazione richiedesse di operare con la scheda alimentata 24 ore su 24, non ci si deve comunque preoccupare perché, per il modo in cui funzionano le operazioni sulle variabili unsigned, se calcoliamo il tempo come differenza tra il valore attuale restituito da millis e il valore letto in precedenza, il problema si risolve da solo. Gli esempi chiariranno la modalità d uso di questa funzione. Primo esempio: far lampeggiare il LED interno di arduino con periodo di 2 secondi. Prima richiamiamo brevemente il programma che svolgeva questo compito utilizzando la funzione delay: 15
#define LED 13 definizione della costante LED void setup() viene eseguita una sola volta all inizio pinmode(led, OUTPUT); definisco il piedino 13 come uscita digitale void loop() Per sempre finché non stacchi la corrente digitalwrite(led, HIGH); Manda un livello alto (5V) al LED delay(1000); attendi 1000 millisecondi digitalwrite(led, LOW); Manda un livello basso (0V) al LED delay(1000); attendi 1000 millisecondi In questo programma, per la maggior parte del tempo, arduino non fa nulla perché deve attendere il completamento delle due chiamate alla funzione delay. Vediamo allora una possibile soluzione che non fa uso di delay: #define LED 13 definizione della costante LED int statoled = LOW; memorizza lo stato del LED unsigned long previousmillis = 0; memorizza l ultimo istante in cui lo stato del LED è stato modificato unsigned long intervallo = 1000; numero di ms dopo il quale modificare lo stato del LED void setup() viene eseguita una sola volta all inizio pinmode(led, OUTPUT); definisco il piedino 13 come uscita digitale void loop() unsigned long currentmillis = millis(); leggo il valore attuale di ms se il valore attuale quello precedente è >= di intervallo if ((currentmillis previousmillis)>=intervallo) previousmillis = currentmillis; salvo l istante in cui ho modificato lo stato del LED if (statoled == LOW) se il led era spento lo accendo e viceversa statoled = HIGH; else statoled = LOW; digitalwrite(led, statoled); scrivo lo stato sul LED fine if((currentmillis fine loop In questo programma non c è nulla che lo blocchi; se voglio eseguire altre istruzioni è sufficiente metterle dopo la fine dell istruzione if ((currentmillis e prima della fine della funzione loop( ). Lo stesso programma può essere rielaborato in forma più compatta nel modo seguente (http:www.leonardomiliani.com/2013/programmiamo-i-compiti-con-millis/) : const int ledpin = 13; piedino a cui è collegato il LED interno int ledstate = 0; stato iniziale del LED unsigned long previousmillis = 0; ultimo istante di aggiornamento LED unsigned long interval = 1000; dopo quanti ms cambiare lo stato del LED void setup() pinmode(ledpin, OUTPUT); imposta il piedino 13 come uscita digitale 16
void loop() unsigned long currentmillis = millis(); if(currentmillis - previousmillis >= interval) previousmillis = currentmillis; ledstate ^= 1; ^ è l operazione xor binaria e inverte lo stato digitalwrite(ledpin, ledstate); Secondo esempio: facciamo lampeggiare il LED interno tenendolo acceso per 100 ms e spento per 750 ms: int ledpin = 13; il piedino a cui è collegato il LED interno int ledstate = LOW; stato iniziale del LED = spento unsigned long previousmillis = 0; memorizza l ultimo istante in cui lo long OnTime = 100; long OffTime = 750; void setup() pinmode(ledpin, OUTPUT); stato del LED è stato modificato millisecondi di tempo con LED acceso millisecondi di tempo con LED spento definisco il piedino 13 come uscita digitale void loop() leggo il valore attuale di ms unsigned long currentmillis = millis(); se il LED era acceso e sono trascorsi 100 ms if((ledstate == HIGH) && (currentmillis - previousmillis >= OnTime)) ledstate = LOW; spegni LED previousmillis = currentmillis; salvo l istante in cui ho digitalwrite(ledpin, ledstate); modificato lo stato del LED agisco sul LED spegnendolo altrimenti, se il LED era spento e sono passati 750 ms else if ((ledstate == LOW) && (currentmillis - previousmillis >= OffTime)) ledstate = HIGH; accendi LED previousmillis = currentmillis; salvo l istante in cui ho digitalwrite(ledpin, ledstate); modificato lo stato del LED agisco sul LED accendendolo Vediamo ora un esempio più complesso, sempre tratto dal sito http:www.leonardomiliani.com/2013/programmiamo-i-compiti-con-millis/ In questo programma vogliamo eseguire due compiti: far lampeggiare il LED interno alla scheda con periodo di 200 ms (100 ms acceso e 100 ms spento) e visualizzare sul monitor seriale un conteggio che si aggiorna ogni secondo. Sono state utilizzate due funzioni per rendere più leggibile il codice. Nota: l ambiente di sviluppo di arduino non ci obbliga a scrivere i prototipi delle funzioni. 17
const byte ledpin = 13; piedino a cui è collegato il LED interno byte ledstate = 0; stato iniziale del LED = spento unsigned long previousmillis1 = 0; memorizza l ultimo istante in cui lo stato del LED è stato modificato numero di ms dopo il quale modificare lo stato del LED unsigned long previousmillis2 = 0;memorizza l ultimo istante in cui ho aggiornato la variabile del conteggio unsigned long interval2 = 1000; numero di ms dopo il quale incrementare il conteggio unsigned long interval1 = 100; unsigned int counter = 0; void setup() Serial.begin(9600); delay(2000); pinmode(ledpin, OUTPUT); variabile che memorizza il conteggio inizializzo il monitor seriale a 9600 baud attendo che la seriale sia inizializzata imposto il piedino del LED come uscita void loop() blinkled(); chiama funzione che fa lampeggiare il LED printounter(); chiama funzione che visualizza il conteggio funzione che fa lampeggiare il LED void blinkled() guardo se devo cambiare lo stato del LED da acceso a spento o viceversa if (millis() - previousmillis1 > interval1) previousmillis1 = millis(); salvo l istante in cui ho modificato lo stato del LED se il LED era spento, lo accendo e viceversa: ledstate ^= 1; digitalwrite(ledpin, ledstate); funzione che visualizza il conteggio sul monitor seriale void printounter() guardo se devo visualizzare il nuovo valore if (millis() - previousmillis2 > interval2) previousmillis2 = millis(); salvo l istante in cui ho incrementato il conteggio Serial.println(++counter); incremento il conteggio e lo visualizzo Ultimo esempio: far lampeggiare due LED con frequenze e tempi di accensione e spegnimento differenti. Queste variabili memorizzano le sequenze di accensione e lo stato attuale dei LED int ledpin1 = 12; il primo LED è collegato al piedino 12 int ledstate1 = LOW; variabile che memorizza lo stato del LED1 unsigned long previousmillis1 = 0; ultimo istante di aggiornamento del LED1 long OnTime1 = 250; millisecondi di tempo acceso long OffTime1 = 750; millisecondi di tempo spento int ledpin2 = 11; 18 il secondo LED è collegato al piedino 11
int ledstate2 = LOW; variabile che memorizza lo stato del LED2 unsigned long previousmillis2 = 0; ultimo istante di aggiornamento del LED2 long OnTime2 = 330; millisecondi di tempo acceso long OffTime2 = 400; millisecondi di tempo spento void setup() imposta i piedini collegati ai LED come uscite: pinmode(ledpin1, OUTPUT); pinmode(ledpin2, OUTPUT); void loop() leggo il valore attuale di ms unsigned long currentmillis = millis(); if((ledstate1 == HIGH) && (currentmillis - previousmillis1 >= OnTime1)) ledstate1 = LOW; stato LED1 = spento previousmillis1 = currentmillis; Memorizzo l istante in cui l ho spento digitalwrite(ledpin1, ledstate1); gisco su LED1 spegnendolo else if ((ledstate1 == LOW) && (currentmillis - previousmillis1 >= OffTime1)) ledstate1 = HIGH; stato LED1 = acceso previousmillis1 = currentmillis; Memorizzo l istante in cui l ho acceso digitalwrite(ledpin1, ledstate1); gisco su LED1 accendendolo if((ledstate2 == HIGH) && (currentmillis - previousmillis2 >= OnTime2)) ledstate2 = LOW; stato LED2 = spento previousmillis2 = currentmillis; Memorizzo l istante in cui l ho spento digitalwrite(ledpin2, ledstate2); gisco su LED2 spegnendolo else if ((ledstate2 == LOW) && (currentmillis - previousmillis2 >= OffTime2)) ledstate2 = HIGH; stato LED2 = acceso previousmillis2 = currentmillis; Memorizzo l istante in cui l ho acceso digitalwrite(ledpin2, ledstate2); gisco su LED2 accendendolo partire dagli esempi riportati si possono realizzare programmi con eventi temporizzati più complessi, senza dover utilizzare la funzione delay( ). Materiale extra rduino e i numeri casuali volte capita di dover utilizzare numeri casuali all interno dei nostri programmi. lassici esempi possono riguardare la realizzazione di un gioco, la simulazione del lancio di un dado, la variazione casuale dei colori in una serie di LED RGB, ecc L ambiente di sviluppo possiede, a tale scopo, la funzione random che restituisce un intero di tipo long (32 bit) e che può essere usata nelle due seguenti modalità: long random(max); ritorna un valore compreso tra 0 (incluso) e max-1 long random(min,max); ritorna un valore compreso tra min (incluso) e max-1 19
Esempi: long casuale; long valoremassimo = 300; casuale = random(valoremassimo); ritorna un valore tra 0 (incluso) e 299 long casuale; long valoremassimo = 3000000; long valoreminimo = 2345; casuale = random(valoreminimo, valoremassimo); ritorna un valore tra 2345 e 2999999 Prima di chiamare la funzione random è necessario però inizializzare il generatore di numeri casuali utilizzando un altra funzione: randomseed. Quest ultima accetta come parametro un numero intero (long oppure int) e genera una sequenza di numeri pseudo-casuali. Ora, ogni volta che si invocherà la funzione random, si otterrà come risultato un numero pseudo-casuale compreso nel campo di valori specificato in precedenza (tra min e max-1). long casuale; long valoremassimo = 3000000; long valoreminimo = 2345; randomseed(100); casuale = random(valoreminimo, valoremassimo); ritorna un valore tra 2345 e 2999999 Il problema è che la sequenza dipende dal valore passato alla funzione randomseed; se faccio ripartire il programma, otterrò sempre la medesima sequenza. Si può risolvere mettendo come argomento della funzione randomseed una lettura ad un ingresso analogico non utilizzato. Infatti, essendo gli ingressi analogici ad alta impedenza, la lettura da uno di essi non utilizzato fornirà numeri abbastanza a caso che potremo usare per generare ogni volta una sequenza diversa di numeri pseudo-casuali. L esempio, preso dal sito www.arduino.cc/en/reference/random e leggermente modificato, visualizza sul monitor seriale una sequenza di numeri pseudo-casuali con la tecnica esposta sopra: long randnumber; void setup() Serial.begin(9600); Se l ingresso analogico 0 è scollegato, il rumore elettrico captato farà in modo che randomseed() generi sequenze pseudo-casuali differenti ogni volta che il programma viene eseguito. randomseed(analogread(0)); void loop() visualizza un numero random number tra 0 e 299 randnumber = random(300); Serial.println(randNumber); visualizza un numero pseudo-casuale tra 10 e 19 randnumber = random(10, 20); 20
Serial.println(randNumber); delay(500); 21