├── LICENSE.txt ├── README.md ├── examples ├── Scheduler_example00_Blink │ └── Scheduler_example00_Blink.ino ├── Scheduler_example00_Blink_Namespace │ └── Scheduler_example00_Blink_Namespace.ino ├── Scheduler_example01 │ └── Scheduler_example01.ino ├── Scheduler_example02 │ └── Scheduler_example02.ino ├── Scheduler_example03 │ └── Scheduler_example03.ino ├── Scheduler_example04_StatusRequest │ └── Scheduler_example04_StatusRequest.ino ├── Scheduler_example05_StatusRequest │ └── Scheduler_example05_StatusRequest.ino ├── Scheduler_example06_IDLE │ └── Scheduler_example06_IDLE.ino ├── Scheduler_example07_WDT │ └── Scheduler_example07_WDT.ino ├── Scheduler_example08_LTS │ └── Scheduler_example08_LTS.ino ├── Scheduler_example09_TimeCritical │ └── Scheduler_example09_TimeCritical.ino ├── Scheduler_example10_Benchmark │ └── Scheduler_example10_Benchmark.ino ├── Scheduler_example11_Priority │ └── Scheduler_example11_Priority.ino ├── Scheduler_example12_Priority │ └── Scheduler_example12_Priority.ino ├── Scheduler_example13_Micros │ └── Scheduler_example13_Micros.ino ├── Scheduler_example14_Yield │ └── Scheduler_example14_Yield.ino ├── Scheduler_example15_STDFunction │ └── Scheduler_example15_STDFunction.ino ├── Scheduler_example16_Multitab │ ├── Scheduler_example16_Multitab.ino │ ├── file1.cpp │ ├── file2.cpp │ ├── header.hpp │ └── ts.cpp ├── Scheduler_example17_Timeout │ └── Scheduler_example17_Timeout.ino ├── Scheduler_example18_StatusRequest_LTS_WDT_Timeout │ └── Scheduler_example18_StatusRequest_LTS_WDT_Timeout.ino ├── Scheduler_example19_Dynamic_Tasks │ └── Scheduler_example19_Dynamic_Tasks.ino ├── Scheduler_example19_Dynamic_Tasks_SelfDestruct │ └── Scheduler_example19_Dynamic_Tasks_SelfDestruct.ino ├── Scheduler_example19_Dynamic_Tasks_stdQueue │ └── Scheduler_example19_Dynamic_Tasks_stdQueue.ino ├── Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object │ ├── Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object.ino │ ├── SuperSensor.cpp │ └── SuperSensor.h ├── Scheduler_example21_OO_Callbacks │ ├── Calculator.cpp │ ├── Calculator.h │ ├── Scheduler_example21_OO_Callbacks.ino │ ├── SuperSensor.cpp │ ├── SuperSensor.h │ ├── Ticker.cpp │ └── Ticker.h ├── Scheduler_example23_IDLE_Callback │ └── Scheduler_example23_IDLE_Callback.ino ├── Scheduler_example24_CPU_LOAD │ └── Scheduler_example24_CPU_LOAD.ino ├── Scheduler_example25_SCHEDULER_CHAIN │ └── Scheduler_example25_SCHEDULER_CHAIN.ino ├── Scheduler_example26_SCHEDULING_OPTIONS │ └── Scheduler_example26_SCHEDULING_OPTIONS.ino ├── Scheduler_example27_PlatformIO │ ├── .gitignore │ ├── .vscode │ │ └── extensions.json │ ├── include │ │ ├── README │ │ ├── led.h │ │ └── main.h │ ├── lib │ │ └── README │ ├── platformio.ini │ ├── src │ │ ├── TaskScheduler.cpp │ │ ├── led.cpp │ │ └── main.cpp │ └── test │ │ └── README ├── Scheduler_example28_Tickless │ └── Scheduler_example28_Tickless.ino └── Scheduler_template │ └── Scheduler_template.ino ├── extras ├── TaskScheduler_html.png └── These documents are no longer current.txt ├── keywords.txt ├── library.json ├── library.properties └── src ├── TScheduler.hpp ├── TSchedulerDeclarations.hpp ├── TaskScheduler.h ├── TaskSchedulerDeclarations.h └── TaskSchedulerSleepMethods.h /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2020, Anatoli Arkhipenko. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its contributors 15 | may be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 22 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 24 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Task Scheduler 2 | ### Cooperative multitasking for Arduino, ESPx, STM32 and other microcontrollers 3 | #### Version 3.8.5: 2024-06-17 [Latest updates](https://github.com/arkhipenko/TaskScheduler/wiki/Latest-Updates) 4 | 5 | [![arduino-library-badge](https://www.ardu-badge.com/badge/TaskScheduler.svg?)](https://www.ardu-badge.com/TaskScheduler)[![xscode](https://img.shields.io/badge/Available%20on-xs%3Acode-blue?style=?style=plastic&logo=appveyor&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRF////////VXz1bAAAAAJ0Uk5T/wDltzBKAAAAlUlEQVR42uzXSwqAMAwE0Mn9L+3Ggtgkk35QwcnSJo9S+yGwM9DCooCbgn4YrJ4CIPUcQF7/XSBbx2TEz4sAZ2q1RAECBAiYBlCtvwN+KiYAlG7UDGj59MViT9hOwEqAhYCtAsUZvL6I6W8c2wcbd+LIWSCHSTeSAAECngN4xxIDSK9f4B9t377Wd7H5Nt7/Xz8eAgwAvesLRjYYPuUAAAAASUVORK5CYII=)](https://xscode.com/arkhipenko/TaskScheduler) 6 | 7 | #### Get expedited support or integration consultation for TaskScheduler [from xs:code](https://xscode.com/arkhipenko/TaskScheduler) 8 | 9 | [![xscode](https://github.com/arkhipenko/resources/blob/master/taskscheduler-banner.png)](https://xscode.com/arkhipenko/TaskScheduler) 10 | --- 11 | 12 | ### OVERVIEW: 13 | A lightweight implementation of cooperative multitasking (task scheduling). An easier alternative to preemptive programming and frameworks like FreeRTOS. 14 | 15 | **Why cooperative?** 16 | 17 | You mostly do not need to worry about pitfalls of concurrent processing (races, deadlocks, livelocks, resource sharing, etc.). The fact of cooperative processing takes care of such issues by design. 18 | 19 | _“Everybody who learns concurrency and thinks they understand it, ends up finding mysterious races they thought weren’t possible, and discovers that they didn’t actually understand it yet after all.”_ **Herb Sutter, chair of the ISO C++ standards committee, Microsoft.** 20 | 21 | **Main features:** 22 | 1. Periodic task execution, with dynamic execution period in `milliseconds` (default) or `microseconds` (if explicitly enabled) – frequency of execution 23 | 2. Number of iterations (limited or infinite number of iterations) 24 | 3. Execution of tasks in predefined sequence 25 | 4. Dynamic change of task execution parameters (frequency, number of iterations, callback methods) 26 | 5. Power saving via entering **IDLE** sleep mode when tasks are not scheduled to run 27 | 6. Support for event-driven task invocation via Status Request object 28 | 7. Support for task IDs and Control Points for error handling and watchdog timer 29 | 8. Support for Local Task Storage pointer (allowing use of same callback code for multiple tasks) 30 | 9. Support for layered task prioritization 31 | 10. Support for `std::functions` (tested on `ESPx` and `STM32` only) 32 | 11. Overall task timeout 33 | 12. Static and dynamic callback method binding 34 | 13. CPU load / idle statistics for time critical applications 35 | 14. Scheduling options with priority for original schedule (with and without catchup) and interval 36 | 15. Ability to pause/resume and enable/disable scheduling 37 | 16. Thread-safe scheduling while running under preemptive scheduler (i. e., FreeRTOS) 38 | 17. Optional self-destruction of dynamically created tasks upon disable 39 | 18. Support for "tickless" execution under FreeRTOS (continous sleep until next scheduled task invocation) 40 | 41 | Scheduling overhead: between `15` and `18` microseconds per scheduling pass (Arduino UNO rev 3 @ `16MHz` clock, single scheduler w/o prioritization) 42 | 43 | **TaskScheduler** was tested on the following platforms: 44 | * Arduino Uno R3 45 | * Arduino Nano 46 | * Arduino Micro 47 | * ATtiny85 48 | * ESP8266 49 | * ESP32 50 | * Teensy (tested on Teensy 3.5) 51 | * nRF52 (tested on nRF52832) 52 | * nRF52 Adafruit Core (tested on nRF52840 with v3.6.2 workround) 53 | * STM32 (tested on Mini USB STM32F103RCBT6 ARM Cortex-M3 leaflabs Leaf maple mini module F) 54 | * MSP430 and MSP432 boards 55 | * Raspberry Pi (requires external `Arduino.h` and `millis()` implementation) 56 | 57 | 58 | 59 | ​ **Don't just take my word for it - try it for yourself on [Wokwi](https://wokwi.com/playground/task-scheduler)** 60 | 61 | 62 | 63 | --- 64 | ![TaskScheduler process diagram](https://github.com/arkhipenko/TaskScheduler/raw/master/extras/TaskScheduler_html.png) 65 | --- 66 | ### Changelog is located [here.](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog) 67 | 68 | 69 | #### For detailed functionality overview please refer to TaskScheduler documentation in the 'extras' folder or in the [Wiki page](https://github.com/arkhipenko/TaskScheduler/wiki). 70 | 71 | ### User Feedback: 72 | 73 | "I've used https://github.com/arkhipenko/TaskScheduler with great success. Running LED patterns, monitoring button presses, reading data from an accelerometer, auto advancing to the next pattern, reading data from Serial. All at the same time." - [here](https://www.reddit.com/r/FastLED/comments/b3rfzf/wanna_try_some_code_that_is_powerfuldangerous/) 74 | 75 | "There are libraries that do this automatically on Arduino too, allowing you to schedule [cooperative] multitasking and sleep the uC between tasks. E.g. https://github.com/arkhipenko/TaskScheduler is really good, I've used it before. You basically queue up a list of task callbacks and a schedule in your `setup()` and then do a call to `tasks.execute()` in `loop()`, which pops off the next task that is due in a queue or sleeps otherwise. It's simple, but much more straightforward than manually using `if millis() - last > delta1... else sleep()` and not as rigid as using the timer ISRs (which really serve a different purpose)." - [here](https://news.ycombinator.com/item?id=14848906) 76 | 77 | "I took the controller with me on a business trip and spend the night getting the basic code framework out. It is going to run on top of Arkhipenko’s TaskScheduler. (https://github.com/arkhipenko/TaskScheduler) This should help me isolate any issues between the different control systems while managing the different task’s timing requirements." - [here](https://hackaday.io/project/167479/logs) 78 | 79 | "it's really cool and useful, for whenver you want your MCU to do more than 1 task" - [here](https://gitter.im/FastLED/public?at=5947e23dd83c50560c22d5b6) 80 | 81 | "I encourage you to use it in the Arduino environment, it allows you to save a lot of time (and code lines) wherever you need to schedule, i.e. run many tasks that should to perform at different frequencies and when we want to have the greatest control over the performance of these tasks and we want good diagnostic of errors." - [here](https://www.elektroda.pl/rtvforum/topic3599980.html) 82 | 83 | "arkhipenko/TaskScheduler is still my choice for now, especially when I get my pull request in, so we can have that idle 1 ms sleep feature for free." - [here](http://stm32duinoforum.com/forum/viewtopic_f_18_t_4299.html) 84 | 85 | "The difference with milis is basically that you don’t have to be using logics to manage the executions, but the module itself does it. This will allow us to make code more readable and easier to maintain. In addition, we must take into account the extra functions it provides, such as saving energy when not in use, or changing settings dynamically." - [here](https://www.electrosoftcloud.com/en/arduino-taskscheduler-no-more-millis-or-delay/) 86 | 87 | 88 | 89 | ### Check out what TaskScheduler can do: 90 | 91 | #### Around the world: 92 | 93 | * Ninja Timer: Giant 7-Segment Display at Adafruit.com 94 | https://learn.adafruit.com/ninja-timer-giant-7-segment-display/timer-code 95 | * Playing with NeoPixel to create a nice #smartBulb IoT 96 | https://www.zerozone.it/linux-e-open-source/giocare-con-i-neopixel-per-realizzare-un-simpatico-smartbulb-iot/16760 97 | * Adding a timer to XK X6 Transmitter 98 | https://www.elvinplay.com/adding-a-timer-to-xk-x6-transmitter-en/ 99 | * Arduino Bluetooth remote control + ultrasonic anti-collision car 100 | https://xie.infoq.cn/article/0f27dbbebcc2b99b35132b262 101 | * WEMOS D1 Mini로 Ad-hoc WIFI network 102 | https://m.blog.naver.com/sonyi/221330334326 103 | * [3 Devo](http://3devo.eu/) - Quality 3D printing filament, now made accessible and affordable 104 | (http://3devo.eu/license-information/) 105 | 106 | 107 | * [Houston midi](https://github.com/chaffneue/houston) clock project - TaskScheduler with microseconds resolution 108 | 109 | >by chaffneue: 110 | >>My first arduino project. It's a multi-master midi controller with a shared clock and 111 | auto count in behaviour. 112 | 113 | youtube: https://www.youtube.com/watch?v=QRof550TtXo 114 | 115 | 116 | * [Hackabot Nano](http://hackarobot.com/) by Funnyvale - Compact Plug and Play Arduino compatible robotic kit 117 | https://www.kickstarter.com/projects/hackarobot/hackabot-nano-compact-plug-and-play-arduino-robot 118 | * Discrete Time Systems Wiki - 119 | https://sistemas-en-tiempo-discreto.fandom.com/es/wiki/Tiempo_Real 120 | 121 | #### My projects: 122 | 123 | * Interactive "Do Not Disturb" sign in a shape of Minecraft Sword (ESP32) 124 | (https://www.instructables.com/id/Interactive-Minecraft-Do-Not-Enter-SwordSign-ESP32/) 125 | * Interactive Predator Costume with Real-Time Head Tracking Plasma Cannon (Teensy, Arduino Nano) 126 | (https://www.instructables.com/id/Interactive-Predator-Costume-With-Head-Tracking-Pl/) 127 | * IoT APIS v2 - Autonomous IoT-enabled Automated Plant Irrigation System (ESP8266) 128 | (http://www.instructables.com/id/IoT-APIS-V2-Autonomous-IoT-enabled-Automated-Plant/) 129 | * APIS - Automated Plant Irrigation System (Arduino Uno) 130 | (http://www.instructables.com/id/APIS-Automated-Plant-Irrigation-System/) 131 | 132 | * Party Lights LEDs music visualization (Leaf Maple Mini) 133 | (https://www.instructables.com/id/Portable-Party-Lights/) 134 | * Arduino Nano based Hexbug Scarab Robotic Spider (Arduino Nano) 135 | (http://www.instructables.com/id/Arduino-Nano-based-Hexbug-Scarab-Robotic-Spider/) 136 | * Wave your hand to control OWI Robotic Arm... no strings attached (Arduino Uno and Nano) 137 | (http://www.instructables.com/id/Wave-your-hand-to-control-OWI-Robotic-Arm-no-strin/) 138 | 139 | 140 | * Interactive Halloween Pumpkin (Arduino Uno) 141 | (http://www.instructables.com/id/Interactive-Halloween-Pumpkin/) 142 | -------------------------------------------------------------------------------- /examples/Scheduler_example00_Blink/Scheduler_example00_Blink.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Every example set must have a LED blink example 3 | For this one the idea is to have as many ways to blink the LED 4 | as I can think of. So, here we go. 5 | 6 | Tested on: 7 | - Arduino Nano 8 | - ESP8266 9 | - ESP32 10 | - STM32 Maple Mini 11 | */ 12 | 13 | // ---------------------------------------- 14 | // The following "defines" control library functionality at compile time, 15 | // and should be used in the main sketch depending on the functionality required 16 | // 17 | // #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns 18 | #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between runs if no callback methods were invoked during the pass 19 | #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only 20 | // #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids 21 | // #define _TASK_LTS_POINTER // Compile with support for local task storage pointer 22 | // #define _TASK_PRIORITY // Support for layered scheduling priority 23 | // #define _TASK_MICRO_RES // Support for microsecond resolution 24 | // #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY) 25 | // #define _TASK_DEBUG // Make all methods and variables public for debug purposes 26 | // #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations 27 | // #define _TASK_TIMEOUT // Support for overall task timeout 28 | // #define _TASK_OO_CALLBACKS // Support for callbacks via inheritance 29 | // #define _TASK_EXPOSE_CHAIN // Methods to access tasks in the task chain 30 | // #define _TASK_SCHEDULING_OPTIONS // Support for multiple scheduling options 31 | // #define _TASK_DEFINE_MILLIS // Force forward declaration of millis() and micros() "C" style 32 | // #define _TASK_EXTERNAL_TIME // Custom millis() and micros() methods 33 | // #define _TASK_THREAD_SAFE // Enable additional checking for thread safety 34 | // #define _TASK_SELF_DESTRUCT // Enable tasks to "self-destruct" after disable 35 | #include 36 | 37 | // Debug and Test options 38 | #define _DEBUG_ 39 | //#define _TEST_ 40 | 41 | #ifdef _DEBUG_ 42 | #define _PP(a) Serial.print(a); 43 | #define _PL(a) Serial.println(a); 44 | #else 45 | #define _PP(a) 46 | #define _PL(a) 47 | #endif 48 | 49 | // LED_BUILTIN 13 50 | #if defined( ARDUINO_ARCH_ESP32 ) 51 | #define LED_BUILTIN 23 // esp32 dev2 kit does not have LED 52 | #endif 53 | 54 | // Scheduler 55 | Scheduler ts; 56 | 57 | /* 58 | Approach 1: LED is driven by the boolean variable; false = OFF, true = ON 59 | */ 60 | #define PERIOD1 500 61 | #define DURATION 10000 62 | void blink1CB(); 63 | Task tBlink1 ( PERIOD1 * TASK_MILLISECOND, DURATION / PERIOD1, &blink1CB, &ts, true ); 64 | 65 | /* 66 | Approac 2: two callback methods: one turns ON, another turns OFF 67 | */ 68 | #define PERIOD2 400 69 | void blink2CB_ON(); 70 | void blink2CB_OFF(); 71 | Task tBlink2 ( PERIOD2 * TASK_MILLISECOND, DURATION / PERIOD2, &blink2CB_ON, &ts, false ); 72 | 73 | /* 74 | Approach 3: Use RunCounter 75 | */ 76 | #define PERIOD3 300 77 | void blink3CB(); 78 | Task tBlink3 (PERIOD3 * TASK_MILLISECOND, DURATION / PERIOD3, &blink3CB, &ts, false); 79 | 80 | /* 81 | Approach 4: Use status request objects to pass control from one task to the other 82 | */ 83 | #define PERIOD4 200 84 | bool blink41OE(); 85 | void blink41(); 86 | void blink42(); 87 | void blink42OD(); 88 | Task tBlink4On ( PERIOD4 * TASK_MILLISECOND, TASK_ONCE, blink41, &ts, false, &blink41OE ); 89 | Task tBlink4Off ( PERIOD4 * TASK_MILLISECOND, TASK_ONCE, blink42, &ts, false, NULL, &blink42OD ); 90 | 91 | 92 | /* 93 | Approach 5: Two interleaving tasks 94 | */ 95 | #define PERIOD5 600 96 | bool blink51OE(); 97 | void blink51(); 98 | void blink52(); 99 | void blink52OD(); 100 | Task tBlink5On ( 600 * TASK_MILLISECOND, DURATION / PERIOD5, &blink51, &ts, false, &blink51OE ); 101 | Task tBlink5Off ( 600 * TASK_MILLISECOND, DURATION / PERIOD5, &blink52, &ts, false, NULL, &blink52OD ); 102 | 103 | 104 | /* 105 | Approach 6: RunCounter-based with random intervals 106 | */ 107 | #define PERIOD6 300 108 | void blink6CB(); 109 | bool blink6OE(); 110 | void blink6OD(); 111 | Task tBlink6 ( PERIOD6 * TASK_MILLISECOND, DURATION / PERIOD6, &blink6CB, &ts, false, &blink6OE, &blink6OD ); 112 | 113 | void setup() { 114 | // put your setup code here, to run once: 115 | #if defined(_DEBUG_) || defined(_TEST_) 116 | Serial.begin(115200); 117 | delay(TASK_SECOND); 118 | _PL("TaskScheduler Blink example"); 119 | _PL("Blinking for 10 seconds using various techniques\n"); 120 | delay(2 * TASK_SECOND); 121 | #endif 122 | pinMode(LED_BUILTIN, OUTPUT); 123 | } 124 | 125 | void loop() { 126 | ts.execute(); 127 | } 128 | 129 | inline void LEDOn() { 130 | digitalWrite( LED_BUILTIN, HIGH ); 131 | } 132 | 133 | inline void LEDOff() { 134 | digitalWrite( LED_BUILTIN, LOW ); 135 | } 136 | 137 | // === 1 ======================================= 138 | bool LED_state = false; 139 | void blink1CB() { 140 | if ( tBlink1.isFirstIteration() ) { 141 | _PP(millis()); 142 | _PL(": Blink1 - simple flag driven"); 143 | LED_state = false; 144 | } 145 | 146 | if ( LED_state ) { 147 | LEDOff(); 148 | LED_state = false; 149 | } 150 | else { 151 | LEDOn(); 152 | LED_state = true; 153 | } 154 | 155 | if ( tBlink1.isLastIteration() ) { 156 | tBlink2.restartDelayed( 2 * TASK_SECOND ); 157 | LEDOff(); 158 | } 159 | } 160 | 161 | 162 | // === 2 ====================================== 163 | void blink2CB_ON() { 164 | if ( tBlink2.isFirstIteration() ) { 165 | _PP(millis()); 166 | _PL(": Blink2 - 2 callback methods"); 167 | } 168 | 169 | LEDOn(); 170 | tBlink2.setCallback( &blink2CB_OFF ); 171 | 172 | if ( tBlink2.isLastIteration() ) { 173 | tBlink3.restartDelayed( 2 * TASK_SECOND ); 174 | LEDOff(); 175 | } 176 | } 177 | 178 | 179 | void blink2CB_OFF() { 180 | 181 | LEDOff(); 182 | tBlink2.setCallback( &blink2CB_ON ); 183 | 184 | if ( tBlink2.isLastIteration() ) { 185 | tBlink3.restartDelayed( 2 * TASK_SECOND ); 186 | LEDOff(); 187 | } 188 | } 189 | 190 | 191 | // === 3 ===================================== 192 | void blink3CB() { 193 | if ( tBlink3.isFirstIteration() ) { 194 | _PP(millis()); 195 | _PL(": Blink3 - Run Counter driven"); 196 | } 197 | 198 | if ( tBlink3.getRunCounter() & 1 ) { 199 | LEDOn(); 200 | } 201 | else { 202 | LEDOff(); 203 | } 204 | 205 | if ( tBlink3.isLastIteration() ) { 206 | tBlink4On.setOnEnable( &blink41OE ); 207 | tBlink4On.restartDelayed( 2 * TASK_SECOND ); 208 | LEDOff(); 209 | } 210 | } 211 | 212 | 213 | // === 4 ============================================= 214 | int counter = 0; 215 | bool blink41OE() { 216 | _PP(millis()); 217 | _PL(": Blink4 - Internal status request based"); 218 | counter = 0; 219 | tBlink4On.setOnEnable( NULL ); 220 | return true; 221 | } 222 | 223 | void blink41() { 224 | // _PP(millis()); 225 | // _PL(": blink41"); 226 | LEDOn(); 227 | StatusRequest* r = tBlink4On.getInternalStatusRequest(); 228 | tBlink4Off.waitForDelayed( r ); 229 | counter++; 230 | } 231 | 232 | void blink42() { 233 | // _PP(millis()); 234 | // _PL(": blink42"); 235 | LEDOff(); 236 | StatusRequest* r = tBlink4Off.getInternalStatusRequest(); 237 | tBlink4On.waitForDelayed( r ); 238 | counter++; 239 | } 240 | 241 | 242 | void blink42OD() { 243 | if ( counter >= DURATION / PERIOD4 ) { 244 | tBlink4On.disable(); 245 | tBlink4Off.disable(); 246 | 247 | tBlink5On.setOnEnable( &blink51OE ); 248 | tBlink5On.restartDelayed( 2 * TASK_SECOND ); 249 | tBlink5Off.restartDelayed( 2 * TASK_SECOND + PERIOD5 / 2 ); 250 | LEDOff(); 251 | } 252 | } 253 | 254 | 255 | // === 5 ========================================== 256 | bool blink51OE() { 257 | _PP(millis()); 258 | _PL(": Blink5 - Two interleaving tasks"); 259 | tBlink5On.setOnEnable( NULL ); 260 | return true; 261 | } 262 | void blink51() { 263 | // _PP(millis()); 264 | // _PL(": blink51"); 265 | LEDOn(); 266 | } 267 | void blink52() { 268 | // _PP(millis()); 269 | // _PL(": blink52"); 270 | LEDOff(); 271 | } 272 | void blink52OD() { 273 | tBlink6.restartDelayed( 2 * TASK_SECOND ); 274 | LEDOff(); 275 | } 276 | 277 | 278 | // === 6 ============================================ 279 | long interval6 = 0; 280 | bool blink6OE() { 281 | _PP(millis()); 282 | _PP(": Blink6 - RunCounter + Random ON interval = "); 283 | interval6 = random( 100, 901 ); 284 | tBlink6.setInterval( interval6 ); 285 | _PL( interval6 ); 286 | tBlink6.delay( 2 * TASK_SECOND ); 287 | 288 | return true; 289 | } 290 | 291 | void blink6CB() { 292 | if ( tBlink6.getRunCounter() & 1 ) { 293 | LEDOn(); 294 | tBlink6.setInterval( interval6 ); 295 | } 296 | else { 297 | LEDOff(); 298 | tBlink6.setInterval( TASK_SECOND - interval6 ); 299 | } 300 | } 301 | 302 | void blink6OD() { 303 | tBlink1.restartDelayed( 2 * TASK_SECOND ); 304 | LEDOff(); 305 | } 306 | -------------------------------------------------------------------------------- /examples/Scheduler_example00_Blink_Namespace/Scheduler_example00_Blink_Namespace.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Every example set must have a LED blink example 3 | For this one the idea is to have as many ways to blink the LED 4 | as I can think of. So, here we go. 5 | 6 | Tested on: 7 | - Bluefruit nRF52840 8 | */ 9 | 10 | 11 | // ---------------------------------------- 12 | // The following "defines" control library functionality at compile time, 13 | // and should be used in the main sketch depending on the functionality required 14 | // 15 | // #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns 16 | #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between runs if no callback methods were invoked during the pass 17 | #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only 18 | // #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids 19 | // #define _TASK_LTS_POINTER // Compile with support for local task storage pointer 20 | // #define _TASK_PRIORITY // Support for layered scheduling priority 21 | // #define _TASK_MICRO_RES // Support for microsecond resolution 22 | // #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY) 23 | // #define _TASK_DEBUG // Make all methods and variables public for debug purposes 24 | // #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations 25 | // #define _TASK_TIMEOUT // Support for overall task timeout 26 | // #define _TASK_OO_CALLBACKS // Support for callbacks via inheritance 27 | // #define _TASK_EXPOSE_CHAIN // Methods to access tasks in the task chain 28 | // #define _TASK_SCHEDULING_OPTIONS // Support for multiple scheduling options 29 | // #define _TASK_DEFINE_MILLIS // Force forward declaration of millis() and micros() "C" style 30 | // #define _TASK_EXTERNAL_TIME // Custom millis() and micros() methods 31 | // #define _TASK_THREAD_SAFE // Enable additional checking for thread safety 32 | // #define _TASK_SELF_DESTRUCT // Enable tasks to "self-destruct" after disable 33 | 34 | #include 35 | 36 | // Debug and Test options 37 | //#define _DEBUG_ 38 | //#define _TEST_ 39 | 40 | #ifdef _DEBUG_ 41 | #define _PP(a) Serial.print(a); 42 | #define _PL(a) Serial.println(a); 43 | #else 44 | #define _PP(a) 45 | #define _PL(a) 46 | #endif 47 | 48 | // LED_BUILTIN 13 49 | #if defined( ARDUINO_ARCH_ESP32 ) 50 | #define LED_BUILTIN 23 // esp32 dev2 kit does not have LED 51 | #endif 52 | 53 | // Scheduler 54 | TsScheduler ts; 55 | 56 | /* 57 | Approach 1: LED is driven by the boolean variable; false = OFF, true = ON 58 | */ 59 | #define PERIOD1 500 60 | #define DURATION 10000 61 | void blink1CB(); 62 | TsTask tBlink1 ( PERIOD1 * TASK_MILLISECOND, DURATION / PERIOD1, &blink1CB, &ts, true ); 63 | 64 | /* 65 | Approac 2: two callback methods: one turns ON, another turns OFF 66 | */ 67 | #define PERIOD2 400 68 | void blink2CB_ON(); 69 | void blink2CB_OFF(); 70 | TsTask tBlink2 ( PERIOD2 * TASK_MILLISECOND, DURATION / PERIOD2, &blink2CB_ON, &ts, false ); 71 | 72 | /* 73 | Approach 3: Use RunCounter 74 | */ 75 | #define PERIOD3 300 76 | void blink3CB(); 77 | TsTask tBlink3 (PERIOD3 * TASK_MILLISECOND, DURATION / PERIOD3, &blink3CB, &ts, false); 78 | 79 | /* 80 | Approach 4: Use status request objects to pass control from one task to the other 81 | */ 82 | #define PERIOD4 200 83 | bool blink41OE(); 84 | void blink41(); 85 | void blink42(); 86 | void blink42OD(); 87 | TsTask tBlink4On ( PERIOD4 * TASK_MILLISECOND, TASK_ONCE, blink41, &ts, false, &blink41OE ); 88 | TsTask tBlink4Off ( PERIOD4 * TASK_MILLISECOND, TASK_ONCE, blink42, &ts, false, NULL, &blink42OD ); 89 | 90 | 91 | /* 92 | Approach 5: Two interleaving tasks 93 | */ 94 | #define PERIOD5 600 95 | bool blink51OE(); 96 | void blink51(); 97 | void blink52(); 98 | void blink52OD(); 99 | TsTask tBlink5On ( 600 * TASK_MILLISECOND, DURATION / PERIOD5, &blink51, &ts, false, &blink51OE ); 100 | TsTask tBlink5Off ( 600 * TASK_MILLISECOND, DURATION / PERIOD5, &blink52, &ts, false, NULL, &blink52OD ); 101 | 102 | 103 | /* 104 | Approach 6: RunCounter-based with random intervals 105 | */ 106 | #define PERIOD6 300 107 | void blink6CB(); 108 | bool blink6OE(); 109 | void blink6OD(); 110 | TsTask tBlink6 ( PERIOD6 * TASK_MILLISECOND, DURATION / PERIOD6, &blink6CB, &ts, false, &blink6OE, &blink6OD ); 111 | 112 | void setup() { 113 | // put your setup code here, to run once: 114 | #if defined(_DEBUG_) || defined(_TEST_) 115 | Serial.begin(115200); 116 | delay(TASK_SECOND); 117 | _PL("TaskScheduler Blink example"); 118 | _PL("Blinking for 10 seconds using various techniques\n"); 119 | delay(2 * TASK_SECOND); 120 | #endif 121 | pinMode(LED_BUILTIN, OUTPUT); 122 | } 123 | 124 | void loop() { 125 | ts.execute(); 126 | } 127 | 128 | inline void LEDOn() { 129 | digitalWrite( LED_BUILTIN, HIGH ); 130 | } 131 | 132 | inline void LEDOff() { 133 | digitalWrite( LED_BUILTIN, LOW ); 134 | } 135 | 136 | // === 1 ======================================= 137 | bool LED_state = false; 138 | void blink1CB() { 139 | if ( tBlink1.isFirstIteration() ) { 140 | _PP(millis()); 141 | _PL(": Blink1 - simple flag driven"); 142 | LED_state = false; 143 | } 144 | 145 | if ( LED_state ) { 146 | LEDOff(); 147 | LED_state = false; 148 | } 149 | else { 150 | LEDOn(); 151 | LED_state = true; 152 | } 153 | 154 | if ( tBlink1.isLastIteration() ) { 155 | tBlink2.restartDelayed( 2 * TASK_SECOND ); 156 | LEDOff(); 157 | } 158 | } 159 | 160 | 161 | // === 2 ====================================== 162 | void blink2CB_ON() { 163 | if ( tBlink2.isFirstIteration() ) { 164 | _PP(millis()); 165 | _PL(": Blink2 - 2 callback methods"); 166 | } 167 | 168 | LEDOn(); 169 | tBlink2.setCallback( &blink2CB_OFF ); 170 | 171 | if ( tBlink2.isLastIteration() ) { 172 | tBlink3.restartDelayed( 2 * TASK_SECOND ); 173 | LEDOff(); 174 | } 175 | } 176 | 177 | 178 | void blink2CB_OFF() { 179 | 180 | LEDOff(); 181 | tBlink2.setCallback( &blink2CB_ON ); 182 | 183 | if ( tBlink2.isLastIteration() ) { 184 | tBlink3.restartDelayed( 2 * TASK_SECOND ); 185 | LEDOff(); 186 | } 187 | } 188 | 189 | 190 | // === 3 ===================================== 191 | void blink3CB() { 192 | if ( tBlink3.isFirstIteration() ) { 193 | _PP(millis()); 194 | _PL(": Blink3 - Run Counter driven"); 195 | } 196 | 197 | if ( tBlink3.getRunCounter() & 1 ) { 198 | LEDOn(); 199 | } 200 | else { 201 | LEDOff(); 202 | } 203 | 204 | if ( tBlink3.isLastIteration() ) { 205 | tBlink4On.setOnEnable( &blink41OE ); 206 | tBlink4On.restartDelayed( 2 * TASK_SECOND ); 207 | LEDOff(); 208 | } 209 | } 210 | 211 | 212 | // === 4 ============================================= 213 | int counter = 0; 214 | bool blink41OE() { 215 | _PP(millis()); 216 | _PL(": Blink4 - Internal status request based"); 217 | counter = 0; 218 | tBlink4On.setOnEnable( NULL ); 219 | return true; 220 | } 221 | 222 | void blink41() { 223 | // _PP(millis()); 224 | // _PL(": blink41"); 225 | LEDOn(); 226 | TsStatusRequest* r = tBlink4On.getInternalStatusRequest(); 227 | tBlink4Off.waitForDelayed( r ); 228 | counter++; 229 | } 230 | 231 | void blink42() { 232 | // _PP(millis()); 233 | // _PL(": blink42"); 234 | LEDOff(); 235 | TsStatusRequest* r = tBlink4Off.getInternalStatusRequest(); 236 | tBlink4On.waitForDelayed( r ); 237 | counter++; 238 | } 239 | 240 | 241 | void blink42OD() { 242 | if ( counter >= DURATION / PERIOD4 ) { 243 | tBlink4On.disable(); 244 | tBlink4Off.disable(); 245 | 246 | tBlink5On.setOnEnable( &blink51OE ); 247 | tBlink5On.restartDelayed( 2 * TASK_SECOND ); 248 | tBlink5Off.restartDelayed( 2 * TASK_SECOND + PERIOD5 / 2 ); 249 | LEDOff(); 250 | } 251 | } 252 | 253 | 254 | // === 5 ========================================== 255 | bool blink51OE() { 256 | _PP(millis()); 257 | _PL(": Blink5 - Two interleaving tasks"); 258 | tBlink5On.setOnEnable( NULL ); 259 | return true; 260 | } 261 | void blink51() { 262 | // _PP(millis()); 263 | // _PL(": blink51"); 264 | LEDOn(); 265 | } 266 | void blink52() { 267 | // _PP(millis()); 268 | // _PL(": blink52"); 269 | LEDOff(); 270 | } 271 | void blink52OD() { 272 | tBlink6.restartDelayed( 2 * TASK_SECOND ); 273 | LEDOff(); 274 | } 275 | 276 | 277 | // === 6 ============================================ 278 | long interval6 = 0; 279 | bool blink6OE() { 280 | _PP(millis()); 281 | _PP(": Blink6 - RunCounter + Random ON interval = "); 282 | interval6 = random( 100, 901 ); 283 | tBlink6.setInterval( interval6 ); 284 | _PL( interval6 ); 285 | tBlink6.delay( 2 * TASK_SECOND ); 286 | 287 | return true; 288 | } 289 | 290 | void blink6CB() { 291 | if ( tBlink6.getRunCounter() & 1 ) { 292 | LEDOn(); 293 | tBlink6.setInterval( interval6 ); 294 | } 295 | else { 296 | LEDOff(); 297 | tBlink6.setInterval( TASK_SECOND - interval6 ); 298 | } 299 | } 300 | 301 | void blink6OD() { 302 | tBlink1.restartDelayed( 2 * TASK_SECOND ); 303 | LEDOff(); 304 | } 305 | -------------------------------------------------------------------------------- /examples/Scheduler_example01/Scheduler_example01.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * TaskScheduler Test 3 | * 4 | * Initially only tasks 1 and 2 are enabled 5 | * Task1 runs every 2 seconds 10 times and then stops 6 | * Task2 runs every 3 seconds indefinitely 7 | * Task1 enables Task3 at its first run 8 | * Task3 run every 5 seconds 9 | * Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds 10 | * At the end Task2 is the only task running every 1/2 seconds 11 | */ 12 | 13 | 14 | #include 15 | 16 | // Callback methods prototypes 17 | void t1Callback(); 18 | void t2Callback(); 19 | void t3Callback(); 20 | 21 | //Tasks 22 | Task t4(); 23 | Task t1(2000, 10, &t1Callback); 24 | Task t2(3000, TASK_FOREVER, &t2Callback); 25 | Task t3(5000, TASK_FOREVER, &t3Callback); 26 | 27 | Scheduler runner; 28 | 29 | 30 | void t1Callback() { 31 | Serial.print("t1: "); 32 | Serial.println(millis()); 33 | 34 | if (t1.isFirstIteration()) { 35 | runner.addTask(t3); 36 | t3.enable(); 37 | Serial.println("t1: enabled t3 and added to the chain"); 38 | } 39 | 40 | if (t1.isLastIteration()) { 41 | t3.disable(); 42 | runner.deleteTask(t3); 43 | t2.setInterval(500); 44 | Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500"); 45 | } 46 | } 47 | 48 | void t2Callback() { 49 | Serial.print("t2: "); 50 | Serial.println(millis()); 51 | 52 | } 53 | 54 | void t3Callback() { 55 | Serial.print("t3: "); 56 | Serial.println(millis()); 57 | 58 | } 59 | 60 | void setup () { 61 | Serial.begin(115200); 62 | Serial.println("Scheduler TEST"); 63 | 64 | runner.init(); 65 | Serial.println("Initialized scheduler"); 66 | 67 | runner.addTask(t1); 68 | Serial.println("added t1"); 69 | 70 | runner.addTask(t2); 71 | Serial.println("added t2"); 72 | 73 | delay(5000); 74 | 75 | t1.enable(); 76 | Serial.println("Enabled t1"); 77 | t2.enable(); 78 | Serial.println("Enabled t2"); 79 | } 80 | 81 | 82 | void loop () { 83 | runner.execute(); 84 | } -------------------------------------------------------------------------------- /examples/Scheduler_example02/Scheduler_example02.ino: -------------------------------------------------------------------------------- 1 | #define _TASK_SLEEP_ON_IDLE_RUN 2 | #include 3 | 4 | Scheduler runner; 5 | // Callback methods prototypes 6 | void t1Callback(); 7 | void t2Callback(); 8 | void t3Callback(); 9 | 10 | // Tasks 11 | Task t4(); 12 | Task t1(2000, 10, &t1Callback, &runner, true); //adding task to the chain on creation 13 | Task t2(3000, TASK_FOREVER, &t2Callback, &runner, true); //adding task to the chain on creation 14 | Task t3(5000, TASK_FOREVER, &t3Callback); 15 | 16 | // Test 17 | // Initially only tasks 1 and 2 are enabled 18 | // Task1 runs every 2 seconds 10 times and then stops 19 | // Task2 runs every 3 seconds indefinitely 20 | // Task1 enables Task3 at its first run 21 | // Task3 run every 5 seconds 22 | // loop() runs every 1 second (a default scheduler delay, if no shorter tasks' interval is detected) 23 | // Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds 24 | // Because Task2 interval is shorter than Scheduler default tick, loop() executes ecery 1/2 seconds now 25 | // At the end Task2 is the only task running every 1/2 seconds 26 | // 27 | // NOTE that t1 and t2 are affected by the delay() function in the setup() method and are scheduled immediately twice to "catch up" with millis(). 28 | 29 | 30 | 31 | 32 | 33 | void t1Callback() { 34 | Serial.print("t1: "); 35 | Serial.println(millis()); 36 | 37 | if (t1.isFirstIteration()) { 38 | runner.addTask(t3); 39 | t3.enable(); 40 | Serial.println("t1: enabled t3 and added to the chain"); 41 | } 42 | 43 | if (t1.isLastIteration()) { 44 | t3.disable(); 45 | runner.deleteTask(t3); 46 | t2.setInterval(500); 47 | Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500"); 48 | } 49 | } 50 | 51 | void t2Callback() { 52 | Serial.print("t2: "); 53 | Serial.println(millis()); 54 | 55 | } 56 | 57 | void t3Callback() { 58 | Serial.print("t3: "); 59 | Serial.println(millis()); 60 | 61 | } 62 | 63 | void setup () { 64 | Serial.begin(115200); 65 | delay(5000); 66 | Serial.println("Scheduler TEST"); 67 | 68 | runner.startNow(); // set point-in-time for scheduling start 69 | } 70 | 71 | 72 | void loop () { 73 | 74 | runner.execute(); 75 | 76 | // Serial.println("Loop ticks at: "); 77 | // Serial.println(millis()); 78 | } -------------------------------------------------------------------------------- /examples/Scheduler_example03/Scheduler_example03.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * TaskScheduler Test of OnEnable and OnDisable methods and illustration of using wrapper tasks for timout purposes 3 | * 4 | * A wrapper task runs every 10 seconds and initiates the test case 5 | * Another task is run once for 5 seconds, and serves as a LED blinking timeout - 5 seconds 6 | * Finally, a dedicated task which controls LED is running periodically until stopped, and makes the LED blink with 0.5 to 1 second interval. 7 | * 8 | */ 9 | 10 | #define _TASK_SLEEP_ON_IDLE_RUN 11 | #include 12 | 13 | #ifndef LED_BUILTIN 14 | #define LED_BUILTIN 13 // define appropriate pin for your board 15 | #endif 16 | 17 | Scheduler ts; 18 | 19 | // Callback methods prototypes 20 | void WrapperCallback(); 21 | bool BlinkOnEnable(); 22 | void BlinkOnDisable(); 23 | void LEDOn(); 24 | void LEDOff(); 25 | 26 | // Tasks 27 | Task tWrapper(10000L, TASK_FOREVER, &WrapperCallback, &ts, true); 28 | Task tBlink(5000, TASK_ONCE, NULL, &ts, false, &BlinkOnEnable, &BlinkOnDisable); 29 | Task tLED(0, TASK_FOREVER, NULL, &ts, false, NULL, &LEDOff); 30 | 31 | void WrapperCallback() { 32 | tBlink.restartDelayed(); // LED blinking is initiated 33 | //every 30 seconds for 5 seconds 34 | } 35 | 36 | 37 | // Upon being enabled, tBlink will define the parameters 38 | // and enable LED blinking task, which actually controls 39 | // the hardware (LED in this example) 40 | bool BlinkOnEnable() { 41 | tLED.setInterval( 200 + random(801) ); 42 | tLED.setCallback( &LEDOn); 43 | tLED.enable(); 44 | 45 | return true; // Task should be enabled 46 | } 47 | 48 | // tBlink does not really need a callback function 49 | // since it just waits for 5 seconds for the first 50 | // and only iteration to occur. Once the iteration 51 | // takes place, tBlink is disabled by the Scheduler, 52 | // thus executing its OnDisable method below. 53 | 54 | void BlinkOnDisable() { 55 | tLED.disable(); 56 | } 57 | 58 | void LEDOn () { 59 | digitalWrite(LED_BUILTIN , HIGH); 60 | tLED.setCallback( &LEDOff); 61 | } 62 | 63 | void LEDOff () { 64 | digitalWrite(LED_BUILTIN , LOW); 65 | tLED.setCallback( &LEDOn); 66 | } 67 | 68 | // Note that LEDOff method serves as OnDisable method 69 | // to make sure the LED is turned off when the tBlink 70 | // task finishes (or disabled ahead of time) 71 | 72 | void setup() { 73 | // put your setup code here, to run once: 74 | pinMode(LED_BUILTIN , OUTPUT); 75 | } 76 | 77 | void loop() { 78 | // put your main code here, to run repeatedly: 79 | ts.execute(); 80 | } -------------------------------------------------------------------------------- /examples/Scheduler_example04_StatusRequest/Scheduler_example04_StatusRequest.ino: -------------------------------------------------------------------------------- 1 | /** This test demonstrates interaction between three simple tasks via StatusRequest object. 2 | * Task T1 runs every 5 seconds and signals completion of a status request st. 3 | * Tasks T2 and T3 are waiting on the same request (st) 4 | * Task T3 does not renew its interest in status request st, so it is only invoked once (first iteration) 5 | * Task T2 is invoked every time st completes, because it renews its interest in status of status request object st every iteration of T1 6 | */ 7 | 8 | #define _TASK_SLEEP_ON_IDLE_RUN 9 | #define _TASK_STATUS_REQUEST 10 | #include 11 | 12 | StatusRequest st; 13 | 14 | Scheduler ts; 15 | 16 | // Callback methods prototypes 17 | void Callback1(); 18 | void Disable1(); 19 | void Callback2(); 20 | void Callback3(); 21 | void PrepareStatus(); 22 | 23 | // Tasks 24 | Task t1(5000, TASK_ONCE, &Callback1, &ts, true, NULL, &Disable1); 25 | Task t2(&Callback2, &ts); 26 | Task t3(&Callback3, &ts); 27 | 28 | /** T1 callback 29 | * T1 just signals completion of st every 5 seconds 30 | */ 31 | void Callback1() { 32 | Serial.println("T1: Signaling completion of ST"); 33 | st.signalComplete(); 34 | } 35 | 36 | /** T1 On Disable callback 37 | * This callback renews the status request and restarts T1 delayed to run again in 5 seconds 38 | */ 39 | void Disable1() { 40 | PrepareStatus(); 41 | t1.restartDelayed(); 42 | } 43 | 44 | /** T2 callback 45 | * Invoked when status request st completes 46 | */ 47 | void Callback2() { 48 | Serial.println("T2: Invoked due to completion of ST"); 49 | } 50 | 51 | 52 | /** T3 callback 53 | * Invoked when status request st completes. 54 | * This is only run once since T3 does not renew its interest in the status request st after first iteration 55 | */ 56 | void Callback3() { 57 | Serial.println("T3: Invoked due to completion of ST"); 58 | 59 | } 60 | 61 | /** Prepare Status request st for another iteration 62 | * 63 | */ 64 | void PrepareStatus() { 65 | st.setWaiting(); // set the statusrequest object for waiting 66 | t2.waitFor(&st); // request tasks 1 & 2 to wait on the object st 67 | } 68 | 69 | 70 | /** Main Arduino code 71 | * Not much to do here. Just init Serial and set the initial status request 72 | */ 73 | void setup() { 74 | 75 | Serial.begin(115200); 76 | delay(1000); 77 | Serial.println("TaskScheduler: Status Request Test 1. Simple Test."); 78 | 79 | ts.startNow(); 80 | PrepareStatus(); 81 | t3.waitFor(&st); 82 | 83 | t1.delay(); 84 | } 85 | 86 | void loop() { 87 | 88 | ts.execute(); 89 | 90 | } -------------------------------------------------------------------------------- /examples/Scheduler_example05_StatusRequest/Scheduler_example05_StatusRequest.ino: -------------------------------------------------------------------------------- 1 | /** This test emulates querying 3 sensors once every 10 seconds, each could respond with a different delay 2 | * (ultrasonic sensors for instance) and printing a min value of the three when all three have reported their values. 3 | * The overall timeout of 1 second is setup as well. 4 | * An error message needs to be printed if a timeout occurred instead of a value. 5 | */ 6 | 7 | 8 | #define _TASK_SLEEP_ON_IDLE_RUN 9 | #define _TASK_STATUS_REQUEST 10 | #include 11 | 12 | #ifdef ARDUINO_ARCH_STM32F1 13 | #define A0 3 14 | #endif 15 | 16 | StatusRequest measure; 17 | 18 | Scheduler ts; 19 | 20 | // Callback methods prototypes 21 | void CycleCallback(); 22 | void MeasureCallback(); 23 | bool MeasureEnable(); 24 | void MeasureDisable(); 25 | void CalcCallback(); 26 | void S1Callback(); bool S1Enable(); 27 | void S2Callback(); bool S2Enable(); 28 | void S3Callback(); bool S3Enable(); 29 | 30 | // Tasks 31 | Task tCycle(10000, TASK_FOREVER, &CycleCallback, &ts, true); 32 | Task tMeasure(1000, TASK_ONCE, &MeasureCallback, &ts, false, &MeasureEnable, &MeasureDisable); 33 | Task tCalculate(&CalcCallback, &ts); 34 | Task tSensor1(0, TASK_ONCE, &S1Callback, &ts, false, &S1Enable); 35 | Task tSensor2(0, TASK_ONCE, &S2Callback, &ts, false, &S2Enable); 36 | Task tSensor3(0, TASK_ONCE, &S3Callback, &ts, false, &S3Enable); 37 | 38 | 39 | long distance, d1, d2, d3; 40 | 41 | void CycleCallback() { 42 | Serial.println("CycleCallback: Initiating measurement cycle every 10 seconds"); 43 | 44 | tMeasure.restartDelayed(); 45 | } 46 | 47 | 48 | 49 | bool MeasureEnable() { 50 | Serial.println("MeasureEnable: Activating sensors"); 51 | 52 | distance = 0; 53 | measure.setWaiting(3); // Set the StatusRequest to wait for 3 signals. 54 | tCalculate.waitFor(&measure); 55 | 56 | tSensor1.restartDelayed(); 57 | tSensor2.restartDelayed(); 58 | tSensor3.restartDelayed(); 59 | 60 | return true; 61 | } 62 | 63 | void MeasureCallback() { 64 | Serial.println("MeasureCallback: Invoked by calculate task or one second later"); 65 | 66 | if (measure.pending()) { 67 | tCalculate.disable(); 68 | measure.signalComplete(-1); // signal error 69 | Serial.println("MeasureCallback: Timeout!"); 70 | } 71 | else { 72 | Serial.print("MeasureCallback: Min distance=");Serial.println(distance); 73 | } 74 | } 75 | 76 | void MeasureDisable() { 77 | Serial.println("MeasureDisable: Cleaning up"); 78 | 79 | tSensor1.disable(); 80 | tSensor2.disable(); 81 | tSensor3.disable(); 82 | } 83 | 84 | 85 | void CalcCallback() { 86 | Serial.println("CalcCallback: calculating"); 87 | distance = -1; 88 | if ( measure.getStatus() >= 0) { // only calculate if statusrequest ended successfully 89 | distance = d1 < d2 ? d1 : d2; 90 | distance = d3 < distance ? d3 : distance; 91 | tMeasure.forceNextIteration(); 92 | } 93 | } 94 | 95 | 96 | /** Simulation code for sensor 1 97 | * ---------------------------- 98 | */ 99 | bool S1Enable() { 100 | Serial.print("S1Enable: Triggering sensor1. Delay="); 101 | 102 | tSensor1.setInterval( random(1200) ); // Simulating sensor delay, which could go over 1 second and cause timeout 103 | d1 = 0; 104 | 105 | Serial.println( tSensor1.getInterval() ); 106 | return true; 107 | } 108 | 109 | void S1Callback() { 110 | Serial.print("S1Callback: Emulating measurement. d1="); 111 | d1 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement 112 | measure.signal(); 113 | 114 | Serial.println(d1); 115 | } 116 | 117 | 118 | /** Simulation code for sensor 2 119 | * ---------------------------- 120 | */ 121 | bool S2Enable() { 122 | Serial.print("S2Enable: Triggering sensor2. Delay="); 123 | 124 | tSensor2.setInterval( random(1200) ); // Simulating sensor delay, which could go over 1 second and cause timeout 125 | d2 = 0; 126 | 127 | Serial.println( tSensor2.getInterval() ); 128 | return true; 129 | } 130 | 131 | void S2Callback() { 132 | Serial.print("S2Callback: Emulating measurement. d2="); 133 | d2 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement 134 | measure.signal(); 135 | 136 | Serial.println(d2); 137 | } 138 | 139 | 140 | /** Simulation code for sensor 3 141 | * ---------------------------- 142 | */ 143 | bool S3Enable() { 144 | Serial.print("S3Enable: Triggering sensor3. Delay="); 145 | 146 | tSensor3.setInterval( random(1200) ); // Simulating sensor delay, which could go over 1 second and cause timeout 147 | d3 = 0; 148 | 149 | Serial.println( tSensor3.getInterval() ); 150 | return true; 151 | } 152 | 153 | void S3Callback() { 154 | Serial.print("S3Callback: Emulating measurement. d3="); 155 | d3 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement 156 | measure.signal(); 157 | 158 | Serial.println(d3); 159 | } 160 | 161 | 162 | /** Main Arduino code 163 | * Not much is left here - everything is taken care of by the framework 164 | */ 165 | void setup() { 166 | 167 | Serial.begin(115200); 168 | Serial.println("TaskScheduler StatusRequest Sensor Emulation Test. Complex Test."); 169 | 170 | #ifdef ARDUINO_ARCH_STM32F1 171 | pinMode(A0, INPUT_ANALOG); 172 | #endif 173 | 174 | randomSeed(analogRead(A0)+millis()); 175 | } 176 | 177 | void loop() { 178 | 179 | ts.execute(); 180 | 181 | } 182 | -------------------------------------------------------------------------------- /examples/Scheduler_example06_IDLE/Scheduler_example06_IDLE.ino: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This is a test to prove that processor really goes into IDLE sleep. 4 | * For this setup: 5 | * 6 | * 7 | 8 | Task c(10, -1, &Count, &ts); 9 | Task t(10000, 1, NULL, &ts, true, &tOn, &tOff); 10 | 11 | The result are: 12 | 13 | 1): With #define _TASK_SLEEP_ON_IDLE_RUN enabled 14 | On Arduino Uno: 15 | Start 16 | c1=10771 - v2.5.0 (v1.9.0: same) 17 | c2=1001 18 | 19 | On Teensy 3.5 (120MHz ARM): 20 | Start 21 | c1=21065 22 | c2=1001 23 | 24 | On esp8266 (80 MHz) 25 | Start 26 | c1=10492 27 | c2=1001 28 | 29 | On STM32F103RCBT6 (Maple Mini @72 MHz) 30 | Start 31 | c1=21004 32 | c2=1001 33 | 34 | and 35 | 36 | 2): With #define _TASK_SLEEP_ON_IDLE_RUN disabled (commented out) 37 | Arduino Uno: 38 | Start 39 | c1=722426 - v3.0.2 40 | c1=635735 - v2.5.0 41 | c1=551947 - v1.9.0 42 | c2=1001 43 | 44 | On Teensy 3.5 (120MHz ARM): 45 | Start 46 | c1=2690322 47 | c2=1001 48 | 49 | On esp8266 (80 MHz) 50 | Start 51 | c1=351085 (689833 at 160Mhz) 52 | c2=1001 53 | 54 | On STM32F103RCBT6 (Maple Mini @72 MHz) 55 | Start 56 | c1=4665019 57 | c2=1001 58 | 59 | C1 in scenario 2) is much higher than in scenario 1) because processor is put to sleep for 1), but not for 2) 60 | 61 | */ 62 | 63 | 64 | /** 65 | * Compile and run once with _TASK_SLEEP_ON_IDLE_RUN enabled, then with _TASK_SLEEP_ON_IDLE_RUN disabled. 66 | * Compare the results. 67 | */ 68 | 69 | //#define _TASK_SLEEP_ON_IDLE_RUN 70 | #include 71 | 72 | Scheduler ts; 73 | 74 | // Callback methods prototypes 75 | void Count(); 76 | bool tOn(); void tOff(); 77 | 78 | // Tasks 79 | Task c(10, TASK_FOREVER, &Count, &ts); 80 | Task t(10000, TASK_ONCE, NULL, &ts, true, &tOn, &tOff); 81 | 82 | 83 | volatile unsigned long c1, c2; 84 | bool tOn() { 85 | c1 = 0; 86 | c2 = 0; 87 | c.enable(); 88 | 89 | return true; 90 | } 91 | 92 | void tOff() { 93 | c.disable(); 94 | Serial.print("c1=");Serial.println(c1); 95 | Serial.print("c2=");Serial.println(c2); 96 | } 97 | 98 | void setup() { 99 | // put your setup code here, to run once: 100 | Serial.begin(115200); 101 | delay(1000); 102 | Serial.println("Start"); 103 | 104 | ts.startNow(); 105 | t.delay(); 106 | } 107 | 108 | void Count() { 109 | c2++; 110 | } 111 | 112 | 113 | void loop() { 114 | // put your main code here, to run repeatedly: 115 | ts.execute(); 116 | c1++; 117 | } -------------------------------------------------------------------------------- /examples/Scheduler_example07_WDT/Scheduler_example07_WDT.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * TaskScheduler Test sketch - use of task IDs and watchdog timer to identify hung tasks 3 | * THIS SKETCH RUNS ON AVR BOARDS ONLY 4 | * Test case: 5 | * Watchdog timer is set to 2 seconds (interrupt + reset) 6 | * A hearbeat task (resetting the watchdog timer) is scheduled with 500 ms interval 7 | * A number of tasks are running every 1 second and "rolling the dice" 0..19. If 5, task is made to enter infinite loop 8 | * Device should reset in 2 seconds after a task enters infinite loop 9 | * A task id and a control point number are saved to EEPROM prior to device reset, and are displayed after reboot. 10 | * In real life, device might chose to NOT activate certain tasks which failed previously (failed sensors for instance) 11 | */ 12 | 13 | #define _TASK_SLEEP_ON_IDLE_RUN 14 | #define _TASK_WDT_IDS 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | Scheduler ts; 21 | 22 | // Callback methods prototypes 23 | void TaskCB(); 24 | void HB(); bool HBOn(); void HBOff(); 25 | 26 | // Three tasks emulating accidental infinite loop 27 | Task tTask1(TASK_SECOND, TASK_FOREVER, &TaskCB, &ts, true); 28 | Task tTask2(TASK_SECOND, TASK_FOREVER, &TaskCB, &ts, true); 29 | Task tTask3(TASK_SECOND, TASK_FOREVER, &TaskCB, &ts, true); 30 | 31 | // Heartbeat task - resetting the watchdog timer periodically 32 | // Initiates WDT on enable, and deactivates it on disable 33 | Task tHB(500, TASK_FOREVER, &HB, &ts, false, &HBOn, &HBOff); 34 | 35 | /** 36 | * Emulating task callback function 37 | * Prints task id and randomly "hangs" in two places. 38 | * Control points are stored on the task prior to section which might hang, 39 | * making this information available to the WDT interrupt handler 40 | */ 41 | void TaskCB() { 42 | Task& T = ts.currentTask(); 43 | 44 | Serial.print("Task #:"); 45 | Serial.print(T.getId()); 46 | Serial.print(" current iteration = "); 47 | Serial.println(T.getRunCounter()); 48 | 49 | // Hang if random number between 0 and 19 is 5 (5% probability) 50 | T.setControlPoint(10); 51 | if (random(20) == 5) for(;;); 52 | 53 | // Hang if random number between 0 and 99 is more that 95 (5% probability) 54 | T.setControlPoint(95); 55 | if (random(100) > 94) for(;;); 56 | } 57 | 58 | /** 59 | * This On Enable method sets up the WDT 60 | * for interrupt and reset after 2 seconds 61 | */ 62 | bool HBOn() { 63 | 64 | //disable interrupts 65 | cli(); 66 | //reset watchdog 67 | wdt_reset(); 68 | //set up WDT interrupt 69 | WDTCSR = (1<id); 85 | Serial.print("Sum: "); Serial.println(vars[i]->sum); 86 | Serial.print("Product: "); Serial.println(vars[i]->product); 87 | Serial.println(); 88 | } 89 | } 90 | 91 | 92 | /** 93 | * This method is executed when each calculator task is enabled 94 | * The purpose is to initiate all local variables 95 | */ 96 | bool CalcOn() { 97 | Task& T = ts.currentTask(); 98 | task_var& var = *((task_var*) T.getLtsPointer()); 99 | 100 | // Initialize local variables 101 | var.id = T.getId(); 102 | var.sum = 0; 103 | var.product = var.id; 104 | 105 | return true; 106 | } 107 | 108 | 109 | /** 110 | * This method performs simple calculations on task's local variables 111 | */ 112 | void Calculate() { 113 | Task& T = ts.currentTask(); 114 | // Another way to get to LTS pointer: 115 | task_var& var = *((task_var*) ts.currentLts()); 116 | 117 | 118 | Serial.print("Calculating for task: "); 119 | Serial.print(T.getId()); 120 | Serial.print("; Task id per LTS is: "); 121 | Serial.println( var.id ); 122 | 123 | var.sum += T.getId(); 124 | var.product = var.product * 10; 125 | 126 | } 127 | 128 | 129 | /** 130 | * Standard Arduino setup and loop methods 131 | */ 132 | void setup() { 133 | Serial.begin(115200); 134 | 135 | randomSeed(analogRead(0)+analogRead(5)); 136 | 137 | pinMode(13, OUTPUT); 138 | digitalWrite(13, LOW); 139 | 140 | Serial.println("Local Task Storage pointer test"); 141 | 142 | tWrapper.enableDelayed(); 143 | } 144 | 145 | void loop() { 146 | ts.execute(); 147 | } -------------------------------------------------------------------------------- /examples/Scheduler_example09_TimeCritical/Scheduler_example09_TimeCritical.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * TaskScheduler Test 3 | * Illustration of use of Time Critical Information 4 | * 5 | * Task1 runs every 1 second indefinitely 6 | * On each run it reports how delayed the invokation of the callback method was, 7 | * and what was the scheduling overun. 8 | * Each run task 1 is dealyed randomly for up to 2 seconds, thus simulating scheduling overrun 9 | */ 10 | 11 | #define _TASK_TIMECRITICAL 12 | #define _TASK_SLEEP_ON_IDLE_RUN 13 | #include 14 | 15 | // Callback methods prototypes 16 | void t1Callback(); 17 | 18 | 19 | //Tasks 20 | Task t1(1000, -1, &t1Callback); 21 | 22 | Scheduler runner; 23 | 24 | 25 | void t1Callback() { 26 | Serial.print(millis()); 27 | Serial.print(": overrun = "); 28 | Serial.print(t1.getOverrun()); 29 | Serial.print(", start delayed by "); 30 | Serial.println(t1.getStartDelay()); 31 | 32 | int i = random(2000); 33 | Serial.print("Delaying for "); Serial.println(i); 34 | delay(i); 35 | } 36 | 37 | void setup () { 38 | Serial.begin(115200); 39 | Serial.println("Scheduler TimeCritical TEST"); 40 | 41 | runner.init(); 42 | Serial.println("Initialized scheduler"); 43 | 44 | runner.addTask(t1); 45 | Serial.println("added t1. Waiting for 5 seconds."); 46 | 47 | delay(5000); 48 | 49 | t1.enable(); 50 | 51 | Serial.println("Enabled t1"); 52 | } 53 | 54 | 55 | void loop () { 56 | runner.execute(); 57 | } 58 | -------------------------------------------------------------------------------- /examples/Scheduler_example10_Benchmark/Scheduler_example10_Benchmark.ino: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This is a test to benchmark TaskScheduler execution. 4 | * 5 | * This test executes 1,000,000 cycles of a task with empty callback method 6 | * Compiled with different options, you can assess the impact of each on the size of the Task object 7 | * and the execution overhead of the main execution pass route. 8 | * 9 | * Sample execution times (in milliseconds per 1M iterations) are provided below. 10 | * The test board is Arduino UNO 16MHz processor. 11 | * 12 | 13 | TaskScheduler 2.1.0: 14 | No modifiers 15 | Duration=19869 16 | 17 | with SLEEP 18 | Duration=20058 19 | 20 | with status request: 21 | Duration=20058 22 | 23 | with time critical: 24 | Duration=27289 25 | 26 | 27 | TaskScheduler 1.9.0: 28 | No modifiers 29 | Duration=15656 30 | 31 | with SLEEP 32 | Duration=16285 33 | 34 | with status request: 35 | Duration=16600 36 | 37 | with rollover fix: 38 | Duration=18109 39 | 40 | 41 | TaskScheduler 1.8.5: 42 | Duration=15719 43 | 44 | with SLEEP 45 | Duration=16348 46 | 47 | with status request: 48 | Duration=18360 49 | 50 | with rollover fix: 51 | Duration=18423 52 | 53 | */ 54 | 55 | 56 | //#define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns 57 | //#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only 58 | //#define _TASK_WDT_IDS // Compile with support for wdt control points and task ids 59 | //#define _TASK_LTS_POINTER // Compile with support for local task storage pointer 60 | //#define _TASK_SLEEP_ON_IDLE_RUN 61 | //#define _TASK_MICRO_RES 62 | #include 63 | 64 | Scheduler ts; 65 | 66 | // Callback methods prototypes 67 | bool tOn(); void tOff(); 68 | void callback(); 69 | 70 | // Tasks 71 | Task t(TASK_IMMEDIATE, 1000000, &callback, &ts, false, &tOn, &tOff); 72 | 73 | unsigned long c1, c2; 74 | 75 | bool tOn() { 76 | c1 = millis(); 77 | c2 = 0; 78 | 79 | return true; 80 | } 81 | 82 | void tOff() { 83 | c2 = millis(); 84 | Serial.println("done."); 85 | Serial.print("Tstart =");Serial.println(c1); 86 | Serial.print("Tfinish=");Serial.println(c2); 87 | Serial.print("Duration=");Serial.println(c2-c1); 88 | } 89 | 90 | void setup() { 91 | // put your setup code here, to run once: 92 | Serial.begin(115200); 93 | Serial.print("Start..."); 94 | 95 | t.enable(); 96 | } 97 | 98 | void callback() { 99 | 100 | } 101 | 102 | 103 | void loop() { 104 | // put your main code here, to run repeatedly: 105 | ts.execute(); 106 | } 107 | -------------------------------------------------------------------------------- /examples/Scheduler_example11_Priority/Scheduler_example11_Priority.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a test of TaskScheduler layered priority funtionality 3 | * 4 | * Current test employs two priority layers: 5 | * Base scheduler runs tasks t1, t2 and t3 6 | * High priority scheduler runs tasks t4 and t5 7 | * 8 | * Sequence of task scheduling (not execution!) is: 9 | * 4, 5, 1, 4, 5, 2, 4, 5, 3 = one base scheduler pass 10 | * 11 | * Scheduling overhead (at 20 micros per one pass) is: (B + B * H) * T = (3 + 3 * 2) * 18 = 162 micros 12 | * where 13 | * B - number of tasks in the base scheduler's chain 14 | * H - number of tasks in the high priority scheduler's chain 15 | * T - scheduling overhead for 1 pass (~15-18 microseconds) 16 | * 17 | * Actual task execution order: 18 | 19 | Scheduler Priority Test 20 | Task: 40: 0 Start delay = 0 21 | Task: 50: 10 Start delay = 10 22 | Task: 1: 21 Start delay = 21 23 | Task: 2: 31 Start delay = 31 24 | Task: 3: 41 Start delay = 41 25 | 26 | Task: 40: 500 Start delay = 0 27 | Task: 40: 1000 Start delay = 0 28 | Task: 50: 1010 Start delay = 10 29 | Task: 1: 1021 Start delay = 20 30 | Task: 40: 1500 Start delay = 0 31 | Task: 40: 2000 Start delay = 0 32 | Task: 50: 2011 Start delay = 11 33 | Task: 1: 2022 Start delay = 21 34 | Task: 2: 2032 Start delay = 32 35 | Task: 40: 2500 Start delay = 0 36 | Task: 40: 3000 Start delay = 0 37 | Task: 50: 3010 Start delay = 10 38 | Task: 1: 3021 Start delay = 20 39 | Task: 3: 3032 Start delay = 32 40 | 41 | Task: 40: 3500 Start delay = 0 42 | Task: 40: 4000 Start delay = 0 43 | Task: 50: 4011 Start delay = 11 44 | Task: 1: 4022 Start delay = 21 45 | Task: 2: 4032 Start delay = 32 46 | Task: 40: 4500 Start delay = 0 47 | Task: 40: 5000 Start delay = 0 48 | Task: 50: 5010 Start delay = 10 49 | Task: 1: 5021 Start delay = 20 50 | Task: 40: 5500 Start delay = 0 51 | Task: 40: 6000 Start delay = 0 52 | Task: 50: 6010 Start delay = 10 53 | Task: 1: 6022 Start delay = 21 54 | Task: 2: 6032 Start delay = 32 55 | Task: 3: 6043 Start delay = 42 56 | 57 | */ 58 | 59 | #define _TASK_SLEEP_ON_IDLE_RUN 60 | #define _TASK_PRIORITY 61 | #define _TASK_WDT_IDS 62 | #define _TASK_TIMECRITICAL 63 | #include 64 | 65 | Scheduler r, hpr; 66 | 67 | // Callback methods prototypes 68 | void tCallback(); 69 | 70 | // Tasks 71 | Task t1(1000, TASK_FOREVER, &tCallback, &r); //adding task to the chain on creation 72 | Task t2(2000, TASK_FOREVER, &tCallback, &r); 73 | Task t3(3000, TASK_FOREVER, &tCallback, &r); 74 | 75 | Task t4(500, TASK_FOREVER, &tCallback, &hpr); //adding task to the chain on creation 76 | Task t5(1000, TASK_FOREVER, &tCallback, &hpr); //adding task to the chain on creation 77 | 78 | void tCallback() { 79 | Scheduler &s = Scheduler::currentScheduler(); 80 | Task &t = s.currentTask(); 81 | 82 | Serial.print("Task: "); Serial.print(t.getId());Serial.print(":\t"); 83 | Serial.print(millis()); Serial.print("\tStart delay = "); Serial.println(t.getStartDelay()); 84 | delay(10); 85 | 86 | if (t.getId() == 3) Serial.println(); 87 | } 88 | 89 | void setup () { 90 | Serial.begin(115200); 91 | Serial.println("Scheduler Priority Test"); 92 | 93 | t4.setId(40); 94 | t5.setId(50); 95 | 96 | r.setHighPriorityScheduler(&hpr); 97 | r.enableAll(true); // this will recursively enable the higher priority tasks as well 98 | } 99 | 100 | 101 | void loop () { 102 | 103 | r.execute(); 104 | 105 | } 106 | -------------------------------------------------------------------------------- /examples/Scheduler_example12_Priority/Scheduler_example12_Priority.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a test of TaskScheduler layered priority funtionality 3 | * 4 | * Current test employs three priority layers: 5 | * Base scheduler runs tasks t1, t2 and t3 6 | * High priority scheduler runs tasks t4 and t5 7 | * Highest priority scheduler runs tasks t6 and t7 8 | * 9 | * Sequence of task scheduling (not execution!) is: 10 | * 6, 7, 4, 6, 7, 5, 1, 6, 7, 4, 6, 7, 5, 2, 6, 7, 4, 6, 7, 5, 3 = one base scheduler pass 11 | * 12 | * Scheduling overhead (at 20 micros per one pass) is: (B + B * H + B * H * C) * T = (3 + 3 * 2 + 3 * 2 * 2) * 18 = 378 micros 13 | * where 14 | * B - number of tasks in the base scheduler's chain 15 | * H - number of tasks in the high priority scheduler's chain 16 | * C - number of tasks in the critical priority scheduler's chain 17 | * T - scheduling overhead for 1 pass (~15-18 microseconds) 18 | * 19 | * Actual task execution order: 20 | 21 | Scheduler Priority Test 22 | Task: 600: 0 Start delay = 0 23 | Task: 700: 10 Start delay = 10 24 | Task: 40: 21 Start delay = 21 25 | Task: 50: 31 Start delay = 31 26 | Task: 1: 43 Start delay = 41 27 | Task: 2: 53 Start delay = 53 28 | Task: 3: 63 Start delay = 63 29 | 30 | Task: 600: 500 Start delay = 0 31 | Task: 40: 510 Start delay = 10 32 | Task: 600: 1000 Start delay = 0 33 | Task: 700: 1010 Start delay = 10 34 | Task: 40: 1021 Start delay = 21 35 | Task: 50: 1032 Start delay = 32 36 | Task: 1: 1043 Start delay = 43 37 | Task: 600: 1500 Start delay = 0 38 | Task: 40: 1510 Start delay = 10 39 | Task: 600: 2000 Start delay = 0 40 | Task: 700: 2011 Start delay = 11 41 | Task: 40: 2022 Start delay = 22 42 | Task: 50: 2032 Start delay = 32 43 | Task: 1: 2043 Start delay = 43 44 | Task: 2: 2054 Start delay = 54 45 | Task: 600: 2500 Start delay = 0 46 | Task: 40: 2510 Start delay = 10 47 | Task: 600: 3000 Start delay = 0 48 | Task: 700: 3010 Start delay = 10 49 | Task: 40: 3021 Start delay = 21 50 | Task: 50: 3032 Start delay = 32 51 | Task: 1: 3043 Start delay = 43 52 | Task: 3: 3053 Start delay = 53 53 | 54 | Task: 600: 3500 Start delay = 0 55 | Task: 40: 3510 Start delay = 10 56 | Task: 600: 4000 Start delay = 0 57 | Task: 700: 4011 Start delay = 11 58 | Task: 40: 4022 Start delay = 22 59 | Task: 50: 4032 Start delay = 32 60 | Task: 1: 4043 Start delay = 43 61 | Task: 2: 4054 Start delay = 54 62 | Task: 600: 4500 Start delay = 0 63 | Task: 40: 4510 Start delay = 10 64 | Task: 600: 5000 Start delay = 0 65 | Task: 700: 5010 Start delay = 10 66 | Task: 40: 5021 Start delay = 21 67 | Task: 50: 5031 Start delay = 31 68 | Task: 1: 5043 Start delay = 43 69 | Task: 600: 5500 Start delay = 0 70 | Task: 40: 5511 Start delay = 11 71 | Task: 600: 6000 Start delay = 0 72 | Task: 700: 6010 Start delay = 10 73 | Task: 40: 6022 Start delay = 22 74 | Task: 50: 6032 Start delay = 32 75 | Task: 1: 6043 Start delay = 43 76 | Task: 2: 6053 Start delay = 53 77 | Task: 3: 6065 Start delay = 65 78 | 79 | */ 80 | 81 | #define _TASK_SLEEP_ON_IDLE_RUN 82 | #define _TASK_PRIORITY 83 | #define _TASK_WDT_IDS 84 | #define _TASK_TIMECRITICAL 85 | #include 86 | 87 | Scheduler r; 88 | Scheduler hpr; 89 | Scheduler cpr; 90 | 91 | // Callback methods prototypes 92 | void tCallback(); 93 | 94 | // Tasks 95 | Task t1(1000, TASK_FOREVER, &tCallback, &r); //adding task to the chain on creation 96 | Task t2(2000, TASK_FOREVER, &tCallback, &r); 97 | Task t3(3000, TASK_FOREVER, &tCallback, &r); 98 | 99 | Task t4(500, TASK_FOREVER, &tCallback, &hpr); //adding task to the chain on creation 100 | Task t5(1000, TASK_FOREVER, &tCallback, &hpr); //adding task to the chain on creation 101 | 102 | Task t6(500, TASK_FOREVER, &tCallback, &cpr); //adding task to the chain on creation 103 | Task t7(1000, TASK_FOREVER, &tCallback, &cpr); //adding task to the chain on creation 104 | 105 | void tCallback() { 106 | Scheduler &s = Scheduler::currentScheduler(); 107 | Task &t = s.currentTask(); 108 | 109 | Serial.print("Task: "); Serial.print(t.getId());Serial.print(":\t"); 110 | Serial.print(millis()); Serial.print("\tStart delay = "); Serial.println(t.getStartDelay()); 111 | delay(10); 112 | 113 | if (t.getId() == 3) Serial.println(); 114 | } 115 | 116 | void setup () { 117 | Serial.begin(115200); 118 | Serial.println("Scheduler Priority Test"); 119 | 120 | t4.setId(40); 121 | t5.setId(50); 122 | 123 | t6.setId(600); 124 | t7.setId(700); 125 | 126 | r.setHighPriorityScheduler(&hpr); 127 | hpr.setHighPriorityScheduler(&cpr); 128 | r.enableAll(true); // this will recursively enable the higher priority tasks as well 129 | } 130 | 131 | 132 | void loop () { 133 | 134 | r.execute(); 135 | 136 | } 137 | -------------------------------------------------------------------------------- /examples/Scheduler_example13_Micros/Scheduler_example13_Micros.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * TaskScheduler Test of microsecond scheduling resolution 3 | * 4 | * Task 1 runs starting with 211 microseconds intervals, doubling the interval on every iteration 5 | * until it wraps when interval reaches about 72 minutes mark 6 | * 7 | * Task 2 provides heartbeat at a steady 5 seconds intervals 8 | * 9 | */ 10 | 11 | #define _TASK_MICRO_RES 12 | #include 13 | 14 | #define T1_INIT (211L) 15 | 16 | Scheduler runner; 17 | // Callback methods prototypes 18 | void t1Callback(); 19 | void t1OnDisable(); 20 | void t2Callback(); 21 | 22 | 23 | unsigned long t1_interval = T1_INIT; 24 | 25 | // Tasks 26 | Task t1(t1_interval, 1, &t1Callback, &runner, true, NULL, &t1OnDisable); //adding task to the chain on creation 27 | Task t2(5 * TASK_SECOND, TASK_FOREVER, &t2Callback, &runner, true); //adding task to the chain on creation 28 | 29 | 30 | 31 | void t1Callback() { 32 | unsigned long t = micros(); 33 | Serial.print("t1: "); 34 | Serial.println(t); 35 | } 36 | 37 | void t1OnDisable() { 38 | t1_interval += t1_interval; 39 | if (t1_interval < T1_INIT) t1_interval = T1_INIT; 40 | t1.setInterval(t1_interval); 41 | t1.restartDelayed(); 42 | } 43 | 44 | void t2Callback() { 45 | unsigned long t = micros(); 46 | Serial.print("t2: "); 47 | Serial.print(t); 48 | Serial.println(" heartbeat"); 49 | } 50 | 51 | 52 | void setup () { 53 | Serial.begin(115200); 54 | Serial.println("Scheduler TEST Microsecond Resolution"); 55 | 56 | Serial.println("5 seconds delay"); 57 | delay(5000); 58 | 59 | runner.startNow(); // This creates a new scheduling starting point for all ACTIVE tasks. 60 | // PLEASE NOTE - THIS METHOD DOES NOT ACTIVATE TASKS, JUST RESETS THE START TIME 61 | t1.delay(); // Tasks which need to start delayed, need to be delayed again after startNow(); 62 | 63 | // Alternatively, tasks should be just enabled at the bottom of setup() method 64 | // runner.enableAll(); 65 | // t1.delay(); 66 | } 67 | 68 | 69 | void loop () { 70 | runner.execute(); 71 | } 72 | -------------------------------------------------------------------------------- /examples/Scheduler_example14_Yield/Scheduler_example14_Yield.ino: -------------------------------------------------------------------------------- 1 | /** 2 | This test illustrates the use if yield methods and internal StatusRequest objects 3 | THIS TEST HAS BEEN TESTED ON NODEMCU V.2 (ESP8266) 4 | 5 | The WiFi initialization and NTP update is executed in parallel to blinking the onboard LED 6 | and an external LED connected to D2 (GPIO04) 7 | Try running with and without correct WiFi parameters to observe the difference in behaviour 8 | */ 9 | 10 | #define _TASK_SLEEP_ON_IDLE_RUN 11 | #define _TASK_STATUS_REQUEST 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | Scheduler ts; 18 | 19 | // Callback methods prototypes 20 | void connectInit(); 21 | void ledCallback(); 22 | bool ledOnEnable(); 23 | void ledOnDisable(); 24 | void ledOn(); 25 | void ledOff(); 26 | void ntpUpdateInit(); 27 | 28 | // Tasks 29 | 30 | Task tConnect (TASK_SECOND, TASK_FOREVER, &connectInit, &ts, true); 31 | Task tLED (TASK_IMMEDIATE, TASK_FOREVER, &ledCallback, &ts, false, &ledOnEnable, &ledOnDisable); 32 | 33 | // Tasks running on events 34 | Task tNtpUpdate (&ntpUpdateInit, &ts); 35 | 36 | // Replace with WiFi parameters of your Access Point/Router: 37 | const char *ssid = "wifi_network"; 38 | const char *pwd = "wifi_password"; 39 | 40 | long ledDelayOn, ledDelayOff; 41 | 42 | #define LEDPIN D0 // Onboard LED pin - linked to WiFi 43 | #define LEDPIN2 D2 // External LED 44 | #define CONNECT_TIMEOUT 30 // Seconds 45 | #define CONNECT_OK 0 // Status of successful connection to WiFi 46 | #define CONNECT_FAILED (-99) // Status of failed connection to WiFi 47 | 48 | // NTP Related Definitions 49 | #define NTP_PACKET_SIZE 48 // NTP time stamp is in the first 48 bytes of the message 50 | 51 | IPAddress timeServerIP; // time.nist.gov NTP server address 52 | const char* ntpServerName = "time.nist.gov"; 53 | byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets 54 | unsigned long epoch; 55 | 56 | WiFiUDP udp; // A UDP instance to let us send and receive packets over UDP 57 | 58 | #define LOCAL_NTP_PORT 2390 // Local UDP port for NTP update 59 | 60 | 61 | 62 | void setup() { 63 | Serial.begin(74880); 64 | Serial.println(F("TaskScheduler test #14 - Yield and internal StatusRequests")); 65 | Serial.println(F("==========================================================")); 66 | Serial.println(); 67 | 68 | pinMode (LEDPIN, OUTPUT); 69 | pinMode (LEDPIN2, OUTPUT); 70 | 71 | tNtpUpdate.waitFor( tConnect.getInternalStatusRequest() ); // NTP Task will start only after connection is made 72 | } 73 | 74 | void loop() { 75 | ts.execute(); // Only Scheduler should be executed in the loop 76 | } 77 | 78 | /** 79 | Initiate connection to the WiFi network 80 | */ 81 | void connectInit() { 82 | Serial.print(millis()); 83 | Serial.println(F(": connectInit.")); 84 | Serial.println(F("WiFi parameters: ")); 85 | Serial.print(F("SSID: ")); Serial.println(ssid); 86 | Serial.print(F("PWD : ")); Serial.println(pwd); 87 | 88 | WiFi.mode(WIFI_STA); 89 | WiFi.hostname("esp8266"); 90 | WiFi.begin(ssid, pwd); 91 | yield(); 92 | 93 | ledDelayOn = TASK_SECOND / 2; 94 | ledDelayOff = TASK_SECOND / 4; 95 | tLED.enable(); 96 | 97 | tConnect.yield(&connectCheck); // This will pass control back to Scheduler and then continue with connection checking 98 | } 99 | 100 | /** 101 | Periodically check if connected to WiFi 102 | Re-request connection every 5 seconds 103 | Stop trying after a timeout 104 | */ 105 | void connectCheck() { 106 | Serial.print(millis()); 107 | Serial.println(F(": connectCheck.")); 108 | 109 | if (WiFi.status() == WL_CONNECTED) { // Connection established 110 | Serial.print(millis()); 111 | Serial.print(F(": Connected to AP. Local ip: ")); 112 | Serial.println(WiFi.localIP()); 113 | tConnect.disable(); 114 | } 115 | else { 116 | 117 | if (tConnect.getRunCounter() % 5 == 0) { // re-request connection every 5 seconds 118 | 119 | Serial.print(millis()); 120 | Serial.println(F(": Re-requesting connection to AP...")); 121 | 122 | WiFi.disconnect(true); 123 | yield(); // This is an esp8266 standard yield to allow linux wifi stack run 124 | WiFi.hostname("esp8266"); 125 | WiFi.mode(WIFI_STA); 126 | WiFi.begin(ssid, pwd); 127 | yield(); // This is an esp8266 standard yield to allow linux wifi stack run 128 | } 129 | 130 | if (tConnect.getRunCounter() == CONNECT_TIMEOUT) { // Connection Timeout 131 | tConnect.getInternalStatusRequest()->signal(CONNECT_FAILED); // Signal unsuccessful completion 132 | tConnect.disable(); 133 | 134 | Serial.print(millis()); 135 | Serial.println(F(": connectOnDisable.")); 136 | Serial.print(millis()); 137 | Serial.println(F(": Unable to connect to WiFi.")); 138 | 139 | ledDelayOn = TASK_SECOND / 16; // Blink LEDs quickly due to error 140 | ledDelayOff = TASK_SECOND / 16; 141 | tLED.enable(); 142 | } 143 | } 144 | } 145 | 146 | /** 147 | Initiate NTP update if connection was established 148 | */ 149 | void ntpUpdateInit() { 150 | Serial.print(millis()); 151 | Serial.println(F(": ntpUpdateInit.")); 152 | 153 | if ( tConnect.getInternalStatusRequest()->getStatus() != CONNECT_OK ) { // Check status of the Connect Task 154 | Serial.print(millis()); 155 | Serial.println(F(": cannot update NTP - not connected.")); 156 | return; 157 | } 158 | 159 | udp.begin(LOCAL_NTP_PORT); 160 | if ( WiFi.hostByName(ntpServerName, timeServerIP) ) { //get a random server from the pool 161 | 162 | Serial.print(millis()); 163 | Serial.print(F(": timeServerIP = ")); 164 | Serial.println(timeServerIP); 165 | 166 | sendNTPpacket(timeServerIP); // send an NTP packet to a time server 167 | } 168 | else { 169 | Serial.print(millis()); 170 | Serial.println(F(": NTP server address lookup failed.")); 171 | tLED.disable(); 172 | udp.stop(); 173 | tNtpUpdate.disable(); 174 | return; 175 | } 176 | 177 | ledDelayOn = TASK_SECOND / 8; 178 | ledDelayOff = TASK_SECOND / 8; 179 | tLED.enable(); 180 | 181 | tNtpUpdate.set( TASK_SECOND, CONNECT_TIMEOUT, &ntpCheck ); 182 | tNtpUpdate.enableDelayed(); 183 | } 184 | 185 | /** 186 | * Check if NTP packet was received 187 | * Re-request every 5 seconds 188 | * Stop trying after a timeout 189 | */ 190 | void ntpCheck() { 191 | Serial.print(millis()); 192 | Serial.println(F(": ntpCheck.")); 193 | 194 | if ( tNtpUpdate.getRunCounter() % 5 == 0) { 195 | 196 | Serial.print(millis()); 197 | Serial.println(F(": Re-requesting NTP update...")); 198 | 199 | udp.stop(); 200 | yield(); 201 | udp.begin(LOCAL_NTP_PORT); 202 | sendNTPpacket(timeServerIP); 203 | return; 204 | } 205 | 206 | if ( doNtpUpdateCheck()) { 207 | Serial.print(millis()); 208 | Serial.println(F(": NTP Update successful")); 209 | 210 | Serial.print(millis()); 211 | Serial.print(F(": Unix time = ")); 212 | Serial.println(epoch); 213 | 214 | tLED.disable(); 215 | tNtpUpdate.disable(); 216 | udp.stop(); 217 | } 218 | else { 219 | if ( tNtpUpdate.isLastIteration() ) { 220 | Serial.print(millis()); 221 | Serial.println(F(": NTP Update failed")); 222 | tLED.disable(); 223 | udp.stop(); 224 | } 225 | } 226 | } 227 | 228 | /** 229 | * Send NTP packet to NTP server 230 | */ 231 | unsigned long sendNTPpacket(IPAddress & address) 232 | { 233 | Serial.print(millis()); 234 | Serial.println(F(": sendNTPpacket.")); 235 | 236 | // set all bytes in the buffer to 0 237 | memset(packetBuffer, 0, NTP_PACKET_SIZE); 238 | // Initialize values needed to form NTP request 239 | // (see URL above for details on the packets) 240 | packetBuffer[0] = 0b11100011; // LI, Version, Mode 241 | packetBuffer[1] = 0; // Stratum, or type of clock 242 | packetBuffer[2] = 6; // Polling Interval 243 | packetBuffer[3] = 0xEC; // Peer Clock Precision 244 | // 8 bytes of zero for Root Delay & Root Dispersion 245 | packetBuffer[12] = 49; 246 | packetBuffer[13] = 0x4E; 247 | packetBuffer[14] = 49; 248 | packetBuffer[15] = 52; 249 | 250 | // all NTP fields have been given values, now 251 | // you can send a packet requesting a timestamp: 252 | udp.beginPacket(address, 123); //NTP requests are to port 123 253 | udp.write(packetBuffer, NTP_PACKET_SIZE); 254 | udp.endPacket(); 255 | yield(); 256 | } 257 | 258 | /** 259 | * Check if a packet was recieved. 260 | * Process NTP information if yes 261 | */ 262 | bool doNtpUpdateCheck() { 263 | 264 | Serial.print(millis()); 265 | Serial.println(F(": doNtpUpdateCheck.")); 266 | 267 | yield(); 268 | int cb = udp.parsePacket(); 269 | if (cb) { 270 | // We've received a packet, read the data from it 271 | udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer 272 | 273 | //the timestamp starts at byte 40 of the received packet and is four bytes, 274 | // or two words, long. First, esxtract the two words: 275 | 276 | unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); 277 | unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); 278 | // combine the four bytes (two words) into a long integer 279 | // this is NTP time (seconds since Jan 1 1900): 280 | unsigned long secsSince1900 = highWord << 16 | lowWord; 281 | 282 | // now convert NTP time into everyday time: 283 | // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: 284 | const unsigned long seventyYears = 2208988800UL; 285 | // subtract seventy years: 286 | epoch = secsSince1900 - seventyYears; 287 | return (epoch != 0); 288 | } 289 | return false; 290 | } 291 | 292 | /** 293 | * Flip the LED state based on the current state 294 | */ 295 | bool ledState; 296 | void ledCallback() { 297 | if ( ledState ) ledOff(); 298 | else ledOn(); 299 | } 300 | 301 | /** 302 | * Make sure the LED starts lit 303 | */ 304 | bool ledOnEnable() { 305 | ledOn(); 306 | return true; 307 | } 308 | 309 | /** 310 | * Make sure LED ends dimmed 311 | */ 312 | void ledOnDisable() { 313 | ledOff(); 314 | } 315 | 316 | /** 317 | * Turn LEDs on. 318 | * Set appropriate delay. 319 | * PLEASE NOTE: NodeMCU onbaord LED is active-low 320 | */ 321 | void ledOn() { 322 | ledState = true; 323 | digitalWrite(LEDPIN, LOW); 324 | digitalWrite(LEDPIN2, HIGH); 325 | tLED.delay( ledDelayOn ); 326 | } 327 | 328 | /** 329 | * Turn LEDs off. 330 | * Set appropriate delay. 331 | * PLEASE NOTE: NodeMCU onbaord LED is active-low 332 | */ 333 | void ledOff() { 334 | ledState = false; 335 | digitalWrite(LEDPIN, HIGH); 336 | digitalWrite(LEDPIN2, LOW); 337 | tLED.delay( ledDelayOff ); 338 | } 339 | 340 | -------------------------------------------------------------------------------- /examples/Scheduler_example15_STDFunction/Scheduler_example15_STDFunction.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * TaskScheduler Test sketch - Showing how to use std::function 3 | * to get acces to variables from within the task callback function 4 | * 5 | * Support for std::function is only available for ESP8266 architecture 6 | */ 7 | #define _TASK_SLEEP_ON_IDLE_RUN 8 | #define _TASK_STD_FUNCTION // Compile with support for std::function 9 | #include 10 | 11 | Scheduler ts; 12 | int counter = 0; 13 | 14 | class Calculator { 15 | public: 16 | int cumSum = 0; // cumulative sum 17 | Calculator(int b) { 18 | // Pass the this pointer, so that we get access to this->cumSum 19 | // Also pass a copy of b 20 | calculateTask.set(TASK_SECOND, TASK_FOREVER, [this, b]() { 21 | counter++; 22 | Serial.printf("%u. %u: cumSum = %u + %u\t", counter, millis(), cumSum, b); 23 | cumSum += b; 24 | Serial.printf("Resulting cumulative sum: %u\n", cumSum); 25 | }); 26 | ts.addTask(calculateTask); 27 | calculateTask.enable(); 28 | } 29 | 30 | Task calculateTask; 31 | }; 32 | 33 | Calculator calc1(2); 34 | Calculator calc2(4); 35 | Calculator calc3(8); 36 | 37 | // Disable tasks after 10 seconds 38 | Task tWrapper(10*TASK_SECOND, TASK_ONCE, []() { 39 | ts.disableAll(); 40 | }, &ts); 41 | 42 | /** 43 | * Standard Arduino setup and loop methods 44 | */ 45 | void setup() { 46 | Serial.begin(74880); 47 | Serial.println("std::function test"); 48 | tWrapper.enableDelayed(); 49 | } 50 | 51 | void loop() { 52 | ts.execute(); 53 | } -------------------------------------------------------------------------------- /examples/Scheduler_example16_Multitab/Scheduler_example16_Multitab.ino: -------------------------------------------------------------------------------- 1 | //This file is intentionally left blank. 2 | // 3 | //Arduino IDE plays some dirty tricks on the main sketch .ino file: 4 | //it rearranges #includes, blindly creates forward definitions, 5 | //includes every file in the project that does not have .c or .cpp 6 | //file extension. 7 | // 8 | //Usually it all turns well if you have only one source file and you are either 9 | //inexperienced or really expert C++ Arduino programmer. 10 | //For the folks with the middle ground skills level, when you want 11 | //to split your code into several .cpp files, it is best to leave 12 | //this main sketch empty. 13 | // 14 | //It doesn't matter where you define the void loop() and void setup(). 15 | //Just make sure there is exactly one definition of each. 16 | // 17 | //And if you want to use standard Arduino functions 18 | //like digitalWrite or the Serial object - just add #include. 19 | -------------------------------------------------------------------------------- /examples/Scheduler_example16_Multitab/file1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "header.hpp" 3 | 4 | 5 | //Declare the functions we want to use before we are ready to define them 6 | void t1Callback(); 7 | 8 | 9 | // Tasks 10 | Task t1(2000, 10, &t1Callback, &runner, true); //adding task to the chain on creation 11 | Task t3(5000, TASK_FOREVER, &t3Callback); 12 | 13 | 14 | void t1Callback() { 15 | Serial.print("t1: "); 16 | Serial.println(millis()); 17 | 18 | if (t1.isFirstIteration()) { 19 | runner.addTask(t3); 20 | t3.enable(); 21 | Serial.println("t1: enabled t3 and added to the chain"); 22 | } 23 | 24 | if (t1.isLastIteration()) { 25 | t3.disable(); 26 | runner.deleteTask(t3); 27 | t2.setInterval(500); 28 | Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500"); 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /examples/Scheduler_example16_Multitab/file2.cpp: -------------------------------------------------------------------------------- 1 | // Test the same as example#2: 2 | // Initially only tasks 1 and 2 are enabled 3 | // Task1 runs every 2 seconds 10 times and then stops 4 | // Task2 runs every 3 seconds indefinitely 5 | // Task1 enables Task3 at its first run 6 | // Task3 run every 5 seconds 7 | // loop() runs every 1 second (a default scheduler delay, if no shorter tasks' interval is detected) 8 | // Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds 9 | // Because Task2 interval is shorter than Scheduler default tick, loop() executes ecery 1/2 seconds now 10 | // At the end Task2 is the only task running every 1/2 seconds 11 | 12 | 13 | //Header that declares all shared objects between .cpp files 14 | #include "header.hpp" 15 | 16 | #include //for Serial and delay 17 | 18 | Scheduler runner; //Let the scheduler live here, in the main file, ok? 19 | 20 | 21 | //Pretend, that the t2 task is a special task, 22 | //that needs to live in file2 object file. 23 | void t2Callback() { 24 | Serial.print("t2: "); 25 | Serial.println(millis()); 26 | } 27 | Task t2(3000, TASK_FOREVER, &t2Callback, &runner, true); 28 | 29 | //Lets define t3Callback here. We are going to use it in file1 30 | //for Task 1. 31 | void t3Callback() { 32 | Serial.print("t3: "); 33 | Serial.println(millis()); 34 | } 35 | 36 | 37 | 38 | 39 | 40 | 41 | void setup () { 42 | Serial.begin(115200); 43 | delay(5000); 44 | Serial.println("Scheduler TEST (multi-tab)"); 45 | 46 | runner.startNow(); // set point-in-time for scheduling start 47 | } 48 | 49 | 50 | void loop () { 51 | runner.execute(); 52 | 53 | // Serial.println("Loop ticks at: "); 54 | // Serial.println(millis()); 55 | } 56 | -------------------------------------------------------------------------------- /examples/Scheduler_example16_Multitab/header.hpp: -------------------------------------------------------------------------------- 1 | //This is the place to declare every single function 2 | //and global variable that is going to be reused between cpp files. 3 | 4 | 5 | //We are going to use the TaskScheduler, but only the declarations part. 6 | //Remember to put customization macros before the #include: 7 | #define _TASK_SLEEP_ON_IDLE_RUN 8 | #include 9 | 10 | //Let the runner object be a global, single instance shared between object files. 11 | extern Scheduler runner; 12 | extern Task t2; //the t2 is defined in file2, but we need to access it from file1. 13 | 14 | //This function needs to be shared (between file2 and file1). 15 | void t3Callback(); 16 | -------------------------------------------------------------------------------- /examples/Scheduler_example16_Multitab/ts.cpp: -------------------------------------------------------------------------------- 1 | //This is the only .cpp file that gets the #include. 2 | //Without it, the linker would not find necessary TaskScheduler's compiled code. 3 | // 4 | //Remember to put customization macros here as well. 5 | // 6 | //And don't import any common headers (here: header.hpp) 7 | // 8 | //Really. This file needs to be short. All stuff is in TaskScheduler.h. 9 | 10 | // #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns 11 | #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass 12 | // #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only 13 | // #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids 14 | // #define _TASK_LTS_POINTER // Compile with support for local task storage pointer 15 | // #define _TASK_PRIORITY // Support for layered scheduling priority 16 | // #define _TASK_MICRO_RES // Support for microsecond resolution 17 | // #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY) 18 | // #define _TASK_DEBUG // Make all methods and variables public for debug purposes 19 | 20 | #include 21 | -------------------------------------------------------------------------------- /examples/Scheduler_example17_Timeout/Scheduler_example17_Timeout.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This eaxmple illustrates the use of overall Task timeout functionality: 3 | 4 | Task 1 - runs every 1 seconds and times out in 10 seconds 5 | Task 2 - runs every 5 seconds and resets the timeout every run, so runs continuosly even though the timeout is set to 10 seconds 6 | */ 7 | 8 | 9 | // #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns 10 | #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass 11 | //#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only 12 | // #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids 13 | // #define _TASK_LTS_POINTER // Compile with support for local task storage pointer 14 | // #define _TASK_PRIORITY // Support for layered scheduling priority 15 | // #define _TASK_MICRO_RES // Support for microsecond resolution 16 | // #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY) 17 | // #define _TASK_DEBUG // Make all methods and variables public for debug purposes 18 | // #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations 19 | #define _TASK_TIMEOUT 20 | 21 | #include 22 | 23 | Scheduler ts; 24 | 25 | void task1Callback(); 26 | void task1OnDisable(); 27 | void task2Callback(); 28 | void task2OnDisable(); 29 | 30 | Task t1(1 * TASK_SECOND, TASK_FOREVER, &task1Callback, &ts, false, NULL, &task1OnDisable); 31 | Task t2(5 * TASK_SECOND, TASK_FOREVER, &task2Callback, &ts, false, NULL, &task2OnDisable); 32 | 33 | void setup() { 34 | // put your setup code here, to run once: 35 | Serial.begin(115200); 36 | 37 | Serial.println("TaskScheduler Timeout example"); 38 | Serial.println("============================="); 39 | 40 | t1.setTimeout(10 * TASK_SECOND); 41 | t2.setTimeout(10 * TASK_SECOND); 42 | 43 | ts.enableAll(); 44 | } 45 | 46 | void loop() { 47 | // put your main code here, to run repeatedly: 48 | ts.execute(); 49 | } 50 | 51 | 52 | void task1Callback() { 53 | Serial.print("Task 1:\t"); 54 | Serial.print(millis()); 55 | Serial.print(": t/out="); 56 | Serial.print(t1.getTimeout()); 57 | Serial.print("\tms until t/out="); 58 | Serial.println( t1.untilTimeout()); 59 | 60 | } 61 | void task1OnDisable() { 62 | if (t1.timedOut()) { 63 | Serial.println("Task 1 has timed out. Restarting"); 64 | t1.setInterval(1 * TASK_SECOND); 65 | t1.setIterations(15); 66 | t1.setTimeout(TASK_NOTIMEOUT); 67 | t1.enable(); 68 | } 69 | else { 70 | Serial.println("Task 1 has been disabled"); 71 | } 72 | } 73 | 74 | void task2Callback() { 75 | Serial.print("Task 2:\t"); 76 | Serial.print(millis()); 77 | Serial.print(": t/out="); 78 | Serial.print(t2.getTimeout()); 79 | Serial.print("\tms until t/out="); 80 | Serial.println( t2.untilTimeout()); 81 | t2.resetTimeout(); 82 | } 83 | void task2OnDisable() { 84 | if (t2.timedOut()) { 85 | Serial.println("Task 2 has timed out"); 86 | } 87 | else { 88 | Serial.println("Task 2 has been disabled"); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /examples/Scheduler_example18_StatusRequest_LTS_WDT_Timeout/Scheduler_example18_StatusRequest_LTS_WDT_Timeout.ino: -------------------------------------------------------------------------------- 1 | /** 2 | This is example 5 rewritten with Timeout, LTS and WDT functioanlity: 3 | - 1 second timeout is set for the main calculation task 4 | - LTS is used to address individual array elements for each sensor sinlce the callback code is shared 5 | - WDT is used to set the Task ID and use that as an index for array of distances (alternative to LTS) 6 | 7 | Original description: 8 | ==================== 9 | This test emulates querying 3 sensors once every 10 seconds, each could respond with a different delay 10 | (ultrasonic sensors for instance) and printing a min value of the three when all three have reported their values. 11 | The overall timeout of 1 second is setup as well. 12 | An error message needs to be printed if a timeout occurred instead of a value. 13 | 14 | Example5: 15 | Sketch uses 6066 bytes (18%) of program storage space. Maximum is 32256 bytes. 16 | Global variables use 1039 bytes (50%) of dynamic memory, leaving 1009 bytes for local variables. Maximum is 2048 bytes. 17 | Example 18: 18 | Sketch uses 5142 bytes (15%) of program storage space. Maximum is 32256 bytes. 19 | Global variables use 878 bytes (42%) of dynamic memory, leaving 1170 bytes for local variables. Maximum is 2048 bytes. 20 | */ 21 | 22 | // #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns 23 | #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass 24 | #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only 25 | #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids 26 | #define _TASK_LTS_POINTER // Compile with support for local task storage pointer 27 | #define _TASK_PRIORITY // Support for layered scheduling priority 28 | // #define _TASK_MICRO_RES // Support for microsecond resolution 29 | // #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY) 30 | #define _TASK_DEBUG // Make all methods and variables public for debug purposes 31 | #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations 32 | #define _TASK_TIMEOUT // Support for overall task timeout 33 | 34 | #include 35 | 36 | StatusRequest measure; 37 | 38 | Scheduler ts, hts; 39 | 40 | // Callback methods prototypes 41 | void CycleCallback(); 42 | void CalcCallback(); 43 | bool CalcEnable(); 44 | void CalcDisable(); 45 | void SCallback(); bool SEnable(); 46 | 47 | // Tasks 48 | Task tSensor1(0, TASK_ONCE, &SCallback, &ts, false, &SEnable); // task ID = 1 49 | Task tSensor2(0, TASK_ONCE, &SCallback, &ts, false, &SEnable); // task ID = 2 50 | Task tSensor3(0, TASK_ONCE, &SCallback, &ts, false, &SEnable); // task ID = 3 51 | 52 | Task tCycle(10000, TASK_FOREVER, &CycleCallback, &hts); 53 | Task tCalculate(TASK_IMMEDIATE , TASK_ONCE, &CalcCallback, &hts, false, &CalcEnable, &CalcDisable); 54 | 55 | #define NO_OF_SENSORS 3 56 | long distance, d[NO_OF_SENSORS + 1], d_lts[NO_OF_SENSORS]; // d[] will be populated via task ID used as array indexes, d_lts will be addressed via LTS pointers 57 | 58 | void CycleCallback() { 59 | Serial.println(); 60 | Serial.print(millis()); Serial.print(":\t"); 61 | Serial.println("CycleCallback: Initiating measurement cycle every 10 seconds"); 62 | 63 | distance = 0; 64 | measure.setWaiting(NO_OF_SENSORS); // Set the StatusRequest to wait for 3 signals. 65 | tCalculate.waitFor(&measure); 66 | } 67 | 68 | bool CalcEnable() { 69 | Serial.print(millis()); Serial.print(":\t"); 70 | Serial.println("CalcEnable: OnEnable"); 71 | Serial.println("Activating sensors and setting timeout"); 72 | 73 | tSensor1.restartDelayed(); 74 | tSensor2.restartDelayed(); 75 | tSensor3.restartDelayed(); 76 | 77 | return true; 78 | } 79 | 80 | void CalcDisable() { 81 | if (tCalculate.timedOut()) { 82 | measure.signalComplete(-1); // signal error 83 | Serial.print(millis()); Serial.print(":\t"); 84 | Serial.println("MeasureCallback: ***** Timeout *****"); 85 | // tSensor1.disable(); 86 | // tSensor2.disable(); 87 | // tSensor3.disable(); 88 | } 89 | } 90 | 91 | 92 | void CalcCallback() { 93 | Serial.print(millis()); Serial.print(":\t"); 94 | Serial.println("CalcCallback: calculating"); 95 | distance = -1; 96 | if ( measure.getStatus() >= 0) { // only calculate if statusrequest ended successfully 97 | distance = d[1] < d[2] ? d[1] : d[2]; 98 | distance = d[3] < distance ? d[3] : distance; 99 | Serial.print("CalcCallback: Min distance="); Serial.println(distance); 100 | Serial.println(); 101 | } 102 | } 103 | 104 | 105 | /** Simulation code for all sensors 106 | ------------------------------- 107 | */ 108 | bool SEnable() { 109 | Task &t = ts.currentTask(); 110 | int i = t.getId(); 111 | 112 | Serial.print(millis()); Serial.print(":\t"); 113 | Serial.print("SEnable: TaskID="); 114 | Serial.println(i); 115 | Serial.print("Triggering sensor. Delay="); 116 | 117 | t.setInterval( random(1200) ); // Simulating sensor delay, which could go over 1 second and cause timeout 118 | // One way to update the 3 distances with one codebase - use task id as an index 119 | d[i] = 0; 120 | 121 | // Another way to update the 3 distances with one codebase - use LTS pointers 122 | int *pd = (int*) t.getLtsPointer(); 123 | *pd = 0; 124 | 125 | Serial.println( t.getInterval() ); 126 | return true; 127 | } 128 | 129 | void SCallback() { 130 | Task &t = ts.currentTask(); 131 | int i = t.getId(); 132 | 133 | Serial.print(millis()); Serial.print(":\t"); 134 | Serial.print("SCallback: TaskID="); 135 | Serial.println(i); 136 | Serial.print("Emulating measurement. d="); 137 | 138 | d[i] = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement 139 | int *pd = (int*) t.getLtsPointer(); 140 | *pd = d[i]; 141 | 142 | measure.signal(); 143 | 144 | Serial.print(d[i]); 145 | Serial.print("\t"); 146 | Serial.println(*pd); 147 | } 148 | 149 | /** Main Arduino code 150 | Not much is left here - everything is taken care of by the framework 151 | */ 152 | void setup() { 153 | 154 | Serial.begin(115200); 155 | Serial.println("TaskScheduler StatusRequest Sensor Emulation Test. Complex Test."); 156 | randomSeed(analogRead(A0) + millis()); 157 | 158 | tSensor1.setLtsPointer(&d_lts[0]); 159 | tSensor2.setLtsPointer(&d_lts[1]); 160 | tSensor3.setLtsPointer(&d_lts[2]); 161 | 162 | ts.setHighPriorityScheduler(&hts); 163 | 164 | tCalculate.setTimeout(1 * TASK_SECOND); 165 | tCycle.enable(); 166 | } 167 | 168 | void loop() { 169 | 170 | ts.execute(); 171 | 172 | } 173 | -------------------------------------------------------------------------------- /examples/Scheduler_example19_Dynamic_Tasks/Scheduler_example19_Dynamic_Tasks.ino: -------------------------------------------------------------------------------- 1 | /** 2 | TaskScheduler Test sketch - test of Task destructor 3 | Test case: 4 | Main task runs every 100 milliseconds 100 times and in 50% cases generates a task object 5 | which runs 1 to 10 times with 100 ms to 5 s interval, and then destroyed. 6 | Garbage collection deletes all the tasks which have finished (enabled in their respective 7 | OnDisable methods) 8 | 9 | This sketch uses the following libraries: 10 | - FreeMemory library: https://github.com/McNeight/MemoryFree 11 | - QueueArray library: https://playground.arduino.cc/Code/QueueArray/ 12 | */ 13 | 14 | #define _TASK_WDT_IDS // To enable task unique IDs 15 | #define _TASK_SLEEP_ON_IDLE_RUN // Compile with support for entering IDLE SLEEP state for 1 ms if not tasks are scheduled to run 16 | #define _TASK_LTS_POINTER // Compile with support for Local Task Storage pointer 17 | #include 18 | #include 19 | 20 | int freeMemory(); 21 | 22 | #if defined (ARDUINO_ARCH_AVR) 23 | #include 24 | #elif defined(__arm__) 25 | extern "C" char* sbrk(int incr); 26 | static int freeMemory() { 27 | char top = 't'; 28 | return &top - reinterpret_cast(sbrk(0)); 29 | } 30 | #elif defined (ARDUINO_ARCH_ESP8266) || defined (ARDUINO_ARCH_ESP32) 31 | int freeMemory() { return ESP.getFreeHeap();} 32 | #else 33 | // Supply your own freeMemory method 34 | int freeMemory() { return 0;} 35 | #endif 36 | 37 | Scheduler ts; 38 | 39 | // Callback methods prototypes 40 | void MainLoop(); 41 | void GC(); 42 | 43 | // Statis task 44 | Task tMain(100 * TASK_MILLISECOND, 100, &MainLoop, &ts, true); 45 | Task tGarbageCollection(200 * TASK_MILLISECOND, TASK_FOREVER, &GC, &ts, false); 46 | 47 | 48 | void Iteration(); 49 | bool OnEnable(); 50 | void OnDisable(); 51 | 52 | int noOfTasks = 0; 53 | QueueArray toDelete; 54 | 55 | void MainLoop() { 56 | Serial.print(millis()); Serial.print("\t"); 57 | Serial.print("MainLoop run: "); 58 | int i = tMain.getRunCounter(); 59 | Serial.print(i); Serial.print(F(".\t")); 60 | 61 | if ( random(0, 101) > 50 ) { // generate a new task only in 50% of cases 62 | // Generating another task 63 | long p = random(100, 5001); // from 100 ms to 5 seconds 64 | long j = random(1, 11); // from 1 to 10 iterations) 65 | Task *t = new Task(p, j, Iteration, &ts, false, OnEnable, OnDisable); 66 | 67 | Serial.print(F("Generated a new task:\t")); Serial.print(t->getId()); Serial.print(F("\tInt, Iter = \t")); 68 | Serial.print(p); Serial.print(", "); Serial.print(j); 69 | Serial.print(F("\tFree mem=")); Serial.print(freeMemory()); 70 | Serial.print(F("\tNo of tasks=")); Serial.println(++noOfTasks); 71 | t->enable(); 72 | } 73 | else { 74 | Serial.println(F("Skipped generating a task")); 75 | } 76 | } 77 | 78 | 79 | void Iteration() { 80 | Task &t = ts.currentTask(); 81 | 82 | Serial.print(millis()); Serial.print("\t"); 83 | Serial.print("Task N"); Serial.print(t.getId()); Serial.print(F("\tcurrent iteration: ")); 84 | int i = t.getRunCounter(); 85 | Serial.println(i); 86 | } 87 | 88 | bool OnEnable() { 89 | // to-do: think of something to put in here. 90 | return true; 91 | } 92 | 93 | void OnDisable() { 94 | Task *t = &ts.currentTask(); 95 | unsigned int tid = t->getId(); 96 | toDelete.push(t); 97 | tGarbageCollection.enableIfNot(); 98 | 99 | Serial.print(millis()); Serial.print("\t"); 100 | Serial.print("Task N"); Serial.print(tid); Serial.println(F("\tfinished")); 101 | } 102 | 103 | /** 104 | Standard Arduino setup and loop methods 105 | */ 106 | void setup() { 107 | Serial.begin(115200); 108 | 109 | randomSeed(analogRead(0) + analogRead(5)); 110 | noOfTasks = 0; 111 | 112 | Serial.println(F("Dynamic Task Creation/Destruction Example")); 113 | Serial.println(); 114 | 115 | Serial.print(F("Free mem=")); Serial.print(freeMemory()); 116 | Serial.print(F("\tNo of tasks=")); Serial.println(noOfTasks); 117 | Serial.println(); 118 | } 119 | 120 | void GC() { 121 | if ( toDelete.isEmpty() ) { 122 | tGarbageCollection.disable(); 123 | return; 124 | } 125 | Task *t = toDelete.pop(); 126 | Serial.print(millis()); Serial.print("\t"); 127 | Serial.print("Task N"); Serial.print(t->getId()); Serial.println(F("\tdestroyed")); 128 | Serial.print("Free mem="); Serial.print(freeMemory()); 129 | Serial.print(F("\tNo of tasks=")); Serial.println(--noOfTasks); 130 | delete t; 131 | } 132 | 133 | void loop() { 134 | ts.execute(); 135 | } 136 | 137 | 138 | -------------------------------------------------------------------------------- /examples/Scheduler_example19_Dynamic_Tasks_SelfDestruct/Scheduler_example19_Dynamic_Tasks_SelfDestruct.ino: -------------------------------------------------------------------------------- 1 | /** 2 | TaskScheduler Test sketch - test of Task destructor 3 | Test case: 4 | Main task runs every 100 milliseconds 100 times and in 50% cases generates a task object 5 | which runs 1 to 10 times with 100 ms to 5 s interval, and then destroyed. 6 | Self-destruct feature takes care of the task deletion after tasks complete 7 | Last 5 tasks are deleted with disableAll() scheduler method as a test 8 | 9 | This sketch uses the following libraries: 10 | - FreeMemory library: https://github.com/McNeight/MemoryFree 11 | - QueueArray library: https://playground.arduino.cc/Code/QueueArray/ 12 | */ 13 | 14 | #define _TASK_WDT_IDS // To enable task unique IDs 15 | #define _TASK_SLEEP_ON_IDLE_RUN // Compile with support for entering IDLE SLEEP state for 1 ms if not tasks are scheduled to run 16 | #define _TASK_LTS_POINTER // Compile with support for Local Task Storage pointer 17 | #define _TASK_SELF_DESTRUCT // Enable tasks to "self-destruct" after disable 18 | #include 19 | 20 | int freeMemory(); 21 | 22 | #if defined (ARDUINO_ARCH_AVR) 23 | #include 24 | #elif defined(__arm__) 25 | extern "C" char* sbrk(int incr); 26 | static int freeMemory() { 27 | char top = 't'; 28 | return &top - reinterpret_cast(sbrk(0)); 29 | } 30 | #elif defined (ARDUINO_ARCH_ESP8266) || defined (ARDUINO_ARCH_ESP32) 31 | int freeMemory() { return ESP.getFreeHeap();} 32 | #else 33 | // Supply your own freeMemory method 34 | int freeMemory() { return 0;} 35 | #endif 36 | 37 | Scheduler ts; 38 | 39 | // Callback methods prototypes 40 | void MainLoop(); 41 | void GC(); 42 | 43 | // Statis task 44 | Task tMain(100 * TASK_MILLISECOND, 100, &MainLoop, &ts, true); 45 | 46 | void Iteration(); 47 | bool OnEnable(); 48 | void OnDisable(); 49 | 50 | int noOfTasks = 0; 51 | long maxIterations = 0; 52 | 53 | void MainLoop() { 54 | Serial.print(millis()); Serial.print("\t"); 55 | Serial.print("MainLoop run: "); 56 | int i = tMain.getRunCounter(); 57 | Serial.print(i); Serial.print(F(".\t")); 58 | 59 | if ( random(0, 101) > 50 ) { // generate a new task only in 50% of cases 60 | // Generating another task 61 | long p = random(100, 5001); // from 100 ms to 5 seconds 62 | long j = random(1, 11); // from 1 to 10 iterations) 63 | maxIterations = ( j > maxIterations ) ? j : maxIterations; 64 | Task *t = new Task(p, j, Iteration, &ts, false, OnEnable, OnDisable, true); // enable self-destruct 65 | 66 | Serial.print(F("Generated a new task:\t")); Serial.print(t->getId()); Serial.print(F("\tInt, Iter = \t")); 67 | Serial.print(p); Serial.print(", "); Serial.print(j); 68 | Serial.print(F("\tFree mem=")); Serial.print(freeMemory()); 69 | Serial.print(F("\tNo of tasks=")); Serial.println(++noOfTasks); 70 | t->enable(); 71 | } 72 | else { 73 | Serial.println(F("Skipped generating a task")); 74 | } 75 | } 76 | 77 | 78 | void Iteration() { 79 | Task &t = ts.currentTask(); 80 | 81 | Serial.print(millis()); Serial.print("\t"); 82 | Serial.print("Task N"); Serial.print(t.getId()); Serial.print(F("\tcurrent iteration: ")); 83 | int i = t.getRunCounter(); 84 | Serial.println(i); 85 | 86 | if (i >= maxIterations) { 87 | t.disable(); 88 | maxIterations *= 2; // self-disable exactly one task 89 | } 90 | } 91 | 92 | bool OnEnable() { 93 | // to-do: think of something to put in here. 94 | return true; 95 | } 96 | 97 | void OnDisable() { 98 | Task *t = &ts.currentTask(); 99 | unsigned int tid = t->getId(); 100 | 101 | Serial.print(millis()); Serial.print("\t"); 102 | Serial.print("Task N"); Serial.print(tid); Serial.println(F("\tfinished")); 103 | Serial.print(F("\tNo of tasks=")); Serial.println(--noOfTasks); 104 | } 105 | 106 | /** 107 | Standard Arduino setup and loop methods 108 | */ 109 | void setup() { 110 | Serial.begin(115200); 111 | 112 | randomSeed(analogRead(0) + analogRead(5)); 113 | noOfTasks = 0; 114 | 115 | Serial.println(F("Dynamic Task Creation/Destruction Example")); 116 | Serial.println(); 117 | 118 | Serial.print(F("Free mem=")); Serial.print(freeMemory()); 119 | Serial.print(F("\tNo of tasks=")); Serial.println(noOfTasks); 120 | Serial.println(); 121 | } 122 | 123 | void loop() { 124 | ts.execute(); 125 | if ( millis() > 5000 && noOfTasks <= 5 ) { 126 | ts.disableAll(); 127 | Serial.print(F("\tFree mem=")); Serial.println(freeMemory()); 128 | while(1); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /examples/Scheduler_example19_Dynamic_Tasks_stdQueue/Scheduler_example19_Dynamic_Tasks_stdQueue.ino: -------------------------------------------------------------------------------- 1 | /** 2 | TaskScheduler Test sketch - test of Task destructor 3 | Test case: 4 | Main task runs every 100 milliseconds 100 times and in 50% cases generates a task object 5 | which runs 1 to 10 times with 100 ms to 5 s interval, and then destroyed. 6 | Garbage collection deletes all the tasks which have finished (enabled in their respective 7 | OnDisable methods) 8 | 9 | This sketch uses the following libraries: 10 | - FreeMemory library: https://github.com/McNeight/MemoryFree 11 | - QueueArray library: https://playground.arduino.cc/Code/QueueArray/ 12 | */ 13 | 14 | #define _TASK_WDT_IDS // To enable task unique IDs 15 | #define _TASK_SLEEP_ON_IDLE_RUN // Compile with support for entering IDLE SLEEP state for 1 ms if not tasks are scheduled to run 16 | #define _TASK_LTS_POINTER // Compile with support for Local Task Storage pointer 17 | 18 | #include 19 | 20 | #include 21 | 22 | int freeMemory(); 23 | 24 | #if defined (ARDUINO_ARCH_AVR) 25 | #include 26 | #elif defined(__arm__) 27 | extern "C" char* sbrk(int incr); 28 | static int freeMemory() { 29 | char top = 't'; 30 | return &top - reinterpret_cast(sbrk(0)); 31 | } 32 | #elif defined (ARDUINO_ARCH_ESP8266) || defined (ARDUINO_ARCH_ESP32) 33 | int freeMemory() { return ESP.getFreeHeap();} 34 | #else 35 | // Supply your own freeMemory method 36 | int freeMemory() { return 0;} 37 | #endif 38 | 39 | Scheduler ts; 40 | 41 | // Callback methods prototypes 42 | void MainLoop(); 43 | void GC(); 44 | 45 | // Statis task 46 | Task tMain(100 * TASK_MILLISECOND, 100, &MainLoop, &ts, true); 47 | Task tGarbageCollection(200 * TASK_MILLISECOND, TASK_FOREVER, &GC, &ts, false); 48 | 49 | 50 | void Iteration(); 51 | bool OnEnable(); 52 | void OnDisable(); 53 | 54 | int noOfTasks = 0; 55 | std::queue toDelete; 56 | 57 | void MainLoop() { 58 | Serial.print(millis()); Serial.print("\t"); 59 | Serial.print("MainLoop run: "); 60 | int i = tMain.getRunCounter(); 61 | Serial.print(i); Serial.print(F(".\t")); 62 | 63 | if ( random(0, 101) > 50 ) { // generate a new task only in 50% of cases 64 | // Generating another task 65 | long p = random(100, 5001); // from 100 ms to 5 seconds 66 | long j = random(1, 11); // from 1 to 10 iterations) 67 | Task *t = new Task(p, j, Iteration, &ts, false, OnEnable, OnDisable); 68 | 69 | Serial.print(F("Generated a new task:\t")); Serial.print(t->getId()); Serial.print(F("\tInt, Iter = \t")); 70 | Serial.print(p); Serial.print(", "); Serial.print(j); 71 | Serial.print(F("\tFree mem=")); Serial.print(freeMemory()); 72 | Serial.print(F("\tNo of tasks=")); Serial.println(++noOfTasks); 73 | t->enable(); 74 | } 75 | else { 76 | Serial.println(F("Skipped generating a task")); 77 | } 78 | } 79 | 80 | 81 | void Iteration() { 82 | Task &t = ts.currentTask(); 83 | 84 | Serial.print(millis()); Serial.print("\t"); 85 | Serial.print("Task N"); Serial.print(t.getId()); Serial.print(F("\tcurrent iteration: ")); 86 | int i = t.getRunCounter(); 87 | Serial.println(i); 88 | } 89 | 90 | bool OnEnable() { 91 | // to-do: think of something to put in here. 92 | return true; 93 | } 94 | 95 | void OnDisable() { 96 | Task *t = &ts.currentTask(); 97 | unsigned int tid = t->getId(); 98 | toDelete.push(t); 99 | tGarbageCollection.enableIfNot(); 100 | 101 | Serial.print(millis()); Serial.print("\t"); 102 | Serial.print("Task N"); Serial.print(tid); Serial.println(F("\tfinished")); 103 | } 104 | 105 | /** 106 | Standard Arduino setup and loop methods 107 | */ 108 | void setup() { 109 | Serial.begin(115200); 110 | 111 | randomSeed(analogRead(0) + analogRead(5)); 112 | noOfTasks = 0; 113 | 114 | Serial.println(F("Dynamic Task Creation/Destruction Example")); 115 | Serial.println(); 116 | 117 | Serial.print(F("Free mem=")); Serial.print(freeMemory()); 118 | Serial.print(F("\tNo of tasks=")); Serial.println(noOfTasks); 119 | Serial.println(); 120 | } 121 | 122 | void GC() { 123 | if ( toDelete.empty() ) { 124 | tGarbageCollection.disable(); 125 | return; 126 | } 127 | Task *t = toDelete.front(); toDelete.pop(); 128 | Serial.print(millis()); Serial.print("\t"); 129 | Serial.print("Task N"); Serial.print(t->getId()); Serial.println(F("\tdestroyed")); 130 | Serial.print("Free mem="); Serial.print(freeMemory()); 131 | Serial.print(F("\tNo of tasks=")); Serial.println(--noOfTasks); 132 | delete t; 133 | } 134 | 135 | void loop() { 136 | ts.execute(); 137 | } 138 | -------------------------------------------------------------------------------- /examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object.ino: -------------------------------------------------------------------------------- 1 | /** 2 | This is example 5 rewritten with Timeout, LTS, WDT functioanlity + multitab and extra classes 3 | - 1 second timeout is set for the main calculation task 4 | - LTS is used to address task-specific sensor class object 5 | - WDT is used to set the Task ID and use that for identifying the tasks (debug) 6 | 7 | Original description: 8 | ==================== 9 | This test emulates querying 1 to 10 sensors once every 10 seconds, each could respond with a different delay 10 | (ultrasonic sensors for instance) and printing a max value of them when all have reported their values. 11 | The overall timeout of 1 second is setup as well. 12 | An error message needs to be printed if a timeout occurred instead of a distance value. 13 | 14 | Task and SuperSensor objects are dynamically created and destroyed as needed every 10 seconds 15 | */ 16 | 17 | // #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns 18 | #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass 19 | #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only 20 | #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids 21 | #define _TASK_LTS_POINTER // Compile with support for local task storage pointer 22 | #define _TASK_PRIORITY // Support for layered scheduling priority 23 | // #define _TASK_MICRO_RES // Support for microsecond resolution 24 | // #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY) 25 | #define _TASK_DEBUG // Make all methods and variables public for debug purposes 26 | #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations 27 | #define _TASK_TIMEOUT // Support for overall task timeout 28 | 29 | #include 30 | #include "SuperSensor.h" 31 | 32 | StatusRequest measure; 33 | 34 | Scheduler ts, hts; 35 | 36 | // Callback methods prototypes 37 | void CycleCallback(); 38 | void CalcCallback(); 39 | bool CalcEnable(); 40 | void CalcDisable(); 41 | void SCallback(); 42 | bool SEnable(); 43 | void SDisable(); 44 | 45 | // Tasks 46 | 47 | Task tCycle(10000, TASK_FOREVER, &CycleCallback, &hts); 48 | Task tCalculate(TASK_IMMEDIATE , TASK_ONCE, &CalcCallback, &hts, false, &CalcEnable, &CalcDisable); 49 | 50 | int numberSensors; 51 | long distance; 52 | int pins[] = { 1, 9, 3, 7, 5, 6, 4, 8, 2, 10 }; 53 | 54 | void CycleCallback() { 55 | Serial.println();Serial.println();Serial.println(); 56 | Serial.print(millis()); Serial.print(":\t"); 57 | Serial.println("CycleCallback: Initiating measurement cycle every 10 seconds"); 58 | Serial.print("Number of sensors="); 59 | 60 | numberSensors = random(1, 11); // 1 to 10 sensors, randomly 61 | distance = 0; 62 | Serial.println(numberSensors); 63 | 64 | measure.setWaiting(numberSensors); // Set the StatusRequest to wait for 3 signals. 65 | tCalculate.waitFor(&measure); 66 | tCalculate.setTimeout(1000 * TASK_MILLISECOND); 67 | } 68 | 69 | bool CalcEnable() { 70 | Serial.print(millis()); Serial.print(":\t"); 71 | Serial.println("CalcEnable: OnEnable"); 72 | Serial.println("Activating sensors"); 73 | 74 | 75 | for (int i = 0; i < numberSensors; i++) { 76 | Task *t = new Task(TASK_MILLISECOND, TASK_FOREVER, &SCallback, &ts, false, &SEnable, &SDisable); 77 | SuperSensor *s = new SuperSensor( pins[i] ); 78 | t->setLtsPointer( (void*) s); 79 | t->setId(i + 1); 80 | 81 | s->begin(); 82 | 83 | t->restartDelayed(); 84 | } 85 | 86 | return true; 87 | } 88 | 89 | void CalcDisable() { 90 | if (tCalculate.timedOut()) { 91 | measure.signalComplete(-1); // signal error 92 | Serial.print(millis()); Serial.print(":\t"); 93 | Serial.println("MeasureCallback: ***** Timeout *****"); 94 | } 95 | ts.disableAll(false); // only disable tasks in the ts scheduler 96 | } 97 | 98 | 99 | void CalcCallback() { 100 | Serial.print(millis()); Serial.print(":\t"); 101 | Serial.println("CalcCallback: calculating"); 102 | if ( measure.getStatus() >= 0) { // only calculate if statusrequest ended successfully 103 | Serial.print("CalcCallback: Max distance="); Serial.println(distance); 104 | Serial.println(); 105 | } 106 | } 107 | 108 | 109 | /** Simulation code for all sensors 110 | ------------------------------- 111 | */ 112 | bool SEnable() { 113 | Task &t = ts.currentTask(); 114 | int i = t.getId(); 115 | 116 | Serial.print(millis()); Serial.print(":\t"); 117 | Serial.print("SEnable: TaskID="); 118 | Serial.println(i); 119 | Serial.print("Triggering sensor. Delay="); 120 | 121 | 122 | // Another way to update the distances with one codebase - use LTS pointers 123 | SuperSensor *s = (SuperSensor*) t.getLtsPointer(); 124 | 125 | long dly = s->trigger(); 126 | 127 | 128 | Serial.println( dly ); 129 | return true; 130 | } 131 | 132 | void SCallback() { 133 | Task &t = ts.currentTask(); 134 | 135 | SuperSensor *s = (SuperSensor*) t.getLtsPointer(); 136 | if ( s->measurementReady() ) { 137 | int i = t.getId(); 138 | Serial.print(millis()); Serial.print(":\t"); 139 | Serial.print("SCallback: TaskID="); 140 | Serial.println(i); 141 | Serial.print("Emulating measurement. d="); 142 | 143 | long d = s->value(); 144 | if ( d > distance ) distance = d; 145 | 146 | Serial.println(d); 147 | 148 | measure.signal(); 149 | t.disable(); 150 | } 151 | } 152 | 153 | void SDisable() { 154 | Task &t = ts.currentTask(); 155 | int i = t.getId(); 156 | 157 | Serial.print(millis()); Serial.print(":\t"); 158 | Serial.print("SDisable: TaskID="); 159 | Serial.println(i); 160 | 161 | SuperSensor *s = (SuperSensor*) ts.currentLts(); 162 | s->stop(); 163 | 164 | delete s; 165 | delete &t; 166 | } 167 | 168 | /** Main Arduino code 169 | Not much is left here - everything is taken care of by the framework 170 | */ 171 | void setup() { 172 | 173 | Serial.begin(115200); 174 | Serial.println("TaskScheduler StatusRequest Sensor Emulation Test. Complex Test."); 175 | randomSeed(analogRead(A0) + millis()); 176 | 177 | ts.setHighPriorityScheduler(&hts); 178 | 179 | tCalculate.setTimeout(1 * TASK_SECOND); 180 | tCycle.enable(); 181 | } 182 | 183 | void loop() { 184 | ts.execute(); 185 | } 186 | -------------------------------------------------------------------------------- /examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/SuperSensor.cpp: -------------------------------------------------------------------------------- 1 | #include "SuperSensor.h" 2 | 3 | SuperSensor::SuperSensor(int aPin) { 4 | iPin = aPin; 5 | } 6 | 7 | SuperSensor::~SuperSensor() { 8 | iValue = -1; 9 | } 10 | 11 | void SuperSensor::begin() { 12 | iDelay = random(300, 1500); 13 | iValue = -1; 14 | } 15 | 16 | void SuperSensor::stop() { 17 | //nothing to do 18 | } 19 | 20 | long SuperSensor::trigger() { 21 | iStart = millis(); 22 | return iDelay; 23 | } 24 | 25 | bool SuperSensor::measurementReady() { 26 | if ( millis() - iStart > iDelay ) { 27 | iValue = random(501); 28 | return true; 29 | } 30 | return false; 31 | } 32 | 33 | long SuperSensor::value() { 34 | return iValue; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/SuperSensor.h: -------------------------------------------------------------------------------- 1 | #ifndef _SUPER_SENSOR_H 2 | #define _SUPER_SENSOR_H 3 | 4 | 5 | #include "Arduino.h" 6 | #include 7 | 8 | class SuperSensor { 9 | public: 10 | SuperSensor(int aPin); 11 | ~SuperSensor(); 12 | void begin(); 13 | void stop(); 14 | long trigger(); 15 | bool measurementReady(); 16 | long value(); 17 | 18 | private: 19 | long iDelay; 20 | long iValue; 21 | int iPin; 22 | unsigned long iStart; 23 | }; 24 | 25 | #endif // _SUPER_SENSOR_H 26 | -------------------------------------------------------------------------------- /examples/Scheduler_example21_OO_Callbacks/Calculator.cpp: -------------------------------------------------------------------------------- 1 | #include "Calculator.h" 2 | #include "SuperSensor.h" 3 | 4 | #if defined (ARDUINO_ARCH_AVR) 5 | #include 6 | #endif 7 | 8 | #if defined(__arm__) 9 | extern "C" char* sbrk(int incr); 10 | static int freeMemory() { 11 | char top = 't'; 12 | return &top - reinterpret_cast(sbrk(0)); 13 | } 14 | 15 | #endif 16 | Calculator::Calculator( Scheduler* aS, Scheduler* aSensors) : Task(aS) { 17 | iS = aSensors; 18 | setTimeout(1000 * TASK_MILLISECOND); 19 | } 20 | 21 | bool Calculator::Callback() { 22 | Serial.print(millis()); Serial.print(":\t"); 23 | Serial.println("CalcCallback: calculating"); 24 | if ( getStatusRequest()->getStatus() >= 0) { // only calculate if statusrequest ended successfully 25 | Serial.print("CalcCallback: Max distance="); Serial.println(distance); 26 | } 27 | return false; 28 | } 29 | 30 | extern int pins[]; 31 | 32 | bool Calculator::OnEnable() { 33 | Serial.print(millis()); Serial.print(":\t"); 34 | Serial.println("CalcEnable: OnEnable"); 35 | Serial.println("Activating sensors"); 36 | 37 | StatusRequest* sr = getStatusRequest(); 38 | iNS = sr->getCount(); 39 | 40 | distance = 0; 41 | for (int i = 0; i < iNS; i++) { 42 | SuperSensor *s = new SuperSensor( iS, pins[i], this, sr); 43 | s->setId(i + 1); 44 | s->begin(); 45 | s->restartDelayed(); 46 | } 47 | 48 | return true; 49 | } 50 | 51 | void Calculator::OnDisable() { 52 | if ( timedOut() ) { 53 | getStatusRequest()->signalComplete(-1); // signal error 54 | Serial.print(millis()); Serial.print(":\t"); 55 | Serial.println("MeasureCallback: ***** Timeout *****"); 56 | } 57 | iS->disableAll(false); // only disable tasks in the ts scheduler 58 | #if defined (ARDUINO_ARCH_AVR) || defined(__arm__) 59 | Serial.print("Free mem = "); Serial.println(freeMemory()); Serial.println(); 60 | #endif 61 | } 62 | 63 | void Calculator::reportDistance(long aD) { 64 | if (distance < aD) distance = aD; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /examples/Scheduler_example21_OO_Callbacks/Calculator.h: -------------------------------------------------------------------------------- 1 | #ifndef _CALCULATOR_H 2 | #define _CALCULATOR_H 3 | 4 | #include "Arduino.h" 5 | 6 | #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass 7 | #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only 8 | #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids 9 | #define _TASK_PRIORITY // Support for layered scheduling priority 10 | #define _TASK_TIMEOUT // Support for overall task timeout 11 | #define _TASK_OO_CALLBACKS // Support for dynamic callback method binding 12 | 13 | #include 14 | 15 | class Calculator : public Task { 16 | public: 17 | Calculator( Scheduler* aS, Scheduler* aSensors); 18 | 19 | void reportDistance(long aD); 20 | 21 | bool Callback(); 22 | bool OnEnable(); 23 | void OnDisable(); 24 | 25 | private: 26 | Scheduler* iS; 27 | 28 | long distance; 29 | int iNS; 30 | 31 | }; 32 | 33 | #endif // _CALCULATOR_H 34 | 35 | -------------------------------------------------------------------------------- /examples/Scheduler_example21_OO_Callbacks/Scheduler_example21_OO_Callbacks.ino: -------------------------------------------------------------------------------- 1 | /** 2 | This is example 5 rewritten with dynamic binding of callback methods 3 | - 1 second timeout is set for the main calculation task 4 | - LTS is used to address task-specific sensor class object 5 | - WDT is used to set the Task ID and use that for identifying the tasks (debug) 6 | 7 | Original description: 8 | ==================== 9 | This test emulates querying 1 to 10 sensors once every 10 seconds, each could respond with a different delay 10 | (ultrasonic sensors for instance) and printing a max value of them when all have reported their values. 11 | The overall timeout of 1 second is setup as well. 12 | An error message needs to be printed if a timeout occurred instead of a distance value. 13 | 14 | Task and SuperSensor objects are dynamically created and destroyed as needed every 10 seconds 15 | 16 | This sketch uses a FreeMemory library: https://github.com/McNeight/MemoryFree 17 | FreeMemory for ARM32 boards is based on: http://www.stm32duino.com/viewtopic.php?f=18&t=2065 18 | */ 19 | 20 | 21 | #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass 22 | #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only 23 | #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids 24 | #define _TASK_PRIORITY // Support for layered scheduling priority 25 | #define _TASK_TIMEOUT // Support for overall task timeout 26 | #define _TASK_OO_CALLBACKS 27 | 28 | #include 29 | 30 | #include "SuperSensor.h" 31 | #include "Calculator.h" 32 | #include "Ticker.h" 33 | 34 | StatusRequest measure; 35 | 36 | Scheduler ts, hts; 37 | 38 | // Tasks 39 | 40 | Calculator* tCalculate; 41 | Ticker* tCycle; 42 | 43 | int pins[] = { 1, 9, 3, 7, 5, 6, 4, 8, 2, 10 }; 44 | 45 | #ifdef ARDUINO_ARCH_STM32F1 46 | #define A0 3 47 | #endif 48 | 49 | /** Main Arduino code 50 | Not much is left here - everything is taken care of by the framework 51 | */ 52 | void setup() { 53 | 54 | Serial.begin(115200); 55 | delay(1000); 56 | while (!Serial) {} 57 | Serial.println("TaskScheduler StatusRequest Sensor Emulation Test. Complex Test."); 58 | 59 | #ifdef ARDUINO_ARCH_STM32F1 60 | pinMode(A0, INPUT_ANALOG); 61 | #endif 62 | 63 | randomSeed(analogRead(A0) + millis()); 64 | 65 | ts.setHighPriorityScheduler(&hts); 66 | 67 | tCalculate = new Calculator (&hts, &ts); 68 | tCycle = new Ticker (&hts, (Task*) tCalculate, &measure); 69 | 70 | tCalculate->setTimeout(1 * TASK_SECOND); 71 | tCycle->enable(); 72 | } 73 | 74 | void loop() { 75 | ts.execute(); 76 | } 77 | -------------------------------------------------------------------------------- /examples/Scheduler_example21_OO_Callbacks/SuperSensor.cpp: -------------------------------------------------------------------------------- 1 | #include "SuperSensor.h" 2 | 3 | SuperSensor::SuperSensor(Scheduler* aScheduler, int aPin, Calculator* aC, StatusRequest* aS) : Task(TASK_MILLISECOND, TASK_FOREVER, aScheduler, false) { 4 | iPin = aPin; 5 | iC = aC; 6 | iS = aS; 7 | } 8 | 9 | SuperSensor::~SuperSensor() { 10 | iValue = -1; 11 | } 12 | 13 | void SuperSensor::begin() { 14 | iDelay = random(300, 1500); 15 | iValue = -1; 16 | } 17 | 18 | void SuperSensor::stop() { 19 | //nothing to do 20 | } 21 | 22 | long SuperSensor::trigger() { 23 | iStart = millis(); 24 | return iDelay; 25 | } 26 | 27 | bool SuperSensor::measurementReady() { 28 | if ( millis() - iStart > iDelay ) { 29 | iValue = random(501); 30 | return true; 31 | } 32 | return false; 33 | } 34 | 35 | long SuperSensor::value() { 36 | return iValue; 37 | } 38 | 39 | bool SuperSensor::OnEnable() { 40 | int i = getId(); 41 | 42 | Serial.print(millis()); Serial.print(":\t"); 43 | Serial.print("SEnable: TaskID="); 44 | Serial.println(i); 45 | Serial.print("Triggering sensor. Delay="); 46 | 47 | long dly = trigger(); 48 | 49 | Serial.println( dly ); 50 | return true; 51 | } 52 | 53 | bool SuperSensor::Callback() { 54 | 55 | if ( measurementReady() ) { 56 | int i = getId(); 57 | Serial.print(millis()); Serial.print(":\t"); 58 | Serial.print("SCallback: TaskID="); 59 | Serial.println(i); 60 | Serial.print("Emulating measurement. d="); 61 | 62 | long d = value(); 63 | iC->reportDistance(d); 64 | 65 | Serial.println(d); 66 | 67 | iS->signal(); 68 | disable(); 69 | delete this; 70 | return true; 71 | } 72 | return false; 73 | } 74 | 75 | void SuperSensor::OnDisable() { 76 | int i = getId(); 77 | 78 | Serial.print(millis()); Serial.print(":\t"); 79 | Serial.print("SDisable: TaskID="); 80 | Serial.println(i); 81 | 82 | stop(); 83 | } 84 | -------------------------------------------------------------------------------- /examples/Scheduler_example21_OO_Callbacks/SuperSensor.h: -------------------------------------------------------------------------------- 1 | #ifndef _SUPER_SENSOR_H 2 | #define _SUPER_SENSOR_H 3 | 4 | 5 | #include "Arduino.h" 6 | 7 | #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass 8 | #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only 9 | #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids 10 | #define _TASK_PRIORITY // Support for layered scheduling priority 11 | #define _TASK_TIMEOUT // Support for overall task timeout 12 | #define _TASK_OO_CALLBACKS 13 | 14 | #include 15 | #include "Calculator.h" 16 | 17 | //class Calculator; 18 | 19 | class SuperSensor : public Task { 20 | 21 | public: 22 | SuperSensor(Scheduler* aScheduler, int aPin, Calculator* aC, StatusRequest* aS); 23 | ~SuperSensor(); 24 | 25 | void begin(); 26 | void stop(); 27 | long trigger(); 28 | bool measurementReady(); 29 | long value(); 30 | 31 | bool Callback(); 32 | bool OnEnable(); 33 | void OnDisable(); 34 | 35 | private: 36 | long iDelay; 37 | long iValue; 38 | int iPin; 39 | unsigned long iStart; 40 | Calculator* iC; 41 | StatusRequest* iS; 42 | }; 43 | 44 | #endif // _SUPER_SENSOR_H 45 | -------------------------------------------------------------------------------- /examples/Scheduler_example21_OO_Callbacks/Ticker.cpp: -------------------------------------------------------------------------------- 1 | #include "Ticker.h" 2 | 3 | Ticker::Ticker(Scheduler* aS, Task* aCalc, StatusRequest* aM) : Task(10000, TASK_FOREVER, aS, false) { 4 | iCalc = aCalc; 5 | iMeasure = aM; 6 | } 7 | 8 | bool Ticker::Callback() { 9 | Serial.println(); Serial.println(); Serial.println(); 10 | Serial.print(millis()); Serial.print(":\t"); 11 | Serial.println("CycleCallback: Initiating measurement cycle every 10 seconds"); 12 | 13 | int numberSensors = random(1, 11); // 1 to 10 sensors, randomly 14 | Serial.print("Number of sensors="); 15 | Serial.println(numberSensors); 16 | 17 | iMeasure->setWaiting(numberSensors); // Set the StatusRequest to wait for 1 to 10 signals. 18 | iCalc->waitFor(iMeasure); 19 | } 20 | 21 | -------------------------------------------------------------------------------- /examples/Scheduler_example21_OO_Callbacks/Ticker.h: -------------------------------------------------------------------------------- 1 | #ifndef _TICKER_H 2 | #define _TICKER_H 3 | 4 | #include "Arduino.h" 5 | 6 | #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass 7 | #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only 8 | #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids 9 | #define _TASK_PRIORITY // Support for layered scheduling priority 10 | #define _TASK_TIMEOUT // Support for overall task timeout 11 | #define _TASK_OO_CALLBACKS 12 | 13 | #include 14 | 15 | class Ticker : public Task { 16 | public: 17 | Ticker(Scheduler* aS, Task* aCalc, StatusRequest* aM); 18 | ~Ticker() {}; 19 | 20 | bool Callback(); 21 | 22 | private: 23 | Task *iCalc; 24 | StatusRequest* iMeasure; 25 | }; 26 | 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /examples/Scheduler_example23_IDLE_Callback/Scheduler_example23_IDLE_Callback.ino: -------------------------------------------------------------------------------- 1 | /* 2 | An example of using scheduler's custom sleep callback method 3 | 4 | An empty loop is executed for 10 seconds with a 10 ms. interval 5 | The first time it is excuted with an empty sleep callback, and 6 | the second time with a 1 ms delay in the custom callback 7 | 8 | RESULTS: 9 | 10 | Arduino Nano: 11 | ================================= 12 | Testing empty sleep callback... 13 | cEmptyCallback=1001 14 | cEmptyTotal=423866 15 | Testing 1 ms delayed sleep callback... 16 | cDelayCallback=1001 17 | cDelayTotal=10669 18 | 19 | 20 | ESP8266 at 80MHz 21 | ================================= 22 | Testing empty sleep callback... 23 | cEmptyCallback=1001 24 | cEmptyTotal=278101 25 | Testing 1 ms delayed sleep callback... 26 | cDelayCallback=1001 27 | cDelayTotal=10493 28 | 29 | 30 | ESP8266 at 160MHz 31 | ================================= 32 | Testing empty sleep callback... 33 | cEmptyCallback=1001 34 | cEmptyTotal=546041 35 | Testing 1 ms delayed sleep callback... 36 | cDelayCallback=1001 37 | cDelayTotal=10746 38 | 39 | 40 | Maple Mini STM32 board at 70MHz -O3 code optimization 41 | ================================== 42 | Testing empty sleep callback... 43 | cEmptyCallback=1001 44 | cEmptyTotal=2689973 45 | Testing 1 ms delayed sleep callback... 46 | cDelayCallback=1001 47 | cDelayTotal=10958 48 | 49 | 50 | esp32 at 240MHz 51 | ================================== 52 | Testing empty sleep callback... 53 | cEmptyCallback=1001 54 | cEmptyTotal=492851 55 | Testing 1 ms delayed sleep callback... 56 | cDelayCallback=1001 57 | cDelayTotal=11002 58 | 59 | 60 | */ 61 | 62 | #define _TASK_SLEEP_ON_IDLE_RUN 63 | #include 64 | 65 | Scheduler ts; 66 | 67 | // Callback methods prototypes 68 | void Count(); 69 | bool tEmptyOn(); 70 | void tEmptyOff(); 71 | bool tDelayOn(); 72 | void tDelayOff(); 73 | 74 | // Sleep methods prototypes 75 | void sEmpty(unsigned long aT); 76 | void sDelay(unsigned long aT); 77 | 78 | // Tasks 79 | Task tCount ( 10, TASK_FOREVER, &Count, &ts, false ); 80 | Task tEmpty ( 10000, TASK_ONCE, NULL, &ts, false, &tEmptyOn, &tEmptyOff ); 81 | Task tDelay ( 10000, TASK_ONCE, NULL, &ts, false, &tDelayOn, &tDelayOff ); 82 | 83 | 84 | volatile unsigned long cEmptyCallback, cEmptyTotal, cDelayCallback, cDelayTotal; 85 | volatile unsigned long *cCB, *cTL; 86 | 87 | void setup() { 88 | Serial.begin(115200); 89 | delay(5000); 90 | Serial.println("Start counting..."); 91 | ts.setSleepMethod( &sEmpty ); 92 | tEmpty.restartDelayed(); 93 | } 94 | 95 | 96 | void sEmpty(unsigned long aT) { 97 | } 98 | 99 | void sDelay(unsigned long aT) { 100 | delay(1); 101 | } 102 | 103 | bool tEmptyOn() { 104 | Serial.println("Testing empty sleep callback..."); 105 | cCB = &cEmptyCallback; 106 | cTL = &cEmptyTotal; 107 | 108 | *cCB = 0; 109 | *cTL = 0; 110 | 111 | tCount.restart(); 112 | 113 | return true; 114 | } 115 | 116 | void tEmptyOff() { 117 | tCount.disable(); 118 | 119 | Serial.print("cEmptyCallback="); Serial.println(*cCB); 120 | Serial.print("cEmptyTotal="); Serial.println(*cTL); 121 | 122 | ts.setSleepMethod( &sDelay ); 123 | tDelay.restartDelayed(); 124 | } 125 | 126 | 127 | bool tDelayOn() { 128 | Serial.println("Testing 1 ms delayed sleep callback..."); 129 | cCB = &cDelayCallback; 130 | cTL = &cDelayTotal; 131 | 132 | *cCB = 0; 133 | *cTL = 0; 134 | 135 | tCount.restart(); 136 | 137 | return true; 138 | } 139 | 140 | void tDelayOff() { 141 | tCount.disable(); 142 | 143 | Serial.print("cDelayCallback="); Serial.println(*cCB); 144 | Serial.print("cDelayTotal="); Serial.println(*cTL); 145 | } 146 | 147 | void Count() { 148 | (*cCB)++; 149 | } 150 | 151 | 152 | 153 | void loop() { 154 | // put your main code here, to run repeatedly: 155 | ts.execute(); 156 | (*cTL)++; 157 | } 158 | -------------------------------------------------------------------------------- /examples/Scheduler_example24_CPU_LOAD/Scheduler_example24_CPU_LOAD.ino: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | This sketch collects scheduling overhead and CPU Idle Sleep information. 4 | A task is invoked every 10 milliseconds for 10 seconds. 5 | CPU statistics are collected 6 | 7 | Compile and run once with _TASK_SLEEP_ON_IDLE_RUN enabled, then with _TASK_SLEEP_ON_IDLE_RUN disabled. 8 | Compare the results. 9 | */ 10 | 11 | #define _TASK_ESP8266_DLY_THRESHOLD 50L 12 | #define _TASK_ESP32_DLY_THRESHOLD 40L 13 | 14 | #define _TASK_SLEEP_ON_IDLE_RUN 15 | #define _TASK_TIMECRITICAL 16 | #include 17 | 18 | Scheduler ts; 19 | 20 | // Callback methods prototypes 21 | void Count(); 22 | bool tOn(); void tOff(); 23 | 24 | // Tasks 25 | Task c(10, TASK_FOREVER, &Count, &ts); 26 | Task t(10000, TASK_ONCE, NULL, &ts, true, &tOn, &tOff); 27 | 28 | 29 | volatile unsigned long c1, c2; 30 | bool tOn() { 31 | c1 = 0; 32 | c2 = 0; 33 | c.enable(); 34 | 35 | return true; 36 | } 37 | 38 | void tOff() { 39 | c.disable(); 40 | unsigned long cpuTot = ts.getCpuLoadTotal(); 41 | unsigned long cpuCyc = ts.getCpuLoadCycle(); 42 | unsigned long cpuIdl = ts.getCpuLoadIdle(); 43 | 44 | Serial.print("Loop counts c1="); Serial.println(c1); 45 | Serial.print("Task counts c2="); Serial.println(c2); 46 | Serial.print("Total CPU time="); Serial.print(cpuTot); Serial.println(" micros"); 47 | Serial.print("Scheduling Overhead CPU time="); Serial.print(cpuCyc); Serial.println(" micros"); 48 | Serial.print("Idle Sleep CPU time="); Serial.print(cpuIdl); Serial.println(" micros"); 49 | Serial.print("Productive work CPU time="); Serial.print(cpuTot - cpuIdl - cpuCyc); Serial.println(" micros"); 50 | Serial.println(); 51 | 52 | float idle = (float)cpuIdl / (float)cpuTot * 100; 53 | Serial.print("CPU Idle Sleep "); Serial.print(idle); Serial.println(" % of time."); 54 | 55 | float prod = (float)(cpuIdl + cpuCyc) / (float)cpuTot * 100; 56 | Serial.print("Productive work (not idle, not scheduling)"); Serial.print(100.00 - prod); Serial.println(" % of time."); 57 | 58 | } 59 | 60 | void setup() { 61 | // put your setup code here, to run once: 62 | Serial.begin(115200); 63 | delay(1000); 64 | Serial.println("CPU Time Measurement"); 65 | Serial.println("Start"); 66 | 67 | ts.startNow(); 68 | t.delay(); 69 | ts.cpuLoadReset(); 70 | } 71 | 72 | void Count() { 73 | c2++; // number of task callback invocations 74 | 75 | // Try different delay intervals to see CPU statistics change 76 | // delay(1); 77 | // delay(5); 78 | // delay(10); 79 | // delay(20); 80 | } 81 | 82 | 83 | void loop() { 84 | // put your main code here, to run repeatedly: 85 | ts.execute(); 86 | c1++; // number of loop() cycles 87 | } 88 | -------------------------------------------------------------------------------- /examples/Scheduler_example25_SCHEDULER_CHAIN/Scheduler_example25_SCHEDULER_CHAIN.ino: -------------------------------------------------------------------------------- 1 | /* 2 | TaskScheduler Example #25 3 | 4 | Create 10 random Tasks. Prints the entire chain. 5 | Then executes chain once printing the remaining chain from currently active task 6 | Deletes all tasks afterwards 7 | */ 8 | 9 | // #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns 10 | #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass 11 | // #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only 12 | #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids 13 | // #define _TASK_LTS_POINTER // Compile with support for local task storage pointer 14 | // #define _TASK_PRIORITY // Support for layered scheduling priority 15 | // #define _TASK_MICRO_RES // Support for microsecond resolution 16 | // #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY) 17 | // #define _TASK_DEBUG // Make all methods and variables public for debug purposes 18 | // #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations 19 | // #define _TASK_TIMEOUT // Support for overall task timeout 20 | // #define _TASK_OO_CALLBACKS // Support for dynamic callback method binding 21 | // #define _TASK_DEFINE_MILLIS // Force forward declaration of millis() and micros() "C" style 22 | #define _TASK_EXPOSE_CHAIN // Methods to access tasks in the task chain 23 | 24 | #include 25 | #include 26 | 27 | // Debug and Test options 28 | #define _DEBUG_ 29 | //#define _TEST_ 30 | 31 | #ifdef _DEBUG_ 32 | #define _PP(a) Serial.print(a); 33 | #define _PL(a) Serial.println(a); 34 | #else 35 | #define _PP(a) 36 | #define _PL(a) 37 | #endif 38 | 39 | 40 | // Scheduler 41 | Scheduler ts; 42 | 43 | void taskCallback(); 44 | void cleanUp(); 45 | 46 | Task tManager(6000, TASK_ONCE, &cleanUp, &ts, false); 47 | /* 48 | Scheduling defines: 49 | TASK_MILLISECOND 50 | TASK_SECOND 51 | TASK_MINUTE 52 | TASK_HOUR 53 | TASK_IMMEDIATE 54 | TASK_FOREVER 55 | TASK_ONCE 56 | TASK_NOTIMEOUT 57 | */ 58 | 59 | int noOfTasks = 10; 60 | QueueArray toDelete; 61 | 62 | void setup() { 63 | // put your setup code here, to run once: 64 | #if defined(_DEBUG_) || defined(_TEST_) 65 | Serial.begin(115200); 66 | delay(2000); 67 | _PL("Scheduler Example: Expose Scheduler Task Chain"); _PL(); 68 | #endif 69 | 70 | _PL("Generating a random chain of tasks"); 71 | for (int i = 0; i < noOfTasks; i++) { 72 | long p = random(100, 5001); // from 100 ms to 5 seconds 73 | long j = random(1, 11); // from 1 to 10 iterations) 74 | Task *t = new Task(p, j, &taskCallback, &ts, false); 75 | _PP(F("Generated a new task:\t")); _PP(t->getId()); _PP(F("\tInt, Iter = \t")); 76 | _PP(p); Serial.print(", "); _PL(j); 77 | if ( random(1, 100) > 50 ) { 78 | t->enable(); 79 | } 80 | else { 81 | t->enableDelayed(); 82 | } 83 | toDelete.push(t); 84 | } 85 | _PL(); 86 | _PL("Printing the entire chain"); 87 | Task* f = ts.getFirstTask(); 88 | Task* l = ts.getLastTask(); 89 | while (f) { 90 | _PP("Task #"); _PL(f->getId()); 91 | f = f->getNextTask(); 92 | } 93 | _PL(); 94 | tManager.enableDelayed(); 95 | } 96 | 97 | 98 | void loop() { 99 | ts.execute(); 100 | } 101 | 102 | 103 | void taskCallback() { 104 | _PP(millis()); 105 | _PP(": taskCallback() of task #"); 106 | Task* t = ts.getCurrentTask(); 107 | t->disable(); 108 | _PL(t->getId()); 109 | Task* l = ts.getLastTask(); 110 | while (t) { 111 | _PP("#"); _PP(t->getId()); 112 | if (t->getNextTask() != NULL) _PP("->"); 113 | t = t->getNextTask(); 114 | } 115 | _PL(); _PL(); 116 | } 117 | 118 | void cleanUp() { 119 | _PL("Deleting tasks:"); 120 | do { 121 | Task* t = toDelete.pop(); 122 | _PP("Deleting task #"); _PL(t->getId()); 123 | delete t; 124 | } while (!toDelete.isEmpty()); 125 | 126 | for (;;) ; 127 | } 128 | -------------------------------------------------------------------------------- /examples/Scheduler_example26_SCHEDULING_OPTIONS/Scheduler_example26_SCHEDULING_OPTIONS.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Example of scheduling options: 3 | 4 | t1 - is a default option with priority given to schedule, i.e., scheduler tries 5 | maintain original schedule and performs task "catch up" to ensure the number 6 | of iterations that were supposed to happen do happen. 7 | 8 | t2 - is an option with priority given to schedule, but without "catch up" 9 | the scheduler will try to maintain original schedule, but next task invocation 10 | is always scheduled to happen in the future 11 | 12 | t3 - is a option with priority given to interval. Task are scheduled always in the 13 | future from the point of their current invocation start using task's interval. 14 | 15 | 16 | */ 17 | // ==== DEFINES =================================================================================== 18 | 19 | // ==== Debug and Test options ================== 20 | #define _DEBUG_ 21 | //#define _TEST_ 22 | 23 | //===== Debugging macros ======================== 24 | #ifdef _DEBUG_ 25 | #define SerialD Serial 26 | #define _PM(a) SerialD.print(millis()); SerialD.print(": "); SerialD.println(a) 27 | #define _PP(a) SerialD.print(a) 28 | #define _PL(a) SerialD.println(a) 29 | #define _PX(a) SerialD.println(a, HEX) 30 | #else 31 | #define _PM(a) 32 | #define _PP(a) 33 | #define _PL(a) 34 | #define _PX(a) 35 | #endif 36 | 37 | 38 | 39 | 40 | // ==== INCLUDES ================================================================================== 41 | 42 | // ==== Uncomment desired compile options ================================= 43 | // #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns 44 | // #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass 45 | // #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only 46 | // #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids 47 | // #define _TASK_LTS_POINTER // Compile with support for local task storage pointer 48 | // #define _TASK_PRIORITY // Support for layered scheduling priority 49 | // #define _TASK_MICRO_RES // Support for microsecond resolution 50 | // #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY) 51 | // #define _TASK_DEBUG // Make all methods and variables public for debug purposes 52 | // #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations 53 | // #define _TASK_TIMEOUT // Support for overall task timeout 54 | // #define _TASK_OO_CALLBACKS // Support for dynamic callback method binding 55 | // #define _TASK_DEFINE_MILLIS // Force forward declaration of millis() and micros() "C" style 56 | // #define _TASK_EXPOSE_CHAIN // Methods to access tasks in the task chain 57 | #define _TASK_SCHEDULING_OPTIONS // Support for multiple scheduling options 58 | #include 59 | 60 | 61 | 62 | // ==== GLOBALS =================================================================================== 63 | // ==== Scheduler ============================== 64 | Scheduler ts; 65 | 66 | void t1CB(); 67 | void t2CB(); 68 | void t3CB(); 69 | 70 | // ==== Scheduling defines (cheat sheet) ===================== 71 | /* 72 | TASK_MILLISECOND 73 | TASK_SECOND 74 | TASK_MINUTE 75 | TASK_HOUR 76 | TASK_IMMEDIATE 77 | TASK_FOREVER 78 | TASK_ONCE 79 | TASK_NOTIMEOUT 80 | 81 | TASK_SCHEDULE - schedule is a priority, with "catch up" (default) 82 | TASK_SCHEDULE_NC - schedule is a priority, without "catch up" 83 | TASK_INTERVAL - interval is a priority, without "catch up" 84 | */ 85 | 86 | // ==== Task definitions ======================== 87 | Task t1_schedule (100 * TASK_MILLISECOND, 10, &t1CB, &ts); 88 | Task t2_schedule_nc (100 * TASK_MILLISECOND, 10, &t2CB, &ts); 89 | Task t3_interval (100 * TASK_MILLISECOND, 10, &t3CB, &ts); 90 | 91 | 92 | 93 | // ==== CODE ====================================================================================== 94 | 95 | /**************************************************************************/ 96 | /*! 97 | @brief Standard Arduino SETUP method - initialize sketch 98 | @param none 99 | @returns none 100 | */ 101 | /**************************************************************************/ 102 | void setup() { 103 | // put your setup code here, to run once: 104 | #if defined(_DEBUG_) || defined(_TEST_) 105 | Serial.begin(115200); 106 | delay(1000); 107 | _PL("Scheduling Options: setup()"); 108 | #endif 109 | 110 | t2_schedule_nc.setSchedulingOption(TASK_SCHEDULE_NC); 111 | t3_interval.setSchedulingOption(TASK_INTERVAL); 112 | 113 | 114 | _PM("t1 start time"); 115 | t1_schedule.enable(); 116 | delay(10); 117 | 118 | _PM("t2 start time"); 119 | t2_schedule_nc.enable(); 120 | delay(10); 121 | 122 | _PM("t3 start time"); 123 | t3_interval.enable(); 124 | 125 | delay(333); 126 | _PM("333 ms delay ended"); 127 | } 128 | 129 | 130 | /**************************************************************************/ 131 | /*! 132 | @brief Standard Arduino LOOP method - using with TaskScheduler there 133 | should be nothing here but ts.execute() 134 | @param none 135 | @returns none 136 | */ 137 | /**************************************************************************/ 138 | void loop() { 139 | ts.execute(); 140 | } 141 | 142 | 143 | /**************************************************************************/ 144 | /*! 145 | @brief Callback method of task1 - explain 146 | @param none 147 | @returns none 148 | */ 149 | /**************************************************************************/ 150 | void t1CB() { 151 | _PM("t1CB()"); 152 | // task code 153 | delay(10); 154 | } 155 | 156 | 157 | /**************************************************************************/ 158 | /*! 159 | @brief Callback method of task2 - explain 160 | @param none 161 | @returns none 162 | */ 163 | /**************************************************************************/ 164 | void t2CB() { 165 | _PM("t2CB()"); 166 | // task code 167 | delay(10); 168 | } 169 | 170 | /**************************************************************************/ 171 | /*! 172 | @brief Callback method of task3 - explain 173 | @param none 174 | @returns none 175 | */ 176 | /**************************************************************************/ 177 | void t3CB() { 178 | _PM("t3CB()"); 179 | // task code 180 | delay(10); 181 | } 182 | -------------------------------------------------------------------------------- /examples/Scheduler_example27_PlatformIO/.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode/.browse.c_cpp.db* 3 | .vscode/c_cpp_properties.json 4 | .vscode/launch.json 5 | .vscode/ipch 6 | -------------------------------------------------------------------------------- /examples/Scheduler_example27_PlatformIO/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /examples/Scheduler_example27_PlatformIO/include/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project header files. 3 | 4 | A header file is a file containing C declarations and macro definitions 5 | to be shared between several project source files. You request the use of a 6 | header file in your project source file (C, C++, etc) located in `src` folder 7 | by including it, with the C preprocessing directive `#include'. 8 | 9 | ```src/main.c 10 | 11 | #include "header.h" 12 | 13 | int main (void) 14 | { 15 | ... 16 | } 17 | ``` 18 | 19 | Including a header file produces the same results as copying the header file 20 | into each source file that needs it. Such copying would be time-consuming 21 | and error-prone. With a header file, the related declarations appear 22 | in only one place. If they need to be changed, they can be changed in one 23 | place, and programs that include the header file will automatically use the 24 | new version when next recompiled. The header file eliminates the labor of 25 | finding and changing all the copies as well as the risk that a failure to 26 | find one copy will result in inconsistencies within a program. 27 | 28 | In C, the usual convention is to give header files names that end with `.h'. 29 | It is most portable to use only letters, digits, dashes, and underscores in 30 | header file names, and at most one dot. 31 | 32 | Read more about using header files in official GCC documentation: 33 | 34 | * Include Syntax 35 | * Include Operation 36 | * Once-Only Headers 37 | * Computed Includes 38 | 39 | https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html 40 | -------------------------------------------------------------------------------- /examples/Scheduler_example27_PlatformIO/include/led.h: -------------------------------------------------------------------------------- 1 | #ifndef _TS27_LED_H 2 | #define _TS27_LED_H 3 | 4 | // LED_BUILTIN 13 5 | #if defined( ARDUINO_ARCH_ESP32 ) 6 | #define LED_BUILTIN 23 // esp32 dev2 kit does not have LED 7 | #endif 8 | 9 | void LEDOff(); 10 | void LEDOn(); 11 | 12 | #endif // _TS27_LED_H -------------------------------------------------------------------------------- /examples/Scheduler_example27_PlatformIO/include/main.h: -------------------------------------------------------------------------------- 1 | #ifndef _TS27_MAIN_H 2 | #define _TS27_MAIN_H 3 | 4 | #include 5 | 6 | #ifdef _DEBUG_ 7 | #define _PP(a) Serial.print(a); 8 | #define _PL(a) Serial.println(a); 9 | #else 10 | #define _PP(a) 11 | #define _PL(a) 12 | #endif 13 | 14 | #define PERIOD1 500 15 | #define DURATION 10000 16 | 17 | #define PERIOD2 400 18 | 19 | #define PERIOD3 300 20 | 21 | #define PERIOD4 200 22 | 23 | #define PERIOD5 600 24 | 25 | #define PERIOD6 300 26 | 27 | #endif -------------------------------------------------------------------------------- /examples/Scheduler_example27_PlatformIO/lib/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link into executable file. 4 | 5 | The source code of each library should be placed in a an own separate directory 6 | ("lib/your_library_name/[here are source files]"). 7 | 8 | For example, see a structure of the following two libraries `Foo` and `Bar`: 9 | 10 | |--lib 11 | | | 12 | | |--Bar 13 | | | |--docs 14 | | | |--examples 15 | | | |--src 16 | | | |- Bar.c 17 | | | |- Bar.h 18 | | | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html 19 | | | 20 | | |--Foo 21 | | | |- Foo.c 22 | | | |- Foo.h 23 | | | 24 | | |- README --> THIS FILE 25 | | 26 | |- platformio.ini 27 | |--src 28 | |- main.c 29 | 30 | and a contents of `src/main.c`: 31 | ``` 32 | #include 33 | #include 34 | 35 | int main (void) 36 | { 37 | ... 38 | } 39 | 40 | ``` 41 | 42 | PlatformIO Library Dependency Finder will find automatically dependent 43 | libraries scanning project source files. 44 | 45 | More information about PlatformIO Library Dependency Finder 46 | - https://docs.platformio.org/page/librarymanager/ldf.html 47 | -------------------------------------------------------------------------------- /examples/Scheduler_example27_PlatformIO/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [env:esp32dev] 12 | platform = espressif32 13 | board = esp32dev 14 | framework = arduino 15 | 16 | [env] 17 | lib_deps = 18 | arkhipenko/TaskScheduler @ ^3.4.0 19 | 20 | build_flags = 21 | ; -D _TASK_TIMECRITICAL 22 | -D _TASK_SLEEP_ON_IDLE_RUN 23 | -D _TASK_STATUS_REQUEST 24 | ; -D _TASK_WDT_IDS 25 | ; -D _TASK_LTS_POINTER 26 | ; -D _TASK_PRIORITY 27 | ; -D _TASK_MICRO_RES 28 | ; -D _TASK_STD_FUNCTION 29 | ; -D _TASK_DEBUG 30 | ; -D _TASK_INLINE 31 | ; -D _TASK_TIMEOUT 32 | ; -D _TASK_OO_CALLBACKS 33 | ; -D _TASK_EXPOSE_CHAIN 34 | ; -D _TASK_SCHEDULING_OPTIONS 35 | ; -D _TASK_DEFINE_MILLIS 36 | ; -D _TASK_EXTERNAL_TIME 37 | -D _DEBUG_ 38 | ; -D _TEST_ 39 | -------------------------------------------------------------------------------- /examples/Scheduler_example27_PlatformIO/src/TaskScheduler.cpp: -------------------------------------------------------------------------------- 1 | #include -------------------------------------------------------------------------------- /examples/Scheduler_example27_PlatformIO/src/led.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "led.h" 3 | #include "main.h" 4 | 5 | void LEDOn() { 6 | digitalWrite( LED_BUILTIN, HIGH ); 7 | } 8 | 9 | void LEDOff() { 10 | digitalWrite( LED_BUILTIN, LOW ); 11 | } 12 | -------------------------------------------------------------------------------- /examples/Scheduler_example27_PlatformIO/src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Every example set must have a LED blink example 3 | For this one the idea is to have as many ways to blink the LED 4 | as I can think of. So, here we go. 5 | 6 | Tested on: 7 | - Arduino Nano 8 | - ESP8266 9 | - ESP32 10 | - STM32 Maple Mini 11 | */ 12 | 13 | #include 14 | 15 | #include "main.h" 16 | #include "led.h" 17 | 18 | // Scheduler 19 | Scheduler ts; 20 | 21 | /* 22 | Approach 1: LED is driven by the boolean variable; false = OFF, true = ON 23 | */ 24 | void blink1CB(); 25 | Task tBlink1 ( PERIOD1 * TASK_MILLISECOND, DURATION / PERIOD1, &blink1CB, &ts, true ); 26 | 27 | /* 28 | Approach 2: two callback methods: one turns ON, another turns OFF 29 | */ 30 | void blink2CB_ON(); 31 | void blink2CB_OFF(); 32 | Task tBlink2 ( PERIOD2 * TASK_MILLISECOND, DURATION / PERIOD2, &blink2CB_ON, &ts, false ); 33 | 34 | /* 35 | Approach 3: Use RunCounter 36 | */ 37 | void blink3CB(); 38 | Task tBlink3 (PERIOD3 * TASK_MILLISECOND, DURATION / PERIOD3, &blink3CB, &ts, false); 39 | 40 | /* 41 | Approach 4: Use status request objects to pass control from one task to the other 42 | */ 43 | bool blink41OE(); 44 | void blink41(); 45 | void blink42(); 46 | void blink42OD(); 47 | Task tBlink4On ( PERIOD4 * TASK_MILLISECOND, TASK_ONCE, blink41, &ts, false, &blink41OE ); 48 | Task tBlink4Off ( PERIOD4 * TASK_MILLISECOND, TASK_ONCE, blink42, &ts, false, NULL, &blink42OD ); 49 | 50 | 51 | /* 52 | Approach 5: Two interleaving tasks 53 | */ 54 | bool blink51OE(); 55 | void blink51(); 56 | void blink52(); 57 | void blink52OD(); 58 | Task tBlink5On ( 600 * TASK_MILLISECOND, DURATION / PERIOD5, &blink51, &ts, false, &blink51OE ); 59 | Task tBlink5Off ( 600 * TASK_MILLISECOND, DURATION / PERIOD5, &blink52, &ts, false, NULL, &blink52OD ); 60 | 61 | 62 | /* 63 | Approach 6: RunCounter-based with random intervals 64 | */ 65 | void blink6CB(); 66 | bool blink6OE(); 67 | void blink6OD(); 68 | Task tBlink6 ( PERIOD6 * TASK_MILLISECOND, DURATION / PERIOD6, &blink6CB, &ts, false, &blink6OE, &blink6OD ); 69 | 70 | void setup() { 71 | // put your setup code here, to run once: 72 | #if defined(_DEBUG_) || defined(_TEST_) 73 | Serial.begin(115200); 74 | delay(TASK_SECOND); 75 | _PL("TaskScheduler Blink example"); 76 | _PL("Blinking for 10 seconds using various techniques\n"); 77 | delay(2 * TASK_SECOND); 78 | #endif 79 | pinMode(LED_BUILTIN, OUTPUT); 80 | } 81 | 82 | void loop() { 83 | ts.execute(); 84 | } 85 | 86 | // === 1 ======================================= 87 | bool LED_state = false; 88 | void blink1CB() { 89 | if ( tBlink1.isFirstIteration() ) { 90 | _PP(millis()); 91 | _PL(": Blink1 - simple flag driven"); 92 | LED_state = false; 93 | } 94 | 95 | if ( LED_state ) { 96 | LEDOff(); 97 | LED_state = false; 98 | } 99 | else { 100 | LEDOn(); 101 | LED_state = true; 102 | } 103 | 104 | if ( tBlink1.isLastIteration() ) { 105 | tBlink2.restartDelayed( 2 * TASK_SECOND ); 106 | LEDOff(); 107 | } 108 | } 109 | 110 | 111 | // === 2 ====================================== 112 | void blink2CB_ON() { 113 | if ( tBlink2.isFirstIteration() ) { 114 | _PP(millis()); 115 | _PL(": Blink2 - 2 callback methods"); 116 | } 117 | 118 | LEDOn(); 119 | tBlink2.setCallback( &blink2CB_OFF ); 120 | 121 | if ( tBlink2.isLastIteration() ) { 122 | tBlink3.restartDelayed( 2 * TASK_SECOND ); 123 | LEDOff(); 124 | } 125 | } 126 | 127 | 128 | void blink2CB_OFF() { 129 | 130 | LEDOff(); 131 | tBlink2.setCallback( &blink2CB_ON ); 132 | 133 | if ( tBlink2.isLastIteration() ) { 134 | tBlink3.restartDelayed( 2 * TASK_SECOND ); 135 | LEDOff(); 136 | } 137 | } 138 | 139 | 140 | // === 3 ===================================== 141 | void blink3CB() { 142 | if ( tBlink3.isFirstIteration() ) { 143 | _PP(millis()); 144 | _PL(": Blink3 - Run Counter driven"); 145 | } 146 | 147 | if ( tBlink3.getRunCounter() & 1 ) { 148 | LEDOn(); 149 | } 150 | else { 151 | LEDOff(); 152 | } 153 | 154 | if ( tBlink3.isLastIteration() ) { 155 | tBlink4On.setOnEnable( &blink41OE ); 156 | tBlink4On.restartDelayed( 2 * TASK_SECOND ); 157 | LEDOff(); 158 | } 159 | } 160 | 161 | 162 | // === 4 ============================================= 163 | int counter = 0; 164 | bool blink41OE() { 165 | _PP(millis()); 166 | _PL(": Blink4 - Internal status request based"); 167 | counter = 0; 168 | tBlink4On.setOnEnable( NULL ); 169 | return true; 170 | } 171 | 172 | void blink41() { 173 | // _PP(millis()); 174 | // _PL(": blink41"); 175 | LEDOn(); 176 | StatusRequest* r = tBlink4On.getInternalStatusRequest(); 177 | tBlink4Off.waitForDelayed( r ); 178 | counter++; 179 | } 180 | 181 | void blink42() { 182 | // _PP(millis()); 183 | // _PL(": blink42"); 184 | LEDOff(); 185 | StatusRequest* r = tBlink4Off.getInternalStatusRequest(); 186 | tBlink4On.waitForDelayed( r ); 187 | counter++; 188 | } 189 | 190 | 191 | void blink42OD() { 192 | if ( counter >= DURATION / PERIOD4 ) { 193 | tBlink4On.disable(); 194 | tBlink4Off.disable(); 195 | 196 | tBlink5On.setOnEnable( &blink51OE ); 197 | tBlink5On.restartDelayed( 2 * TASK_SECOND ); 198 | tBlink5Off.restartDelayed( 2 * TASK_SECOND + PERIOD5 / 2 ); 199 | LEDOff(); 200 | } 201 | } 202 | 203 | 204 | // === 5 ========================================== 205 | bool blink51OE() { 206 | _PP(millis()); 207 | _PL(": Blink5 - Two interleaving tasks"); 208 | tBlink5On.setOnEnable( NULL ); 209 | return true; 210 | } 211 | void blink51() { 212 | // _PP(millis()); 213 | // _PL(": blink51"); 214 | LEDOn(); 215 | } 216 | void blink52() { 217 | // _PP(millis()); 218 | // _PL(": blink52"); 219 | LEDOff(); 220 | } 221 | void blink52OD() { 222 | tBlink6.restartDelayed( 2 * TASK_SECOND ); 223 | LEDOff(); 224 | } 225 | 226 | 227 | // === 6 ============================================ 228 | long interval6 = 0; 229 | bool blink6OE() { 230 | _PP(millis()); 231 | _PP(": Blink6 - RunCounter + Random ON interval = "); 232 | interval6 = random( 100, 901 ); 233 | tBlink6.setInterval( interval6 ); 234 | _PL( interval6 ); 235 | tBlink6.delay( 2 * TASK_SECOND ); 236 | 237 | return true; 238 | } 239 | 240 | void blink6CB() { 241 | if ( tBlink6.getRunCounter() & 1 ) { 242 | LEDOn(); 243 | tBlink6.setInterval( interval6 ); 244 | } 245 | else { 246 | LEDOff(); 247 | tBlink6.setInterval( TASK_SECOND - interval6 ); 248 | } 249 | } 250 | 251 | void blink6OD() { 252 | tBlink1.restartDelayed( 2 * TASK_SECOND ); 253 | LEDOff(); 254 | } 255 | -------------------------------------------------------------------------------- /examples/Scheduler_example27_PlatformIO/test/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for PlatformIO Unit Testing and project tests. 3 | 4 | Unit Testing is a software testing method by which individual units of 5 | source code, sets of one or more MCU program modules together with associated 6 | control data, usage procedures, and operating procedures, are tested to 7 | determine whether they are fit for use. Unit testing finds problems early 8 | in the development cycle. 9 | 10 | More information about PlatformIO Unit Testing: 11 | - https://docs.platformio.org/page/plus/unit-testing.html 12 | -------------------------------------------------------------------------------- /examples/Scheduler_example28_Tickless/Scheduler_example28_Tickless.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * TaskScheduler Test 3 | * 4 | * This test illustrates how to use TS's tickless support functionality 5 | * Tickless support enables more deterministic sleep by calculating time delay until next task invocation 6 | * during every pass. That delay could then be used to put microcontroller in sleep mode continously 7 | * instead of in small intervals 8 | * 9 | * Initially only tasks 1 and 2 are enabled 10 | * Task1 runs every 2 seconds 10 times and then stops 11 | * Task2 runs every 3 seconds indefinitely 12 | * Task1 enables Task3 at its first run 13 | * Task3 run every 5 seconds 14 | * Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds 15 | * At the end Task2 is the only task running every 1/2 seconds 16 | */ 17 | 18 | #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between runs if no callback methods were invoked during the pass 19 | #define _TASK_TICKLESS // Enable support for tickless sleep on FreeRTOS 20 | #define _TASK_DO_NOT_YIELD // Disable yield() method in execute() 21 | 22 | #include 23 | 24 | // Callback methods prototypes 25 | void t1Callback(); 26 | void t2Callback(); 27 | void t3Callback(); 28 | 29 | //Tasks 30 | Task t4(); 31 | Task t1(2000, 10, &t1Callback); 32 | Task t2(3000, TASK_FOREVER, &t2Callback); 33 | Task t3(5000, TASK_FOREVER, &t3Callback); 34 | 35 | Scheduler runner; 36 | 37 | 38 | void t1Callback() { 39 | Serial.print("t1: "); 40 | Serial.println(millis()); 41 | 42 | if (t1.isFirstIteration()) { 43 | runner.addTask(t3); 44 | t3.enable(); 45 | Serial.println("t1: enabled t3 and added to the chain"); 46 | } 47 | 48 | if (t1.isLastIteration()) { 49 | t3.disable(); 50 | runner.deleteTask(t3); 51 | t2.setInterval(500); 52 | Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500"); 53 | } 54 | } 55 | 56 | void t2Callback() { 57 | Serial.print("t2: "); 58 | Serial.println(millis()); 59 | 60 | // comment this line out if you want to test t2's 500 ms explicit delay 61 | // as-is this delay tests that task in catch up mode will prevent explicit tickless delay 62 | delay(501); 63 | 64 | } 65 | 66 | void t3Callback() { 67 | Serial.print("t3: "); 68 | Serial.println(millis()); 69 | 70 | } 71 | 72 | void setup () { 73 | Serial.begin(115200); 74 | Serial.println("Scheduler TEST"); 75 | 76 | runner.init(); 77 | Serial.println("Initialized scheduler"); 78 | 79 | runner.addTask(t1); 80 | Serial.println("added t1"); 81 | 82 | runner.addTask(t2); 83 | Serial.println("added t2"); 84 | 85 | delay(1000); 86 | 87 | t1.enable(); 88 | Serial.println("Enabled t1"); 89 | t2.enable(); 90 | Serial.println("Enabled t2"); 91 | } 92 | 93 | unsigned long nr = 0; 94 | void loop () { 95 | runner.execute(); 96 | nr = runner.getNextRun(); 97 | if ( nr ) { 98 | Serial.println("TS stats:"); 99 | Serial.print("\tTotal tasks: "); Serial.println(runner.getTotalTasks() ); 100 | Serial.print("\tActive tasks: "); Serial.println(runner.getActiveTasks() ); 101 | Serial.print("\tInvoked tasks: "); Serial.println(runner.getInvokedTasks() ); 102 | Serial.print("Next scheduling pass in "); Serial.print(nr); Serial.println(" ms"); 103 | delay(nr); 104 | } 105 | } -------------------------------------------------------------------------------- /examples/Scheduler_template/Scheduler_template.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Your code description here 3 | (c) you, 20XX 4 | All rights reserved. 5 | 6 | Functionality: 7 | 8 | Version log: 9 | 10 | 20XX-MM-DD: 11 | v1.0.0 - Initial release 12 | 13 | */ 14 | // ==== DEFINES =================================================================================== 15 | 16 | // ==== Debug and Test options ================== 17 | #define _DEBUG_ 18 | //#define _TEST_ 19 | 20 | //===== Debugging macros ======================== 21 | #ifdef _DEBUG_ 22 | #define SerialD Serial 23 | #define _PM(a) SerialD.print(millis()); SerialD.print(": "); SerialD.println(a) 24 | #define _PP(a) SerialD.print(a) 25 | #define _PL(a) SerialD.println(a) 26 | #define _PX(a) SerialD.println(a, HEX) 27 | #else 28 | #define _PM(a) 29 | #define _PP(a) 30 | #define _PL(a) 31 | #define _PX(a) 32 | #endif 33 | 34 | 35 | 36 | 37 | // ==== INCLUDES ================================================================================== 38 | 39 | // ==== Uncomment desired compile options ================================= 40 | // ---------------------------------------- 41 | // The following "defines" control library functionality at compile time, 42 | // and should be used in the main sketch depending on the functionality required 43 | // Should be defined BEFORE #include !!! 44 | // 45 | // #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns 46 | // #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between runs if no callback methods were invoked during the pass 47 | // #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only 48 | // #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids 49 | // #define _TASK_LTS_POINTER // Compile with support for local task storage pointer 50 | // #define _TASK_PRIORITY // Support for layered scheduling priority 51 | // #define _TASK_MICRO_RES // Support for microsecond resolution 52 | // #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY) 53 | // #define _TASK_DEBUG // Make all methods and variables public for debug purposes 54 | // #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations 55 | // #define _TASK_TIMEOUT // Support for overall task timeout 56 | // #define _TASK_OO_CALLBACKS // Support for callbacks via inheritance 57 | // #define _TASK_EXPOSE_CHAIN // Methods to access tasks in the task chain 58 | // #define _TASK_SCHEDULING_OPTIONS // Support for multiple scheduling options 59 | // #define _TASK_DEFINE_MILLIS // Force forward declaration of millis() and micros() "C" style 60 | // #define _TASK_EXTERNAL_TIME // Custom millis() and micros() methods 61 | // #define _TASK_THREAD_SAFE // Enable additional checking for thread safety 62 | // #define _TASK_SELF_DESTRUCT // Enable tasks to "self-destruct" after disable 63 | 64 | #include 65 | 66 | 67 | 68 | // ==== GLOBALS =================================================================================== 69 | // ==== Scheduler ============================== 70 | Scheduler ts; 71 | 72 | void task1Callback(); 73 | void task2Callback(); 74 | 75 | // ==== Scheduling defines (cheat sheet) ===================== 76 | /* 77 | TASK_MILLISECOND - one millisecond in millisecond/microseconds 78 | TASK_SECOND - one second in millisecond/microseconds 79 | TASK_MINUTE - one minute in millisecond/microseconds 80 | TASK_HOUR - one hour in millisecond/microseconds 81 | TASK_IMMEDIATE - schedule task to runn as soon as possible 82 | TASK_FOREVER - run task indefinitely 83 | TASK_ONCE - run task once 84 | TASK_NOTIMEOUT - set timeout interval to No Timeout 85 | 86 | TASK_SCHEDULE - schedule is a priority, with "catch up" (default) 87 | TASK_SCHEDULE_NC - schedule is a priority, without "catch up" 88 | TASK_INTERVAL - interval is a priority, without "catch up" 89 | 90 | TASK_SR_OK - status request triggered with an OK code (all good) 91 | TASK_SR_ERROR - status request triggered with an ERROR code 92 | TASK_SR_CANCEL - status request was cancelled 93 | TASK_SR_ABORT - status request was aborted 94 | TASK_SR_TIMEOUT - status request timed out 95 | */ 96 | 97 | // ==== Task definitions ======================== 98 | Task t1 (100 * TASK_MILLISECOND, TASK_FOREVER, &task1Callback, &ts, true); 99 | Task t2 (TASK_IMMEDIATE, 100 /* times */, &task2Callback, &ts, true); 100 | 101 | 102 | 103 | // ==== CODE ====================================================================================== 104 | 105 | /**************************************************************************/ 106 | /*! 107 | @brief Standard Arduino SETUP method - initialize sketch 108 | @param none 109 | @returns none 110 | */ 111 | /**************************************************************************/ 112 | void setup() { 113 | // put your setup code here, to run once: 114 | #if defined(_DEBUG_) || defined(_TEST_) 115 | Serial.begin(115200); 116 | delay(2000); 117 | _PL("Scheduler Template: setup()"); 118 | #endif 119 | } 120 | 121 | 122 | /**************************************************************************/ 123 | /*! 124 | @brief Standard Arduino LOOP method - using with TaskScheduler there 125 | should be nothing here but ts.execute() 126 | @param none 127 | @returns none 128 | */ 129 | /**************************************************************************/ 130 | void loop() { 131 | ts.execute(); 132 | } 133 | 134 | 135 | /**************************************************************************/ 136 | /*! 137 | @brief Callback method of task1 - explain 138 | @param none 139 | @returns none 140 | */ 141 | /**************************************************************************/ 142 | void task1Callback() { 143 | _PM("task1Callback()"); 144 | // task code 145 | } 146 | 147 | 148 | /**************************************************************************/ 149 | /*! 150 | @brief Callback method of task2 - explain 151 | @param none 152 | @returns none 153 | */ 154 | /**************************************************************************/ 155 | void task2Callback() { 156 | _PM("task2Callback()"); 157 | // task code 158 | } 159 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /extras/TaskScheduler_html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arkhipenko/TaskScheduler/16ec5be7bafbeb197fe22e1d4a90b622fd879eff/extras/TaskScheduler_html.png -------------------------------------------------------------------------------- /extras/These documents are no longer current.txt: -------------------------------------------------------------------------------- 1 | As of May 2020 all offline documents are deprecated. 2 | For the latest documentation of TaskScheduler please visit: 3 | https://github.com/arkhipenko/TaskScheduler/wiki 4 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For TaskManager 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | Scheduler KEYWORD1 10 | StatusRequest KEYWORD1 11 | Task KEYWORD1 12 | TS KEYWORD1 13 | TaskScheduler KEYWORD1 14 | TsScheduler KEYWORD1 15 | TsStatusRequest KEYWORD1 16 | TsTask KEYWORD1 17 | TsTaskScheduler KEYWORD1 18 | 19 | ####################################### 20 | # Methods and Functions (KEYWORD2) 21 | ####################################### 22 | 23 | abort KEYWORD2 24 | addTask KEYWORD2 25 | adjust KEYWORD2 26 | allowSleep KEYWORD2 27 | Callback KEYWORD2 28 | cancel KEYWORD2 29 | canceled KEYWORD2 30 | completed KEYWORD2 31 | cpuLoadReset KEYWORD2 32 | currentLts KEYWORD2 33 | delay KEYWORD2 34 | deleteTask KEYWORD2 35 | disable KEYWORD2 36 | disableAll KEYWORD2 37 | disableOnLastIteration KEYWORD2 38 | enable KEYWORD2 39 | enableAll KEYWORD2 40 | enableDelayed KEYWORD2 41 | enableIfNot KEYWORD2 42 | execute KEYWORD2 43 | forceNextIteration KEYWORD2 44 | getActiveTasks KEYWORD2 45 | getControlPoint KEYWORD2 46 | getCount KEYWORD2 47 | getCount KEYWORD2 48 | getCpuLoadCycle KEYWORD2 49 | getCpuLoadIdle KEYWORD2 50 | getCpuLoadTotal KEYWORD2 51 | getCurrentTask KEYWORD2 52 | getFirstTask KEYWORD2 53 | getId KEYWORD2 54 | getInternalStatusRequest KEYWORD2 55 | getInterval KEYWORD2 56 | getInvokedTasks KEYWORD2 57 | getIterations KEYWORD2 58 | getLastTask KEYWORD2 59 | getLtsPointer KEYWORD2 60 | getNextTask KEYWORD2 61 | getNextRun KEYWORD2 62 | getOverrun KEYWORD2 63 | getPreviousTask KEYWORD2 64 | getRunCounter KEYWORD2 65 | getSchedulingOption KEYWORD2 66 | getSelfDestruct KEYWORD2 67 | getStartDelay KEYWORD2 68 | getStatus KEYWORD2 69 | getStatusRequest KEYWORD2 70 | getTimeout KEYWORD2 71 | getTotalTasks KEYWORD2 72 | init KEYWORD2 73 | isEnabled KEYWORD2 74 | isFirstIteration KEYWORD2 75 | isLastIteration KEYWORD2 76 | isOverrun KEYWORD2 77 | OnDisable KEYWORD2 78 | OnEnable KEYWORD2 79 | pause KEYWORD2 80 | pending KEYWORD2 81 | resetTimeout KEYWORD2 82 | restart KEYWORD2 83 | restartDelayed KEYWORD2 84 | resume KEYWORD2 85 | set KEYWORD2 86 | setCallback KEYWORD2 87 | setControlPoint KEYWORD2 88 | setHighPriorityScheduler KEYWORD2 89 | setId KEYWORD2 90 | setInterval KEYWORD2 91 | setIterations KEYWORD2 92 | setLtsPointer KEYWORD2 93 | setOnDisable KEYWORD2 94 | setOnEnable KEYWORD2 95 | setSchedulingOption KEYWORD2 96 | setSelfDestruct KEYWORD2 97 | setSleepMethod KEYWORD2 98 | setTimeout KEYWORD2 99 | setWaiting KEYWORD2 100 | signal KEYWORD2 101 | signalComplete KEYWORD2 102 | startNow KEYWORD2 103 | timedOut KEYWORD2 104 | timeUntilNextIteration KEYWORD2 105 | untilTimeout KEYWORD2 106 | waitFor KEYWORD2 107 | waitForDelayed KEYWORD2 108 | yield KEYWORD2 109 | yieldOnce KEYWORD2 110 | 111 | ####################################### 112 | # Constants (LITERAL1) 113 | 114 | _TASK_DEBUG LITERAL1 115 | _TASK_INLINE LITERAL1 116 | _TASK_LTS_POINTER LITERAL1 117 | _TASK_MICRO_RES LITERAL1 118 | _TASK_OO_CALLBACKS LITERAL1 119 | _TASK_PRIORITY LITERAL1 120 | _TASK_SLEEP_ON_IDLE_RUN LITERAL1 121 | _TASK_STATUS_REQUEST LITERAL1 122 | _TASK_STD_FUNCTION LITERAL1 123 | _TASK_TIMECRITICAL LITERAL1 124 | _TASK_TIMEOUT LITERAL1 125 | _TASK_WDT_IDS LITERAL1 126 | _TASK_EXPOSE_CHAIN LITERAL1 127 | _TASK_DEFINE_MILLIS LITERAL1 128 | _TASK_SCHEDULING_OPTIONS LITERAL1 129 | _TASK_DEFINE_MILLIS LITERAL1 130 | _TASK_EXTERNAL_TIME LITERAL1 131 | _TASK_THREAD_SAFE LITERAL1 132 | _TASK_SELF_DESTRUCT LITERAL1 133 | SleepCallback LITERAL1 134 | TASK_FOREVER LITERAL1 135 | TASK_HOUR LITERAL1 136 | TASK_IMMEDIATE LITERAL1 137 | TASK_MILLISECOND LITERAL1 138 | TASK_MINUTE LITERAL1 139 | TASK_NOTIMEOUT LITERAL1 140 | TASK_ONCE LITERAL1 141 | TASK_SECOND LITERAL1 142 | TaskCallback LITERAL1 143 | TaskOnDisable LITERAL1 144 | TaskOnEnable LITERAL1 145 | TASK_SCHEDULE LITERAL1 146 | TASK_SCHEDULE_NC LITERAL1 147 | TASK_INTERVAL LITERAL1 148 | TASK_SR_OK LITERAL1 149 | TASK_SR_ERROR LITERAL1 150 | TASK_SR_CANCEL LITERAL1 151 | TASK_SR_ABORT LITERAL1 152 | TASK_SR_TIMEOUT LITERAL1 153 | TASK_INTERVAL_KEEP LITERAL1 154 | TASK_INTERVAL_RECALC LITERAL1 155 | TASK_INTERVAL_RESET LITERAL1 156 | 157 | ####################################### 158 | 159 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TaskScheduler", 3 | "keywords": "multitasking, cooperative, event, task, taskscheduler, scheduling", 4 | "description": "Cooperative multitasking for Arduino, ESPx, STM32 and other microcontrollers", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/arkhipenko/TaskScheduler" 9 | }, 10 | "authors": 11 | [ 12 | { 13 | "name": "Anatoli Arkhipenko", 14 | "email": "arkhipenko@hotmail.com", 15 | "url": "https://github.com/arkhipenko", 16 | "maintainer": true 17 | } 18 | ], 19 | "version": "3.8.5", 20 | "frameworks": "arduino", 21 | "platforms": "*" 22 | } 23 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=TaskScheduler 2 | version=3.8.5 3 | author=Anatoli Arkhipenko 4 | maintainer=Anatoli Arkhipenko 5 | sentence=Cooperative multitasking for Arduino, ESPx, STM32 and other microcontrollers. 6 | paragraph=Supports: periodic task execution (with dynamic execution period in milliseconds or microseconds – frequency of execution), number of iterations (limited or infinite number of iterations), execution of tasks in predefined sequence, dynamic change of task execution parameters (frequency, number of iterations, callback methods), power saving via entering IDLE sleep mode when tasks are not scheduled to run, event-driven task invocation via Status Request object, task IDs and Control Points for error handling and watchdog timer, Local Task Storage pointer (allowing use of same callback code for multiple tasks), layered task prioritization, std::functions (where supported), overall task timeout, static and dynamic callback method binding. 7 | category=Timing 8 | url=https://github.com/arkhipenko/TaskScheduler.git 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/TScheduler.hpp: -------------------------------------------------------------------------------- 1 | // Cooperative multitasking library for Arduino 2 | // Copyright (c) 2015-2019 Anatoli Arkhipenko 3 | 4 | /* 5 | This is a namespace wrapper to avoid collision with frameworks having their 6 | own class named Scheduler (i.e. nordicnrf52) 7 | include it instead of into your sketch and use Class name wrapper - 'TaskScheduler' 8 | i.e. 9 | TaskScheduler ts; 10 | */ 11 | 12 | #pragma once 13 | #include 14 | 15 | namespace TS{ 16 | #include "TaskScheduler.h" 17 | } 18 | 19 | using TsScheduler = TS::Scheduler; 20 | using TsTask = TS::Task; 21 | #ifdef _TASK_STATUS_REQUEST 22 | using TsStatusRequest = TS::StatusRequest; 23 | #endif 24 | #ifdef _TASK_SLEEP_ON_IDLE_RUN 25 | using TsSleepCallback = TS::SleepCallback; 26 | #endif 27 | using TsTaskCallback = TS::TaskCallback; 28 | using TsTaskOnDisable = TS::TaskOnDisable; 29 | using TsTaskOnEnable = TS::TaskOnEnable; 30 | -------------------------------------------------------------------------------- /src/TSchedulerDeclarations.hpp: -------------------------------------------------------------------------------- 1 | // Cooperative multitasking library for Arduino 2 | // Copyright (c) 2015-2019 Anatoli Arkhipenko 3 | 4 | /* 5 | This is a namespace wrapper to avoid collision with frameworks having their 6 | own class named Scheduler (i.e. nordicnrf52) 7 | include it instead of into your sketch and 8 | use Class name wrapper - 'TaskScheduler' 9 | i.e. 10 | TaskScheduler ts; 11 | */ 12 | 13 | #pragma once 14 | 15 | namespace TS{ 16 | #include "TaskSchedulerDeclarations.h" 17 | } 18 | 19 | using TsScheduler = TS::Scheduler; 20 | using TsTask = TS::Task; 21 | #ifdef _TASK_STATUS_REQUEST 22 | using TsStatusRequest = TS::StatusRequest; 23 | #endif 24 | #ifdef _TASK_SLEEP_ON_IDLE_RUN 25 | using TsSleepCallback = TS::SleepCallback; 26 | #endif 27 | using TsTaskCallback = TS::TaskCallback; 28 | using TsTaskOnDisable = TS::TaskOnDisable; 29 | using TsTaskOnEnable = TS::TaskOnEnable; 30 | -------------------------------------------------------------------------------- /src/TaskSchedulerDeclarations.h: -------------------------------------------------------------------------------- 1 | // Cooperative multitasking library for Arduino 2 | // Copyright (c) 2015-2023 Anatoli Arkhipenko 3 | 4 | #include 5 | #include 6 | 7 | #ifndef _TASKSCHEDULERDECLARATIONS_H_ 8 | #define _TASKSCHEDULERDECLARATIONS_H_ 9 | 10 | // ---------------------------------------- 11 | // The following "defines" control library functionality at compile time, 12 | // and should be used in the main sketch depending on the functionality required 13 | // 14 | // #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns 15 | // #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between runs if no callback methods were invoked during the pass 16 | // #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only 17 | // #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids 18 | // #define _TASK_LTS_POINTER // Compile with support for local task storage pointer 19 | // #define _TASK_PRIORITY // Support for layered scheduling priority 20 | // #define _TASK_MICRO_RES // Support for microsecond resolution 21 | // #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY) 22 | // #define _TASK_DEBUG // Make all methods and variables public for debug purposes 23 | // #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations 24 | // #define _TASK_TIMEOUT // Support for overall task timeout 25 | // #define _TASK_OO_CALLBACKS // Support for callbacks via inheritance 26 | // #define _TASK_EXPOSE_CHAIN // Methods to access tasks in the task chain 27 | // #define _TASK_SCHEDULING_OPTIONS // Support for multiple scheduling options 28 | // #define _TASK_DEFINE_MILLIS // Force forward declaration of millis() and micros() "C" style 29 | // #define _TASK_EXTERNAL_TIME // Custom millis() and micros() methods 30 | // #define _TASK_THREAD_SAFE // Enable additional checking for thread safety 31 | // #define _TASK_SELF_DESTRUCT // Enable tasks to "self-destruct" after disable 32 | // #define _TASK_TICKLESS // Enable support for tickless sleep on FreeRTOS 33 | // #define _TASK_DO_NOT_YIELD // Disable yield() method in execute() 34 | 35 | class Scheduler; 36 | 37 | #define TASK_SCHEDULE 0 // default 38 | #define TASK_SCHEDULE_NC 1 // schedule + no catch-ups (always in the future) 39 | #define TASK_INTERVAL 2 // interval (always in the future) 40 | 41 | #ifdef _TASK_DEBUG 42 | #define _TASK_SCOPE public 43 | #else 44 | #define _TASK_SCOPE private 45 | #endif 46 | 47 | // task scheduling iteration common options 48 | #define TASK_IMMEDIATE 0 49 | #define TASK_FOREVER (-1) 50 | #define TASK_ONCE 1 51 | 52 | // options for setIntervalNodelay() method 53 | #define TASK_INTERVAL_KEEP 0 54 | #define TASK_INTERVAL_RECALC 1 55 | #define TASK_INTERVAL_RESET 2 56 | 57 | #ifdef _TASK_TIMEOUT 58 | #define TASK_NOTIMEOUT 0 59 | #endif 60 | 61 | #ifdef _TASK_PRIORITY 62 | extern Scheduler* iCurrentScheduler; 63 | #endif // _TASK_PRIORITY 64 | 65 | #ifdef _TASK_INLINE 66 | #define INLINE inline 67 | #else 68 | #define INLINE 69 | #endif 70 | 71 | #ifdef _TASK_EXTERNAL_TIME 72 | uint32_t external_millis(); 73 | #define _task_millis() external_millis() 74 | #ifdef _TASK_MICRO_RES 75 | uint32_t external_micros(); 76 | #define _task_micros() external_micros() 77 | #endif // _TASK_MICRO_RES 78 | #endif // _TASK_EXTERNAL_TIME 79 | 80 | #ifndef _TASK_MICRO_RES 81 | 82 | #define TASK_MILLISECOND 1UL 83 | #define TASK_SECOND 1000UL 84 | #define TASK_MINUTE 60000UL 85 | #define TASK_HOUR 3600000UL 86 | 87 | #else 88 | 89 | #define TASK_MILLISECOND 1000UL 90 | #define TASK_SECOND 1000000UL 91 | #define TASK_MINUTE 60000000UL 92 | #define TASK_HOUR 3600000000UL 93 | 94 | #endif // _TASK_MICRO_RES 95 | 96 | #ifdef _TASK_TICKLESS 97 | #define _TASK_NEXTRUN_UNDEFINED 0b0 98 | #define _TASK_NEXTRUN_IMMEDIATE 0b1 99 | #define _TASK_NEXTRUN_TIMED 0x10 100 | #endif // _TASK_TICKLESS 101 | 102 | #ifdef _TASK_STATUS_REQUEST 103 | 104 | #define TASK_SR_OK 0 105 | #define TASK_SR_ERROR (-1) 106 | #define TASK_SR_CANCEL (-32766) 107 | #define TASK_SR_ABORT (-32767) 108 | #define TASK_SR_TIMEOUT (-32768) 109 | 110 | #define _TASK_SR_NODELAY 1 111 | #define _TASK_SR_DELAY 2 112 | 113 | class StatusRequest { 114 | friend class Scheduler; 115 | public: 116 | INLINE StatusRequest(); 117 | INLINE void setWaiting(unsigned int aCount = 1); 118 | INLINE bool signal(int aStatus = 0); 119 | INLINE void signalComplete(int aStatus = 0); 120 | INLINE bool pending(); 121 | INLINE bool completed(); 122 | INLINE int getStatus(); 123 | INLINE int getCount(); 124 | 125 | #ifdef _TASK_TIMEOUT 126 | INLINE void setTimeout(unsigned long aTimeout) { iTimeout = aTimeout; }; 127 | INLINE unsigned long getTimeout() { return iTimeout; }; 128 | INLINE void resetTimeout(); 129 | INLINE long untilTimeout(); 130 | #endif 131 | 132 | _TASK_SCOPE: 133 | unsigned int iCount; // number of statuses to wait for. waiting for more that 65000 events seems unreasonable: unsigned int should be sufficient 134 | int iStatus; // status of the last completed request. negative = error; zero = OK; positive = OK with a specific status (see TASK_SR_ constants) 135 | 136 | #ifdef _TASK_TIMEOUT 137 | unsigned long iTimeout; // Task overall timeout 138 | unsigned long iStarttime; // millis at task start time 139 | #endif // _TASK_TIMEOUT 140 | }; 141 | #endif // _TASK_STATUS_REQUEST 142 | 143 | #ifdef _TASK_STD_FUNCTION 144 | #include 145 | typedef std::function TaskCallback; 146 | typedef std::function TaskOnDisable; 147 | typedef std::function TaskOnEnable; 148 | #else 149 | typedef void (*TaskCallback)(); 150 | typedef void (*TaskOnDisable)(); 151 | typedef bool (*TaskOnEnable)(); 152 | #endif // _TASK_STD_FUNCTION 153 | 154 | 155 | #ifdef _TASK_SLEEP_ON_IDLE_RUN 156 | typedef void (*SleepCallback)( unsigned long aDuration ); 157 | 158 | extern Scheduler* iSleepScheduler; 159 | extern SleepCallback iSleepMethod; 160 | #endif // _TASK_SLEEP_ON_IDLE_RUN 161 | 162 | typedef struct { 163 | bool enabled : 1; // indicates that task is enabled or not. 164 | bool inonenable : 1; // indicates that task execution is inside OnEnable method (preventing infinite loops) 165 | bool canceled : 1; // indication that task has been canceled prior to normal end of all iterations or regular call to disable() 166 | #ifdef _TASK_SELF_DESTRUCT 167 | bool selfdestruct : 1; // indication that task has been requested to self-destruct on disable 168 | bool sd_request : 1; // request for scheduler to delete task object and take task out of the queue 169 | #endif // _TASK_SELF_DESTRUCT 170 | #ifdef _TASK_STATUS_REQUEST 171 | uint8_t waiting : 2; // indication if task is waiting on the status request 172 | #endif // _TASK_STATUS_REQUEST 173 | 174 | #ifdef _TASK_TIMEOUT 175 | bool timeout : 1; // indication if task timed out 176 | #endif // _TASK_TIMEOUT 177 | } __task_status; 178 | 179 | 180 | class Task { 181 | friend class Scheduler; 182 | public: 183 | 184 | #ifdef _TASK_OO_CALLBACKS 185 | INLINE Task(unsigned long aInterval=0, long aIterations=0, Scheduler* aScheduler=NULL, bool aEnable=false 186 | #ifdef _TASK_SELF_DESTRUCT 187 | , bool aSelfDestruct=false); 188 | #else 189 | ); 190 | #endif // #ifdef _TASK_SELF_DESTRUCT 191 | #else 192 | INLINE Task(unsigned long aInterval=0, long aIterations=0, TaskCallback aCallback=NULL, Scheduler* aScheduler=NULL, bool aEnable=false, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL 193 | #ifdef _TASK_SELF_DESTRUCT 194 | , bool aSelfDestruct=false); 195 | #else 196 | ); 197 | #endif // #ifdef _TASK_SELF_DESTRUCT 198 | #endif // _TASK_OO_CALLBACKS 199 | 200 | 201 | #ifdef _TASK_STATUS_REQUEST 202 | #ifdef _TASK_OO_CALLBACKS 203 | // INLINE Task(Scheduler* aScheduler=NULL); 204 | INLINE Task(Scheduler* aScheduler); 205 | #else 206 | // INLINE Task(TaskCallback aCallback=NULL, Scheduler* aScheduler=NULL, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL); 207 | INLINE Task(TaskCallback aCallback, Scheduler* aScheduler, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL); 208 | #endif // _TASK_OO_CALLBACKS 209 | #endif // _TASK_STATUS_REQUEST 210 | 211 | virtual INLINE ~Task(); 212 | 213 | #ifdef _TASK_TIMEOUT 214 | INLINE void setTimeout(unsigned long aTimeout, bool aReset=false); 215 | INLINE void resetTimeout(); 216 | INLINE unsigned long getTimeout(); 217 | INLINE long untilTimeout(); 218 | INLINE bool timedOut(); 219 | #endif 220 | 221 | INLINE bool enable(); 222 | INLINE bool enableIfNot(); 223 | INLINE bool enableDelayed(unsigned long aDelay=0); 224 | INLINE bool restart(); 225 | INLINE bool restartDelayed(unsigned long aDelay=0); 226 | 227 | INLINE void delay(unsigned long aDelay=0); 228 | INLINE void adjust(long aInterval); 229 | INLINE void forceNextIteration(); 230 | INLINE bool disable(); 231 | INLINE void abort(); 232 | INLINE void cancel(); 233 | INLINE bool isEnabled(); 234 | INLINE bool canceled(); 235 | 236 | #ifdef _TASK_SCHEDULING_OPTIONS 237 | INLINE unsigned int getSchedulingOption() { return iOption; } 238 | INLINE void setSchedulingOption(unsigned int aOption) { iOption = aOption; } 239 | #endif //_TASK_SCHEDULING_OPTIONS 240 | 241 | #ifdef _TASK_OO_CALLBACKS 242 | INLINE void set(unsigned long aInterval, long aIterations); 243 | #else 244 | INLINE void set(unsigned long aInterval, long aIterations, TaskCallback aCallback,TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL); 245 | #endif // _TASK_OO_CALLBACKS 246 | INLINE void setInterval(unsigned long aInterval); 247 | INLINE void setIntervalNodelay(unsigned long aInterval, unsigned int aOption = TASK_INTERVAL_KEEP); 248 | INLINE unsigned long getInterval(); 249 | INLINE void setIterations(long aIterations); 250 | INLINE long getIterations(); 251 | INLINE unsigned long getRunCounter(); 252 | 253 | #ifdef _TASK_SELF_DESTRUCT 254 | INLINE void setSelfDestruct(bool aSelfDestruct=true) { iStatus.selfdestruct = aSelfDestruct; } 255 | INLINE bool getSelfDestruct() { return iStatus.selfdestruct; } 256 | #endif // #ifdef _TASK_SELF_DESTRUCT 257 | 258 | #ifdef _TASK_OO_CALLBACKS 259 | virtual INLINE bool Callback() =0; // return true if run was "productive - this will disable sleep on the idle run for next pass 260 | virtual INLINE bool OnEnable(); // return true if task should be enabled, false if it should remain disabled 261 | virtual INLINE void OnDisable(); 262 | #else 263 | INLINE void setCallback(TaskCallback aCallback) ; 264 | INLINE void setOnEnable(TaskOnEnable aCallback) ; 265 | INLINE void setOnDisable(TaskOnDisable aCallback) ; 266 | INLINE void yield(TaskCallback aCallback); 267 | INLINE void yieldOnce(TaskCallback aCallback); 268 | #endif // _TASK_OO_CALLBACKS 269 | 270 | INLINE bool isFirstIteration() ; 271 | INLINE bool isLastIteration() ; 272 | 273 | #ifdef _TASK_TIMECRITICAL 274 | INLINE long getOverrun() ; 275 | INLINE long getStartDelay() ; 276 | #endif // _TASK_TIMECRITICAL 277 | 278 | #ifdef _TASK_STATUS_REQUEST 279 | INLINE bool waitFor(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1); 280 | INLINE bool waitForDelayed(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1); 281 | INLINE StatusRequest* getStatusRequest() ; 282 | INLINE StatusRequest* getInternalStatusRequest() ; 283 | #endif // _TASK_STATUS_REQUEST 284 | 285 | #ifdef _TASK_WDT_IDS 286 | INLINE void setId(unsigned int aID) ; 287 | INLINE unsigned int getId() ; 288 | INLINE void setControlPoint(unsigned int aPoint) ; 289 | INLINE unsigned int getControlPoint() ; 290 | #endif // _TASK_WDT_IDS 291 | 292 | #ifdef _TASK_LTS_POINTER 293 | INLINE void setLtsPointer(void *aPtr) ; 294 | INLINE void* getLtsPointer() ; 295 | #endif // _TASK_LTS_POINTER 296 | 297 | #ifdef _TASK_EXPOSE_CHAIN 298 | INLINE Task* getPreviousTask() { return iPrev; }; // pointer to the previous task in the chain, NULL if first or not set 299 | INLINE Task* getNextTask() { return iNext; }; // pointer to the next task in the chain, NULL if last or not set 300 | #endif // _TASK_EXPOSE_CHAIN 301 | 302 | 303 | _TASK_SCOPE: 304 | INLINE void reset(); 305 | 306 | volatile __task_status iStatus; 307 | volatile unsigned long iInterval; // execution interval in milliseconds (or microseconds). 0 - immediate 308 | volatile unsigned long iDelay; // actual delay until next execution (usually equal iInterval) 309 | volatile unsigned long iPreviousMillis; // previous invocation time (millis). Next invocation = iPreviousMillis + iInterval. Delayed tasks will "catch up" 310 | 311 | #ifdef _TASK_SCHEDULING_OPTIONS 312 | unsigned int iOption; // scheduling option 313 | #endif // _TASK_SCHEDULING_OPTIONS 314 | 315 | #ifdef _TASK_TIMECRITICAL 316 | volatile long iOverrun; // negative if task is "catching up" to it's schedule (next invocation time is already in the past) 317 | volatile long iStartDelay; // actual execution of the task's callback method was delayed by this number of millis 318 | #endif // _TASK_TIMECRITICAL 319 | 320 | volatile long iIterations; // number of iterations left. 0 - last iteration. -1 - infinite iterations 321 | long iSetIterations; // number of iterations originally requested (for restarts) 322 | unsigned long iRunCounter; // current number of iteration (starting with 1). Resets on enable. 323 | 324 | #ifndef _TASK_OO_CALLBACKS 325 | TaskCallback iCallback; // pointer to the void callback method 326 | TaskOnEnable iOnEnable; // pointer to the bool OnEnable callback method 327 | TaskOnDisable iOnDisable; // pointer to the void OnDisable method 328 | #endif // _TASK_OO_CALLBACKS 329 | 330 | Task *iPrev, *iNext; // pointers to the previous and next tasks in the chain 331 | Scheduler *iScheduler; // pointer to the current scheduler 332 | 333 | #ifdef _TASK_STATUS_REQUEST 334 | StatusRequest *iStatusRequest; // pointer to the status request task is or was waiting on 335 | StatusRequest iMyStatusRequest; // internal Status request to let other tasks know of completion 336 | #endif // _TASK_STATUS_REQUEST 337 | 338 | #ifdef _TASK_WDT_IDS 339 | unsigned int iTaskID; // task ID (for debugging and watchdog identification) 340 | unsigned int iControlPoint; // current control point within the callback method. Reset to 0 by scheduler at the beginning of each pass 341 | #endif // _TASK_WDT_IDS 342 | 343 | #ifdef _TASK_LTS_POINTER 344 | void *iLTS; // pointer to task's local storage. Needs to be recast to appropriate type (usually a struct). 345 | #endif // _TASK_LTS_POINTER 346 | 347 | #ifdef _TASK_TIMEOUT 348 | unsigned long iTimeout; // Task overall timeout 349 | unsigned long iStarttime; // millis at task start time 350 | #endif // _TASK_TIMEOUT 351 | 352 | 353 | #ifdef _TASK_THREAD_SAFE 354 | volatile uint8_t iMutex; // a mutex to pause scheduling during chages to the task 355 | #endif 356 | }; 357 | 358 | class Scheduler { 359 | friend class Task; 360 | public: 361 | INLINE Scheduler(); 362 | // ~Scheduler(); 363 | INLINE void init(); 364 | INLINE void addTask(Task& aTask); 365 | INLINE void deleteTask(Task& aTask); 366 | INLINE void pause() { iPaused = true; }; 367 | INLINE void resume() { iPaused = false; }; 368 | INLINE void enable() { iEnabled = true; }; 369 | INLINE void disable() { iEnabled = false; }; 370 | #ifdef _TASK_PRIORITY 371 | INLINE void disableAll(bool aRecursive = true); 372 | INLINE void enableAll(bool aRecursive = true); 373 | INLINE void startNow(bool aRecursive = true); // reset ALL active tasks to immediate execution NOW. 374 | #else 375 | INLINE void disableAll(); 376 | INLINE void enableAll(); 377 | INLINE void startNow(); // reset ALL active tasks to immediate execution NOW. 378 | #endif 379 | 380 | INLINE bool execute(); // Returns true if none of the tasks' callback methods was invoked (true = idle run) 381 | 382 | INLINE Task& currentTask() ; // DEPRICATED 383 | INLINE Task* getCurrentTask() ; // Returns pointer to the currently active task 384 | INLINE long timeUntilNextIteration(Task& aTask); // return number of ms until next iteration of a given Task 385 | 386 | INLINE unsigned long getActiveTasks() { return iActiveTasks; } 387 | INLINE unsigned long getTotalTasks() { return iTotalTasks; } 388 | INLINE unsigned long getInvokedTasks() { return iInvokedTasks; } 389 | #ifdef _TASK_TICKLESS 390 | INLINE unsigned long getNextRun() { return iNextRun; } 391 | #endif 392 | 393 | #ifdef _TASK_SLEEP_ON_IDLE_RUN 394 | INLINE void allowSleep(bool aState = true); 395 | INLINE void setSleepMethod( SleepCallback aCallback ); 396 | #endif // _TASK_SLEEP_ON_IDLE_RUN 397 | 398 | #ifdef _TASK_LTS_POINTER 399 | INLINE void* currentLts(); 400 | #endif // _TASK_LTS_POINTER 401 | 402 | #ifdef _TASK_TIMECRITICAL 403 | INLINE bool isOverrun(); 404 | INLINE void cpuLoadReset(); 405 | INLINE unsigned long getCpuLoadCycle(){ return iCPUCycle; }; 406 | INLINE unsigned long getCpuLoadIdle() { return iCPUIdle; }; 407 | INLINE unsigned long getCpuLoadTotal(); 408 | #endif // _TASK_TIMECRITICAL 409 | 410 | #ifdef _TASK_PRIORITY 411 | INLINE void setHighPriorityScheduler(Scheduler* aScheduler); 412 | INLINE static Scheduler& currentScheduler() { return *(iCurrentScheduler); }; 413 | #endif // _TASK_PRIORITY 414 | 415 | #ifdef _TASK_EXPOSE_CHAIN 416 | INLINE Task* getFirstTask() { return iFirst; }; // pointer to the previous task in the chain, NULL if first or not set 417 | INLINE Task* getLastTask() { return iLast; }; // pointer to the next task in the chain, NULL if last or not set 418 | #endif // _TASK_EXPOSE_CHAIN 419 | 420 | _TASK_SCOPE: 421 | Task *iFirst, *iLast, *iCurrent; // pointers to first, last and current tasks in the chain 422 | 423 | volatile bool iPaused, iEnabled; 424 | unsigned long iActiveTasks; 425 | unsigned long iTotalTasks; 426 | unsigned long iInvokedTasks; 427 | 428 | #ifdef _TASK_SLEEP_ON_IDLE_RUN 429 | bool iAllowSleep; // indication if putting MC to IDLE_SLEEP mode is allowed by the program at this time. 430 | #endif // _TASK_SLEEP_ON_IDLE_RUN 431 | 432 | #ifdef _TASK_PRIORITY 433 | Scheduler* iHighPriority; // Pointer to a higher priority scheduler 434 | #endif // _TASK_PRIORITY 435 | 436 | #ifdef _TASK_TIMECRITICAL 437 | unsigned long iCPUStart; 438 | unsigned long iCPUCycle; 439 | unsigned long iCPUIdle; 440 | #endif // _TASK_TIMECRITICAL 441 | 442 | #ifdef _TASK_TICKLESS 443 | unsigned long iNextRun; 444 | #endif 445 | }; 446 | 447 | 448 | #endif /* _TASKSCHEDULERDECLARATIONS_H_ */ 449 | -------------------------------------------------------------------------------- /src/TaskSchedulerSleepMethods.h: -------------------------------------------------------------------------------- 1 | // Cooperative multitasking library for Arduino 2 | // Copyright (c) 2015-2022 Anatoli Arkhipenko 3 | 4 | #ifndef _TASKSCHEDULERSLEEPMETHODS_H_ 5 | #define _TASKSCHEDULERSLEEPMETHODS_H_ 6 | 7 | 8 | #if defined( ARDUINO_ARCH_AVR ) // Could be used only for AVR-based boards. 9 | 10 | #include 11 | #include 12 | 13 | void SleepMethod( unsigned long aDuration ) { 14 | set_sleep_mode(SLEEP_MODE_IDLE); 15 | sleep_enable(); 16 | /* Now enter sleep mode. */ 17 | sleep_mode(); 18 | 19 | /* The program will continue from here after the timer timeout ~1 ms */ 20 | sleep_disable(); /* First thing to do is disable sleep. */ 21 | } 22 | // ARDUINO_ARCH_AVR 23 | 24 | 25 | #elif defined( CORE_TEENSY ) 26 | void SleepMethod( unsigned long aDuration ) { 27 | asm("wfi"); 28 | } 29 | //CORE_TEENSY 30 | 31 | 32 | #elif defined( ARDUINO_ARCH_ESP8266 ) 33 | 34 | #ifndef _TASK_ESP8266_DLY_THRESHOLD 35 | #define _TASK_ESP8266_DLY_THRESHOLD 200L 36 | #endif 37 | extern "C" { 38 | #include "user_interface.h" 39 | } 40 | 41 | void SleepMethod( unsigned long aDuration ) { 42 | // to do: find suitable sleep function for esp8266 43 | if ( aDuration < _TASK_ESP8266_DLY_THRESHOLD) delay(1); // ESP8266 implementation of delay() uses timers and yield 44 | } 45 | // ARDUINO_ARCH_ESP8266 46 | 47 | 48 | #elif defined( ARDUINO_ARCH_ESP32 ) 49 | 50 | #include 51 | 52 | #ifndef _TASK_ESP32_DLY_THRESHOLD 53 | #define _TASK_ESP32_DLY_THRESHOLD 200L 54 | #endif 55 | extern unsigned long tStart, tFinish; 56 | const unsigned long tRem = 1000-_TASK_ESP32_DLY_THRESHOLD; 57 | 58 | void SleepMethod( unsigned long aDuration ) { 59 | if ( aDuration < tRem ) { 60 | esp_sleep_enable_timer_wakeup((uint64_t) (1000 - aDuration)); 61 | esp_light_sleep_start(); 62 | } 63 | } 64 | // ARDUINO_ARCH_ESP32 65 | 66 | 67 | #elif defined( ARDUINO_ARCH_STM32F1 ) 68 | 69 | #include 70 | #include 71 | 72 | void SleepMethod( unsigned long aDuration ) { 73 | // Now go into stop mode, wake up on interrupt. 74 | // Systick interrupt will run every 1 milliseconds. 75 | asm(" wfi"); 76 | } 77 | // ARDUINO_ARCH_STM32 78 | 79 | 80 | #elif defined( ENERGIA_ARCH_MSP432 ) 81 | 82 | void SleepMethod( unsigned long aDuration ) { 83 | delay(1); 84 | } 85 | // ENERGIA_ARCH_MSP432 86 | 87 | 88 | #elif defined( ENERGIA_ARCH_MSP430 ) 89 | 90 | void SleepMethod( unsigned long aDuration ) { 91 | sleep(1); 92 | } 93 | // ENERGIA_ARCH_MSP430 94 | 95 | 96 | #else 97 | void SleepMethod( unsigned long aDuration ) { 98 | } 99 | 100 | #endif // SLEEP METHODS 101 | 102 | #endif // _TASKSCHEDULERSLEEPMETHODS_H_ --------------------------------------------------------------------------------