looper - un semplice schedulatore senza timer/interrupt

nel secondo credo che

#define ENDDELAY(v) }

debba essere

#define ENDDELAY }

:grin:

edit: buon debug!

@nid69: siccome io spiego sempre per bene tutto il codice, ora tu ti prendi caffè e brioscia, poi con calma ti metti lì e spieghi ad un programmatore NON professionista, cioè io.., cos'è che fa il tuo codice. ;)

Effettivamente non è chiaro neanche a me. Non è la stessa cosa di:

while(currentMillis - previousMillis1 > interval1){
    // qui metti codice per eseguire mentre conta il primo delay
}
while(currentMillis - previousMillis2 > interval2){
    // qui metti codice per eseguire mentre conta il secondo delay
}

Esatto però le chiama curr# e prev# dove # è il secondo numero dato alla macro. Però non funsiona perché prev# viene distrutta a fine loop e riceeata ogni loop. Forse rendendola statica o global, non ricordo come ma mi pare sia possibile.

Quindi una macro che crea variabili globali di nascosto...

Come dicevo prima buon debug

Ps ma il secondo definea chè serve?! Credo si possa togliere

Il precompilatore sostituisce brutalmente quello che si definisce con la #define. E' possibile però passare anche dei "parametri" Molte librerie nascondo alcune implementazioni con le define. Se vai a vedere dentro la Arduino.h scoprirai ad esempio che esiste la #define noInterrupts() cli(), ovvero quando scrivi nello sketch noInterrupts() questa viene sostituita dalla funzione cli(). In teoria le 2 macro che ho scritto volevano fare una cosa molto brutta, l'ho detto. Nascondere questo pezzo di codice in due macro parametriche:

unsigned long prev1=0; 
unsigned long curr1=millis(); 
if(curr1-prev1

E' una if e non una while, le variabili sono esterne al blocco, perciò sono locali. Effettivamente NON funziona perchè prev non mantiene il valore. ooops!!! Il parametro t sarebbe il tempo della delay, v un numero per creare variabili con nomi sempre diverse. Volevo usare COUNTER del prepocessore di gcc ma essendo righe diverse non son riuscito a farmi dare dal preprocessore variabili con nomi sempre diversi. Ripeto, è una cavolata, io non lo adotterei mai, tanto vale usare il codice completo scritto sopra. Però la potenza della #define, veramente mitica.

Non mi riferivo alla #define, so a che serve ;). Intendevo come il tuo codice risolveva (se lo faceva) il problema di testato, ossia di istruire il compilatore ad eseguire esclusioni intelligenti e temporizzate del codice ;)

Cosi dovrebbe funzionare, mi sembra giusto cercare di dare una versione funzionante. Ma NON la usate, è una cavolata.

#define MACRO_CONCAT( x, y ) x##y
#define INITDELAY(v) unsigned long MACRO_CONCAT(g_prev,v)=0; unsigned long MACRO_CONCAT(g_curr,v);
#define BEGINDELAY(t,v) MACRO_CONCAT(g_curr,v)=millis(); if(MACRO_CONCAT(g_curr,v)-MACRO_CONCAT(g_prev,v)<(t)) { 
#define ENDDELAY(v) MACRO_CONCAT(g_prev,v)=MACRO_CONCAT(g_curr,v); }

// sketch
INITDELAY(1)
INITDELAY(2)

void setup()
{ }
void loop()
{ BEGINDELAY(500,1)
    // qui metti codice per eseguire mentre conta il primo delay
  ENDDELAY(1)
  BEGINDELAY(600,2)
    // qui metti codice per eseguire mentre conta il secondo delay
  ENDDELAY(2)
}

@Leo mi hai detto di spiegare (giustamente) per chi non conosce il precompilatore. Si, cercavo di "risolvere" il problema di @Testa. Mi chiedevo ieri sera se era possibile nascondere quel che si fa con la millis() (come detto anche da lui stess) e ci ho provato.

No, non fraintendermi :sweat_smile: Credevo che avessi trovato davvero una soluzione funzionante e siccome io non sono un programmatore professionista immaginavo che avessi sfruttato qualche procedura o tecnica particolare delle #define ;)

ancora meglio:

usando static si può creare una variabile globale dall'interno di un blocco. In oltre l'inizializzazione fatta nella riga di inizializzazionbe vien fatta una sola volta; quidi se nel primo esempio rendi prev static risolvi il problema.

La tua ultima versione è ok, però se metti un { a inizio BEGINDELAY e un } alla ENDDELAY, gcurr diventa una variabile dichiarata all'interno del blocco e quindi non è più soggetta a problemi di visibilità in tutto il loop. quindi puoi dargli un nome fisso.

io farei così (proprio a volersi fare male), usando il blocco doppio, che rende il codice un pelo più leggibile, e la static, evitando così la #define INITDELAY:

#define MACRO_CONCAT( x, y ) x##y
#define BEGINDELAY(t,v) { static unsigned long MACRO_CONCAT(prev,v)=0; unsigned long timeHidden123=millis(); if(timeHidden123-MACRO_CONCAT(prev,v)<(t)) { 
#define ENDDELAY(v) MACRO_CONCAT(prev,v)=timeHidden123;}}

void setup()
{ }

void loop()
{ BEGINDELAY(500,1)
    // qui metti codice per eseguire mentre conta il primo delay
  ENDDELAY(1)
  BEGINDELAY(600,2)
    // qui metti codice per eseguire mentre conta il secondo delay
  ENDDELAY(2)
}

Bene @Lesto, ora c’e’ una versione funzionante. @Leo ora abbiamo risposto a @Testa. Si può fare, anche se poco elegante e secondo me poco utile.
:grin:

secondo il mio punto di vista è uno spreco di risorse ed un complicarsi la vita per niente, ;) dato che tutto ritorna ad essere un semplice if..then.

