From 85f6d7b0661035dbba835502129d487335872491 Mon Sep 17 00:00:00 2001
From: Anatoli Arkhipenko
cooperative multitasking for Arduino microcontrollers
-Version 1.8.3: 2015-11-05
+Version 1.8.4: 2015-11-17
OVERVIEW:
@@ -122,7 +122,7 @@ than the Scheduler's execute() method).Below is the flowchart of a Task lifecycle:
-

TaskScheduler @@ -297,6 +297,611 @@ enabled by placing appropriate #define statements in front of the
TASK PRIORITY AND +COOPERATIVE MULTITASKING:
+TaskScheduler does +not support task priority functionality. I have been thinking a +lot about it (especially since Symbian's Active Objects, which +TaskScheduler is inspired by, support it), but decided against it. +
+
+
This is why:
+
+
+
+
1. Execution chains are +simple and efficient. The main idea is to minimize scheduling +overhead by Scheduler going through the chain. Implementing true +priority would require looking ahead through the entire chain, +ranking and aging tasks by priorities. It would slow the execute() +loop significantly.
+
+
+
+
2. TaskScheduler is NOT +a pre-emptive multi-tasking library. Nor is it a Real-Time OS. There +is no way to break execution of one task in favor of another. +Therefore callback methods require careful programming for +cooperative behavior.
+This has, however, +significant benefits: you don't need to worry about concurrency +inside the callback method, since only one callback method runs at a +time, and could not be interrupted. All resources are yours for that +period of time, noone can switch the value of variables (except +interrupt functions of course...), etc. It is a stable and +predictable environment, and it helps a lot with writing stable code. +
+
+
+
+
A number of things +could be done instead of priorities:
+
+
+
+
1. Schedule your +critical tasks to run more frequently than the other tasks
+.
(Since you can
+control the interval, you could also change the task to run more or
+less frequently as the situation demands).
+
2. If one particular +callback routine is critical, create a couple of tasks referring to +the same callback and "sprinkle" them around the chain:
+
+
+
+
Scheduler +ts;
+
+
Task +t1(20, TASK_FOREVER, &callback1, &ts);
+
+
Task +t2(1000, TASK_FOREVER, &callback2, &ts);
+
+
Task +t3(20, TASK_FOREVER, &callback1, &ts);
+
+
Task +t4(1000, TASK_FOREVER, &callback4, &ts);
+
+
t3.delay(10);
+
+
+
+
Note that t1 and t3 +call the same callback method, and are shifted in time by 10 millis. +So effectively callback1 will be called every 10 millis, but would be +"sandwiched" between t2 and t4. +
+
+
+
+
+
3. Use short efficient +callback methods written for cooperative multitasking.
+
+
+
What that means is:
+
+
+
+
a) +DO NOT use Arduino's delay() +function. It is blocking and will hold the entire chain. +Instead break the callback method into two, switch the callback +method of the task where delay is necessary and delay the task by +that number of millis. You get your delay, and other tasks get a +chance to run:
+
+
+
+
instead +of:
+
+
+
+
+ void +callback() {
+
+
+ ... +stuff
+
+
+ delay(1000);
+
+
+ ... +more stuff
+
+
+ }
+
+
+ +
+
+
do +this:
+
+
+
+
+ void +callback1() {
+
+
+ ... +stuff
+
+
+ t1.setCallback(&callback2);
+
+
+ t1.delay(1000);
+
+
+ }
+
+
+
+ void +callback2() {
+
+
+ ... +more stuff
+
+
+ t1.setCallback(&callback1);
+
+
+ }
+
+
+
+
+
+
b) +Same goes to pulseIn() +function. If you have to use it, set the timeout parameter such that +it is not a default 1 second. PulseIn functionality could be achieved +via pin interrupts, and that solution is non-blocking.
+
+
+
+
c) +Do don run long loops (for or do/while) in you callback methods. Make +the main arduino loop be the loop driver for you:
+
+
+
+
instead +of:
+
+
+
+
+ void +callback() {
+
+
+ +
+
+
+ for(int +i=0; i<1000; i++) {
+
+
+ ... +stuff // one loop action
+
+
+ }
+
+
+ }
+
+
+
+
do +this:
+
+
+
+
+ Task +t1(TASK_IMMEDIATE, 1000, &callback);
+
+
+ +
+
+
+ void +callback() {
+
+
+ int +i = t1.getRunCounter() -1;
+
+
+ ... +stuff // one loop action
+
+
+ }
+
+
+
+
or +this:
+
+
+
+
+ Task +t1(TASK_IMMEDIATE, 1000, &callback, true, &t1On);
+
+
+ +
+
+
+ int +i;
+
+
+ bool +t1On() {
+
+
+ i += 0;
+
+
+ return +true;
+
+
+ }
+
+
+ +
+
+
+ void +callback() {
+
+
+ ... +stuff // one loop action
+
+
+ i++;
+
+
+ }
+
+
+
+
+
+REMEMBER: you are already inside the loop - take advantage of it. +
+
+
d) +Break long running callback methods into several shorter ones, and +pass control from one to the other via setCallback() +method:
+
+
+
+
+ Task +t1(TASK_IMMEDIATE, TASK_FAREVER, &callback);
+
+
+ +
+
+
+ void +callback() {
+
+
+ ... +do some stuff
+
+
+ t1.setCallback(&callback_step2);
+
+
+ }
+
+
+ void +callback_step2() {
+
+
+ ... +do more stuff
+
+
+ t1.setCallback(&callback_step3);
+
+
+ }
+
+
+ void +callback_step3() {
+
+
+ ... +do last part of the stuff
+
+
+ t1.setCallback(&callback);
+
+
+ t1.delay(1000);
+
+
+ }
+
+
+
+
This +will execute all parts of the callback function in three successive +steps, sheduled immediately, but allowing other tasks in the cahin to +run. Notince that task is scheduled to run immediately, and 1 second +period is achieved by delaying the task for 1000 millis at the last +step. +
+
+
Alternatively +you could schedule the task to run every 1000 millis and use +forceNextIteration() +method in steps 1 and 2 (but not 3!)
+
+
+
+
+ Task +t1(1000, TASK_FOREVER, &callback);
+
+
+ +
+
+
+ void +callback() {
+
+
+ ... +do some stuff
+
+
+ t1.setCallback(&callback_step2);
+
+
+ t1.forceNextIteration();
+
+
+ }
+
+
+ void +callback_step2() {
+
+
+ ... +do more stuff
+
+
+ t1.setCallback(&callback_step3);
+
+
+ t1.forceNextIteration();
+
+
+ }
+
+
+ void +callback_step3() {
+
+
+ ... +do last part of the stuff
+
+
+ t1.setCallback(&callback);
+
+
+ }
+
+
+
+
+
+
e) +Compile the library with _TASK_TIMECRITICAL +enabled and check if your tasks are falling behind schedule. If they +are - you need to optimize your code further (or maybe re-evaluate +your schedule). If they are not - all is well and you don't need to +do anything. E.g., I have a spider robot which needs to measure +distance, control motors, and keep track of the angle via querying +gyroscope and accelerometer every 10 ms. The idea was to flash +onboard LED if any of the tasks fall behind. At 10 ms interval for +the gyro the LED does not flash, which means none of the tasks are +blocking the others from starting on time. +
+
+
+
@@ -502,9 +1107,8 @@ is a task which was enabled and requires execution.
NOTE: -if task being enabled is -not assigned to a scheduler and is not part of execution chain, then -task will not be enabled.
+if task being enabled is not assigned to a scheduler and is not +part of execution chain, then task will not be enabled.
NOTE: @@ -513,8 +1117,9 @@ task will not be enabled.
must return a value of true for task to be enabled. If OnEnable returns false, task remains disabled. OnEnable is invoked every time enable is called, -regardless if task is already enabled or not. - +regardless if task is already enabled or not. Alignment to current +millis() is performed after OnEnable exits, so any changes to +the interval inside OnEnable is taken into consideration.
NOTE: @@ -703,8 +1308,7 @@ and number of iterations. default waitForDelayed() sets tasks interval to a supplied value or (if omitted or zero) keeps the current interval, so delayed execution will take place when the event happens. It also sets the -number of iterations to 1 by -default if not supplied. +number of iterations to 1 by default if not supplied.
When Status Request object completes, all tasks waiting on it are executed @@ -972,10 +1576,9 @@ statements after execute inside the loop()
bool isOverrun()
If library is compiled with _TASK_TIMECRITICAL -enabled, this method returns true -if currently invoked task has overrun its scheduled start time when -it was invoked. Returns false -if task has been invoked according to schedule.
+enabled, this method returns true if currently invoked task +has overrun its scheduled start time when it was invoked. Returns +false if task has been invoked according to schedule.
@@ -2145,5 +2748,8 @@ time examples of TaskScheduler are available here: