Durante la scrittura di uno sketch, l'IDE fornisce informazioni sulla correttezza delle parole chiave, colorando in modo differente quelle che riconosce appartenenti al lessico del linguaggio di programmazione, inoltre, in fase di compilazione, l'IDE ci invia messaggi nel caso in cui individui degli errori di sintassi nell'uso delle istruzioni.

Una volta che la compilazione e il caricamento su Arduino sono andati a buon fine, non siamo ancora sicuri che il programma esegua esattamente quello che ci eravamo prefissati: possono esserci degli errori di programmazione, difficilmente individuabili con la sola osservazione del comportamento del circuito.

Durante l'esecuzione di uno sketch, per effettuare la ricerca di eventuali errori di programmazione (debugging), si può interagire con Arduino mediante il Monitor Seriale.

 

Gli elementi principali della finestra del Monitor Seriale sono:

  1. Trasmissione da PC verso Arduino
  2. Trasmissione da Arduino verso PC

Le principali istruzioni per l'impiego del Monitor Seriale sono:

Serial.begin(9600); → imposta in setup la velocità di comunicazione a 9600 bps (bit per secondo).

PC → Arduino

Serial.read(); → restituisce il valore trasmesso dal PC via monitor seriale. 

Serial.available(); → restituisce il numero di byte arrivati dal PC  sulla porta seriale e disponibili per la lettura.

flush(); → cancella la coda dei dati arrivati dal PC sulla //sulla porta seriale.

 

Arduino → PC (RX)

Serial.println(val); →stampa sul monitor il valore di val (variabile o costante) e va a capo; al posto di val può esserci una stringa di caratteri compresa tra apici: ad esempio "La velocità è:"

Serial.print(val); //È come Serial.println(val) ma non va a capo dopo la stampa

 

L'esempio seguente è utile per sperimentare le principali istruzioni per la trasmissione e la ricezione mediante Monitor Seriale.

 

Scambio di informazioni tra PC e Arduino, con il Monitor Seriale

Invia un carattere dal monitor seriale; Arduino lo riceve e poi lo ritrasmette al PC scrivendo sul monitor «Ho ricevuto»: seguito dal codice ASCII (in decimale) del carattere.

Per esempio, se scrivi il carattere a e premi invio nella linea in alto della finestra, nel campo di ricezione del monitor seriale appare: Ho ricevuto: 97 (il codice ASCII decimale corrispondente alla lettera a minuscola è 97).

Soluzione

Il codice dello sketch 

int byteRicevuto = 0; 

void setup()
{

Serial.begin(9600);
}

void loop()
{

if (Serial.available() > 0)
   {

    byteRicevuto = Serial.read();
    Serial.print("Ho ricevuto:");
    Serial.println(byteRicevuto);
    }
}

 

Una volta effettuata la messa a punto del programma, le istruzioni relative al Monitor Seriale possono essere rimosse, anche perché Arduino in genere è destinato a funzionare stand alone, senza computer con cui scambiare dati sul Monitor Seriale.

Oltre che per il debugging, il Monitor Seriale può essere utilizzato per sostituire, in fase di sviluppo, un display non ancora disponibile su cui visualizzare dei dati, come nel seguente esercizio. 

Visualizza un conteggio con il Monitor Seriale Conta il numero di pressioni di un pulsante (collegato al digital pin 7 di Arduino) e visualizza il risultato sul Monitor Seriale.

Soluzione

il codice dello sketch

bool puls;
const int pinPuls=7; 
int cont=0; 

void setup()

{
pinMode(pinPuls, INPUT_PULLUP); 
Serial.begin(9600); 
}

void loop()

{
do{
puls = digitalRead(pinPuls);
}while(puls);

cont++; 
Serial.print("N° pressioni:");
Serial.println(cont); 
delay(20); 

do{
puls = digitalRead(pinPuls); 
}while(puls==0); 
delay(20); 
}

 

 

 

La trasmissione seriale

All'interno di un computer o di un microprocessore/microcontrollore la trasmissione dei dati avviene su più linee contemporaneamente (trasmissione parallela sul bus dati). In questo modo ad ogni clock si possono inviare più bit in uno stesso istante, rendendo veloce la comunicazione.

All'esterno del computer, nella comunicazione con le periferiche, si preferisce inviare un bit alla volta (trasmissione seriale); uno dei motivi è ridurre il numero di fili nei cavi, rendendoli più maneggevoli ed economici.

Uno standard diffuso per la trasmissione seriale è l'USB, acronimo di Universal Serial Bus (Bus Universale Seriale).

Il Monitor Seriale usa la trasmissione seriale per la comunicazione con il computer via USB. Tale comunicazione è replicata sui digital pin 0 (RX dal computer) e pin 1 (TX verso il computer), per cui, se si usa il Monitor Seriale, i digital pin 0 e 1 non devono essere utilizzati per l'input/output.

