├── keywords.txt ├── library.properties ├── library.json ├── examples ├── SetInterval │ └── SetInterval.ino ├── SetTimeout │ └── SetTimeout.ino ├── DallasTemperature │ └── DallasTemperature.ino ├── ScheduledIn │ └── ScheduledIn.ino ├── ChangeScheduled │ └── ChangeScheduled.ino └── MultiBlink │ └── MultiBlink.ino ├── Tasker.h ├── LICENSE └── README.md /keywords.txt: -------------------------------------------------------------------------------- 1 | Tasker KEYWORD1 2 | 3 | setTimeout KEYWORD2 4 | setInterval KEYWORD2 5 | setRepeated KEYWORD2 6 | cancel KEYWORD2 7 | clearTimeout KEYWORD2 8 | clearInterval KEYWORD2 9 | scheduledIn KEYWORD2 10 | loop KEYWORD2 11 | isPrioritized KEYWORD2 12 | setPrioritized KEYWORD2 13 | 14 | TASKER_MAX_TASKS LITERAL1 15 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Tasker 2 | version=2.0.3 3 | author=Petr Stehlík 4 | maintainer=Petr Stehlík 5 | sentence=Get rid of delay() calls, schedule tasks/functions instead. 6 | paragraph=Allows to schedule tasks (your functions) to be called once, repeatedly or indefinitely in a given time frame. Features simple and familiar Javascript-like syntax. Does not use any hardware interrupts, relies on cooperative multitasking. Simple, clean and highly portable. 7 | category=Timing 8 | url=https://github.com/joysfera/arduino-tasker 9 | architectures=* 10 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Tasker", 3 | "keywords": "task", 4 | "description": "Allows to schedule tasks (your functions) to be called once, repeatedly or indefinitely in a given time frame. Features simple and familiar Javascript-like syntax. Does not use any hardware interrupts, relies on cooperative multitasking. Clean, simple and highly portable.", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/joysfera/arduino-tasker" 9 | }, 10 | "version": "2.0.3", 11 | "frameworks": "arduino", 12 | "platforms": "*" 13 | } 14 | -------------------------------------------------------------------------------- /examples/SetInterval/SetInterval.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Example of usage of the Tasker for Arduino 3 | * Written by Petr Stehlik petr@pstehlik.cz 4 | * Released into the public domain 5 | */ 6 | 7 | #include "Tasker.h" 8 | Tasker tasker; 9 | 10 | void task1() 11 | { 12 | Serial.print(millis()); 13 | Serial.println(F(": task1 called")); 14 | } 15 | 16 | void setup() 17 | { 18 | Serial.begin(115200); 19 | Serial.println("Tasker: Demo of setInterval()"); 20 | 21 | // call my function 'task1' every 3 seconds 22 | tasker.setInterval(task1, 3000); 23 | } 24 | 25 | void loop() 26 | { 27 | tasker.loop(); 28 | // your code can be here 29 | } 30 | 31 | -------------------------------------------------------------------------------- /examples/SetTimeout/SetTimeout.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Example of usage of the Tasker for Arduino 3 | * Written by Petr Stehlik petr@pstehlik.cz 4 | * Released into the public domain 5 | */ 6 | 7 | #include "Tasker.h" 8 | Tasker tasker; 9 | 10 | void task1() 11 | { 12 | Serial.print(millis()); 13 | Serial.println(F(": task1 called")); 14 | 15 | unsigned r = random(1, 5); 16 | Serial.print(F("will call it again in ")); Serial.print(r); Serial.println(F(" seconds")); 17 | tasker.setTimeout(task1, r * 1000); 18 | } 19 | 20 | void setup() 21 | { 22 | Serial.begin(115200); 23 | Serial.println(F("Tasker: Demo of chaining setTimeout() calls")); 24 | 25 | Serial.println(F("will call task1 in exactly 5 seconds")); 26 | tasker.setTimeout(task1, 5000); 27 | } 28 | 29 | void loop() 30 | { 31 | tasker.loop(); 32 | // your code can be here 33 | } 34 | 35 | -------------------------------------------------------------------------------- /examples/DallasTemperature/DallasTemperature.ino: -------------------------------------------------------------------------------- 1 | // 2 | // Example of non-blocking reading of DS18B20 temperature sensor 3 | // 4 | 5 | #include "Tasker.h" 6 | #include "OneWire.h" 7 | #include "DallasTemperature.h" 8 | 9 | Tasker tasker; 10 | OneWire oneWire(4); 11 | DallasTemperature sensor(&oneWire); 12 | 13 | void readSensor() { 14 | // read the actual temperature after it's been converted 15 | float temperature = sensor.getTempC(0); 16 | // do what you need with the temperature here 17 | } 18 | 19 | void startConversion() { 20 | // start temperature conversion (does not block) 21 | sensor.requestTemperatures(); 22 | // schedule reading the actual temperature in 750 milliseconds 23 | tasker.setTimeout(readSensor, 750); 24 | } 25 | 26 | void setup() { 27 | sensor.begin(); 28 | // do not block during temperature conversion 29 | sensor.setWaitForConversion(false); 30 | // read temperature every 5 seconds 31 | tasker.setInterval(startConversion, 5000); 32 | } 33 | 34 | void loop() { 35 | tasker.loop(); 36 | } 37 | -------------------------------------------------------------------------------- /examples/ScheduledIn/ScheduledIn.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Example of usage of the Tasker for Arduino 3 | * Written by Petr Stehlik petr@pstehlik.cz 4 | * Released into the public domain 5 | */ 6 | 7 | #include "Tasker.h" 8 | Tasker tasker; 9 | 10 | void task1() 11 | { 12 | static byte counter = 0; 13 | Serial.print(millis()); 14 | Serial.print(F(": task1 called: ")); 15 | counter++; 16 | Serial.println(counter); 17 | } 18 | 19 | void task2() 20 | { 21 | unsigned long ms = tasker.scheduledIn(task1); 22 | if (ms > 0) { 23 | Serial.print(F("task1 will be called in ")); Serial.print(ms); Serial.println(F(" milliseconds")); 24 | } 25 | else { 26 | Serial.println("task1 will not be called anymore"); 27 | 28 | // stop task2 as it is no longer needed 29 | tasker.cancel(task2); 30 | } 31 | } 32 | 33 | void setup() 34 | { 35 | Serial.begin(115200); 36 | Serial.println("Tasker: Demo of setInterval(), setRepeated(), scheduledIn() and cancel()"); 37 | 38 | // schedule task1 to be called every 4 seconds but five times only 39 | tasker.setRepeated(task1, 4000, 5); 40 | 41 | // schedule task2 to be called every 500 milliseconds 42 | tasker.setInterval(task2, 500); 43 | } 44 | 45 | void loop() 46 | { 47 | tasker.loop(); 48 | // your code can be here 49 | } 50 | 51 | -------------------------------------------------------------------------------- /examples/ChangeScheduled/ChangeScheduled.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Example of usage of the Tasker for Arduino 3 | * Written by Petr Stehlik petr@pstehlik.cz 4 | * Released into the public domain 5 | */ 6 | 7 | #include "Tasker.h" 8 | Tasker tasker; 9 | 10 | void task1() 11 | { 12 | static unsigned long last = 0; 13 | Serial.print(millis()); 14 | Serial.print(F(": task1 called after ")); 15 | Serial.println(millis() - last); 16 | last = millis(); 17 | } 18 | 19 | void task2() 20 | { 21 | Serial.print(millis()); 22 | Serial.print(F(": reset and change the interval of task1 to ")); 23 | unsigned r = random(1000, 3000); 24 | Serial.println(r); 25 | tasker.setInterval(task1, r); 26 | } 27 | 28 | void task3() 29 | { 30 | Serial.print(millis()); 31 | Serial.println(": STOP"); 32 | tasker.cancel(task1); 33 | tasker.cancel(task2); 34 | } 35 | 36 | void setup() 37 | { 38 | Serial.begin(115200); 39 | Serial.println("Tasker: Demo of changing scheduled task setup at runtime"); 40 | 41 | // set task1 to be called every 2 seconds 42 | tasker.setInterval(task1, 2000); 43 | 44 | // every 8 seconds call task2 that changes the settings of task1 45 | tasker.setInterval(task2, 8000); 46 | 47 | // after 60 seconds stop both tasks 48 | tasker.setTimeout(task3, 60000); 49 | } 50 | 51 | void loop() 52 | { 53 | tasker.loop(); 54 | // your code can be here 55 | } 56 | 57 | -------------------------------------------------------------------------------- /examples/MultiBlink/MultiBlink.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Example of usage of the Tasker for Arduino 3 | * Written by Petr Stehlik petr@pstehlik.cz 4 | * Released into the public domain 5 | */ 6 | 7 | // define max number of tasks to save precious Arduino RAM 8 | #define TASKER_MAX_TASKS 4 9 | #include "Tasker.h" 10 | 11 | Tasker tasker; 12 | 13 | /* 14 | * Example of chaining tasks by calling the setTimeout() from a task 15 | */ 16 | void blink1() 17 | { 18 | byte pin = 13; 19 | bool led = !digitalRead(pin); 20 | digitalWrite(pin, led); 21 | tasker.setTimeout(blink1, led ? 300 : 700); 22 | } 23 | 24 | /* 25 | * Example of one function called by two tasks - the 'pin' 26 | * input value is defined when creating the tasks 27 | */ 28 | void blink2(int pin) 29 | { 30 | digitalWrite(pin, !digitalRead(pin)); 31 | } 32 | 33 | void setup() 34 | { 35 | pinMode(13, OUTPUT); // default Arduino LED 36 | pinMode(12, OUTPUT); // attach additional LED 37 | pinMode(11, OUTPUT); // attach additional LED 38 | 39 | // called once after 5000 milliseconds, no optional parameter used 40 | tasker.setTimeout(blink1, 5000); 41 | // called every 750 milliseconds forever, with optional parameter pin = 12 42 | tasker.setInterval(blink2, 750, 12); 43 | // called every 1000 milliseconds 10 times, with optional parameter pin = 11 44 | tasker.setRepeated(blink2, 1000, 10, 11); 45 | } 46 | 47 | void loop() 48 | { 49 | tasker.loop(); 50 | } 51 | 52 | -------------------------------------------------------------------------------- /Tasker.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Tasker for Arduino - cooperative task scheduler with Javascript like API 3 | * Copyleft (c) 2015-2018 Petr Stehlik petr@pstehlik.cz 4 | * Distributed under the GNU LGPL http://www.gnu.org/licenses/lgpl.txt 5 | */ 6 | 7 | #ifndef _tasker_h 8 | #define _tasker_h 9 | 10 | #ifndef TASKER_MAX_TASKS 11 | #define TASKER_MAX_TASKS 10 // max 254 entries, one occupies 14 bytes of RAM 12 | #endif 13 | 14 | #include "Arduino.h" 15 | 16 | typedef void (*TaskCallback0)(void); 17 | typedef void (*TaskCallback1)(int); 18 | 19 | class Tasker 20 | { 21 | public: 22 | Tasker(bool prioritized = false); 23 | 24 | bool setTimeout(TaskCallback0 func, unsigned long interval, byte prio = TASKER_MAX_TASKS); 25 | bool setTimeout(TaskCallback1 func, unsigned long interval, int param, byte prio = TASKER_MAX_TASKS); 26 | 27 | bool setInterval(TaskCallback0 func, unsigned long interval, byte prio = TASKER_MAX_TASKS); 28 | bool setInterval(TaskCallback1 func, unsigned long interval, int param, byte prio = TASKER_MAX_TASKS); 29 | 30 | bool setRepeated(TaskCallback0 func, unsigned long interval, unsigned int repeat, byte prio = TASKER_MAX_TASKS); 31 | bool setRepeated(TaskCallback1 func, unsigned long interval, unsigned int repeat, int param, byte prio = TASKER_MAX_TASKS); 32 | 33 | bool cancel(TaskCallback0 func); 34 | bool cancel(TaskCallback1 func, int param); 35 | 36 | bool clearTimeout(TaskCallback0 func) { return cancel(func); }; 37 | bool clearTimeout(TaskCallback1 func, int param) { return cancel(func, param); }; 38 | 39 | bool clearInterval(TaskCallback0 func) { return cancel(func); }; 40 | bool clearInterval(TaskCallback1 func, int param) { return cancel(func, param); }; 41 | 42 | unsigned long scheduledIn(TaskCallback0 func); 43 | unsigned long scheduledIn(TaskCallback1 func, int param); 44 | 45 | void loop(void); 46 | 47 | bool isPrioritized() { return t_prioritized; } 48 | void setPrioritized(bool prioritized) { t_prioritized = prioritized; } 49 | 50 | private: 51 | struct TASK { 52 | TaskCallback1 call; 53 | int param; 54 | unsigned long interval; 55 | unsigned long lastRun; 56 | unsigned int repeat; 57 | }; 58 | 59 | int findTask(TaskCallback0 func); 60 | int findTask(TaskCallback1 func, int param); 61 | bool addTask(TaskCallback1 func, unsigned long interval, unsigned int repeat, int param, byte prio); 62 | bool removeTask(int t_idx); 63 | 64 | TASK tasks[TASKER_MAX_TASKS]; 65 | byte t_count; 66 | bool t_prioritized; 67 | static const int NO_PARAMETER = -1; 68 | }; 69 | 70 | 71 | Tasker::Tasker(bool prioritized) 72 | { 73 | t_count = 0; 74 | t_prioritized = prioritized; 75 | } 76 | 77 | bool Tasker::setTimeout(TaskCallback0 func, unsigned long interval, byte prio) 78 | { 79 | return setRepeated(func, interval, 1, prio); 80 | } 81 | 82 | bool Tasker::setTimeout(TaskCallback1 func, unsigned long interval, int param, byte prio) 83 | { 84 | return setRepeated(func, interval, 1, param, prio); 85 | } 86 | 87 | bool Tasker::setInterval(TaskCallback1 func, unsigned long interval, int param, byte prio) 88 | { 89 | return setRepeated(func, interval, 0, param, prio); 90 | } 91 | 92 | bool Tasker::setInterval(TaskCallback0 func, unsigned long interval, byte prio) 93 | { 94 | return setRepeated(func, interval, 0, prio); 95 | } 96 | 97 | bool Tasker::setRepeated(TaskCallback0 func, unsigned long interval, unsigned int repeat, byte prio) 98 | { 99 | return addTask((TaskCallback1)func, interval, repeat, NO_PARAMETER, prio); 100 | } 101 | 102 | bool Tasker::setRepeated(TaskCallback1 func, unsigned long interval, unsigned int repeat, int param, byte prio) 103 | { 104 | if (param < 0) param = 0; // param can be nonnegative only 105 | return addTask(func, interval, repeat, param, prio); 106 | } 107 | 108 | bool Tasker::cancel(TaskCallback0 func) 109 | { 110 | return removeTask(findTask(func)); 111 | } 112 | 113 | bool Tasker::cancel(TaskCallback1 func, int param) 114 | { 115 | return removeTask(findTask(func, param)); 116 | } 117 | 118 | unsigned long Tasker::scheduledIn(TaskCallback0 func) 119 | { 120 | return scheduledIn((TaskCallback1)func, NO_PARAMETER); 121 | } 122 | 123 | // how long before the given task will be called 124 | // return 0 = task is not scheduled 125 | // return 1 = scheduled to run as soon as possible 126 | // return X = time period in milliseconds 127 | unsigned long Tasker::scheduledIn(TaskCallback1 func, int param) 128 | { 129 | int t_idx = findTask(func, param); 130 | if (t_idx >= 0) { 131 | TASK &t = tasks[t_idx]; 132 | unsigned long now = millis(); 133 | if (now - t.lastRun >= t.interval) 134 | return 1; // scheduled to run as soon as possible 135 | else 136 | return t.lastRun + t.interval - now; 137 | } 138 | return 0; 139 | } 140 | 141 | void Tasker::loop(void) 142 | { 143 | byte t_idx = 0; 144 | unsigned long now = millis(); 145 | while(t_idx < t_count) { 146 | bool inc = true; 147 | TASK &t = tasks[t_idx]; 148 | if (now - t.lastRun >= t.interval) { 149 | int param = t.param; 150 | TaskCallback1 call = t.call; 151 | 152 | t.lastRun += t.interval; 153 | if (t.repeat > 0 && --t.repeat == 0) { 154 | // drop the finished task by removing its slot 155 | removeTask(t_idx); 156 | inc = false; 157 | } 158 | 159 | if (param >= 0) // param can be nonnegative only 160 | (*(call))(param); 161 | else 162 | (*(TaskCallback0)(call))(); 163 | 164 | if (t_prioritized) 165 | break; 166 | now = millis(); 167 | } 168 | if (inc) 169 | t_idx++; 170 | } 171 | } 172 | 173 | int Tasker::findTask(TaskCallback0 func) 174 | { 175 | return findTask((TaskCallback1)func, NO_PARAMETER); 176 | } 177 | 178 | int Tasker::findTask(TaskCallback1 func, int param) 179 | { 180 | for(byte t_idx = 0; t_idx < t_count; t_idx++) { 181 | TASK &t = tasks[t_idx]; 182 | if (t.call == func && t.param == param) 183 | return t_idx; 184 | } 185 | return -1; 186 | } 187 | 188 | bool Tasker::addTask(TaskCallback1 func, unsigned long interval, unsigned int repeat, int param, byte prio) 189 | { 190 | byte pos = (prio < t_count) ? prio : t_count; // position of newly added task is based on priority 191 | 192 | int idx = findTask(func, param); 193 | if (idx >= 0) { 194 | removeTask(idx); // if there's a matching task then remove it first 195 | pos = idx; // new task will replace the original one 196 | } 197 | 198 | if (t_count >= TASKER_MAX_TASKS || interval == 0) 199 | return false; 200 | 201 | if (pos < t_count) 202 | memmove(tasks+pos+1, tasks+pos, sizeof(TASK)*(t_count-pos)); 203 | TASK &t = tasks[pos]; 204 | t.call = func; 205 | t.interval = interval; 206 | t.param = param; 207 | t.lastRun = millis(); 208 | t.repeat = repeat; 209 | t_count++; 210 | return true; 211 | } 212 | 213 | bool Tasker::removeTask(int t_idx) 214 | { 215 | if (t_idx >= 0 && t_idx < t_count) { 216 | memmove(tasks+t_idx, tasks+t_idx+1, sizeof(TASK)*(t_count-t_idx-1)); 217 | t_count--; 218 | return true; 219 | } 220 | return false; 221 | } 222 | 223 | #endif // _tasker_h 224 | // vim: tabstop=4 shiftwidth=4 noexpandtab cindent 225 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | 167 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Tasker for Arduino 2 | ================== 3 | 4 | This is a cooperative scheduler for running multiple tasks on Arduino. The tasks are 5 | called automatically at specified times for specified number of times. This frees your 6 | program from timing logic and makes your Arduino look like it's doing several things at once. 7 | 8 | *Cooperative* means that the tasks you create need to behave nicely - 9 | to co-operate together by running for a short time only, otherwise it will not work well. 10 | *Scheduler* means that each task has its own schedule so Tasker knows when the tasks are to be 11 | started (in what time since now) and how many times they should be invoked 12 | (only once, X times or forever). 13 | 14 | The "co-operation" is best achieved by creating small, short running tasks (functions). 15 | Basically wherever you'd need to include the infamous `delay()` call in your Arduino program 16 | that's the place where you actually want to break the code flow, split 17 | the source code into separate functions and let Tasker run them as separate tasks. 18 | 19 | There are many similar libraries for the same purpose available on the Internet 20 | but they are either buggy (don't handle timer overflow) or too complicated, cumbersome to use, 21 | unnecessary object-oriented or otherwise hard to understand and follow. 22 | 23 | This Tasker library is carefully designed to be extremely simple 24 | yet very powerful. It has short, clean API and clear implementation that fits 25 | on a single page and can be reviewed easily. 26 | Best of all, its API is intentionally similar to JavaScript's timer 27 | functions so HTML/JavaScript programmers will feel right at home. 28 | For the unfamiliar with JavaScript there's a short example code included that 29 | illustrates the whole API and its best usage. 30 | 31 | ChangeLog 32 | --------- 33 | * version 2.0 brings two new great features: now you can **modify scheduled tasks** and also **cancel them**. 34 | - to modify task setup (change their timing or priority) simply call the `setTimeout()`/`setInterval()`/`setRepeated()` functions again. 35 | - to stop/cancel a scheduled task and remove from Tasker's queue call the new function `cancel()`. 36 | - if familiar with Javascript you can call `clearTimeout()` and `clearInterval()` (identical with `cancel()`). 37 | - to find out when a given task will be called use the new `scheduledIn()` function. 38 | - another important change is making the optional `int` parameter passed into your functions truly optional, so if you don't want to use it you don't need to declare your function with it. I.e. the `void myFunction(int /*unused*/)` is a history now - use simple and clean `void myFunction()`. 39 | - Please read the *Upgrading from v1.2 to v2.0* paragraph below for further details. 40 | 41 | * version 1.4 changes the default priority value when **Tasker** is instantiated without the optional parameter. In previous versions the priority was enabled by default, now it is disabled. Users of **Tasker** found the prioritized handling of tasks rather counter-intuitive because it could happen almost randomly that some tasks were sometimes not executed at all (when a higher priority task ran for too long). Whoever wants to keep the original behaviour please instantiate **Tasker** like this: `Tasker tasker(TRUE);`. There are also two new functions that help to query or set the priority value: `isPrioritized()` and `setPrioritized(bool)`. 42 | 43 | * version 1.3 removes the `run()` function - please call `tasker.loop()` in your Arduino `loop()` function instead. This makes **Tasker** much more Arduino friendly and compatible with far more platforms where the Arduino 'kernel' does some housekeeping behind the scenes and needs the `loop()` to be running for it. It also allowed me to remove the `yield()` call that didn't really bring anything but issues on some platforms. 44 | 45 | * version 1.2 adds optional priorities when defining tasks 46 | 47 | * version 1.1 adds clear example of DS18B20 handling 48 | 49 | 50 | Upgrading from v1.2 to v2.0 51 | --------------------------- 52 | Versions 1.3-2.0 released in May 2018 introduced some small API changes that were not backward compatible so you may need to update your source code (see below for details). Changing library API is always better avoided but the collected user feedback in last year led me to simplify the API and made it more intuitive to use, which is so good thing that it was worth changing the API a bit. These are the things you might need to update in your application when using Tasker: 53 | 54 | ### default value of Tasker constructor has changed 55 | If you rely on the prioritized task execution (most users don't!) then enable it explicitly by adding (TRUE) as the ctor parameter because it's no longer enabled by default: 56 | 57 | | old code | new code | 58 | |----------------------|-----------------------| 59 | | `Tasker tasker;` | `Tasker tasker(TRUE);`| 60 | 61 | Let me repeat that the prioritized task execution may cause that some tasks with lower priority are sometimes not executed if the tasks with higher priority spent too much time. This might lead to some head scratching when you're missing some function calls randomly. So most users will be happier with the default constructor without any parameter: `Tasker tasker;` 62 | 63 | ### change run() to loop() 64 | If you were using the `tasker.run()` function please change it for calling `tasker.loop()` in your Arduino loop(): 65 | 66 | old code: 67 | ```cpp 68 | // originally Tasker suggested to call run() as the last thing in setup() 69 | void setup() { 70 | ... 71 | tasker.run(); 72 | } 73 | // Arduino loop() was then unused 74 | void loop() { } 75 | ``` 76 | 77 | new code: 78 | ```cpp 79 | // now Tasker needs to have tasker.loop() called in Arduino loop() 80 | void setup() { 81 | ... 82 | } 83 | void loop() { 84 | tasker.loop(); 85 | // you can add your code here, too 86 | } 87 | ``` 88 | 89 | ### remove unused parameter from task declaration 90 | If you don't use the additional parameter in your task/function then simply remove it: 91 | 92 | | old code | new code | 93 | |--------------------------------------|------------------------------| 94 | | `void myFunction(int /* unused */) {`| `void myFunction() {` | 95 | 96 | ### optional int parameter must be nonnegative 97 | Functions/tasks can be called with an optional `int` parameter. Since v2.0 its value (specified in ``setTimeout()`` etc) must be nonnegative, i.e. 0 or greater. 98 | 99 | How to use 100 | ---------- 101 | 102 | 1. install **Tasker** from **Arduino Library Manager** or create new *Tasker* folder under your Arduino projects' libraries folder and place Tasker files there so the header file ends in *./libraries/Tasker/Tasker.h* 103 | 2. in Arduino IDE load File -> Examples -> Tasker -> MultiBlink (or other examples found there) 104 | 3. see how easy it is to add three tasks and run them all at once (or how to read the DS18B20 without waiting) 105 | 4. use that example as a basis for your own code 106 | 107 | Tasker API 108 | ---------- 109 | 110 | * `Tasker([bool prioritized])`. The class constructor takes 111 | an optional bool flag (that is set to false if omitted). If this flag 112 | is TRUE then the Tasker prioritizes the scheduled tasks. If the flag 113 | is FALSE then the Tasker considers all scheduled tasks equal. More about priorities later. 114 | 115 | ```cpp 116 | Tasker tasker; // creates non-prioritizing tasker 117 | Tasker tasker(TRUE); // creates prioritizing tasker 118 | ``` 119 | 120 | * `setTimeout(function, time_in_milliseconds [, optional_int [, optional_priority]])` 121 | Tasker will call the *function* in *time_in_milliseconds* from now. 122 | It will run the function only once. May pass the *optional_int* parameter (nonnegative) into the called function. 123 | When the task finishes its Tasker slot is made available for new tasks (more about slots later). 124 | 125 | * `setInterval(function, time_in_milliseconds [, optional_int [, optional_priority]])` 126 | Tasker will call the *function* repeatedly and forever, every 127 | *time_in_milliseconds* from now on. 128 | May pass the *optional_int* parameter (nonnegative) into the called function. 129 | 130 | * `setRepeated(function, time, number_of_repeats [, optional_int [, optional_priority]])` 131 | Tasker will call the *function* repeatedly for *number_of_repeats*, 132 | every *time* (in_milliseconds) from now on. 133 | May pass the *optional_int* parameter (nonnegative) into the called function. 134 | When the task finishes (after its last iteration) its Tasker slot is made available for new tasks. 135 | 136 | * `cancel(function [, optional_int ])` 137 | If Tasker has the *function* in its scheduler queue (added there by either of those three functions above) 138 | it will cancel any further execution of the function and will remove it from its scheduler queue instantly. 139 | Its Tasker slot is made available for new tasks, of course. 140 | If you happened to add certain *function* to Tasker several times with different optional int parameters 141 | then you need to use the same optional int parameter when calling the `cancel()` so that Tasker 142 | knows which of the several task slots with the same *function* to remove. 143 | 144 | * `clearTimeout(function [, optional_int ])` is identical to `cancel()`, it just 145 | uses the well known Javascript API. 146 | 147 | * `clearInterval(function [, optional_int ])` is identical to `cancel()`, it just 148 | uses the well known Javascript API. 149 | 150 | * `scheduledIn(function [, optional_int ])` returns number of milliseconds till calling the given *function*. Returned 0 means that *function* (with *optional_int*) is not in Tasker's queue so it will never be called. 151 | 152 | * `loop()` when called it runs the Tasker scheduler and process all waiting tasks, then ends. 153 | It's best to let your program call this Tasker function as often as possible, ideally in the Arduino's `loop()` function: 154 | 155 | ```cpp 156 | void loop() { 157 | tasker.loop(); 158 | } 159 | ``` 160 | 161 | Task priorities (optional) 162 | -------------------------- 163 | If the Tasker constructor was called with a parameter (TRUE) then the internal 164 | scheduler will prioritize the tasks in its queue. Tasks added later have lower 165 | priority than those added earlier, unless you specify their priority with 166 | optional parameter: the lower its value the higher priority, 0 = highest priority. 167 | 168 | ```cpp 169 | Tasker tasker(TRUE); 170 | tasker.setInterval(most_important_fn, ..); // formerly added calls have automatically higher priority 171 | tasker.setInterval(less_important_fn, ..); // the later added calls the lower priority they have 172 | tasker.setInterval(highest_priority_fn, .., .., 0); // unless you specify the priority explicitly by the last parameter 173 | ``` 174 | 175 | Normally, when there is enough time for calling each of the scheduled task 176 | at the right time the priorities don't play any role but when a previous task takes 177 | longer time and the scheduler detects that certain tasks are delayed 178 | (are behind their schedule) it needs to decide which task will be run of those 179 | that should have been run already. And that's where the tasks' priorities step 180 | in: the task added earlier or with a higher priority will be chosen. 181 | If the priorities were disabled (by default the are) then the scheduler would simply run the next task 182 | in its queue. If all your tasks are equally important (they most probably are) you might simply ignore the whole idea of priorities and their implementation. 183 | 184 | ```cpp 185 | Tasker tasker; 186 | tasker.setInterval(fn, ..); 187 | tasker.setInterval(equally_important_fn, ..); 188 | tasker.setInterval(order_doesnt_matter_fn, ..); 189 | ``` 190 | 191 | Caveats 192 | ------- 193 | Make sure you define enough task "slots" in the Tasker, before you include its 194 | header file. By default there are 10 slots defined for up to ten tasks running 195 | simultaneously but you might want to increase this number - or even decrease, to 196 | save the precious RAM (each slot takes 14 bytes of RAM). 197 | 198 | ```cpp 199 | #define TASKER_MAX_TASKS 32 200 | #include "Tasker.h" 201 | ``` 202 | 203 | Good news is that Tasker automatically releases slots of finished tasks (those 204 | that were invoked by *setTimeout* or those that were run by *setRepeated* for their last time). 205 | That's why one can chain the tasks with *setTimeout* calls from within 206 | task function or even call one task using *setTimeout* recursively and the slots don't run out. 207 | 208 | I consider this library finished and stable for everyday use. Adding more features 209 | is not expected, the library will stay short, simple and fast. 210 | 211 | Author 212 | ------ 213 | Petr Stehlík 214 | 215 | E-mail: petr@pstehlik.cz 216 | 217 | Web: https://www.pstehlik.cz/ 218 | 219 | Daily active on Google+: https://plus.google.com/+PetrStehl%C3%ADk 220 | 221 | Longer articles are published at blog: http://joysfera.blogspot.com/ 222 | 223 | Sometimes tweets as https://twitter.com/joysfera 224 | --------------------------------------------------------------------------------