Skip to content

Repository files navigation

TaskManager

polling-based cooperative multi-task manager for Arduino

Feature

TaskManager is polling-based flexible task scheduler which can execute two kinds of tasks in several ways. Two kinds of tasks are:

  • task callbacks
  • task classes (the collection of begin()enter()update()exit()idle()reset())

and they can be handled like:

  • execute repeted task with framerate (optionally with N-times limit)
  • execute repeted task with interval (optionally with N-times limit)
  • execute task once after some seconds
  • control timing and behavior of tasks by name and index
  • task callbacks and task classes can be handled in the same way
  • subtask support with two kinds of mode
    • SubTaskMode::SYNC : all subtasks runs at the same time with the parent
    • SubTaskMode::SEQUENCE : subtask runs one by one in order if the current subtask stops

Task Callbacks

#include<TaskManager.h>voidsetup() {
Tasks.add([] {
Serial.println("Hello, World");
})->startFps(1); // call this function in 1[fps]// })->startFpsFor(1, 10); // call this function in 1[fps] 10 times only// })->startInterval(1); // call this function in 1[sec]// })->startIntervalFor(1, 10); // call this function in 1[sec] 10 times only// })->startOnceAfter(1); // call this function once after 1[sec]
}
voidloop() {
Tasks.update(); // automatically execute tasks
}

Timing Control with Task Name or Index

You can control how to execute tasks by using several methods. Please see APIs section for details.

#include<TaskManager.h>voidsetup() {
// just add the task in setup()
Tasks.add("MyTask", [] {
Serial.println("Hello, World");
});
}
voidloop() {
Tasks.update(); // automatically execute tasks// if "MyTask" is not running after 5000[ms],if (millis() > 5000 && !Tasks.isRunning("MyTask")) {
// start task 1[fps]
Tasks.startFps("MyTask", 1);
// or you can do it by index without the name// Tasks.startFps(0, 1);
}
}

Task Class

By using task class, you can manage more comlex task in flexible way. To use it, you need to define your own class using Task::Base class.

#include<TaskManager.h>// Your Blink classclassBlink : publicTask::Base {
bool b;
public:Blink(const String& name) : Task::Base(name) , b(false) {
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
}
virtual~Blink() {}
virtualvoidupdate() override {
digitalWrite(LED_BUILTIN, b);
b = !b;
}
};
voidsetup() {
Tasks.add<Blink>("Blink")->startFps(1); // constructor and begin() will be called
Tasks.startFps("Blink", 1); // of course you can start task by name or index
}
voidloop() {
Tasks.update(); // Blink::enter() will be called if Tasks.udpate() is called for the first time,// and Blink::update() will be automatically called every Tasks.update()// if you erase the task, Blink::exit() will be called after last Blink::update()
}

All methods you can define in your own task class is:

classMyTask : publicTask::Base
{
public:MyTask(const String& name) : Task::Base(name, fps) {}
virtual~MyTask() {}
virtualvoidbegin() override {} // optional: called once when task has createdvirtualvoidenter() override {} // optional: called once when task has startedvirtualvoidupdate() override {} // must be implemented: called every Tasks.update()virtualvoidexit() override {} // optional: called once after task has stoppedvirtualvoididle() override {} // optional: called when task isn't running (every loop)virtualvoidreset() override {} // optional
};

Task Class with Parameters

I recommend to use builder-pattern like method to set parameters to your task class.

#include<TaskManager.h>classSpeak : publicTask::Base {
int num {0};
public:Speak(const String& name)
: Base(name) {
Serial.begin(115200);
}
virtual~Speak() {}
// You can set paramters like builder pattern
Speak* number(constint n) {
num = n;
returnthis;
}
virtualvoidupdate() override {
Serial.print("Task ");
Serial.println(num);
}
};
voidsetup() {
Tasks.add<Speak>("speak")
->number(123) // you can set required parameter like this
->startFps(1);
}
voidloop() {
Tasks.update();
}

SubTasks (SubTaskMode::PARALLEL)

The default behavior of subtasks: SubTaskMode::PARALLEL runs subtasks as same as the general tasks. The only difference is the tasks are organized under the parent task. This mode is useful if you want to organize tasks for several main and sub tasks. Please use subtask() method to use PARALLEL mode.