In realtà, se si impiegano solo istruzioni Serial.print() per la comunicazione di dati verso il computer, in caso di necessità il pin 0 (ricezione dal computer) può essere utilizzato senza creare conflitto, come  si vede nel seguente progetto.

Gara di riflessi

Si progetti un circuito per gestire una gara di riflessi tra due concorrenti A e B, utilizzando i seguenti elementi:

  • un pulsante e un LED rosso per ogni concorrente;
  • un pulsante di start, un LED verde, un buzzer.

Lo svolgimento di una prova è il seguente:

  1. durante la fase di attesa il LED verde e acceso,
  2. quando si preme start il LED verde si spegne per un tempo di durata casuale (da 3 s a 8 s);
  3. al termine del tempo suona il buzzer e si riaccende il LED verde;
  4. il primo concorrente che preme il proprio pulsante vince: il suo LED lampeggia e il buzzer suona sincrono col LED per 5 s;
  5. se alla fine del tempo casuale (al suono del buzzer) uno dei due concorrenti sta già premendo il pulsante, vince l'altro, se ha premuto correttamente.

Inoltre, al termine di ogni prova, deve apparire sul Monitor Seriale:

  • la scritta: «Ha vinto» seguita dal nome del concorrente vincente (A o B);
  • nella riga sotto il punteggio dei due concorrenti, da quando lo sketch è stato caricato o dall'ultimo reset.

Bisogna considerare la possibilità di aggiungere in futuro un display a cristalli liquidi per visualizzare, oltre al vincitore della prova e ai punteggi dei concorrenti, anche il tempo del vincitore e il record.

Soluzione

Si fanno le seguenti scelte hardware.

  • pulsanti:

                      PA (pulsante concorrente A) → pin 13;
                      PB → pin 0;
                      Pstart → pin 8

  • LED:

                    LA (LED concorrente A) → pin 10;
                    LB → pin 6;
                    Lverde → pin 7

  • Buzzer → pin 9

La scelta dei pin tiene conto di una possibile espansione con un display LCD, per il quale si riservano i pin (12, 11, 5, 4, 3, 2), e del fatto che utilizzando il Monitor Seriale solo in TX (pin 1) da Arduino al computer, il pin 0 (RX se riale) può essere collegato al pulsante PB senza conflitto. Analizziamo lo sketch:

/*

Gara di riflessi
3 pulsanti, 3 LED, 1 Buzzer
Risultati su Monitor Seriale
*/
const int pinPA=13; // pin 13 pulsante concorrente A
const int pinPB=0; // pin 0 pulsante concorrente B
const int pinPstart=8; // pin 8 pulsante start
const int pinBuzzer=9; // pin 9 Buzzer
const int pinLA=10; // pin 10 LED concorrente A
const int pinLB=6; // pin 6 LED concorrente B
const int pinLverde=7; // pin 7 LED verde
bool PA; // stato del pulsante A
bool PB; // stato del pulsante B
bool Pstart; // stato del pulsante Start
int totWinA=0; // contatore vittorie concorrente A
int totWinB=0; // contatore vittorie concorrente B
int nota; // frequenza della nota del vincitore

void setup(){

pinMode(pinPA, INPUT_PULLUP); // pin digitale 13 input (pull-up interno)
pinMode(pinPB, INPUT_PULLUP); // pin digitale 0 input (pull-up interno)
pinMode(pinPstart, INPUT_PULLUP); // pin digitale 8 input (pull-up interno)
pinMode(pinBuzzer, OUTPUT); // pin digitale 9 output (Buzzer)
pinMode(pinLA, OUTPUT); // pin digitale 10 output
pinMode(pinLB, OUTPUT); // pin digitale 6 output
pinMode(pinLverde, OUTPUT); // pin digitale 7 output

randomSeed(analogRead(0)); // inizializza la funzione random, leggendo del rumore
// sul pin scollegato analog input 0
Serial.begin(9600); // apri la porta seriale alla velocità di 9600 bps
}

void loop(){

digitalWrite(pinLverde, HIGH); // accendi il LED verde
do{
Pstart = digitalRead(pinPstart); // leggi il pulsante Start
} while (Pstart==1); // finchè il pulsante Start non è premuto

digitalWrite(pinLverde, LOW); // spegni il LED verde
delay(random(3000, 8000)); // attendi un tempo casuale tra 3 s e 8 s
PA = digitalRead(pinPA); // leggi il pulsante PA
PB = digitalRead(pinPB); // leggi il pulsante PB
tone (pinBuzzer, 262); // suona la nota Do (262 Hz)
digitalWrite(pinLverde, HIGH); // riaccendi il LED verde

if(PA==0 || PB==0) { //se uno dei due concorrenti ha premuto in anticipo
if(PA==0 && PB==1) vincitore(pinLB); //se PA è già premuto vince B (chiama la function "vincitore")
if(PA==1 && PB==0) vincitore(pinLA); //se PB è già premuto vince A (chiama la function "vincitore")
if(PA==0 && PB==0) Serial.println("Gara non valida"); // se entrambi hanno premuto in anticipo
}
else { // se è tutto regolare aspetta la prima pressione
do {
PA = digitalRead(pinPA); // leggi il pulsante PA
PB = digitalRead(pinPB); // leggi il pulsante PB
} while(PA && PB); // finchè non viene premuto un pulsante

if(PA==0) vincitore(pinLA); // se P1 è premuto vince A (chiama la function "vincitore")
if(PB==0) vincitore(pinLB); // se P2 è premuto vince B (chiama la function "vincitore")
}
noTone (pinBuzzer); // spegni il buzzer
}

