Explorar el Código

v2.5.0 - final cleanup

Anatoli Arkhipenko hace 8 años
padre
commit
088edefd70

+ 27 - 27
LICENSE.txt

@@ -1,27 +1,27 @@
-Copyright (c) 2015, Anatoli Arkhipenko.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, 
-   this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice,
-   this list of conditions and the following disclaimer in the documentation
-   and/or other materials provided with the distribution.
-
-3. Neither the name of the copyright holder nor the names of its contributors
-   may be used to endorse or promote products derived from this software without
-   specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
-IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
-OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
+Copyright (c) 2015, Anatoli Arkhipenko.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, 
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+   may be used to endorse or promote products derived from this software without
+   specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.

+ 184 - 184
README

@@ -1,184 +1,184 @@
-Task Scheduler – cooperative multitasking for Arduino and ESP8266 microcontrollers
-Version 2.5.0: 2017-08-30
-
-If you find TaskScheduler useful for your Arduino project, please drop me an email: arkhipenko@hotmail.com
-----------------------------------------------------------------------------------------------------------
-
-OVERVIEW:
- A lightweight implementation of cooperative multitasking (task scheduling) supporting:
- 1. Periodic task execution (with dynamic execution period in milliseconds or microseconds)
- 2. Number of iterations (n times)
- 3. Execution of tasks in predefined sequence
- 4. Dynamic change of task execution parameters (frequency, number of iterations, callback function)
- 5. Power saving via entering IDLE sleep mode between tasks are scheduled to run
- 6. Task invocation via Status Request object
- 7. Task IDs and Control Points for error handling and watchdog timer
- 8. Local Task Storage pointer (allowing use of same callback code for multiple tasks)
- 9. Layered task prioritization
-10. Support for std::functions (ESP8266 only)
-
-Scheduling overhead: between 15 and 18 microseconds per scheduling pass (check the benchmark example).
-
-Tested on the following microcontrollers:
- - Arduino Uno R3
- - Arduino Nano
- - Arduino Micro
- - ATtiny85
- - ESP8266 (Node MCU v2.0)
-
-For detailed functionality overview please refer to TaskScheduler documentation in the 'extras' folder.
-=======================================================================================================
-
-Check out what TaskScheduler can do:
-====================================
-  Endeavor - build a space capable craft, with the ability to maneuver both in and out of atmosphere.
-    (by Radical Space Technologies
-     http://radicalspacetechnologies.com/2015/10/01/endeavor-phase-i/
-
-         "So after some digging I found TaskScheduler an awesome little library developed by Anatoli Arkhipenko,
-          his github is here and the documentation is here. Its fantastic, so far I’ve been able to replicate
-          some things I’ve seen in Ardupilot. ..."
-  link: http://radicalspacetechnologies.com/2015/10/25/endeavors-code-definitely-progress/
-
-
-  3 Devo - Quality 3D printing filament, now made accessible and affordable
-     http://3devo.eu/   (http://3devo.eu/license-information/)
-
-
-  Houston midi clock project - TaskScheduler with microseconds resolution
-    (by chaffneue: My first arduino project. It's a multi-master midi controller with a shared clock and
-     auto count in behaviour. Project files on github: https://github.com/chaffneue/houston
-	 youtube: https://www.youtube.com/watch?v=QRof550TtXo)
-
-
-  Hackabot Nano -  Compact Plug and Play Arduino compatible robotic kit
-    (by Funnyvale: http://hackarobot.com/
-     also: https://www.kickstarter.com/projects/hackarobot/hackabot-nano-compact-plug-and-play-arduino-robot)
-
-
-  Arduino Nano based Hexbug Scarab Robotic Spider
-    (by arkhipenko: http://www.instructables.com/id/Arduino-Nano-based-Hexbug-Scarab-Robotic-Spider/)
-
-
-  Wave your hand to control OWI Robotic Arm... no strings attached
-    (by arkhipenko: http://www.instructables.com/id/Wave-your-hand-to-control-OWI-Robotic-Arm-no-strin/)
-
-
-  APIS - Automated Plant Irrigation System
-    (by arkhipenko: http://www.instructables.com/id/APIS-Automated-Plant-Irrigation-System/)
-
-
-  IoT APIS v2 - Autonomous IoT-enabled Automated Plant Irrigation System
-    (by arkhipenko: http://www.instructables.com/id/IoT-APIS-V2-Autonomous-IoT-enabled-Automated-Plant/)
-
-
-  Interactive Halloween Pumpkin
-    (by arkhipenko: http://www.instructables.com/id/Interactive-Halloween-Pumpkin/)
-
-
-Changelog:
-=========
-v2.5.0:
-   2017-04-27 - added optional support for std::functions via _TASK_STD_FUNCTION
-                (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder)
-   2017-08-30 - add _TASK_DEBUG making all methods and variables public FOR DEBUGGING PURPOSES ONLY!
-                *** USE AT YOUR OWN RISK ***
-   2017-08-30 - bug fix: Scheduler::addTask() checks if task is already part of an execution chain (github issue #37)
-   2017-08-30 - support for multi-tab sketches (Contributed by Adam Ryczkowski - https://github.com/adamryczkowski)
-   
-v2.4.0:
-   2017-04-27 - added destructor to the Task class to ensure tasks are disables and taken off the execution chain
-                upon destruction. (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder)
-
-v2.3.0:
-    2017-02-24 - new timeUntilNextIteration() method within Scheduler class - inquire when a particlar task is 
-                 scheduled to run next time
-				 
-v2.2.1:
-    2016-11-30 - inlined constructors. Added "yield()" and "yieldOnce()" functions to easily break down and
-				 chain back together long running callback methods
-    2016-12-16 - added "getCount()" to StatusRequest objects, made every task StatusRequest enabled.
-                 Internal StatusRequest objects are accessible via "getInternalStatusRequest()" method.
-
-v2.2.0:
-    2016-11-17 - all methods made 'inline' to support inclusion of TaskSchedule.h file into other header files
-
-v2.1.0:
-    2016-02-01 - support for microsecond resolution
-    2016-02-02 - added Scheduler baseline start time reset method: startNow()
-
-v2.0.1:
-    2016-01-02 - bug fix: issue#11 Xtensa compiler (esp8266): Declaration of constructor does not match implementation
-
-v2.0.0:
-    2015-12-22 - _TASK_PRIORITY - support for layered task prioritization
-
-v1.9.2:
-    2015-11-28 - _TASK_ROLLOVER_FIX is deprecated (not necessary)
-    2015-12-16 - bug fixes: automatic millis rollover support for delay methods
-    2015-12-17 - new method for _TASK_TIMECRITICAL option: getStartDelay()
-
-v1.9.0:
-    2015-11-24 - packed three byte-long status variables into one byte-long bit array structure data type - saving 2 bytes per each task instance
-
-v1.8.5:
-    2015-11-23 - bug fix: incorrect calculation of next task invocation in case callback changed the interval
-    2015-11-23 - bug fix: Task::set() method calls setInterval() explicitly, therefore delaying the task in the same manner
-
-v1.8.4:
-    2015-11-15 - bug fix: Task alignment with millis() for scheduling purposes should be done after OnEnable, not before. Especially since OnEnable method can change the interval
-
-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
-
-v1.8.2:
-    2015-10-27 - implement Local Task Storage Pointer (allow use of same callback code for different tasks)
-    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.1:
-    2015-10-22 - implement Task id and control points to support identification of failure points for watchdog timer logging
-
-v1.8.0:
-    2015-10-13 - support for status request objects allowing tasks waiting on requests
-    2015-10-13 - moved to a single header file to allow compilation control via #defines from the main sketch
-
-v1.7.0:
-    2015-10-08 - introduced callback run counter - callback functions can branch on the iteration number.
-    2015-10-11 - enableIfNot() - enable a task only if it is not already enabled. Returns true if was already enabled, false if was disabled.
-    2015-10-11 - disable() returns previous enable state (true if was enabled, false if was already disabled)
-    2015-10-11 - introduced callback functions "on enable" and "on disable". On enable runs every time enable is called, on disable runs only if task was enabled
-    2015-10-12 - new Task method: forceNextIteration() - makes next iteration happen immediately during the next pass regardless how much time is left
-
-v1.6.0:
-    2015-09-22 - revert back to having all tasks disable on last iteration.
-    2015-09-22 - deprecated disableOnLastIteration method as a result
-    2015-10-01 - made version numbers semver compliant (documentation only)
-
-v1.5.1:
-    2015-09-21 - bug fix: incorrect handling of active tasks via set() and setIterations().
-		 		 Thanks to Hannes Morgenstern for catching this one
-
-v1.5.0:
-    2015-09-20 - access to currently executing task (for callback functions)
-    2015-09-20 - pass scheduler as a parameter to the task constructor to append the task to the end of the chain
-    2015-09-20 - option to create a task already enabled
-
-v1.4.1:
-    2015-09-15 - more careful placement of AVR-specific includes for sleep functions (compatibility with DUE)
-                         sleep on idle run is no longer a default and should be explicitly compiled with _TASK_SLEEP_ON_IDLE_RUN defined
-
-v1.0.0:
-    2015-02-24 - Initial release
-    2015-02-28 - added delay() and disableOnLastIteration() functions
-    2015-03-25 - changed scheduler execute() function for a more precise delay calculation:
-                 1. Do not delay if any of the tasks ran (making request for immediate execution redundant)
-                 2. Delay is invoked only if none of the tasks ran
-                 3. Delay is based on the min anticipated wait until next task _AND_ the runtime of execute function itself.
-    2015-05-11 - added  restart() and restartDelayed() functions to restart tasks which are on hold after running all iterations
-    2015-05-19 - completely removed  delay from the scheduler since there are no power saving there. using 1 ms sleep instead
+Task Scheduler – cooperative multitasking for Arduino and ESP8266 microcontrollers
+Version 2.5.0: 2017-08-30
+
+If you find TaskScheduler useful for your Arduino project, please drop me an email: arkhipenko@hotmail.com
+----------------------------------------------------------------------------------------------------------
+
+OVERVIEW:
+ A lightweight implementation of cooperative multitasking (task scheduling) supporting:
+ 1. Periodic task execution (with dynamic execution period in milliseconds or microseconds)
+ 2. Number of iterations (n times)
+ 3. Execution of tasks in predefined sequence
+ 4. Dynamic change of task execution parameters (frequency, number of iterations, callback function)
+ 5. Power saving via entering IDLE sleep mode between tasks are scheduled to run
+ 6. Task invocation via Status Request object
+ 7. Task IDs and Control Points for error handling and watchdog timer
+ 8. Local Task Storage pointer (allowing use of same callback code for multiple tasks)
+ 9. Layered task prioritization
+10. Support for std::functions (ESP8266 only)
+
+Scheduling overhead: between 15 and 18 microseconds per scheduling pass (check the benchmark example).
+
+Tested on the following microcontrollers:
+ - Arduino Uno R3
+ - Arduino Nano
+ - Arduino Micro
+ - ATtiny85
+ - ESP8266 (Node MCU v2.0)
+
+For detailed functionality overview please refer to TaskScheduler documentation in the 'extras' folder.
+=======================================================================================================
+
+Check out what TaskScheduler can do:
+====================================
+  Endeavor - build a space capable craft, with the ability to maneuver both in and out of atmosphere.
+    (by Radical Space Technologies
+     http://radicalspacetechnologies.com/2015/10/01/endeavor-phase-i/
+
+         "So after some digging I found TaskScheduler an awesome little library developed by Anatoli Arkhipenko,
+          his github is here and the documentation is here. Its fantastic, so far I’ve been able to replicate
+          some things I’ve seen in Ardupilot. ..."
+  link: http://radicalspacetechnologies.com/2015/10/25/endeavors-code-definitely-progress/
+
+
+  3 Devo - Quality 3D printing filament, now made accessible and affordable
+     http://3devo.eu/   (http://3devo.eu/license-information/)
+
+
+  Houston midi clock project - TaskScheduler with microseconds resolution
+    (by chaffneue: My first arduino project. It's a multi-master midi controller with a shared clock and
+     auto count in behaviour. Project files on github: https://github.com/chaffneue/houston
+	 youtube: https://www.youtube.com/watch?v=QRof550TtXo)
+
+
+  Hackabot Nano -  Compact Plug and Play Arduino compatible robotic kit
+    (by Funnyvale: http://hackarobot.com/
+     also: https://www.kickstarter.com/projects/hackarobot/hackabot-nano-compact-plug-and-play-arduino-robot)
+
+
+  Arduino Nano based Hexbug Scarab Robotic Spider
+    (by arkhipenko: http://www.instructables.com/id/Arduino-Nano-based-Hexbug-Scarab-Robotic-Spider/)
+
+
+  Wave your hand to control OWI Robotic Arm... no strings attached
+    (by arkhipenko: http://www.instructables.com/id/Wave-your-hand-to-control-OWI-Robotic-Arm-no-strin/)
+
+
+  APIS - Automated Plant Irrigation System
+    (by arkhipenko: http://www.instructables.com/id/APIS-Automated-Plant-Irrigation-System/)
+
+
+  IoT APIS v2 - Autonomous IoT-enabled Automated Plant Irrigation System
+    (by arkhipenko: http://www.instructables.com/id/IoT-APIS-V2-Autonomous-IoT-enabled-Automated-Plant/)
+
+
+  Interactive Halloween Pumpkin
+    (by arkhipenko: http://www.instructables.com/id/Interactive-Halloween-Pumpkin/)
+
+
+Changelog:
+=========
+v2.5.0:
+   2017-04-27 - added optional support for std::functions via _TASK_STD_FUNCTION
+                (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder)
+   2017-08-30 - add _TASK_DEBUG making all methods and variables public FOR DEBUGGING PURPOSES ONLY!
+                *** USE AT YOUR OWN RISK ***
+   2017-08-30 - bug fix: Scheduler::addTask() checks if task is already part of an execution chain (github issue #37)
+   2017-08-30 - support for multi-tab sketches (Contributed by Adam Ryczkowski - https://github.com/adamryczkowski)
+   
+v2.4.0:
+   2017-04-27 - added destructor to the Task class to ensure tasks are disables and taken off the execution chain
+                upon destruction. (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder)
+
+v2.3.0:
+    2017-02-24 - new timeUntilNextIteration() method within Scheduler class - inquire when a particlar task is 
+                 scheduled to run next time
+				 
+v2.2.1:
+    2016-11-30 - inlined constructors. Added "yield()" and "yieldOnce()" functions to easily break down and
+				 chain back together long running callback methods
+    2016-12-16 - added "getCount()" to StatusRequest objects, made every task StatusRequest enabled.
+                 Internal StatusRequest objects are accessible via "getInternalStatusRequest()" method.
+
+v2.2.0:
+    2016-11-17 - all methods made 'inline' to support inclusion of TaskSchedule.h file into other header files
+
+v2.1.0:
+    2016-02-01 - support for microsecond resolution
+    2016-02-02 - added Scheduler baseline start time reset method: startNow()
+
+v2.0.1:
+    2016-01-02 - bug fix: issue#11 Xtensa compiler (esp8266): Declaration of constructor does not match implementation
+
+v2.0.0:
+    2015-12-22 - _TASK_PRIORITY - support for layered task prioritization
+
+v1.9.2:
+    2015-11-28 - _TASK_ROLLOVER_FIX is deprecated (not necessary)
+    2015-12-16 - bug fixes: automatic millis rollover support for delay methods
+    2015-12-17 - new method for _TASK_TIMECRITICAL option: getStartDelay()
+
+v1.9.0:
+    2015-11-24 - packed three byte-long status variables into one byte-long bit array structure data type - saving 2 bytes per each task instance
+
+v1.8.5:
+    2015-11-23 - bug fix: incorrect calculation of next task invocation in case callback changed the interval
+    2015-11-23 - bug fix: Task::set() method calls setInterval() explicitly, therefore delaying the task in the same manner
+
+v1.8.4:
+    2015-11-15 - bug fix: Task alignment with millis() for scheduling purposes should be done after OnEnable, not before. Especially since OnEnable method can change the interval
+
+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
+
+v1.8.2:
+    2015-10-27 - implement Local Task Storage Pointer (allow use of same callback code for different tasks)
+    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.1:
+    2015-10-22 - implement Task id and control points to support identification of failure points for watchdog timer logging
+
+v1.8.0:
+    2015-10-13 - support for status request objects allowing tasks waiting on requests
+    2015-10-13 - moved to a single header file to allow compilation control via #defines from the main sketch
+
+v1.7.0:
+    2015-10-08 - introduced callback run counter - callback functions can branch on the iteration number.
+    2015-10-11 - enableIfNot() - enable a task only if it is not already enabled. Returns true if was already enabled, false if was disabled.
+    2015-10-11 - disable() returns previous enable state (true if was enabled, false if was already disabled)
+    2015-10-11 - introduced callback functions "on enable" and "on disable". On enable runs every time enable is called, on disable runs only if task was enabled
+    2015-10-12 - new Task method: forceNextIteration() - makes next iteration happen immediately during the next pass regardless how much time is left
+
+v1.6.0:
+    2015-09-22 - revert back to having all tasks disable on last iteration.
+    2015-09-22 - deprecated disableOnLastIteration method as a result
+    2015-10-01 - made version numbers semver compliant (documentation only)
+
+v1.5.1:
+    2015-09-21 - bug fix: incorrect handling of active tasks via set() and setIterations().
+		 		 Thanks to Hannes Morgenstern for catching this one
+
+v1.5.0:
+    2015-09-20 - access to currently executing task (for callback functions)
+    2015-09-20 - pass scheduler as a parameter to the task constructor to append the task to the end of the chain
+    2015-09-20 - option to create a task already enabled
+
+v1.4.1:
+    2015-09-15 - more careful placement of AVR-specific includes for sleep functions (compatibility with DUE)
+                         sleep on idle run is no longer a default and should be explicitly compiled with _TASK_SLEEP_ON_IDLE_RUN defined
+
+v1.0.0:
+    2015-02-24 - Initial release
+    2015-02-28 - added delay() and disableOnLastIteration() functions
+    2015-03-25 - changed scheduler execute() function for a more precise delay calculation:
+                 1. Do not delay if any of the tasks ran (making request for immediate execution redundant)
+                 2. Delay is invoked only if none of the tasks ran
+                 3. Delay is based on the min anticipated wait until next task _AND_ the runtime of execute function itself.
+    2015-05-11 - added  restart() and restartDelayed() functions to restart tasks which are on hold after running all iterations
+    2015-05-19 - completely removed  delay from the scheduler since there are no power saving there. using 1 ms sleep instead

+ 83 - 83
examples/Scheduler_example01/Scheduler_example01.ino

@@ -1,84 +1,84 @@
-/** 
- *  TaskScheduler Test
- *
- *  Initially only tasks 1 and 2 are enabled
- *  Task1 runs every 2 seconds 10 times and then stops
- *  Task2 runs every 3 seconds indefinitely
- *  Task1 enables Task3 at its first run
- *  Task3 run every 5 seconds
- *  Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds
- *  At the end Task2 is the only task running every 1/2 seconds
- */
- 
- 
-#include <TaskScheduler.h>
-
-// Callback methods prototypes
-void t1Callback();
-void t2Callback();
-void t3Callback();
-
-//Tasks
-Task t4();
-Task t1(2000, 10, &t1Callback);
-Task t2(3000, TASK_FOREVER, &t2Callback);
-Task t3(5000, TASK_FOREVER, &t3Callback);
-
-Scheduler runner;
-
-
-void t1Callback() {
-    Serial.print("t1: ");
-    Serial.println(millis());
-    
-    if (t1.isFirstIteration()) {
-      runner.addTask(t3);
-      t3.enable();
-      Serial.println("t1: enabled t3 and added to the chain");
-    }
-    
-    if (t1.isLastIteration()) {
-      t3.disable();
-      runner.deleteTask(t3);
-      t2.setInterval(500);
-      Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500");
-    }
-}
-
-void t2Callback() {
-    Serial.print("t2: ");
-    Serial.println(millis());
-  
-}
-
-void t3Callback() {
-    Serial.print("t3: ");
-    Serial.println(millis());
-  
-}
-
-void setup () {
-  Serial.begin(115200);
-  Serial.println("Scheduler TEST");
-  
-  runner.init();
-  Serial.println("Initialized scheduler");
-  
-  runner.addTask(t1);
-  Serial.println("added t1");
-  
-  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();
+/** 
+ *  TaskScheduler Test
+ *
+ *  Initially only tasks 1 and 2 are enabled
+ *  Task1 runs every 2 seconds 10 times and then stops
+ *  Task2 runs every 3 seconds indefinitely
+ *  Task1 enables Task3 at its first run
+ *  Task3 run every 5 seconds
+ *  Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds
+ *  At the end Task2 is the only task running every 1/2 seconds
+ */
+ 
+ 
+#include <TaskScheduler.h>
+
+// Callback methods prototypes
+void t1Callback();
+void t2Callback();
+void t3Callback();
+
+//Tasks
+Task t4();
+Task t1(2000, 10, &t1Callback);
+Task t2(3000, TASK_FOREVER, &t2Callback);
+Task t3(5000, TASK_FOREVER, &t3Callback);
+
+Scheduler runner;
+
+
+void t1Callback() {
+    Serial.print("t1: ");
+    Serial.println(millis());
+    
+    if (t1.isFirstIteration()) {
+      runner.addTask(t3);
+      t3.enable();
+      Serial.println("t1: enabled t3 and added to the chain");
+    }
+    
+    if (t1.isLastIteration()) {
+      t3.disable();
+      runner.deleteTask(t3);
+      t2.setInterval(500);
+      Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500");
+    }
+}
+
+void t2Callback() {
+    Serial.print("t2: ");
+    Serial.println(millis());
+  
+}
+
+void t3Callback() {
+    Serial.print("t3: ");
+    Serial.println(millis());
+  
+}
+
+void setup () {
+  Serial.begin(115200);
+  Serial.println("Scheduler TEST");
+  
+  runner.init();
+  Serial.println("Initialized scheduler");
+  
+  runner.addTask(t1);
+  Serial.println("added t1");
+  
+  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();
 }

+ 78 - 78
examples/Scheduler_example02/Scheduler_example02.ino

@@ -1,79 +1,79 @@
-#define _TASK_SLEEP_ON_IDLE_RUN
-#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, 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
-// Task1 runs every 2 seconds 10 times and then stops
-// Task2 runs every 3 seconds indefinitely
-// Task1 enables Task3 at its first run
-// Task3 run every 5 seconds
-// loop() runs every 1 second (a default scheduler delay, if no shorter tasks' interval is detected)
-// 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().
-
-
-
-
-
-void t1Callback() {
-    Serial.print("t1: ");
-    Serial.println(millis());
-    
-    if (t1.isFirstIteration()) {
-      runner.addTask(t3);
-      t3.enable();
-      Serial.println("t1: enabled t3 and added to the chain");
-    }
-    
-    if (t1.isLastIteration()) {
-      t3.disable();
-      runner.deleteTask(t3);
-      t2.setInterval(500);
-      Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500");
-    }
-}
-
-void t2Callback() {
-    Serial.print("t2: ");
-    Serial.println(millis());
-  
-}
-
-void t3Callback() {
-    Serial.print("t3: ");
-    Serial.println(millis());
-  
-}
-
-void setup () {
-  Serial.begin(115200);
-  Serial.println("Scheduler TEST");
-  
-   delay(5000);
-   
-   runner.startNow();  // set point-in-time for scheduling start
-}
-
-
-void loop () {
-  
-  runner.execute();
-  
-//  Serial.println("Loop ticks at: ");
-//  Serial.println(millis());
+#define _TASK_SLEEP_ON_IDLE_RUN
+#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, 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
+// Task1 runs every 2 seconds 10 times and then stops
+// Task2 runs every 3 seconds indefinitely
+// Task1 enables Task3 at its first run
+// Task3 run every 5 seconds
+// loop() runs every 1 second (a default scheduler delay, if no shorter tasks' interval is detected)
+// 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().
+
+
+
+
+
+void t1Callback() {
+    Serial.print("t1: ");
+    Serial.println(millis());
+    
+    if (t1.isFirstIteration()) {
+      runner.addTask(t3);
+      t3.enable();
+      Serial.println("t1: enabled t3 and added to the chain");
+    }
+    
+    if (t1.isLastIteration()) {
+      t3.disable();
+      runner.deleteTask(t3);
+      t2.setInterval(500);
+      Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500");
+    }
+}
+
+void t2Callback() {
+    Serial.print("t2: ");
+    Serial.println(millis());
+  
+}
+
+void t3Callback() {
+    Serial.print("t3: ");
+    Serial.println(millis());
+  
+}
+
+void setup () {
+  Serial.begin(115200);
+  Serial.println("Scheduler TEST");
+  
+   delay(5000);
+   
+   runner.startNow();  // set point-in-time for scheduling start
+}
+
+
+void loop () {
+  
+  runner.execute();
+  
+//  Serial.println("Loop ticks at: ");
+//  Serial.println(millis());
 }

+ 79 - 79
examples/Scheduler_example03/Scheduler_example03.ino

@@ -1,80 +1,80 @@
-/**
- * TaskScheduler Test of OnEnable and OnDisable methods and illustration of using wrapper tasks for timout purposes
- *
- * 	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. 
- *
- */
-
-#define _TASK_SLEEP_ON_IDLE_RUN
-#include <TaskScheduler.h>
-
-#ifndef LED_BUILTIN
-#define LED_BUILTIN 13    // define appropriate pin for your board
-#endif
-
-Scheduler ts;
-
-// 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
-							 //every 30 seconds for 5 seconds
-}
-
-
-// Upon being enabled, tBlink will define the parameters
-// and enable LED blinking task, which actually controls
-// the hardware (LED in this example)
-bool BlinkOnEnable() {
-	tLED.setInterval( 500 + random(501) );
-	tLED.setCallback( &LEDOn);
-	tLED.enable();
-
-	return true; // Task should be enabled
-}
-
-// tBlink does not really need a callback function
-// since it just waits for 5 seconds for the first
-// and only iteration to occur. Once the iteration
-// takes place, tBlink is disabled by the Scheduler,
-// thus executing its OnDisable method below.
-
-void BlinkOnDisable() {
-	tLED.disable();
-}
-
-void LEDOn () {
-	digitalWrite(LED_BUILTIN , HIGH);
-	tLED.setCallback( &LEDOff);
-}
-
-void LEDOff () {
-	digitalWrite(LED_BUILTIN , LOW);
-	tLED.setCallback( &LEDOn);
-}
-
-// Note that LEDOff method serves as OnDisable method
-// to make sure the LED is turned off when the tBlink
-// task finishes (or disabled ahead of time)
-
-void setup() {
-// put your setup code here, to run once:
-  pinMode(LED_BUILTIN , OUTPUT);
-}
-
-void loop() {
-// put your main code here, to run repeatedly:
-	ts.execute();
+/**
+ * TaskScheduler Test of OnEnable and OnDisable methods and illustration of using wrapper tasks for timout purposes
+ *
+ * 	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. 
+ *
+ */
+
+#define _TASK_SLEEP_ON_IDLE_RUN
+#include <TaskScheduler.h>
+
+#ifndef LED_BUILTIN
+#define LED_BUILTIN 13    // define appropriate pin for your board
+#endif
+
+Scheduler ts;
+
+// 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
+							 //every 30 seconds for 5 seconds
+}
+
+
+// Upon being enabled, tBlink will define the parameters
+// and enable LED blinking task, which actually controls
+// the hardware (LED in this example)
+bool BlinkOnEnable() {
+	tLED.setInterval( 500 + random(501) );
+	tLED.setCallback( &LEDOn);
+	tLED.enable();
+
+	return true; // Task should be enabled
+}
+
+// tBlink does not really need a callback function
+// since it just waits for 5 seconds for the first
+// and only iteration to occur. Once the iteration
+// takes place, tBlink is disabled by the Scheduler,
+// thus executing its OnDisable method below.
+
+void BlinkOnDisable() {
+	tLED.disable();
+}
+
+void LEDOn () {
+	digitalWrite(LED_BUILTIN , HIGH);
+	tLED.setCallback( &LEDOff);
+}
+
+void LEDOff () {
+	digitalWrite(LED_BUILTIN , LOW);
+	tLED.setCallback( &LEDOn);
+}
+
+// Note that LEDOff method serves as OnDisable method
+// to make sure the LED is turned off when the tBlink
+// task finishes (or disabled ahead of time)
+
+void setup() {
+// put your setup code here, to run once:
+  pinMode(LED_BUILTIN , OUTPUT);
+}
+
+void loop() {
+// put your main code here, to run repeatedly:
+	ts.execute();
 }

+ 87 - 87
examples/Scheduler_example04_StatusRequest/Scheduler_example04_StatusRequest.ino

@@ -1,88 +1,88 @@
-/** This test demonstrates interaction between three simple tasks via StatusRequest object.
- *  Task T1 runs every 5 seconds and signals completion of a status request st.
- *  Tasks T2 and T3 are waiting on the same request (st)
- *  Task T3 does not renew its interest in status request st, so it is only invoked once (first iteration)
- *  Task T2 is invoked every time st completes, because it renews its interest in status of status request object st every iteration of T1
- */
- 
-#define _TASK_SLEEP_ON_IDLE_RUN
-#define _TASK_STATUS_REQUEST
-#include <TaskScheduler.h>
-
-StatusRequest st;
-
-Scheduler ts; 
-
-// 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);
-
-/** T1 callback
- *  T1 just signals completion of st every 5 seconds
- */
-void Callback1() {
-  Serial.println("T1: Signaling completion of ST");
-  st.signalComplete();
-}
-
-/** T1 On Disable callback
- *  This callback renews the status request and restarts T1 delayed to run again in 5 seconds
- */
-void Disable1() {
-  PrepareStatus();
-  t1.restartDelayed();
-}
-
-/** T2 callback
- *  Invoked when status request st completes
- */
-void Callback2() {
-  Serial.println("T2: Invoked due to completion of ST");  
-}
-
-
-/** T3 callback
- *  Invoked when status request st completes.
- *  This is only run once since T3 does not renew its interest in the status request st after first iteration
- */
- void Callback3() {
-  Serial.println("T3: Invoked due to completion of ST");  
-  
-}
-
-/** Prepare Status request st for another iteration
- *  
- */
-void PrepareStatus() {
-  st.setWaiting();         // set the statusrequest object for waiting 
-  t2.waitFor(&st);  // request tasks 1 & 2 to wait on the object st
-}
-
-
-/** Main Arduino code
- *  Not much to do here. Just init Serial and set the initial status request
- */
-void setup() {
-
-  Serial.begin(115200);
-  Serial.println("TaskScheduler: Status Request Test 1. Simple Test.");  
-
-  PrepareStatus();
-  t3.waitFor(&st);
-
-  t1.delay();
-}
-
-void loop() {
-
-  ts.execute();
-
+/** This test demonstrates interaction between three simple tasks via StatusRequest object.
+ *  Task T1 runs every 5 seconds and signals completion of a status request st.
+ *  Tasks T2 and T3 are waiting on the same request (st)
+ *  Task T3 does not renew its interest in status request st, so it is only invoked once (first iteration)
+ *  Task T2 is invoked every time st completes, because it renews its interest in status of status request object st every iteration of T1
+ */
+ 
+#define _TASK_SLEEP_ON_IDLE_RUN
+#define _TASK_STATUS_REQUEST
+#include <TaskScheduler.h>
+
+StatusRequest st;
+
+Scheduler ts; 
+
+// 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);
+
+/** T1 callback
+ *  T1 just signals completion of st every 5 seconds
+ */
+void Callback1() {
+  Serial.println("T1: Signaling completion of ST");
+  st.signalComplete();
+}
+
+/** T1 On Disable callback
+ *  This callback renews the status request and restarts T1 delayed to run again in 5 seconds
+ */
+void Disable1() {
+  PrepareStatus();
+  t1.restartDelayed();
+}
+
+/** T2 callback
+ *  Invoked when status request st completes
+ */
+void Callback2() {
+  Serial.println("T2: Invoked due to completion of ST");  
+}
+
+
+/** T3 callback
+ *  Invoked when status request st completes.
+ *  This is only run once since T3 does not renew its interest in the status request st after first iteration
+ */
+ void Callback3() {
+  Serial.println("T3: Invoked due to completion of ST");  
+  
+}
+
+/** Prepare Status request st for another iteration
+ *  
+ */
+void PrepareStatus() {
+  st.setWaiting();         // set the statusrequest object for waiting 
+  t2.waitFor(&st);  // request tasks 1 & 2 to wait on the object st
+}
+
+
+/** Main Arduino code
+ *  Not much to do here. Just init Serial and set the initial status request
+ */
+void setup() {
+
+  Serial.begin(115200);
+  Serial.println("TaskScheduler: Status Request Test 1. Simple Test.");  
+
+  PrepareStatus();
+  t3.waitFor(&st);
+
+  t1.delay();
+}
+
+void loop() {
+
+  ts.execute();
+
 }

+ 172 - 172
examples/Scheduler_example05_StatusRequest/Scheduler_example05_StatusRequest.ino

@@ -1,172 +1,172 @@
-/** This test emulates querying 3 sensors once every 10 seconds, each could respond with a different delay
- *    (ultrasonic sensors for instance) and printing a min value of the three when all three have reported their values.
- *    The overall timeout of 1 second is setup as well.
- *    An error message needs to be printed if a timeout occurred instead of a value.
- */
-
-
-#define _TASK_SLEEP_ON_IDLE_RUN
-#define _TASK_STATUS_REQUEST
-#include <TaskScheduler.h>
-
-StatusRequest measure;
-
-Scheduler ts; 
-
-// 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, 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;
-
-void CycleCallback() {
-  Serial.println("CycleCallback: Initiating measurement cycle every 10 seconds");
-
-  tMeasure.restartDelayed();
-}
-
-
-
-bool MeasureEnable() {
-  Serial.println("MeasureEnable: Activating sensors");  
-
-  distance = 0;
-  measure.setWaiting(3); // Set the StatusRequest to wait for 3 signals. 
-  tCalculate.waitFor(&measure);
-
-  tSensor1.restartDelayed();
-  tSensor2.restartDelayed();
-  tSensor3.restartDelayed();
-
-  return true;
-}
-
-void MeasureCallback() {
-  Serial.println("MeasureCallback: Invoked by calculate task or one second later");  
-
-  if (measure.pending()) {
-    tCalculate.disable();
-    measure.signalComplete(-1);  // signal error
-    Serial.println("MeasureCallback: Timeout!");
-  }
-  else {
-    Serial.print("MeasureCallback: Min distance=");Serial.println(distance);
-  }
-}
-
-void MeasureDisable() {
-  Serial.println("MeasureDisable: Cleaning up");  
-  
-  tSensor1.disable();
-  tSensor2.disable();
-  tSensor3.disable();
-}
-
-
-void CalcCallback() {
-  Serial.println("CalcCallback: calculating");  
-  distance = -1;
-  if ( measure.getStatus() >= 0) {  // only calculate if statusrequest ended successfully
-    distance = d1 < d2 ? d1 : d2;
-    distance = d3 < distance ? d3 : distance;
-    tMeasure.forceNextIteration();
-  }
-}
-
-
-/** Simulation code for sensor 1
- *  ----------------------------
- */
-bool S1Enable() {
-  Serial.print("S1Enable: Triggering sensor1. Delay=");  
-
-  tSensor1.setInterval( random(1200) );  // Simulating sensor delay, which could go over 1 second and cause timeout
-  d1 = 0;
-  
-  Serial.println( tSensor1.getInterval() );
-  return true;
-}
-
-void S1Callback() {
-  Serial.print("S1Callback: Emulating measurement. d1=");  
-  d1 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement 
-  measure.signal();
-  
-  Serial.println(d1); 
-}
-
-
-/** Simulation code for sensor 2
- *  ----------------------------
- */
-bool S2Enable() {
-  Serial.print("S2Enable: Triggering sensor2. Delay=");  
-
-  tSensor2.setInterval( random(1200) );  // Simulating sensor delay, which could go over 1 second and cause timeout
-  d2 = 0;
-
-  Serial.println( tSensor2.getInterval() );
-  return true;
-}
-
-void S2Callback() {
-  Serial.print("S2Callback: Emulating measurement. d2=");  
-  d2 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement
-  measure.signal();
-
-  Serial.println(d2);  
-}
-
-
-/** Simulation code for sensor 3
- *  ----------------------------
- */
-bool S3Enable() {
-  Serial.print("S3Enable: Triggering sensor3. Delay=");  
-
-  tSensor3.setInterval( random(1200) );  // Simulating sensor delay, which could go over 1 second and cause timeout
-  d3 = 0;
-
-  Serial.println( tSensor3.getInterval() );
-  return true;
-}
-
-void S3Callback() {
-  Serial.print("S3Callback: Emulating measurement. d3=");  
-  d3 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement
-  measure.signal();
-  
-  Serial.println(d3); 
-}
-
-
-/** Main Arduino code
- *  Not much is left here - everything is taken care of by the framework
- */
-void setup() {
-
-  Serial.begin(115200);
-  Serial.println("TaskScheduler StatusRequest Sensor Emulation Test. Complex Test.");  
-  randomSeed(analogRead(A0)+millis());
-}
-
-void loop() {
-  
-  ts.execute();
-
-}
+/** This test emulates querying 3 sensors once every 10 seconds, each could respond with a different delay
+ *    (ultrasonic sensors for instance) and printing a min value of the three when all three have reported their values.
+ *    The overall timeout of 1 second is setup as well.
+ *    An error message needs to be printed if a timeout occurred instead of a value.
+ */
+
+
+#define _TASK_SLEEP_ON_IDLE_RUN
+#define _TASK_STATUS_REQUEST
+#include <TaskScheduler.h>
+
+StatusRequest measure;
+
+Scheduler ts; 
+
+// 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, 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;
+
+void CycleCallback() {
+  Serial.println("CycleCallback: Initiating measurement cycle every 10 seconds");
+
+  tMeasure.restartDelayed();
+}
+
+
+
+bool MeasureEnable() {
+  Serial.println("MeasureEnable: Activating sensors");  
+
+  distance = 0;
+  measure.setWaiting(3); // Set the StatusRequest to wait for 3 signals. 
+  tCalculate.waitFor(&measure);
+
+  tSensor1.restartDelayed();
+  tSensor2.restartDelayed();
+  tSensor3.restartDelayed();
+
+  return true;
+}
+
+void MeasureCallback() {
+  Serial.println("MeasureCallback: Invoked by calculate task or one second later");  
+
+  if (measure.pending()) {
+    tCalculate.disable();
+    measure.signalComplete(-1);  // signal error
+    Serial.println("MeasureCallback: Timeout!");
+  }
+  else {
+    Serial.print("MeasureCallback: Min distance=");Serial.println(distance);
+  }
+}
+
+void MeasureDisable() {
+  Serial.println("MeasureDisable: Cleaning up");  
+  
+  tSensor1.disable();
+  tSensor2.disable();
+  tSensor3.disable();
+}
+
+
+void CalcCallback() {
+  Serial.println("CalcCallback: calculating");  
+  distance = -1;
+  if ( measure.getStatus() >= 0) {  // only calculate if statusrequest ended successfully
+    distance = d1 < d2 ? d1 : d2;
+    distance = d3 < distance ? d3 : distance;
+    tMeasure.forceNextIteration();
+  }
+}
+
+
+/** Simulation code for sensor 1
+ *  ----------------------------
+ */
+bool S1Enable() {
+  Serial.print("S1Enable: Triggering sensor1. Delay=");  
+
+  tSensor1.setInterval( random(1200) );  // Simulating sensor delay, which could go over 1 second and cause timeout
+  d1 = 0;
+  
+  Serial.println( tSensor1.getInterval() );
+  return true;
+}
+
+void S1Callback() {
+  Serial.print("S1Callback: Emulating measurement. d1=");  
+  d1 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement 
+  measure.signal();
+  
+  Serial.println(d1); 
+}
+
+
+/** Simulation code for sensor 2
+ *  ----------------------------
+ */
+bool S2Enable() {
+  Serial.print("S2Enable: Triggering sensor2. Delay=");  
+
+  tSensor2.setInterval( random(1200) );  // Simulating sensor delay, which could go over 1 second and cause timeout
+  d2 = 0;
+
+  Serial.println( tSensor2.getInterval() );
+  return true;
+}
+
+void S2Callback() {
+  Serial.print("S2Callback: Emulating measurement. d2=");  
+  d2 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement
+  measure.signal();
+
+  Serial.println(d2);  
+}
+
+
+/** Simulation code for sensor 3
+ *  ----------------------------
+ */
+bool S3Enable() {
+  Serial.print("S3Enable: Triggering sensor3. Delay=");  
+
+  tSensor3.setInterval( random(1200) );  // Simulating sensor delay, which could go over 1 second and cause timeout
+  d3 = 0;
+
+  Serial.println( tSensor3.getInterval() );
+  return true;
+}
+
+void S3Callback() {
+  Serial.print("S3Callback: Emulating measurement. d3=");  
+  d3 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement
+  measure.signal();
+  
+  Serial.println(d3); 
+}
+
+
+/** Main Arduino code
+ *  Not much is left here - everything is taken care of by the framework
+ */
+void setup() {
+
+  Serial.begin(115200);
+  Serial.println("TaskScheduler StatusRequest Sensor Emulation Test. Complex Test.");  
+  randomSeed(analogRead(A0)+millis());
+}
+
+void loop() {
+  
+  ts.execute();
+
+}

+ 81 - 81
examples/Scheduler_example06_IDLE/Scheduler_example06_IDLE.ino

@@ -1,82 +1,82 @@
-
-/**
- * This is a test to prove that processor really goes into IDLE sleep.
- * For this setup:
- * 
- *
- 
-Task c(10, -1, &Count, &ts);
-Task t(10000, 1, NULL, &ts, true, &tOn, &tOff);
-
-The result are:
-
-1): With #define _TASK_SLEEP_ON_IDLE_RUN enabled
-Start
-c1=10771 - v2.5.0 (v1.9.0: same)
-c2=1001
-
-
-and
-
-2): With #define _TASK_SLEEP_ON_IDLE_RUN disabled (commented out)
-Start
-c1=635735 - v2.5.0  (v1.9.0: 551947)
-c2=1001
-
-C1 is scenario 2) is much higher than in scenario 1) because processor is put to sleep for 1), but not for 2)
-
- */
-
-
-/**
- * Compile and run once with _TASK_SLEEP_ON_IDLE_RUN enabled, then with _TASK_SLEEP_ON_IDLE_RUN disabled.
- * Compare the results.
- */
- 
-//#define _TASK_SLEEP_ON_IDLE_RUN
-#include <TaskScheduler.h>
-
-Scheduler ts;
-
-// 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;
-bool tOn() {
-  c1 = 0;
-  c2 = 0;
-  c.enable(); 
-  
-  return true;
-}
-
-void tOff() {
-  c.disable();
-  Serial.print("c1=");Serial.println(c1);
-  Serial.print("c2=");Serial.println(c2);
-}
-
-void setup() {
-  // put your setup code here, to run once:
-  Serial.begin(115200);
-  Serial.println("Start");
-  t.delay();
- // ts.allowSleep(false);
-}
-
-void Count() {
-  c2++;
-}
-
-
-void loop() {
-  // put your main code here, to run repeatedly:
-  ts.execute();
-  c1++;
+
+/**
+ * This is a test to prove that processor really goes into IDLE sleep.
+ * For this setup:
+ * 
+ *
+ 
+Task c(10, -1, &Count, &ts);
+Task t(10000, 1, NULL, &ts, true, &tOn, &tOff);
+
+The result are:
+
+1): With #define _TASK_SLEEP_ON_IDLE_RUN enabled
+Start
+c1=10771 - v2.5.0 (v1.9.0: same)
+c2=1001
+
+
+and
+
+2): With #define _TASK_SLEEP_ON_IDLE_RUN disabled (commented out)
+Start
+c1=635735 - v2.5.0  (v1.9.0: 551947)
+c2=1001
+
+C1 is scenario 2) is much higher than in scenario 1) because processor is put to sleep for 1), but not for 2)
+
+ */
+
+
+/**
+ * Compile and run once with _TASK_SLEEP_ON_IDLE_RUN enabled, then with _TASK_SLEEP_ON_IDLE_RUN disabled.
+ * Compare the results.
+ */
+ 
+//#define _TASK_SLEEP_ON_IDLE_RUN
+#include <TaskScheduler.h>
+
+Scheduler ts;
+
+// 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;
+bool tOn() {
+  c1 = 0;
+  c2 = 0;
+  c.enable(); 
+  
+  return true;
+}
+
+void tOff() {
+  c.disable();
+  Serial.print("c1=");Serial.println(c1);
+  Serial.print("c2=");Serial.println(c2);
+}
+
+void setup() {
+  // put your setup code here, to run once:
+  Serial.begin(115200);
+  Serial.println("Start");
+  t.delay();
+ // ts.allowSleep(false);
+}
+
+void Count() {
+  c2++;
+}
+
+
+void loop() {
+  // put your main code here, to run repeatedly:
+  ts.execute();
+  c1++;
 }

+ 131 - 131
examples/Scheduler_example07_WDT/Scheduler_example07_WDT.ino

@@ -1,132 +1,132 @@
-/** 
- *  TaskScheduler Test sketch - use of task IDs and watchdog timer to identify hung tasks
- *  Test case: 
- *    Watchdog timer is set to 2 seconds (interrupt + reset)
- *    A hearbeat task (resetting the watchdog timer) is scheduled with 500 ms interval
- *    A number of tasks are running every 1 second and "rolling the dice" 0..19.  If 5, task is made to enter infinite loop
- *    Device should reset in 2 seconds after a task enters infinite loop
- *    A task id and a control point number are saved to EEPROM prior to device reset, and are displayed after reboot.
- *    In real life, device might chose to NOT activate certain tasks which failed previously (failed sensors for instance)
- */
-
-#define _TASK_SLEEP_ON_IDLE_RUN
-#define _TASK_WDT_IDS
-#include <TaskScheduler.h>
-
-#include <EEPROM.h>
-#include <avr/wdt.h>
-
-Scheduler ts;
-
-// Callback methods prototypes
-void TaskCB(); 
-void HB(); bool HBOn(); void HBOff();
-
-// Three tasks emulating accidental infinite loop
-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, TASK_FOREVER, &HB, &ts, false, &HBOn, &HBOff);
-
-/**
- * Emulating task callback function
- *   Prints task id and randomly "hangs" in two places.
- *   Control points are stored on the task prior to section which might hang,
- *   making this information available to the WDT interrupt handler
- */
-void TaskCB() {
-  Task& T = ts.currentTask();
-  
-  Serial.print("Task #:"); 
-  Serial.print(T.getId()); 
-  Serial.print(" current iteration = "); 
-  Serial.println(T.getRunCounter());
-
-// Hang if random number between 0 and 19 is 5 (5% probability)
-  T.setControlPoint(10);
-  if (random(20) == 5) for(;;);
-  
-// Hang if random number between 0 and 99 is more that 95 (5% probability)
-  T.setControlPoint(95);
-  if (random(100) > 94) for(;;);
-}
-
-/**
- * This On Enable method sets up the WDT
- * for interrupt and reset after 2 seconds
- */
-bool HBOn() {
-  
-  //disable interrupts
-  cli();
-  //reset watchdog
-  wdt_reset();
-  //set up WDT interrupt
-  WDTCSR = (1<<WDCE)|(1<<WDE);
-  //Start watchdog timer with aDelay prescaller
-  WDTCSR = (1<<WDIE)|(1<<WDE)|(WDTO_2S & 0x2F);
-//  WDTCSR = (1<<WDIE)|(WDTO_2S & 0x2F);  // interrupt only without reset
-  //Enable global interrupts
-  sei();
-}
-
-/**
- * This On Disable method disables WDT
- */
-void HBOff() {
-  wdt_disable();
-}
-
-/**
- * This is a periodic reset of WDT
- */
-void HB() {
-  wdt_reset();
-
-}
-
-/**
- * Watchdog timeout ISR
- * 
- */
-ISR(WDT_vect)
-{
-  Task& T = ts.currentTask();
-
-  digitalWrite(13, HIGH);
-  EEPROM.write(0, (byte)T.getId());
-  EEPROM.write(1, (byte)T.getControlPoint());
-  digitalWrite(13, LOW);
-}
-
-/**
- * Standard arduino setup routine
- */
-void setup() {
-  
-  Serial.begin(115200);
-
-  randomSeed(analogRead(0)+analogRead(5));
-  
-  pinMode(13, OUTPUT);
-  digitalWrite(13, LOW); 
-  
- 
-  Serial.println("WDT heartbeat test");
-  Serial.print("Last task before reset="); Serial.println(EEPROM.read(0));
-  Serial.print("Last control point before reset="); Serial.println(EEPROM.read(1));
-  
-  delay(2000);
-
-  tHB.enableDelayed();
-}
-
-/**
- * Not much is left for the loop()
- */
-void loop() {
-  ts.execute();
+/** 
+ *  TaskScheduler Test sketch - use of task IDs and watchdog timer to identify hung tasks
+ *  Test case: 
+ *    Watchdog timer is set to 2 seconds (interrupt + reset)
+ *    A hearbeat task (resetting the watchdog timer) is scheduled with 500 ms interval
+ *    A number of tasks are running every 1 second and "rolling the dice" 0..19.  If 5, task is made to enter infinite loop
+ *    Device should reset in 2 seconds after a task enters infinite loop
+ *    A task id and a control point number are saved to EEPROM prior to device reset, and are displayed after reboot.
+ *    In real life, device might chose to NOT activate certain tasks which failed previously (failed sensors for instance)
+ */
+
+#define _TASK_SLEEP_ON_IDLE_RUN
+#define _TASK_WDT_IDS
+#include <TaskScheduler.h>
+
+#include <EEPROM.h>
+#include <avr/wdt.h>
+
+Scheduler ts;
+
+// Callback methods prototypes
+void TaskCB(); 
+void HB(); bool HBOn(); void HBOff();
+
+// Three tasks emulating accidental infinite loop
+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, TASK_FOREVER, &HB, &ts, false, &HBOn, &HBOff);
+
+/**
+ * Emulating task callback function
+ *   Prints task id and randomly "hangs" in two places.
+ *   Control points are stored on the task prior to section which might hang,
+ *   making this information available to the WDT interrupt handler
+ */
+void TaskCB() {
+  Task& T = ts.currentTask();
+  
+  Serial.print("Task #:"); 
+  Serial.print(T.getId()); 
+  Serial.print(" current iteration = "); 
+  Serial.println(T.getRunCounter());
+
+// Hang if random number between 0 and 19 is 5 (5% probability)
+  T.setControlPoint(10);
+  if (random(20) == 5) for(;;);
+  
+// Hang if random number between 0 and 99 is more that 95 (5% probability)
+  T.setControlPoint(95);
+  if (random(100) > 94) for(;;);
+}
+
+/**
+ * This On Enable method sets up the WDT
+ * for interrupt and reset after 2 seconds
+ */
+bool HBOn() {
+  
+  //disable interrupts
+  cli();
+  //reset watchdog
+  wdt_reset();
+  //set up WDT interrupt
+  WDTCSR = (1<<WDCE)|(1<<WDE);
+  //Start watchdog timer with aDelay prescaller
+  WDTCSR = (1<<WDIE)|(1<<WDE)|(WDTO_2S & 0x2F);
+//  WDTCSR = (1<<WDIE)|(WDTO_2S & 0x2F);  // interrupt only without reset
+  //Enable global interrupts
+  sei();
+}
+
+/**
+ * This On Disable method disables WDT
+ */
+void HBOff() {
+  wdt_disable();
+}
+
+/**
+ * This is a periodic reset of WDT
+ */
+void HB() {
+  wdt_reset();
+
+}
+
+/**
+ * Watchdog timeout ISR
+ * 
+ */
+ISR(WDT_vect)
+{
+  Task& T = ts.currentTask();
+
+  digitalWrite(13, HIGH);
+  EEPROM.write(0, (byte)T.getId());
+  EEPROM.write(1, (byte)T.getControlPoint());
+  digitalWrite(13, LOW);
+}
+
+/**
+ * Standard arduino setup routine
+ */
+void setup() {
+  
+  Serial.begin(115200);
+
+  randomSeed(analogRead(0)+analogRead(5));
+  
+  pinMode(13, OUTPUT);
+  digitalWrite(13, LOW); 
+  
+ 
+  Serial.println("WDT heartbeat test");
+  Serial.print("Last task before reset="); Serial.println(EEPROM.read(0));
+  Serial.print("Last control point before reset="); Serial.println(EEPROM.read(1));
+  
+  delay(2000);
+
+  tHB.enableDelayed();
+}
+
+/**
+ * Not much is left for the loop()
+ */
+void loop() {
+  ts.execute();
 }

+ 146 - 146
examples/Scheduler_example08_LTS/Scheduler_example08_LTS.ino

@@ -1,147 +1,147 @@
-/** 
- *  TaskScheduler Test sketch - use of task's Local Task Storage pointer
- *  Test case: 
- *    Overall test runs for 5 seconds
- *    A number of calculator tasks run every one second, and update their respective variables using Local Task Storage pointer
- *    All calculator tasks use the same callback code, which obtains reference to appropriate variables via LTS pointer
- *    Calculaotr tasks perform simple calculation (as an example):
- *      adding task id number to itself
- *      multiplying task id number by 10
- *      
- *  Upon completion of the overall test, all results are printed out.
- *  Test could be repeated with various number of calculator tasks. 
- *  All that needs to change is data definitions - code is completely agnostic of number of tasks
- */
- 
-#define _TASK_SLEEP_ON_IDLE_RUN  // Compile with support for entering IDLE SLEEP state for 1 ms if not tasks are scheduled to run
-#define _TASK_WDT_IDS           // Compile with support for Task IDs and Watchdog timer
-#define _TASK_LTS_POINTER       // Compile with support for Local Task Storage pointer
-#include <TaskScheduler.h>
-
-// Overall number of calculator tasks:
-#define NO_TASKS  3
-
-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(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(5*TASK_SECOND, TASK_ONCE, NULL, &ts, false, &WrapperOn, &WrapperOff); 
-
-// The below structure is an object referenced by LTS pointer
-typedef struct {
-  unsigned int id;
-  long         sum;
-  long         product;
-} task_var;
-
-// These are actual structures which hold tasks specific values
-task_var v1;
-task_var v2; 
-task_var v3; 
-
-// Arrays below allow indexed access to specific tasks and tasks variables
-Task     *tasks[] = { &t1, &t2, &t3 };
-task_var *vars[]  = { &v1, &v2, &v3 };
-
-
-/**
- * This method is called when a wrapper task is enabled
- * The purpose is to supply LTS pointers to all the tasks
- */
-bool WrapperOn() {
-
-  for (int i=0; i < NO_TASKS; i++) {
-    Task& T = *tasks[i];
-    T.setLtsPointer( vars[i] );
-    T.enableDelayed();
-  }
-  
-  return true;  // Signal that Task could be enabled
-}
-
-/**
- * This method is called when Wrapper task is disabled (after first and only iteration is executed)
- * For each of the calculor tasks the results are printed out. 
- */
-void WrapperOff() {
-  Serial.println("Finished processing");
-  
-  ts.disableAll();
-  
-  for (int i=0; i < NO_TASKS; i++) {
-    Serial.print("ID: "); Serial.println(vars[i]->id);
-    Serial.print("Sum: "); Serial.println(vars[i]->sum);
-    Serial.print("Product: "); Serial.println(vars[i]->product);
-    Serial.println();
-  }
-}
-
-
-/**
- * This method is executed when each calculator task is enabled
- * The purpose is to initiate all local variables
- */
-bool CalcOn() {
-  Task& T = ts.currentTask();
-  task_var& var = *((task_var*) T.getLtsPointer());
-  
-// Initialize local variables
-  var.id = T.getId();
-  var.sum = 0;
-  var.product = var.id;  
-  
-  return true;
-}
-
-
-/**
- * This method performs simple calculations on task's local variables
- */
-void Calculate() {
-  Task& T = ts.currentTask();
-// Another way to get to LTS pointer:  
-  task_var& var = *((task_var*) ts.currentLts());
-
-
-  Serial.print("Calculating for task: ");
-  Serial.print(T.getId());
-  Serial.print("; Task id per LTS is: ");
-  Serial.println( var.id );
-  
-  var.sum += T.getId();
-  var.product = var.product * 10;
-  
-}
-
-
-/**
- * Standard Arduino setup and loop methods
- */
-void setup() {
-  Serial.begin(115200);
-
-  randomSeed(analogRead(0)+analogRead(5));
-  
-  pinMode(13, OUTPUT);
-  digitalWrite(13, LOW); 
- 
-  Serial.println("Local Task Storage pointer test");
-
-  tWrapper.enableDelayed();
-}
-
-void loop() {
-  ts.execute();
+/** 
+ *  TaskScheduler Test sketch - use of task's Local Task Storage pointer
+ *  Test case: 
+ *    Overall test runs for 5 seconds
+ *    A number of calculator tasks run every one second, and update their respective variables using Local Task Storage pointer
+ *    All calculator tasks use the same callback code, which obtains reference to appropriate variables via LTS pointer
+ *    Calculaotr tasks perform simple calculation (as an example):
+ *      adding task id number to itself
+ *      multiplying task id number by 10
+ *      
+ *  Upon completion of the overall test, all results are printed out.
+ *  Test could be repeated with various number of calculator tasks. 
+ *  All that needs to change is data definitions - code is completely agnostic of number of tasks
+ */
+ 
+#define _TASK_SLEEP_ON_IDLE_RUN  // Compile with support for entering IDLE SLEEP state for 1 ms if not tasks are scheduled to run
+#define _TASK_WDT_IDS           // Compile with support for Task IDs and Watchdog timer
+#define _TASK_LTS_POINTER       // Compile with support for Local Task Storage pointer
+#include <TaskScheduler.h>
+
+// Overall number of calculator tasks:
+#define NO_TASKS  3
+
+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(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(5*TASK_SECOND, TASK_ONCE, NULL, &ts, false, &WrapperOn, &WrapperOff); 
+
+// The below structure is an object referenced by LTS pointer
+typedef struct {
+  unsigned int id;
+  long         sum;
+  long         product;
+} task_var;
+
+// These are actual structures which hold tasks specific values
+task_var v1;
+task_var v2; 
+task_var v3; 
+
+// Arrays below allow indexed access to specific tasks and tasks variables
+Task     *tasks[] = { &t1, &t2, &t3 };
+task_var *vars[]  = { &v1, &v2, &v3 };
+
+
+/**
+ * This method is called when a wrapper task is enabled
+ * The purpose is to supply LTS pointers to all the tasks
+ */
+bool WrapperOn() {
+
+  for (int i=0; i < NO_TASKS; i++) {
+    Task& T = *tasks[i];
+    T.setLtsPointer( vars[i] );
+    T.enableDelayed();
+  }
+  
+  return true;  // Signal that Task could be enabled
+}
+
+/**
+ * This method is called when Wrapper task is disabled (after first and only iteration is executed)
+ * For each of the calculor tasks the results are printed out. 
+ */
+void WrapperOff() {
+  Serial.println("Finished processing");
+  
+  ts.disableAll();
+  
+  for (int i=0; i < NO_TASKS; i++) {
+    Serial.print("ID: "); Serial.println(vars[i]->id);
+    Serial.print("Sum: "); Serial.println(vars[i]->sum);
+    Serial.print("Product: "); Serial.println(vars[i]->product);
+    Serial.println();
+  }
+}
+
+
+/**
+ * This method is executed when each calculator task is enabled
+ * The purpose is to initiate all local variables
+ */
+bool CalcOn() {
+  Task& T = ts.currentTask();
+  task_var& var = *((task_var*) T.getLtsPointer());
+  
+// Initialize local variables
+  var.id = T.getId();
+  var.sum = 0;
+  var.product = var.id;  
+  
+  return true;
+}
+
+
+/**
+ * This method performs simple calculations on task's local variables
+ */
+void Calculate() {
+  Task& T = ts.currentTask();
+// Another way to get to LTS pointer:  
+  task_var& var = *((task_var*) ts.currentLts());
+
+
+  Serial.print("Calculating for task: ");
+  Serial.print(T.getId());
+  Serial.print("; Task id per LTS is: ");
+  Serial.println( var.id );
+  
+  var.sum += T.getId();
+  var.product = var.product * 10;
+  
+}
+
+
+/**
+ * Standard Arduino setup and loop methods
+ */
+void setup() {
+  Serial.begin(115200);
+
+  randomSeed(analogRead(0)+analogRead(5));
+  
+  pinMode(13, OUTPUT);
+  digitalWrite(13, LOW); 
+ 
+  Serial.println("Local Task Storage pointer test");
+
+  tWrapper.enableDelayed();
+}
+
+void loop() {
+  ts.execute();
 }

+ 57 - 57
examples/Scheduler_example09_TimeCritical/Scheduler_example09_TimeCritical.ino

@@ -1,57 +1,57 @@
-/** 
- *  TaskScheduler Test
- *  Illustration of use of Time Critical Information
- *
- *  Task1 runs every 1 second indefinitely
- *  On each run it reports how delayed the invokation of the callback method was,
- *  and what was the scheduling overun.
- *  Each run task 1 is dealyed randomly for up to 2 seconds, thus simulating scheduling overrun
- */
- 
- #define _TASK_TIMECRITICAL
- #define _TASK_SLEEP_ON_IDLE_RUN
-#include <TaskScheduler.h>
-
-// Callback methods prototypes
-void t1Callback();
-
-
-//Tasks
-Task t1(1000, -1, &t1Callback);
-
-Scheduler runner;
-
-
-void t1Callback() {
-    Serial.print(millis());
-    Serial.print(": overrun = ");
-    Serial.print(t1.getOverrun());
-    Serial.print(", start delayed by ");
-    Serial.println(t1.getStartDelay());
-    
-    int i = random(2000); 
-    Serial.print("Delaying for "); Serial.println(i);
-    delay(i);
-}
-
-void setup () {
-  Serial.begin(115200);
-  Serial.println("Scheduler TimeCritical TEST");
-  
-  runner.init();
-  Serial.println("Initialized scheduler");
-  
-  runner.addTask(t1);
-  Serial.println("added t1. Waiting for 5 seconds.");
-  
-  delay(5000);
-  
-  t1.enable();
-
-  Serial.println("Enabled t1");
-}
-
-
-void loop () {
-  runner.execute();
-}
+/** 
+ *  TaskScheduler Test
+ *  Illustration of use of Time Critical Information
+ *
+ *  Task1 runs every 1 second indefinitely
+ *  On each run it reports how delayed the invokation of the callback method was,
+ *  and what was the scheduling overun.
+ *  Each run task 1 is dealyed randomly for up to 2 seconds, thus simulating scheduling overrun
+ */
+ 
+ #define _TASK_TIMECRITICAL
+ #define _TASK_SLEEP_ON_IDLE_RUN
+#include <TaskScheduler.h>
+
+// Callback methods prototypes
+void t1Callback();
+
+
+//Tasks
+Task t1(1000, -1, &t1Callback);
+
+Scheduler runner;
+
+
+void t1Callback() {
+    Serial.print(millis());
+    Serial.print(": overrun = ");
+    Serial.print(t1.getOverrun());
+    Serial.print(", start delayed by ");
+    Serial.println(t1.getStartDelay());
+    
+    int i = random(2000); 
+    Serial.print("Delaying for "); Serial.println(i);
+    delay(i);
+}
+
+void setup () {
+  Serial.begin(115200);
+  Serial.println("Scheduler TimeCritical TEST");
+  
+  runner.init();
+  Serial.println("Initialized scheduler");
+  
+  runner.addTask(t1);
+  Serial.println("added t1. Waiting for 5 seconds.");
+  
+  delay(5000);
+  
+  t1.enable();
+
+  Serial.println("Enabled t1");
+}
+
+
+void loop () {
+  runner.execute();
+}

+ 106 - 106
examples/Scheduler_example10_Benchmark/Scheduler_example10_Benchmark.ino

@@ -1,106 +1,106 @@
-
-/**
- * This is a test to benchmark TaskScheduler execution.
- * 
- * This test executes 1,000,000 cycles of a task with empty callback method
- * Compiled with different options, you can assess the impact of each on the size of the Task object
- * and the execution overhead of the main execution pass route.
- *
- * Sample execution times (in milliseconds per 1M iterations) are provided below.
- * The test board is Arduino UNO 16MHz processor.
- *
- 
-TaskScheduler 2.1.0:
-No modifiers
-Duration=19869
-
-with SLEEP
-Duration=20058
-
-with status request:
-Duration=20058
-
-with time critical:
-Duration=27289
-
-
-TaskScheduler 1.9.0:
-No modifiers
-Duration=15656
-
-with SLEEP
-Duration=16285
-
-with status request:
-Duration=16600
-
-with rollover fix:
-Duration=18109
-
-
-TaskScheduler 1.8.5:
-Duration=15719
-
-with SLEEP
-Duration=16348
-
-with status request:
-Duration=18360
-
-with rollover fix:
-Duration=18423
-
- */
-
-
-//#define _TASK_TIMECRITICAL     // Enable monitoring scheduling overruns
-//#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_SLEEP_ON_IDLE_RUN
-//#define _TASK_MICRO_RES
-#include <TaskScheduler.h>
-
-Scheduler ts;
-
-// Callback methods prototypes
-bool tOn(); void tOff();
-void callback();
-
-// Tasks
-Task t(TASK_IMMEDIATE, 1000000, &callback, &ts, false, &tOn, &tOff);
-
-unsigned long c1, c2;
-
-bool tOn() {
-  c1 = millis();
-  c2 = 0;
-  
-  return true;
-}
-
-void tOff() {
-  c2 = millis();
-  Serial.println("done.");
-  Serial.print("Tstart =");Serial.println(c1);
-  Serial.print("Tfinish=");Serial.println(c2);
-  Serial.print("Duration=");Serial.println(c2-c1);
-}
-
-void setup() {
-  // put your setup code here, to run once:
-  Serial.begin(115200);
-  Serial.print("Start...");
-
-  t.enable();
-}
-
-void callback() {
-  
-}
-
-
-void loop() {
-  // put your main code here, to run repeatedly:
-  ts.execute();
-}
+
+/**
+ * This is a test to benchmark TaskScheduler execution.
+ * 
+ * This test executes 1,000,000 cycles of a task with empty callback method
+ * Compiled with different options, you can assess the impact of each on the size of the Task object
+ * and the execution overhead of the main execution pass route.
+ *
+ * Sample execution times (in milliseconds per 1M iterations) are provided below.
+ * The test board is Arduino UNO 16MHz processor.
+ *
+ 
+TaskScheduler 2.1.0:
+No modifiers
+Duration=19869
+
+with SLEEP
+Duration=20058
+
+with status request:
+Duration=20058
+
+with time critical:
+Duration=27289
+
+
+TaskScheduler 1.9.0:
+No modifiers
+Duration=15656
+
+with SLEEP
+Duration=16285
+
+with status request:
+Duration=16600
+
+with rollover fix:
+Duration=18109
+
+
+TaskScheduler 1.8.5:
+Duration=15719
+
+with SLEEP
+Duration=16348
+
+with status request:
+Duration=18360
+
+with rollover fix:
+Duration=18423
+
+ */
+
+
+//#define _TASK_TIMECRITICAL     // Enable monitoring scheduling overruns
+//#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_SLEEP_ON_IDLE_RUN
+//#define _TASK_MICRO_RES
+#include <TaskScheduler.h>
+
+Scheduler ts;
+
+// Callback methods prototypes
+bool tOn(); void tOff();
+void callback();
+
+// Tasks
+Task t(TASK_IMMEDIATE, 1000000, &callback, &ts, false, &tOn, &tOff);
+
+unsigned long c1, c2;
+
+bool tOn() {
+  c1 = millis();
+  c2 = 0;
+  
+  return true;
+}
+
+void tOff() {
+  c2 = millis();
+  Serial.println("done.");
+  Serial.print("Tstart =");Serial.println(c1);
+  Serial.print("Tfinish=");Serial.println(c2);
+  Serial.print("Duration=");Serial.println(c2-c1);
+}
+
+void setup() {
+  // put your setup code here, to run once:
+  Serial.begin(115200);
+  Serial.print("Start...");
+
+  t.enable();
+}
+
+void callback() {
+  
+}
+
+
+void loop() {
+  // put your main code here, to run repeatedly:
+  ts.execute();
+}

+ 1 - 1
examples/Scheduler_example11_Priority/Scheduler_example11_Priority.ino

@@ -102,4 +102,4 @@ void loop () {
   
   r.execute();
   
-}
+}

+ 1 - 1
examples/Scheduler_example12_Priority/Scheduler_example12_Priority.ino

@@ -133,4 +133,4 @@ void loop () {
   
   r.execute();
   
-}
+}

+ 1 - 1
examples/Scheduler_example13_Micros/Scheduler_example13_Micros.ino

@@ -68,4 +68,4 @@ void setup () {
 
 void loop () {
   runner.execute();
-}
+}

+ 339 - 339
examples/Scheduler_example14_Yield/Scheduler_example14_Yield.ino

@@ -1,339 +1,339 @@
-/**
-      This test illustrates the use if yield methods and internal StatusRequest objects
-      THIS TEST HAS BEEN TESTED ON NODEMCU V.2 (ESP8266)
-
-      The WiFi initialization and NTP update is executed in parallel to blinking the onboard LED
-      and an external LED connected to D2 (GPIO04)
-      Try running with and without correct WiFi parameters to observe the difference in behaviour
-*/
-
-#define _TASK_SLEEP_ON_IDLE_RUN
-#define _TASK_STATUS_REQUEST
-#include <TaskScheduler.h>
-
-#include <ESP8266WiFi.h>
-#include <WiFiUdp.h>
-
-Scheduler ts;
-
-// Callback methods prototypes
-void connectInit();
-void ledCallback();
-bool ledOnEnable();
-void ledOnDisable();
-void ledOn();
-void ledOff();
-void ntpUpdateInit();
-
-// Tasks
-
-Task  tConnect    (TASK_SECOND, TASK_FOREVER, &connectInit, &ts, true);
-Task  tLED        (TASK_IMMEDIATE, TASK_FOREVER, &ledCallback, &ts, false, &ledOnEnable, &ledOnDisable);
-
-// Tasks running on events
-Task  tNtpUpdate  (&ntpUpdateInit, &ts);
-
-// Replace with WiFi parameters of your Access Point/Router:
-const char *ssid  =  "wifi_network";
-const char *pwd   =  "wifi_password";
-
-long  ledDelayOn, ledDelayOff;
-
-#define LEDPIN            D0      // Onboard LED pin - linked to WiFi
-#define LEDPIN2           D2      // External LED
-#define CONNECT_TIMEOUT   30      // Seconds
-#define CONNECT_OK        0       // Status of successful connection to WiFi
-#define CONNECT_FAILED    (-99)   // Status of failed connection to WiFi
-
-// NTP Related Definitions
-#define NTP_PACKET_SIZE  48       // NTP time stamp is in the first 48 bytes of the message
-
-IPAddress     timeServerIP;       // time.nist.gov NTP server address
-const char*   ntpServerName = "time.nist.gov";
-byte          packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
-unsigned long epoch;
-
-WiFiUDP udp;                      // A UDP instance to let us send and receive packets over UDP
-
-#define LOCAL_NTP_PORT  2390      // Local UDP port for NTP update
-
-
-
-void setup() {
-  Serial.begin(74880);
-  Serial.println(F("TaskScheduler test #14 - Yield and internal StatusRequests"));
-  Serial.println(F("=========================================================="));
-  Serial.println();
-
-  pinMode (LEDPIN, OUTPUT);
-  pinMode (LEDPIN2, OUTPUT);
-
-  tNtpUpdate.waitFor( tConnect.getInternalStatusRequest() );  // NTP Task will start only after connection is made
-}
-
-void loop() {
-  ts.execute();                   // Only Scheduler should be executed in the loop
-}
-
-/**
-   Initiate connection to the WiFi network
-*/
-void connectInit() {
-  Serial.print(millis());
-  Serial.println(F(": connectInit."));
-  Serial.println(F("WiFi parameters: "));
-  Serial.print(F("SSID: ")); Serial.println(ssid);
-  Serial.print(F("PWD : ")); Serial.println(pwd);
-
-  WiFi.mode(WIFI_STA);
-  WiFi.hostname("esp8266");
-  WiFi.begin(ssid, pwd);
-  yield();
-
-  ledDelayOn = TASK_SECOND / 2;
-  ledDelayOff = TASK_SECOND / 4;
-  tLED.enable();
-
-  tConnect.yield(&connectCheck);            // This will pass control back to Scheduler and then continue with connection checking
-}
-
-/**
-   Periodically check if connected to WiFi
-   Re-request connection every 5 seconds
-   Stop trying after a timeout
-*/
-void connectCheck() {
-  Serial.print(millis());
-  Serial.println(F(": connectCheck."));
-
-  if (WiFi.status() == WL_CONNECTED) {                // Connection established
-    Serial.print(millis());
-    Serial.print(F(": Connected to AP. Local ip: "));
-    Serial.println(WiFi.localIP());
-    tConnect.disable();
-  }
-  else {
-
-    if (tConnect.getRunCounter() % 5 == 0) {          // re-request connection every 5 seconds
-
-      Serial.print(millis());
-      Serial.println(F(": Re-requesting connection to AP..."));
-
-      WiFi.disconnect(true);
-      yield();                                        // This is an esp8266 standard yield to allow linux wifi stack run
-      WiFi.hostname("esp8266");
-      WiFi.mode(WIFI_STA);
-      WiFi.begin(ssid, pwd);
-      yield();                                        // This is an esp8266 standard yield to allow linux wifi stack run
-    }
-
-    if (tConnect.getRunCounter() == CONNECT_TIMEOUT) {  // Connection Timeout
-      tConnect.getInternalStatusRequest()->signal(CONNECT_FAILED);  // Signal unsuccessful completion
-      tConnect.disable();
-
-      Serial.print(millis());
-      Serial.println(F(": connectOnDisable."));
-      Serial.print(millis());
-      Serial.println(F(": Unable to connect to WiFi."));
-
-      ledDelayOn = TASK_SECOND / 16;                  // Blink LEDs quickly due to error
-      ledDelayOff = TASK_SECOND / 16;
-      tLED.enable();
-    }
-  }
-}
-
-/**
-   Initiate NTP update if connection was established
-*/
-void ntpUpdateInit() {
-  Serial.print(millis());
-  Serial.println(F(": ntpUpdateInit."));
-
-  if ( tConnect.getInternalStatusRequest()->getStatus() != CONNECT_OK ) {  // Check status of the Connect Task
-    Serial.print(millis());
-    Serial.println(F(": cannot update NTP - not connected."));
-    return;
-  }
-
-  udp.begin(LOCAL_NTP_PORT);
-  if ( WiFi.hostByName(ntpServerName, timeServerIP) ) { //get a random server from the pool
-
-    Serial.print(millis());
-    Serial.print(F(": timeServerIP = "));
-    Serial.println(timeServerIP);
-
-    sendNTPpacket(timeServerIP); // send an NTP packet to a time server
-  }
-  else {
-    Serial.print(millis());
-    Serial.println(F(": NTP server address lookup failed."));
-    tLED.disable();
-    udp.stop();
-    tNtpUpdate.disable();
-    return;
-  }
-
-  ledDelayOn = TASK_SECOND / 8;
-  ledDelayOff = TASK_SECOND / 8;
-  tLED.enable();
-
-  tNtpUpdate.set( TASK_SECOND, CONNECT_TIMEOUT, &ntpCheck );
-  tNtpUpdate.enableDelayed();
-}
-
-/**
- * Check if NTP packet was received
- * Re-request every 5 seconds
- * Stop trying after a timeout
- */
-void ntpCheck() {
-  Serial.print(millis());
-  Serial.println(F(": ntpCheck."));
-
-  if ( tNtpUpdate.getRunCounter() % 5 == 0) {
-
-    Serial.print(millis());
-    Serial.println(F(": Re-requesting NTP update..."));
-
-    udp.stop();
-    yield();
-    udp.begin(LOCAL_NTP_PORT);
-    sendNTPpacket(timeServerIP);
-    return;
-  }
-
-  if ( doNtpUpdateCheck()) {
-    Serial.print(millis());
-    Serial.println(F(": NTP Update successful"));
-
-    Serial.print(millis());
-    Serial.print(F(": Unix time = "));
-    Serial.println(epoch);
-
-    tLED.disable();
-    tNtpUpdate.disable();
-    udp.stop();
-  }
-  else {
-    if ( tNtpUpdate.isLastIteration() ) {
-      Serial.print(millis());
-      Serial.println(F(": NTP Update failed"));
-      tLED.disable();
-      udp.stop();
-    }
-  }
-}
-
-/**
- * Send NTP packet to NTP server
- */
-unsigned long sendNTPpacket(IPAddress & address)
-{
-  Serial.print(millis());
-  Serial.println(F(": sendNTPpacket."));
-
-  // set all bytes in the buffer to 0
-  memset(packetBuffer, 0, NTP_PACKET_SIZE);
-  // Initialize values needed to form NTP request
-  // (see URL above for details on the packets)
-  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
-  packetBuffer[1] = 0;     // Stratum, or type of clock
-  packetBuffer[2] = 6;     // Polling Interval
-  packetBuffer[3] = 0xEC;  // Peer Clock Precision
-  // 8 bytes of zero for Root Delay & Root Dispersion
-  packetBuffer[12]  = 49;
-  packetBuffer[13]  = 0x4E;
-  packetBuffer[14]  = 49;
-  packetBuffer[15]  = 52;
-
-  // all NTP fields have been given values, now
-  // you can send a packet requesting a timestamp:
-  udp.beginPacket(address, 123); //NTP requests are to port 123
-  udp.write(packetBuffer, NTP_PACKET_SIZE);
-  udp.endPacket();
-  yield();
-}
-
-/**
- * Check if a packet was recieved.
- * Process NTP information if yes
- */
-bool doNtpUpdateCheck() {
-
-  Serial.print(millis());
-  Serial.println(F(": doNtpUpdateCheck."));
-
-  yield();
-  int cb = udp.parsePacket();
-  if (cb) {
-    // We've received a packet, read the data from it
-    udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
-
-    //the timestamp starts at byte 40 of the received packet and is four bytes,
-    // or two words, long. First, esxtract the two words:
-
-    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
-    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
-    // combine the four bytes (two words) into a long integer
-    // this is NTP time (seconds since Jan 1 1900):
-    unsigned long secsSince1900 = highWord << 16 | lowWord;
-
-    // now convert NTP time into everyday time:
-    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
-    const unsigned long seventyYears = 2208988800UL;
-    // subtract seventy years:
-    epoch = secsSince1900 - seventyYears;
-    return (epoch != 0);
-  }
-  return false;
-}
-
-/**
- * Flip the LED state based on the current state
- */
-bool ledState;
-void ledCallback() {
-  if ( ledState ) ledOff();
-  else ledOn();
-}
-
-/**
- * Make sure the LED starts lit
- */
-bool ledOnEnable() {
-  ledOn();
-  return true;
-}
-
-/**
- * Make sure LED ends dimmed
- */
-void ledOnDisable() {
-  ledOff();
-}
-
-/**
- * Turn LEDs on. 
- * Set appropriate delay.
- * PLEASE NOTE: NodeMCU onbaord LED is active-low
- */
-void ledOn() {
-  ledState = true;
-  digitalWrite(LEDPIN, LOW);
-  digitalWrite(LEDPIN2, HIGH);
-  tLED.delay( ledDelayOn );
-}
-
-/**
- * Turn LEDs off. 
- * Set appropriate delay.
- * PLEASE NOTE: NodeMCU onbaord LED is active-low
- */
-void ledOff() {
-  ledState = false;
-  digitalWrite(LEDPIN, HIGH);
-  digitalWrite(LEDPIN2, LOW);
-  tLED.delay( ledDelayOff );
-}
-
+/**
+      This test illustrates the use if yield methods and internal StatusRequest objects
+      THIS TEST HAS BEEN TESTED ON NODEMCU V.2 (ESP8266)
+
+      The WiFi initialization and NTP update is executed in parallel to blinking the onboard LED
+      and an external LED connected to D2 (GPIO04)
+      Try running with and without correct WiFi parameters to observe the difference in behaviour
+*/
+
+#define _TASK_SLEEP_ON_IDLE_RUN
+#define _TASK_STATUS_REQUEST
+#include <TaskScheduler.h>
+
+#include <ESP8266WiFi.h>
+#include <WiFiUdp.h>
+
+Scheduler ts;
+
+// Callback methods prototypes
+void connectInit();
+void ledCallback();
+bool ledOnEnable();
+void ledOnDisable();
+void ledOn();
+void ledOff();
+void ntpUpdateInit();
+
+// Tasks
+
+Task  tConnect    (TASK_SECOND, TASK_FOREVER, &connectInit, &ts, true);
+Task  tLED        (TASK_IMMEDIATE, TASK_FOREVER, &ledCallback, &ts, false, &ledOnEnable, &ledOnDisable);
+
+// Tasks running on events
+Task  tNtpUpdate  (&ntpUpdateInit, &ts);
+
+// Replace with WiFi parameters of your Access Point/Router:
+const char *ssid  =  "wifi_network";
+const char *pwd   =  "wifi_password";
+
+long  ledDelayOn, ledDelayOff;
+
+#define LEDPIN            D0      // Onboard LED pin - linked to WiFi
+#define LEDPIN2           D2      // External LED
+#define CONNECT_TIMEOUT   30      // Seconds
+#define CONNECT_OK        0       // Status of successful connection to WiFi
+#define CONNECT_FAILED    (-99)   // Status of failed connection to WiFi
+
+// NTP Related Definitions
+#define NTP_PACKET_SIZE  48       // NTP time stamp is in the first 48 bytes of the message
+
+IPAddress     timeServerIP;       // time.nist.gov NTP server address
+const char*   ntpServerName = "time.nist.gov";
+byte          packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
+unsigned long epoch;
+
+WiFiUDP udp;                      // A UDP instance to let us send and receive packets over UDP
+
+#define LOCAL_NTP_PORT  2390      // Local UDP port for NTP update
+
+
+
+void setup() {
+  Serial.begin(74880);
+  Serial.println(F("TaskScheduler test #14 - Yield and internal StatusRequests"));
+  Serial.println(F("=========================================================="));
+  Serial.println();
+
+  pinMode (LEDPIN, OUTPUT);
+  pinMode (LEDPIN2, OUTPUT);
+
+  tNtpUpdate.waitFor( tConnect.getInternalStatusRequest() );  // NTP Task will start only after connection is made
+}
+
+void loop() {
+  ts.execute();                   // Only Scheduler should be executed in the loop
+}
+
+/**
+   Initiate connection to the WiFi network
+*/
+void connectInit() {
+  Serial.print(millis());
+  Serial.println(F(": connectInit."));
+  Serial.println(F("WiFi parameters: "));
+  Serial.print(F("SSID: ")); Serial.println(ssid);
+  Serial.print(F("PWD : ")); Serial.println(pwd);
+
+  WiFi.mode(WIFI_STA);
+  WiFi.hostname("esp8266");
+  WiFi.begin(ssid, pwd);
+  yield();
+
+  ledDelayOn = TASK_SECOND / 2;
+  ledDelayOff = TASK_SECOND / 4;
+  tLED.enable();
+
+  tConnect.yield(&connectCheck);            // This will pass control back to Scheduler and then continue with connection checking
+}
+
+/**
+   Periodically check if connected to WiFi
+   Re-request connection every 5 seconds
+   Stop trying after a timeout
+*/
+void connectCheck() {
+  Serial.print(millis());
+  Serial.println(F(": connectCheck."));
+
+  if (WiFi.status() == WL_CONNECTED) {                // Connection established
+    Serial.print(millis());
+    Serial.print(F(": Connected to AP. Local ip: "));
+    Serial.println(WiFi.localIP());
+    tConnect.disable();
+  }
+  else {
+
+    if (tConnect.getRunCounter() % 5 == 0) {          // re-request connection every 5 seconds
+
+      Serial.print(millis());
+      Serial.println(F(": Re-requesting connection to AP..."));
+
+      WiFi.disconnect(true);
+      yield();                                        // This is an esp8266 standard yield to allow linux wifi stack run
+      WiFi.hostname("esp8266");
+      WiFi.mode(WIFI_STA);
+      WiFi.begin(ssid, pwd);
+      yield();                                        // This is an esp8266 standard yield to allow linux wifi stack run
+    }
+
+    if (tConnect.getRunCounter() == CONNECT_TIMEOUT) {  // Connection Timeout
+      tConnect.getInternalStatusRequest()->signal(CONNECT_FAILED);  // Signal unsuccessful completion
+      tConnect.disable();
+
+      Serial.print(millis());
+      Serial.println(F(": connectOnDisable."));
+      Serial.print(millis());
+      Serial.println(F(": Unable to connect to WiFi."));
+
+      ledDelayOn = TASK_SECOND / 16;                  // Blink LEDs quickly due to error
+      ledDelayOff = TASK_SECOND / 16;
+      tLED.enable();
+    }
+  }
+}
+
+/**
+   Initiate NTP update if connection was established
+*/
+void ntpUpdateInit() {
+  Serial.print(millis());
+  Serial.println(F(": ntpUpdateInit."));
+
+  if ( tConnect.getInternalStatusRequest()->getStatus() != CONNECT_OK ) {  // Check status of the Connect Task
+    Serial.print(millis());
+    Serial.println(F(": cannot update NTP - not connected."));
+    return;
+  }
+
+  udp.begin(LOCAL_NTP_PORT);
+  if ( WiFi.hostByName(ntpServerName, timeServerIP) ) { //get a random server from the pool
+
+    Serial.print(millis());
+    Serial.print(F(": timeServerIP = "));
+    Serial.println(timeServerIP);
+
+    sendNTPpacket(timeServerIP); // send an NTP packet to a time server
+  }
+  else {
+    Serial.print(millis());
+    Serial.println(F(": NTP server address lookup failed."));
+    tLED.disable();
+    udp.stop();
+    tNtpUpdate.disable();
+    return;
+  }
+
+  ledDelayOn = TASK_SECOND / 8;
+  ledDelayOff = TASK_SECOND / 8;
+  tLED.enable();
+
+  tNtpUpdate.set( TASK_SECOND, CONNECT_TIMEOUT, &ntpCheck );
+  tNtpUpdate.enableDelayed();
+}
+
+/**
+ * Check if NTP packet was received
+ * Re-request every 5 seconds
+ * Stop trying after a timeout
+ */
+void ntpCheck() {
+  Serial.print(millis());
+  Serial.println(F(": ntpCheck."));
+
+  if ( tNtpUpdate.getRunCounter() % 5 == 0) {
+
+    Serial.print(millis());
+    Serial.println(F(": Re-requesting NTP update..."));
+
+    udp.stop();
+    yield();
+    udp.begin(LOCAL_NTP_PORT);
+    sendNTPpacket(timeServerIP);
+    return;
+  }
+
+  if ( doNtpUpdateCheck()) {
+    Serial.print(millis());
+    Serial.println(F(": NTP Update successful"));
+
+    Serial.print(millis());
+    Serial.print(F(": Unix time = "));
+    Serial.println(epoch);
+
+    tLED.disable();
+    tNtpUpdate.disable();
+    udp.stop();
+  }
+  else {
+    if ( tNtpUpdate.isLastIteration() ) {
+      Serial.print(millis());
+      Serial.println(F(": NTP Update failed"));
+      tLED.disable();
+      udp.stop();
+    }
+  }
+}
+
+/**
+ * Send NTP packet to NTP server
+ */
+unsigned long sendNTPpacket(IPAddress & address)
+{
+  Serial.print(millis());
+  Serial.println(F(": sendNTPpacket."));
+
+  // set all bytes in the buffer to 0
+  memset(packetBuffer, 0, NTP_PACKET_SIZE);
+  // Initialize values needed to form NTP request
+  // (see URL above for details on the packets)
+  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
+  packetBuffer[1] = 0;     // Stratum, or type of clock
+  packetBuffer[2] = 6;     // Polling Interval
+  packetBuffer[3] = 0xEC;  // Peer Clock Precision
+  // 8 bytes of zero for Root Delay & Root Dispersion
+  packetBuffer[12]  = 49;
+  packetBuffer[13]  = 0x4E;
+  packetBuffer[14]  = 49;
+  packetBuffer[15]  = 52;
+
+  // all NTP fields have been given values, now
+  // you can send a packet requesting a timestamp:
+  udp.beginPacket(address, 123); //NTP requests are to port 123
+  udp.write(packetBuffer, NTP_PACKET_SIZE);
+  udp.endPacket();
+  yield();
+}
+
+/**
+ * Check if a packet was recieved.
+ * Process NTP information if yes
+ */
+bool doNtpUpdateCheck() {
+
+  Serial.print(millis());
+  Serial.println(F(": doNtpUpdateCheck."));
+
+  yield();
+  int cb = udp.parsePacket();
+  if (cb) {
+    // We've received a packet, read the data from it
+    udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
+
+    //the timestamp starts at byte 40 of the received packet and is four bytes,
+    // or two words, long. First, esxtract the two words:
+
+    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
+    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
+    // combine the four bytes (two words) into a long integer
+    // this is NTP time (seconds since Jan 1 1900):
+    unsigned long secsSince1900 = highWord << 16 | lowWord;
+
+    // now convert NTP time into everyday time:
+    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
+    const unsigned long seventyYears = 2208988800UL;
+    // subtract seventy years:
+    epoch = secsSince1900 - seventyYears;
+    return (epoch != 0);
+  }
+  return false;
+}
+
+/**
+ * Flip the LED state based on the current state
+ */
+bool ledState;
+void ledCallback() {
+  if ( ledState ) ledOff();
+  else ledOn();
+}
+
+/**
+ * Make sure the LED starts lit
+ */
+bool ledOnEnable() {
+  ledOn();
+  return true;
+}
+
+/**
+ * Make sure LED ends dimmed
+ */
+void ledOnDisable() {
+  ledOff();
+}
+
+/**
+ * Turn LEDs on. 
+ * Set appropriate delay.
+ * PLEASE NOTE: NodeMCU onbaord LED is active-low
+ */
+void ledOn() {
+  ledState = true;
+  digitalWrite(LEDPIN, LOW);
+  digitalWrite(LEDPIN2, HIGH);
+  tLED.delay( ledDelayOn );
+}
+
+/**
+ * Turn LEDs off. 
+ * Set appropriate delay.
+ * PLEASE NOTE: NodeMCU onbaord LED is active-low
+ */
+void ledOff() {
+  ledState = false;
+  digitalWrite(LEDPIN, HIGH);
+  digitalWrite(LEDPIN2, LOW);
+  tLED.delay( ledDelayOff );
+}
+

+ 52 - 52
examples/Scheduler_example15_STDFunction/Scheduler_example15_STDFunction.ino

@@ -1,53 +1,53 @@
-/** 
- * TaskScheduler Test sketch - Showing how to use std::function
- * to get acces to variables from within the task callback function 
- * 
- * Support for std::function is only available for ESP8266 architecture
- */
-#define _TASK_SLEEP_ON_IDLE_RUN
-#define _TASK_STD_FUNCTION   // Compile with support for std::function 
-#include <TaskScheduler.h>
-
-Scheduler ts;
-int counter = 0;
-
-class Calculator {
-public:
-    int cumSum = 0; // cumulative sum
-    Calculator(int b) {
-        // Pass the this pointer, so that we get access to this->cumSum
-        // Also pass a copy of b
-        calculateTask.set(TASK_SECOND, TASK_FOREVER, [this, b]() {
-            counter++;
-            Serial.printf("%u. %u: cumSum = %u + %u\t", counter, millis(), cumSum, b);
-            cumSum += b;
-            Serial.printf("Resulting cumulative sum: %u\n", cumSum);
-        });
-        ts.addTask(calculateTask);
-        calculateTask.enable();
-    }
-
-    Task calculateTask;
-};
-
-Calculator calc1(2);
-Calculator calc2(4);
-Calculator calc3(8);
-
-// Disable tasks after 10 seconds
-Task tWrapper(10*TASK_SECOND, TASK_ONCE, []() {
-    ts.disableAll();
-}, &ts); 
-
-/**
- * Standard Arduino setup and loop methods
- */
-void setup() {
-  Serial.begin(74880);
-  Serial.println("std::function test");
-  tWrapper.enableDelayed();
-}
-
-void loop() {
-  ts.execute();
+/** 
+ * TaskScheduler Test sketch - Showing how to use std::function
+ * to get acces to variables from within the task callback function 
+ * 
+ * Support for std::function is only available for ESP8266 architecture
+ */
+#define _TASK_SLEEP_ON_IDLE_RUN
+#define _TASK_STD_FUNCTION   // Compile with support for std::function 
+#include <TaskScheduler.h>
+
+Scheduler ts;
+int counter = 0;
+
+class Calculator {
+public:
+    int cumSum = 0; // cumulative sum
+    Calculator(int b) {
+        // Pass the this pointer, so that we get access to this->cumSum
+        // Also pass a copy of b
+        calculateTask.set(TASK_SECOND, TASK_FOREVER, [this, b]() {
+            counter++;
+            Serial.printf("%u. %u: cumSum = %u + %u\t", counter, millis(), cumSum, b);
+            cumSum += b;
+            Serial.printf("Resulting cumulative sum: %u\n", cumSum);
+        });
+        ts.addTask(calculateTask);
+        calculateTask.enable();
+    }
+
+    Task calculateTask;
+};
+
+Calculator calc1(2);
+Calculator calc2(4);
+Calculator calc3(8);
+
+// Disable tasks after 10 seconds
+Task tWrapper(10*TASK_SECOND, TASK_ONCE, []() {
+    ts.disableAll();
+}, &ts); 
+
+/**
+ * Standard Arduino setup and loop methods
+ */
+void setup() {
+  Serial.begin(74880);
+  Serial.println("std::function test");
+  tWrapper.enableDelayed();
+}
+
+void loop() {
+  ts.execute();
 }

+ 96 - 95
keywords.txt

@@ -1,95 +1,96 @@
-#######################################
-# Syntax Coloring Map For TaskManager
-#######################################
-
-#######################################
-# Datatypes (KEYWORD1)
-#######################################
-
-Task	KEYWORD1
-Scheduler	KEYWORD1
-StatusRequest	KEYWORD1
-
-#######################################
-# Methods and Functions (KEYWORD2)
-#######################################
-
-init	KEYWORD2
-addTask	KEYWORD2
-deleteTask	KEYWORD2
-disableAll	KEYWORD2
-enableAll	KEYWORD2
-currentTask	KEYWORD2
-currentLts	KEYWORD2
-execute	KEYWORD2
-timeUntilNextIteration	KEYWORD2
-startNow	KEYWORD2
-allowSleep	KEYWORD2
-enable	KEYWORD2
-enableIfNot	KEYWORD2
-enableDelayed	KEYWORD2
-delay	KEYWORD2
-forceNextIteration	KEYWORD2
-restart	KEYWORD2
-restartDelayed	KEYWORD2
-disable	KEYWORD2
-isEnabled	KEYWORD2
-set	KEYWORD2
-setInterval	KEYWORD2
-getInterval	KEYWORD2
-setIterations	KEYWORD2
-getIterations	KEYWORD2
-getRunCounter	KEYWORD2
-setCallback	KEYWORD2
-setOnEnable	KEYWORD2
-setOnDisable	KEYWORD2
-disableOnLastIteration	KEYWORD2
-yield	KEYWORD2
-yieldOnce	KEYWORD2
-getInternalStatusRequest	KEYWORD2
-getCount	KEYWORD2
-getOverrun	KEYWORD2
-getStartDelay	KEYWORD2
-isFirstIteration	KEYWORD2
-isLastIteration	KEYWORD2
-setWaiting	KEYWORD2
-signal	KEYWORD2
-signalComplete	KEYWORD2
-pending	KEYWORD2
-completed	KEYWORD2
-getStatus	KEYWORD2
-waitFor	KEYWORD2
-waitForDelayed	KEYWORD2
-getStatusRequest	KEYWORD2
-getCount	KEYWORD2
-setId	KEYWORD2
-getId	KEYWORD2
-setControlPoint	KEYWORD2
-getControlPoint	KEYWORD2
-setLtsPointer	KEYWORD2
-getLtsPointer	KEYWORD2
-isOverrun	KEYWORD2
-setHighPriorityScheduler	KEYWORD2
-currentScheduler	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_PRIORITY	LITERAL1
-_TASK_MICRO_RES	LITERAL1
-_TASK_STD_FUNCTION	LITERAL1
-_TASK_DEBUG	LITERAL1
-TaskCallback	LITERAL1
-TaskOnDisable	LITERAL1
-TaskOnEnable	LITERAL1
-#######################################
-
+#######################################
+# Syntax Coloring Map For TaskManager
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+Task	KEYWORD1
+Scheduler	KEYWORD1
+StatusRequest	KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+init	KEYWORD2
+addTask	KEYWORD2
+deleteTask	KEYWORD2
+disableAll	KEYWORD2
+enableAll	KEYWORD2
+currentTask	KEYWORD2
+currentLts	KEYWORD2
+execute	KEYWORD2
+timeUntilNextIteration	KEYWORD2
+startNow	KEYWORD2
+allowSleep	KEYWORD2
+enable	KEYWORD2
+enableIfNot	KEYWORD2
+enableDelayed	KEYWORD2
+delay	KEYWORD2
+forceNextIteration	KEYWORD2
+restart	KEYWORD2
+restartDelayed	KEYWORD2
+disable	KEYWORD2
+isEnabled	KEYWORD2
+set	KEYWORD2
+setInterval	KEYWORD2
+getInterval	KEYWORD2
+setIterations	KEYWORD2
+getIterations	KEYWORD2
+getRunCounter	KEYWORD2
+setCallback	KEYWORD2
+setOnEnable	KEYWORD2
+setOnDisable	KEYWORD2
+disableOnLastIteration	KEYWORD2
+yield	KEYWORD2
+yieldOnce	KEYWORD2
+getInternalStatusRequest	KEYWORD2
+getCount	KEYWORD2
+getOverrun	KEYWORD2
+getStartDelay	KEYWORD2
+isFirstIteration	KEYWORD2
+isLastIteration	KEYWORD2
+setWaiting	KEYWORD2
+signal	KEYWORD2
+signalComplete	KEYWORD2
+pending	KEYWORD2
+completed	KEYWORD2
+getStatus	KEYWORD2
+waitFor	KEYWORD2
+waitForDelayed	KEYWORD2
+getStatusRequest	KEYWORD2
+getCount	KEYWORD2
+setId	KEYWORD2
+getId	KEYWORD2
+setControlPoint	KEYWORD2
+getControlPoint	KEYWORD2
+setLtsPointer	KEYWORD2
+getLtsPointer	KEYWORD2
+isOverrun	KEYWORD2
+setHighPriorityScheduler	KEYWORD2
+currentScheduler	KEYWORD2
+#######################################
+# Constants (LITERAL1)
+TASK_MILLISECOND	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_PRIORITY	LITERAL1
+_TASK_MICRO_RES	LITERAL1
+_TASK_STD_FUNCTION	LITERAL1
+_TASK_DEBUG	LITERAL1
+TaskCallback	LITERAL1
+TaskOnDisable	LITERAL1
+TaskOnEnable	LITERAL1
+#######################################
+

+ 22 - 22
library.json

@@ -1,22 +1,22 @@
-{
-  "name": "TaskScheduler",
-  "keywords": "multitasking, cooperative, event, task, taskscheduler, scheduling",
-  "description": "Cooperative multitasking for Arduino and ESP8266 microcontrollers",
-  "repository":
-  {
-    "type": "git",
-    "url": "https://github.com/arkhipenko/TaskScheduler"
-  },
-  "authors":
-  [
-    {
-      "name": "Anatoli Arkhipenko",
-      "email": "arkhipenko@hotmail.com",
-      "url": "https://github.com/arkhipenko",
-      "maintainer": true
-    }
-  ],
-  "version": "2.5.0",
-  "frameworks": "arduino",
-  "platforms": "*"
-}
+{
+  "name": "TaskScheduler",
+  "keywords": "multitasking, cooperative, event, task, taskscheduler, scheduling",
+  "description": "Cooperative multitasking for Arduino and ESP8266 microcontrollers",
+  "repository":
+  {
+    "type": "git",
+    "url": "https://github.com/arkhipenko/TaskScheduler"
+  },
+  "authors":
+  [
+    {
+      "name": "Anatoli Arkhipenko",
+      "email": "arkhipenko@hotmail.com",
+      "url": "https://github.com/arkhipenko",
+      "maintainer": true
+    }
+  ],
+  "version": "2.5.0",
+  "frameworks": "arduino",
+  "platforms": "*"
+}

+ 9 - 9
library.properties

@@ -1,9 +1,9 @@
-name=TaskScheduler
-version=2.5.0
-author=Anatoli Arkhipenko <arkhipenko@hotmail.com>
-maintainer=Anatoli Arkhipenko <arkhipenko@hotmail.com>
-sentence=A light-weight cooperative multitasking library for arduino and esp8266 microcontrollers.
-paragraph=Supports: periodic task execution (with dynamic execution period in milliseconds or microseconds – frequency of execution), number of iterations (limited or infinite number of iterations), execution of tasks in predefined sequence, dynamic change of task execution parameters (frequency, number of iterations, callback methods), power saving via entering IDLE sleep mode when tasks are not scheduled to run, event-driven task invocation via Status Request object, task IDs and Control Points for error handling and watchdog timer, Local Task Storage pointer (allowing use of same callback code for multiple tasks), layered task prioritization, std::functions (esp8266 only).
-category=Timing
-url=https://github.com/arkhipenko/TaskScheduler.git
-architectures=*
+name=TaskScheduler
+version=2.5.0
+author=Anatoli Arkhipenko <arkhipenko@hotmail.com>
+maintainer=Anatoli Arkhipenko <arkhipenko@hotmail.com>
+sentence=A light-weight cooperative multitasking library for arduino and esp8266 microcontrollers.
+paragraph=Supports: periodic task execution (with dynamic execution period in milliseconds or microseconds – frequency of execution), number of iterations (limited or infinite number of iterations), execution of tasks in predefined sequence, dynamic change of task execution parameters (frequency, number of iterations, callback methods), power saving via entering IDLE sleep mode when tasks are not scheduled to run, event-driven task invocation via Status Request object, task IDs and Control Points for error handling and watchdog timer, Local Task Storage pointer (allowing use of same callback code for multiple tasks), layered task prioritization, std::functions (esp8266 only).
+category=Timing
+url=https://github.com/arkhipenko/TaskScheduler.git
+architectures=*

+ 813 - 809
src/TaskScheduler.h

@@ -1,809 +1,813 @@
-// Cooperative multitasking library for Arduino
-// Copyright (c) 2015-2017 Anatoli Arkhipenko
-//
-// Changelog:
-// v1.0.0:
-//     2015-02-24 - Initial release 
-//     2015-02-28 - added delay() and disableOnLastIteration() methods
-//     2015-03-25 - changed scheduler execute() method for a more precise delay calculation:
-//                  1. Do not delay if any of the tasks ran (making request for immediate execution redundant)
-//                  2. Delay is invoked only if none of the tasks ran 
-//                  3. Delay is based on the min anticipated wait until next task _AND_ the runtime of execute method itself.
-//     2015-05-11 - added  restart() and restartDelayed() methods to restart tasks which are on hold after running all iterations
-//     2015-05-19 - completely removed  delay from the scheduler since there are no power saving there. using 1 ms sleep instead
-//
-// v1.4.1:
-//     2015-09-15 - more careful placement of AVR-specific includes for sleep method (compatibility with DUE)
-//                  sleep on idle run is no longer a default and should be explicitly compiled with
-//                 _TASK_SLEEP_ON_IDLE_RUN defined
-//
-// v1.5.0:
-//     2015-09-20 - access to currently executing task (for callback methods)
-//     2015-09-20 - pass scheduler as a parameter to the task constructor to append the task to the end of the chain
-//     2015-09-20 - option to create a task already enabled
-//
-// v1.5.1:
-//     2015-09-21 - bug fix: incorrect handling of active tasks via set() and setIterations(). 
-//                  Thanks to Hannes Morgenstern for catching this one
-//
-// v1.6.0:
-//     2015-09-22 - revert back to having all tasks disable on last iteration.
-//     2015-09-22 - deprecated disableOnLastIteration method as a result
-//     2015-09-22 - created a separate branch 'disable-on-last-iteration' for this
-//     2015-10-01 - made version numbers semver compliant (documentation only)
-//
-// v1.7.0:
-//    2015-10-08 - introduced callback run counter - callback methods can branch on the iteration number. 
-//    2015-10-11 - enableIfNot() - enable a task only if it is not already enabled. Returns true if was already enabled,
-//                 false if was disabled. 
-//    2015-10-11 - disable() returns previous enable state (true if was enabled, false if was already disabled)
-//    2015-10-11 - introduced callback methods "on enable" and "on disable". On enable runs every time enable is called,
-//                 on disable runs only if task was enabled
-//    2015-10-12 - new Task method: forceNextIteration() - makes next iteration happen immediately during the next pass
-//                 regardless how much time is left
-//
-// v1.8.0:
-//    2015-10-13 - support for status request objects allowing tasks waiting on requests
-//    2015-10-13 - moved to a single header file to allow compilation control via #defines from the main sketch
-//
-// v1.8.1:
-//    2015-10-22 - implement Task id and control points to support identification of failure points for watchdog timer logging
-//
-// v1.8.2:
-//    2015-10-27 - implement Local Task Storage Pointer (allow use of same callback code for different tasks)
-//    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
-//
-// v1.8.4:
-//    2015-11-15 - bug fix: Task alignment with millis() for scheduling purposes should be done after OnEnable, not before.
-//                 Especially since OnEnable method can change the interval
-//    2015-11-16 - further optimizations of the task scheduler execute loop
-//
-// v1.8.5:
-//    2015-11-23 - bug fix: incorrect calculation of next task invocation in case callback changed the interval
-//    2015-11-23 - bug fix: Task::set() method calls setInterval() explicitly, therefore delaying the task in the same manner
-//
-// v1.9.0:
-//    2015-11-24 - packed three byte-long status variables into bit array structure data type - saving 2 bytes per each task instance
-//
-// v1.9.2:
-//    2015-11-28 - _TASK_ROLLOVER_FIX is deprecated (not necessary)
-//    2015-12-16 - bug fixes: automatic millis rollover support for delay methods
-//    2015-12-17 - new method for _TASK_TIMECRITICAL option: getStartDelay() 
-//
-// v2.0.0:
-//    2015-12-22 - _TASK_PRIORITY - support for layered task prioritization
-//
-// v2.0.1:
-//    2016-01-02 - bug fix: issue#11 Xtensa compiler (esp8266): Declaration of constructor does not match implementation
-//
-// v2.0.2:
-//    2016-01-05 - bug fix: time constants wrapped inside compile option
-//    2016-01-05 - support for ESP8266 wifi power saving mode for _TASK_SLEEP_ON_IDLE_RUN compile option
-//
-// v2.1.0:
-//    2016-02-01 - support for microsecond resolution
-//    2016-02-02 - added Scheduler baseline start time reset method: startNow()
-//
-// v2.2.0:
-//    2016-11-17 - all methods made 'inline' to support inclusion of TaskSchedule.h file into other header files
-//
-// v2.2.1:
-//    2016-11-30 - inlined constructors. Added "yield()" and "yieldOnce()" functions to easily break down and chain
-//                 back together long running callback methods
-//    2016-12-16 - added "getCount()" to StatusRequest objects, made every task StatusRequest enabled. 
-//                 Internal StatusRequest objects are accessible via "getInternalStatusRequest()" method. 
-//
-// v2.3.0:
-//    2017-02-24 - new timeUntilNextIteration() method within Scheduler class - inquire when a particlar task is 
-//                 scheduled to run next time
-//
-// v2.4.0:
-//    2017-04-27 - added destructor to the Task class to ensure tasks are disables and taken off the execution chain
-//                 upon destruction. (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder)
-//
-// v2.5.0:
-//    2017-04-27 - ESP8266 ONLY: added optional support for std::functions via _TASK_STD_FUNCTION compilation option
-//                 (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder)
-//    2017-08-30 - add _TASK_DEBUG making all methods and variables public FOR DEBUGGING PURPOSES ONLY!
-//                 Use at your own risk!
-//    2017-08-30 - bug fix: Scheduler::addTask() checks if task is already part of an execution chain (github issue #37)
-//    2017-08-30 - support for multi-tab sketches (Contributed by Adam Ryczkowski - https://github.com/adamryczkowski)
-
-
-#include <Arduino.h>
-#include "TaskSchedulerDeclarations.h"
-
-#ifndef _TASKSCHEDULER_H_
-#define _TASKSCHEDULER_H_
-
-// ----------------------------------------
-// The following "defines" control library functionality at compile time,
-// and should be used in the main sketch depending on the functionality required
-// 
-// #define _TASK_TIMECRITICAL      // Enable monitoring scheduling overruns
-// #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass 
-// #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_PRIORITY          // Support for layered scheduling priority
-// #define _TASK_MICRO_RES         // Support for microsecond resolution
-// #define _TASK_STD_FUNCTION      // Support for std::function (ESP8266 ONLY)
-// #define _TASK_DEBUG             // Make all methods and variables public for debug purposes
-
-
-
- #ifdef _TASK_MICRO_RES
- 
- #undef _TASK_SLEEP_ON_IDLE_RUN     // SLEEP_ON_IDLE has only millisecond resolution
- #define _TASK_TIME_FUNCTION() micros()
- 
- #else
- #define _TASK_TIME_FUNCTION() millis()
- 
- #endif  // _TASK_MICRO_RES
- 
- 
-#ifdef _TASK_SLEEP_ON_IDLE_RUN
-
-#ifdef ARDUINO_ARCH_AVR  
-#include <avr/sleep.h>
-#include <avr/power.h>
-#endif  // ARDUINO_ARCH_AVR 
-
-#ifdef ARDUINO_ARCH_ESP8266
-extern "C" {
-#include "user_interface.h"
-}
-#define _TASK_ESP8266_DLY_THRESHOLD 200L
-#endif  // ARDUINO_ARCH_ESP8266
-#endif  // _TASK_SLEEP_ON_IDLE_RUN
-
-
-#ifndef ARDUINO_ARCH_ESP8266
-#ifdef _TASK_STD_FUNCTION
-#error Support for std::function only for ESP8266 architecture
-#undef _TASK_STD_FUNCTION
-#endif // _TASK_STD_FUNCTION
-#endif // ARDUINO_ARCH_ESP8266
-
-#ifdef _TASK_WDT_IDS
-    static unsigned int __task_id_counter = 0; // global task ID counter for assiging task IDs automatically. 
-#endif  // _TASK_WDT_IDS
-
-#ifdef _TASK_PRIORITY
-    Scheduler* iCurrentScheduler;
-#endif // _TASK_PRIORITY
-
-
-// ------------------ TaskScheduler implementation --------------------
-
-
-/** Constructor, uses default values for the parameters
- * so could be called with no parameters.
- */
-Task::Task( unsigned long aInterval, long aIterations, TaskCallback aCallback, Scheduler* aScheduler, bool aEnable, TaskOnEnable aOnEnable, TaskOnDisable aOnDisable ) {
-    reset();
-    set(aInterval, aIterations, aCallback, aOnEnable, aOnDisable);
-    if (aScheduler) aScheduler->addTask(*this);
-#ifdef _TASK_STATUS_REQUEST
-    iStatusRequest = NULL;
-#endif  // _TASK_STATUS_REQUEST
-#ifdef _TASK_WDT_IDS
-    iTaskID = ++__task_id_counter;
-#endif  // _TASK_WDT_IDS
-    if (aEnable) enable();
-}
-
-/** Destructor.
- *  Makes sure the task disabled and deleted out of the chain
- *  prior to being deleted.
- */
-Task::~Task() {
-    disable();
-    if (iScheduler)
-        iScheduler->deleteTask(*this);
-}
-
-
-#ifdef _TASK_STATUS_REQUEST
-
-/** Constructor with reduced parameter list for tasks created for 
- *  StatusRequest only triggering (always immediate and only 1 iteration)
- */
-Task::Task( TaskCallback aCallback, Scheduler* aScheduler, TaskOnEnable aOnEnable, TaskOnDisable aOnDisable ) {
-    reset();
-    set(TASK_IMMEDIATE, TASK_ONCE, aCallback, aOnEnable, aOnDisable);
-    if (aScheduler) aScheduler->addTask(*this);
-    iStatusRequest = NULL;
-#ifdef _TASK_WDT_IDS
-    iTaskID = ++__task_id_counter;
-#endif  // _TASK_WDT_IDS
-}
-
-
-StatusRequest::StatusRequest()
-{
-    iCount = 0; 
-    iStatus = 0; 
-}
-
-void StatusRequest::setWaiting(unsigned int aCount) { iCount = aCount; iStatus = 0; }
-bool StatusRequest::pending() { return (iCount != 0); }
-bool StatusRequest::completed() { return (iCount == 0); }
-int StatusRequest::getStatus() { return iStatus; }
-int StatusRequest::getCount() { return iCount; }
-StatusRequest* Task::getStatusRequest() { return iStatusRequest; }
-StatusRequest* Task::getInternalStatusRequest() { return &iMyStatusRequest; }
-
-/** Signals completion of the StatusRequest by one of the participating events
- *  @param: aStatus - if provided, sets the return code of the StatusRequest: negative = error, 0 (default) = OK, positive = OK with a specific status code
- *  Negative status will complete Status Request fully (since an error occured).
- *  @return: true, if StatusRequest is complete, false otherwise (still waiting for other events)
- */
-bool StatusRequest::signal(int aStatus) {
-    if ( iCount) {  // do not update the status request if it was already completed
-        if (iCount > 0)  --iCount; 
-        if ( (iStatus = aStatus) < 0 ) iCount = 0;   // if an error is reported, the status is requested to be completed immediately
-    }
-    return (iCount == 0); 
-}
-
-void StatusRequest::signalComplete(int aStatus) {
-    if (iCount) { // do not update the status request if it was already completed
-        iCount = 0; 
-        iStatus = aStatus;
-    }
-}
-
-/** Sets a Task to wait until a particular event completes 
- *  @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, unsigned long aInterval, long aIterations) {
-    if ( ( iStatusRequest = aStatusRequest) ) { // assign internal StatusRequest var and check if it is not NULL
-        setIterations(aIterations);
-        setInterval(aInterval); 
-        iStatus.waiting = _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
-        iStatus.waiting = _TASK_SR_DELAY;  // with delay equal to the current interval
-        enable();
-    }
-}
-#endif  // _TASK_STATUS_REQUEST
-
-bool Task::isEnabled() { return iStatus.enabled; }
-
-unsigned long Task::getInterval() { return iInterval; }
-
-long Task::getIterations() { return iIterations; }
-
-unsigned long Task::getRunCounter() { return iRunCounter; }
-
-void Task::setCallback(TaskCallback aCallback) { iCallback = aCallback; }
-
-void Task::setOnEnable(TaskOnEnable aCallback) { iOnEnable = aCallback; }
-
-void Task::setOnDisable(TaskOnDisable aCallback) { iOnDisable = aCallback; }
-    
-/** Resets (initializes) the task/
- * Task is not enabled and is taken out 
- * out of the execution chain as a result
- */
-void Task::reset() {
-    iStatus.enabled = false;
-    iStatus.inonenable = false;
-    iPreviousMillis = 0;
-    iInterval = iDelay = 0;
-    iPrev = NULL;
-    iNext = NULL;
-    iScheduler = NULL;
-    iRunCounter = 0;
-#ifdef _TASK_TIMECRITICAL
-    iOverrun = 0;
-    iStartDelay = 0;
-#endif  // _TASK_TIMECRITICAL
-#ifdef _TASK_WDT_IDS
-    iControlPoint = 0;
-#endif  // _TASK_WDT_IDS
-#ifdef _TASK_LTS_POINTER
-    iLTS = NULL;
-#endif  // _TASK_LTS_POINTER
-#ifdef _TASK_STATUS_REQUEST
-    iStatus.waiting = 0;
-    iMyStatusRequest.signalComplete();
-#endif  // _TASK_STATUS_REQUEST
-}
-
-/** Explicitly set Task execution parameters
- * @param aInterval - execution interval in ms
- * @param aIterations - number of iterations, use -1 for no limit
- * @param aCallback - pointer to the callback method which executes the task actions
- * @param aOnEnable - pointer to the callback method which is called on enable()
- * @param aOnDisable - pointer to the callback method which is called on disable()
- */
-void Task::set(unsigned long aInterval, long aIterations, TaskCallback aCallback, TaskOnEnable aOnEnable, TaskOnDisable aOnDisable) {
-    setInterval(aInterval); 
-    iSetIterations = iIterations = aIterations;
-    iCallback = aCallback;
-    iOnEnable = aOnEnable;
-    iOnDisable = aOnDisable;
-}
-
-/** Sets number of iterations for the task
- * if task is enabled, schedule for immediate execution
- * @param aIterations - number of iterations, use -1 for no limit
- */
-void Task::setIterations(long aIterations) { 
-    iSetIterations = iIterations = aIterations; 
-}
-
-/** Prepare task for next step iteration following yielding of control to the scheduler
- * @param aCallback - pointer to the callback method for the next step
- */
-void Task::yield (TaskCallback aCallback) {
-    iCallback = aCallback;
-    forceNextIteration();
-    
-    // The next 2 lines adjust runcounter and number of iterations 
-    // as if it is the same run of the callback, just split between
-    // a series of callback methods
-    iRunCounter--;
-    if ( iIterations >= 0 ) iIterations++;
-}
-
-/** Prepare task for next step iteration following yielding of control to the scheduler
- * @param aCallback - pointer to the callback method for the next step
- */
-void Task::yieldOnce (TaskCallback aCallback) {
-    yield(aCallback);
-    iIterations = 1;
-}
-
-/** Enables the task 
- *  schedules it for execution as soon as possible,
- *  and resets the RunCounter back to zero
- */
-void Task::enable() {
-    if (iScheduler) { // activation without active scheduler does not make sense
-        iRunCounter = 0;
-        if ( iOnEnable && !iStatus.inonenable ) {
-            Task *current = iScheduler->iCurrent;
-            iScheduler->iCurrent = this;
-            iStatus.inonenable = true;      // Protection against potential infinite loop
-            iStatus.enabled = iOnEnable();
-            iStatus.inonenable = false;     // Protection against potential infinite loop
-            iScheduler->iCurrent = current;
-        }
-        else {
-            iStatus.enabled = true;
-        }
-        iPreviousMillis = _TASK_TIME_FUNCTION() - (iDelay = iInterval);
-#ifdef _TASK_STATUS_REQUEST
-        if ( iStatus.enabled ) {
-            iMyStatusRequest.setWaiting();
-        }
-#endif
-    }
-}
-
-/** Enables the task only if it was not enabled already
- * Returns previous state (true if was already enabled, false if was not)
- */
-bool Task::enableIfNot() {
-    bool previousEnabled = iStatus.enabled;
-    if ( !previousEnabled ) enable();
-    return (previousEnabled);
-}
-
-/** Enables the task 
- * and schedules it for execution after a delay = aInterval
- */
-void Task::enableDelayed(unsigned long aDelay) {
-    enable();
-    delay(aDelay);
-}
-
-/** Delays Task for execution after a delay = aInterval (if task is enabled).
- * leaves task enabled or disabled
- * if aDelay is zero, delays for the original scheduling interval from now
- */
-void Task::delay(unsigned long aDelay) {
-//  if (!aDelay) aDelay = iInterval;
-    iDelay = aDelay ? aDelay : iInterval;
-    iPreviousMillis = _TASK_TIME_FUNCTION(); // - iInterval + aDelay;
-}
-
-/** Schedules next iteration of Task for execution immediately (if enabled)
- * leaves task enabled or disabled
- * Task's original schedule is shifted, and all subsequent iterations will continue from this point in time
- */
-void Task::forceNextIteration() {
-    iPreviousMillis = _TASK_TIME_FUNCTION() - (iDelay = iInterval);
-}
-
-/** Sets the execution interval.
- * Task execution is delayed for aInterval
- * Use  enable() to schedule execution ASAP
- * @param aInterval - new execution interval
- */
-void Task::setInterval (unsigned long aInterval) {
-    iInterval = aInterval; 
-    delay(); // iDelay will be updated by the delay() function
-}
-
-/** Disables task
- * Task will no longer be executed by the scheduler
- * Returns status of the task before disable was called (i.e., if the task was already disabled)
- */
-bool Task::disable() {
-    bool previousEnabled = iStatus.enabled;
-    iStatus.enabled = false;
-    iStatus.inonenable = false; 
-    if (previousEnabled && iOnDisable) {
-        Task *current = iScheduler->iCurrent;
-        iScheduler->iCurrent = this;
-        iOnDisable();
-        iScheduler->iCurrent = current;
-    }
-#ifdef _TASK_STATUS_REQUEST
-    iMyStatusRequest.signalComplete();
-#endif
-    return (previousEnabled);
-}
-
-/** Restarts task
- * Task will run number of iterations again
- */
-void Task::restart() {
-     iIterations = iSetIterations;
-     enable();
-}
-
-/** Restarts task delayed
- * Task will run number of iterations again
- */
-void Task::restartDelayed(unsigned long aDelay) {
-     iIterations = iSetIterations;
-     enableDelayed(aDelay);
-}
-
-bool Task::isFirstIteration() { return (iRunCounter <= 1); } 
-
-bool Task::isLastIteration() { return (iIterations == 0); }
-
-#ifdef _TASK_TIMECRITICAL
-
-long Task::getOverrun() { return iOverrun; }
-long Task::getStartDelay() { return iStartDelay; }
-
-#endif  // _TASK_TIMECRITICAL
-
-
-#ifdef _TASK_WDT_IDS
-
-void Task::setId(unsigned int aID) { iTaskID = aID; }
-unsigned int Task::getId() { return iTaskID; }
-void Task::setControlPoint(unsigned int aPoint) { iControlPoint = aPoint; }
-unsigned int Task::getControlPoint() { return iControlPoint; }
-    
-#endif  // _TASK_WDT_IDS
-
-#ifdef _TASK_LTS_POINTER
-
-void  Task::setLtsPointer(void *aPtr) { iLTS = aPtr; }
-void* Task::getLtsPointer() { return iLTS; }
-    
-#endif  // _TASK_LTS_POINTER
-
-// ------------------ Scheduler implementation --------------------
-
-/** Default constructor.
- * Creates a scheduler with an empty execution chain.
- */
-Scheduler::Scheduler() {
-    init();
-}
-
-/** Initializes all internal varaibles
- */
-void Scheduler::init() { 
-    iFirst = NULL; 
-    iLast = NULL; 
-    iCurrent = NULL; 
-#ifdef _TASK_PRIORITY
-    iHighPriority = NULL;
-#endif  // _TASK_PRIORITY
-#ifdef _TASK_SLEEP_ON_IDLE_RUN
-    allowSleep(true);
-#endif  // _TASK_SLEEP_ON_IDLE_RUN
-}
-
-/** Appends task aTask to the tail of the execution chain.
- * @param &aTask - reference to the Task to be appended.
- * @note Task can only be part of the chain once.
- */
- void Scheduler::addTask(Task& aTask) {
-
-// Avoid adding task twice to the same scheduler
-    if (aTask.iScheduler == this) 
-        return;
-     
-    aTask.iScheduler = this;
-// First task situation: 
-    if (iFirst == NULL) {
-        iFirst = &aTask;
-        aTask.iPrev = NULL;
-    }
-    else {
-// This task gets linked back to the previous last one
-        aTask.iPrev = iLast;
-        iLast->iNext = &aTask;
-    }
-// "Previous" last task gets linked to this one - as this one becomes the last one
-    aTask.iNext = NULL;
-    iLast = &aTask;
-}
-
-/** Deletes specific Task from the execution chain
- * @param &aTask - reference to the task to be deleted from the chain
- */
-void Scheduler::deleteTask(Task& aTask) {
-    if (aTask.iPrev == NULL) {
-        if (aTask.iNext == NULL) {
-            iFirst = NULL;
-            iLast = NULL;
-            return;
-        }
-        else {
-            aTask.iNext->iPrev = NULL;
-            iFirst = aTask.iNext;
-            aTask.iNext = NULL;
-            return;
-        }
-    }
-
-    if (aTask.iNext == NULL) {
-        aTask.iPrev->iNext = NULL;
-        iLast = aTask.iPrev;
-        aTask.iPrev = NULL;
-        return;
-    }
-
-    aTask.iPrev->iNext = aTask.iNext;
-    aTask.iNext->iPrev = aTask.iPrev;
-    aTask.iPrev = NULL;
-    aTask.iNext = NULL;
-}
-
-/** Disables all tasks in the execution chain
- * Convenient for error situations, when the only
- * task remaining active is an error processing task
- * @param aRecursive - if true, tasks of the higher priority chains are disabled as well recursively
- */
-void Scheduler::disableAll(bool aRecursive) {
-    Task    *current = iFirst;
-    while (current) {
-        current->disable();
-        current = current->iNext;
-    }
-#ifdef _TASK_PRIORITY
-    if (aRecursive && iHighPriority) iHighPriority->disableAll(true);
-#endif  // _TASK_PRIORITY
-}
-
-
-/** Enables all the tasks in the execution chain
- * @param aRecursive - if true, tasks of the higher priority chains are enabled as well recursively
- */
- void Scheduler::enableAll(bool aRecursive) {
-    Task    *current = iFirst;
-    while (current) {
-        current->enable();
-        current = current->iNext;
-    }
-#ifdef _TASK_PRIORITY
-    if (aRecursive && iHighPriority) iHighPriority->enableAll(true);
-#endif  // _TASK_PRIORITY
-}
-
-/** Sets scheduler for the higher priority tasks (support for layered task priority)
- * @param aScheduler - pointer to a scheduler for the higher priority tasks
- */
-#ifdef _TASK_PRIORITY
-void Scheduler::setHighPriorityScheduler(Scheduler* aScheduler) {
-    if (aScheduler != this) iHighPriority = aScheduler;  // Setting yourself as a higher priority one will create infinite recursive call
-#ifdef _TASK_SLEEP_ON_IDLE_RUN
-    if (iHighPriority) {
-        iHighPriority->allowSleep(false);       // Higher priority schedulers should not do power management
-    }
-#endif  // _TASK_SLEEP_ON_IDLE_RUN
-};
-#endif  // _TASK_PRIORITY
-
-
-#ifdef _TASK_SLEEP_ON_IDLE_RUN
-void Scheduler::allowSleep(bool aState) { 
-    iAllowSleep = aState; 
-
-#ifdef ARDUINO_ARCH_ESP8266
-    wifi_set_sleep_type( iAllowSleep ? LIGHT_SLEEP_T : NONE_SLEEP_T );
-#endif  // ARDUINO_ARCH_ESP8266
-
-}
-#endif  // _TASK_SLEEP_ON_IDLE_RUN
-
-
-void Scheduler::startNow( bool aRecursive ) {
-    unsigned long t = _TASK_TIME_FUNCTION();
-    
-    iCurrent = iFirst;
-    while (iCurrent) {
-        if ( iCurrent->iStatus.enabled ) iCurrent->iPreviousMillis = t - iCurrent->iDelay;
-        iCurrent = iCurrent->iNext;
-    }
-    
-#ifdef _TASK_PRIORITY
-    if (aRecursive && iHighPriority) iHighPriority->startNow( true );
-#endif  // _TASK_PRIORITY
-}
-
-/** Returns number millis or micros until next scheduled iteration of a given task
- *
- * @param aTask - reference to task which next iteration is in question
- */
-long Scheduler::timeUntilNextIteration(Task& aTask) {
-    
-#ifdef _TASK_STATUS_REQUEST
-    
-    StatusRequest *s = aTask.getStatusRequest();
-    if ( s != NULL && s->pending() ) 
-        return (-1);    // cannot be determined
-#endif
-    if ( !aTask.isEnabled() )
-        return (-1);    // cannot be determined
-    
-    long d = (long) aTask.iDelay - ( (long) ((_TASK_TIME_FUNCTION() - aTask.iPreviousMillis)) );
-    
-    if ( d < 0 ) 
-        return (0); // Task will run as soon as possible
-    return ( d );
-}
-
-Task& Scheduler::currentTask() { return *iCurrent; }
-
-#ifdef _TASK_LTS_POINTER
-void* Scheduler::currentLts() { return iCurrent->iLTS; }
-#endif  // _TASK_LTS_POINTER
-#ifdef _TASK_TIMECRITICAL
-bool Scheduler::isOverrun() { return (iCurrent->iOverrun < 0); }
-#endif  // _TASK_TIMECRITICAL
-
-/** Makes one pass through the execution chain.
- * Tasks are executed in the order they were added to the chain
- * There is no concept of priority
- * Different pseudo "priority" could be achieved
- * by running task more frequently 
- */
-bool Scheduler::execute() {
-    bool     idleRun = true;
-    register unsigned long m, i;  // millis, interval;
-
-#ifdef ARDUINO_ARCH_ESP8266
-      unsigned long t1 = micros();
-      unsigned long t2 = 0;
-#endif  // ARDUINO_ARCH_ESP8266
-
-    iCurrent = iFirst;
-    
-    while (iCurrent) {
-        
-#ifdef _TASK_PRIORITY
-    // If scheduler for higher priority tasks is set, it's entire chain is executed on every pass of the base scheduler
-        if (iHighPriority) idleRun = iHighPriority->execute() && idleRun; 
-        iCurrentScheduler = this;
-#endif  // _TASK_PRIORITY
-
-        do {
-            if ( iCurrent->iStatus.enabled ) {
-
-#ifdef _TASK_WDT_IDS
-    // For each task the control points are initialized to avoid confusion because of carry-over:
-                iCurrent->iControlPoint = 0;
-#endif  // _TASK_WDT_IDS
-    
-    // Disable task on last iteration:
-                if (iCurrent->iIterations == 0) {
-                    iCurrent->disable();
-                    break;
-                }
-                m = _TASK_TIME_FUNCTION();
-                i = iCurrent->iInterval;
-
-#ifdef  _TASK_STATUS_REQUEST
-    // 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->iStatus.waiting ) {
-                    if ( (iCurrent->iStatusRequest)->pending() ) break;
-                    if (iCurrent->iStatus.waiting == _TASK_SR_NODELAY) {
-                        iCurrent->iPreviousMillis = m - (iCurrent->iDelay = i);
-                    }
-                    else {
-                        iCurrent->iPreviousMillis = m;
-                    }
-                    iCurrent->iStatus.waiting = 0;
-                }
-#endif  // _TASK_STATUS_REQUEST
-
-                if ( m - iCurrent->iPreviousMillis < iCurrent->iDelay ) break;
-
-                if ( iCurrent->iIterations > 0 ) iCurrent->iIterations--;  // do not decrement (-1) being a signal of never-ending task
-                iCurrent->iRunCounter++;
-                iCurrent->iPreviousMillis += iCurrent->iDelay;
-
-#ifdef _TASK_TIMECRITICAL
-    // 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) 
-                unsigned long p = iCurrent->iPreviousMillis;
-                iCurrent->iOverrun = (long) ( p + i - m );
-                iCurrent->iStartDelay = (long) ( m - p ); 
-#endif  // _TASK_TIMECRITICAL
-
-                iCurrent->iDelay = i;
-                if ( iCurrent->iCallback ) {
-                    iCurrent->iCallback();
-                    idleRun = false;
-                }
-            }
-        } while (0);    //guaranteed single run - allows use of "break" to exit 
-        iCurrent = iCurrent->iNext;
-#ifdef ARDUINO_ARCH_ESP8266
-        yield();
-#endif  // ARDUINO_ARCH_ESP8266
-    }
-
-#ifdef _TASK_SLEEP_ON_IDLE_RUN
-    if (idleRun && iAllowSleep) {
-
-#ifdef ARDUINO_ARCH_AVR // Could be used only for AVR-based boards. 
-      set_sleep_mode(SLEEP_MODE_IDLE);
-      sleep_enable();
-      /* Now enter sleep mode. */
-      sleep_mode();
-      
-      /* The program will continue from here after the timer timeout ~1 ms */
-      sleep_disable(); /* First thing to do is disable sleep. */
-#endif // ARDUINO_ARCH_AVR
-
-#ifdef ARDUINO_ARCH_ESP8266
-// to do: find suitable sleep function for esp8266
-      t2 = micros() - t1;
-      if (t2 < _TASK_ESP8266_DLY_THRESHOLD) delay(1);   // ESP8266 implementation of delay() uses timers and yield
-#endif  // ARDUINO_ARCH_ESP8266
-    }
-#endif  // _TASK_SLEEP_ON_IDLE_RUN
-
-    return (idleRun);
-}
-
-
-
-#endif /* _TASKSCHEDULER_H_ */
+// Cooperative multitasking library for Arduino
+// Copyright (c) 2015-2017 Anatoli Arkhipenko
+//
+// Changelog:
+// v1.0.0:
+//     2015-02-24 - Initial release 
+//     2015-02-28 - added delay() and disableOnLastIteration() methods
+//     2015-03-25 - changed scheduler execute() method for a more precise delay calculation:
+//                  1. Do not delay if any of the tasks ran (making request for immediate execution redundant)
+//                  2. Delay is invoked only if none of the tasks ran 
+//                  3. Delay is based on the min anticipated wait until next task _AND_ the runtime of execute method itself.
+//     2015-05-11 - added  restart() and restartDelayed() methods to restart tasks which are on hold after running all iterations
+//     2015-05-19 - completely removed  delay from the scheduler since there are no power saving there. using 1 ms sleep instead
+//
+// v1.4.1:
+//     2015-09-15 - more careful placement of AVR-specific includes for sleep method (compatibility with DUE)
+//                  sleep on idle run is no longer a default and should be explicitly compiled with
+//                 _TASK_SLEEP_ON_IDLE_RUN defined
+//
+// v1.5.0:
+//     2015-09-20 - access to currently executing task (for callback methods)
+//     2015-09-20 - pass scheduler as a parameter to the task constructor to append the task to the end of the chain
+//     2015-09-20 - option to create a task already enabled
+//
+// v1.5.1:
+//     2015-09-21 - bug fix: incorrect handling of active tasks via set() and setIterations(). 
+//                  Thanks to Hannes Morgenstern for catching this one
+//
+// v1.6.0:
+//     2015-09-22 - revert back to having all tasks disable on last iteration.
+//     2015-09-22 - deprecated disableOnLastIteration method as a result
+//     2015-09-22 - created a separate branch 'disable-on-last-iteration' for this
+//     2015-10-01 - made version numbers semver compliant (documentation only)
+//
+// v1.7.0:
+//    2015-10-08 - introduced callback run counter - callback methods can branch on the iteration number. 
+//    2015-10-11 - enableIfNot() - enable a task only if it is not already enabled. Returns true if was already enabled,
+//                 false if was disabled. 
+//    2015-10-11 - disable() returns previous enable state (true if was enabled, false if was already disabled)
+//    2015-10-11 - introduced callback methods "on enable" and "on disable". On enable runs every time enable is called,
+//                 on disable runs only if task was enabled
+//    2015-10-12 - new Task method: forceNextIteration() - makes next iteration happen immediately during the next pass
+//                 regardless how much time is left
+//
+// v1.8.0:
+//    2015-10-13 - support for status request objects allowing tasks waiting on requests
+//    2015-10-13 - moved to a single header file to allow compilation control via #defines from the main sketch
+//
+// v1.8.1:
+//    2015-10-22 - implement Task id and control points to support identification of failure points for watchdog timer logging
+//
+// v1.8.2:
+//    2015-10-27 - implement Local Task Storage Pointer (allow use of same callback code for different tasks)
+//    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
+//
+// v1.8.4:
+//    2015-11-15 - bug fix: Task alignment with millis() for scheduling purposes should be done after OnEnable, not before.
+//                 Especially since OnEnable method can change the interval
+//    2015-11-16 - further optimizations of the task scheduler execute loop
+//
+// v1.8.5:
+//    2015-11-23 - bug fix: incorrect calculation of next task invocation in case callback changed the interval
+//    2015-11-23 - bug fix: Task::set() method calls setInterval() explicitly, therefore delaying the task in the same manner
+//
+// v1.9.0:
+//    2015-11-24 - packed three byte-long status variables into bit array structure data type - saving 2 bytes per each task instance
+//
+// v1.9.2:
+//    2015-11-28 - _TASK_ROLLOVER_FIX is deprecated (not necessary)
+//    2015-12-16 - bug fixes: automatic millis rollover support for delay methods
+//    2015-12-17 - new method for _TASK_TIMECRITICAL option: getStartDelay() 
+//
+// v2.0.0:
+//    2015-12-22 - _TASK_PRIORITY - support for layered task prioritization
+//
+// v2.0.1:
+//    2016-01-02 - bug fix: issue#11 Xtensa compiler (esp8266): Declaration of constructor does not match implementation
+//
+// v2.0.2:
+//    2016-01-05 - bug fix: time constants wrapped inside compile option
+//    2016-01-05 - support for ESP8266 wifi power saving mode for _TASK_SLEEP_ON_IDLE_RUN compile option
+//
+// v2.1.0:
+//    2016-02-01 - support for microsecond resolution
+//    2016-02-02 - added Scheduler baseline start time reset method: startNow()
+//
+// v2.2.0:
+//    2016-11-17 - all methods made 'inline' to support inclusion of TaskSchedule.h file into other header files
+//
+// v2.2.1:
+//    2016-11-30 - inlined constructors. Added "yield()" and "yieldOnce()" functions to easily break down and chain
+//                 back together long running callback methods
+//    2016-12-16 - added "getCount()" to StatusRequest objects, made every task StatusRequest enabled. 
+//                 Internal StatusRequest objects are accessible via "getInternalStatusRequest()" method. 
+//
+// v2.3.0:
+//    2017-02-24 - new timeUntilNextIteration() method within Scheduler class - inquire when a particlar task is 
+//                 scheduled to run next time
+//
+// v2.4.0:
+//    2017-04-27 - added destructor to the Task class to ensure tasks are disables and taken off the execution chain
+//                 upon destruction. (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder)
+//
+// v2.5.0:
+//    2017-04-27 - ESP8266 ONLY: added optional support for std::functions via _TASK_STD_FUNCTION compilation option
+//                 (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder)
+//    2017-08-30 - add _TASK_DEBUG making all methods and variables public FOR DEBUGGING PURPOSES ONLY!
+//                 Use at your own risk!
+//    2017-08-30 - bug fix: Scheduler::addTask() checks if task is already part of an execution chain (github issue #37)
+//    2017-08-30 - support for multi-tab sketches (Contributed by Adam Ryczkowski - https://github.com/adamryczkowski)
+
+
+#include <Arduino.h>
+#include "TaskSchedulerDeclarations.h"
+
+#ifndef _TASKSCHEDULER_H_
+#define _TASKSCHEDULER_H_
+
+// ----------------------------------------
+// The following "defines" control library functionality at compile time,
+// and should be used in the main sketch depending on the functionality required
+// 
+// #define _TASK_TIMECRITICAL      // Enable monitoring scheduling overruns
+// #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass 
+// #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_PRIORITY          // Support for layered scheduling priority
+// #define _TASK_MICRO_RES         // Support for microsecond resolution
+// #define _TASK_STD_FUNCTION      // Support for std::function (ESP8266 ONLY)
+// #define _TASK_DEBUG             // Make all methods and variables public for debug purposes
+
+
+
+ #ifdef _TASK_MICRO_RES
+ 
+ #undef _TASK_SLEEP_ON_IDLE_RUN     // SLEEP_ON_IDLE has only millisecond resolution
+ #define _TASK_TIME_FUNCTION() micros()
+ 
+ #else
+ #define _TASK_TIME_FUNCTION() millis()
+ 
+ #endif  // _TASK_MICRO_RES
+ 
+ 
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+
+#ifdef ARDUINO_ARCH_AVR  
+#include <avr/sleep.h>
+#include <avr/power.h>
+#endif  // ARDUINO_ARCH_AVR 
+
+#ifdef ARDUINO_ARCH_ESP8266
+extern "C" {
+#include "user_interface.h"
+}
+#define _TASK_ESP8266_DLY_THRESHOLD 200L
+#endif  // ARDUINO_ARCH_ESP8266
+#endif  // _TASK_SLEEP_ON_IDLE_RUN
+
+
+#ifndef ARDUINO_ARCH_ESP8266
+#ifdef _TASK_STD_FUNCTION
+#error Support for std::function only for ESP8266 architecture
+#undef _TASK_STD_FUNCTION
+#endif // _TASK_STD_FUNCTION
+#endif // ARDUINO_ARCH_ESP8266
+
+#ifdef _TASK_WDT_IDS
+    static unsigned int __task_id_counter = 0; // global task ID counter for assiging task IDs automatically. 
+#endif  // _TASK_WDT_IDS
+
+#ifdef _TASK_PRIORITY
+    Scheduler* iCurrentScheduler;
+#endif // _TASK_PRIORITY
+
+
+// ------------------ TaskScheduler implementation --------------------
+
+
+/** Constructor, uses default values for the parameters
+ * so could be called with no parameters.
+ */
+Task::Task( unsigned long aInterval, long aIterations, TaskCallback aCallback, Scheduler* aScheduler, bool aEnable, TaskOnEnable aOnEnable, TaskOnDisable aOnDisable ) {
+    reset();
+    set(aInterval, aIterations, aCallback, aOnEnable, aOnDisable);
+    if (aScheduler) aScheduler->addTask(*this);
+#ifdef _TASK_STATUS_REQUEST
+    iStatusRequest = NULL;
+#endif  // _TASK_STATUS_REQUEST
+#ifdef _TASK_WDT_IDS
+    iTaskID = ++__task_id_counter;
+#endif  // _TASK_WDT_IDS
+    if (aEnable) enable();
+}
+
+/** Destructor.
+ *  Makes sure the task disabled and deleted out of the chain
+ *  prior to being deleted.
+ */
+Task::~Task() {
+    disable();
+    if (iScheduler)
+        iScheduler->deleteTask(*this);
+}
+
+
+#ifdef _TASK_STATUS_REQUEST
+
+/** Constructor with reduced parameter list for tasks created for 
+ *  StatusRequest only triggering (always immediate and only 1 iteration)
+ */
+Task::Task( TaskCallback aCallback, Scheduler* aScheduler, TaskOnEnable aOnEnable, TaskOnDisable aOnDisable ) {
+    reset();
+    set(TASK_IMMEDIATE, TASK_ONCE, aCallback, aOnEnable, aOnDisable);
+    if (aScheduler) aScheduler->addTask(*this);
+    iStatusRequest = NULL;
+#ifdef _TASK_WDT_IDS
+    iTaskID = ++__task_id_counter;
+#endif  // _TASK_WDT_IDS
+}
+
+
+StatusRequest::StatusRequest()
+{
+    iCount = 0; 
+    iStatus = 0; 
+}
+
+void StatusRequest::setWaiting(unsigned int aCount) { iCount = aCount; iStatus = 0; }
+bool StatusRequest::pending() { return (iCount != 0); }
+bool StatusRequest::completed() { return (iCount == 0); }
+int StatusRequest::getStatus() { return iStatus; }
+int StatusRequest::getCount() { return iCount; }
+StatusRequest* Task::getStatusRequest() { return iStatusRequest; }
+StatusRequest* Task::getInternalStatusRequest() { return &iMyStatusRequest; }
+
+/** Signals completion of the StatusRequest by one of the participating events
+ *  @param: aStatus - if provided, sets the return code of the StatusRequest: negative = error, 0 (default) = OK, positive = OK with a specific status code
+ *  Negative status will complete Status Request fully (since an error occured).
+ *  @return: true, if StatusRequest is complete, false otherwise (still waiting for other events)
+ */
+bool StatusRequest::signal(int aStatus) {
+    if ( iCount) {  // do not update the status request if it was already completed
+        if (iCount > 0)  --iCount; 
+        if ( (iStatus = aStatus) < 0 ) iCount = 0;   // if an error is reported, the status is requested to be completed immediately
+    }
+    return (iCount == 0); 
+}
+
+void StatusRequest::signalComplete(int aStatus) {
+    if (iCount) { // do not update the status request if it was already completed
+        iCount = 0; 
+        iStatus = aStatus;
+    }
+}
+
+/** Sets a Task to wait until a particular event completes 
+ *  @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, unsigned long aInterval, long aIterations) {
+    if ( ( iStatusRequest = aStatusRequest) ) { // assign internal StatusRequest var and check if it is not NULL
+        setIterations(aIterations);
+        setInterval(aInterval); 
+        iStatus.waiting = _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
+        iStatus.waiting = _TASK_SR_DELAY;  // with delay equal to the current interval
+        enable();
+    }
+}
+#endif  // _TASK_STATUS_REQUEST
+
+bool Task::isEnabled() { return iStatus.enabled; }
+
+unsigned long Task::getInterval() { return iInterval; }
+
+long Task::getIterations() { return iIterations; }
+
+unsigned long Task::getRunCounter() { return iRunCounter; }
+
+void Task::setCallback(TaskCallback aCallback) { iCallback = aCallback; }
+
+void Task::setOnEnable(TaskOnEnable aCallback) { iOnEnable = aCallback; }
+
+void Task::setOnDisable(TaskOnDisable aCallback) { iOnDisable = aCallback; }
+    
+/** Resets (initializes) the task/
+ * Task is not enabled and is taken out 
+ * out of the execution chain as a result
+ */
+void Task::reset() {
+    iStatus.enabled = false;
+    iStatus.inonenable = false;
+    iPreviousMillis = 0;
+    iInterval = iDelay = 0;
+    iPrev = NULL;
+    iNext = NULL;
+    iScheduler = NULL;
+    iRunCounter = 0;
+#ifdef _TASK_TIMECRITICAL
+    iOverrun = 0;
+    iStartDelay = 0;
+#endif  // _TASK_TIMECRITICAL
+#ifdef _TASK_WDT_IDS
+    iControlPoint = 0;
+#endif  // _TASK_WDT_IDS
+#ifdef _TASK_LTS_POINTER
+    iLTS = NULL;
+#endif  // _TASK_LTS_POINTER
+#ifdef _TASK_STATUS_REQUEST
+    iStatus.waiting = 0;
+    iMyStatusRequest.signalComplete();
+#endif  // _TASK_STATUS_REQUEST
+}
+
+/** Explicitly set Task execution parameters
+ * @param aInterval - execution interval in ms
+ * @param aIterations - number of iterations, use -1 for no limit
+ * @param aCallback - pointer to the callback method which executes the task actions
+ * @param aOnEnable - pointer to the callback method which is called on enable()
+ * @param aOnDisable - pointer to the callback method which is called on disable()
+ */
+void Task::set(unsigned long aInterval, long aIterations, TaskCallback aCallback, TaskOnEnable aOnEnable, TaskOnDisable aOnDisable) {
+    setInterval(aInterval); 
+    iSetIterations = iIterations = aIterations;
+    iCallback = aCallback;
+    iOnEnable = aOnEnable;
+    iOnDisable = aOnDisable;
+}
+
+/** Sets number of iterations for the task
+ * if task is enabled, schedule for immediate execution
+ * @param aIterations - number of iterations, use -1 for no limit
+ */
+void Task::setIterations(long aIterations) { 
+    iSetIterations = iIterations = aIterations; 
+}
+
+/** Prepare task for next step iteration following yielding of control to the scheduler
+ * @param aCallback - pointer to the callback method for the next step
+ */
+void Task::yield (TaskCallback aCallback) {
+    iCallback = aCallback;
+    forceNextIteration();
+    
+    // The next 2 lines adjust runcounter and number of iterations 
+    // as if it is the same run of the callback, just split between
+    // a series of callback methods
+    iRunCounter--;
+    if ( iIterations >= 0 ) iIterations++;
+}
+
+/** Prepare task for next step iteration following yielding of control to the scheduler
+ * @param aCallback - pointer to the callback method for the next step
+ */
+void Task::yieldOnce (TaskCallback aCallback) {
+    yield(aCallback);
+    iIterations = 1;
+}
+
+/** Enables the task 
+ *  schedules it for execution as soon as possible,
+ *  and resets the RunCounter back to zero
+ */
+void Task::enable() {
+    if (iScheduler) { // activation without active scheduler does not make sense
+        iRunCounter = 0;
+        if ( iOnEnable && !iStatus.inonenable ) {
+            Task *current = iScheduler->iCurrent;
+            iScheduler->iCurrent = this;
+            iStatus.inonenable = true;      // Protection against potential infinite loop
+            iStatus.enabled = iOnEnable();
+            iStatus.inonenable = false;     // Protection against potential infinite loop
+            iScheduler->iCurrent = current;
+        }
+        else {
+            iStatus.enabled = true;
+        }
+        iPreviousMillis = _TASK_TIME_FUNCTION() - (iDelay = iInterval);
+#ifdef _TASK_STATUS_REQUEST
+        if ( iStatus.enabled ) {
+            iMyStatusRequest.setWaiting();
+        }
+#endif
+    }
+}
+
+/** Enables the task only if it was not enabled already
+ * Returns previous state (true if was already enabled, false if was not)
+ */
+bool Task::enableIfNot() {
+    bool previousEnabled = iStatus.enabled;
+    if ( !previousEnabled ) enable();
+    return (previousEnabled);
+}
+
+/** Enables the task 
+ * and schedules it for execution after a delay = aInterval
+ */
+void Task::enableDelayed(unsigned long aDelay) {
+    enable();
+    delay(aDelay);
+}
+
+/** Delays Task for execution after a delay = aInterval (if task is enabled).
+ * leaves task enabled or disabled
+ * if aDelay is zero, delays for the original scheduling interval from now
+ */
+void Task::delay(unsigned long aDelay) {
+//  if (!aDelay) aDelay = iInterval;
+    iDelay = aDelay ? aDelay : iInterval;
+    iPreviousMillis = _TASK_TIME_FUNCTION(); // - iInterval + aDelay;
+}
+
+/** Schedules next iteration of Task for execution immediately (if enabled)
+ * leaves task enabled or disabled
+ * Task's original schedule is shifted, and all subsequent iterations will continue from this point in time
+ */
+void Task::forceNextIteration() {
+    iPreviousMillis = _TASK_TIME_FUNCTION() - (iDelay = iInterval);
+}
+
+/** Sets the execution interval.
+ * Task execution is delayed for aInterval
+ * Use  enable() to schedule execution ASAP
+ * @param aInterval - new execution interval
+ */
+void Task::setInterval (unsigned long aInterval) {
+    iInterval = aInterval; 
+    delay(); // iDelay will be updated by the delay() function
+}
+
+/** Disables task
+ * Task will no longer be executed by the scheduler
+ * Returns status of the task before disable was called (i.e., if the task was already disabled)
+ */
+bool Task::disable() {
+    bool previousEnabled = iStatus.enabled;
+    iStatus.enabled = false;
+    iStatus.inonenable = false; 
+    if (previousEnabled && iOnDisable) {
+        Task *current = iScheduler->iCurrent;
+        iScheduler->iCurrent = this;
+        iOnDisable();
+        iScheduler->iCurrent = current;
+    }
+#ifdef _TASK_STATUS_REQUEST
+    iMyStatusRequest.signalComplete();
+#endif
+    return (previousEnabled);
+}
+
+/** Restarts task
+ * Task will run number of iterations again
+ */
+void Task::restart() {
+     iIterations = iSetIterations;
+     enable();
+}
+
+/** Restarts task delayed
+ * Task will run number of iterations again
+ */
+void Task::restartDelayed(unsigned long aDelay) {
+     iIterations = iSetIterations;
+     enableDelayed(aDelay);
+}
+
+bool Task::isFirstIteration() { return (iRunCounter <= 1); } 
+
+bool Task::isLastIteration() { return (iIterations == 0); }
+
+#ifdef _TASK_TIMECRITICAL
+
+long Task::getOverrun() { return iOverrun; }
+long Task::getStartDelay() { return iStartDelay; }
+
+#endif  // _TASK_TIMECRITICAL
+
+
+#ifdef _TASK_WDT_IDS
+
+void Task::setId(unsigned int aID) { iTaskID = aID; }
+unsigned int Task::getId() { return iTaskID; }
+void Task::setControlPoint(unsigned int aPoint) { iControlPoint = aPoint; }
+unsigned int Task::getControlPoint() { return iControlPoint; }
+    
+#endif  // _TASK_WDT_IDS
+
+#ifdef _TASK_LTS_POINTER
+
+void  Task::setLtsPointer(void *aPtr) { iLTS = aPtr; }
+void* Task::getLtsPointer() { return iLTS; }
+    
+#endif  // _TASK_LTS_POINTER
+
+// ------------------ Scheduler implementation --------------------
+
+/** Default constructor.
+ * Creates a scheduler with an empty execution chain.
+ */
+Scheduler::Scheduler() {
+    init();
+}
+
+/** Initializes all internal varaibles
+ */
+void Scheduler::init() { 
+    iFirst = NULL; 
+    iLast = NULL; 
+    iCurrent = NULL; 
+#ifdef _TASK_PRIORITY
+    iHighPriority = NULL;
+#endif  // _TASK_PRIORITY
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+    allowSleep(true);
+#endif  // _TASK_SLEEP_ON_IDLE_RUN
+}
+
+/** Appends task aTask to the tail of the execution chain.
+ * @param &aTask - reference to the Task to be appended.
+ * @note Task can only be part of the chain once.
+ */
+ void Scheduler::addTask(Task& aTask) {
+
+// Avoid adding task twice to the same scheduler
+    if (aTask.iScheduler == this) 
+        return;
+     
+    aTask.iScheduler = this;
+// First task situation: 
+    if (iFirst == NULL) {
+        iFirst = &aTask;
+        aTask.iPrev = NULL;
+    }
+    else {
+// This task gets linked back to the previous last one
+        aTask.iPrev = iLast;
+        iLast->iNext = &aTask;
+    }
+// "Previous" last task gets linked to this one - as this one becomes the last one
+    aTask.iNext = NULL;
+    iLast = &aTask;
+}
+
+/** Deletes specific Task from the execution chain
+ * @param &aTask - reference to the task to be deleted from the chain
+ */
+void Scheduler::deleteTask(Task& aTask) {
+    if (aTask.iPrev == NULL) {
+        if (aTask.iNext == NULL) {
+            iFirst = NULL;
+            iLast = NULL;
+            return;
+        }
+        else {
+            aTask.iNext->iPrev = NULL;
+            iFirst = aTask.iNext;
+            aTask.iNext = NULL;
+            return;
+        }
+    }
+
+    if (aTask.iNext == NULL) {
+        aTask.iPrev->iNext = NULL;
+        iLast = aTask.iPrev;
+        aTask.iPrev = NULL;
+        return;
+    }
+
+    aTask.iPrev->iNext = aTask.iNext;
+    aTask.iNext->iPrev = aTask.iPrev;
+    aTask.iPrev = NULL;
+    aTask.iNext = NULL;
+}
+
+/** Disables all tasks in the execution chain
+ * Convenient for error situations, when the only
+ * task remaining active is an error processing task
+ * @param aRecursive - if true, tasks of the higher priority chains are disabled as well recursively
+ */
+void Scheduler::disableAll(bool aRecursive) {
+    Task    *current = iFirst;
+    while (current) {
+        current->disable();
+        current = current->iNext;
+    }
+#ifdef _TASK_PRIORITY
+    if (aRecursive && iHighPriority) iHighPriority->disableAll(true);
+#endif  // _TASK_PRIORITY
+}
+
+
+/** Enables all the tasks in the execution chain
+ * @param aRecursive - if true, tasks of the higher priority chains are enabled as well recursively
+ */
+ void Scheduler::enableAll(bool aRecursive) {
+    Task    *current = iFirst;
+    while (current) {
+        current->enable();
+        current = current->iNext;
+    }
+#ifdef _TASK_PRIORITY
+    if (aRecursive && iHighPriority) iHighPriority->enableAll(true);
+#endif  // _TASK_PRIORITY
+}
+
+/** Sets scheduler for the higher priority tasks (support for layered task priority)
+ * @param aScheduler - pointer to a scheduler for the higher priority tasks
+ */
+#ifdef _TASK_PRIORITY
+void Scheduler::setHighPriorityScheduler(Scheduler* aScheduler) {
+    if (aScheduler != this) iHighPriority = aScheduler;  // Setting yourself as a higher priority one will create infinite recursive call
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+    if (iHighPriority) {
+        iHighPriority->allowSleep(false);       // Higher priority schedulers should not do power management
+    }
+#endif  // _TASK_SLEEP_ON_IDLE_RUN
+};
+#endif  // _TASK_PRIORITY
+
+
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+void Scheduler::allowSleep(bool aState) { 
+    iAllowSleep = aState; 
+
+#ifdef ARDUINO_ARCH_ESP8266
+    wifi_set_sleep_type( iAllowSleep ? LIGHT_SLEEP_T : NONE_SLEEP_T );
+#endif  // ARDUINO_ARCH_ESP8266
+
+}
+#endif  // _TASK_SLEEP_ON_IDLE_RUN
+
+
+void Scheduler::startNow( bool aRecursive ) {
+    unsigned long t = _TASK_TIME_FUNCTION();
+    
+    iCurrent = iFirst;
+    while (iCurrent) {
+        if ( iCurrent->iStatus.enabled ) iCurrent->iPreviousMillis = t - iCurrent->iDelay;
+        iCurrent = iCurrent->iNext;
+    }
+    
+#ifdef _TASK_PRIORITY
+    if (aRecursive && iHighPriority) iHighPriority->startNow( true );
+#endif  // _TASK_PRIORITY
+}
+
+/** Returns number millis or micros until next scheduled iteration of a given task
+ *
+ * @param aTask - reference to task which next iteration is in question
+ */
+long Scheduler::timeUntilNextIteration(Task& aTask) {
+    
+#ifdef _TASK_STATUS_REQUEST
+    
+    StatusRequest *s = aTask.getStatusRequest();
+    if ( s != NULL && s->pending() ) 
+        return (-1);    // cannot be determined
+#endif
+    if ( !aTask.isEnabled() )
+        return (-1);    // cannot be determined
+    
+    long d = (long) aTask.iDelay - ( (long) ((_TASK_TIME_FUNCTION() - aTask.iPreviousMillis)) );
+    
+    if ( d < 0 ) 
+        return (0); // Task will run as soon as possible
+    return ( d );
+}
+
+Task& Scheduler::currentTask() { return *iCurrent; }
+
+#ifdef _TASK_LTS_POINTER
+void* Scheduler::currentLts() { return iCurrent->iLTS; }
+#endif  // _TASK_LTS_POINTER
+#ifdef _TASK_TIMECRITICAL
+bool Scheduler::isOverrun() { return (iCurrent->iOverrun < 0); }
+#endif  // _TASK_TIMECRITICAL
+
+/** Makes one pass through the execution chain.
+ * Tasks are executed in the order they were added to the chain
+ * There is no concept of priority
+ * Different pseudo "priority" could be achieved
+ * by running task more frequently 
+ */
+bool Scheduler::execute() {
+    bool     idleRun = true;
+    register unsigned long m, i;  // millis, interval;
+
+#ifdef ARDUINO_ARCH_ESP8266
+      unsigned long t1 = micros();
+      unsigned long t2 = 0;
+#endif  // ARDUINO_ARCH_ESP8266
+
+    iCurrent = iFirst;
+    
+    while (iCurrent) {
+        
+#ifdef _TASK_PRIORITY
+    // If scheduler for higher priority tasks is set, it's entire chain is executed on every pass of the base scheduler
+        if (iHighPriority) idleRun = iHighPriority->execute() && idleRun; 
+        iCurrentScheduler = this;
+#endif  // _TASK_PRIORITY
+
+        do {
+            if ( iCurrent->iStatus.enabled ) {
+
+#ifdef _TASK_WDT_IDS
+    // For each task the control points are initialized to avoid confusion because of carry-over:
+                iCurrent->iControlPoint = 0;
+#endif  // _TASK_WDT_IDS
+    
+    // Disable task on last iteration:
+                if (iCurrent->iIterations == 0) {
+                    iCurrent->disable();
+                    break;
+                }
+                m = _TASK_TIME_FUNCTION();
+                i = iCurrent->iInterval;
+
+#ifdef  _TASK_STATUS_REQUEST
+    // 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->iStatus.waiting ) {
+                    if ( (iCurrent->iStatusRequest)->pending() ) break;
+                    if (iCurrent->iStatus.waiting == _TASK_SR_NODELAY) {
+                        iCurrent->iPreviousMillis = m - (iCurrent->iDelay = i);
+                    }
+                    else {
+                        iCurrent->iPreviousMillis = m;
+                    }
+                    iCurrent->iStatus.waiting = 0;
+                }
+#endif  // _TASK_STATUS_REQUEST
+
+                if ( m - iCurrent->iPreviousMillis < iCurrent->iDelay ) break;
+
+                if ( iCurrent->iIterations > 0 ) iCurrent->iIterations--;  // do not decrement (-1) being a signal of never-ending task
+                iCurrent->iRunCounter++;
+                iCurrent->iPreviousMillis += iCurrent->iDelay;
+
+#ifdef _TASK_TIMECRITICAL
+    // 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) 
+                unsigned long p = iCurrent->iPreviousMillis;
+                iCurrent->iOverrun = (long) ( p + i - m );
+                iCurrent->iStartDelay = (long) ( m - p ); 
+#endif  // _TASK_TIMECRITICAL
+
+                iCurrent->iDelay = i;
+                if ( iCurrent->iCallback ) {
+                    iCurrent->iCallback();
+                    idleRun = false;
+                }
+            }
+        } while (0);    //guaranteed single run - allows use of "break" to exit 
+        iCurrent = iCurrent->iNext;
+#ifdef ARDUINO_ARCH_ESP8266
+        yield();
+#endif  // ARDUINO_ARCH_ESP8266
+    }
+
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+    if (idleRun && iAllowSleep) {
+
+#ifdef ARDUINO_ARCH_AVR // Could be used only for AVR-based boards. 
+      set_sleep_mode(SLEEP_MODE_IDLE);
+      sleep_enable();
+      /* Now enter sleep mode. */
+      sleep_mode();
+      
+      /* The program will continue from here after the timer timeout ~1 ms */
+      sleep_disable(); /* First thing to do is disable sleep. */
+#endif // ARDUINO_ARCH_AVR
+
+#ifdef ARDUINO_ARCH_ESP8266
+// to do: find suitable sleep function for esp8266
+      t2 = micros() - t1;
+      if (t2 < _TASK_ESP8266_DLY_THRESHOLD) delay(1);   // ESP8266 implementation of delay() uses timers and yield
+#endif  // ARDUINO_ARCH_ESP8266
+        
+#ifdef ARDUINO_ARCH_ESP32
+#endif  // ARDUINO_ARCH_ESP32
+        
+    }
+#endif  // _TASK_SLEEP_ON_IDLE_RUN
+
+    return (idleRun);
+}
+
+
+
+#endif /* _TASKSCHEDULER_H_ */

+ 226 - 227
src/TaskSchedulerDeclarations.h

@@ -1,227 +1,226 @@
-// Cooperative multitasking library for Arduino
-// Copyright (c) 2015-2017 Anatoli Arkhipenko
-
-#include <stddef.h> 
-
-#ifndef _TASKSCHEDULERDECLARATIONS_H_
-#define _TASKSCHEDULERDECLARATIONS_H_
-
-// ----------------------------------------
-// The following "defines" control library functionality at compile time,
-// and should be used in the main sketch depending on the functionality required
-// 
-// #define _TASK_TIMECRITICAL      // Enable monitoring scheduling overruns
-// #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass 
-// #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_PRIORITY          // Support for layered scheduling priority
-// #define _TASK_MICRO_RES         // Support for microsecond resolution
-// #define _TASK_STD_FUNCTION      // Support for std::function (ESP8266 ONLY)
-// #define _TASK_DEBUG             // Make all methods and variables public for debug purposes
-
-
-
-
-#ifdef _TASK_DEBUG
-    #define _TASK_SCOPE  public
-#else
-    #define _TASK_SCOPE  private
-#endif
-
-
-#define TASK_IMMEDIATE          0
-#define TASK_FOREVER         (-1)
-#define TASK_ONCE               1
-
-#ifdef _TASK_PRIORITY
-    class Scheduler;
-    extern Scheduler* iCurrentScheduler;
-#endif // _TASK_PRIORITY
-
-#ifndef _TASK_MICRO_RES
-
-#define TASK_SECOND         1000UL
-#define TASK_MINUTE        60000UL
-#define TASK_HOUR        3600000UL
-
-#else
-
-#define TASK_SECOND      1000000UL
-#define TASK_MINUTE     60000000UL
-#define TASK_HOUR     3600000000UL
-
-#endif  // _TASK_MICRO_RES
-
-
-#ifdef _TASK_STATUS_REQUEST
-
-#define _TASK_SR_NODELAY    1
-#define _TASK_SR_DELAY      2
-
-class StatusRequest {
-  public:
-    StatusRequest();
-    void setWaiting(unsigned int aCount = 1);
-    bool signal(int aStatus = 0);
-    void signalComplete(int aStatus = 0);
-    bool pending();
-    bool completed();
-    int getStatus();
-    int getCount();
-
-  _TASK_SCOPE:
-    unsigned int  iCount;          // number of statuses to wait for. waiting for more that 65000 events seems unreasonable: unsigned int should be sufficient
-    int           iStatus;         // status of the last completed request. negative = error;  zero = OK; positive = OK with a specific status
-};
-#endif  // _TASK_STATUS_REQUEST
-
-#ifdef _TASK_STD_FUNCTION
-#include <functional>
-typedef std::function<void()> TaskCallback;
-typedef std::function<void()> TaskOnDisable;
-typedef std::function<bool()> TaskOnEnable;
-#else
-typedef void (*TaskCallback)();
-typedef void (*TaskOnDisable)();
-typedef bool (*TaskOnEnable)();
-#endif
-
-typedef struct  {
-    bool  enabled    : 1;           // indicates that task is enabled or not.
-    bool  inonenable : 1;           // indicates that task execution is inside OnEnable method (preventing infinite loops)
-#ifdef _TASK_STATUS_REQUEST
-    byte  waiting    : 2;           // indication if task is waiting on the status request
-#endif
-} __task_status;
-
-class Scheduler; 
-
-
-class Task {
-  friend class Scheduler;
-  public:
-    Task(unsigned long aInterval=0, long aIterations=0, TaskCallback aCallback=NULL, Scheduler* aScheduler=NULL, bool aEnable=false, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL);
-#ifdef _TASK_STATUS_REQUEST
-    Task(TaskCallback aCallback=NULL, Scheduler* aScheduler=NULL, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL);
-#endif  // _TASK_STATUS_REQUEST
-    ~Task();
-
-    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();
-    bool isEnabled();
-    void set(unsigned long aInterval, long aIterations, TaskCallback aCallback,TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL);
-    void setInterval(unsigned long aInterval);
-    unsigned long getInterval();
-    void setIterations(long aIterations);
-    long getIterations();
-    unsigned long getRunCounter() ;
-    void setCallback(TaskCallback aCallback) ;
-    void setOnEnable(TaskOnEnable aCallback) ;
-    void setOnDisable(TaskOnDisable aCallback) ;
-    void yield(TaskCallback aCallback);
-    void yieldOnce(TaskCallback aCallback);
-    bool isFirstIteration() ;
-    bool isLastIteration() ;
-    
-#ifdef _TASK_TIMECRITICAL
-    long getOverrun() ;
-    long getStartDelay() ;
-#endif  // _TASK_TIMECRITICAL
-
-#ifdef _TASK_STATUS_REQUEST
-    void waitFor(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1);
-    void waitForDelayed(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1);
-    StatusRequest* getStatusRequest() ;
-    StatusRequest* getInternalStatusRequest() ;
-#endif  // _TASK_STATUS_REQUEST
-
-#ifdef _TASK_WDT_IDS
-    void setId(unsigned int aID) ;
-    unsigned int getId() ;
-    void setControlPoint(unsigned int aPoint) ;
-    unsigned int getControlPoint() ;
-#endif  // _TASK_WDT_IDS
-
-#ifdef _TASK_LTS_POINTER
-    void  setLtsPointer(void *aPtr) ;
-    void* getLtsPointer() ;
-#endif  // _TASK_LTS_POINTER
-
-  _TASK_SCOPE:
-    void reset();
-
-    volatile __task_status    iStatus;
-    volatile unsigned long    iInterval;             // execution interval in milliseconds (or microseconds). 0 - immediate
-    volatile unsigned long    iDelay;                // actual delay until next execution (usually equal iInterval)
-    volatile unsigned long    iPreviousMillis;       // previous invocation time (millis).  Next invocation = iPreviousMillis + iInterval.  Delayed tasks will "catch up" 
-#ifdef _TASK_TIMECRITICAL
-    volatile long             iOverrun;              // negative if task is "catching up" to it's schedule (next invocation time is already in the past)
-    volatile long             iStartDelay;           // actual execution of the task's callback method was delayed by this number of millis
-#endif  // _TASK_TIMECRITICAL
-    volatile long             iIterations;           // number of iterations left. 0 - last iteration. -1 - infinite iterations
-    long                      iSetIterations;        // number of iterations originally requested (for restarts)
-    unsigned long             iRunCounter;           // current number of iteration (starting with 1). Resets on enable. 
-    TaskCallback              iCallback;             // pointer to the void callback method
-    TaskOnEnable              iOnEnable;             // pointer to the bolol OnEnable callback method
-    TaskOnDisable             iOnDisable;            // pointer to the void OnDisable method
-    Task                     *iPrev, *iNext;         // pointers to the previous and next tasks in the chain
-    Scheduler                *iScheduler;            // pointer to the current scheduler
-#ifdef _TASK_STATUS_REQUEST
-    StatusRequest            *iStatusRequest;        // pointer to the status request task is or was waiting on
-    StatusRequest             iMyStatusRequest;      // internal Status request to let other tasks know of completion
-#endif  // _TASK_STATUS_REQUEST
-#ifdef _TASK_WDT_IDS
-    unsigned int              iTaskID;               // task ID (for debugging and watchdog identification)
-    unsigned int              iControlPoint;         // current control point within the callback method. Reset to 0 by scheduler at the beginning of each pass
-#endif  // _TASK_WDT_IDS
-#ifdef _TASK_LTS_POINTER
-    void                     *iLTS;                  // pointer to task's local storage. Needs to be recast to appropriate type (usually a struct).
-#endif  // _TASK_LTS_POINTER
-};
-
-class Scheduler {
-  friend class Task;
-  public:
-    Scheduler();
-    void init();
-    void addTask(Task& aTask);
-    void deleteTask(Task& aTask);
-    void disableAll(bool aRecursive = true);
-    void enableAll(bool aRecursive = true);
-    bool execute();                              // Returns true if none of the tasks' callback methods was invoked (true = idle run)
-    void startNow(bool aRecursive = true);       // reset ALL active tasks to immediate execution NOW.
-    Task& currentTask() ;
-    long timeUntilNextIteration(Task& aTask);    // return number of ms until next iteration of a given Task
-#ifdef _TASK_SLEEP_ON_IDLE_RUN
-    void allowSleep(bool aState = true);
-#endif  // _TASK_SLEEP_ON_IDLE_RUN
-#ifdef _TASK_LTS_POINTER
-    void* currentLts() ;
-#endif  // _TASK_LTS_POINTER
-#ifdef _TASK_TIMECRITICAL
-    bool isOverrun() ;
-#endif  // _TASK_TIMECRITICAL
-#ifdef _TASK_PRIORITY
-    void setHighPriorityScheduler(Scheduler* aScheduler);
-    static Scheduler& currentScheduler() { return *(iCurrentScheduler); };
-#endif  // _TASK_PRIORITY
-
-  _TASK_SCOPE:
-    Task       *iFirst, *iLast, *iCurrent;        // pointers to first, last and current tasks in the chain
-#ifdef _TASK_SLEEP_ON_IDLE_RUN
-    bool        iAllowSleep;                      // indication if putting avr to IDLE_SLEEP mode is allowed by the program at this time. 
-#endif  // _TASK_SLEEP_ON_IDLE_RUN
-#ifdef _TASK_PRIORITY
-    Scheduler  *iHighPriority;                    // Pointer to a higher priority scheduler
-#endif  // _TASK_PRIORITY
-};
-
-
-#endif /* _TASKSCHEDULERDECLARATIONS_H_ */
+// Cooperative multitasking library for Arduino
+// Copyright (c) 2015-2017 Anatoli Arkhipenko
+
+#include <stddef.h> 
+
+#ifndef _TASKSCHEDULERDECLARATIONS_H_
+#define _TASKSCHEDULERDECLARATIONS_H_
+
+// ----------------------------------------
+// The following "defines" control library functionality at compile time,
+// and should be used in the main sketch depending on the functionality required
+// 
+// #define _TASK_TIMECRITICAL      // Enable monitoring scheduling overruns
+// #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass 
+// #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_PRIORITY          // Support for layered scheduling priority
+// #define _TASK_MICRO_RES         // Support for microsecond resolution
+// #define _TASK_STD_FUNCTION      // Support for std::function (ESP8266 ONLY)
+// #define _TASK_DEBUG             // Make all methods and variables public for debug purposes
+
+#ifdef _TASK_DEBUG
+    #define _TASK_SCOPE  public
+#else
+    #define _TASK_SCOPE  private
+#endif
+
+
+#define TASK_IMMEDIATE          0
+#define TASK_FOREVER         (-1)
+#define TASK_ONCE               1
+
+#ifdef _TASK_PRIORITY
+    class Scheduler;
+    extern Scheduler* iCurrentScheduler;
+#endif // _TASK_PRIORITY
+
+#ifndef _TASK_MICRO_RES
+
+#define TASK_MILLISECOND       1UL
+#define TASK_SECOND         1000UL
+#define TASK_MINUTE        60000UL
+#define TASK_HOUR        3600000UL
+
+#else
+
+#define TASK_MILLISECOND    1000UL
+#define TASK_SECOND      1000000UL
+#define TASK_MINUTE     60000000UL
+#define TASK_HOUR     3600000000UL
+
+#endif  // _TASK_MICRO_RES
+
+
+#ifdef _TASK_STATUS_REQUEST
+
+#define _TASK_SR_NODELAY    1
+#define _TASK_SR_DELAY      2
+
+class StatusRequest {
+  public:
+    StatusRequest();
+    void setWaiting(unsigned int aCount = 1);
+    bool signal(int aStatus = 0);
+    void signalComplete(int aStatus = 0);
+    bool pending();
+    bool completed();
+    int getStatus();
+    int getCount();
+
+  _TASK_SCOPE:
+    unsigned int  iCount;          // number of statuses to wait for. waiting for more that 65000 events seems unreasonable: unsigned int should be sufficient
+    int           iStatus;         // status of the last completed request. negative = error;  zero = OK; positive = OK with a specific status
+};
+#endif  // _TASK_STATUS_REQUEST
+
+#ifdef _TASK_STD_FUNCTION
+#include <functional>
+typedef std::function<void()> TaskCallback;
+typedef std::function<void()> TaskOnDisable;
+typedef std::function<bool()> TaskOnEnable;
+#else
+typedef void (*TaskCallback)();
+typedef void (*TaskOnDisable)();
+typedef bool (*TaskOnEnable)();
+#endif
+
+typedef struct  {
+    bool  enabled    : 1;           // indicates that task is enabled or not.
+    bool  inonenable : 1;           // indicates that task execution is inside OnEnable method (preventing infinite loops)
+#ifdef _TASK_STATUS_REQUEST
+    byte  waiting    : 2;           // indication if task is waiting on the status request
+#endif
+} __task_status;
+
+class Scheduler; 
+
+
+class Task {
+  friend class Scheduler;
+  public:
+    Task(unsigned long aInterval=0, long aIterations=0, TaskCallback aCallback=NULL, Scheduler* aScheduler=NULL, bool aEnable=false, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL);
+#ifdef _TASK_STATUS_REQUEST
+    Task(TaskCallback aCallback=NULL, Scheduler* aScheduler=NULL, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL);
+#endif  // _TASK_STATUS_REQUEST
+    ~Task();
+
+    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();
+    bool isEnabled();
+    void set(unsigned long aInterval, long aIterations, TaskCallback aCallback,TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL);
+    void setInterval(unsigned long aInterval);
+    unsigned long getInterval();
+    void setIterations(long aIterations);
+    long getIterations();
+    unsigned long getRunCounter() ;
+    void setCallback(TaskCallback aCallback) ;
+    void setOnEnable(TaskOnEnable aCallback) ;
+    void setOnDisable(TaskOnDisable aCallback) ;
+    void yield(TaskCallback aCallback);
+    void yieldOnce(TaskCallback aCallback);
+    bool isFirstIteration() ;
+    bool isLastIteration() ;
+    
+#ifdef _TASK_TIMECRITICAL
+    long getOverrun() ;
+    long getStartDelay() ;
+#endif  // _TASK_TIMECRITICAL
+
+#ifdef _TASK_STATUS_REQUEST
+    void waitFor(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1);
+    void waitForDelayed(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1);
+    StatusRequest* getStatusRequest() ;
+    StatusRequest* getInternalStatusRequest() ;
+#endif  // _TASK_STATUS_REQUEST
+
+#ifdef _TASK_WDT_IDS
+    void setId(unsigned int aID) ;
+    unsigned int getId() ;
+    void setControlPoint(unsigned int aPoint) ;
+    unsigned int getControlPoint() ;
+#endif  // _TASK_WDT_IDS
+
+#ifdef _TASK_LTS_POINTER
+    void  setLtsPointer(void *aPtr) ;
+    void* getLtsPointer() ;
+#endif  // _TASK_LTS_POINTER
+
+  _TASK_SCOPE:
+    void reset();
+
+    volatile __task_status    iStatus;
+    volatile unsigned long    iInterval;             // execution interval in milliseconds (or microseconds). 0 - immediate
+    volatile unsigned long    iDelay;                // actual delay until next execution (usually equal iInterval)
+    volatile unsigned long    iPreviousMillis;       // previous invocation time (millis).  Next invocation = iPreviousMillis + iInterval.  Delayed tasks will "catch up" 
+#ifdef _TASK_TIMECRITICAL
+    volatile long             iOverrun;              // negative if task is "catching up" to it's schedule (next invocation time is already in the past)
+    volatile long             iStartDelay;           // actual execution of the task's callback method was delayed by this number of millis
+#endif  // _TASK_TIMECRITICAL
+    volatile long             iIterations;           // number of iterations left. 0 - last iteration. -1 - infinite iterations
+    long                      iSetIterations;        // number of iterations originally requested (for restarts)
+    unsigned long             iRunCounter;           // current number of iteration (starting with 1). Resets on enable. 
+    TaskCallback              iCallback;             // pointer to the void callback method
+    TaskOnEnable              iOnEnable;             // pointer to the bolol OnEnable callback method
+    TaskOnDisable             iOnDisable;            // pointer to the void OnDisable method
+    Task                     *iPrev, *iNext;         // pointers to the previous and next tasks in the chain
+    Scheduler                *iScheduler;            // pointer to the current scheduler
+#ifdef _TASK_STATUS_REQUEST
+    StatusRequest            *iStatusRequest;        // pointer to the status request task is or was waiting on
+    StatusRequest             iMyStatusRequest;      // internal Status request to let other tasks know of completion
+#endif  // _TASK_STATUS_REQUEST
+#ifdef _TASK_WDT_IDS
+    unsigned int              iTaskID;               // task ID (for debugging and watchdog identification)
+    unsigned int              iControlPoint;         // current control point within the callback method. Reset to 0 by scheduler at the beginning of each pass
+#endif  // _TASK_WDT_IDS
+#ifdef _TASK_LTS_POINTER
+    void                     *iLTS;                  // pointer to task's local storage. Needs to be recast to appropriate type (usually a struct).
+#endif  // _TASK_LTS_POINTER
+};
+
+class Scheduler {
+  friend class Task;
+  public:
+    Scheduler();
+    void init();
+    void addTask(Task& aTask);
+    void deleteTask(Task& aTask);
+    void disableAll(bool aRecursive = true);
+    void enableAll(bool aRecursive = true);
+    bool execute();                              // Returns true if none of the tasks' callback methods was invoked (true = idle run)
+    void startNow(bool aRecursive = true);       // reset ALL active tasks to immediate execution NOW.
+    Task& currentTask() ;
+    long timeUntilNextIteration(Task& aTask);    // return number of ms until next iteration of a given Task
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+    void allowSleep(bool aState = true);
+#endif  // _TASK_SLEEP_ON_IDLE_RUN
+#ifdef _TASK_LTS_POINTER
+    void* currentLts() ;
+#endif  // _TASK_LTS_POINTER
+#ifdef _TASK_TIMECRITICAL
+    bool isOverrun() ;
+#endif  // _TASK_TIMECRITICAL
+#ifdef _TASK_PRIORITY
+    void setHighPriorityScheduler(Scheduler* aScheduler);
+    static Scheduler& currentScheduler() { return *(iCurrentScheduler); };
+#endif  // _TASK_PRIORITY
+
+  _TASK_SCOPE:
+    Task       *iFirst, *iLast, *iCurrent;        // pointers to first, last and current tasks in the chain
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+    bool        iAllowSleep;                      // indication if putting avr to IDLE_SLEEP mode is allowed by the program at this time. 
+#endif  // _TASK_SLEEP_ON_IDLE_RUN
+#ifdef _TASK_PRIORITY
+    Scheduler  *iHighPriority;                    // Pointer to a higher priority scheduler
+#endif  // _TASK_PRIORITY
+};
+
+
+#endif /* _TASKSCHEDULERDECLARATIONS_H_ */