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

Support for taask IDs and Control Points

 * Automatically and manually assigned task IDs
 * Manually assigned Control Points within task's callback function
 * Updated existing examples
 * New example sketches
Anatoli Arkhipenko 10 лет назад
Родитель
Сommit
ccd1047d02

+ 8 - 4
README

@@ -1,5 +1,5 @@
 Task Scheduler – cooperative multitasking for Arduino microcontrollers
-Version 1.8.0: 2015-10-15
+Version 1.8.1: 2015-10-22
  
  
 OVERVIEW:
@@ -10,6 +10,7 @@ A lightweight implementation of cooperative multitasking (task scheduling) suppo
 4. Dynamic change of task execution parameters (frequency, number of iterations, callback function)
 5. Power saving via entering IDLE sleep mode between tasks are scheduled to run
 6. Support for task invocation via Status Request object
+7. Support for task IDs and Control Points for error handling and watchdog timer
 
 
 Changelog:
@@ -49,6 +50,9 @@ v1.7.0:
     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
-	
+    2015-10-13 - support for status request objects allowing tasks waiting on requests
+    2015-10-13 - moved to a single header file to allow compilation control via #defines from the main sketch
+
+v1.8.1:
+    2015-10-22 - implement Task id and control points to support identification of failure points for watchdog timer logging
+	

+ 13 - 16
examples/Scheduler_example/Scheduler_example.ino

@@ -1,3 +1,16 @@
+/** 
+ *  TaskScheduler Test
+ *
+ *  Initially only tasks 1 and 2 are enabled
+ *  Task1 runs every 2 seconds 10 times and then stops
+ *  Task2 runs every 3 seconds indefinitely
+ *  Task1 enables Task3 at its first run
+ *  Task3 run every 5 seconds
+ *  Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds
+ *  At the end Task2 is the only task running every 1/2 seconds
+ */
+ 
+ 
 #include <TaskScheduler.h>
 
 Task t4();
@@ -5,18 +18,6 @@ Task t1(2000, 10, &t1Callback);
 Task t2(3000, -1, &t2Callback);
 Task t3(5000, -1, &t3Callback);
 
-// Test
-// Initially only tasks 1 and 2 are enabled
-// Task1 runs every 2 seconds 10 times and then stops
-// Task2 runs every 3 seconds indefinitely
-// Task1 enables Task3 at its first run
-// Task3 run every 5 seconds
-// loop() runs every 1 second (a default scheduler delay, if no shorter tasks' interval is detected)
-// Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds
-// Because Task2 interval is shorter than Scheduler default tick, loop() executes ecery 1/2 seconds now
-// At the end Task2 is the only task running every 1/2 seconds
-
-
 Scheduler runner;
 
 