#include<TaskManager.h>
#include"Speak.h"voidsetup() {
Serial.begin(115200);
delay(2000);
Tasks.add<Speak>("Main")
// Just iadd subtasks by subtask() method// This is completely same as usual tasks other than// it can be controlled only while parent task is running
->subtask<Speak>("Sub1", [&](TaskRef<Speak> task) {
task->number(1); // configure subtasks by lambda
task->setAutoErase(true);
})
->subtask<Speak>("Sub2", [&](TaskRef<Speak> task) {
task->number(2);
task->setAutoErase(false);
})
->subtask<Speak>("Sub3", [&](TaskRef<Speak> task) {
task->number(3);
task->setAutoErase(false);
});
Tasks["Main"]->startFps(1.);
(*Tasks["Main"])["Sub1"]->startFps(1.);
(*Tasks["Main"])["Sub2"]->startFps(1.);
(*Tasks["Main"])["Sub3"]->startFps(1.);
}
voidloop() {
Tasks.update();
}

SubTasks (SubTaskMode::SYNC)

SubTaskMode::SYNC runs subtasks synchronously with parent task. If parent task starts, subtasks also start. If parent task stops, subtasks also stop. Please use sync() method and lambda function to add/configure subtasks.

#include<TaskManager.h>// ...// Use Speak class defined above// ...voidsetup() {
Tasks.add<Speak>("Main")
->sync<Speak>("Sub1", [&](TaskRef<Speak> task) {
task->number(1); // configure subtasks by lambda
})
->sync<Speak>("Sub2", [&](TaskRef<Speak> task) {
task->number(2);
})
->sync<Speak>("Sub3", [&](TaskRef<Speak> task) {
task->number(3);
});
// Running task Main also runs all subtasks at the same time
Tasks["Main"]->startFps(1.);
}
voidloop() {
Tasks.update(); // Runs Main, Sub1, Sub2, and Sub3 at the same time
}

SubTasks (SubTaskMode::SEQUENCE)

On the other hand, SubTaskMode::SEQUENCE runs subtasks one by one if the current subtask stops. There are two way to control it. One is "Auto Run" and the other is "Manual Run". Please use then() method for both way.

Automatically run subtasks one by one

  • All subtask should have duration
  • Parent task should have longer duration than the sum of subtasks' duration
  • If the current task has finished the duration, next subtask starts running automatically
#include<TaskManager.h>// ...// Use Speak class defined above// ...voidsetup() {
Tasks.add<Speak>("Main")
// Add subtasks
->then<Speak>("Sub1", 3, [&](TaskRef<Speak> task) {
task->number(1);
})
->then<Speak>("Sub2", 3, [&](TaskRef<Speak> task) {
task->number(2);
})
->then<Speak>("Sub3", 3, [&](TaskRef<Speak> task) {
task->number(3);
});
// You can also choose whether to loopbool b_loop = true;
// Running task Main also runs first subtask
Tasks["Main"]->startFpsForSec(1., 12, b_loop);
}
voidloop() {
Tasks.update(); // Runs Sub1 -> Sub2 -> Sub3 in order if each subtask stops
}

Manually run next subtasks

  • At least one subtask should NOT have duration (the timings of subtasks should not be fixed)
  • nextSubTask() method will stop current subtask and run next subtask
#include<TaskManager.h>// ...// Use Speak class defined above// ...voidsetup() {
Tasks.add<Speak>("Main")
// Add subtasks
->then<Speak>("Sub1", [&](TaskRef<Speak> task) {
task->number(1);
})
->then<Speak>("Sub2", [&](TaskRef<Speak> task) {
task->number(2);
})
->then<Speak>("Sub3", [&](TaskRef<Speak> task) {
task->number(3);
});
// Running task Main also runs first subtask
Tasks["Main"]->startFps(1.);
}
voidloop() {
Tasks.update();
while (Serial.available()) {
char c = Serial.read();
if (c == 'n') {
// nextSubTask can run next subtask anytime if the conditions above are satisfiedbool success = Tasks["Main"]->nextSubTask();
if (success) {
Serial.println("Let's run next subtask");
} else {
Serial.println("No next task -> restart Main task");
Tasks["Main"]->restart();
}
}
}
}

Limitation for subtasks (only for NO-STL boards)

For AVR boards (e.g. Uno, Leonard, Mega, etc.), the number of subtasks is limited to 4 by default. Please define TASKMANAGER_MAX_SUBTASKS as follows to change the number of subtasks.

#defineTASKMANAGER_MAX_SUBTASKS8// define this before including TaskManager
#include<TaskManager.h>

Other Options

Enable Error Info

Error information report is disabled by default. You can enable it by defining this macro.

#defineTASKMANAGER_DEBUGLOG_ENABLE

Also you can change debug info stream by calling this macro (default: Serial).

DEBUG_LOG_ATTACH_STREAM(Serial1);

See DebugLog for details.

APIs

TaskManager

Ref<TaskEmpty> add(const Func& task);
Ref<TaskEmpty> add(const String& name, const Func& task);
Ref<TaskEmpty> add(const FuncWithTaskPtr& task);
Ref<TaskEmpty> add(const String& name, const FuncWithTaskPtr& task);
template <typename TaskType> Ref<TaskType> add();
template <typename TaskType> Ref<TaskType> add(const String& name);
voidupdate();
voidupdate(const String& name);
voidupdate(constsize_t idx);
voidreset();
boolreset(const String& name);
boolreset(constsize_t idx);
boolerase(const String& name);
boolerase(constsize_t idx);
voidclear();
boolempty() const;
size_tsize() const;
boolexists(const String& name) const;
size_tgetActiveTaskSize() const;
voidsetAutoErase(constbool b);
template <typename TaskType = Base> Ref<TaskType> getTaskByName(const String& name) const;
template <typename TaskType = Base> Ref<TaskType> getTaskByIndex(constsize_t i) const;
template <typename TaskType = Base> Ref<TaskType> operator[](const String& name) const;
template <typename TaskType = Base> Ref<TaskType> operator[](constsize_t i) const;
// ========== Task method wrappers ==========voidstart();
voidstop();
voidplay();
voidpause();
voidrestart();
voidstartFromSec(constdouble from_sec);
voidstartFromMsec(constdouble from_ms);
voidstartFromUsec(constdouble from_us);
voidstartForSec(constdouble for_sec, constbool loop = false);
voidstartForMsec(constdouble for_ms, constbool loop = false);
voidstartForUsec(constdouble for_us, constbool loop = false);
voidstartFromForSec(constdouble from_sec, constdouble for_sec, constbool loop = false);
voidstartFromForMsec(constdouble from_ms, constdouble for_ms, constbool loop = false);
voidstartFromForUsec(constdouble from_us, constdouble for_us, constbool loop = false);
voidstartFromForUsec64(constint64_t from_us, constint64_t for_us, constbool loop = false);
voidstartFromCount(constdouble from_count);
voidstartForCount(constdouble for_count, constbool loop = false);
voidstartFromForCount(constdouble from_count, constdouble for_count, constbool loop = false);
voidstartIntervalSec(constdouble interval_sec);
voidstartIntervalMsec(constdouble interval_ms);
voidstartIntervalUsec(constdouble interval_us);
voidstartIntervalFromSec(constdouble interval_sec, constdouble from_sec);
voidstartIntervalFromMsec(constdouble interval_ms, constdouble from_ms);
voidstartIntervalFromUsec(constdouble interval_us, constdouble from_us);
voidstartIntervalSecFromCount(constdouble interval_sec, constdouble from_count);
voidstartIntervalMsecFromCount(constdouble interval_ms, constdouble from_count);
voidstartIntervalUsecFromCount(constdouble interval_us, constdouble from_count);
voidstartIntervalForSec(constdouble interval_sec, constdouble for_sec, constbool loop = false);
voidstartIntervalForMsec(constdouble interval_ms, constdouble for_ms, constbool loop = false);
voidstartIntervalForUsec(constdouble interval_us, constdouble for_us, constbool loop = false);
voidstartIntervalSecForCount(constdouble interval_sec, constdouble for_count, constbool loop = false);
voidstartIntervalMsecForCount(constdouble interval_ms, constdouble for_count, constbool loop = false);
voidstartIntervalUsecForCount(constdouble interval_us, constdouble for_count, constbool loop = false);
voidstartIntervalFromForSec(constdouble interval_sec, constdouble from_sec, constdouble for_sec, constbool loop = false);
voidstartIntervalFromForMsec(constdouble interval_ms, constdouble from_ms, constdouble for_ms, constbool loop = false);
voidstartIntervalFromForUsec(constdouble interval_us, constdouble from_us, constdouble for_us, constbool loop = false);
voidstartIntervalSecFromForCount(constdouble interval_sec, constdouble from_count, constdouble for_count, constbool loop = false);
voidstartIntervalMsecFromForCount(constdouble interval_ms, constdouble from_count, constdouble for_count, constbool loop = false);
voidstartIntervalUsecFromForCount(constdouble interval_us, constdouble from_count, constdouble for_count, constbool loop = false);
voidstartFromFrame(constdouble from_frame);
voidstartForFrame(constdouble for_frame, constbool loop = false);
voidstartFromForFrame(constdouble from_frame, constdouble for_frame, constbool loop = false);
voidstartFps(constdouble fps);
voidstartFpsFromSec(constdouble fps, constdouble from_sec);
voidstartFpsFromMsec(constdouble fps, constdouble from_ms);
voidstartFpsFromUsec(constdouble fps, constdouble from_us);
voidstartFpsFromFrame(constdouble fps, constdouble from_frame);
voidstartFpsForSec(constdouble fps, constdouble for_sec, constbool loop = false);
voidstartFpsForMsec(constdouble fps, constdouble for_ms, constbool loop = false);
voidstartFpsForUsec(constdouble fps, constdouble for_us, constbool loop = false);
voidstartFpsForFrame(constdouble fps, constdouble for_frame, constbool loop = false);
voidstartFpsFromForSec(constdouble fps, constdouble from_sec, constdouble for_sec, constbool loop = false);
voidstartFpsFromForMsec(constdouble fps, constdouble from_ms, constdouble for_ms, constbool loop = false);
voidstartFpsFromForUsec(constdouble fps, constdouble from_us, constdouble for_us, constbool loop = false);
voidstartFpsFromForFrame(constdouble fps, constdouble from_frame, constdouble for_frame, constbool loop = false);
voidstartOnce();
voidstartOnceAfterSec(constdouble after_sec);
voidstartOnceAfterMsec(constdouble after_ms);
voidstartOnceAfterUsec(constdouble after_us);
voidsetOffsetSec(constdouble sec);
voidsetOffsetMsec(constdouble ms);
voidsetOffsetUsec(constdouble us);
voidsetOffsetUsec64(constint64_t us);
voidaddOffsetSec(constdouble sec);
voidaddOffsetMsec(constdouble ms);
voidaddOffsetUsec(constdouble us);
voidaddOffsetUsec64(constint64_t us);
voidsetDurationSec(constdouble sec);
voidsetDurationMsec(constdouble ms);
voidsetDurationUsec(constdouble us);
voidsetDurationUsec64(constint64_t us);
voidsetTimeSec(constdouble sec);
voidsetTimeMsec(constdouble ms);
voidsetTimeUsec(constdouble us);
voidsetTimeUsec64(constint64_t us);
voidsetIntervalSec(constdouble sec);
voidsetIntervalMsec(constdouble ms);
voidsetIntervalUsec(constdouble us);
voidsetIntervalUsec64(constint64_t us);
voidsetLoop(constbool b);
voidsetOffsetCount(constdouble count);
voidsetOffsetFrame(constdouble frame);
voidsetFrameRate(constfloat fps);

