aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnatoli Arkhipenko <arkhipenko@hotmail.com>2015-11-14 17:46:16 -0500
committerAnatoli Arkhipenko <arkhipenko@hotmail.com>2015-11-14 17:46:16 -0500
commitdc152db48e325a5d03be0d48a07c119d97d11e59 (patch)
tree72fdcd4c9feee8da743e9540ce58580e7392a7d9
parentef2329bdeade723f560cd0f7e933c9f3119677f3 (diff)
Version 1.8.3 number of addtions and improvementsv1.8.3
* support for task activation on a status request with arbitrary interval and number of iterations (0 and 1 are still default values) * implement waitForDelayed() method to allow task activation on the status request completion delayed for one current interval * added callback methods prototypes to all examples for Arduino IDE 1.6.6 compatibility * added several constants to be used as task parameters for readability (e.g, TASK_FOREVER, TASK_SECOND, etc.) * significant optimization of the scheduler's execute loop, including millis() rollover fix option
-rw-r--r--README12
-rw-r--r--examples/Scheduler_example/Scheduler_example.ino29
-rw-r--r--examples/Scheduler_example2/Scheduler_example2.ino21
-rw-r--r--examples/Scheduler_example3/Scheduler_example3.ino17
-rw-r--r--examples/Scheduler_example4_StatusRequest/Scheduler_example4_StatusRequest.ino12
-rw-r--r--examples/Scheduler_example5_StatusRequest/Scheduler_example5_StatusRequest.ino27
-rw-r--r--examples/Scheduler_example6_IDLE/Scheduler_example6_IDLE.ino11
-rw-r--r--examples/Scheduler_example7_WDT/Scheduler_example7_WDT.ino14
-rw-r--r--examples/Scheduler_example8_LTS/Scheduler_example8_LTS.ino15
-rw-r--r--extras/TaskScheduler.docbin172544 -> 178688 bytes
-rw-r--r--extras/TaskScheduler.html194
-rw-r--r--keywords.txt14
-rw-r--r--library.properties2
-rw-r--r--src/TaskScheduler.h213
14 files changed, 394 insertions, 187 deletions
diff --git a/README b/README
index 1a8139b..da0db04 100644
--- a/README
+++ b/README
@@ -1,7 +1,6 @@
Task Scheduler – cooperative multitasking for Arduino microcontrollers
-Version 1.8.2: 2015-10-29
-
-
+Version 1.8.3: 2015-11-05
+
OVERVIEW:
A lightweight implementation of cooperative multitasking (task scheduling) supporting:
1. Periodic task execution (with dynamic execution period in milliseconds)
@@ -62,3 +61,10 @@ v1.8.2:
2015-10-27 - protection against infinite loop in OnEnable (if enable() methods are called within OnEnable)
2015-10-29 - new currentLts() method in the scheduler class returns current task's LTS pointer in one call
+v1.8.3:
+ 2015-11-05 - support for task activation on a status request with arbitrary interval and number of iterations (0 and 1 are still default values)
+ 2015-11-05 - implement waitForDelayed() method to allow task activation on the status request completion delayed for one current interval
+ 2015-11-09 - added callback methods prototypes to all examples for Arduino IDE 1.6.6 compatibility
+ 2015-11-14 - added several constants to be used as task parameters for readability (e.g, TASK_FOREVER, TASK_SECOND, etc.)
+ 2015-11-14 - significant optimization of the scheduler's execute loop, including millis() rollover fix option
+
diff --git a/examples/Scheduler_example/Scheduler_example.ino b/examples/Scheduler_example/Scheduler_example.ino
index 74b60f9..2175e57 100644
--- a/examples/Scheduler_example/Scheduler_example.ino
+++ b/examples/Scheduler_example/Scheduler_example.ino
@@ -13,10 +13,16 @@
#include <TaskScheduler.h>
+// Callback methods prototypes
+void t1Callback();
+void t2Callback();
+void t3Callback();
+
+//Tasks
Task t4();
Task t1(2000, 10, &t1Callback);
-Task t2(3000, -1, &t2Callback);
-Task t3(5000, -1, &t3Callback);
+Task t2(3000, TASK_FOREVER, &t2Callback);
+Task t3(5000, TASK_FOREVER, &t3Callback);
Scheduler runner;
@@ -26,8 +32,8 @@ void t1Callback() {
Serial.println(millis());
if (t1.isFirstIteration()) {
- t3.enable();
- runner.addTask(t3);
+ runner.addTask(t3);
+ t3.enable();
Serial.println("t1: enabled t3 and added to the chain");
}
@@ -55,12 +61,6 @@ void setup () {
Serial.begin(115200);
Serial.println("Scheduler TEST");
-
- t1.enable();
- Serial.println("Enabled t1");
- t2.enable();
- Serial.println("Enabled t2");
-
runner.init();
Serial.println("Initialized scheduler");
@@ -69,11 +69,16 @@ void setup () {
runner.addTask(t2);
Serial.println("added t2");
-
+
delay(5000);
+
+ t1.enable();
+ Serial.println("Enabled t1");
+ t2.enable();
+ Serial.println("Enabled t2");
}
void loop () {
runner.execute();
-}
+} \ No newline at end of file
diff --git a/examples/Scheduler_example2/Scheduler_example2.ino b/examples/Scheduler_example2/Scheduler_example2.ino
index 0e7f9a9..c76551a 100644
--- a/examples/Scheduler_example2/Scheduler_example2.ino
+++ b/examples/Scheduler_example2/Scheduler_example2.ino
@@ -2,11 +2,16 @@
#include <TaskScheduler.h>
Scheduler runner;
+// Callback methods prototypes
+void t1Callback();
+void t2Callback();
+void t3Callback();
+// Tasks
Task t4();
Task t1(2000, 10, &t1Callback, &runner, true); //adding task to the chain on creation
-Task t2(3000, -1, &t2Callback, &runner, true); //adding task to the chain on creation
-Task t3(5000, -1, &t3Callback);
+Task t2(3000, TASK_FOREVER, &t2Callback, &runner, true); //adding task to the chain on creation
+Task t3(5000, TASK_FOREVER, &t3Callback);
// Test
// Initially only tasks 1 and 2 are enabled
@@ -18,6 +23,8 @@ Task t3(5000, -1, &t3Callback);
// Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds
// Because Task2 interval is shorter than Scheduler default tick, loop() executes ecery 1/2 seconds now
// At the end Task2 is the only task running every 1/2 seconds
+//
+// NOTE that t1 and t2 are affected by the delay() function in the setup() method and are scheduled immediately twice to "catch up" with millis().
@@ -28,8 +35,8 @@ void t1Callback() {
Serial.println(millis());
if (t1.isFirstIteration()) {
- t3.enable();
- runner.addTask(t3);
+ runner.addTask(t3);
+ t3.enable();
Serial.println("t1: enabled t3 and added to the chain");
}
@@ -65,6 +72,6 @@ void loop () {
runner.execute();
- Serial.println("Loop ticks at: ");
- Serial.println(millis());
-}
+// Serial.println("Loop ticks at: ");
+// Serial.println(millis());
+} \ No newline at end of file
diff --git a/examples/Scheduler_example3/Scheduler_example3.ino b/examples/Scheduler_example3/Scheduler_example3.ino
index 7111ac3..115f7bc 100644
--- a/examples/Scheduler_example3/Scheduler_example3.ino
+++ b/examples/Scheduler_example3/Scheduler_example3.ino
@@ -1,7 +1,7 @@
/**
* TaskScheduler Test of OnEnable and OnDisable methods and illustration of using wrapper tasks for timout purposes
*
- * A wrapper task runs every 30 seconds and initiates the test case
+ * A wrapper task runs every 10 seconds and initiates the test case
* Another task is run once for 5 seconds, and serves as a LED blinking timeout - 5 seconds
* Finally, a dedicated task which controls LED is running periodically until stopped, and makes the LED blink with 0.5 to 1 second interval.
*
@@ -14,9 +14,17 @@
Scheduler ts;
-Task tWrapper(30000L, -1, &WrapperCallback, &ts, true);
-Task tBlink(5000, 1, NULL, &ts, false, &BlinkOnEnable, &BlinkOnDisable);
-Task tLED(0, -1, NULL, &ts, false, NULL, &LEDOff);
+// Callback methods prototypes
+void WrapperCallback();
+bool BlinkOnEnable();
+void BlinkOnDisable();
+void LEDOn();
+void LEDOff();
+
+// Tasks
+Task tWrapper(10000L, TASK_FOREVER, &WrapperCallback, &ts, true);
+Task tBlink(5000, TASK_ONCE, NULL, &ts, false, &BlinkOnEnable, &BlinkOnDisable);
+Task tLED(0, TASK_FOREVER, NULL, &ts, false, NULL, &LEDOff);
void WrapperCallback() {
tBlink.restartDelayed(); // LED blinking is initiated
@@ -61,6 +69,7 @@ void LEDOff () {
void setup() {
// put your setup code here, to run once:
+ pinMode(LEDPIN, OUTPUT);
}
void loop() {
diff --git a/examples/Scheduler_example4_StatusRequest/Scheduler_example4_StatusRequest.ino b/examples/Scheduler_example4_StatusRequest/Scheduler_example4_StatusRequest.ino
index 080de05..22f1dbc 100644
--- a/examples/Scheduler_example4_StatusRequest/Scheduler_example4_StatusRequest.ino
+++ b/examples/Scheduler_example4_StatusRequest/Scheduler_example4_StatusRequest.ino
@@ -13,7 +13,15 @@ StatusRequest st;
Scheduler ts;
-Task t1(5000, 1, &Callback1, &ts, true, NULL, &Disable1);
+// Callback methods prototypes
+void Callback1();
+void Disable1();
+void Callback2();
+void Callback3();
+void PrepareStatus();
+
+// Tasks
+Task t1(5000, TASK_ONCE, &Callback1, &ts, true, NULL, &Disable1);
Task t2(&Callback2, &ts);
Task t3(&Callback3, &ts);
@@ -77,4 +85,4 @@ void loop() {
ts.execute();
-}
+} \ No newline at end of file
diff --git a/examples/Scheduler_example5_StatusRequest/Scheduler_example5_StatusRequest.ino b/examples/Scheduler_example5_StatusRequest/Scheduler_example5_StatusRequest.ino
index d1f2e5c..ff2d850 100644
--- a/examples/Scheduler_example5_StatusRequest/Scheduler_example5_StatusRequest.ino
+++ b/examples/Scheduler_example5_StatusRequest/Scheduler_example5_StatusRequest.ino
@@ -7,22 +7,29 @@
#define _TASK_SLEEP_ON_IDLE_RUN
#define _TASK_STATUS_REQUEST
-
#include <TaskScheduler.h>
-
-
-
StatusRequest measure;
Scheduler ts;
-Task tCycle(10000, -1, &CycleCallback, &ts, true);
-Task tMeasure(1000, 1, &MeasureCallback, &ts, false, &MeasureEnable, &MeasureDisable);
+// Callback methods prototypes
+void CycleCallback();
+void MeasureCallback();
+bool MeasureEnable();
+void MeasureDisable();
+void CalcCallback();
+void S1Callback(); bool S1Enable();
+void S2Callback(); bool S2Enable();
+void S3Callback(); bool S3Enable();
+
+// Tasks
+Task tCycle(10000, TASK_FOREVER, &CycleCallback, &ts, true);
+Task tMeasure(1000, TASK_ONCE, &MeasureCallback, &ts, false, &MeasureEnable, &MeasureDisable);
Task tCalculate(&CalcCallback, &ts);
-Task tSensor1(0, 1, &S1Callback, &ts, false, &S1Enable);
-Task tSensor2(0, 1, &S2Callback, &ts, false, &S2Enable);
-Task tSensor3(0, 1, &S3Callback, &ts, false, &S3Enable);
+Task tSensor1(0, TASK_ONCE, &S1Callback, &ts, false, &S1Enable);
+Task tSensor2(0, TASK_ONCE, &S2Callback, &ts, false, &S2Enable);
+Task tSensor3(0, TASK_ONCE, &S3Callback, &ts, false, &S3Enable);
long distance, d1, d2, d3;
@@ -162,4 +169,4 @@ void loop() {
ts.execute();
-}
+} \ No newline at end of file
diff --git a/examples/Scheduler_example6_IDLE/Scheduler_example6_IDLE.ino b/examples/Scheduler_example6_IDLE/Scheduler_example6_IDLE.ino
index f4df5d8..52f14c0 100644
--- a/examples/Scheduler_example6_IDLE/Scheduler_example6_IDLE.ino
+++ b/examples/Scheduler_example6_IDLE/Scheduler_example6_IDLE.ino
@@ -38,8 +38,13 @@ C1 is scenario 2) is much higher than in scenario 1) because processor is put to
Scheduler ts;
-Task c(10, -1, &Count, &ts);
-Task t(10000, 1, NULL, &ts, true, &tOn, &tOff);
+// Callback methods prototypes
+void Count();
+bool tOn(); void tOff();
+
+// Tasks
+Task c(10, TASK_FOREVER, &Count, &ts);
+Task t(10000, TASK_ONCE, NULL, &ts, true, &tOn, &tOff);
volatile unsigned long c1, c2;
@@ -74,4 +79,4 @@ void loop() {
// put your main code here, to run repeatedly:
ts.execute();
c1++;
-}
+} \ No newline at end of file
diff --git a/examples/Scheduler_example7_WDT/Scheduler_example7_WDT.ino b/examples/Scheduler_example7_WDT/Scheduler_example7_WDT.ino
index ab880d2..260209f 100644
--- a/examples/Scheduler_example7_WDT/Scheduler_example7_WDT.ino
+++ b/examples/Scheduler_example7_WDT/Scheduler_example7_WDT.ino
@@ -18,14 +18,18 @@
Scheduler ts;
+// Callback methods prototypes
+void TaskCB();
+void HB(); bool HBOn(); void HBOff();
+
// Three tasks emulating accidental infinite loop
-Task tTask1(1000, -1, &TaskCB, &ts, true);
-Task tTask2(1000, -1, &TaskCB, &ts, true);
-Task tTask3(1000, -1, &TaskCB, &ts, true);
+Task tTask1(TASK_SECOND, TASK_FOREVER, &TaskCB, &ts, true);
+Task tTask2(TASK_SECOND, TASK_FOREVER, &TaskCB, &ts, true);
+Task tTask3(TASK_SECOND, TASK_FOREVER, &TaskCB, &ts, true);
// Heartbeat task - resetting the watchdog timer periodically
// Initiates WDT on enable, and deactivates it on disable
-Task tHB(500, -1, &HB, &ts, false, &HBOn, &HBOff);
+Task tHB(500, TASK_FOREVER, &HB, &ts, false, &HBOn, &HBOff);
/**
* Emulating task callback function
@@ -125,4 +129,4 @@ void setup() {
*/
void loop() {
ts.execute();
-}
+} \ No newline at end of file
diff --git a/examples/Scheduler_example8_LTS/Scheduler_example8_LTS.ino b/examples/Scheduler_example8_LTS/Scheduler_example8_LTS.ino
index 238617e..8d8df06 100644
--- a/examples/Scheduler_example8_LTS/Scheduler_example8_LTS.ino
+++ b/examples/Scheduler_example8_LTS/Scheduler_example8_LTS.ino
@@ -23,16 +23,21 @@
Scheduler ts;
+// Callback methods prototypes
+void Calculate(); bool CalcOn();
+bool WrapperOn(); void WrapperOff();
+
+// Tasks
// Calculator tasks.
// Note that all three tasks use the same callback methods
// They will be updating specific variables based on the
// Locat Task Storage pointers
-Task t1(1000, -1, &Calculate, &ts, false, &CalcOn);
-Task t2(1000, -1, &Calculate, &ts, false, &CalcOn);
-Task t3(1000, -1, &Calculate, &ts, false, &CalcOn);
+Task t1(TASK_SECOND, TASK_FOREVER, &Calculate, &ts, false, &CalcOn);
+Task t2(TASK_SECOND, TASK_FOREVER, &Calculate, &ts, false, &CalcOn);
+Task t3(TASK_SECOND, TASK_FOREVER, &Calculate, &ts, false, &CalcOn);
// add more calc tasks here if necessary
-Task tWrapper(5000, 1, NULL, &ts, false, &WrapperOn, &WrapperOff);
+Task tWrapper(5*TASK_SECOND, TASK_ONCE, NULL, &ts, false, &WrapperOn, &WrapperOff);
// The below structure is an object referenced by LTS pointer
typedef struct {
@@ -139,4 +144,4 @@ void setup() {
void loop() {
ts.execute();
-}
+} \ No newline at end of file
diff --git a/extras/TaskScheduler.doc b/extras/TaskScheduler.doc
index 9995a43..85331e7 100644
--- a/extras/TaskScheduler.doc
+++ b/extras/TaskScheduler.doc
Binary files differ
diff --git a/extras/TaskScheduler.html b/extras/TaskScheduler.html
index 651a927..8d83085 100644
--- a/extras/TaskScheduler.html
+++ b/extras/TaskScheduler.html
@@ -6,7 +6,7 @@
<META NAME="GENERATOR" CONTENT="LibreOffice 3.5 (Linux)">
<META NAME="CREATED" CONTENT="20150206;16300000">
<META NAME="CHANGEDBY" CONTENT="Anatoli Arkhipenko">
- <META NAME="CHANGED" CONTENT="20151029;23230000">
+ <META NAME="CHANGED" CONTENT="20151105;22050000">
<META NAME="Info 1" CONTENT="">
<META NAME="Info 2" CONTENT="">
<META NAME="Info 3" CONTENT="">
@@ -15,8 +15,8 @@
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in; direction: ltr; color: #000000; widows: 0; orphans: 0 }
- P.western { font-family: "Liberation Serif", "MS PMincho", serif; font-size: 12pt; so-language: en-US }
- P.cjk { font-family: "WenQuanYi Micro Hei", "MS Mincho"; font-size: 12pt }
+ P.western { font-family: "Liberation Serif", "Times New Roman", serif; font-size: 12pt; so-language: en-US }
+ P.cjk { font-family: "WenQuanYi Micro Hei", "MS Mincho"; font-size: 12pt; so-language: zh-CN }
P.ctl { font-family: "Lohit Hindi", "MS Mincho"; font-size: 12pt; so-language: hi-IN }
A:link { color: #0000ff }
-->
@@ -28,7 +28,7 @@ Scheduler</B></FONT></P>
<P CLASS="western" STYLE="margin-bottom: 0in"><B>cooperative
multitasking for Arduino microcontrollers</B></P>
<P CLASS="western" STYLE="margin-bottom: 0in; border-top: none; border-bottom: 1px solid #000000; border-left: none; border-right: none; padding-top: 0in; padding-bottom: 0.01in; padding-left: 0in; padding-right: 0in">
-<FONT SIZE=2 STYLE="font-size: 11pt"><B>Version 1.8.2: 2015-10-27</B></FONT></P>
+<FONT SIZE=2 STYLE="font-size: 11pt"><B>Version 1.8.3: 2015-11-05</B></FONT></P>
<P CLASS="western" STYLE="margin-bottom: 0in"><BR>
</P>
<P CLASS="western" STYLE="margin-bottom: 0in"><B>OVERVIEW</B>:</P>
@@ -122,7 +122,7 @@ than the Scheduler's <B>execute</B>() method).</P>
</P>
<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
<B>Below is the flowchart of a Task lifecycle:</B></P>
-<P CLASS="western" STYLE="margin-bottom: 0in"><IMG SRC="TaskScheduler_html.png" NAME="graphics1" ALIGN=BOTTOM WIDTH=664 HEIGHT=690 BORDER=0></P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><IMG SRC="TaskScheduler_html_m68472eb8.png" NAME="graphics1" ALIGN=BOTTOM WIDTH=664 HEIGHT=655 BORDER=0></P>
<P CLASS="western" STYLE="margin-bottom: 0in"><BR>
</P>
<P CLASS="western" STYLE="margin-bottom: 0in"><B>TaskScheduler</B>
@@ -276,6 +276,19 @@ LTS pointer to the appropriate pointer type.
</P>
<P CLASS="western" STYLE="margin-bottom: 0in"><BR>
</P>
+<P CLASS="western" STYLE="margin-bottom: 0in">#define
+<B>_TASK_ROLLOVER_FIX</B></P>
+<P CLASS="western" STYLE="margin-bottom: 0in">…will compile
+TaskScheduler with support for millis() rollover approximately every
+47 days. Only required if you plan for your sketch to run for
+extended amount of time (over 47 days).</P>
+<P CLASS="western" STYLE="margin-bottom: 0in">The problem here is
+when scheduler calculates next execution time for a task, a rollover
+may place next execution time “in the past” and cause task to be
+invoked incorrectly once every 47 days.
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><BR>
+</P>
<P CLASS="western" STYLE="margin-bottom: 0in"><B>NOTE: </B>above
parameters are<B> DISABLED </B>by default, and need to be explicitly
enabled by placing appropriate #define statements in front of the
@@ -488,7 +501,13 @@ is a task which was enabled and requires execution.
</P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
</P>
-<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><B>Note:
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><B>NOTE:
+</B><SPAN STYLE="font-weight: normal">if task</SPAN> being enabled is
+not assigned to a scheduler and is not part of execution chain, then
+task <B>will not</B> be enabled.</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><B>NOTE:
</B>enable() invokes task’s <B>OnEnable</B> method (if not NULL)
<B>immediately</B>, which can prepare task for execution. <B>OnEnable</B>
must return a value of <B>true</B> for task to be enabled. If
@@ -662,17 +681,38 @@ METHODS:</B></P>
<BR>
</P>
<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
-<B>void waitFor(StatusRequest* aStatusRequest);</B></P>
+<B>void waitFor(StatusRequest* aStatusRequest, unsigned long
+aInterval = 0, long aIterations = 1)</B></P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
+<B>void waitForDelayed(StatusRequest* aStatusRequest, unsigned long
+aInterval = 0, long aIterations = 1)</B></P>
<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
<BR>
</P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">If
-compiled with support for Status Requests, this method makes task
+compiled with support for Status Requests, these methods make task
wait for the completion of <B>aStatusRequest</B> event.
</P>
-<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><B>waitFor()
-</B>sets tasks interval to <B>0 (zero)</B> for immediate execution
-when event happens, and also sets the number of <B>iterations to 1</B>.</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">By
+default <B>waitFor() </B>sets tasks interval to <B>0 (zero)</B> for
+immediate execution when event happens, and also sets the number of
+<B>iterations to 1</B>. However, you can specify different interval
+and number of iterations.
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">By
+default <B>waitForDelayed() </B>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 <B>iterations to 1</B><SPAN STYLE="font-weight: normal"> by
+default if not supplied</SPAN>.
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">When
+Status Request object completes, all tasks waiting on it are executed
+during next scheduling pass. Tasks waiting via <B>waitFor</B>()
+method are executed immediately. Tasks waiting via <B>waitForDelayed</B>()
+method are activated, but executed after current or supplied interval
+delay.
+</P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><B>Note:
aStatusRequest</B> should be “activated” by calling <B>setWaiting()
</B>method before making a task wait on it. Otherwise, the task will
@@ -703,8 +743,8 @@ a StatusReqeust object this Task was waiting on.
</P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
</P>
-<P CLASS="western" STYLE="margin-bottom: 0in"><B>TASK ID, CONTROL
-POINTS METHODS:</B></P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
+<B>TASK ID, CONTROL POINTS METHODS:</B></P>
<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
<BR>
</P>
@@ -929,6 +969,13 @@ statements after <B>execute</B> inside the <B>loop()</B>
</P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
</P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><B>bool isOverrun()</B></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">If
+library is compiled with <FONT FACE="Courier New, monospace">_TASK_TIMECRITICAL</FONT>
+enabled, this method returns <B>true</B><SPAN STYLE="font-weight: normal">
+if currently invoked task has overrun its scheduled start time when
+it was invoked. Returns </SPAN><B>false</B><SPAN STYLE="font-weight: normal">
+if task has been invoked according to schedule.</SPAN></P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
</P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
@@ -955,7 +1002,7 @@ status of “completed” on creation.
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
</P>
<P CLASS="western" STYLE="margin-bottom: 0in"><B>void
-setWaiting(unsigned int aCount)</B></P>
+setWaiting(unsigned int aCount = 1)</B></P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Activates
Status Request object. By default each object is set to wait on one
event only, however, if <B>aCount</B> is supplied, Status Request can
@@ -1023,6 +1070,46 @@ completion of a request.</P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
</P>
<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
+<B>CONSTANTS:</B></P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in; page-break-after: avoid">
+<B>TASK_SECOND (1000)</B></P>
+<P CLASS="western" STYLE="margin-left: 0.98in; margin-bottom: 0in">Task
+interval of 1 second</P>
+<P CLASS="western" STYLE="margin-left: 0.98in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in; page-break-after: avoid">
+<B>TASK_MINUTE (60000)</B></P>
+<P CLASS="western" STYLE="margin-left: 0.98in; margin-bottom: 0in">Task
+interval of 1 minute</P>
+<P CLASS="western" STYLE="margin-left: 0.98in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in; page-break-after: avoid">
+<B>TASK_HOUR (3600000)</B></P>
+<P CLASS="western" STYLE="margin-left: 0.98in; margin-bottom: 0in">Task
+interval of 1 hour</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in; page-break-after: avoid">
+<B>TASK_FOREVER (-1)</B></P>
+<P CLASS="western" STYLE="margin-left: 0.98in; margin-bottom: 0in">Task
+number of iterations for infinite number of iterations</P>
+<P CLASS="western" STYLE="margin-left: 0.98in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in; page-break-after: avoid">
+<B>TASK_ONCE (1)</B></P>
+<P CLASS="western" STYLE="margin-left: 0.98in; margin-bottom: 0in">Task
+single iteration</P>
+<P CLASS="western" STYLE="margin-left: 0.98in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in; page-break-after: avoid">
+<B>TASK_IMMEDIATE (0)</B></P>
+<P CLASS="western" STYLE="margin-left: 0.98in; margin-bottom: 0in">Task
+interval for immediate execution</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-before: always; page-break-after: avoid">
<B>IMPLEMENTATION SCENARIOS AND IDEAS:</B></P>
<P CLASS="western" STYLE="margin-bottom: 0in"><BR>
</P>
@@ -1048,11 +1135,11 @@ becomes a task:</P>
<P CLASS="western" STYLE="margin-bottom: 0in"><BR>
</P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
-<B>tMeasure</B> (TMEASURE_INTERVAL*SECOND, -1,
+<B>tMeasure</B> (TMEASURE_INTERVAL*SECOND, TASK_FOREVER,
&amp;measureCallback);<BR>Task <B>tWater</B>
(TWATER_INTERVAL*SECOND, RETRIES, &amp;waterCallback);<BR>Task
-<B>tDisplay</B> (TDISPLAY_INTERVAL*SECOND, -1, &amp;displayCallback);
-<BR></FONT></FONT><BR>
+<B>tDisplay</B> (TDISPLAY_INTERVAL*SECOND, TASK_FOREVER,
+&amp;displayCallback); <BR></FONT></FONT><BR>
</P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Scheduler
</FONT></FONT><FONT FACE="Courier New, monospace"><FONT SIZE=2><B>taskManager</B></FONT></FONT><FONT FACE="Courier New, monospace"><FONT SIZE=2>;</FONT></FONT><BR><BR>
@@ -1065,7 +1152,7 @@ to run once for every time the pump is turned on:</P>
</P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
</FONT></FONT><FONT FACE="Courier New, monospace"><FONT SIZE=2><B>tWaterOff</B></FONT></FONT><FONT FACE="Courier New, monospace"><FONT SIZE=2>
-(WATERTIME*SECOND, 1,</FONT></FONT><FONT FACE="Courier New, monospace">
+(WATERTIME*SECOND, TASK_ONCE,</FONT></FONT><FONT FACE="Courier New, monospace">
&amp;waterOffCallback);</FONT><BR><BR>
</P>
<P CLASS="western" STYLE="margin-bottom: 0in">Example of the callback
@@ -1084,9 +1171,9 @@ tWater.enableDelayed();<BR>}</FONT></FONT></P>
waterCallback() {<BR> if (tWater.getIterations()) {</FONT></FONT></P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>//
If this is not the last iteration = turn the pump on<BR>
-motorOn();<BR> tWaterOff.set(parameters.watertime * SECOND, 1,
-&amp;waterOffCallback);<BR> tWaterOff.enableDelayed();<BR>
-return;<BR> }</FONT></FONT></P>
+motorOn();<BR> tWaterOff.set(parameters.watertime * TASK_SECOND,
+TASK_ONCE, &amp;waterOffCallback);<BR>
+tWaterOff.enableDelayed();<BR> return;<BR> }</FONT></FONT></P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>//
We could not reach target humidity – something is wrong<BR>
motorOff;<BR> taskManager.disableAll();<BR> tError.enable();<BR>}</FONT></FONT></P>
@@ -1146,7 +1233,7 @@ Blinking LED 2 times a second could be achieved this way</FONT></P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Scheduler
ts;</FONT></FONT></P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
-tLedBlinker (500, -1, &amp;ledOnCallback, &amp;ts, true);</FONT></FONT></P>
+tLedBlinker (500, TASK_FOREVER, &amp;ledOnCallback, &amp;ts, true);</FONT></FONT></P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
</P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
@@ -1212,7 +1299,7 @@ this case, define a tasks with two callbacks:</FONT></P>
<P CLASS="western" STYLE="margin-bottom: 0in"><BR>
</P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
-<B>tWork</B> (T_INTERVAL, -1, &amp;workCallbackInit);</FONT></FONT></P>
+<B>tWork</B> (T_INTERVAL, TASK_FOREVER, &amp;workCallbackInit);</FONT></FONT></P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">…</P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
</P>
@@ -1290,16 +1377,17 @@ determine the length of ultrasonic pulse.</FONT></P>
&lt;PinChangeInt.h&gt;<BR><BR><BR>#define TRIGGERPIN 5<BR>#define
ECHOPIN 6<BR><BR>Output&lt;TRIGGERPIN&gt;
pTrigger;<BR>Input&lt;ECHOPIN&gt; pEcho;<BR><BR>Scheduler
-r;<BR><BR>Task tMeasure(1000, -1, &amp;measureCallback, &amp;r,
-true);<BR>Task tDisplay(1000, -1, &amp;displayCallback, &amp;r,
-true);<BR>Task tPing(0, 1, &amp;pingCalcCallback, &amp;r,
-false);<BR><BR><BR>volatile bool pulseBusy = false;<BR>volatile bool
-pulseTimeout = false;<BR>volatile unsigned long pulseStart =
-0;<BR>volatile unsigned long pulseStop = 0;<BR>volatile unsigned long
-pingDistance = 0;<BR><BR><BR>void pingTrigger(unsigned long aTimeout)
-{<BR> if (pulseBusy) return; // do not trigger if in the middle of
-a pulse<BR> if (pEcho == HIGH) return; // do not trigger if ECHO pin
-is high<BR> <BR> pulseBusy = true;<BR> pulseTimeout = false;</FONT></FONT></P>
+r;<BR><BR>Task tMeasure(TASK_SECOND, TASK_FOREVER, &amp;measureCallback,
+&amp;r, true);<BR>Task tDisplay(TASK_SECOND, TASK_FOREVER,
+&amp;displayCallback, &amp;r, true);<BR>Task tPing(TASK_IMMEDIATE,
+TASK_ONCE, &amp;pingCalcCallback, &amp;r, false);<BR><BR><BR>volatile
+bool pulseBusy = false;<BR>volatile bool pulseTimeout =
+false;<BR>volatile unsigned long pulseStart = 0;<BR>volatile unsigned
+long pulseStop = 0;<BR>volatile unsigned long pingDistance =
+0;<BR><BR><BR>void pingTrigger(unsigned long aTimeout) {<BR> if
+(pulseBusy) return; // do not trigger if in the middle of a pulse<BR>
+ if (pEcho == HIGH) return; // do not trigger if ECHO pin is high<BR>
+ <BR> pulseBusy = true;<BR> pulseTimeout = false;</FONT></FONT></P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2><BR>
pTrigger = LOW;<BR> delayMicroseconds(4);<BR> pTrigger = HIGH;<BR><BR>
tPing.setInterval (aTimeout);<BR><BR> delayMicroseconds(10);<BR>
@@ -1396,12 +1484,13 @@ ts;</FONT></FONT></P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
</P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
-tWrapper(30000, -1, &amp;WrapperCallback, &amp;ts, true);</FONT></FONT></P>
+tWrapper(30000, TASK_FOREVER, &amp;WrapperCallback, &amp;ts, true);</FONT></FONT></P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
-tBlink(5000, 1, NULL, &amp;ts, false, &amp;BlinkOnEnable,
+tBlink(5000, TASK_ONCE, NULL, &amp;ts, false, &amp;BlinkOnEnable,
&amp;BlinkOnDisable);</FONT></FONT></P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
-tLED(0, -1, NULL, &amp;ts, false, NULL, &amp;LEDOff);</FONT></FONT></P>
+tLED(TASK_IMMEDIATE, TASK_FOREVER, NULL, &amp;ts, false, NULL,
+&amp;LEDOff);</FONT></FONT></P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
</P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
@@ -1582,18 +1671,21 @@ ts; </FONT></FONT>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
</P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
-tCycle(10000, -1, &amp;CycleCallback, &amp;ts, true);</FONT></FONT></P>
+tCycle(10000, TASK_FOREVER, &amp;CycleCallback, &amp;ts, true);</FONT></FONT></P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
-tMeasure(1000, 1, &amp;MeasureCallback, &amp;ts, false,
-&amp;MeasureEnable, &amp;MeasureDisable);</FONT></FONT></P>
+tMeasure(TASK_SECOND, TASK_ONCE, &amp;MeasureCallback, &amp;ts,
+false, &amp;MeasureEnable, &amp;MeasureDisable);</FONT></FONT></P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
tCalculate(&amp;CalcCallback, &amp;ts);</FONT></FONT></P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
-tSensor1(0, 1, &amp;S1Callback, &amp;ts, false, &amp;S1Enable);</FONT></FONT></P>
+tSensor1(TASK_IMMEDIATE, TASK_ONCE, &amp;S1Callback, &amp;ts, false,
+&amp;S1Enable);</FONT></FONT></P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
-tSensor2(0, 1, &amp;S2Callback, &amp;ts, false, &amp;S2Enable);</FONT></FONT></P>
+tSensor2(TASK_IMMEDIATE, TASK_ONCE, &amp;S2Callback, &amp;ts, false,
+&amp;S2Enable);</FONT></FONT></P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
-tSensor3(0, 1, &amp;S3Callback, &amp;ts, false, &amp;S3Enable);</FONT></FONT></P>
+tSensor3(TASK_IMMEDIATE, TASK_ONCE, &amp;S3Callback, &amp;ts, false,
+&amp;S3Enable);</FONT></FONT></P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
</P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
@@ -1973,13 +2065,13 @@ that both tasks refer to the same callback methods)</FONT></FONT></P>
</P>
<P LANG="" CLASS="western" STYLE="margin-left: 0.98in; margin-bottom: 0in">
<FONT FACE="Courier New, monospace"><FONT SIZE=2>Scheduler ts;</FONT></FONT></P>
-<P LANG="" CLASS="western" STYLE="margin-left: 0.98in; margin-bottom: 0in">
-<FONT FACE="Courier New, monospace"><FONT SIZE=2>Task t1(100, -1,
-&amp;Measure, &amp;ts, false, &amp;MeasureOn); </FONT></FONT>
+<P CLASS="western" STYLE="margin-left: 0.98in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2><SPAN LANG="">Task
+t1(100, </SPAN>TASK_FOREVER<SPAN LANG="">, &amp;Measure, &amp;ts,
+false, &amp;MeasureOn); </SPAN></FONT></FONT>
</P>
-<P LANG="" CLASS="western" STYLE="margin-left: 0.98in; margin-bottom: 0in">
-<FONT FACE="Courier New, monospace"><FONT SIZE=2>Task t2(100, -1,
-&amp;Measure, &amp;ts, false, &amp;MeasureOn); </FONT></FONT>
+<P CLASS="western" STYLE="margin-left: 0.98in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2><SPAN LANG="">Task
+t2(100, </SPAN>TASK_FOREVER<SPAN LANG="">, &amp;Measure, &amp;ts,
+false, &amp;MeasureOn); </SPAN></FONT></FONT>
</P>
<P CLASS="western" STYLE="margin-left: 0.98in; margin-bottom: 0in"><BR>
</P>
@@ -2047,9 +2139,9 @@ information and implementation options.</FONT></FONT></P>
<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Real
time examples of TaskScheduler are available here:</FONT></FONT></P>
<OL>
- <LI><P CLASS="western" STYLE="margin-bottom: 0in"><FONT COLOR="#0000ff"><U><A HREF="http://www.instructables.com/id/APIS-Automated-Plant-Irrigation-System/"><FONT FACE="Courier New, monospace"><FONT SIZE=2>http://www.instructables.com/id/APIS-Automated-Plant-Irrigation-System/</FONT></FONT></A></U></FONT></P>
- <LI><P CLASS="western" STYLE="margin-bottom: 0in"><FONT COLOR="#0000ff"><U><A HREF="http://www.instructables.com/id/Wave-your-hand-to-control-OWI-Robotic-Arm-no-strin/"><FONT FACE="Courier New, monospace"><FONT SIZE=2>http://www.instructables.com/id/Wave-your-hand-to-control-OWI-Robotic-Arm-no-strin/</FONT></FONT></A></U></FONT></P>
- <LI><P CLASS="western" STYLE="margin-bottom: 0in"><FONT COLOR="#0000ff"><U><A HREF="http://www.instructables.com/id/Arduino-Nano-based-Hexbug-Scarab-Robotic-Spider"><FONT FACE="Courier New, monospace"><FONT SIZE=2>http://www.instructables.com/id/Arduino-Nano-based-Hexbug-Scarab-Robotic-Spider</FONT></FONT></A></U></FONT></P>
+ <LI><P CLASS="western" STYLE="margin-bottom: 0in"><FONT COLOR="#0000ff"><U><A HREF="http://www.instructables.com/id/APIS-Automated-Plant-Irrigation-System/"><FONT FACE="Courier New, monospace">http://www.instructables.com/id/APIS-Automated-Plant-Irrigation-System/</FONT></A></U></FONT></P>
+ <LI><P CLASS="western" STYLE="margin-bottom: 0in"><FONT COLOR="#0000ff"><U><A HREF="http://www.instructables.com/id/Wave-your-hand-to-control-OWI-Robotic-Arm-no-strin/"><FONT FACE="Courier New, monospace">http://www.instructables.com/id/Wave-your-hand-to-control-OWI-Robotic-Arm-no-strin/</FONT></A></U></FONT></P>
+ <LI><P CLASS="western" STYLE="margin-bottom: 0in"><FONT COLOR="#0000ff"><U><A HREF="http://www.instructables.com/id/Arduino-Nano-based-Hexbug-Scarab-Robotic-Spider"><FONT FACE="Courier New, monospace">http://www.instructables.com/id/Arduino-Nano-based-Hexbug-Scarab-Robotic-Spider</FONT></A></U></FONT></P>
</OL>
<P CLASS="western" STYLE="margin-bottom: 0in"><BR>
</P>
diff --git a/keywords.txt b/keywords.txt
index 6bbbc3a..c8a9f22 100644
--- a/keywords.txt
+++ b/keywords.txt
@@ -52,6 +52,7 @@ pending KEYWORD2
completed KEYWORD2
getStatus KEYWORD2
waitFor KEYWORD2
+waitForDelayed KEYWORD2
getStatusRequest KEYWORD2
setId KEYWORD2
getId KEYWORD2
@@ -59,8 +60,21 @@ setControlPoint KEYWORD2
getControlPoint KEYWORD2
setLtsPointer KEYWORD2
getLtsPointer KEYWORD2
+isOverrun KEYWORD2
#######################################
# Constants (LITERAL1)
+TASK_SECOND LITERAL1
+TASK_MINUTE LITERAL1
+TASK_HOUR LITERAL1
+TASK_FOREVER LITERAL1
+TASK_IMMEDIATE LITERAL1
+TASK_ONCE LITERAL1
+_TASK_TIMECRITICAL LITERAL1
+_TASK_SLEEP_ON_IDLE_RUN LITERAL1
+_TASK_STATUS_REQUEST LITERAL1
+_TASK_WDT_IDS LITERAL1
+_TASK_LTS_POINTER LITERAL1
+_TASK_ROLLOVER_FIX LITERAL1
#######################################
diff --git a/library.properties b/library.properties
index d347938..a442e9d 100644
--- a/library.properties
+++ b/library.properties
@@ -1,5 +1,5 @@
name=TaskScheduler
-version=1.8.2
+version=1.8.3
author=Anatoli Arkhipenko <arkhipenko@hotmail.com>
maintainer=Anatoli Arkhipenko <arkhipenko@hotmail.com>
sentence=A light-weight cooperative multitasking library for arduino microcontrollers.
diff --git a/src/TaskScheduler.h b/src/TaskScheduler.h
index b1f3ca0..228a8d0 100644
--- a/src/TaskScheduler.h
+++ b/src/TaskScheduler.h
@@ -50,6 +50,13 @@
// 2015-10-27 - bug: currentTask() method returns incorrect Task reference if called within OnEnable and OnDisable methods
// 2015-10-27 - protection against infinite loop in OnEnable (if enable() methods are called within OnEnable)
// 2015-10-29 - new currentLts() method in the scheduler class returns current task's LTS pointer in one call
+//
+// v1.8.3:
+// 2015-11-05 - support for task activation on a status request with arbitrary interval and number of iterations (0 and 1 are still default values)
+// 2015-11-05 - implement waitForDelayed() method to allow task activation on the status request completion delayed for one current interval
+// 2015-11-09 - added callback methods prototypes to all examples for Arduino IDE 1.6.6 compatibility
+// 2015-11-14 - added several constants to be used as task parameters for readability (e.g, TASK_FOREVER, TASK_SECOND, etc.)
+// 2015-11-14 - significant optimization of the scheduler's execute loop, including millis() rollover fix option
/* ============================================
@@ -92,6 +99,7 @@ THE SOFTWARE.
* #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
* #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
* #define _TASK_LTS_POINTER // Compile with support for local task storage pointer
+ * #define _TASK_ROLLOVER_FIX // Compensate for millis() rollover once every 47 days
*/
#ifdef _TASK_SLEEP_ON_IDLE_RUN
@@ -99,8 +107,18 @@ THE SOFTWARE.
#include <avr/power.h>
#endif
+#define TASK_IMMEDIATE 0
+#define TASK_SECOND 1000L
+#define TASK_MINUTE 60000L
+#define TASK_HOUR 3600000L
+#define TASK_FOREVER (-1)
+#define TASK_ONCE 1
#ifdef _TASK_STATUS_REQUEST
+
+#define _TASK_SR_NODELAY 1
+#define _TASK_SR_DELAY 2
+
class StatusRequest {
public:
StatusRequest() {iCount = 0; iStatus = 0; }
@@ -113,7 +131,7 @@ class StatusRequest {
private:
unsigned int iCount; // waiting for more that 65000 events seems unreasonable: unsigned int should be sufficient
- int iStatus; // negative = error; zero = OK; >positive = OK with a specific status
+ int iStatus; // negative = error; zero = OK; >positive = OK with a specific status
};
#endif
@@ -123,76 +141,78 @@ class Scheduler;
class Task {
friend class Scheduler;
public:
- Task(unsigned long aInterval=0, long aIterations=0, void (*aCallback)()=NULL, Scheduler* aScheduler=NULL, boolean aEnable=false, bool (*aOnEnable)()=NULL, void (*aOnDisable)()=NULL);
+ Task(unsigned long aInterval=0, long aIterations=0, void (*aCallback)()=NULL, Scheduler* aScheduler=NULL, boolean aEnable=false, bool (*aOnEnable)()=NULL, void (*aOnDisable)()=NULL);
#ifdef _TASK_STATUS_REQUEST
- Task(void (*aCallback)()=NULL, Scheduler* aScheduler=NULL, bool (*aOnEnable)()=NULL, void (*aOnDisable)()=NULL);
+ Task(void (*aCallback)()=NULL, Scheduler* aScheduler=NULL, bool (*aOnEnable)()=NULL, void (*aOnDisable)()=NULL);
#endif
- void enable();
- bool enableIfNot();
- void enableDelayed(unsigned long aDelay=0);
- void delay(unsigned long aDelay=0);
- void forceNextIteration();
- void restart();
- void restartDelayed(unsigned long aDelay=0);
- bool disable();
- inline bool isEnabled() { return iEnabled; }
- void set(unsigned long aInterval, long aIterations, void (*aCallback)(),bool (*aOnEnable)()=NULL, void (*aOnDisable)()=NULL);
- void setInterval(unsigned long aInterval);
- inline unsigned long getInterval() { return iInterval; }
- void setIterations(long aIterations);
- inline long getIterations() { return iIterations; }
- inline unsigned long getRunCounter() { return iRunCounter; }
- inline void setCallback(void (*aCallback)()) { iCallback = aCallback; }
- inline void setOnEnable(bool (*aCallback)()) { iOnEnable = aCallback; }
- inline void setOnDisable(void (*aCallback)()) { iOnDisable = aCallback; }
+ void enable();
+ bool enableIfNot();
+ void enableDelayed(unsigned long aDelay=0);
+ void delay(unsigned long aDelay=0);
+ void forceNextIteration();
+ void restart();
+ void restartDelayed(unsigned long aDelay=0);
+ bool disable();
+ inline bool isEnabled() { return iEnabled; }
+ void set(unsigned long aInterval, long aIterations, void (*aCallback)(),bool (*aOnEnable)()=NULL, void (*aOnDisable)()=NULL);
+ void setInterval(unsigned long aInterval);
+ inline unsigned long getInterval() { return iInterval; }
+ void setIterations(long aIterations);
+ inline long getIterations() { return iIterations; }
+ inline unsigned long getRunCounter() { return iRunCounter; }
+ inline void setCallback(void (*aCallback)()) { iCallback = aCallback; }
+ inline void setOnEnable(bool (*aCallback)()) { iOnEnable = aCallback; }
+ inline void setOnDisable(void (*aCallback)()) { iOnDisable = aCallback; }
#ifdef _TASK_TIMECRITICAL
- inline long getOverrun() { return iOverrun; }
+ inline long getOverrun() { return iOverrun; }
#endif
- inline bool isFirstIteration() { return (iRunCounter <= 1); }
- inline bool isLastIteration() { return (iIterations == 0); }
+ inline bool isFirstIteration() { return (iRunCounter <= 1); }
+ inline bool isLastIteration() { return (iIterations == 0); }
#ifdef _TASK_STATUS_REQUEST
- void waitFor(StatusRequest* aStatusRequest);
- inline StatusRequest* getStatusRequest() {return iStatusRequest; }
+ void waitFor(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1);
+ void waitForDelayed(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1);
+ inline StatusRequest* getStatusRequest() {return iStatusRequest; }
#endif
#ifdef _TASK_WDT_IDS
- inline void setId(unsigned int aID) { iTaskID = aID; }
- inline unsigned int getId() { return iTaskID; }
- inline void setControlPoint(unsigned int aPoint) { iControlPoint = aPoint; }
- inline unsigned int getControlPoint() { return iControlPoint; }
+ inline void setId(unsigned int aID) { iTaskID = aID; }
+ inline unsigned int getId() { return iTaskID; }
+ inline void setControlPoint(unsigned int aPoint) { iControlPoint = aPoint; }
+ inline unsigned int getControlPoint() { return iControlPoint; }
#endif
#ifdef _TASK_LTS_POINTER
- inline void setLtsPointer(void *aPtr) { iLTS = aPtr; }
- inline void* getLtsPointer() { return iLTS; }
+ inline void setLtsPointer(void *aPtr) { iLTS = aPtr; }
+ inline void* getLtsPointer() { return iLTS; }
#endif
private:
- void reset();
+ void reset();
- volatile bool iEnabled;
- bool iInOnEnable;
- volatile unsigned long iInterval;
- volatile unsigned long iPreviousMillis;
+ volatile bool iEnabled;
+ volatile bool iInOnEnable;
+ volatile unsigned long iInterval;
+ volatile unsigned long iPreviousMillis;
#ifdef _TASK_TIMECRITICAL
- volatile long iOverrun;
+ volatile long iOverrun;
#endif
- volatile long iIterations;
- long iSetIterations;
- unsigned long iRunCounter;
- void (*iCallback)();
- bool (*iOnEnable)();
- void (*iOnDisable)();
- Task *iPrev, *iNext;
- Scheduler *iScheduler;
+ volatile long iIterations;
+ long iSetIterations;
+ unsigned long iRunCounter;
+ void (*iCallback)();
+ bool (*iOnEnable)();
+ void (*iOnDisable)();
+ Task *iPrev, *iNext;
+ Scheduler *iScheduler;
#ifdef _TASK_STATUS_REQUEST
- StatusRequest *iStatusRequest;
+ StatusRequest *iStatusRequest;
+ byte iWaiting;
#endif
#ifdef _TASK_WDT_IDS
- unsigned int iTaskID;
- unsigned int iControlPoint;
+ unsigned int iTaskID;
+ unsigned int iControlPoint;
#endif
#ifdef _TASK_LTS_POINTER
- void *iLTS;
+ void *iLTS;
#endif
};
@@ -213,6 +233,9 @@ class Scheduler {
#ifdef _TASK_LTS_POINTER
inline void* currentLts() {return iCurrent->iLTS; }
#endif
+#ifdef _TASK_TIMECRITICAL
+ inline bool isOverrun() { return (iCurrent->iOverrun < 0); }
+#endif
private:
Task *iFirst, *iLast, *iCurrent;
@@ -282,14 +305,23 @@ void StatusRequest::signalComplete(int aStatus) {
* @param: aStatusRequest - a pointer for the StatusRequest to wait for.
* If aStatusRequest is NULL, request for waiting is ignored, and the waiting task is not enabled.
*/
-void Task::waitFor(StatusRequest* aStatusRequest) {
+void Task::waitFor(StatusRequest* aStatusRequest, unsigned long aInterval, long aIterations) {
if ( ( iStatusRequest = aStatusRequest) ) { // assign internal StatusRequest var and check if it is not NULL
- setIterations(1);
- setInterval(0);
+ setIterations(aIterations);
+ setInterval(aInterval);
+ iWaiting = _TASK_SR_NODELAY; // no delay
enable();
}
}
+void Task::waitForDelayed(StatusRequest* aStatusRequest, unsigned long aInterval, long aIterations) {
+ if ( ( iStatusRequest = aStatusRequest) ) { // assign internal StatusRequest var and check if it is not NULL
+ setIterations(aIterations);
+ if ( aInterval ) setInterval(aInterval); // For the dealyed version only set the interval if it was not a zero
+ iWaiting = _TASK_SR_DELAY; // with delay equal to the current interval
+ enable();
+ }
+}
#endif
/** Resets (initializes) the task/
@@ -312,6 +344,9 @@ void Task::reset() {
#ifdef _TASK_LTS_POINTER
iLTS = NULL;
#endif
+#ifdef _TASK_STATUS_REQUEST
+ iWaiting = 0;
+#endif
}
/** Explicitly set Task execution parameters
@@ -533,6 +568,7 @@ void Scheduler::execute() {
#ifdef _TASK_SLEEP_ON_IDLE_RUN
bool idleRun = true;
#endif
+ unsigned long targetMillis;
iCurrent = iFirst;
@@ -540,50 +576,59 @@ void Scheduler::execute() {
do {
if (iCurrent->iEnabled) {
#ifdef _TASK_WDT_IDS
- // For each task the control points are initialized to avoid confusion because of carry-over
+ // For each task the control points are initialized to avoid confusion because of carry-over:
iCurrent->iControlPoint = 0;
#endif
+
+ // Disable task on last iteration:
if (iCurrent->iIterations == 0) {
iCurrent->disable();
break;
}
#ifdef _TASK_STATUS_REQUEST
- // If StatusRequest object was provided (not NULL) and it is still bending, this task should not run
- // Otherwise, continue with execution as usual. Tasks waiting to StatusRequest usually have interval = 0, and iterations = 1
- // so they will run once immediately.
- if ( iCurrent->iStatusRequest && (iCurrent->iStatusRequest)->pending() ) break;
+ // If StatusRequest object was provided, and still pending, and task is waiting, this task should not run
+ // Otherwise, continue with execution as usual. Tasks waiting to StatusRequest need to be rescheduled according to
+ // how they were placed into waiting state (waitFor or waitForDelayed)
+ if ( iCurrent->iWaiting ) {
+ if ( (iCurrent->iStatusRequest)->pending() ) break;
+ iCurrent->iPreviousMillis = (iCurrent->iWaiting == _TASK_SR_NODELAY) ? millis()-iCurrent->iInterval : millis();
+ iCurrent->iWaiting = 0;
+ }
#endif
- if ( iCurrent->iInterval > 0 ) {
- unsigned long targetMillis = iCurrent->iPreviousMillis + iCurrent->iInterval;
- if ( targetMillis <= millis() ) {
- if ( iCurrent->iIterations > 0 ) iCurrent->iIterations--; // do not decrement (-1) being a signal of eternal task
- iCurrent->iRunCounter++;
- iCurrent->iPreviousMillis += iCurrent->iInterval;
-
+ // Determine when current task is supposed to run
+ // Once every 47 days there is a rollover execution which will occur due to millis and targetMillis rollovers
+ // That is why there is an option to compile with rollover fix
+ // Example
+ // iPreviousMillis = 65000
+ // iInterval = 600
+ // millis() = 65500
+ // targetMillis = 65000 + 600 = (should be 65600) 65 (due to rollover)
+ // so 65 < 65500. should be 65600 > 65500. - task will be scheduled incorrectly
+ // since targetMillis (65) < iPreviousMillis (65000), rollover fix kicks in:
+ // iPreviousMillis(65000) > millis(65500) - iInterval(600) = 64900 - task will not be scheduled
+
+ targetMillis = iCurrent->iPreviousMillis + iCurrent->iInterval;
+ #ifdef _TASK_ROLLOVER_FIX
+ if ( targetMillis < iCurrent->iPreviousMillis ) { // targetMillis rolled over!
+ if ( iCurrent->iPreviousMillis > ( millis() - iCurrent->iInterval) ) break;
+ }
+ else
+ #endif
+ if ( targetMillis > millis() ) break;
+
+ iCurrent->iPreviousMillis = targetMillis;
#ifdef _TASK_TIMECRITICAL
- // Updated_previous+current should put us into the future, so iOverrun should be positive or zero.
+ // Updated_previous+current interval should put us into the future, so iOverrun should be positive or zero.
// If negative - the task is behind (next execution time is already in the past)
- iCurrent->iOverrun = (long) (iCurrent->iPreviousMillis + iCurrent->iInterval - millis());
+ iCurrent->iOverrun = (long) (iCurrent->iPreviousMillis + iCurrent->iInterval - millis());
#endif
-
- if ( iCurrent->iCallback ) {
- ( *(iCurrent->iCallback) )();
- #ifdef _TASK_SLEEP_ON_IDLE_RUN
- idleRun = false;
- #endif
- }
- break;
- }
- }
- else {
- if ( iCurrent->iIterations > 0 ) iCurrent->iIterations--; // do not decrement (-1) being a signal of eternal task
- iCurrent->iRunCounter++;
- if ( iCurrent->iCallback ) {
- ( *(iCurrent->iCallback) )();
+ if ( iCurrent->iIterations > 0 ) iCurrent->iIterations--; // do not decrement (-1) being a signal of never-ending task
+ iCurrent->iRunCounter++;
+ if ( iCurrent->iCallback ) {
+ ( *(iCurrent->iCallback) )();
#ifdef _TASK_SLEEP_ON_IDLE_RUN
- idleRun = false;
+ idleRun = false;
#endif
- }
}
}
} while (0); //guaranteed single run - allows use of "break" to exit