Dopo aver rilasciato il leOS, mi è stato fatto notare che spesso alcune librerie di terzi usano il timer 2, risultando incompatibili con leOS. Altri mi hanno chiesto uno schedulatore più semplice, sempre indipendente dai timer/interrupt del microcontrollore.
Rielaborando il core del leOS è nato looper (che si potrebbe tradurre in un orrido “ciclicatore”), che altro non è se non una routine che esegue ad intervalli prefissati altre sub-routine. looper non usa timer o interrupt ma si basa esclusivamente sulla funzione millis() di Arduino.
A differenza di leOS, va richiamato manualmente all’interno del ciclo principale del proprio sketch ma, rispetto ad esso, consuma meno risorse ed è indipendente dall’hardware, potendo essere compilato su ogni microcontrollore supportato dall’IDE di Arduino.
Per usare looper, scaricate il pacchetto che trovate in fondo all’articolo e scompattatelo all’interno della vostra cartella delle librerie (/home/utente/sketchbook/libraries su Linux, \Documenti\Arduino\libraries su Windows). Fatto questo, includete il core di looper all’inizio del vostro sketch e create una nuova istanza della libreria:
#include "looper.h" looper myScheduler;
Fatto questo, siete pronti ad usare looper. Looper mette a disposizione questi 4 metodi per gestire i compiti, che nel looper si chiamano “jobs”:
myScheduler.addJob(funzione, intervallo_in_ms[, ONE_TIME]); myScheduler.removeJob(funzione); myScheduler.pauseJob(funzione); myScheduler.restartJob(funzione);
La sintassi è identica a quella dei corrispondenti metodi di leOS con la differenza che “Task” è stato sostituito da Job per far capire che nel looper le funzioni da eseguire non sono indipendenti dal codice principale (nel leOS esse sono eseguite da uno scheduler gestito da un interrupt). Con .addJob si aggiunge allo scheduler un nuovo job indicato da “funzione”, con un intervallo di esecuzione specificato da “intervallo_in_ms”. L’aggiunta del parametro opzionale ONE_TIME indica a looper che questo task deve essere eseguito 1 volta sola e poi eliminato.
.removeJob elimina il task indicato, mentre .pauseJob e .restartJob rispettivamente mettono in pausa e riavviano un determinato job.
Per far lavorare looper basta aggiungere la chiamata al metodo .scheduler() in qualunque parte del loop principale:
void loop() { ... myScheduler.scheduler(); }
Una importante caratteristica di looper è il metodo .myDelay() che permette di fermare l’esecuzione del codice contenuto nel loop() senza fermare l’esecuzione dei job che sono presenti nello scheduler. Per usare questa funzione basta sostituire la funzione delay() di Arduino con .myDelay():
... myScheduler.myDelay(intervallo in ms); ...
Visto che looper non si basa sugli interrupt, la sua precisione dipende totalmente dalla durata del loop principale. looper funziona bene se il loop principale ha una durata inferiore a quella del task con il tempo di intervallo più breve perché altrimenti il task verrà eseguito non alla scadenza prefissata ma dopo il loop principale. looper non viene invocato automaticamente ma solo manualmente: è in pratica una routine come tutte le altre che richiama altre routine.
Corredano la libreria diversi esempi che mostrano l’utilizzo di looper.
[notice]Il codice basato sulla precedente versione di looper deve essere aggiornato per renderlo compatibile con la nuova sintassi dei metodi.[/notice]
[important]NOTA PER GLI UTENTI DI leOS:
a partire dalla versione 1.0.0, looper può essere utilizzato abbinato al leOS: uno degli esempi allegati mostra proprio questa possibilità.[/important]
That seems to be a very nice tool for me. But I have a simple problem (maybe a general C++ problem than a problem with your schedular?):
I want to add a member function of an object to the schedular:
— cut here —
#include “looper.h”
#include
#include “DMX_RGB.h”
looper myScheduler;
DMX_RGB LEDBar1(9,10,11,8);
DMX_RGB LEDBar2(12,13,14,8);
DMX_RGB LEDBar3(15,16,17,8);
DMX_RGB Spot1(1,2,3,7);
void setup()
{
myScheduler.addTask(LEDBar1.worker,MASTER_TIMER);
}
void loop()
{
myScheduler.scheduler();
}
— cut here —
But the compiler complies:
LightShow.cpp: In function ‘void setup()’:
LightShow:13: error: no matching function for call to ‘looper::addTask(, int)’
How can I add an obejct member function to the schedular?
Regards, Codeman
What does LEDBar1 is? The compiler’s saying that it’s not a function.
LEDBar1 ist an instance ob the DMX_RGB object. Here is the class definition:
class DMX_RGB {
private:
byte _red;
byte _green;
byte _blue;
byte _dim;
byte _red_memory;
byte _green_memory;
byte _blue_memory;
int _steps;
int _addr_red;
int _addr_green;
int _addr_blue;
int _addr_dim;
float _stepping_red;
float _stepping_green;
float _stepping_blue;
public:
DMX_RGB(int addr_red, int addr_green, int addr_blue, int addr_dim);
~DMX_RGB();
void set_rgbd(byte r, byte g, byte b, byte d);
void set_r(byte r);
void set_g(byte g);
void set_b(byte b);
void set_d(byte d);
void fade_to(byte r, byte g, byte b, int time, int steps);
void fade_to(byte r, byte g, byte b, int time);
void set_steps(int steps);
void flash(byte flashes, int min_time, int max_time, int fade_time);
void worker();
};
As you see there is a public method named “worker()” which I want to be called from looper. So I thaught that LEDBar1.worker() should be the function I can call with looper::addTask…
OK. Try creating a little function inside your code, put in it the call for the method and then add that function to the scheduler.
I.e.:
void setup() {
myScheduler.addTask(tempFunction,MASTER_TIMER);
….
}
void loop() {
myScheduler.scheduler();
}
vodi tempFunction() {
LEDBar1.worker();
}
With a function wrapper it seems to work! Thanks a lot!
What is the maximum delay that can be obtained ?
I would like to run a task every 5 hours, is this possible ?
5 hours = 5 * 60 * 60 *1000 = 18 000 000
but this does not seem to work
You have to edit the file named looper.cpp and go to line #36. Here is the test about the max allowed interval, that by default is fixed at 3600000UL, that is 1 hour. Change it with whatever your want (keep in mind that this values MUST be less than 2^32-1 because it’s an unsigned long). Don’t forget the “UL” after the number.
Thanks for you reply, i will try what you propose.
Ciao,
è possibile schedulare una funzione da eseguire una volta sola (ONE_TIME) in modo condizionato?
Ad esempio far partire una temporizzazione solo se var1>var2?
Se scrivo if (var1>var2) {myScheduler.addJob(funzione, intervallo_in_ms[, ONE_TIME]);}
nel main loop() lo scheduler viene riattivato ad ogni ciclo, mentre se lo includo nel setup() lo fa solo all’avvio del programma.
Ho la necessità, a partire da una condizione iniziale verificata, di temporizzare un certo tempo e durante questo tempo fare qualcosa, dopo di che ripetere questi passi ma con nuovi valori della condizione iniziale.
GRAZIE in anticipo per la risposta!!!
Usa una variabile di stato, inizialmente impostata a false oppure a 0, che setti a true oppure ad 1, non appena hai lanciato il job di tipo one-time. Ovviamente il lancio lo farai solo se la variabile di stato sarà false, e la imposterai a true non appena fatto l’abbio.
boolean eseguito = false;
.....
if ((var1>var2) && (!eseguito)) {
myScheduler.addJob(funzione, intervallo_in_ms[, ONE_TIME]);
eseguito = true;
}
In questo modo funzione verrà lanciato solo una volta e mai più nel ripetersi del loop.
Grazie, mi sei stato di grande aiuto. Così mi è stato possibile semplificare tantissimo il programma. I looper sono davvero delle funzioni utilissime!!! Complimenti!
I have tested your example “looper_onetime_task”, I changed the first task to “myScheduler.addJob(flashLed1, 2000, ONE_TIME)”.And then your example have a mistake: The last job did not stop. lightOffLed3() was called witout ending. Can you help me?
I will investigate.
Effectively there was a bug, the lead to repeatedly execute the last job if the scheduled jobs were all one-time jobs.
Now it’s fixed in the new release 1.0.1.
Ciao, complimenti per queste guide scritte in maniera semplice, io e da poco che sto provando a smanettare con arduino, ho uno sketch cun un dht22 che mi fa vedere temperatura e umidita su un diplay i2c, ho provato a dare un ritardo alla disativazine di un uscita con la funzione myDelay, ma quando sta temporizzando i dati sul diplay sembra che non si aggiornino, lo deve fare?
Poi vorrei sapere una cosa, vedendo tutti gli esempi sembra che il tempo di accensione e spegnimento di un led siano sempre uguali, si puo fare ad esempio un uscita 30 minuti spenta e 2 minuti accesa ovviamente in loop.
L’esecuzione di più compiti insieme deve essere gestita accuratamente via software perché Arduino non ha un sistema operativo che supporta il multitasking per cui devi stare attento a non bloccare in qualche loop l’esecuzione del programma: in questo caso, ovviamente, il micro esegue un compito e basta.
On/off di uscite con tempi così lunghi si possono fare ma tutto dipende da cosa deve fare contemporaneamente il programma, perché forse potresti usare la swRTC e crearti un piccolo sistema ad orari con quella libreria.