Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / IoT / Arduino

Scheduler Library for Arduino

3.80/5 (3 votes)
21 Feb 2014CPOL3 min read 27.9K   384  
Revised timer for Arduino

Introduction

The existing Timer library written by Simon Monk has a drawback that you cannot use it with your classes directly. You can only pass standard function pointer as timer callback, or you have to make some part of your classes public, and I don't like to do such things. There is no way to pass a member function of a class as callback in this library. With Scheduler, you can do this, and some other cool properties.

Background

There are some good articles on function pointers, one of them is the following, written by Don Clugston:

I won't get into deeps about the subject, but to state simply, you cannot use standard function pointers and member function pointers interchangeably.

Let's test it:

First of all, we need a class with a member function to be used as callback for Timer.

C++
class TimerClient {
public:
    void timerClientCallback() {
        Serial.println("timerClientCallback");
    };
};

And the test application where you use this class with a Timer.

C++
#include <Arduino.h>
#include <Timer.h>
#include "TimerClient.h"

Timer t;
TimerClient c;
 
void setup() {
    t.after(1000, c.timerClientCallback); // passing member function as callback
}
 
void loop() {
    t.update();
}

And this makes the compiler angry with your code.

Quote:
test1.ino: In function 'void setup()':


test1:10: error: no matching function for call to 'Timer::after(int, <unresolved overloaded function type>)'


D:\projects\arduino\libraries\Timer/Timer.h:39: note: candidates are: int8_t Timer::after(long unsigned int, void (*)())
 

Because "void (*fptr)()" is different from "void (AnyClass::*fptr)()".

Overview

Basically, Scheduler manages Events and Events informs SchedulerClients. When you set SchedulerClient as a base for your class, you can register it in Scheduler by using every function. Simplified class diagram is the following:

Image 1

Figure 1: Classes

What You Can Do With Scheduler

  • You can register a client with every function in order to be informed at the end of each period.
  • You can do this repeatCount times.
  • You may want an ordinary function (int (*f)(void)) as callback. Then you can use SchedulerClient as a wrapper by constructing it with SchedulerClient(f).
  • You can provide a member function by overriding schedulerCallback virtual function.
  • You may want to be informed at the end of the last period, which means that your SchedulerClient is informed repeatCount times. You can do this by overriding schedulerEnded virtual function.
  • You can oscillate a pin (set HIGH to LOW, or LOW to HIGH).
  • You can use Scheduler without constructing an instance. Static getInstance function provides one for you.

Pros

  • You can use member functions as good as ordinary functions.
  • Dynamic allocation of events: An event is created when it is needed.
  • Recycled usage of events: When an event ends, it is used instead of creating a new one for the next request.
  • Global access to a Scheduler instance: Scheduler holds a static instance of itself for instant usage. Therefore, you don't need instantiate one when you need it.

Cons

  • There can exist at most 10 active Event per Scheduler. When a call (every or oscillate) returns -1, it means that you have to wait or create another instance. This applies to static instance, too.
  • You cannot pass ordinary functions directly as callback. You have to wrap it with a SchedulerClient.

Using Code

I don't want to bother you with many lines of code here, therefore I will only show you the basic usage.

C++
// file: test1.ino
// a derived SchedulerClient
SchedulerClient2 *cl2 = new SchedulerClient2();
// A SchedulerClient which wraps cb1
SchedulerClient cl1(cb1);
 
// Scheduler instance
Scheduler s;
 
void setup() {
    Serial.begin(9600);
 
    // 'every' test with ordinary function
    if(s.every(&cl1, 1000)<0)
        printFail("--1");
    // 'every' test with member function
    if(s.every(cl2, 3000)<0)
        printFail("--2");
    // 'every' test for repeatCount & schedulerEnded
    if(s.every(cl2, 2000, 3)<0)
        printFail("--3");
 
    // 'oscillate' test over LED
    if(s.oscillate(LED_PIN, 1000, HIGH)<0)
        printFail("--4");
 
    // consume all events
    for(int i=0; i<10; i++) {
        if(s.every(cl2, 1000, 2)<0) {
            printFail("");
            Serial.println(i);
        }
    }
}

And SchedulerClient2:

C++
// file SchedulerClient2.h 
class SchedulerClient2: public SchedulerClient {
public:
    void schedulerCallback(int eventId) {
        Serial.print("Callback from event(SchedulerClient2): ");
        Serial.println(eventId);
    };
    void schedulerEnded(int eventId) {
        Serial.print("Event end(SchedulerClient2): ");
        Serial.println(eventId);
    };
};

As you see in the sample codes, the only thing to do in a client is to override schedulerCallback virtual function and schedulerEnded virtual function if you like.

I have provided all sources at the download section. It is simple, but useful, I think. What you should do to use in your Arduino project is to extract the file in a folder under your Arduino libraries directory, as always.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)