@@ -74,9 +75,5 @@ void setup () {
 
 
 void loop () {
-  
   runner.execute();
-  
-  Serial.println("Loop ticks at: ");
-  Serial.println(millis());
 }

+ 9 - 0
examples/Scheduler_example3/Scheduler_example3.ino

@@ -1,3 +1,12 @@
+/**
+ * TaskScheduler Test of OnEnable and OnDisable methods and illustration of using wrapper tasks for timout purposes
+ *
+ * 	A wrapper task runs every 30 seconds and initiates the test case
+ * 	Another task is run once for 5 seconds, and serves as a LED blinking timeout - 5 seconds
+ * 	Finally, a dedicated task which controls LED is running periodically until stopped, and makes the LED blink with 0.5 to 1 second interval. 
+ *
+ */
+
 #define _TASK_SLEEP_ON_IDLE_RUN
 #include <TaskScheduler.h>
 

+ 79 - 80
examples/Scheduler_example4_StatusRequest/Scheduler_example4_StatusRequest.ino

@@ -1,81 +1,80 @@
-/** 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();
-
+/** 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.");  
+
+  PrepareStatus();
+  t3.waitFor(&st);
+
+  t1.delay();
+}
+
+void loop() {
+
+  ts.execute();
+
 }

+ 77 - 0
examples/Scheduler_example6_IDLE/Scheduler_example6_IDLE.ino

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

+ 128 - 0
examples/Scheduler_example7_WDT/Scheduler_example7_WDT.ino

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

BIN
extras/TaskScheduler.doc


+ 131 - 12
extras/TaskScheduler.html

@@ -6,7 +6,7 @@
 	<META NAME="GENERATOR" CONTENT="LibreOffice 3.5  (Linux)">
 	<META NAME="CREATED" CONTENT="20150206;16300000">
 	<META NAME="CHANGEDBY" CONTENT="Anatoli Arkhipenko">
-	<META NAME="CHANGED" CONTENT="20151015;23250000">
+	<META NAME="CHANGED" CONTENT="20151022;23060000">
 	<META NAME="Info 1" CONTENT="">
 	<META NAME="Info 2" CONTENT="">
 	<META NAME="Info 3" CONTENT="">
@@ -18,14 +18,15 @@
 		P.western { font-family: "Liberation Serif", "MS PMincho", serif; font-size: 12pt; so-language: en-US }
 		P.cjk { font-family: "WenQuanYi Micro Hei", "MS Mincho"; font-size: 12pt; so-language: zh-CN }
 		P.ctl { font-family: "Lohit Hindi", "MS Mincho"; font-size: 12pt; so-language: hi-IN }
+		A:link { color: #0000ff }
 	-->
 	</STYLE>
 </HEAD>
-<BODY LANG="en-US" TEXT="#000000" BGCOLOR="#ffffff" DIR="LTR">
+<BODY LANG="en-US" TEXT="#000000" LINK="#0000ff" BGCOLOR="#ffffff" DIR="LTR">
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>Task Scheduler –
 cooperative multitasking for Arduino microcontrollers</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"><B>Version 1.8.1:
+2015-10-21</B></P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 </P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>OVERVIEW</B>:</P>
@@ -46,6 +47,8 @@ supporting:</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>
+	<LI><P CLASS="western" STYLE="margin-bottom: 0in">Support for task
+	IDs and Control Points for error handling and watchdog timer</P>
 </OL>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 </P>
@@ -196,8 +199,38 @@ 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"><BR>
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in">#define <B>_TASK_WDT_IDS</B></P>
+<P CLASS="western" STYLE="margin-bottom: 0in">…will compile
+TaskScheduler with support for Task Ids and Control Points. Each task
+can be (and is by default) assigned an ID, which could be used to
+identify the task in case there is a problem with it. Furthermore
+within the task, Control Points could be defined to further help with
+pinpointing potential problem areas. For instance, the tasks which
+deal with external resources (sensors, serial communications,
+anything hardware dependent) can be blocked (or hung), by failed
+hardware. In this case, a watchdog timer could be employed to trap
+such a failed task, and identify which one (by task id) and where in
+the task (by a control point) the problem is likely located.</P>
 <P CLASS="western" STYLE="margin-bottom: 0in"> 
 </P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><B>Note: </B>by
+default, talk IDs are assigned sequentially (1, 2, 3, …) to the
+tasks as they are being created. Programmer can assign a specific
+task id. <B>Task ids are unsigned integers.</B></P>
+<P CLASS="western" STYLE="margin-bottom: 0in">Control points provide
+a way to identify potential problem points within a task. Control
+points are <B>unsigned integers </B>as well. Please note that there
+is only one control point per task, and it is set to zero when the
+task’s callback function is invoked (this is done to prevent
+“stray” control point from previous task(s) confusing the
+matters.</P>
+<P CLASS="western" STYLE="margin-bottom: 0in">Example #7 contains a
+test of task ID and control points functionality.  
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><BR>
+</P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>NOTE: above
 parameters are DISABLED by default, and need to be explicitly
 enabled.</B></P>
@@ -207,11 +240,12 @@ enabled.</B></P>
 </P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 </P>
-<P CLASS="western" STYLE="margin-bottom: 0in"><B>API DOCUMENTATION:</B></P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><FONT SIZE=4><B>API
+DOCUMENTATION:</B></FONT></P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 </P>
 <P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
-<B>TASKS:</B></P>
+<FONT SIZE=4><B>TASKS:</B></FONT></P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 </P>
 <P CLASS="western" STYLE="margin-bottom: 0in">CREATION:</P>
@@ -545,6 +579,11 @@ ran through all their allocated iterations are disabled.
 </P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 </P>
+<P CLASS="western" STYLE="margin-bottom: 0in"><B>STATUS REQUEST
+METHODS:</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>void waitFor(StatusRequest* aStatusRequest);</B></P>
 <P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
@@ -575,17 +614,73 @@ 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 CLASS="western" STYLE="margin-bottom: 0in"><B>TASK ID, CONTROL
+POINTS METHODS:</B></P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
+<BR>
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
+<B>void setId(unsigned int aID);</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 Task IDs, this method will set the task ID
+explicitly. 
 </P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Calling
+this method is not necessary as task IDs are assigned automatically
+during task creation: 1, 2, 3, …</P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><BR>
 </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>
+<B>unsigned int getId();</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 Task IDs, this method return current task’s
+ID.</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>CREATION:</B></P>
+<B>void setControlPoint (unsigned int aPoint);</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 Task IDs, this method will set a control
+point in the task’s code. Control points are similar to “try…catch”
+blocks, with control point ID specifying where in the code the “try”
+part started, and a mechanism like watchdog timer providing the
+“catch” functionality.</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>unsigned int getControlPoint()</B></P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in; page-break-after: avoid">
+<BR>
+</P>
+<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in; page-break-after: avoid">
+If compiled with support for Task IDs, this method will return
+currently set control point for this task.</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">
+<BR>
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid">
+<BR>
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-inside: avoid; page-break-after: avoid">
+<FONT SIZE=4><B>TASK SCHEDULER:</B></FONT></P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-inside: avoid; page-break-after: avoid">
+<BR>
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-inside: avoid; page-break-after: avoid">
+<B>CREATION:</B></P>
+<P CLASS="western" STYLE="margin-bottom: 0in; page-break-inside: avoid; page-break-after: avoid">
 <BR>
 </P>
 <P CLASS="western" STYLE="margin-bottom: 0in"><B>Scheduler()</B><BR><BR>
@@ -1325,8 +1420,6 @@ 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>
@@ -1683,5 +1776,31 @@ loop() {</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>
+<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>FUTHER
+	INFROMATION</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="Courier New, monospace"><FONT SIZE=2>Please
+refer to examples, provided with TaskScheduler package for further
+information and implementation options.</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>Real
+time examples of TaskScheduler are available here:</FONT></FONT></P>
+<OL>
+	<LI><P CLASS="western" STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2><FONT COLOR="#0000ff"><U><A HREF="http://www.instructables.com/id/APIS-Automated-Plant-Irrigation-System/">http://www.instructables.com/id/APIS-Automated-Plant-Irrigation-System/</A></U></FONT></FONT></FONT></P>
+	<LI><P CLASS="western" STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2><FONT COLOR="#0000ff"><U><A HREF="http://www.instructables.com/id/Wave-your-hand-to-control-OWI-Robotic-Arm-no-strin/">http://www.instructables.com/id/Wave-your-hand-to-control-OWI-Robotic-Arm-no-strin/</A></U></FONT></FONT></FONT></P>
+	<LI><P CLASS="western" STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2><FONT COLOR="#0000ff"><U><A HREF="http://www.instructables.com/id/Arduino-Nano-based-Hexbug-Scarab-Robotic-Spider">http://www.instructables.com/id/Arduino-Nano-based-Hexbug-Scarab-Robotic-Spider</A></U></FONT></FONT></FONT></P>
+</OL>
+<P CLASS="western" STYLE="margin-bottom: 0in"><BR>
+</P>
 </BODY>
 </HTML>

+ 4 - 0
keywords.txt

@@ -52,6 +52,10 @@ completed	KEYWORD2
 getStatus	KEYWORD2
 waitFor	KEYWORD2
 getStatusRequest	KEYWORD2
+setId	KEYWORD2
+getId	KEYWORD2
+setControlPoint	KEYWORD2
+getControlPoint	KEYWORD2
 
 #######################################
 # Constants (LITERAL1)

+ 1 - 1
library.properties

@@ -1,5 +1,5 @@
 name=TaskScheduler
-version=1.8.0
+version=1.8.1
 author=Anatoli Arkhipenko <arkhipenko@hotmail.com>
 maintainer=Anatoli Arkhipenko <arkhipenko@hotmail.com>
 sentence=A light-weight cooperative multitasking library for arduino microcontrollers.

+ 31 - 3
src/TaskScheduler.h

@@ -1,4 +1,4 @@
-// Cooperative multitasking library for Arduino version 1.8.0
+// Cooperative multitasking library for Arduino version 1.8.1
 // Copyright (c) 2015 Anatoli Arkhipenko
 //
 // Changelog:
@@ -41,7 +41,9 @@
 // v1.8.0:
 //	  2015-10-13 - support for status request objects allowing tasks waiting on requests
 //	  2015-10-13 - moved to a single header file to allow compilation control via #defines from the main sketch
-
+//
+// v1.8.1:
+//	  2015-10-22 - implement Task id and control points to support identification of failure points for watchdog timer logging
 
 /* ============================================
 Cooperative multitasking library code is placed under the MIT license
@@ -81,6 +83,7 @@ THE SOFTWARE.
  *	#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
+ *	#define _TASK_WDT_IDS		// Compile with support of wdt control points and task ids
  */
 
 #ifdef _TASK_SLEEP_ON_IDLE_RUN
@@ -165,6 +168,12 @@ class Task {
 	void waitFor(StatusRequest* aStatusRequest);
 	inline StatusRequest* getStatusRequest() {return iStatusRequest; }
 #endif
+#ifdef _TASK_WDT_IDS
+	inline void setId(unsigned int aID) { iTaskID = aID; }
+	inline unsigned int getId() { return iTaskID; }
+	inline void setControlPoint(unsigned int aPoint) { iControlPoint = aPoint; }
+	inline unsigned int getControlPoint() { return iControlPoint; }
+#endif
 	
 	
     private:
@@ -187,11 +196,17 @@ class Task {
 #ifdef _TASK_STATUS_REQUEST
 	StatusRequest			*iStatusRequest;
 #endif
+#ifdef _TASK_WDT_IDS
+	unsigned int			iTaskID;
+	unsigned int			iControlPoint;
+#endif
 };
 
 
 // ------------------ TaskScheduler implementation --------------------
-
+#ifdef _TASK_WDT_IDS
+	static unsigned int __task_id_counter = 0;
+#endif
 /** Constructor, uses default values for the parameters
  * so could be called with no parameters.
  */
@@ -203,6 +218,9 @@ Task::Task( unsigned long aInterval, long aIterations, void (*aCallback)(), Sche
 #ifdef _TASK_STATUS_REQUEST
 	iStatusRequest = NULL;
 #endif
+#ifdef _TASK_WDT_IDS
+	iTaskID = ++__task_id_counter;
+#endif
 }
 
 
@@ -216,6 +234,9 @@ Task::Task( void (*aCallback)(), Scheduler* aScheduler, bool (*aOnEnable)(), voi
 	set(0, 1, aCallback, aOnEnable, aOnDisable);
 	if (aScheduler) aScheduler->addTask(*this);
 	iStatusRequest = NULL;
+#ifdef _TASK_WDT_IDS
+	iTaskID = ++__task_id_counter;
+#endif
 }
 
 /** Signals completion of the StatusRequest by one of the participating events
@@ -266,6 +287,9 @@ void Task::reset() {
 #ifdef _TASK_TIMECRITICAL
 	iOverrun = 0;
 #endif
+#ifdef _TASK_WDT_IDS
+	iControlPoint = 0;
+#endif
 }
 
 /** Explicitly set Task execution parameters
@@ -476,6 +500,10 @@ void Scheduler::execute() {
 	while (iCurrent) { 
 		do {   		
 			if (iCurrent->iEnabled) {
+	#ifdef _TASK_WDT_IDS
+	// For each task the control points are initialized to avoid confusion because of carry-over
+				iCurrent->iControlPoint = 0;
+	#endif
 				if (iCurrent->iIterations == 0) {
 					iCurrent->disable();
 					break;