Pages: [1]   Go Down
Author Topic: SimpleTimer library  (Read 4358 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
God Member
*****
Karma: 2
Posts: 596
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,
    I've developed a simple library to fire timed events.
Resolution is 1ms.

A more complete solution is to use Time + TimeAlarm libraries, which also have clock features, but are bigger than this lib (compiled code size).

Here's the code:

SimpleTimer.h:
Code:
/*
 * SimpleTimer.h
 *
 */

#ifndef SIMPLETIMER_H
#define SIMPLETIMER_H

#include <WProgram.h>

typedef void (*timer_callback)(void);

class SimpleTimer {

public:
    // maximum number of timers
    const static int MAX_TIMERS = 10;

    // setTimer() constants
    const static int RUN_FOREVER = 0;
    const static int RUN_ONCE = 1;

    // constructor
    SimpleTimer();

    // this function must be called inside loop()
    void run();

    // call function f every d milliseconds
    int setInterval(long d, timer_callback f);

    // call function f once after d milliseconds
    int setTimeout(long d, timer_callback f);

    // call function f every d milliseconds for n times
    int setTimer(long d, timer_callback f, int n);

    // destroy the specified timer
    void deleteTimer(int numTimer);

    // returns true if the specified timer is enabled
    boolean isEnabled(int numTimer);

    // enables the specified timer
    void enable(int numTimer);

    // disables the specified timer
    void disable(int numTimer);

    // enables or disables the specified timer
    // based on its current state
    void toggle(int numTimer);

    // returns the number of used timers
    int getNumTimers();

private:
    // value returned by the millis() function
    // in the previous run() call
    long prev_millis[MAX_TIMERS];

    // pointers to the callback functions
    timer_callback callbacks[MAX_TIMERS];

    // delay values
    long delays[MAX_TIMERS];

    // number of runs to be executed for each timer
    int maxNumRuns[MAX_TIMERS];

    // number of executed runs for each timer
    int numRuns[MAX_TIMERS];

    // which timers are enabled
    boolean enabled[MAX_TIMERS];

    // actual number of timers in use
    int numTimers;
};

#endif

SimpleTimer.cpp:

Code:
#include "SimpleTimer.h"


SimpleTimer::SimpleTimer() {
    long current_millis = millis();

    for (int i = 0; i < MAX_TIMERS; i++) {
        enabled[i] = false;
        callbacks[i] = 0;
        prev_millis[i] = current_millis;
    }

    numTimers = 0;
}


void SimpleTimer::run() {
    int i;
    long current_millis;

    // get current time
    current_millis = millis();

    for (i = 0; i < MAX_TIMERS; i++) {

        // only process active timers
        if (callbacks[i]) {
            
            // is it time to process this timer ?
            if (current_millis - prev_millis[i] >= delays[i]) {

                // update time
                prev_millis[i] = current_millis;

                // check if the timer callback has to be executed
                if (enabled[i]) {

                    // "run forever" timers must always be executed
                    if (maxNumRuns[i] == RUN_FOREVER) {
                        (*callbacks[i])();
                    }
                    // other timers get executed the specified number of times
                    else if (numRuns[i] < maxNumRuns[i]) {
                        (*callbacks[i])();
                        numRuns[i]++;

                        // after the last run, delete the timer
                        // to save some cycles
                        if (numRuns[i] >= maxNumRuns[i]) {
                            deleteTimer(i);
                        }
                    }
                }
            }
        }
    }
}


int SimpleTimer::setTimer(long d, timer_callback f, int n) {
    if (numTimers >= MAX_TIMERS) {
        return -1;
    }

    delays[numTimers] = d;
    callbacks[numTimers] = f;
    maxNumRuns[numTimers] = n;
    enabled[numTimers] = true;

    numTimers++;

    return (numTimers - 1);
}


int SimpleTimer::setInterval(long d, timer_callback f) {
    return setTimer(d, f, RUN_FOREVER);
}


int SimpleTimer::setTimeout(long d, timer_callback f) {
    return setTimer(d, f, RUN_ONCE);
}


void SimpleTimer::deleteTimer(int numTimer) {
    if (numTimer >= MAX_TIMERS) {
        return;
    }

    // nothing to disable if no timers are in use
    if (numTimers == 0) {
        return;
    }

    callbacks[numTimer] = 0;
    enabled[numTimer] = false;
    delays[numTimer] = 0;

    // update number of timers
    numTimers--;
}


boolean SimpleTimer::isEnabled(int numTimer) {
    if (numTimer >= MAX_TIMERS) {
        return false;
    }

    return enabled[numTimer];
}


void SimpleTimer::enable(int numTimer) {
    if (numTimer >= MAX_TIMERS) {
        return;
    }

    enabled[numTimer] = true;
}


void SimpleTimer::disable(int numTimer) {
    if (numTimer >= MAX_TIMERS) {
        return;
    }

    enabled[numTimer] = false;
}


void SimpleTimer::toggle(int numTimer) {
    if (numTimer >= MAX_TIMERS) {
        return;
    }

    enabled[numTimer] = !enabled[numTimer];
}


int SimpleTimer::getNumTimers() {
    return numTimers;
}


Example sketch (modified version of Time+TimeAlarm example):

Code:
/*
 * SimpleTimerAlarmExample.pde
 *
 * A timer is called every 15 seconds
 * Another timer is called once only after 10 seconds
 * A third timer is called 10 times.
 *
 */

#include <SimpleTimer.h>

SimpleTimer timer;
  
void setup()
{
  Serial.begin(9600);    
  Serial.println("SimpleTimer Example");
  Serial.println("One timer is triggered every 15 seconds");
  Serial.println("Another timer is set to trigger only once after 10 seconds");
  Serial.println("Another timer is set to trigger 10 times");
  Serial.println();
  
  timer.setInterval(15000, RepeatTask);            // timer for every 15 seconds    
  timer.setTimeout(10000, OnceOnlyTask);            // called once after 10 seconds
  timer.setInterval(1000, digitalClockDisplay);
  timer.setTimer(1200, tenTimesTask, 10);
}

void RepeatTask()
{
  Serial.println("15 second timer");        
}

void OnceOnlyTask()
{
  Serial.println("This timer only triggers once");  
}

void tenTimesTask() {
  static int k = 0;
  k++;
  Serial.print("called ");
  Serial.print(k);
  Serial.println(" / 10 times.");
}

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

void digitalClockDisplay()
{
  int h,m,s;
  s = millis() / 1000;
  m = s / 60;
  h = s / 3600;
  s = s - m * 60;
  m = m - h * 60;
  Serial.print(h);
  printDigits(m);
  printDigits(s);
  Serial.println();
}

void printDigits(int digits)
{
  // utility function for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}
Logged

Norway@Oslo
Offline Offline
Edison Member
*
Karma: 12
Posts: 2033
loveArduino(true);
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

You can add it to the list of timing related libraries:
http://www.arduino.cc/playground/Main/GeneralCodeLibrary#Timing_code

smiley
« Last Edit: June 10, 2010, 07:03:04 am by AlphaBeta » Logged

0
Offline Offline
God Member
*****
Karma: 2
Posts: 596
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Moved to http://www.arduino.cc/playground/Code/SimpleTimer

Thanks :-)
Logged

Corvallis, OR
Offline Offline
Newbie
*
Karma: 0
Posts: 14
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi guys, this is a useful library, however, I have found a bug which is easily remedied.

The issue is that prev_millis is not reset for each timer on first creation. Let's say you create a timer, it will be assigned ID = 0. That timer ends, now you create a new timer. The code will just reuse the old ID which is now free, so this second timer will also get assigned ID = 0. However, since prev_millis isn't reset on creation, it will also be assigned prev_millis[0] from the previous timer.

The time that has passed since the first timer was "destroyed" and the new timer created is essentially now reflected in prev_milllis[0]. This time may very well exceed whatever timeout you have on your new timer, so you might be surprised to find that it expires immediately.

The fix is simple enough. In "SimpleTimer.cpp", in the SimpleTimer::SetTimer function, you need to add this line:
prev_millis[numTimers] = millis();

The new function should look like this:

Code:
int SimpleTimer::setTimer(long d, timer_callback f, int n) {
    if (numTimers >= MAX_TIMERS) {
        return -1;
    }

    delays[numTimers] = d;
    callbacks[numTimers] = f;
    maxNumRuns[numTimers] = n;
    enabled[numTimers] = true;
    prev_millis[numTimers] = millis();
    
    numTimers++;

    return (numTimers - 1);
}

This ensures that every timer you create, even if it is reusing some old TimerID, will have its "start time" set correctly.

Maybe someone could update the library source...


Luke
Logged

Pages: [1]   Go Up
Jump to: