My motivation to write a task scheduler framework is to help me faster develop new Arduino projects. In most projects, we repeat the same tasks over and over before getting to play with the new stuff.
You can jump into the code right now by downloading it from my github account at:
https://github.com/pchretien/scheduler.
ITask Interface
Before using the scheduler functionalities, we have to explore the ITask interface. ITask is an interface that you must implement for all tasks to be scheduled and executed by the Scheduler. This interface has only two methods, “setup” and “run”.
The ITask::setup() method of your task object will be called in the global setup() function of your Arduino application. You place in this function all the initialization code your task object needs to execute correctly.
The ITask::run(Scheduler*) method is the method called by the scheduler when your task object is scheduled to run. It receives a reference to the Scheduler object so that your task object can re-schedule itself or another task.
class ITask { public: virtual void setup() = 0; virtual void run(Scheduler* scheduler) = 0; };
Scheduler Singleton
The scheduler has two methods to schedule a task to run. To schedule a task to run immediately you use the queue(ITask*) method. This will add a task to the queue of tasks ready to run.
Then, to schedule a task to run in the future, you should use the schedule(ITask*, int) method. This method will set a task to run in a defined number of milliseconds in the future.
class Scheduler { public: Scheduler(); void setup(); void processMessages(); void queue(ITask* task); void clearQueue(); void schedule(ITask*, int); void clearSchedule(); private: ITask* _taskQueue[QUEUE_MAX]; ScheduleItem _taskSchedule[SCHEDULE_MAX]; };
The setup() method should be called from the main setup() function of your Arduino application. Finally, you can clear the queue and the schedule by calling the clearQueue() and clearSchedule() functions.
Task Implementation
In this example I will create a task that will turn on/off a LED every 500 milliseconds. First, you must derive your task class from the ITask interface.
class Blinker : public ITask { public: void setup(); void run(Scheduler*); Blinker(int, int); private: int _pin; int _period; int _state; };
Following is the implementation of this task …
The constructor initializes internal values. Here, you can put all the code you want to run at the time of creation of the Task object.
Blinker::Blinker(int pin, int period) { _pin = pin; _state = 0; _period = period; }
The “setup” method will be called from the setup() method of your Arduino program. In this example we only need to configure the digital pin #13 as an output.
void Blinker::setup() { pinMode( _pin, OUTPUT); }
The “run” method will be called by the scheduler when the task is ready to run. In this example, the first thing the Task does is to re-schedule itself to run in _period milliseconds. Once re-scheduled, the Task will toggle the LED connected on pin number _pin of the Arduino micro-controller.
void Blinker::run(Scheduler* scheduler) { scheduler->schedule(this, _period); _state = (_state>0)?0:1; digitalWrite(_pin, _state); }
Finally you must create the task and schedule it for the first run. In this example, the Task is created in the global scope and scheduled in the setup() method.
// Create a task scheduler singleton Scheduler __scheduler; ... // Create custom tasks Blinker _blinker12(12, 1000); ... void setup() { ... _blinker12.setup(); ... __scheduler.setup(); __scheduler.queue(&_blinker12); // or ... //__scheduler.schedule(&_blinker12, 1000); ... } void loop() { __scheduler.processMessages(); }
You can download the complete example from my Github account. In this example I drive five different Tasks in parallel. Two tasks are driving servo motors, two others are driving LEDs and the last is printing data on the serial port.
This is a work-in-progress project and will keep you informed of new developments as I go. If you try it, please leave me your impressions in the comment section below or contact me via my Github account.
March 14, 2016
Thank you very much for your wonderful code. I really impressive to your code.
By the way, with your power code may I control LED with different DELAY()? For instance:
digitalWrite(12, HIGH);
delay(2000);
digitalWrite(12, LOW);
delay(100);
Thanks beforehand,
Sovatna,
March 16, 2016
Thank you very much for your supper coding. However, how it would possibly be using to control DELAY()
For ex.
Blinker::Blinker(int pin, int state, int interval)
Blinker _blinker12(12, LOW, 50);
Blinker _blinker12(12, HIGH, 1000);
Please kindly show me the way.
Sovatna,