Home - lapalisse/FlexTimer GitHub Wiki
Introduction
Why did I do this library? I was looking at a simple ways to do repeating actions on Arduino systems (or Unix), but couldn't really find anything powerful enough for my needs. There are tons of bits of software doing this though, in different ways. I like simplicity, and power, and also I don't like to think about complicated things (like 32 bits overflow!), so these were the main constraints that drove me. I hope you'll find it useful.
You may also think that software timers is inefficient nowadays, as we have multi-core systems, but when you think of it, it is not. Software timers are simple, and the fact that you do the difficult part in one thread makes it easier to write your code. Multi-threaded code can be tricky to write and extremely tricky to test and debug. And you can still move the heavy stuff out of the main loop, in one or several parallel threads if you need.
Software timers is probably a very good way to handle user side interrupts; it won't be real-time that's true, but can be pretty close to real-time, and won't consume too much cpu time if you use a sleep function in your main loop.
Getting Started with version 1.0
Version 1 of the FlexTimer library is meant for the Arduino environment. It is extremely simple, yet powerful. It's aim is to facilitate the handling of repeating tasks, and do this in an elegant way by hiding all the difficulties (like time value going back to zero).
"Version 1" has a limited set of functionalities, and it is strongly discouraged to add any new functionality to this version. The idea behind this is to keep it as small as possible. The C language has been chosen for this version, and I think it is meaningful to leave it like that. The algorithm used has a few limitations, but it has been a choice mainly driven by simplicity.
If you want more functionalities, go have a look at "version 2", or C++ oriented versions...
I believe you will find it simple to use and flexible enough for most needs.
Example1: displaying a message every 100ms
void do_it() {
printf("Message every 100ms\n");
}
void setup() {
FT_init();
FT_insert_timer(100, FT_RUN_FOREVER, &do_it, NULL);
}
void loop() {
FT_check_and_do();
}
Example2: two different messages with a parameter
Note the use of the function FT_sleep_and_do() instead of FT_check_and_do(): this one does a sleep, until it's time to do something: the calculation of the duration of the sleep is automatic and supposedly optimal, yet it is still simple.
void do_it(void* param) {
char* msg = (char*)param;
printf("Message: %s\n", msg);
}
char msg1[] = "Bonjour";
char msg2[] = "Salut";
void setup() {
FT_init();
FT_insert_timer(100, FT_RUN_FOREVER, &do_it, msg1);
FT_insert_timer(33, FT_RUN_FOREVER, &do_it, msg2);
}
void loop() {
FT_sleep_and_do();
}
Arduino classic LED example
int ledState = LOW;
void blink() {
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
digitalWrite(13, ledState);
}
void setup() {
FT_init();
FT_insert_timer(1000, FT_RUN_FOREVER, &blink, NULL);
}
void loop() {
FT_wait_and_do();
}
Arduino not so classic LED example
Every 3 seconds, blink 10 times during one second! Note the use of the constant FT_ONE_SECOND: this value is independant of the length of the time unit used...
int led = 13;
int state = 0;
void blink() {
if (state == 0) {
digitalWrite(led, HIGH);
} else {
digitalWrite(led, LOW);
}
state = 1 - state;
}
void blink_10_times_during_one_second() {
FT_insert_timer(FT_ONE_SECOND/20, 20, &blink, NULL);
}
void setup() {
FT_init();
FT_insert_timer(3*FT_ONE_SECOND, FT_RUN_FOREVER, &blink_10_times_during_one_second, NULL);
}
void loop() {
FT_wait_and_do();
}
Arduino many LEDs at random speeds example
typedef struct LED {
int pin;
int state;
} LED;
void blink(void* param) {
LED led = (LED*)param;
if (led->state == 0) {
digitalWrite(led->pin, HIGH);
} else {
digitalWrite(led->pin, LOW);
}
led->state = 1 - led->state;
}
const int N = 10;
void setup() {
LED leds[N];
int i;
FT_init();
for (i = 0; i < N; i++) {
leds[i].state = 0;
leds[i].pin = 13 + i;
FT_insert_timer(random(1, FT_ONE_SECOND), FT_RUN_FOREVER, &blink, leds[i]);
}
}
void loop() {
FT_wait_and_do();
}
// Now, try to do this without this library, and you'll understand...
// And, it handles 32-bit overflow automatically!
Arduino many LEDs + change the speed of blinking every 3 seconds!
typedef struct LED {
int pin;
int state;
} LED;
void blink(void* param) {
LED led = (LED*)param;
if (led->state == 0) {
digitalWrite(led->pin, HIGH);
} else {
digitalWrite(led->pin, LOW);
}
led->state = 1 - led->state;
}
const int N = 10;
void change_speeds(void* param) {
FT_timer *timer = (FT_timer_t)param;
for (int i = 0; i < N; i++) {
timer->delay = random(1, FT_ONE_SECOND);
}
}
void setup() {
LED leds[N];
FT_timer_t timers[N];
int i;
FT_init();
for (i = 0; i < N; i++) {
leds[i].state = 0;
leds[i].pin = 13 + i;
timers[i] = FT_insert_timer(random(1, FT_ONE_SECOND), FT_RUN_FOREVER, &blink, leds[i]);
}
FT_insert_timer(3*FT_ONE_SECOND, FT_RUN_FOREVER, &change_speeds, timers);
}
void loop() {
FT_wait_and_do();
}
Arduino pass an int
void blink(void* param) {
int led = &((int*)param);
???
}
void setup() {
LED leds[N];
FT_timer_t timers[N];
int int_value = 5;
FT_init();
timers[i] = FT_insert_timer(random(1, FT_ONE_SECOND), FT_RUN_FOREVER, &blink, &int_value);
FT_insert_timer(3*FT_ONE_SECOND, FT_RUN_FOREVER, &change_speeds, timers);
}
void loop() {
FT_wait_and_do();
}
Arduino infinite timer stopping
void blink(void* param) {
if (???) {
t->repeat = 0;
}
}
void setup() {
int n = 100000;
FT_init();
FT_insert_timer(3*FT_ONE_SECOND, FT_RUN_FOREVER, &stop_after_1000, NULL);
}
void loop() {
FT_sleep_and_do();
}
Other granularities
You probably noticed that this library is using milliseconds as the base time unit. This is the default, and it can be changed, but you'll have to recompile. When you recompile, check flextimer.h for these defines, and comment the "MS" line, then uncomment the "US" line.
//#define FT_ARDUINO_MS
#define FT_ARDUINO_US
//#define FT_ARDUINO_SECOND
Recompile, that's it you have a microsecond granularity. Please note that the constant FT_ONE_SECOND will now be valued by 1000000. If you use this constant in your program instead of fixed numbers ("1000"), you won't need to make any modification to your program.
A good idea is to always base your delays on these constants, like:
// Do some stuff
int one_tenth_of_a_second = FT_ONE_SECOND/10;
There is no current nanosecond implementation, but we'll add it as soon as we have interesting Arduino time functions working at this level.
You can also have a one second granularity, but I don't find it that interesting for what I do. Well, it exists, and if it's meaningful to you, it's there for you to use it.
It is also possible to add fancy granularities, like having the smallest time unit being 250ms, for example. This can be interesting if you want the full 32 bits span of integers (for delays) for your timers, and still have a reasonably small time unit. You can also use it for more precise timers, like say having a 10ms precision and working at ms practically will have your ticks be more precise.
32-bit?
You may have noticed that all timers are using 32 bits wide integers. This is the default, and it can't be changed easily, as the "system" functions use this size.
It is assumed that when the timer reaches 2^32-1, the next value will be 0. It is relatively easy to change this 32 bit limit to another power of 2, but a little bit more complicated if your timer is not aligned on powers of two (like if the final value is say 1000000 for example): in that case, you'll have to modify the code.
Please note, there are functions provided to handle very long times, and this is done with 64 bits integers, but it's something like an extension of the 32 bits timers.
Other platforms
This library is meant for the Arduino systems. That said, it works very well with Unix too. You can recompile it, for Unix with the granularity that you want.
//#define FT_UNIX_MS
//#define FT_UNIX_US
//#define FT_UNIX_SECOND
Now, if you want use this library on other systems, it's probably easy to adapt, but this is no every day project. You may first have a look at how FT_ARDUINO_MS is implemented, then try locate the different parts where this define is used. This may probably be enough to manage your new system! You may also come back to me, for advice...
Optimization
What if you want a faster library? There are simple ways to make this library faster. You can remove the FT_PARANOIA define and recompile it. This will remove some extra checks, used to detect bad situations, and double-checking of values and the limits they should be within.
//#define FT_PARANOIA
If you have a very big number of timers being active at the same time, you can also reimplement the push_timer function. Basically it implements a chained list on all timers, and we insert timers in this list at a proper location. Simply put, a B-Tree or something equivalent may be faster, again, if you have many timers... If this is needed, get in contact with me.
Max number of timers
In flextimer.h, you'll find a constant that's defined:
#define FT_MAX_NUMBER_OF_TIMERS (10)
You can easily modify this constant to authorize more timers at the same time.
#define FT_MALLOC
//#define FT_
//#define FT_
These defines are there to choose the way memory allocation will be done. Remember, on Arduino systems, malloc is discouraged.