Premessa
Dopo aver visto come simulare il multitasking con Arduino grazie alla funzione millis() nella prima e seconda parte dell'articolo, andiamo a vedere la programmazione a stati finiti.Il problema fondamentale che iniziava a presentarsi nella seconda parte è che il programma tende a diventare piuttosto mastodontico mano a mano che si aggiungono nuovi elementi che ineragiscono tra loro.
Dopo poco tempo l'utente inizia a sentire l'esigenza di dividere il codice in funzioni: si tende, quindi, a raccogliere le istruzioni in "gruppi" organizzati per rendere lo sketch più facile da leggere e gestire --i romani dicevano "Divide et impera"--.
Non è scopo di questo articolo vedere cosa sia una funzione e come si passino argomenti ad essa; l'autore presuppone che il lettore sappia creare una funzione sull'IDE di Arduino.
Fatta questa doverosa premessa andiamo a vedere come iniziare la programmazione a stati finiti su Arduino ed alleggerire la lettura del codice dei nostri sketch.
La programmazione a stati finiti
La programmazione a stati finiti consiste nel dividere il codice in blocchi diversi.Tali blocchi sono racchiusi in una funzione che rappresenta un singolo evento e raccoglie porzioni di codice appartenenti al medesimo "livello logico" e nel quale si aggiorna, alla fine, la variabile-tempo (rif. prima parte e seconda parte dell'articolo) associata riassegnandogli il millis().
Si riprenda l'esempio visto in precedenza (rif. fondo pagina della seconda parte) e vediamo nella pratica in cosa consista.
Lo riporto qui di seguito per comodità del lettore.
#include <Servo.h>Il codice già ampiamente spiegato permette di far muovere due servo motori al variare dell'intensità di luce che insiste su due sensori di luce perpendicolari fra loro.
#define SENSORELUCE1 0
#define SENSORELUCE2 5
int letturaluce1=0;
int letturaluce2=0;
int luce1;
int luce2;
int luce1temp;
int luce2temp;
Servo servo1;
Servo servo2;
int posservo1;
int posservo2;
int k;
unsigned long time;
unsigned long servotime1;
unsigned long servotime2;
unsigned long letturadati;
unsigned long letturaluce_time;
void setup(){
pinMode (SENSORELUCE1, INPUT);
pinMode (SENSORELUCE2, INPUT);
servo1.attach(9);
servo2.attach(10);
Serial.begin(9600);
time=millis();
servotime1=millis();
servotime2=millis();
letturadati=millis();
letturaluce_time=millis();
k=0;
luce1=0;
luce2=0;
luce1temp=0;
luce2temp=0;
}
void loop(){
time=millis();
if(time>letturaluce_time+5){
luce1=analogRead(SENSORELUCE1);
luce2=analogRead(SENSORELUCE2);
k=k+1;
luce1temp=luce1temp+luce1;
luce2temp=luce2temp+luce2;
}
if(k=20){
k=0;
letturaluce1=luce1temp/20;
letturaluce2=luce2temp/20;
luce1temp=0;
luce2temp=0;
}
if(time>servotime1+15){
posservo1= analogRead(letturaluce1);
posservo1=map(posservo1,0,1023,0,179);
servo1.write(posservo1);
servotime1=millis();
}
if(time>servotime2+15){
posservo2= analogRead(letturaluce2);
posservo2=map(posservo2,0,1023,0,179);
servo2.write(posservo2);
servotime2=millis();
}
if(time>letturadati+3000){
Serial.print("Sensore 1 : ");
Serial.print(letturaluce1,DEC);
Serial.print(" Posizione motore 1 : ");
Serial.println(posservo1,DEC);
Serial.print("Sensore 2 : ");
Serial.print(letturaluce2,DEC);
Serial.print(" Posizione motore 2 : ");
Serial.println(posservo2,DEC);
Serial.print(" Differenza : ");
Serial.println(letturaluce1-letturaluce2);
letturadati=millis();
}
}
Questo codice si "divide" in porzioni ben definite:
- La sezione in cui si leggono i sensori
- La sezione in cui si fanno muovere i motori
- La sezione nella quale si invia alla seriale un output.
Ogni funzione infatti si aggiunge ad un tab differente: per fare questo si clicchi la freccina verso destra nell'angolo in alto a destra dell'IDE e poi su "New Tab".
Si dia un nome alla tab che DEVE essere uguale al nome della funzione che si scriverà all'interno.
Come si vede in figura quindi, si aggiungeranno tre Tab; una per ciascuna funzione:
- leggiLuce
- muoviServoMotori
- scriviSuOutput
Leggi Luce
Questa funzione racchiude le istruzioni che arduino deve svolgere per leggere i sensori di luce.In particolare si notino le 20 letture necessarie per "ammortizzare" gli sbalzi del sensore analogico e il reset della variabile tempo alla fine della funzione.
void leggiLuce(){
if(time>letturaluce_time+5){
luce1=analogRead(SENSORELUCE1);
luce2=analogRead(SENSORELUCE2);
k=k+1;
luce1temp=luce1temp+luce1;
luce2temp=luce2temp+luce2;
}
if(k=20){
k=0;
letturaluce1=luce1temp/20;
letturaluce2=luce2temp/20;
luce1temp=0;
luce2temp=0;
}
letturaluce_time=millis();
}
Muovi servo motori
Questa funzione, come si intuisce, raccoglie le istruzioni sul movimento dei Servi.Anche in questo caso si noti l'azzeramento della variabile-tempo al fondo della funzione.
/* Muove i SERVI*/
void muoviServoMotori(){
if(time>servotime1+15){
posservo1= analogRead(letturaluce1);
posservo1=map(posservo1,0,1023,0,179);
servo1.write(posservo1);
servotime1=millis();
}
if(time>servotime2+15){
posservo2= analogRead(letturaluce2);
posservo2=map(posservo2,0,1023,0,179);
servo2.write(posservo2);
servotime2=millis();
}
}
Scrivi su Outout
Infine lo scrivi output si comporta "a livello logico" come le funzioni viste in precedenza. Esso scrive sulla seriale le informazioni da visualizzare all'utente ogni 3 secondi.void scriviSuOutput(){
if(time>letturadati+3000){
Serial.print("Sensore 1: ");
Serial.println(letturaluce1,DEC);
Serial.print("Posizione motore 1: ");
Serial.println(posservo1,DEC);
Serial.print("Sensore 2: ");
Serial.println(letturaluce2,DEC);
Serial.print("Posizione motore 2: ");
Serial.println(posservo2,DEC);
Serial.print("Differenza : ");
Serial.println(letturaluce1-letturaluce2);
letturadati=millis();
}
}
Sketch principale
A questo punto lo sketck principale si riduce sensibilmente.Ogni tab racchiude le porzioni di codice e ci possiamo soffermare sulla dinamica del programma.
#include <Servo.h>Come si può vedere la funzione loop() viene ridotta veramente all'osso.
#define SENSORELUCE1 0
#define SENSORELUCE2 5
int letturaluce1=0;
int letturaluce2=0;
int luce1;
int luce2;
int luce1temp;
int luce2temp;
Servo servo1;
Servo servo2;
int posservo1;
int posservo2;
int k;
unsigned long time;
unsigned long servotime1;
unsigned long servotime2;
unsigned long letturadati;
unsigned long letturaluce_time;
void setup(){
pinMode (SENSORELUCE1, INPUT);
pinMode (SENSORELUCE2, INPUT);
servo1.attach(9);
servo2.attach(10);
Serial.begin(9600);
time=millis();
servotime1=millis();
servotime2=millis();
letturadati=millis();
letturaluce_time=millis();
k=0;
luce1=0;
luce2=0;
luce1temp=0;
luce2temp=0;
}
void loop(){
time=millis();
leggiLuce();
muoviServoMotori();
scriviSuOutput();
}
Questo sketck si riduce ad un banale programmino.
Nel loop non è stata aggiunta alcuna "logica" ma è facile immaginare degli if che determinano l'attivazione di una o più funzioni a seconda di determinati eventi...
Conclusione
Frequentando il forum di Arduino, noto che le persone spesso entrano in crisi nel momento in cui gli sketch aumentano di dimensioni.Spesso si tende a desistere nei propri progetti per questo motivo.
Trovo che l'applicazione dei consigli dati in questi tre articoli (multitasking + programmazione a stati finiti) risolva la maggior parte delle problematiche a livello di programmazione dovuta all'aggiunta di più componenti all'arduino.
Personalmente incontro maggiori difficoltà nelle saldature ma questo è un problema della mia mancanza di manualità :-)
Argomento precedente: "Multitasking Arduino: millis() -- PARTE 2"
grande vittorio. Se continui così mi commuovo ;)-
RispondiEliminaveramente interessante.
Ottima guida
RispondiEliminaDopo quella sulla programmazione dell'ATMEGA, questa mi servirà davvero tanto per il mio progettone di elicottero XD
RispondiEliminaThanks
Uhmmm.
RispondiEliminaProgetto sull'elicottero?
Dicci di più mi ha sempre appassionato questo genere di progetti!!
Ciaoo Vittorio
RispondiEliminaSono veramente soddisfatto dei tuoi articoli!
MI hai fatto capire tante belle cose..grazie
Grazie Massimiliano,
RispondiEliminacommenti come questi sono sempre graditi e stimolo a proseguire!
Mi piace tanto quell'if(k=20) ;)
RispondiEliminaCiao,
RispondiEliminagrazie mille per i 3 articoli, li ho trovati davvero utili.
Ero difatti indeciso se fiondarmi in questa nuova esperienza, arduino, o no, ma dopo questi chiarimenti direi di poter provare!
La mia paura era propio che essendo procedurale, non c'era alcun metodo per raggirare il problema del delay!
Ciao Davide,
RispondiEliminami fa estremamente piacere leggere questo commento.
A presto,
Vittorio
ahahah!
RispondiEliminae io che stavo diventando matto per 2 operazioni "parallele"...
Grazie mille!
ciao vittorio,
RispondiEliminasto cercando di risolvere un piccolo problema.
Dico subito che sono neofita riguardo arduino&co. :)
ho realizzato attraverso i tutorial sul sito di arduino un semplice circuito con una fotoresistenza e l'ho fatto girare su arduino con lo sketch "standard firmata" e tramite maxuino riesco a ricevere il segnale.
ho provato allora ad aggiungere un'altra fotoresistenza, ma non arriva nessun segnale :(
come devo operare sullo sketch per ricevere entrambi i valori?
ti ringrazio in anticipo :)
Grande queste tre 3 lezioni sono state veramente utili.
RispondiEliminaGrazie calucio24.
RispondiEliminaSei davvero gentile :-P
Ciao a tutti .... una domanda forse stupida.... cosa succede " la mattina dopo"?
RispondiEliminaovvero l'inseguitore si feram gaurdandi ad ovest... riesce da solo all'alba a perceipre che viene luce da una direzione di 180°?
Grazie
ciao Vittorio
RispondiEliminaGRAZIE !
pensa che questo problema dei listati lunghissimi e che a volte diventano incomprensibili se non mettendo continue note per capire a cosa fanno riferimento alcune funzioni, me lo sono posto parecchie volte, e avevo parzialmente risolto cercando di ottimizzare il codice in blocchi funzionali.
del sistema della programmazione a stati finiti aprendo altre " Schede " " Tab " tramite la freccetta a destra, (neanche me ne ero accorto a che servisse :-) ), non ne evevo mai sentito parlare addirittura !.
ma perche non mettere quel menu nel menu in alto ??? almeno uno lo vede e si chiede a cosa serve......
comunque sei un GRANDE !
arrigrazie !!
ciao
Salvatore B
Grazie a te Salvatore.
EliminaSe questi brevi articoli possono servire ne sono solo contento :-)
bell'articolo.
RispondiEliminami togli una curiosita, come riesci ad inserire in blogspot.it il codice Fortattato e colorato correttamente ?
grazie
ciao
Ciao Roberto, ci sono diverse utility che fanno questo lavoro.
EliminaUna volta ne avevo trovato uno online e se fai una ricerca ne troverai senza dubbio diversi. Per quel codice in particolare usai una utility per Linux che si chiamava htmltidy: un po' ostica ma funzionale no? :-)
Prima di tutto mi unisco ai tanti complimenti inviati: meritatissimi!
RispondiEliminadevo inserire i rintocchi per una campana (ora e quarti d'ora) all'interno di un ciclo di un orologio con LCD con un delay di circa 1 sec. il tempo max dei rintocchi potrebbe essere di 24+3 sec. durante questo periodo di tempo vorrei mantenere l'LCD funzionante ed attive le altre funzioni. puoi darmi qualche consiglio? intanto mi leggo i tuoi articoli. grazie molte Feliciano
Ciao Feliciano, l'unico consiglio che posso darti che di mettere un tempo di delay tra un rintocco e l'altro di un secondo circa. In questo modo l'LCD dovrebbe rimanere tranquillamente acceso perchè la funzione millis deciderà se far partire l'impulso di suonare la campana o mantenere lo schermo acceso
RispondiEliminaComplimenti Vittorio, e mille grazie per i 3 articoli.
RispondiEliminaNozioni essenziali per redigere al meglio sketch particolarmente lunghi o complessi.
Grazie!
Paolo
Complimenti Vittorio, e mille grazie per i 3 articoli.
RispondiEliminaNozioni essenziali per redigere al meglio sketch particolarmente lunghi o complessi.
Grazie!
Paolo
Ti ringrazio vivamente di questi articoloni con spiegazioni.. :) buon tutto
RispondiEliminaCiao Vittorio! Sei un grande. Hai reso semplice e chiaro un argomento che è caustico! Ti ringrazio davvero tanto! Sto facendo un progetto per l'azienda in cui lavoro e sto utilizzando arduino! E' un sistema che gestirà le sirene dello stabilimento mediante doppino e DTMF. Avrò un master con arduino mega e circa 30 slave con arduino uno. Ti ringrazio per i consigli. Il tuo lavoro è veramente prezioso!
RispondiEliminaCiao by Davide
sono Daniele Negrato e sto sperimentando Arduino uno, ma siccome sono abituato a lavorare con i PLC, volevo sapere se esisteva un programma che utilizzi il linguaggio dei PLC per Arduino.
RispondiEliminaGrazie dell'attenzione.
buona serata