leo72: secondo il mio punto di vista è uno spreco di risorse ed un complicarsi la vita per niente, ;) dato che tutto ritorna ad essere un semplice if..then.

:grin: :grin: :grin:

io invece credo che il principiante lo preferisca, tanto di cosa significano le define nemmeno gli frega nulla, ed il gestire con un begindelay/endelay un delay non bloccante sia molto intuitivo, non e' l'utopistico testatoDelay() ma e' sempre meglio del gestire il millis.

ma il tutto puo' ulteriormente essere nascosto in una libreria ? in modo che invece di avere le define si dice: we bello usa la libreria newdelay

a questo punto questa libreria, che diventerebbe importantissima a livello planetario porterebbe il mio nome in quanto ideatore, quello di nid69ita in qualita' di operaio, e quello di lesto in qualita' di consulente. Leo invece nulla in quanto ostacolatore del progetto :stuck_out_tongue_closed_eyes:

Basta che metti le #define in un file .h
Che sò, si potrebbe chiamare testonidlesto.h :grin:

#define MACRO_CONCAT( x, y ) x##y
#define BEGINDELAY(t,v) { static unsigned long MACRO_CONCAT(prev,v)=0; unsigned long timeHidden123=millis(); if(timeHidden123-MACRO_CONCAT(prev,v)<(t)) { 
#define ENDDELAY(v) MACRO_CONCAT(prev,v)=timeHidden123;}}

Testato, io sarò anche assonnato, ora, ma non vedo differenza fra questa newDelay ed il looper in sé, perché in pratica tu vuoi uno scheduler software.
Io ho scritto una delay intelligente, che permette di eseguire altri compiti mentre sta facendo ciò che il suo nome dice, ossia fermare il codice.
Tu stai portando avanti l’idea di uno scheduler, perché questo è il tuo newdelay. Tu vuoi un qualcosa che passi da un punto del programma, faccia un if per verificare che non sia giunto il tempo di eseguire un certo compito, e poi scorra avanti. Mi spieghi però come si deve comportare la tua newdelay nel caso ci sia qualcos’altro subito dopo?
Esempio:

void loop() {
  BEGINDELAY(500,1)
    // qui metti codice per eseguire mentre conta il primo delay
  ENDDELAY(1)
  BEGINDELAY(600,2)
    // qui metti codice per eseguire mentre conta il secondo delay
  ENDDELAY(2)
  ....altro codice....
}

Quel “qualcosa” verrà eseguito sempre perché la tua newdelay non è una delay. Come sono confuso io, lo sarà anche l’utente che la userà pensando ad un qualcosa che ferma il codice mentre invece non lo fa.
La tua idea di newdelay è in realtà ciò che è uno scheduler, eseguire cioè dei compiti ad intervalli prefissati. O mi sbaglio?

Non ti sbagli per nulla. Effettivamente il nome è ambiguo. Se al posto del if, mettiamo while, forse la risolviamo, ma mica c'era intenzione di fare una libreria :grin: Per me è stata solo una pensata fatta più per gioco/sfida. Se poi @Testa la vuol usare... però bisogna adeguarla.

#define MACRO_CONCAT( x, y ) x##y
#define IFDELAY(t,v) { static unsigned long MACRO_CONCAT(prev,v)=0; unsigned long timeHidden123=millis(); if(timeHidden123-MACRO_CONCAT(prev,v)>(t)) { MACRO_CONCAT(prev,v)=timeHidden123;
#define WHILEDELAY(t,v) { unsigned long MACRO_CONCAT(prev,v)=millis(); while(millis()-MACRO_CONCAT(prev,v)<(t)) { 
#define ENDDELAY }}

void setup()
{ delay(2000);
  Serial.begin(9600);
}

void loop()
{ Serial.println("prima di if");
  IFDELAY(1000,1)
     Serial.println("--------- dentro la if");
  ENDDELAY
  Serial.println("dopo la if");

/*  commentato per provare il pezzo sopra senza questo sotto
  Serial.println("inizio while");
  WHILEDELAY(500,2)
     Serial.println("==== dentro la while");
  ENDDELAY
  Serial.println("fine while");
*/
 }

*** Edit: ho aggiustato il codice e provato con Arduino. Ora mi sembra okay, anche se è solo una cosa accademica, non pretende di essere una libreria o chissà che. Ci sono 2 tipi di delay, anche se NON è corretto chiamarli delay. E' stato solo un modo per cercare di nascondere l'uso della millis() che non per tutti è semplice da capire.

con la while così hai creato un loop infinito, e anche se la correggi hai un sistema per esegiore la stessa cosa per un tot di tempo e poi eseguire altro..

che è un'altra cosa ancora.

Secondo me è più intuitiva la storia dei Task, se visto come un calendario; all'inizio fissi degli appuntamenti, che possono essere eseguiti con un certo ritardo e poi eventualmente ripetuti con un altro ritardo.

La libreria di leo è il calendario, le funzioni cosa fare all'appuntamento, e schedule() il modo per dire all'arduino di controllare se c'è qualche appuntamento in questo momento (o qualche appuntamento scaduto, che arduino porterà comunque a termine.. non è che se oggi non porti fuori la spazzatura non lo farai mai più, quando te ne ricordi provvedi il prima possibile)

Io direi di ripulire il topic e separare le discussioni.

Se Testato vuol portare avanti la questione della newDelay come la intende lui lo faccio senza problemi. Per me può altrimenti restare tutto qui, si tratta sempre di una discussione inerente il modo di eseguire determinati compiti con uno scadenzario.

Bè però non centra nulla col titolo della discussione. Ma visto che l'hai aperto tu decidi tu. :D