Task::Base

This class inherits FrameRateCounter. Please refer the link for available inherited methods.

boolhasEnter() const {
boolhasExit() const {
Base* setAutoErase(constbool b) {
boolisAutoErase() const {
const String& getName() const {
// =========== SubTask Creation ==========template <typename TaskType> Base* subtask(const std::function<void(Ref<TaskType>)>& setup);
template <typename TaskType> Base* subtask(const String& name, const std::function<void(Ref<TaskType>)>& setup);
template <typename TaskType> Base* sync(const std::function<void(Ref<TaskType>)>& setup);
template <typename TaskType> Base* sync(const String& name, const std::function<void(Ref<TaskType>)>& setup);
template <typename TaskType> Base* then(const std::function<void(Ref<TaskType>)>& setup);
template <typename TaskType> Base* then(const String& name, const std::function<void(Ref<TaskType>)>& setup);
template <typename TaskType> Base* then(constdouble sec, const std::function<void(Ref<TaskType>)>& setup);
template <typename TaskType> Base* then(const String& name, constdouble sec, const std::function<void(Ref<TaskType>)>& setup);
Base* hold(constdouble sec);
// =========== SubTask Utility ==========
Vec<Ref<Base>>& getSubTasks();
const Vec<Ref<Base>>& getSubTasks() const;
Base* setSubTaskIndex(constsize_t i);
size_tgetSubTaskIndex() const;
Base* setSubTaskMode(const SubTaskMode m);
SubTaskMode getSubTaskMode() const;
boolhasSubTasks() const;
size_tnumSubTasks() const;
boolexistsSubTask(const String& name) const;
template <typename TaskType = Base> Ref<TaskType> getSubTaskByName(const String& name) const;
template <typename TaskType = Base> Ref<TaskType> getSubTaskByIndex(constsize_t i) const;
template <typename TaskType = Base> Ref<TaskType> operator[](const String& name) const;
template <typename TaskType = Base> Ref<TaskType> operator[](constsize_t i) const;
// ========== only for SubTaskMode::SEQUENCE ==========boolnextSubTask();

Types

enumclassSubTaskMode : uint8_t {
NA,
PARALLEL,
SYNC,
SEQUENCE
};

Dependent Libraries

Embedded Libraries

License

MIT

About

polling-based cooperative multi-task manager for Arduino

Resources

Stars

36 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages