Просмотр исходного кода

TaskScheduler version 1.8.0 - added support for event completion signalling

 * Introduced option to compile with support for Status Rquests - ability to wait for and signal event completion
 * Moved all code to one header file allowing control at compile time via #define statments.
Anatoli Arkhipenko 10 лет назад
Родитель
Сommit
bb20df9c65

+ 14 - 3
README

@@ -1,5 +1,5 @@
 Task Scheduler – cooperative multitasking for Arduino microcontrollers
 Task Scheduler – cooperative multitasking for Arduino microcontrollers
-Version 1.7.0: 2015-10-12
+Version 1.8.0: 2015-10-15
  
  
  
  
 OVERVIEW:
 OVERVIEW:
@@ -9,9 +9,11 @@ A lightweight implementation of cooperative multitasking (task scheduling) suppo
 3. Execution of tasks in predefined sequence
 3. Execution of tasks in predefined sequence
 4. Dynamic change of task execution parameters (frequency, number of iterations, callback function)
 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
 5. Power saving via entering IDLE sleep mode between tasks are scheduled to run
- 
- 
+6. Support for task invocation via Status Request object
+
+
 Changelog:
 Changelog:
+v1.0.0:
     2015-02-24 - Initial release 
     2015-02-24 - Initial release 
     2015-02-28 - added delay() and disableOnLastIteration() functions
     2015-02-28 - added delay() and disableOnLastIteration() functions
     2015-03-25 - changed scheduler execute() function for a more precise delay calculation:
     2015-03-25 - changed scheduler execute() function for a more precise delay calculation:
@@ -20,16 +22,20 @@ Changelog:
                  3. Delay is based on the min anticipated wait until next task _AND_ the runtime of execute function itself.
                  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-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
     2015-05-19 - completely removed  delay from the scheduler since there are no power saving there. using 1 ms sleep instead
+	
 v1.4.1:
 v1.4.1:
     2015-09-15 - more careful placement of AVR-specific includes for sleep functions (compatibility with DUE)
     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
                          sleep on idle run is no longer a default and should be explicitly compiled with _TASK_SLEEP_ON_IDLE_RUN defined
+						 
 v1.5.0:
 v1.5.0:
     2015-09-20 - access to currently executing task (for callback functions)
     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 - 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
     2015-09-20 - option to create a task already enabled
+	
 v1.5.1:
 v1.5.1:
     2015-09-21 - bug fix: incorrect handling of active tasks via set() and setIterations(). 
     2015-09-21 - bug fix: incorrect handling of active tasks via set() and setIterations(). 
 		 		 Thanks to Hannes Morgenstern for catching this one
 		 		 Thanks to Hannes Morgenstern for catching this one
+				 
 v1.6.0:
 v1.6.0:
     2015-09-22 - revert back to having all tasks disable on last iteration.
     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 - deprecated disableOnLastIteration method as a result
@@ -41,3 +47,8 @@ v1.7.0:
     2015-10-11 - disable() returns previous enable state (true if was enabled, false if was already 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-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
     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
+	

+ 81 - 0
examples/Scheduler_example4_StatusRequest/Scheduler_example4_StatusRequest.ino

@@ -0,0 +1,81 @@
+/** 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; 
+
+Task t1(5000, 1, &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.");  
+  // put your setup code here, to run once:
+  PrepareStatus();
+  t3.waitFor(&st);
+
+  t1.delay();
+}
+
+void loop() {
+  // put your main code here, to run repeatedly:
+  ts.execute();
+
+}

+ 165 - 0
examples/Scheduler_example5_StatusRequest/Scheduler_example5_StatusRequest.ino

@@ -0,0 +1,165 @@
+/** 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; 
+
+Task tCycle(10000, -1, &CycleCallback, &ts, true);
+Task tMeasure(1000, 1, &MeasureCallback, &ts, false, &MeasureEnable, &MeasureDisable);
+Task tCalculate(&CalcCallback, &ts);
+Task tSensor1(0, 1, &S1Callback, &ts, false, &S1Enable);
+Task tSensor2(0, 1, &S2Callback, &ts, false, &S2Enable);
+Task tSensor3(0, 1, &S3Callback, &ts, false, &S3Enable);
+
+
+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.restart();
+  tSensor2.restart();
+  tSensor3.restart();
+
+  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(A1)+millis());
+}
+
+void loop() {
+  
+  ts.execute();
+
+}

BIN
extras/TaskScheduler.doc


+ 641 - 29
extras/TaskScheduler.html

@@ -6,7 +6,7 @@
 	<META NAME="GENERATOR" CONTENT="LibreOffice 3.5  (Linux)">
 	<META NAME="GENERATOR" CONTENT="LibreOffice 3.5  (Linux)">
 	<META NAME="CREATED" CONTENT="20150206;16300000">
 	<META NAME="CREATED" CONTENT="20150206;16300000">
 	<META NAME="CHANGEDBY" CONTENT="Anatoli Arkhipenko">
 	<META NAME="CHANGEDBY" CONTENT="Anatoli Arkhipenko">
-	<META NAME="CHANGED" CONTENT="20151012;23280000">
+	<META NAME="CHANGED" CONTENT="20151015;23250000">
 	<META NAME="Info 1" CONTENT="">
 	<META NAME="Info 1" CONTENT="">
 	<META NAME="Info 2" CONTENT="">
 	<META NAME="Info 2" CONTENT="">
 	<META NAME="Info 3" CONTENT="">
 	<META NAME="Info 3" CONTENT="">
@@ -24,8 +24,8 @@
 <BODY LANG="en-US" TEXT="#000000" BGCOLOR="#ffffff" DIR="LTR">
 <BODY LANG="en-US" TEXT="#000000" BGCOLOR="#ffffff" DIR="LTR">
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>Task Scheduler –
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>Task Scheduler –
 cooperative multitasking for Arduino microcontrollers</B></P>
 cooperative multitasking for Arduino microcontrollers</B></P>
-<P CLASS="western" STYLE="margin-bottom: 0in"><B>Version 1.7.0:
-2015-10-12</B></P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><B>Version 1.8.0:
+2015-10-15</B></P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 </P>
 </P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>OVERVIEW</B>:</P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>OVERVIEW</B>:</P>
@@ -44,6 +44,8 @@ supporting:</P>
 	function)</P>
 	function)</P>
 	<LI><P CLASS="western" STYLE="margin-bottom: 0in">Power saving via
 	<LI><P CLASS="western" STYLE="margin-bottom: 0in">Power saving via
 	entering IDLE sleep mode between tasks are scheduled to run</P>
 	entering IDLE sleep mode between tasks are scheduled to run</P>
+	<LI><P CLASS="western" STYLE="margin-bottom: 0in">Support for task
+	invocation via Status Request object</P>
 </OL>
 </OL>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 </P>
 </P>
@@ -52,6 +54,8 @@ supporting:</P>
 container concept that links together:</P>
 container concept that links together:</P>
 <OL>
 <OL>
 	<LI><P CLASS="western" STYLE="margin-bottom: 0in">Execution interval</P>
 	<LI><P CLASS="western" STYLE="margin-bottom: 0in">Execution interval</P>
+	<LI><P CLASS="western" STYLE="margin-bottom: 0in">Execution event
+	(Status Request)</P>
 	<LI><P CLASS="western" STYLE="margin-bottom: 0in">Number of
 	<LI><P CLASS="western" STYLE="margin-bottom: 0in">Number of
 	execution iterations</P>
 	execution iterations</P>
 	<LI><P CLASS="western" STYLE="margin-bottom: 0in">Piece of code
 	<LI><P CLASS="western" STYLE="margin-bottom: 0in">Piece of code
@@ -104,7 +108,7 @@ tasks in the chain always requires immediate execution (aInterval =
 </P>
 </P>
 <P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
 <P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
 <B>Below is the flowchart of a Task lifecycle:</B></P>
 <B>Below is the flowchart of a Task lifecycle:</B></P>
-<P CLASS="western" STYLE="margin-bottom: 0in"><IMG SRC="TaskScheduler_html.png" NAME="graphics1" ALIGN=BOTTOM WIDTH=664 HEIGHT=769 BORDER=0></P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><IMG SRC="TaskScheduler_html.png" NAME="graphics1" ALIGN=BOTTOM WIDTH=664 HEIGHT=747 BORDER=0></P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 </P>
 </P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>Note: </B>Task
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>Note: </B>Task
@@ -124,6 +128,28 @@ to run in a truly periodical manner (in absolute time terms).
 </P>
 </P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 </P>
 </P>
+<P CLASS="western" STYLE="margin-bottom: 0in">In addition to
+time-only invocation, tasks can be scheduled to wait on an event
+employing StatusRequest objects (more about Status Requests later).</P>
+<P CLASS="western" STYLE="margin-bottom: 0in">Consider a scenario
+when one task (t1) is performing a function which affects execution
+of many tasks (t2, t3). In this case the task t1 will “signal”
+completion of its function via Status Request object. Tasks t2 and t3
+are “waiting” on the same Status Request object. As soon as
+status request completes, t2 and t3 are activated.</P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in">Alternative scenario is
+the ne task (t1) and waiting for the completion of a number of tasks
+(t2, t3). When done, t2 and t3 signal completion of their functions,
+t1 is invoked. 
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in">Please see the examples
+at the end of this document.</P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><BR>
+</P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>COMPILE PARAMETERS:</B></P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>COMPILE PARAMETERS:</B></P>
 <P CLASS="western" STYLE="margin-bottom: 0in">This library could be
 <P CLASS="western" STYLE="margin-bottom: 0in">This library could be
 compiled with several options. 
 compiled with several options. 
@@ -163,6 +189,15 @@ conserve power.  Device in SLEEP_MODE_IDLE wakes up to all hardware
 and timer interrupts, so scheduling is kept current.</P>
 and timer interrupts, so scheduling is kept current.</P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 </P>
 </P>
+<P CLASS="western" STYLE="margin-bottom: 0in">#define
+<B>_TASK_STATUS_REQUEST</B></P>
+<P CLASS="western" STYLE="margin-bottom: 0in">…will compile
+TaskScheduler with support for StatusRequest object. Status Requests
+are objects allowing tasks to wait on an event, and signal event
+completion to each other. 
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in"> 
+</P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>NOTE: above
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>NOTE: above
 parameters are DISABLED by default, and need to be explicitly
 parameters are DISABLED by default, and need to be explicitly
 enabled.</B></P>
 enabled.</B></P>
@@ -175,7 +210,8 @@ enabled.</B></P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>API DOCUMENTATION:</B></P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>API DOCUMENTATION:</B></P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 </P>
 </P>
-<P CLASS="western" STYLE="margin-bottom: 0in"><B>TASKS:</B></P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
+<B>TASKS:</B></P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 </P>
 </P>
 <P CLASS="western" STYLE="margin-bottom: 0in">CREATION:</P>
 <P CLASS="western" STYLE="margin-bottom: 0in">CREATION:</P>
@@ -197,7 +233,7 @@ tasks are created <B>disabled</B> by default.</P>
 <P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
 <P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
 <B>Task(unsigned long aInterval, long aIterations, void
 <B>Task(unsigned long aInterval, long aIterations, void
 (*aCallback)(), Scheduler* aScheduler, bool aEnable, bool
 (*aCallback)(), Scheduler* aScheduler, bool aEnable, bool
-(*aOnEnable)(), void (*aOnDisable)(),);</B></P>
+(*aOnEnable)(), void (*aOnDisable)())</B></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 </P>
 </P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Constructor
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Constructor
@@ -249,6 +285,18 @@ task is scheduled for execution immediately. Enable tasks with delay
 to defer first run of the task.</P>
 to defer first run of the task.</P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 </P>
 </P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
+<B>Task(void (*aCallback)(), Scheduler* aScheduler, bool
+(*aOnEnable)(), void (*aOnDisable)())</B></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">If
+compiled with support for Status Request objects, this constructor
+creates a Task for activation on event (since such tasks must run
+<B>waitFor() </B>method, their <I>interval</I>, <I>iteration</I> and
+<I>enabled</I> status will be set by that method.</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>INFORMATION</B></P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>INFORMATION</B></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">The
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">The
 following 3 “getter” functions return task status
 following 3 “getter” functions return task status
@@ -329,7 +377,8 @@ current pass is the last iteration.
 </P>
 </P>
 <P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
 <P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
 <B>CONTROL:</B></P>
 <B>CONTROL:</B></P>
-<P CLASS="western" STYLE="margin-bottom: 0in"><BR>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
+<BR>
 </P>
 </P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>void enable();</B></P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>void enable();</B></P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
@@ -496,16 +545,49 @@ ran through all their allocated iterations are disabled.
 </P>
 </P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 </P>
 </P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
+<B>void waitFor(StatusRequest* aStatusRequest);</B></P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
+<BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">If
+compiled with support for Status Requests, this method makes task
+wait for the completion of <B>aStatusRequest</B> event. 
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><B>waitFor()
+</B>sets tasks interval to <B>0 (zero)</B> for immediate execution
+when event happens, and also sets the number of <B>iterations to 1</B>.</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><B>Note:
+aStatusRequest</B> should be “activated” by calling <B>setWaiting()
+</B>method before making a task wait on it. Otherwise, the task will
+execute immediately. 
+</P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 </P>
 </P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 </P>
 </P>
-<P CLASS="western" STYLE="margin-bottom: 0in"><B>TASK SCHEDULER:</B></P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><B>StatusRequest*
+getStatusRequest()</B></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Returns
+a StatusReqeust object this Task was waiting on. 
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 </P>
 </P>
-<P CLASS="western" STYLE="margin-bottom: 0in"><B>CREATION:</B></P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
+<B>TASK SCHEDULER:</B></P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 </P>
 </P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
+<B>CREATION:</B></P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
+<BR>
+</P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>Scheduler()</B><BR><BR>
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>Scheduler()</B><BR><BR>
 </P>
 </P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Default
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Default
@@ -614,6 +696,97 @@ statements after <B>execute</B> inside the <B>loop()</B>
 </P>
 </P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 </P>
 </P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
+<B>STATUS REQUEST:</B></P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
+<BR>
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
+<B>CREATION:</B></P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
+<BR>
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><B>StatusRequest()</B><BR><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Default
+constructor. 
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Takes
+no parameters. Creates Status Request object, which is assigned a
+status of “completed” on creation. 
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><B>void
+setWaiting(unsigned int aCount)</B></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Activates
+Status Request object. By default each object is set to wait on one
+event only, however, if <B>aCount</B> is supplied, Status Request can
+wait on multiple events. For instance, <B>setWaiting(3)</B> will wait
+on three signals. An example could be waiting for completion of
+measurements from 3 sensors. 
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><B>bool signal(int
+aStatus)</B></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Signals
+completion of the event to the Status Request object, and passes a
+completion code, which could be interrogated later. 
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><B>Note:
+</B> passing a <B>negative</B> status code to the status request
+object is considered reporting an error condition, and will complete
+the status request regardless of how many outstanding signals it is
+still waiting for. 
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><B>Note</B>:
+only the latest status code is kept.</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><B>bool signalComplete
+(int aStatus)</B></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Signals
+completion of <B>ALL</B> events to the Status Request object, and
+passes a completion code, which could be interrogated later. The
+status request completes regardless of how many events it is still
+waiting on. 
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><B>bool pending() </B>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Returns
+<B>true</B> if status request is still waiting for event or events to
+happen.</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><B>bool completed () </B>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Returns
+<B>true</B> if status has completed.</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><B>int getStatus()</B></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Returns
+the status code passed to the status request object by the <B>signal()
+</B>and <B>signalComplete() </B>methods. 
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Any
+<B>positive</B> number is considered a successful completion status.</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">A
+0 (zero) is considered a default successful completion status.</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Any
+<B>negative</B> number is considered an error code and unsuccessful
+completion of a request.</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
 <P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
 <P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
 <B>IMPLEMENTATION SCENARIOS AND IDEAS:</B></P>
 <B>IMPLEMENTATION SCENARIOS AND IDEAS:</B></P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
@@ -971,12 +1144,26 @@ is the implementation using TaskScheduler </FONT>
 </P>
 </P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 </P>
 </P>
-<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2><BR>#include
-&lt;TaskScheduler.h&gt;<BR><BR>#define  LEDPIN		13<BR><BR><BR>Scheduler
-ts;<BR></FONT></FONT><BR>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2><BR></FONT></FONT><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>#define
+_TASK_SLEEP_ON_IDLE_RUN</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>#include
+&lt;TaskScheduler.h&gt;</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>#define
+ LEDPIN    13</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Scheduler
+ts;</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 </P>
 </P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
-tWrapper(30000L, -1, &amp;WrapperCallback, &amp;ts, true);</FONT></FONT></P>
+tWrapper(30000, -1, &amp;WrapperCallback, &amp;ts, true);</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
 tBlink(5000, 1, NULL, &amp;ts, false, &amp;BlinkOnEnable,
 tBlink(5000, 1, NULL, &amp;ts, false, &amp;BlinkOnEnable,
 &amp;BlinkOnDisable);</FONT></FONT></P>
 &amp;BlinkOnDisable);</FONT></FONT></P>
@@ -986,12 +1173,16 @@ tLED(0, -1, NULL, &amp;ts, false, NULL, &amp;LEDOff);</FONT></FONT></P>
 </P>
 </P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
 WrapperCallback() {</FONT></FONT></P>
 WrapperCallback() {</FONT></FONT></P>
-<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>	tBlink.restartDelayed();
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.println(&quot;In
+WrapperCallback&quot;);</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>tBlink.restartDelayed();
   // LED blinking  is initiated </FONT></FONT>
   // LED blinking  is initiated </FONT></FONT>
 </P>
 </P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">  
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">  
-<FONT FACE="Courier New, monospace"><FONT SIZE=2>					   //every 30
-seconds for 5 seconds</FONT></FONT></P>
+            <FONT FACE="Courier New, monospace"><FONT SIZE=2>//every
+30 seconds for 5 seconds</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 </P>
 </P>
@@ -1005,15 +1196,22 @@ and enable LED blinking task, which actually controls</FONT></FONT></P>
 the hardware (LED in this example)</FONT></FONT></P>
 the hardware (LED in this example)</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>bool
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>bool
 BlinkOnEnable() {</FONT></FONT></P>
 BlinkOnEnable() {</FONT></FONT></P>
-<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>	tLED.setInterval(
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.println(&quot;In
+BlinkOnEnable&quot;);</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>tLED.setInterval(
 500 + random(501) );</FONT></FONT></P>
 500 + random(501) );</FONT></FONT></P>
-<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>	tLED.setCallback(
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>tLED.setCallback(
 &amp;LEDOn);</FONT></FONT></P>
 &amp;LEDOn);</FONT></FONT></P>
-<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>	tLED.enable();</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>tLED.enable();</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 </P>
 </P>
-<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>	return
-true;  // Task should be enabled</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>return true;  //
+Task should be enabled</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 </P>
 </P>
@@ -1033,24 +1231,38 @@ thus executing its OnDisable method below.</FONT></FONT></P>
 </P>
 </P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
 BlinkOnDisable() {</FONT></FONT></P>
 BlinkOnDisable() {</FONT></FONT></P>
-<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>	tLED.disable();</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.println(&quot;In
+BlinkOnDisable&quot;);</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>tLED.disable();</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 </P>
 </P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
 LEDOn () {</FONT></FONT></P>
 LEDOn () {</FONT></FONT></P>
-<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>	digitalWrite(LEDPIN,
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.println(&quot;In
+LEDOn&quot;);</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>digitalWrite(LEDPIN,
 HIGH);</FONT></FONT></P>
 HIGH);</FONT></FONT></P>
-<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>	tLED.setCallback(
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>tLED.setCallback(
 &amp;LEDOff);</FONT></FONT></P>
 &amp;LEDOff);</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 </P>
 </P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
 LEDOff () {</FONT></FONT></P>
 LEDOff () {</FONT></FONT></P>
-<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>	digitalWrite(LEDPIN,
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.println(&quot;In
+LEDOff&quot;);</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>digitalWrite(LEDPIN,
 LOW);</FONT></FONT></P>
 LOW);</FONT></FONT></P>
-<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>	tLED.setCallback(
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>tLED.setCallback(
 &amp;LEDOn);</FONT></FONT></P>
 &amp;LEDOn);</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
@@ -1064,12 +1276,412 @@ task finishes (or disabled ahead of time)</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 </P>
 </P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
-setup() {<BR>  // put your setup code here, to run once:<BR>}<BR><BR>void
-loop() {<BR>  // put your main code here, to run repeatedly:<BR> 
-ts.execute();<BR>}</FONT></FONT></P>
+setup() {</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.begin(115200);
+</FONT></FONT>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>pinMode(LEDPIN,
+OUTPUT); </FONT></FONT>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
+loop() {</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>// put your main
+code here, to run repeatedly:</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>ts.execute();</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<OL START=3>
+	<LI><P CLASS="western" STYLE="margin-bottom: 0in"><FONT FACE="Times New Roman, serif"><B>USING
+	STATUS REQUEST OBJECTS  </B></FONT>
+	</P>
+</OL>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Times New Roman, serif"><FONT SIZE=2 STYLE="font-size: 11pt">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.</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Times New Roman, serif"><FONT SIZE=2 STYLE="font-size: 11pt">The
+overall timeout of 1 second is setup as well.</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Times New Roman, serif"><FONT SIZE=2 STYLE="font-size: 11pt">An
+error message needs to be printed if a timeout occurred instead of a
+value.</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>#define
+_TASK_SLEEP_ON_IDLE_RUN</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>#define
+_TASK_STATUS_REQUEST</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>#include
+&lt;TaskScheduler.h&gt;</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>StatusRequest
+measure;</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Scheduler
+ts; </FONT></FONT>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
+tCycle(10000, -1, &amp;CycleCallback, &amp;ts, true);</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
+tMeasure(1000, 1, &amp;MeasureCallback, &amp;ts, false,
+&amp;MeasureEnable, &amp;MeasureDisable);</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
+tCalculate(&amp;CalcCallback, &amp;ts);</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
+tSensor1(0, 1, &amp;S1Callback, &amp;ts, false, &amp;S1Enable);</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
+tSensor2(0, 1, &amp;S2Callback, &amp;ts, false, &amp;S2Enable);</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task
+tSensor3(0, 1, &amp;S3Callback, &amp;ts, false, &amp;S3Enable);</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>long
+distance, d1, d2, d3;</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
+CycleCallback() {</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.println(&quot;CycleCallback:
+Initiating measurement cycle every 10 seconds&quot;);</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 </P>
 </P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>tMeasure.restartDelayed();</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
 </P>
 </P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>bool
+MeasureEnable() {</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.println(&quot;MeasureEnable:
+Activating sensors&quot;);  </FONT></FONT>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>distance = 0;</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>measure.setWaiting(3);
+// Set the StatusRequest to wait for 3 signals. </FONT></FONT>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>tCalculate.waitFor(&amp;measure);</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>tSensor1.restart();</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>tSensor2.restart();</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>tSensor3.restart();</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>return true;</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
+MeasureCallback() {</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.println(&quot;MeasureCallback:
+Invoked by calculate task or one second later&quot;);  </FONT></FONT>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>if
+(measure.pending()) {</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">  
+ <FONT FACE="Courier New, monospace"><FONT SIZE=2>tCalculate.disable();</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">  
+ <FONT FACE="Courier New, monospace"><FONT SIZE=2>measure.signalComplete(-1);
+ // signal error</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">  
+ <FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.println(&quot;MeasureCallback:
+Timeout!&quot;);</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>else {</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">  
+ <FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.print(&quot;MeasureCallback:
+Min distance=&quot;);Serial.println(distance);</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
+MeasureDisable() {</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.println(&quot;MeasureDisable:
+Cleaning up&quot;);  </FONT></FONT>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">  
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>tSensor1.disable();</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>tSensor2.disable();</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>tSensor3.disable();</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
+CalcCallback() {</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.println(&quot;CalcCallback:
+calculating&quot;);  </FONT></FONT>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>distance = -1;</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>if (
+measure.getStatus() &gt;= 0) {  // only calculate if statusrequest
+ended successfully</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">  
+ <FONT FACE="Courier New, monospace"><FONT SIZE=2>distance = d1 &lt;
+d2 ? d1 : d2;</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">  
+ <FONT FACE="Courier New, monospace"><FONT SIZE=2>distance = d3 &lt;
+distance ? d3 : distance;</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">  
+ <FONT FACE="Courier New, monospace"><FONT SIZE=2>tMeasure.forceNextIteration();</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>/**
+Simulation code for sensor 1</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> <FONT FACE="Courier New, monospace"><FONT SIZE=2>*
+ ----------------------------</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>*/</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>bool
+S1Enable() {</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.print(&quot;S1Enable:
+Triggering sensor1. Delay=&quot;);  </FONT></FONT>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>tSensor1.setInterval(
+random(1200) );  // Simulating sensor delay, which could go over 1
+second and cause timeout</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>d1 = 0;</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">  
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.println(
+tSensor1.getInterval() );</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>return true;</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
+S1Callback() {</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.print(&quot;S1Callback:
+Emulating measurement. d1=&quot;);  </FONT></FONT>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>d1 = random(501); //
+pick a value from 0 to 500 &quot;centimeters&quot; simulating a
+measurement </FONT></FONT>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>measure.signal();</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">  
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.println(d1); </FONT></FONT>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>/**
+Simulation code for sensor 2</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> <FONT FACE="Courier New, monospace"><FONT SIZE=2>*
+ ----------------------------</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>*/</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>bool
+S2Enable() {</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.print(&quot;S2Enable:
+Triggering sensor2. Delay=&quot;);  </FONT></FONT>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>tSensor2.setInterval(
+random(1200) );  // Simulating sensor delay, which could go over 1
+second and cause timeout</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>d2 = 0;</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.println(
+tSensor2.getInterval() );</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>return true;</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
+S2Callback() {</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.print(&quot;S2Callback:
+Emulating measurement. d2=&quot;);  </FONT></FONT>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>d2 = random(501); //
+pick a value from 0 to 500 &quot;centimeters&quot; simulating a
+measurement</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>measure.signal();</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.println(d2); 
+</FONT></FONT>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>/**
+Simulation code for sensor 3</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> <FONT FACE="Courier New, monospace"><FONT SIZE=2>*
+ ----------------------------</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>*/</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>bool
+S3Enable() {</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.print(&quot;S3Enable:
+Triggering sensor3. Delay=&quot;);  </FONT></FONT>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>tSensor3.setInterval(
+random(1200) );  // Simulating sensor delay, which could go over 1
+second and cause timeout</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>d3 = 0;</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.println(
+tSensor3.getInterval() );</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>return true;</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
+S3Callback() {</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.print(&quot;S3Callback:
+Emulating measurement. d3=&quot;);  </FONT></FONT>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>d3 = random(501); //
+pick a value from 0 to 500 &quot;centimeters&quot; simulating a
+measurement</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>measure.signal();</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">  
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.println(d3); </FONT></FONT>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>/**
+Main Arduino code</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> <FONT FACE="Courier New, monospace"><FONT SIZE=2>*
+ Not much is left here - everything is taken care of by the framework</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>*/</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
+setup() {</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.begin(115200);</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>Serial.println(&quot;TaskScheduler
+StatusRequest Sensor Emulation Test. Complex Test.&quot;);  </FONT></FONT>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>randomSeed(analogRead(A1)+millis());</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void
+loop() {</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">  
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> 
+<FONT FACE="Courier New, monospace"><FONT SIZE=2>ts.execute();</FONT></FONT></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P>
 </BODY>
 </BODY>
 </HTML>
 </HTML>

+ 9 - 0
keywords.txt

@@ -8,6 +8,7 @@
 
 
 Task	KEYWORD1
 Task	KEYWORD1
 Scheduler	KEYWORD1
 Scheduler	KEYWORD1
+StatusRequest	KEYWORD1
 
 
 #######################################
 #######################################
 # Methods and Functions (KEYWORD2)
 # Methods and Functions (KEYWORD2)
@@ -43,6 +44,14 @@ disableOnLastIteration	KEYWORD2
 getOverrun	KEYWORD2
 getOverrun	KEYWORD2
 isFirstIteration	KEYWORD2
 isFirstIteration	KEYWORD2
 isLastIteration	KEYWORD2
 isLastIteration	KEYWORD2
+setWaiting	KEYWORD2
+signal	KEYWORD2
+signalComplete	KEYWORD2
+pending	KEYWORD2
+completed	KEYWORD2
+getStatus	KEYWORD2
+waitFor	KEYWORD2
+getStatusRequest	KEYWORD2
 
 
 #######################################
 #######################################
 # Constants (LITERAL1)
 # Constants (LITERAL1)

+ 2 - 2
library.properties

@@ -1,9 +1,9 @@
 name=TaskScheduler
 name=TaskScheduler
-version=1.7.0
+version=1.8.0
 author=Anatoli Arkhipenko <arkhipenko@hotmail.com>
 author=Anatoli Arkhipenko <arkhipenko@hotmail.com>
 maintainer=Anatoli Arkhipenko <arkhipenko@hotmail.com>
 maintainer=Anatoli Arkhipenko <arkhipenko@hotmail.com>
 sentence=A light-weight cooperative multitasking library for arduino microcontrollers.
 sentence=A light-weight cooperative multitasking library for arduino microcontrollers.
-paragraph=Enables developers to achieve pseudo multi-tasking. The library is NOT pre-emptive, and as such requires cooperative programming to be used; does NOT use any of the timers therefore does not affect PWM pins.
+paragraph=Enables developers to achieve pseudo multi-tasking. The library is NOT pre-emptive, and as such requires cooperative programming to be used; does NOT use any of the timers therefore does not affect PWM pins. Includes support for status request objects, allowing tasks to wait on and signal event completion between each other. 
 category=Schedulers
 category=Schedulers
 url=https://github.com/arkhipenko/TaskScheduler.git
 url=https://github.com/arkhipenko/TaskScheduler.git
 architectures=*
 architectures=*

+ 0 - 323
src/TaskScheduler.cpp

@@ -1,323 +0,0 @@
-// Cooperative multitasking library for Arduino version 1.7.0
-// Copyright (c) 2015 Anatoli Arkhipenko
-//
-
-/* ============================================
-Cooperative multitasking library code is placed under the MIT license
-Copyright (c) 2015 Anatoli Arkhipenko
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-===============================================
-*/
-
-#include "TaskScheduler.h"
-
-// ------------------ Task implementation --------------------
-
-/** Constructor, uses default default values for the parameters
- * so could be called with no parameters.
- */
-Task::Task( unsigned long aInterval, long aIterations, void (*aCallback)(), Scheduler* aScheduler, bool aEnable, bool (*aOnEnable)(), void (*aOnDisable)() ) {
-	reset();
-	set(aInterval, aIterations, aCallback);
-	setOnEnable(aOnEnable);
-	setOnDisable(aOnDisable);
-	if (aScheduler) aScheduler->addTask(*this);
-	if (aEnable) enable();
-}
-
-/** Resets (initializes) the task/
- * Task is not enabled and is taken out 
- * out of the execution chain as a result
- */
-void Task::reset() {
-	iEnabled = false;
-	iPreviousMillis = 0;
-	iPrev = NULL;
-	iNext = NULL;
-	iScheduler = NULL;
-	iRunCounter = 0;
-#ifdef _TASK_TIMECRITICAL
-	iOverrun = 0;
-#endif
-}
-
-/** 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 function which executes the task actions
- * @param aOnEnable - pointer to the callback function which is called on enable()
- * @param aOnDisable - pointer to the callback function which is called on disable()
- */
-void Task::set(unsigned long aInterval, long aIterations, void (*aCallback)(),bool (*aOnEnable)(), void (*aOnDisable)()) {
-	iInterval = 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; 
-}
-
-/** Enables the task 
- *  schedules it for execution as soon as possible,
- *  and resets the RunCounter back to zero
- */
-void Task::enable() {
-	iEnabled = iOnEnable ? (*iOnEnable)() : true;
-	iRunCounter = 0;
-	iPreviousMillis = millis() - iInterval;
-}
-
-/** 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 = iEnabled;
-	if (!iEnabled) 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;
-	iPreviousMillis = millis() - 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 = millis() - 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();
-}
-
-/** 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 = iEnabled;
-	if (iEnabled && iOnDisable) (*iOnDisable)();
-	iEnabled = false;
-	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);
-}
-
-// ------------------ Scheduler implementation --------------------
-
-/** Default constructor.
- * Creates a scheduler with an empty execution chain.
- */
-Scheduler::Scheduler() {
-	init();
-#ifdef _TASK_SLEEP_ON_IDLE_RUN
-	iAllowSleep = true;
-#endif
-}
-
-/** 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) {
-
-	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
- */
-void Scheduler::disableAll() {
-	Task	*current = iFirst;
-	while (current) {
-		current->disable();
-		current = current->iNext;
-	}
-}
-
-
-/** Enables all the tasks in the execution chain
- */
- void Scheduler::enableAll() {
-	Task	*current = iFirst;
-	while (current) {
-		current->enable();
-		current = current->iNext;
-	}
-}
-
-/** 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 
- */
-void Scheduler::execute() {
-#ifdef _TASK_SLEEP_ON_IDLE_RUN
-	bool		idleRun = true;
-#endif
-	
-	iCurrent = iFirst;
-
-	while (iCurrent) { 
-		do {   		
-			if (iCurrent->iEnabled) {
-				if (iCurrent->iIterations == 0) {
-					iCurrent->disable();
-					break;
-				}
-				if (iCurrent->iInterval > 0) {
-					unsigned long targetMillis = iCurrent->iPreviousMillis + iCurrent->iInterval;
-					if (targetMillis <= millis()) {
-						if (iCurrent->iIterations > 0) iCurrent->iIterations--;  // do not decrement (-1) being a signal of eternal task
-						iCurrent->iRunCounter++;
-						iCurrent->iPreviousMillis += iCurrent->iInterval;
-
-	#ifdef _TASK_TIMECRITICAL
-	// Updated_previous+current should put us into the future, so iOverrun should be positive or zero. 
-	// If negative - the task is behind (next execution time is already in the past) 
-						iCurrent->iOverrun = (long) (iCurrent->iPreviousMillis + iCurrent->iInterval - millis());
-	#endif
-
-						if (iCurrent->iCallback) {
-							(*(iCurrent->iCallback))();
-	#ifdef _TASK_SLEEP_ON_IDLE_RUN
-							idleRun = false;
-	#endif
-						}
-						break;
-					}
-				}
-				else {
-					if (iCurrent->iIterations > 0) iCurrent->iIterations--;  // do not decrement (-1) being a signal of eternal task
-					iCurrent->iRunCounter++;
-					if (iCurrent->iCallback) {
-						(*(iCurrent->iCallback))();
-	#ifdef _TASK_SLEEP_ON_IDLE_RUN
-						idleRun = false;
-	#endif
-					}
-				}
-			}
-		} while (0); //guaranteed single run - allows use of "break" to exit 
-		iCurrent = iCurrent->iNext;
-	}
-
-#ifdef _TASK_SLEEP_ON_IDLE_RUN
-  	if (idleRun && iAllowSleep) {
-  	  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
-}
-
-

+ 398 - 6
src/TaskScheduler.h

@@ -1,7 +1,8 @@
-// Cooperative multitasking library for Arduino version 1.7.0
+// Cooperative multitasking library for Arduino version 1.8.0
 // Copyright (c) 2015 Anatoli Arkhipenko
 // Copyright (c) 2015 Anatoli Arkhipenko
 //
 //
 // Changelog:
 // Changelog:
+// v1.0.0:
 //     2015-02-24 - Initial release 
 //     2015-02-24 - Initial release 
 //     2015-02-28 - added delay() and disableOnLastIteration() functions
 //     2015-02-28 - added delay() and disableOnLastIteration() functions
 //     2015-03-25 - changed scheduler execute() function for a more precise delay calculation:
 //     2015-03-25 - changed scheduler execute() function for a more precise delay calculation:
@@ -10,16 +11,20 @@
 //                  3. Delay is based on the min anticipated wait until next task _AND_ the runtime of execute function itself.
 //                  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-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
 //     2015-05-19 - completely removed  delay from the scheduler since there are no power saving there. using 1 ms sleep instead
+//
 // v1.4.1:
 // v1.4.1:
 //     2015-09-15 - more careful placement of AVR-specific includes for sleep functions (compatibility with DUE)
 //     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
 //                          sleep on idle run is no longer a default and should be explicitly compiled with _TASK_SLEEP_ON_IDLE_RUN defined
+//
 // v1.5.0:
 // v1.5.0:
 //	   2015-09-20 - access to currently executing task (for callback functions)
 //	   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 - 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
 //     2015-09-20 - option to create a task already enabled
+//
 // v1.5.1:
 // v1.5.1:
 //	   2015-09-21 - bug fix: incorrect handling of active tasks via set() and setIterations(). 
 //	   2015-09-21 - bug fix: incorrect handling of active tasks via set() and setIterations(). 
 //					Thanks to Hannes Morgenstern for catching this one
 //					Thanks to Hannes Morgenstern for catching this one
+//
 // v1.6.0:
 // v1.6.0:
 //	   2015-09-22 - revert back to having all tasks disable on last iteration.
 //	   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 - deprecated disableOnLastIteration method as a result
@@ -32,6 +37,11 @@
 //	  2015-10-11 - disable() returns previous enable state (true if was enabled, false if was already 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-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
 //	  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
+
 
 
 /* ============================================
 /* ============================================
 Cooperative multitasking library code is placed under the MIT license
 Cooperative multitasking library code is placed under the MIT license
@@ -64,9 +74,14 @@ THE SOFTWARE.
 #ifndef _TASKSCHEDULER_H_
 #ifndef _TASKSCHEDULER_H_
 #define _TASKSCHEDULER_H_
 #define _TASKSCHEDULER_H_
 
 
-//#define _TASK_DEBUG
-//#define _TASK_TIMECRITICAL
-//#define _TASK_SLEEP_ON_IDLE_RUN
+/** ----------------------------------------
+ * 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 functions 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
+ */
 
 
 #ifdef _TASK_SLEEP_ON_IDLE_RUN
 #ifdef _TASK_SLEEP_ON_IDLE_RUN
 #include <avr/sleep.h>
 #include <avr/sleep.h>
@@ -74,6 +89,24 @@ THE SOFTWARE.
 #endif
 #endif
 
 
 
 
+#ifdef _TASK_STATUS_REQUEST
+class StatusRequest {
+	public:
+		StatusRequest() {iCount = 0; iStatus = 0; }
+		inline void setWaiting(unsigned int aCount = 1) { iCount = aCount; iStatus = 0; }
+		bool signal(int aStatus = 0);
+		void signalComplete(int aStatus = 0);
+		inline bool pending() { return (iCount != 0); }
+		inline bool completed() { return (iCount == 0); }
+		inline int getStatus() { return iStatus; }
+		
+	private:
+		unsigned int	iCount;
+		int			iStatus;  // negative = error;  zero = OK; >positive = OK with a specific status
+};
+#endif
+
+
 class Task; 
 class Task; 
 
 
 class Scheduler {
 class Scheduler {
@@ -87,7 +120,7 @@ class Scheduler {
 		void execute();
 		void execute();
 		inline Task& currentTask() {return *iCurrent; }
 		inline Task& currentTask() {return *iCurrent; }
 #ifdef _TASK_SLEEP_ON_IDLE_RUN
 #ifdef _TASK_SLEEP_ON_IDLE_RUN
-		void allowSleep(bool aState) { iAllowSleep = aState; }
+		void allowSleep(bool aState = true) { iAllowSleep = aState; }
 #endif
 #endif
 
 
 	private:
 	private:
@@ -101,6 +134,9 @@ class Task {
     friend class Scheduler;
     friend class Scheduler;
     public:
     public:
 	Task(unsigned long aInterval=0, long aIterations=0, void (*aCallback)()=NULL, Scheduler* aScheduler=NULL, boolean aEnable=false, bool (*aOnEnable)()=NULL, void (*aOnDisable)()=NULL);
 	Task(unsigned long aInterval=0, long aIterations=0, void (*aCallback)()=NULL, Scheduler* aScheduler=NULL, boolean aEnable=false, bool (*aOnEnable)()=NULL, void (*aOnDisable)()=NULL);
+#ifdef _TASK_STATUS_REQUEST
+	Task(void (*aCallback)()=NULL, Scheduler* aScheduler=NULL, bool (*aOnEnable)()=NULL, void (*aOnDisable)()=NULL);
+#endif
 
 
 	void enable();
 	void enable();
 	bool enableIfNot();
 	bool enableIfNot();
@@ -125,7 +161,12 @@ class Task {
 #endif
 #endif
 	inline bool isFirstIteration() { return (iRunCounter <= 1); } 
 	inline bool isFirstIteration() { return (iRunCounter <= 1); } 
 	inline bool isLastIteration() { return (iIterations == 0); }
 	inline bool isLastIteration() { return (iIterations == 0); }
-
+#ifdef _TASK_STATUS_REQUEST
+	void waitFor(StatusRequest* aStatusRequest);
+	inline StatusRequest* getStatusRequest() {return iStatusRequest; }
+#endif
+	
+	
     private:
     private:
 	void reset();
 	void reset();
 
 
@@ -143,7 +184,358 @@ class Task {
 	void					(*iOnDisable)();
 	void					(*iOnDisable)();
 	Task					*iPrev, *iNext;
 	Task					*iPrev, *iNext;
 	Scheduler				*iScheduler;
 	Scheduler				*iScheduler;
+#ifdef _TASK_STATUS_REQUEST
+	StatusRequest			*iStatusRequest;
+#endif
 };
 };
 
 
 
 
+// ------------------ TaskScheduler implementation --------------------
+
+/** Constructor, uses default values for the parameters
+ * so could be called with no parameters.
+ */
+Task::Task( unsigned long aInterval, long aIterations, void (*aCallback)(), Scheduler* aScheduler, bool aEnable, bool (*aOnEnable)(), void (*aOnDisable)() ) {
+	reset();
+	set(aInterval, aIterations, aCallback, aOnEnable, aOnDisable);
+	if (aScheduler) aScheduler->addTask(*this);
+	if (aEnable) enable();
+#ifdef _TASK_STATUS_REQUEST
+	iStatusRequest = NULL;
+#endif
+}
+
+
+#ifdef _TASK_STATUS_REQUEST
+
+/** Constructor with reduced parameter list for tasks created for 
+ *  StatusRequest only triggering (always immediate and only 1 iteration)
+ */
+Task::Task( void (*aCallback)(), Scheduler* aScheduler, bool (*aOnEnable)(), void (*aOnDisable)() ) {
+	reset();
+	set(0, 1, aCallback, aOnEnable, aOnDisable);
+	if (aScheduler) aScheduler->addTask(*this);
+	iStatusRequest = NULL;
+}
+
+/** 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) {
+	if ( ( iStatusRequest = aStatusRequest) ) { // assign internal StatusRequest var and check if it is not NULL
+		setIterations(1);
+		setInterval(0); 
+		enable();
+	}
+}
+
+#endif
+
+/** Resets (initializes) the task/
+ * Task is not enabled and is taken out 
+ * out of the execution chain as a result
+ */
+void Task::reset() {
+	iEnabled = false;
+	iPreviousMillis = 0;
+	iPrev = NULL;
+	iNext = NULL;
+	iScheduler = NULL;
+	iRunCounter = 0;
+#ifdef _TASK_TIMECRITICAL
+	iOverrun = 0;
+#endif
+}
+
+/** 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 function which executes the task actions
+ * @param aOnEnable - pointer to the callback function which is called on enable()
+ * @param aOnDisable - pointer to the callback function which is called on disable()
+ */
+void Task::set(unsigned long aInterval, long aIterations, void (*aCallback)(),bool (*aOnEnable)(), void (*aOnDisable)()) {
+	iInterval = 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; 
+}
+
+/** Enables the task 
+ *  schedules it for execution as soon as possible,
+ *  and resets the RunCounter back to zero
+ */
+void Task::enable() {
+	iRunCounter = 0;
+	iPreviousMillis = millis() - iInterval;
+	iEnabled = iOnEnable ? (*iOnEnable)() : true;
+}
+
+/** 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 = iEnabled;
+	if (!iEnabled) 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;
+	iPreviousMillis = millis() - 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 = millis() - 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();
+}
+
+/** 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 = iEnabled;
+	iEnabled = false;
+	if (previousEnabled && iOnDisable) (*iOnDisable)();
+	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);
+}
+
+// ------------------ Scheduler implementation --------------------
+
+/** Default constructor.
+ * Creates a scheduler with an empty execution chain.
+ */
+Scheduler::Scheduler() {
+	init();
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+	iAllowSleep = true;
+#endif
+}
+
+/** 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) {
+
+	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
+ */
+void Scheduler::disableAll() {
+	Task	*current = iFirst;
+	while (current) {
+		current->disable();
+		current = current->iNext;
+	}
+}
+
+
+/** Enables all the tasks in the execution chain
+ */
+ void Scheduler::enableAll() {
+	Task	*current = iFirst;
+	while (current) {
+		current->enable();
+		current = current->iNext;
+	}
+}
+
+/** 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 
+ */
+void Scheduler::execute() {
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+	bool		idleRun = true;
+#endif
+	
+	iCurrent = iFirst;
+
+	while (iCurrent) { 
+		do {   		
+			if (iCurrent->iEnabled) {
+				if (iCurrent->iIterations == 0) {
+					iCurrent->disable();
+					break;
+				}
+	#ifdef  _TASK_STATUS_REQUEST
+	// If StatusRequest object was provided (not NULL) and it is still bending, this task should not run
+	// Otherwise, continue with execution as usual.  Tasks waiting to StatusRequest usually have  interval = 0, and iterations = 1
+	// so they will run once immediately. 
+				if ( iCurrent->iStatusRequest && (iCurrent->iStatusRequest)->pending() ) break;
+	#endif
+				if ( iCurrent->iInterval > 0 ) {
+					unsigned long targetMillis = iCurrent->iPreviousMillis + iCurrent->iInterval;
+					if ( targetMillis <= millis() ) {
+						if ( iCurrent->iIterations > 0 ) iCurrent->iIterations--;  // do not decrement (-1) being a signal of eternal task
+						iCurrent->iRunCounter++;
+						iCurrent->iPreviousMillis += iCurrent->iInterval;
+
+	#ifdef _TASK_TIMECRITICAL
+	// Updated_previous+current should put us into the future, so iOverrun should be positive or zero. 
+	// If negative - the task is behind (next execution time is already in the past) 
+						iCurrent->iOverrun = (long) (iCurrent->iPreviousMillis + iCurrent->iInterval - millis());
+	#endif
+
+						if ( iCurrent->iCallback ) {
+							( *(iCurrent->iCallback) )();
+	#ifdef _TASK_SLEEP_ON_IDLE_RUN
+							idleRun = false;
+	#endif
+						}
+						break;
+					}
+				}
+				else {
+					if ( iCurrent->iIterations > 0 ) iCurrent->iIterations--;  // do not decrement (-1) being a signal of eternal task
+					iCurrent->iRunCounter++;
+					if ( iCurrent->iCallback ) {
+						( *(iCurrent->iCallback) )();
+	#ifdef _TASK_SLEEP_ON_IDLE_RUN
+						idleRun = false;
+	#endif
+					}
+				}
+			}
+		} while (0); //guaranteed single run - allows use of "break" to exit 
+		iCurrent = iCurrent->iNext;
+	}
+
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+  	if (idleRun && iAllowSleep) {
+  	  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
+}
+
+
+
 #endif /* _TASKSCHEDULER_H_ */
 #endif /* _TASKSCHEDULER_H_ */