After the release of leOS, someone brought my attention on the fact that often third-party libraries use timer 2 so they are not compatible with my leOS. Someone else asked me for a simplier scheduler, that could be beyond microcontroller’s timers & interrupts.
So, working on the code of leOS I’ve obtained looper, that is just a routine that executed at fixed intervals other sub-routines. looper doesn’t use timers nor interrupts: it only uses the millis() function of the Arduino core.
Unlike leOS, the user has to manually call looper inside the main loop of his sketch but, unlike leOS, it consumes much less resources and it doesn’t rely on the hardware on which it’s running, so it can be compiled on any kind of microcontroller that can be supported by the Arduino IDE.
To use looper, just download the attached packaged and extract it into your /libraries folder (usually /home/user/sketchbook/libraries on Linux boxes, \Documents\Arduino\libraries on Windows boxes). After that, include the core library into your sketch’s code and create a new instance of looper:
#include "looper.h" looper myScheduler;
Now you’re ready to use looper. It has these methods with which you can manage your routines, that now have the name of “jobs”:
myScheduler.addJob(function, interval_in_ms[, ONETIME]); myScheduler.removeJob(function); myScheduler.pauseJob(function); myScheduler.restartJob(function);
The sintax is exactly the same of that one of leOS with the difference that now the keyword “Task” has been replaced by “Job”. With .addJob you add a job named “function” into the scheduler to be executed every “interval_in_ms” milliseconds. Using the optional paramether ONE_TIME you tell looper that this task has to be executed only once.
.removeJob just removes a job while .pauseJob and .restartJob pause a task and start it again, respectively. To let looper work, you have to call the .scheduler() method in any point of your main loop:
void loop() { ... myScheduler.scheduler(); }
An interesting feature of looper is the new method .myDelay() that can stop the execution of the code of the main loop() while it still continues to execute the jobs that are present into the scheduler. To use this feature you just have to use the method .myDelay instead of the Arduino function delay():
... myScheduler.myDelay(interval in ms); ...
Due to the fact that looper doesn’t use interrupts, its precision relies completely on the duration of the main loop. The main loop must have an execution time that has to be less than the shortest interval of your scheduled tasks otherwise they will be executed with an interval what will be equal to that one of the main loop. Remember that looper is just a routine that executes other routines.
Some examples are included into the library to show how to use looper.
[notice]The code that used the previous version of looper has to be checked to change old methods and use the new syntax.[/notice]
[important]NOTE FOR leOS USERS:
since version 1.0.0, looper can be used together with leOS: an example shows how to use both in a sketch.[/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.