void vincitore (int winner){ // FUNCTION che esegue le operazioni relative al vincitore;
// il pin del LED è passato e associato al parametro winner
digitalWrite(pinLverde, LOW); // spegni il LED verde

if(winner==pinLA){ // se ha vinto A
Serial.println("Ha vinto A"); // scrivi sul serial Monitor "Ha vinto A"
totWinA++; // incrementa i punti di A
nota=440; // assegna alla variabile nota il valore 440 (LA)
}
if(winner==pinLB){ // se ha vinto B
Serial.println("Ha vinto B"); // scrivi sul serial Monitor "Ha vinto B"
totWinB++; // incrementa i punti di B
nota=587; // assegna alla variabile nota il valore 587 (RE)
}
// scrivi sul serial monitor i punti totali di A e di B
Serial.print("Punti totali A:");
Serial.print(totWinA);
Serial.print(" B:");
Serial.println(totWinB);

// fai lampeggiare il LED e suona la nota del vincitore, per 5 volte
for(int cont =1; cont<=5; cont++){ // per 5 volte
tone (pinBuzzer, nota); // suona la nota del vincitore
digitalWrite(winner, HIGH); // accendi il LED del vincitore
delay(500); // aspetta 500 ms
noTone (pinBuzzer); // interrompi la nota
digitalWrite(winner, LOW); // spegni il LED
delay(500); // aspetta 500 ms
}
} 

Nella prima parte dello sketch puoi notare, oltre alle varie definizioni e inizializzazioni, l'istruzione

randomSeed(analogRead(0));

che serve ad inizializzare la funzione random, usata nel loop per genera re un tempo casuale tra 3 s e 8 s: analogRead(0) legge la tensione presente sul pin analog input 0, che, essendo scollegato, sarà un valore casuale dovuto al rumore captato sul pin 0.

La seconda parte dello sketch è la funzione loop():

  • i commenti descrivono il flusso del programma, che evolve come richiesto dalle specifiche;
  • si può notare l'uso della funzione random, come parametro dell'istruzione delay, per ottenere un ritardo casuale, compreso tra 3000 ms e 8000 ms, random(min, max) va usata abbinata a randomSeed(analogRead(0)), contenuta in setup, per ottenere un numero casuale tra i valori min e max;
  • nell'istruzione if (PA==| | PB==0), il simbolo realizza la funzione logica OR tra le due espressioni: il risultato è VERO se almeno una delle due è VERA, cioè se almeno un concorrente ha premuto (A o B);
  • nell'istruzione if (PA==0 && PB==1), il simbolo && realizza la funzione logica AND tra le due espressioni: il risultato è VERO se le espressioni sono entrambe VERE, cioè se A ha premuto durante il suono mentre B non premeva (quindi vince B);
  • notare che in entrambe le istruzioni if le graffe non sono necessarie perché il blocco è di una sola riga;
  • in vari punti dello sketch si nomina vincitore(): questa è una chiamata alla function vincitore, definita nella terza parte dello sketch, alla quale si comunica (con il parametro tra parentesi) il pin del LED del vincitore (pinLA o pinLB). La function farà lampeggiare tale LED e altre operazioni relative al vincitore della sfida.

La terza porzione di sketch è costituita dalla function vincitore, scritta fuori dalla funzione loop().

La function vincitore, quando viene nominata nel loop(), esegue le seguenti operazioni:

  1. riceve il pin del LED del vincitore (pinLA o pinB) e lo associa alla variabile winner;
  2. scrive sul Monitor chi ha vinto e poi incrementa il contatore dei punti del vincitore;
  3. scrive sul Monitor i punti totali di A e B;
  4. fa suonare il buzzer (con note diverse per ciascun concorrente) e fa lampeggiare il LED del vincitore in modo sincrono per 5 volte.

Poi l'esecuzione del programma ritorna al loop() e prosegue dal punto successivo a quello in cui è stata chiamata la function. Nota l'uso delle istruzioni Serial.print() e Serial.println(), per comporre sul Monitor Seriale le scritte richieste.