Strega comanda colore è un gioco classico dell'infanzia
esiste anche in una versione alternativa (meno diffusa)
Strega comanda dimensione #numero
e i bambini devono trovare oggetti di quella dimensione
ecco quindi che per far "passare" numeri dalla seriale ho inventato
"Seriale comanda colore"
premessa: seriale non vuol dire nulla, potrebbe essere seriale, II5, file, Seriale Software
non importa, basta che sia uno stream
io scriverò sempre seriale, ma intendo lo stream che si sta usando
premessa II la vendetta
non intendo ne risolvere problemi specifici ne compilare i miei programmi qui presentati
do solo spunti, poi che ha orecchie per intendere intenderà...
Cominciamo con una grande condizione propedeutica:
Sappiamo quali sono le caratteristiche del "flusso" di dati
ovvero come viene scritto
cosa viene scritto
ogni quanto viene scritto
e via così
la mia non è una domanda: è una condizione
se non sapete cosa e quanto e come e...
è inutile che si prosegua la lettura, andate ad informarvi
Ricordate sempre la:
Seconda legge di Nelson (che sono io): Remember, researching how to solve a problem is often necessary before a programmer can teach the computer how to solve it
ovvero:
Ricorda, la ricerca su come risolvere un problema è spesso necessaria prima che un programmatore possa insegnare al computer come risolverlo (Bjarne Stroustrup)
facciamo quindi adesso degli esempi di "ricezione" da seriale
esempio: numeri codificati in ASCII, interi, con separatore/identificatore di fine/terminatore
rappresentanti una sola variabile, sempre la stessa ripetuta ogni... oppure semplicemente aggiornata
prima domanda:
conosciamo il terminatore?
NO? come nel gioco dell'oca: tornate alla casella "andate ad informarvi"
SI? bene
è un carattere o una stringa?
Come dite? cosa è una stringa? tornate alla casella "andate ad informarvi"
poniamo caso che sia un carattere, potrebbe essere un semplice spazio (' ' oppure 32 oppure 0x20) o magari un fine riga, carattere speciale che si indica '\r' o ....)
bene, si prosegue
seconda considerazione:
se la trasmissione avviene rapidamente ci troviamo a dover facilmente gestire stringhe "lunghe", che potrebbe rallentare il programma
oppure anche se il programma fosse bloccante ci troveremmo facilmente nella condizione di perdere parte della trasmissione, a causa del riempimento del buffer di seriale durante la parte bloccante del programma
sono sicuro che non vi stupirete se alla domanda "e come si evitano programmi bloccanti?" la risposta sarà: come nel gioco dell'oca - tornate alla casella "andate ad informarvi"
da adesso domande e risposte del genere saranno "evitate" in questa discussione
siccome noi non vogliamo trovarci a dover "gestire" perdite di dati dobbiamo evitare di usare metodi bloccanti, anche per la lettura da seriale
chi avesse un minimo (ma proprio minimo) di dimestichezza con il linguaggio di Arduino potrebbe pensare che una cosa del tipo
String parola;
parola=Serial.readString;
int valore;
valore=atoi(parola);
Non provateci nemmeno
questo programma è il "male"
usa l'allocazione dinamica, è bloccante e non fa un vero controllo di start e stop
rischiate di trovarvi col programma principale bloccato ad aspettare inutilmente un valore che non viene trasmesso in questo momento
e questo chiude ogni considerazione del genere
pertanto gli "arrivi" dalla seriale vanno gestiti "carattere per carattere" e usando funzioni "semplici" del linguaggio
altra questione è certamente "dove" salvare il valore "in arrivo"
dato che se "aggiorniamo" il valore mano a mano che arrivano successivi caratteri appare evidente che "durante" una ricezione il valore non è corretto, invece "appena" dopo una ricezione corretto lo è, ma lo rimane per poco tempo
quindi serve di avere una "variabile di appoggio" che deve essere usata come "blocco d'appunti" e solo al termine della ricezione copiata nella variabile "giusta"
fatto questo "cominciamo il circo"
// per prima cosa la variabile da tenere aggiornata
int variabile; // che fantasia, vero?
// comunque deve essere globale, per poter essere aggiornata da una funzione separata
// il terminatore
#define TERMINATORE ' ' // al posto dello spazio scrivete il giusto terminatore
// e che non venga in mente di usare const int const char o similia, siamo su un Arduio, la memoria è poca e si usano macro!
invece dentro nella loop() andiamo a vedere se la seriale ha ricevuto un carattere e ed eventualmente lo trattiamo
.
. qui tutta la loop..
.
if (Serial.available())
{
// leggo il carattere
char c=Serial.read();
tratta(c);
//ovviamente che fa il lavoro sporco è la tratta()
}
.
. anche qui la loop prosegue...
.
come vedete se la loop è scritta bene (ovvero non bloccante) basta aggiungere una manciatina di righe per avere l'aggiornamento della variabile da seriale
il lavoro duro lo fa la tratta
void tratta(char c)
{
// una variabile locale per conservare il valore in ricezione
// naturalmente static per non perderne il valore
static int attuale=0; // sempre inizializzare variabili locali
// qui bisogna ragionare un poco
// i caratteri si dividono in tre categorie:
// terminatore, che chiude la ricezione e "manda avanti" il risultato
if (c==TERMINATORE)
{
// scrivo sulla variabile globale il valore trovato finora
variabile=attuale;
// azzero la locale per ricominciare i conti
attuale=0;
// basta, lavoro finito, torno alla loop
return;
}
// oppure cifre, che devono venire conteggiate
if (isdigit(c))
{
locale = locale * 10 + c -'0';
// per sapere come fnziona leggere il K&R
// cifra elaborata, lavoro finito
return;
}
// oppure caratteri di riempimento, inutili
// che appunto non tratto
// e quindi lavoro finito
return;
}
di questo modo, dopo la prima trasmissione, in "variabile" si trova sempre l'ultimo valore trasmesso
invece la funzione tratta() sta aggiornando il "prossimo" valore, attualmente in trasmissione
il tutto è velocissimo e non rischia di rallentare mai la loop() e nemmeno di intasare i buffer di seriale