diff options
| author | Anatoli Arkhipenko <arkhipenko@hotmail.com> | 2015-09-21 22:01:29 -0400 |
|---|---|---|
| committer | Anatoli Arkhipenko <arkhipenko@hotmail.com> | 2015-09-21 22:01:29 -0400 |
| commit | c79b83937230514877c511ef5bb6e7df2bcd7dff (patch) | |
| tree | 81d8e39edb8a29cc2eb0428c7844281152556f6f | |
'version_1.51'
| -rw-r--r-- | TaskScheduler.cpp | 294 | ||||
| -rw-r--r-- | TaskScheduler.h | 133 | ||||
| -rw-r--r-- | documentation/TaskScheduler.doc | bin | 0 -> 61952 bytes | |||
| -rw-r--r-- | documentation/TaskScheduler.htm | 1271 | ||||
| -rw-r--r-- | examples/Scheduler_example/Scheduler_example.ino | 82 | ||||
| -rw-r--r-- | examples/Scheduler_example2/Scheduler_example2.ino | 70 | ||||
| -rw-r--r-- | keywords.txt | 44 |
7 files changed, 1894 insertions, 0 deletions
diff --git a/TaskScheduler.cpp b/TaskScheduler.cpp new file mode 100644 index 0000000..5599f9c --- /dev/null +++ b/TaskScheduler.cpp @@ -0,0 +1,294 @@ +// Cooperative multitasking library for Arduino version 1.51 +// 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, boolean aEnable) { + reset(); + set(aInterval, aIterations, aCallback); + 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; + iDisableOnLastIteration = false; +#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 + */ +void Task::set(unsigned long aInterval, long aIterations, void (*aCallback)()) { + iInterval = aInterval; + iSetIterations = iIterations = aIterations; + iCallback = aCallback; + if (iEnabled) enable(); +} + +/** 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; + if (iEnabled) enable(); +} + +/** Enables the task + * and schedules it for execution as soon as possible + */ +void Task::enable() { + iEnabled = true; + iPreviousMillis = millis() - iInterval; +} + +/** Enables the task + * and schedules it for execution after a delay = aInterval + */ +void Task::enableDelayed(unsigned long aDelay) { + iEnabled = true; + 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; +} + +/** 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 loner be executed by the scheduler + */ +void Task::disable() { + iEnabled = false; +} + +/** 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) { + if (iCurrent->iDisableOnLastIteration) 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->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 + 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 +} + + diff --git a/TaskScheduler.h b/TaskScheduler.h new file mode 100644 index 0000000..fae04ce --- /dev/null +++ b/TaskScheduler.h @@ -0,0 +1,133 @@ +// Cooperative multitasking library for Arduino version 1.51 +// Copyright (c) 2015 Anatoli Arkhipenko +// +// Changelog: +// 2015-02-24 - Initial release +// 2015-02-28 - added delay() and disableOnLastIteration() functions +// 2015-03-25 - changed scheduler execute() function for a more precise delay calculation: +// 1. Do not delay if any of the tasks ran (making request for immediate execution redundant) +// 2. Delay is invoked only if none of the tasks ran +// 3. Delay is based on the min anticipated wait until next task _AND_ the runtime of execute function itself. +// 2015-05-11 - added restart() and restartDelayed() functions to restart tasks which are on hold after running all iterations +// 2015-05-19 - completely removed delay from the scheduler since there are no power saving there. using 1 ms sleep instead +// v1.41: +// 2015-09-15 - more careful placement of AVR-specific includes for sleep functions (compatibility with DUE) +// sleep on idle run is no longer a default and should be explicitly compiled with _TASK_SLEEP_ON_IDLE_RUN defined +// v1.50: +// 2015-09-20 - access to currently executing task (for callback functions) +// 2015-09-20 - pass scheduler as a parameter to the task constructor to append the task to the end of the chain +// 2015-09-20 - option to create a task already enabled +// v1.51: +// 2015-09-21 - bug fix: incorrect handling of active tasks via set() and setIterations(). +// Thanks to Hannes Morgenstern for catching this one + + +/* ============================================ +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 <Arduino.h> + + +#ifndef _TASKSCHEDULER_H_ +#define _TASKSCHEDULER_H_ + +//#define _TASK_DEBUG +//#define _TASK_TIMECRITICAL +//#define _TASK_SLEEP_ON_IDLE_RUN + + +#ifdef _TASK_SLEEP_ON_IDLE_RUN +#include <avr/sleep.h> +#include <avr/power.h> +#endif + + +class Task; + +class Scheduler { + public: + Scheduler(); + inline void init() { iFirst = NULL; iLast = NULL; iCurrent = NULL; } + void addTask(Task& aTask); + void deleteTask(Task& aTask); + void disableAll(); + void enableAll(); + void execute(); + inline Task& currentTask() {return *iCurrent; } +#ifdef _TASK_SLEEP_ON_IDLE_RUN + void allowSleep(bool aState) { iAllowSleep = aState; } +#endif + + private: + Task *iFirst, *iLast, *iCurrent; +#ifdef _TASK_SLEEP_ON_IDLE_RUN + bool iAllowSleep; +#endif +}; + +class Task { + friend class Scheduler; + public: + Task(unsigned long aInterval=0, long aIterations=0, void (*aCallback)()=NULL, Scheduler* aScheduler=NULL, boolean aEnable=false); + + void enable(); + void enableDelayed(unsigned long aDelay=0); + void delay(unsigned long aDelay=0); + void restart(); + void restartDelayed(unsigned long aDelay=0); + void disable(); + inline bool isEnabled() { return iEnabled; } + void set(unsigned long aInterval, long aIterations, void (*aCallback)()); + void setInterval(unsigned long aInterval); + inline unsigned long getInterval() { return iInterval; } + void setIterations(long aIterations); + inline long getIterations() { return iIterations; } + inline void setCallback(void (*aCallback)()) { iCallback = aCallback; } + inline void disableOnLastIteration(bool aBool) { iDisableOnLastIteration = aBool; } // default is false +#ifdef _TASK_TIMECRITICAL + inline long getOverrun() { return iOverrun; } +#endif + inline bool isFirstIteration() { return (iIterations >= iSetIterations-1); } + inline bool isLastIteration() { return (iIterations == 0); } + + private: + void reset(); + + volatile bool iEnabled; + volatile bool iDisableOnLastIteration; + volatile unsigned long iInterval; + volatile unsigned long iPreviousMillis; +#ifdef _TASK_TIMECRITICAL + volatile long iOverrun; +#endif + volatile long iIterations; + long iSetIterations; + void (*iCallback)(); + Task *iPrev, *iNext; + Scheduler *iScheduler; +}; + + +#endif /* _TASKSCHEDULER_H_ */ diff --git a/documentation/TaskScheduler.doc b/documentation/TaskScheduler.doc Binary files differnew file mode 100644 index 0000000..9c7e7a8 --- /dev/null +++ b/documentation/TaskScheduler.doc diff --git a/documentation/TaskScheduler.htm b/documentation/TaskScheduler.htm new file mode 100644 index 0000000..3b37f1b --- /dev/null +++ b/documentation/TaskScheduler.htm @@ -0,0 +1,1271 @@ +<html> + +<head> +<meta http-equiv=Content-Type content="text/html; charset=windows-1252"> +<meta name=Generator content="Microsoft Word 14 (filtered)"> +<style> +<!-- + /* Font Definitions */ + @font-face + {font-family:"Liberation Serif";} +@font-face + {font-family:OpenSymbol;} +@font-face + {font-family:"Liberation Sans";} + /* Style Definitions */ + p.MsoNormal, li.MsoNormal, div.MsoNormal + {margin:0in; + margin-bottom:.0001pt; + font-size:12.0pt; + font-family:"Liberation Serif";} +p.MsoCaption, li.MsoCaption, div.MsoCaption + {margin-top:6.0pt; + margin-right:0in; + margin-bottom:6.0pt; + margin-left:0in; + font-size:12.0pt; + font-family:"Liberation Serif"; + font-style:italic;} +p.MsoList, li.MsoList, div.MsoList + {margin-top:0in; + margin-right:0in; + margin-bottom:6.0pt; + margin-left:0in; + font-size:12.0pt; + font-family:"Liberation Serif";} +p.MsoBodyText, li.MsoBodyText, div.MsoBodyText + {mso-style-link:"Body Text Char"; + margin-top:0in; + margin-right:0in; + margin-bottom:6.0pt; + margin-left:0in; + font-size:12.0pt; + font-family:"Liberation Serif";} +span.BodyTextChar + {mso-style-name:"Body Text Char"; + mso-style-link:"Body Text"; + font-family:"Liberation Serif";} +p.Heading, li.Heading, div.Heading + {mso-style-name:Heading; + margin-top:12.0pt; + margin-right:0in; + margin-bottom:6.0pt; + margin-left:0in; + page-break-after:avoid; + font-size:14.0pt; + font-family:"Liberation Sans";} +p.Index, li.Index, div.Index + {mso-style-name:Index; + margin:0in; + margin-bottom:.0001pt; + font-size:12.0pt; + font-family:"Liberation Serif";} +span.WW8Num1z0 + {mso-style-name:WW8Num1z0;} +span.WW8Num1z1 + {mso-style-name:WW8Num1z1;} +span.WW8Num1z2 + {mso-style-name:WW8Num1z2;} +span.WW8Num1z3 + {mso-style-name:WW8Num1z3;} +span.WW8Num1z4 + {mso-style-name:WW8Num1z4;} +span.WW8Num1z5 + {mso-style-name:WW8Num1z5;} +span.WW8Num1z6 + {mso-style-name:WW8Num1z6;} +span.WW8Num1z7 + {mso-style-name:WW8Num1z7;} +span.WW8Num1z8 + {mso-style-name:WW8Num1z8;} +span.WW8Num2z0 + {mso-style-name:WW8Num2z0;} +span.WW8Num2z1 + {mso-style-name:WW8Num2z1;} +span.WW8Num2z2 + {mso-style-name:WW8Num2z2;} +span.WW8Num2z3 + {mso-style-name:WW8Num2z3;} +span.WW8Num2z4 + {mso-style-name:WW8Num2z4;} +span.WW8Num2z5 + {mso-style-name:WW8Num2z5;} +span.WW8Num2z6 + {mso-style-name:WW8Num2z6;} +span.WW8Num2z7 + {mso-style-name:WW8Num2z7;} +span.WW8Num2z8 + {mso-style-name:WW8Num2z8;} +span.WW8Num3z0 + {mso-style-name:WW8Num3z0; + font-weight:normal;} +span.WW8Num3z1 + {mso-style-name:WW8Num3z1;} +span.WW8Num3z2 + {mso-style-name:WW8Num3z2;} +span.WW8Num3z3 + {mso-style-name:WW8Num3z3;} +span.WW8Num3z4 + {mso-style-name:WW8Num3z4;} +span.WW8Num3z5 + {mso-style-name:WW8Num3z5;} +span.WW8Num3z6 + {mso-style-name:WW8Num3z6;} +span.WW8Num3z7 + {mso-style-name:WW8Num3z7;} +span.WW8Num3z8 + {mso-style-name:WW8Num3z8;} +span.WW8Num4z0 + {mso-style-name:WW8Num4z0;} +span.WW8Num4z1 + {mso-style-name:WW8Num4z1;} +span.WW8Num4z2 + {mso-style-name:WW8Num4z2;} +span.WW8Num4z3 + {mso-style-name:WW8Num4z3;} +span.WW8Num4z4 + {mso-style-name:WW8Num4z4;} +span.WW8Num4z5 + {mso-style-name:WW8Num4z5;} +span.WW8Num4z6 + {mso-style-name:WW8Num4z6;} +span.WW8Num4z7 + {mso-style-name:WW8Num4z7;} +span.WW8Num4z8 + {mso-style-name:WW8Num4z8;} +span.WW8Num5z0 + {mso-style-name:WW8Num5z0; + font-family:"Times New Roman","serif"; + font-weight:bold;} +span.WW8Num5z1 + {mso-style-name:WW8Num5z1;} +span.WW8Num5z2 + {mso-style-name:WW8Num5z2;} +span.WW8Num5z3 + {mso-style-name:WW8Num5z3;} +span.WW8Num5z4 + {mso-style-name:WW8Num5z4;} +span.WW8Num5z5 + {mso-style-name:WW8Num5z5;} +span.WW8Num5z6 + {mso-style-name:WW8Num5z6;} +span.WW8Num5z7 + {mso-style-name:WW8Num5z7;} +span.WW8Num5z8 + {mso-style-name:WW8Num5z8;} +span.WW8Num6z0 + {mso-style-name:WW8Num6z0;} +span.WW8Num6z1 + {mso-style-name:WW8Num6z1;} +span.WW8Num6z2 + {mso-style-name:WW8Num6z2;} +span.WW8Num6z3 + {mso-style-name:WW8Num6z3;} +span.WW8Num6z4 + {mso-style-name:WW8Num6z4;} +span.WW8Num6z5 + {mso-style-name:WW8Num6z5;} +span.WW8Num6z6 + {mso-style-name:WW8Num6z6;} +span.WW8Num6z7 + {mso-style-name:WW8Num6z7;} +span.WW8Num6z8 + {mso-style-name:WW8Num6z8;} +span.WW8Num7z0 + {mso-style-name:WW8Num7z0;} +span.WW8Num7z1 + {mso-style-name:WW8Num7z1;} +span.WW8Num7z2 + {mso-style-name:WW8Num7z2;} +span.WW8Num7z3 + {mso-style-name:WW8Num7z3;} +span.WW8Num7z4 + {mso-style-name:WW8Num7z4;} +span.WW8Num7z5 + {mso-style-name:WW8Num7z5;} +span.WW8Num7z6 + {mso-style-name:WW8Num7z6;} +span.WW8Num7z7 + {mso-style-name:WW8Num7z7;} +span.WW8Num7z8 + {mso-style-name:WW8Num7z8;} +span.WW-DefaultParagraphFont + {mso-style-name:"WW-Default Paragraph Font";} +span.Absatz-Standardschriftart + {mso-style-name:Absatz-Standardschriftart;} +span.NumberingSymbols + {mso-style-name:"Numbering Symbols";} +span.Bullets + {mso-style-name:Bullets; + font-family:OpenSymbol;} +.MsoChpDefault + {font-size:10.0pt;} +@page WordSection1 + {size:8.5in 11.0in; + margin:56.7pt 56.7pt 56.7pt 56.7pt;} +div.WordSection1 + {page:WordSection1;} + /* List Definitions */ + ol + {margin-bottom:0in;} +ul + {margin-bottom:0in;} +--> +</style> + +</head> + +<body bgcolor=white lang=EN-US style='line-break:strict'> + +<div class=WordSection1> + +<p class=MsoNormal><b>Task Scheduler – cooperative multitasking for Arduino microcontrollers</b></p> + +<p class=MsoNormal><b>Version 1.51: 2015-09-20</b></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal><b>REQUIREMENT</b>:</p> + +<p class=MsoNormal>A lightweight implementation of the task scheduling +supporting:</p> + +<ol style='margin-top:0in' start=1 type=1> + <li class=MsoNormal>execution period (n times per second)</li> + <li class=MsoNormal>number of iterations (n times)</li> + <li class=MsoNormal>execution of tasks in predefined sequence</li> + <li class=MsoNormal>dynamic change of the execution parameters for both tasks + and execution schedule</li> + <li class=MsoNormal>power saving via entering IDLE sleep mode if no tasks are + scheduled to run</li> +</ol> + +<p class=MsoNormal> </p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal><b>IDEA</b>:</p> + +<p class=MsoNormal>“Task” is a container concept that links together:</p> + +<ol style='margin-top:0in' start=1 type=1> + <li class=MsoNormal>Execution interval</li> + <li class=MsoNormal>Number of execution iterations</li> + <li class=MsoNormal>A piece of code performing the task activities (callback + function)</li> +</ol> + +<p class=MsoNormal> </p> + +<p class=MsoNormal>Tasks are linked into execution chains, which are processed +by the “Scheduler” in the order they are linked.</p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal>Tasks are responsible for supporting cooperative +multitasking by being “good neighbors”, i.e., running their callback functions +in a non-blocking way and releasing control as soon as possible. </p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal>“Scheduler” is executing Tasks' callback functions in the +order the tasks were added to the chain, from first to last. Scheduler stops +and exists after processing the chain once in order to allow other statements +in the main code of <b>loop()</b> function to run. This a “scheduling +pass”.</p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal>If compiled with <span style='font-family:"Courier New"'>_TASK_SLEEP_ON_IDLE_RUN</span> +enabled, the scheduler will place processor into IDLE sleep mode (for +approximately 1 ms, as the timer interrupt will wake it up), after what is +determined to be an “idle” pass. An Idle Pass is a pass through the chain when +no Tasks were scheduled to run their callback functions. This is done to avoid +repetitive empty passes through the chain when no tasks need to be executed. If +any of the tasks in the chain always requires immediate execution (aInterval = +0), then there will be no end-of-pass delay.</p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal><b>Note: </b>Task Scheduler uses <b>millis()</b> to +determine if tasks are ready to be invoked. Therefore, if you put your device +to any “deep” sleep mode disabling timer interrupts, the <b>millis()</b> count +will be suspended, leading to effective suspension of scheduling. Upon wake up, +active tasks need to be re-enabled, which will effectively reset their internal +time scheduling variables to the new value of <b>millis(). </b>Time spent in +deep sleep mode should be considered “frozen”, i.e., if a task was scheduled to +run in 1 second from now, and device was put to sleep for 5 minutes, upon wake +up, the task will still be scheduled 1 second from the time of wake up. +Executing <b>enable() </b>function on this tasks will make it run as soon as +possible. This is a concern only for tasks which are required to run in a truly +periodical manner (in absolute time terms). </p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal><b>COMPILE PARAMETERS:</b></p> + +<p class=MsoNormal>This library could be compiled with several options. </p> + +<p class=MsoNormal>These parameters must be defined before inclusion of the +library header file into the sketch.</p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal>#define <b>_TASK_TIMECRITICAL</b></p> + +<p class=MsoNormal>...will compile the library with time critical tracking +option enabled.</p> + +<p class=MsoNormal>Time critical option keeps track where next execution time +of the task falls, and makes it available via API through Task::<b> getOverrun() +</b>function. If <b>getOverrun </b>returns a negative value, this Task’s next +execution time is in the past, and task is behind schedule. This most probably +means that either task’s callback function runtime is too long, or the +execution interval is too short (then schedule is too aggressive).</p> + +<p class=MsoNormal>A positive value indicates that task is on schedule, and +callback functions have enough time to finish before the next scheduled pass. </p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal>#define <b>_TASK_SLEEP_ON_IDLE_RUN</b></p> + +<p class=MsoNormal>...will compile the library with the <b>sleep</b> option +enabled (AVR boards only).</p> + +<p class=MsoNormal>When enabled, scheduler will put the microcontroller into <b>SLEEP_MODE_IDLE</b> +state if none of the tasks’ callback functions were activated during pass. <b>IDLE</b> +state is interrupted by timers once every 1 ms. Helps conserve power. Device +in SLEEP_MODE_IDLE wakes up to all hardware and timer interrupts, so scheduling +is kept current.</p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal><b>NOTE: above parameters are DISABLED by default, and need +to be explicitly enabled.</b></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal><b>API DOCUMENTATION:</b></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal><b>TASKS:</b></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal>CREATION:</p> + +<p class=MsoNormal><b>Task();</b></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal style='margin-left:35.45pt'>Default constructor. </p> + +<p class=MsoNormal style='margin-left:35.45pt'>Takes no parameters and creates +a task that could be scheduled to run at every scheduling pass indefinitely, +but does not have a callback function defined, so no execution will actually +take place. </p> + +<p class=MsoNormal style='margin-left:35.45pt'>All tasks are created <b>disabled</b> +by default.</p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal style='page-break-after:avoid'><b>Task(unsigned long aInterval, +long aIterations, void (*aCallback)(), Scheduler* aScheduler, bool aEnable);</b></p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal style='margin-left:35.45pt'>Constructor with parameters. </p> + +<p class=MsoNormal style='margin-left:35.45pt'>Creates a task that is scheduled +to run every <aInterval> milliseconds, <aIterations> times, +executing <aCallback> function on every pass. </p> + +<p class=MsoNormal style='margin-left:71.45pt;text-indent:-.25in'>1.<span +style='font-size:7.0pt;font-family:"Times New Roman","serif"'> +</span>aInterval is in milliseconds</p> + +<p class=MsoNormal style='margin-left:71.45pt;text-indent:-.25in'>2.<span +style='font-size:7.0pt;font-family:"Times New Roman","serif"'> +</span>aIteration in number of times, -1 for indefinite execution<br> +<b>Note: </b>Tasks do not remember the number of iteration set initially. After +the iterations are done, internal iteration counter is 0. If you need to +perform another set of iterations, you need to set the number of iterations +again. <br> +<b>Note: </b>Tasks which performed all their iterations remain active. </p> + +<p class=MsoNormal style='margin-left:71.45pt;text-indent:-.25in'>3.<span +style='font-size:7.0pt;font-family:"Times New Roman","serif"'> +</span>aCallback is a pointer to a void function without parameters</p> + +<p class=MsoNormal style='margin-left:71.45pt;text-indent:-.25in'>4.<span +style='font-size:7.0pt;font-family:"Times New Roman","serif"'> +</span>aScheduler – <b>optional</b> reference to existing scheduler. If +supplied (not NULL) this task will be appended to the task chain of the current +scheduler). <b>Default=NULL</b></p> + +<p class=MsoNormal style='margin-left:71.45pt;text-indent:-.25in'>5.<span +style='font-size:7.0pt;font-family:"Times New Roman","serif"'> +</span>aEnable – <b>optional</b>. Value of <b>true </b>will create task +enabled. <b>Default = false</b></p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal style='margin-left:35.45pt'>All tasks are created <b>disabled</b> +by default (unless aEnable = true). You have to explicitly enable the task for +execution.</p> + +<p class=MsoNormal style='margin-left:35.45pt'>Enabled task is scheduled for execution +immediately. Enable tasks with delay (standard execution interval or specific +execution interval) in order to defer first run of the task.</p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal><b>INFORMATION</b></p> + +<p class=MsoNormal style='margin-left:35.45pt'>The following 3 “getter” +functions return task status (enabled/disabled), execution interval in +milliseconds, number of <b><i>remaining</i></b> iterations. </p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal style='margin-left:35.45pt'><b>bool isEnabled() </b></p> + +<p class=MsoNormal style='margin-left:35.45pt'><b>unsigned long getInterval()</b></p> + +<p class=MsoNormal style='margin-left:35.45pt'><b>long getIterations() </b></p> + +<p class=MsoNormal style='margin-left:35.45pt'><b> </b></p> + +<p class=MsoNormal><b>long getOverrun()</b></p> + +<p class=MsoNormal style='margin-left:35.45pt'>If library is compiled with <span +style='font-family:"Courier New"'>_TASK_TIMECRITICAL</span> enabled, tasks are +monitored for “long running” scenario. A “long running” task is a task that +does not finish processing its callback functions quickly, and thus creates a +situation for itself and other tasks where they don't run on a scheduled +interval, but rather “catch up” and are behind. When task scheduler sets +the next execution target time, it adds Task's execution interval to the +previously scheduled execution time:</p> + +<p class=MsoNormal style='margin-left:35.45pt'> +<b>next execution time = previous execution time + task execution interval</b></p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal style='margin-left:35.45pt'>If <b>next execution time</b> happens +to be already in the past (<b>next execution time</b> < <b>millis()</b>), then +task is considered <b><i>overrun</i></b>. <b>GetOverrun</b> function returns +number of milliseconds between next execution time and current time. If the <b>value +is negative</b>, the task is overrun by that many milliseconds. </p> + +<p class=MsoNormal style='margin-left:35.45pt'>Positive value indicate number +of milliseconds of slack this task has for execution purposes. </p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal><b>bool isFirstIteration()</b></p> + +<p class=MsoNormal><b>bool isLastIteration()</b></p> + +<p class=MsoNormal style='margin-left:35.45pt'>For tasks with a defined number +of iterations, indicates whether current pass is a first or a last iteration of +the task (respectively). </p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal style='page-break-after:avoid'><b>CONTROL:</b></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal><b>void enable();</b></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal style='margin-left:35.45pt'>Enables the task, and schedules +it for immediate execution (without delay) at this or next scheduling pass +depending on when the task was enabled. Scheduler will execute the next pass +without any delay because there is a task which was enabled and requires +execution. </p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal><b>void delay();</b></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal style='margin-left:35.45pt'>Schedules the task for execution +after a delay (aInterval), but does not change the enabled/disabled status of +the task. </p> + +<p class=MsoNormal style='margin-left:35.45pt'><b> </b></p> + +<p class=MsoNormal><b>void enableDelayed();</b></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal style='margin-left:35.45pt'>Enables the task, and schedules +it for execution after a delay (aInterval). </p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal><b>void enableDelayed (unsigned long aDelay);</b></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal style='margin-left:35.45pt'>Enables the task, and schedules +it for execution after a specific delay (aDelay, which maybe different from aInterval). +</p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal><b>void restart();</b></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal style='margin-left:35.45pt'>For tasks with limited number of +iterations only, <b>restart</b> function will re-enable the task, set the +number of iterations back to when the task was created and and schedule the +task for execution as soon as possible.</p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal><b>void restartDelayed (unsigned long aDelay);</b></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal style='margin-left:35.45pt'>Same as <b>restart() </b>function, +with the only difference being that Task is scheduled to run first iteration +after a delay = <b>aDelay</b> milliseconds.</p> + +<p class=MsoNormal><b> </b></p> + +<p class=MsoNormal><b>void disable();</b></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal style='margin-left:35.45pt'>Disables the task. Scheduler +will not execute this task any longer, even if it remains in the chain. Task +can be later re-enabled for execution. </p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal><b>void disableOnLastIteration (bool aBool);</b></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal style='margin-left:35.45pt'>Controls iterative task behavior +on the last iteration. If <b>aBool</b> is <b>true</b> task will be disabled +after last iteration. If <b>aBool</b> is <b>false</b>, task will remain active +(but not invoked until new number of iterations is set)</p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal><b>void set(unsigned long aInterval, long aIterations, void (*aCallback)());</b></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal style='margin-left:35.45pt'>Allows dynamic control of all +task execution parameters in one function call. If task being modified is +active, it will be scheduled for execution immediately. </p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal>Next three “setter” functions allow changes of individual +task execution control parameters. </p> + +<p class=MsoNormal style='margin-left:35.45pt'><b>void setInterval (unsigned +long aInterval) </b></p> + +<p class=MsoNormal style='margin-left:35.45pt'><b>void setIterations (long aIterations) +</b></p> + +<p class=MsoNormal style='margin-left:35.45pt'><b>void setCallback (void +(*aCallback)()) </b></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal><b>Note: </b>Next execution time calculation takes place <b>after</b> +the callback function is called, so new interval will be used immediately by +the scheduler. For the situations when one task is changing the interval +parameter for the other, <b>setInterval</b> function calls <b>delay </b>explicitly +to guarantee schedule change, however it <b>does not </b>enable the task if +task is disabled. If task being modified is active, <b>setIterations </b>will +make the task be scheduled for execution immediately.</p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal><b> </b></p> + +<p class=MsoNormal><b>TASK SCHEDULER:</b></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal><b>CREATION:</b></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal style='margin-bottom:12.0pt'><b>Scheduler()</b></p> + +<p class=MsoNormal style='margin-left:35.45pt'>Default constructor. </p> + +<p class=MsoNormal style='margin-left:35.45pt'>Takes no parameters. Creates +task scheduler with default parameters and an empty task queue. </p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal><b>void init()</b></p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal style='margin-left:35.45pt'>Initializes the task queue and +scheduler parameters, Executed as part of constructor, so don't need to be +explicitly called after creation.</p> + +<p class=MsoNormal style='margin-left:35.45pt'><b>Note: </b>be default (if +compiled with <span style='font-family:"Courier New"'>_TASK_TIMECRITICAL</span> +enabled) scheduler is allowed to put processor to IDLE sleep mode. If this +behavior was changed via <b>allowSleep() </b>function, <b>inti() </b>will <b>NOT</b> +reset allow sleep particular parameter. </p> + +<p class=MsoNormal><br> +<b>void addTask(Task& aTask)</b></p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal style='margin-left:35.45pt'>Adds task aTask to the execution +queue (or chain) of tasks by appending it to the end of the chain. If two tasks +are scheduled for execution, the sequence will match the order tasks are +appended to the chain. However, in reality, due to different timing of task +execution, the actual order will be different. </p> + +<p class=MsoNormal style='margin-left:35.45pt'><b>Note: </b>Currently, changing +the execution dynamically is not supported. </p> + +<p class=MsoNormal style='margin-left:35.45pt'>If you need to reorder the queue +– initialize the scheduler and re-add the tasks in a different order. </p> + +<p class=MsoNormal><br> +<b>void deleteTask(Task& aTask)</b></p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal style='margin-left:35.45pt'>Deletes task aTask from the +execution chain. The chain of remaining tasks is linked together (i.e</p> + +<p class=MsoNormal style='margin-left:35.45pt'>if original task chain is 1 → +2 → 3 → 4, deleting 3 will result in 1 → 2 → 4).</p> + +<p class=MsoNormal style='margin-left:35.45pt'><b>Note: </b>it is not required +to delete a task from the chain. A disabled task will not be executed anyway, +but you save a few microseconds per scheduling pass by deleting it, since it is +not even considered for execution. </p> + +<p class=MsoNormal style='margin-left:35.45pt'>An example of proper use of this +function would be running some sort of <b>initialize</b> task in the chain, and +then deleting it from the chain since it only needs to run once. </p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal><b>void allowSleep(bool aState) </b></p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal style='margin-left:35.45pt'>Available in API only if compiled +with <span style='font-family:"Courier New"'>_TASK_TIMECRITICAL</span> enabled. +Controls whether scheduler is allowed (<b>aState =true</b>), or not (<b>aState =false</b>) +to put processor into IDLE sleep mode in case not tasks are scheduled to run.</p> + +<p class=MsoNormal style='margin-left:35.45pt'>The <b>default</b> behavior of +scheduler upon creation is to allow sleep mode. </p> + +<p class=MsoNormal><br> +<b>void enableAll()</b></p> + +<p class=MsoNormal><b>void disableAll()</b></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal style='margin-left:35.45pt'>enables and disables +(respectively) all tasks in the chain. Convenient if your need to +enable/disable majority of the tasks (i.e. disable all and then enable one). </p> + +<p class=MsoNormal style='margin-bottom:12.0pt'><br> +<b>Task& currentTask()</b></p> + +<p class=MsoNormal style='margin-left:35.45pt'>Returns reference to the task, +currently executing via <b>execute()</b> loop. Could be used by callback +functions to identify which of the </p> + +<p class=MsoNormal><br> +<b>void execute()</b></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal style='margin-left:35.45pt'>Executes one scheduling pass, +including end-of-pass sleep. This function typically placed inside the <b>loop()</b> +function of the sketch. Since <b>execute</b> exits after every pass, you can +put additional statements after <b>execute</b> inside the <b>loop()</b> </p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal style='margin-left:35.45pt'> </p> + +<p class=MsoNormal style='page-break-after:avoid'><b>IMPLEMENTATION SCENARIOS +AND IDEAS:</b></p> + +<p class=MsoNormal> </p> + +<ol style='margin-top:0in' start=1 type=1> + <li class=MsoNormal><b>EVENT DRIVEN PROGRAMMING</b></li> +</ol> + +<p class=MsoNormal> </p> + +<p class=MsoNormal>Each of the processes of your application becomes a separate +and distinct programming area, which may or may not interact and control each +other. </p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal>Example: </p> + +<p class=MsoNormal>In a plant watering system you need to measure soil +humidity, control pump and display the results</p> + +<p class=MsoNormal>Each of the areas becomes a task:</p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal style='margin-top:0in;margin-right:0in;margin-bottom:12.0pt; +margin-left:35.45pt'><span style='font-size:10.0pt;font-family:"Courier New"'>Task +<b>tMeasure</b> (TMEASURE_INTERVAL*SECOND, -1, &measureCallback);<br> +Task <b>tWater</b> (TWATER_INTERVAL*SECOND, RETRIES, +&waterCallback);<br> +Task <b>tDisplay</b> (TDISPLAY_INTERVAL*SECOND, -1, &displayCallback); </span></p> + +<p class=MsoNormal style='margin-top:0in;margin-right:0in;margin-bottom:12.0pt; +margin-left:35.45pt'><span style='font-size:10.0pt;font-family:"Courier New"'>Scheduler +<b>taskManager</b>;</span></p> + +<p class=MsoNormal>Further, once you turn on the pump, you keep it running for +TWATER_INTERVAL interval and then turn it off. Turning off a pump is also a +task which only needs to run once for every time the pump is turned on:</p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal style='margin-top:0in;margin-right:0in;margin-bottom:12.0pt; +margin-left:35.45pt'><span style='font-size:10.0pt;font-family:"Courier New"'>Task +<b>tWaterOff</b> (WATERTIME*SECOND, 1,</span><span style='font-family:"Courier New"'> +&waterOffCallback);</span></p> + +<p class=MsoNormal>Example of the callback function:</p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'>void waterOffCallback() {<br> + motorOff();<br> + tWater.enableDelayed();<br> +}</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'> </span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'>or</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'> </span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'>void waterCallback() {<br> + if (tWater.getIterations()) {</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'>// If this is not the last iteration = turn the pump +on<br> + motorOn();<br> + tWaterOff.set(parameters.watertime * SECOND, 1, +&waterOffCallback);<br> + tWaterOff.enableDelayed();<br> + return;<br> + }</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'>// We could not reach target humidity – something is +wrong<br> + motorOff;<br> + taskManager.disableAll();<br> + tError.enable();<br> +}</span></p> + +<p class=MsoNormal><span style='font-family:"Courier New"'> </span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'>Your +sample <b>setup</b>() and <b>loop</b>() (partially) are as follows. </span></p> + +<p class=MsoNormal><b><span style='font-family:"Times New Roman","serif"'>Note: +</span></b><span style='font-family:"Times New Roman","serif"'>please note that +tWater is <b>not</b> activated during setup(). It is activated by tMeasure +callback once the watering conditions are met. </span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'> </span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'> +</span><span style='font-family:"Courier New"'>setup()</span></p> + +<p class=MsoNormal><span style='font-family:"Courier New"'> + ...</span></p> + +<p class=MsoNormal style='margin-top:0in;margin-right:0in;margin-bottom:12.0pt; +margin-left:35.45pt'><span style='font-family:"Courier New"'> +tWater.setIterations(parameters.retries);<br> + tWaterOff.setInterval(parameters.watertime * SECOND);<br> +<br> +<br> + taskManager.init();<br> + taskManager.addTask(tMeasure);<br> + taskManager.addTask(tDisplay);<br> + taskManager.addTask(tWater);<br> + taskManager.addTask(tWaterOff);<br> + <br> + tMeasure.enable();<br> + tDisplay.enable();<br> +<br> + currentHumidity = measureHumidity();<br> +}<br> +<br> +<br> +void loop ()<br> +{<br> + taskManager.execute();<br> +}</span></p> + +<p class=MsoNormal style='margin-top:0in;margin-right:0in;margin-bottom:12.0pt; +margin-left:35.45pt'> </p> + +<ol style='margin-top:0in' start=2 type=1> + <li class=MsoNormal><b><span style='font-family:"Times New Roman","serif"'>“NATIVE” + SUPPORT FOR FINITE STATE MACHINE</span></b></li> +</ol> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'> </span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'>Define +“states” as callback function or functions. Each callback function executes +activities specific to a “state” and then “transitions” to the next state by +assigning next callback function to the task. </span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'>Transition +from one state to the next is achieved by setting next callback function at the +end of preceding one. </span></p> + +<p class=MsoNormal><b><span style='font-family:"Times New Roman","serif"'>Note: +</span></b><span style='font-family:"Times New Roman","serif"'>do not call the +next callback function. Let the schedule take care of that during the next +pass. (Thus letting other tasks run). </span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'> </span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'>Example: +Blinking LED 2 times a second could be achieved this way</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Times New Roman","serif"'> </span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'>Task +tLedBlinker (500, -1, &ledOnCallback);</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'>Scheduler +taskManager;</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'> </span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'>void +ledOnCallback() {</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'> +turnLedOn();</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'> +tLedBlinker.setCallback(&ledOffCallback);</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'>}</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'> </span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'>void +ledOffCallback() {</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'> +turnLedOff();</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'> +tLedBlinker.setCallback(&ledOnCallback);</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'>}</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'> </span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'>setup() +{</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'> +taskManager.init();<br> + taskManager.addTask(tLedBlinker);</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'> +</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'> +tLedBlinker.enable();</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'>}</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'> </span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'>loop +() {</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Courier New"'> +taskManager.execute();</span></p> + +<p class=MsoNormal style='margin-top:0in;margin-right:0in;margin-bottom:12.0pt; +margin-left:35.45pt'><span style='font-family:"Courier New"'>}</span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'>Obviously +the example is simple, but gives the idea of how the tasks could be used to go +through states.</span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'> </span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'> </span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'> </span></p> + +<ol style='margin-top:0in' start=3 type=1> + <li class=MsoNormal><b><span style='font-family:"Times New Roman","serif"'>MULTIPLE + POSSIBLE CALLBACKS FOR TASK</span></b></li> +</ol> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'> </span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'>There may +be a need to select an option for callback function based on certain criteria, +or randomly. </span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'>You can +achieve that by defining an array of callback function pointers and selecting +one based on the criteria you need. </span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'>Example: +when a robot detects an obstacle, it may go left, right backwards, etc. Each of +the “directions” or “behaviors” are represented by a different callback +function. </span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'> </span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'>Another +example of using multiple callbacks:</span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'>You may +need to “initialize” variables for a particular task.</span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'>In this +case, define a tasks with two callbacks:</span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'> </span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'>Task <b>tWork</b> (T_INTERVAL, -1, +&workCallbackInit);</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'>…</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Times New Roman","serif"'> </span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'>void workCallbackInit() {</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'> // do your +initializationstuff here</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'> </span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'> // finally assigne +the main callback function </span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'> +tWork.setCallback(&workCallback);</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'>}</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-family:"Times New Roman","serif"'> </span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'>void workCallback() {</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'> // main callback +function</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'> …</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'>}</span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'> </span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'>The task +will initialize during first execution pass and switch to “regular” callback +execution starting with second pass. There is a delay between first and second +passes of the task (scheduling period, if defined). In order to execute the +second pass immediately after initialization first pass, change the above code +like this:</span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'> </span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'>void workCallbackInit() {</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'> // do your +initializationstuff here</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'> </span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'> // finally assigne the +main callback function </span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'> +tWork.setCallback(&workCallback);</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'> tWork.enable();</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'>}</span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'> </span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'>The task +will run initialization first, then immediately second pass, and then switch to +processing at regular intervals starting with a third pass. </span></p> + +<p class=MsoNormal> </p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'> </span></p> + +<ol style='margin-top:0in' start=4 type=1> + <li class=MsoNormal><b><span style='font-family:"Times New Roman","serif"'>INTERRUP-DRIVEN + EXECUTION SUPPORT </span></b></li> +</ol> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'> </span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'>In case +of interrupt-driven program flow, tasks could be scheduled to run once to +request asynchronous execution (request), and then re-enabled (restarted) again +with a different callback function to process the results. </span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'> </span></p> + +<p class=MsoNormal><b><span style='font-family:"Times New Roman","serif"'>Example</span></b><span +style='font-family:"Times New Roman","serif"'>: event driven distance +calculation for ultrasonic pulses. EchoPin #6 triggers pin change interrupts on +rising and falling edges to determine the length of ultrasonic pulse.</span></p> + +<p class=MsoNormal><span style='font-family:"Times New Roman","serif"'> </span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'>#include <DirectIO.h><br> +#include <TaskScheduler.h><br> +#include <PinChangeInt.h><br> +<br> +<br> +#define TRIGGERPIN 5<br> +#define ECHOPIN 6<br> +<br> +Output<TRIGGERPIN> pTrigger;<br> +Input<ECHOPIN> pEcho;<br> +<br> +Scheduler r;<br> +<br> +Task tMeasure(1000, -1, &measureCallback);<br> +Task tDisplay(1000, -1, &displayCallback);<br> +Task tPing(0, 1, &pingCalcCallback);<br> +<br> +<br> +volatile bool pulseBusy = false;<br> +volatile bool pulseTimeout = false;<br> +volatile unsigned long pulseStart = 0;<br> +volatile unsigned long pulseStop = 0;<br> +volatile unsigned long pingDistance = 0;<br> +<br> +<br> +void pingTrigger(unsigned long aTimeout) {<br> + if (pulseBusy) return; // do not trigger if in the middle of a +pulse<br> + if (pEcho == HIGH) return; // do not trigger if ECHO pin is high<br> + <br> + pulseBusy = true;<br> + pulseTimeout = false;</span></p> + +<p class=MsoNormal style='margin-top:0in;margin-right:0in;margin-bottom:12.0pt; +margin-left:35.45pt'><span style='font-size:10.0pt;font-family:"Courier New"'><br> + pTrigger = LOW;<br> + delayMicroseconds(4);<br> + pTrigger = HIGH;<br> +<br> + tPing.setInterval (aTimeout);<br> +<br> + delayMicroseconds(10);<br> + pTrigger = LOW; </span></p> + +<p class=MsoNormal style='margin-top:0in;margin-right:0in;margin-bottom:12.0pt; +margin-left:35.45pt'><span style='font-size:10.0pt;font-family:"Courier New"'> +tPing.restartDelayed(); // timeout countdown starts now</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'>// will start the pulse clock on the rising edge of +ECHO pin</span></p> + +<p class=MsoNormal style='margin-top:0in;margin-right:0in;margin-bottom:12.0pt; +margin-left:35.45pt'><span style='font-size:10.0pt;font-family:"Courier New"'> +PCintPort::attachInterrupt(ECHOPIN, &pingStartClock, RISING); <br> +}<br> +<br> +</span></p> + +<p class=MsoNormal style='margin-left:35.45pt'><span style='font-size:10.0pt; +font-family:"Courier New"'>// Start clock on the <b>rising</b> edge of the +ultrasonic pulse<br> +void pingStartClock() {<br> + pulseStart = micros();<br> + PCintPort::detachInterrupt(ECHOPIN); // not sure this is necessary<br> + PCintPort::attachInterrupt(ECHOPIN, &pingStopClock, FALLING); <br> + tPing.restartDelayed();<br> +}<br> +<br> +// Stop clock on the <b>falling</b> edge of the ultrasonic pulse<br> +void pingStopClock() {<br> + pulseStop = micros();<br> + PcintPort::detachInterrupt(ECHOPIN);</span></p> + +<p class=MsoNormal style='margin-top:0in;margin-right:0in;margin-bottom:12.0pt; +margin-left:35.45pt'><span style='font-size:10.0pt;font-family:"Courier New"'> +pingDistance = pulseStop - pulseStart;<br> + pulseBusy = false;<br> + tPing.disable(); // disable timeout<br> +}</span></p> + +<p class=MsoNormal style='margin-top:0in;margin-right:0in;margin-bottom:12.0pt; +margin-left:35.45pt'><span style='font-size:10.0pt;font-family:"Courier New"'>// +Stop clock because of the timeout – the wave did not return<br> +void pingCalcCallback() {<br> + if (pulseBusy) {<br> + pingStopClock();<br> + }<br> + pulseTimeout = true;<br> +}<br> +<br> +<br> +<br> +// Initial measure callback sets the trigger<br> +void measureCallback() {<br> + if (pulseBusy) { // already measuring, try again<br> + tMeasure.enable();<br> + return;<br> + }<br> + pingTrigger(30); // 30 milliseconds or max range of ~5.1 meters<br> + tMeasure.setCallback(&measureCallbackWait);<br> +}</span></p> + +<p class=MsoNormal style='margin-top:0in;margin-right:0in;margin-bottom:12.0pt; +margin-left:35.45pt'><span style='font-size:10.0pt;font-family:"Courier New"'>// +Wait for the measurement to <br> +void measureCallbackWait() {<br> + if (pulseBusy) return;<br> + tMeasure.setCallback(&measureCallback); <br> +}<br> +<br> +<br> +bool state = true;<br> +<br> +void displayCallback() {<br> + char d[256];<br> + <br> + unsigned long cm = pingDistance * 17 / 100; // cm<br> + <br> + snprintf(d, 256, "pulseStart = %8lu\tpulseStop=%8lu\tdistance, +cm=%8lu", pulseStart, pulseStop, cm);<br> + Serial.println(d);<br> + <br> +}<br> +<br> +void setup() {<br> + // put your setup code here, to run once:<br> + <br> + Serial.begin(115200);<br> + <br> +<br> + pTrigger = LOW;<br> + pEcho = LOW;<br> + <br> + r.init();<br> + r.addTask(tDisplay);<br> + r.addTask(tMeasure);<br> + r.addTask(tPing);<br> + <br> + r.enableAll();<br> + tPing.disable();<br> +}<br> +<br> +void loop() {<br> + // put your main code here, to run repeatedly:<br> + r.execute();<br> +}</span></p> + +</div> + +</body> + +</html> diff --git a/examples/Scheduler_example/Scheduler_example.ino b/examples/Scheduler_example/Scheduler_example.ino new file mode 100644 index 0000000..dece85a --- /dev/null +++ b/examples/Scheduler_example/Scheduler_example.ino @@ -0,0 +1,82 @@ +#include <TaskScheduler.h> + +Task t4(); +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; + + +void t1Callback() { + Serial.print("t1: "); + Serial.println(millis()); + + if (t1.isFirstIteration()) { + t3.enable(); + runner.addTask(t3); + Serial.println("t1: enabled t3 and added to the chain"); + } + + if (t1.isLastIteration()) { + t3.disable(); + runner.deleteTask(t3); + t2.setInterval(500); + Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500"); + } +} + +void t2Callback() { + Serial.print("t2: "); + Serial.println(millis()); + +} + +void t3Callback() { + Serial.print("t3: "); + Serial.println(millis()); + +} + +void setup () { + Serial.begin(115200); + Serial.println("Scheduler TEST"); + + + t1.enable(); + Serial.println("Enabled t1"); + t2.enable(); + Serial.println("Enabled t2"); + + runner.init(); + Serial.println("Initialized scheduler"); + + runner.addTask(t1); + Serial.println("added t1"); + + runner.addTask(t2); + Serial.println("added t2"); + + delay(5000); +} + + +void loop () { + + runner.execute(); + + Serial.println("Loop ticks at: "); + Serial.println(millis()); +} diff --git a/examples/Scheduler_example2/Scheduler_example2.ino b/examples/Scheduler_example2/Scheduler_example2.ino new file mode 100644 index 0000000..0e7f9a9 --- /dev/null +++ b/examples/Scheduler_example2/Scheduler_example2.ino @@ -0,0 +1,70 @@ +#define _TASK_SLEEP_ON_IDLE_RUN +#include <TaskScheduler.h> + +Scheduler runner; + +Task t4(); +Task t1(2000, 10, &t1Callback, &runner, true); //adding task to the chain on creation +Task t2(3000, -1, &t2Callback, &runner, true); //adding task to the chain on creation +Task t3(5000, -1, &t3Callback); + +// 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 + + + + + +void t1Callback() { + Serial.print("t1: "); + Serial.println(millis()); + + if (t1.isFirstIteration()) { + t3.enable(); + runner.addTask(t3); + Serial.println("t1: enabled t3 and added to the chain"); + } + + if (t1.isLastIteration()) { + t3.disable(); + runner.deleteTask(t3); + t2.setInterval(500); + Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500"); + } +} + +void t2Callback() { + Serial.print("t2: "); + Serial.println(millis()); + +} + +void t3Callback() { + Serial.print("t3: "); + Serial.println(millis()); + +} + +void setup () { + Serial.begin(115200); + Serial.println("Scheduler TEST"); + + delay(5000); +} + + +void loop () { + + runner.execute(); + + Serial.println("Loop ticks at: "); + Serial.println(millis()); +} diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..68c2b9f --- /dev/null +++ b/keywords.txt @@ -0,0 +1,44 @@ +####################################### +# Syntax Coloring Map For TaskManager +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +Task KEYWORD1 +Scheduler KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +init KEYWORD2 +addTask KEYWORD2 +deleteTask KEYWORD2 +disableAll KEYWORD2 +enableAll KEYWORD2 +execute KEYWORD2 +allowSleep KEYWORD2 +enable KEYWORD2 +enableDelayed KEYWORD2 +delay KEYWORD2 +restart KEYWORD2 +restartDelayed KEYWORD2 +disable KEYWORD2 +isEnabled KEYWORD2 +set KEYWORD2 +setInterval KEYWORD2 +getInterval KEYWORD2 +setIterations KEYWORD2 +getIterations KEYWORD2 +setCallback KEYWORD2 +disableOnLastIteration KEYWORD2 +getOverrun KEYWORD2 +isFirstIteration KEYWORD2 +isLastIteration KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + |
