Con Arduino UNO (in futuro MEGA) stò realizzando un antifurto domestico,
unito ad una piccola automazione.
Data la necessità di avere a disposizione un discreto numero di temporizzatori
ho sviluppato allo scopo tale software, che verrà richiamato dal loop priciale
ad ogni 1/10".
if (Timer > 0) { // timer ancora attivo
Timer --; // decrementa timer
if (!Timer){ // termine conteggio
... lista comandi; // esecuzione prog. utente
}
}
Tutto funziona senza intoppi e secondo le mie aspettative, solo mi preoccupavo di ottimizzare il codice,
visto che lo stesso sarà ripetuto per tutti gli altri temporizzatori che andrò ad utilizzare.
Ti converrebbe usare un array in cui mettere tutti i tempi dei vari controlli e poi fare un ciclo unico in cui con un for fai una scansione di questo array e richiami le subroutine che devono essere eseguite. In questo modo eviti di farti X variabili per tenere il tempo di altrettanti X subroutine da lanciare e X cicli if di controllo.
un ottimizzazione è specifica del codice / sensori.
Tanto per cominciare fai in modo che funzioni; "l'ottimizzazione prematura è la fonte di molti mali" (premature optimization is the root of all evil di DonaldKnuth)
poi posta il codice funzionante che vediamo che si può fare
Per la chiamata al timer, per praticità, ho utilizzato delay, ma nella versione defintiva usero millis().
Appena si invia un byte sulla seriale, si accende per 6" il led 13.
Attendo osservazioni
le variabili (e gli oggetti) chiamale con lettera iniziale minuscola, è convenzione.
Sono i nomi di classi che iniziano in maiuscolo, e le costanti tutte scritte in maiuscolo con _ al posto degli spazi.
Se scrivi C il _ lo usi anche nei nomi_delle_variabili, se usi il C++ allora siUsaIlCamelCase
se conosci hai volglia di rimanere in C allora consiglio studio dei puntatori a funzioni, e l'uso di un arrai non di byte ma di struttura che contiene il byte di timeout, e il puntatore a funzione da eseguire (e magari anche un unsigned long con il tempo dell'ultima esecuzione)
se invece vuoi il C++, fai un array di classe astratta Timer (o chiamala cone voi) che contiene le cose di cui sopra, però al posto di puntatore a funzione metti un metodo astratto run() e un metodo check() e un costruttore (o un metodo) a cui passi il tempo di attivazione;
ogni loop() fai un for sull'array e chiami ilmetodo check(), che controlla (ed eventualmente chiama) il metordo run().
a questo punto estendi la classe Timer con varie sotto-classi, che implementano il metodo run() con le implementazioni specifiche.
Confesso che mi sono avvicinato ad Arduino senza alcuna conoscenza del C e C++.
L'unica scuola che ho frequentato sono state le pagine di questo forum, unite a qualche manuale scaricato dalla rete e/o acquistato, pertanto faccio un'estrema fatica seguire il tuo ultimo post.
Pur cosapevole che il mio modo di programmazione o sintassi non risponda alle sane regole citate,
per il tempo a mia disposizione, sono costretto dedicarlo sopratutto per portare a termine questo progetto.
L'unica cosa che mi preme è realizzare un codice efficente in fase di esecuzione, anche se a scapito di una programmazione
poco ortodossa.
Per come mi sono organizzato il sistema, saranno le varie routine ad attivare di volta in volta i vari timer.
Per fare un banale esempio: "la luce delle scale"
Da qualche parte del software un ingresso o una sequenza di manovre attiverà un uscita e caricherà un dato sul timer.
Quando il timer arriverà a '0' disattiverà uscita. Come dire, non dovrò più preoccuparmi a spegnere la luce.
L'esempio analogo l'ho volutamente inserito nel listato, solo che sarà un carattere generico ricevuto sulla RS232
ad attivare il led 13.
E' una procedura che ho messo a punto strada facendo, ma sta dando dei buoni frutti.
Più o meno rispondete tutti così, perché non vi rendete conto che il programma può diventare molto impegnativo da sviluppare man mano che cresce di dimensioni. Quando ciò accade si dice che il codice non è mantenibile.
Il codice diventa non mantenibile quando:
L'introduzione di una nuova funzionalità comporta malfunzionamento generale.
Difficoltà di debug, fino alla impossibilità completa di debug.
Difficoltà nel rimuovere o modificare una o più funzionalità presente (il codice smette di funzionare)
Difficoltà nel comprendere cosa e come lavora il codice dopo pochi mesi passati senza lavorarci.
Per questi motivo lesto ti ha suggerito le modifiche e non per una questione di stile.
Le convenzioni non fanno parte del linguaggio ma sono parte fondamentale del processo di programmazione.
Le poche convenzioni da rispettare per Arduino style sono minime e facili da apprendere. Si possono acquisire
le convenzioni anche copiando dal codice degli esempi forniti con l'ide.
Il codice che hai scritto non stabilisce alcuna convenzione, tuttavia visto che ridotto di dimensioni io
come altri programmatori riusciamo a non confonderci, man man che il programma cresce di dimensioni
l'unico in grado di comprendere cosa fa il codice sarai tu ma solo durante lo sviluppo, la manutenzione dopo
mesi ti creerà parecchie difficoltà e potresti anche arrivare a chiederti; ma l'ho scritto io?
Alcuni programmi in assembler usano contare il tempo come hai fatto tu, ma senza l'inconveniente che si presenta nel tuo codice, che è facilmente aggirabile facendo uso di millis(). Il problema nel tuo codice è che
il loop principale gira ad una velocità di 100ms a ciclo, quindi 10 cicli ogni secondo. Ti eri posto il problema della efficienza e non capisco perché usi il delay(100) che è tutto il contrario di efficiente.
// all'inizio dello sketch
#define TIME_TRIGGER_100MS 100
unsigned long timeTrigger100ms = 0;
void loop() {
// delay (100);
if (!timeTrigger100ms)
timeTrigger100ms = millis();
if ( (millis() - timeTrigger100ms) > TIME_TRIGGER_100MS) {
Tmr(); // run each 100ms
}
// run at full speed
if (Serial.available()) {
RxCar = Serial.read();
Timer[2]=60;
digitalWrite(pinLed,HIGH);
}
}
In prima battatuta ho inteso e seguito il suggerimento di Leo, perchè convinto che tale struttura mi avrebbe offerto dei vantaggi in futuro e sopratutto alla mia portata.
Senza mettere in dubbio la validità di quanto suggerito da lesto e dal tuo post, mancando una mia specifica preparazione di base, ho confessato la mia impreparazione a seguirne le indicazioni. Leggendo in questo forum vari post sull'argomento, ogni volta che ho tentato di approfondire l'argomento, mi sono ritrovato più confuso di prima.
Per la chiamata al timer, per praticità, ho utilizzato delay, ma nella versione defintiva usero millis().
Per la storia dell'uso di delay() mi sembrava già aver precisato che la cosa.
Visto che stiamo parlando di ottimizzare il codice, vi sarei grato avere un ulteriore aiuto.
Su un post del forum ho scoperto questa linea di codice: Serial.print ( F("Messaggio") );
Da quanto ho capito, la stringa del "messaggio" non viene allocata nell'area delle variabili, ma in quella del codice.
Forse l'ultima volta che ho consultato il reference del serial dormivo, perchè la funzione "F()" mi è risultata completamente nuova.
Esiste una pagina, una sorta di news dove vengono elencati l'inserimento di nuove funzioni o comandi nell'IDE?
lelebum:
Forse l'ultima volta che ho consultato il reference del serial dormivo, perchè la funzione "F()" mi è risultata completamente nuova.
Esiste una pagina, una sorta di news dove vengono elencati l'inserimento di nuove funzioni o comandi nell'IDE?
Non è nel reference, è descrita nella pagina del Playground che parla della memoria, a fondo pagina ... QUI
Guglielmo
P.S. : Anche perché NON è una funzione ma una macro. E' definita in WString.h come :
class __FlashStringHelper;
#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))
Dai manuali che ho acquistato e scaricato dal web, è la prima volta che la vedo.
Se non fosse per la mia abitudine di dare un'occhiata ai vari post, non ne avrei mai conosciuto l'esistenza.