diff options
| author | Anatoli Arkhipenko <arkhipenko@hotmail.com> | 2015-11-14 17:46:16 -0500 |
|---|---|---|
| committer | Anatoli Arkhipenko <arkhipenko@hotmail.com> | 2015-11-14 17:46:16 -0500 |
| commit | dc152db48e325a5d03be0d48a07c119d97d11e59 (patch) | |
| tree | 72fdcd4c9feee8da743e9540ce58580e7392a7d9 | |
| parent | ef2329bdeade723f560cd0f7e933c9f3119677f3 (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-- | README | 12 | ||||
| -rw-r--r-- | examples/Scheduler_example/Scheduler_example.ino | 29 | ||||
| -rw-r--r-- | examples/Scheduler_example2/Scheduler_example2.ino | 21 | ||||
| -rw-r--r-- | examples/Scheduler_example3/Scheduler_example3.ino | 17 | ||||
| -rw-r--r-- | examples/Scheduler_example4_StatusRequest/Scheduler_example4_StatusRequest.ino | 12 | ||||
| -rw-r--r-- | examples/Scheduler_example5_StatusRequest/Scheduler_example5_StatusRequest.ino | 27 | ||||
| -rw-r--r-- | examples/Scheduler_example6_IDLE/Scheduler_example6_IDLE.ino | 11 | ||||
| -rw-r--r-- | examples/Scheduler_example7_WDT/Scheduler_example7_WDT.ino | 14 | ||||
| -rw-r--r-- | examples/Scheduler_example8_LTS/Scheduler_example8_LTS.ino | 15 | ||||
| -rw-r--r-- | extras/TaskScheduler.doc | bin | 172544 -> 178688 bytes | |||
| -rw-r--r-- | extras/TaskScheduler.html | 194 | ||||
| -rw-r--r-- | keywords.txt | 14 | ||||
| -rw-r--r-- | library.properties | 2 | ||||
| -rw-r--r-- | src/TaskScheduler.h | 213 |
14 files changed, 394 insertions, 187 deletions
@@ -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 Binary files differindex 9995a43..85331e7 100644 --- a/extras/TaskScheduler.doc +++ b/extras/TaskScheduler.doc 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, &measureCallback);<BR>Task <B>tWater</B> (TWATER_INTERVAL*SECOND, RETRIES, &waterCallback);<BR>Task -<B>tDisplay</B> (TDISPLAY_INTERVAL*SECOND, -1, &displayCallback); -<BR></FONT></FONT><BR> +<B>tDisplay</B> (TDISPLAY_INTERVAL*SECOND, TASK_FOREVER, +&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"> &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, -&waterOffCallback);<BR> tWaterOff.enableDelayed();<BR> -return;<BR> }</FONT></FONT></P> +motorOn();<BR> tWaterOff.set(parameters.watertime * TASK_SECOND, +TASK_ONCE, &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, &ledOnCallback, &ts, true);</FONT></FONT></P> +tLedBlinker (500, TASK_FOREVER, &ledOnCallback, &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, &workCallbackInit);</FONT></FONT></P> +<B>tWork</B> (T_INTERVAL, TASK_FOREVER, &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> <PinChangeInt.h><BR><BR><BR>#define TRIGGERPIN 5<BR>#define ECHOPIN 6<BR><BR>Output<TRIGGERPIN> pTrigger;<BR>Input<ECHOPIN> pEcho;<BR><BR>Scheduler -r;<BR><BR>Task tMeasure(1000, -1, &measureCallback, &r, -true);<BR>Task tDisplay(1000, -1, &displayCallback, &r, -true);<BR>Task tPing(0, 1, &pingCalcCallback, &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, &measureCallback, +&r, true);<BR>Task tDisplay(TASK_SECOND, TASK_FOREVER, +&displayCallback, &r, true);<BR>Task tPing(TASK_IMMEDIATE, +TASK_ONCE, &pingCalcCallback, &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, &WrapperCallback, &ts, true);</FONT></FONT></P> +tWrapper(30000, TASK_FOREVER, &WrapperCallback, &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, &ts, false, &BlinkOnEnable, +tBlink(5000, TASK_ONCE, NULL, &ts, false, &BlinkOnEnable, &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, &ts, false, NULL, &LEDOff);</FONT></FONT></P> +tLED(TASK_IMMEDIATE, TASK_FOREVER, NULL, &ts, false, NULL, +&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, &CycleCallback, &ts, true);</FONT></FONT></P> +tCycle(10000, TASK_FOREVER, &CycleCallback, &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, &MeasureCallback, &ts, false, -&MeasureEnable, &MeasureDisable);</FONT></FONT></P> +tMeasure(TASK_SECOND, TASK_ONCE, &MeasureCallback, &ts, +false, &MeasureEnable, &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(&CalcCallback, &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, &S1Callback, &ts, false, &S1Enable);</FONT></FONT></P> +tSensor1(TASK_IMMEDIATE, TASK_ONCE, &S1Callback, &ts, false, +&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, &S2Callback, &ts, false, &S2Enable);</FONT></FONT></P> +tSensor2(TASK_IMMEDIATE, TASK_ONCE, &S2Callback, &ts, false, +&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, &S3Callback, &ts, false, &S3Enable);</FONT></FONT></P> +tSensor3(TASK_IMMEDIATE, TASK_ONCE, &S3Callback, &ts, false, +&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, -&Measure, &ts, false, &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="">, &Measure, &ts, +false, &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, -&Measure, &ts, false, &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="">, &Measure, &ts, +false, &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 |
