SimpleTimer Library for Arduino
Author:  Marcello Romani
Contact: mromani@ottotecnica.com
License: GNU LGPL 2.1+


Navigation


Information

Description

This is (yet another) simple library to launch timed actions.

It's based on millis(), thus it has 1 ms resolution.

It uses polling, so no guarantee can be made about the exact time when a callback is fired. For example, if you setup the library so that it calls a function every 2ms, but this function requires 5ms to complete, then you'll have an invocation every 5ms.

For applications where non-strict timing is enough, not using interrupts avoids potential problems with global variables shared between the interrupt service routine and the main program, and doesn't consune a hardware timer.

Download

Go to the Get the code section and hit the "get the code" link at the bottom of each code block.

Installation

  1. Create a folder named SimpleTimer in you sketchbook/libraries folder.
  2. Copy-n-paste the code for SimpleTimer.h and SimpleTimer.cpp into your favourite text editor, and save those two files under the SimpleTimer folder you just created.
  3. Launch or restart the Arduino IDE to let it detect the new library.
  4. Copy-n-paste the example code (see below) to get you started with the library.

Usage

#include <SimpleTimer.h>

// the timer object
SimpleTimer timer;

// a function to be executed periodically
void repeatMe() {
    Serial.print("Uptime (s): ");
    Serial.println(millis() / 1000);
}

void setup() {
    Serial.begin(9600);
    timer.setInterval(1000, repeatMe);
}

void loop() {
    timer.run();
}


Theory

The base goal is to be able to execute a particular piece of code every n milliseconds, without using interrupts.

The algorithm looks like this:

lastMillis = 0
forever do:
    if (millis() - lastMillis > n)
        call the particular piece of code
        lastMillis = millis()
    end
end

Links

Other libraries with similar or broader goals can be found in the here


Functions

SimpleTimer()

The constructor. You usually need only one SimpleTimer object in a sketch.

SimpleTimer timer;

int setInterval(long d, timer_callback f)

Call function f every d milliseconds. The callback function must be declared as void f().

void repeatMe() {
    // do something
}

timerId = timer.setInterval(1000, repeatMe);

int setTimeout(long d, timer_callback f)

Call function f once after d milliseconds. The callback function must be declared as void f(). After f has been called, the interval is deleted, therefore the value timerId is no longer valid.

void callMeLater() {
    // do something
}

timerId = timer.setTimeout(1000, callMeLater);

int setTimer(long d, timer_callback f, int n)

Call function f every d milliseconds for n times. The callback function must be declared as void f(). After f has been called the specified number of times, the interval is deleted, therefore the value timerId is no longer valid.

void repeatMeFiveTimes() {
    // do something
}

timerId = timer.setTimer(1000, repeatMeFiveTimes, 5);

boolean isEnabled(int timerId)

Returns true if the specified timer is enabled

if(timer.isEnabled(timerId) {
    // do domething
}

void enable(int timerId)

Enables the specified timer.

timer.enable(timerId);

void disable(int timerId)

Disables the specified timer.

timer.disable(timerId);

void toggle(int timerId)

Enables the specified timer if it's currently disabled, and vice-versa.

timer.toggle(timerId);

void restartTimer(int timerId)

Causes the specified timer to start counting from "now", i.e. the instant when restartTimer is called. The timer callback is not fired. A use case for this function is for example the implementation of a watchdog timer (pseudocode follows).

void wdCallback() {
    alert user or perform action to restore
    program state (e.g. reset the microprocessor)
}

wd_timer_id;

void setup() {
    wd_timer_id = timer.setInterval(10000, wdCallback);
}

void loop() {
    timer.run();

    big complex critical code

    timer.restartTimer(wd_timer_id);
}

void deleteTimer(int timerId)

Free the specified timerId slot. You should need to call this only if you have interval slots that you don't need anymore. The other timer types are automatically deleted once the specified number of repetitions have been executed.

void getNumTimers()

Return the number of used slots in a timer object.

n = timer.getNumTimers();


Example

  1. /*
  2.  * SimpleTimerAlarmExample.pde
  3.  *
  4.  * Based on usage example for Time + TimeAlarm libraries
  5.  *
  6.  * A timer is called every 15 seconds
  7.  * Another timer is called once only after 10 seconds
  8.  * A third timer is called 10 times.
  9.  *
  10.  */
  11.  
  12. #include <SimpleTimer.h>
  13.  
  14. // There must be one global SimpleTimer object.
  15. // More SimpleTimer objects can be created and run,
  16. // although there is little point in doing so.
  17. SimpleTimer timer;
  18.  
  19. // function to be called repeatedly
  20. void RepeatTask() {
  21.   Serial.println("15 second timer");        
  22. }
  23.  
  24. // function to be called just once
  25. void OnceOnlyTask() {
  26.   Serial.println("This timer only triggers once");  
  27. }
  28.  
  29. // function to be called exactly 10 times
  30. void TenTimesTask() {
  31.   static int k = 0;
  32.   k++;
  33.   Serial.print("called ");
  34.   Serial.print(k);
  35.   Serial.println(" / 10 times.");
  36. }
  37.  
  38. // print current arduino "uptime" on the serial port
  39. void DigitalClockDisplay() {
  40.   int h,m,s;
  41.   s = millis() / 1000;
  42.   m = s / 60;
  43.   h = s / 3600;
  44.   s = s - m * 60;
  45.   m = m - h * 60;
  46.   Serial.print(h);
  47.   printDigits(m);
  48.   printDigits(s);
  49.   Serial.println();
  50. }
  51.  
  52. //
  53. // utility function for digital clock display:
  54. // prints preceding colon and leading 0
  55. //
  56. void printDigits(int digits) {
  57.   Serial.print(":");
  58.   if(digits < 10)
  59.     Serial.print('0');
  60.   Serial.print(digits);
  61. }
  62.  
  63. void setup() {
  64.   Serial.begin(9600);
  65.  
  66.   // welcome message
  67.   Serial.println("SimpleTimer Example");
  68.   Serial.println("One timer is triggered every 15 seconds");
  69.   Serial.println("Another timer is set to trigger only once after 10 seconds");
  70.   Serial.println("Another timer is set to trigger 10 times");
  71.   Serial.println();
  72.  
  73.   // timed actions setup
  74.   timer.setInterval(15000, RepeatTask);
  75.   timer.setTimeout(10000, OnceOnlyTask);
  76.   timer.setInterval(1000, DigitalClockDisplay);
  77.   timer.setTimer(1200, TenTimesTask, 10);
  78. }
  79.  
  80. void loop() {
  81.   // this is where the "polling" occurs
  82.   timer.run();
  83. }


Get the code

  1. /*
  2.  * SimpleTimer.h
  3.  *
  4.  * SimpleTimer - A timer library for Arduino.
  5.  * Author: mromani@ottotecnica.com
  6.  * Copyright (c) 2010 OTTOTECNICA Italy
  7.  *
  8.  * This library is free software; you can redistribute it
  9.  * and/or modify it under the terms of the GNU Lesser
  10.  * General Public License as published by the Free Software
  11.  * Foundation; either version 2.1 of the License, or (at
  12.  * your option) any later version.
  13.  *
  14.  * This library is distributed in the hope that it will
  15.  * be useful, but WITHOUT ANY WARRANTY; without even the
  16.  * implied warranty of MERCHANTABILITY or FITNESS FOR A
  17.  * PARTICULAR PURPOSE.  See the GNU Lesser General Public
  18.  * License for more details.
  19.  *
  20.  * You should have received a copy of the GNU Lesser
  21.  * General Public License along with this library; if not,
  22.  * write to the Free Software Foundation, Inc.,
  23.  * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  24.  *
  25.  */
  26.  
  27.  
  28. #ifndef SIMPLETIMER_H
  29. #define SIMPLETIMER_H
  30.  
  31. #if defined(ARDUINO) && ARDUINO >= 100
  32. #include <Arduino.h>
  33. #else
  34. #include <WProgram.h>
  35. #endif
  36.  
  37. typedef void (*timer_callback)(void);
  38.  
  39. class SimpleTimer {
  40.  
  41. public:
  42.     // maximum number of timers
  43.     const static int MAX_TIMERS = 10;
  44.  
  45.     // setTimer() constants
  46.     const static int RUN_FOREVER = 0;
  47.     const static int RUN_ONCE = 1;
  48.  
  49.     // constructor
  50.     SimpleTimer();
  51.  
  52.     // this function must be called inside loop()
  53.     void run();
  54.  
  55.     // call function f every d milliseconds
  56.     int setInterval(long d, timer_callback f);
  57.  
  58.     // call function f once after d milliseconds
  59.     int setTimeout(long d, timer_callback f);
  60.  
  61.     // call function f every d milliseconds for n times
  62.     int setTimer(long d, timer_callback f, int n);
  63.  
  64.     // destroy the specified timer
  65.     void deleteTimer(int numTimer);
  66.  
  67.     // restart the specified timer
  68.     void restartTimer(int numTimer);
  69.  
  70.     // returns true if the specified timer is enabled
  71.     boolean isEnabled(int numTimer);
  72.  
  73.     // enables the specified timer
  74.     void enable(int numTimer);
  75.  
  76.     // disables the specified timer
  77.     void disable(int numTimer);
  78.  
  79.     // enables the specified timer if it's currently disabled,
  80.     // and vice-versa
  81.     void toggle(int numTimer);
  82.  
  83.     // returns the number of used timers
  84.     int getNumTimers();
  85.  
  86.     // returns the number of available timers
  87.     int getNumAvailableTimers() { return MAX_TIMERS - numTimers; };
  88.  
  89. private:
  90.     // deferred call constants
  91.     const static int DEFCALL_DONTRUN = 0;       // don't call the callback function
  92.     const static int DEFCALL_RUNONLY = 1;       // call the callback function but don't delete the timer
  93.     const static int DEFCALL_RUNANDDEL = 2;      // call the callback function and delete the timer
  94.  
  95.     // find the first available slot
  96.     int findFirstFreeSlot();
  97.  
  98.     // value returned by the millis() function
  99.     // in the previous run() call
  100.     unsigned long prev_millis[MAX_TIMERS];
  101.  
  102.     // pointers to the callback functions
  103.     timer_callback callbacks[MAX_TIMERS];
  104.  
  105.     // delay values
  106.     long delays[MAX_TIMERS];
  107.  
  108.     // number of runs to be executed for each timer
  109.     int maxNumRuns[MAX_TIMERS];
  110.  
  111.     // number of executed runs for each timer
  112.     int numRuns[MAX_TIMERS];
  113.  
  114.     // which timers are enabled
  115.     boolean enabled[MAX_TIMERS];
  116.  
  117.     // deferred function call (sort of) - N.B.: this array is only used in run()
  118.     int toBeCalled[MAX_TIMERS];
  119.  
  120.     // actual number of timers in use
  121.     int numTimers;
  122. };
  123.  
  124. #endif


  1. /*
  2.  * SimpleTimer.cpp
  3.  *
  4.  * SimpleTimer - A timer library for Arduino.
  5.  * Author: mromani@ottotecnica.com
  6.  * Copyright (c) 2010 OTTOTECNICA Italy
  7.  *
  8.  * This library is free software; you can redistribute it
  9.  * and/or modify it under the terms of the GNU Lesser
  10.  * General Public License as published by the Free Software
  11.  * Foundation; either version 2.1 of the License, or (at
  12.  * your option) any later version.
  13.  *
  14.  * This library is distributed in the hope that it will
  15.  * be useful, but WITHOUT ANY WARRANTY; without even the
  16.  * implied warranty of MERCHANTABILITY or FITNESS FOR A
  17.  * PARTICULAR PURPOSE.  See the GNU Lesser General Public
  18.  * License for more details.
  19.  *
  20.  * You should have received a copy of the GNU Lesser
  21.  * General Public License along with this library; if not,
  22.  * write to the Free Software Foundation, Inc.,
  23.  * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  24.  */
  25.  
  26.  
  27. #include "SimpleTimer.h"
  28.  
  29.  
  30. // Select time function:
  31. //static inline unsigned long elapsed() { return micros(); }
  32. static inline unsigned long elapsed() { return millis(); }
  33.  
  34.  
  35. SimpleTimer::SimpleTimer() {
  36.     unsigned long current_millis = elapsed();
  37.  
  38.     for (int i = 0; i < MAX_TIMERS; i++) {
  39.         enabled[i] = false;
  40.         callbacks[i] = 0;                   // if the callback pointer is zero, the slot is free, i.e. doesn't "contain" any timer
  41.         prev_millis[i] = current_millis;
  42.         numRuns[i] = 0;
  43.     }
  44.  
  45.     numTimers = 0;
  46. }
  47.  
  48.  
  49. void SimpleTimer::run() {
  50.     int i;
  51.     unsigned long current_millis;
  52.  
  53.     // get current time
  54.     current_millis = elapsed();
  55.  
  56.     for (i = 0; i < MAX_TIMERS; i++) {
  57.  
  58.         toBeCalled[i] = DEFCALL_DONTRUN;
  59.  
  60.         // no callback == no timer, i.e. jump over empty slots
  61.         if (callbacks[i]) {
  62.  
  63.             // is it time to process this timer ?
  64.             // see http://arduino.cc/forum/index.php/topic,124048.msg932592.html#msg932592
  65.  
  66.             if (current_millis - prev_millis[i] >= delays[i]) {
  67.  
  68.                 // update time
  69.                 //prev_millis[i] = current_millis;
  70.                 prev_millis[i] += delays[i];
  71.  
  72.                 // check if the timer callback has to be executed
  73.                 if (enabled[i]) {
  74.  
  75.                     // "run forever" timers must always be executed
  76.                     if (maxNumRuns[i] == RUN_FOREVER) {
  77.                         toBeCalled[i] = DEFCALL_RUNONLY;
  78.                     }
  79.                     // other timers get executed the specified number of times
  80.                     else if (numRuns[i] < maxNumRuns[i]) {
  81.                         toBeCalled[i] = DEFCALL_RUNONLY;
  82.                         numRuns[i]++;
  83.  
  84.                         // after the last run, delete the timer
  85.                         if (numRuns[i] >= maxNumRuns[i]) {
  86.                             toBeCalled[i] = DEFCALL_RUNANDDEL;
  87.                         }
  88.                     }
  89.                 }
  90.             }
  91.         }
  92.     }
  93.  
  94.     for (i = 0; i < MAX_TIMERS; i++) {
  95.         switch(toBeCalled[i]) {
  96.             case DEFCALL_DONTRUN:
  97.                 break;
  98.  
  99.             case DEFCALL_RUNONLY:
  100.                 (*callbacks[i])();
  101.                 break;
  102.  
  103.             case DEFCALL_RUNANDDEL:
  104.                 (*callbacks[i])();
  105.                 deleteTimer(i);
  106.                 break;
  107.         }
  108.     }
  109. }
  110.  
  111.  
  112. // find the first available slot
  113. // return -1 if none found
  114. int SimpleTimer::findFirstFreeSlot() {
  115.     int i;
  116.  
  117.     // all slots are used
  118.     if (numTimers >= MAX_TIMERS) {
  119.         return -1;
  120.     }
  121.  
  122.     // return the first slot with no callback (i.e. free)
  123.     for (i = 0; i < MAX_TIMERS; i++) {
  124.         if (callbacks[i] == 0) {
  125.             return i;
  126.         }
  127.     }
  128.  
  129.     // no free slots found
  130.     return -1;
  131. }
  132.  
  133.  
  134. int SimpleTimer::setTimer(long d, timer_callback f, int n) {
  135.     int freeTimer;
  136.  
  137.     freeTimer = findFirstFreeSlot();
  138.     if (freeTimer < 0) {
  139.         return -1;
  140.     }
  141.  
  142.     if (f == NULL) {
  143.         return -1;
  144.     }
  145.  
  146.     delays[freeTimer] = d;
  147.     callbacks[freeTimer] = f;
  148.     maxNumRuns[freeTimer] = n;
  149.     enabled[freeTimer] = true;
  150.     prev_millis[freeTimer] = elapsed();
  151.  
  152.     numTimers++;
  153.  
  154.     return freeTimer;
  155. }
  156.  
  157.  
  158. int SimpleTimer::setInterval(long d, timer_callback f) {
  159.     return setTimer(d, f, RUN_FOREVER);
  160. }
  161.  
  162.  
  163. int SimpleTimer::setTimeout(long d, timer_callback f) {
  164.     return setTimer(d, f, RUN_ONCE);
  165. }
  166.  
  167.  
  168. void SimpleTimer::deleteTimer(int timerId) {
  169.     if (timerId >= MAX_TIMERS) {
  170.         return;
  171.     }
  172.  
  173.     // nothing to delete if no timers are in use
  174.     if (numTimers == 0) {
  175.         return;
  176.     }
  177.  
  178.     // don't decrease the number of timers if the
  179.     // specified slot is already empty
  180.     if (callbacks[timerId] != NULL) {
  181.         callbacks[timerId] = 0;
  182.         enabled[timerId] = false;
  183.         toBeCalled[timerId] = DEFCALL_DONTRUN;
  184.         delays[timerId] = 0;
  185.         numRuns[timerId] = 0;
  186.  
  187.         // update number of timers
  188.         numTimers--;
  189.     }
  190. }
  191.  
  192.  
  193. // function contributed by code@rowansimms.com
  194. void SimpleTimer::restartTimer(int numTimer) {
  195.     if (numTimer >= MAX_TIMERS) {
  196.         return;
  197.     }
  198.  
  199.     prev_millis[numTimer] = elapsed();
  200. }
  201.  
  202.  
  203. boolean SimpleTimer::isEnabled(int numTimer) {
  204.     if (numTimer >= MAX_TIMERS) {
  205.         return false;
  206.     }
  207.  
  208.     return enabled[numTimer];
  209. }
  210.  
  211.  
  212. void SimpleTimer::enable(int numTimer) {
  213.     if (numTimer >= MAX_TIMERS) {
  214.         return;
  215.     }
  216.  
  217.     enabled[numTimer] = true;
  218. }
  219.  
  220.  
  221. void SimpleTimer::disable(int numTimer) {
  222.     if (numTimer >= MAX_TIMERS) {
  223.         return;
  224.     }
  225.  
  226.     enabled[numTimer] = false;
  227. }
  228.  
  229.  
  230. void SimpleTimer::toggle(int numTimer) {
  231.     if (numTimer >= MAX_TIMERS) {
  232.         return;
  233.     }
  234.  
  235.     enabled[numTimer] = !enabled[numTimer];
  236. }
  237.  
  238.  
  239. int SimpleTimer::getNumTimers() {
  240.     return numTimers;
  241. }


About this page

Last Modified: May 19, 2014, at 08:09 AM
By: mromani\\

Share