初始化提交

This commit is contained in:
王立帮
2024-07-20 22:09:06 +08:00
commit c247dd07a6
6876 changed files with 2743096 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
Copyright (c) 2015-2020, Anatoli Arkhipenko.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,136 @@
# Task Scheduler
### Cooperative multitasking for Arduino, ESPx, STM32 and other microcontrollers
#### Version 3.6.0: 2021-12-17 [Latest updates](https://github.com/arkhipenko/TaskScheduler/wiki/Latest-Updates)
[![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)
#### Get expedited support or integration consultation for TaskScheduler [from xs:code](https://xscode.com/arkhipenko/TaskScheduler)
[![xscode](https://github.com/arkhipenko/resources/blob/master/taskscheduler-banner.png)](https://xscode.com/arkhipenko/TaskScheduler)
---
### OVERVIEW:
A lightweight implementation of cooperative multitasking (task scheduling). An easier alternative to preemptive programming and frameworks like FreeRTOS.
**Why cooperative?**
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.
_“Everybody who learns concurrency and thinks they understand it, ends up finding mysterious races they thought werent possible, and discovers that they didnt actually understand it yet after all.”_ **Herb Sutter, chair of the ISO C++ standards committee, Microsoft.**
**Main features:**
1. Periodic task execution, with dynamic execution period in `milliseconds` (default) or `microseconds` (if explicitly enabled) frequency of execution
2. Number of iterations (limited or infinite number of iterations)
3. Execution of tasks in predefined sequence
4. Dynamic change of task execution parameters (frequency, number of iterations, callback methods)
5. Power saving via entering **IDLE** sleep mode when tasks are not scheduled to run
6. Support for event-driven task invocation via Status Request object
7. Support for task IDs and Control Points for error handling and watchdog timer
8. Support for Local Task Storage pointer (allowing use of same callback code for multiple tasks)
9. Support for layered task prioritization
10. Support for `std::functions` (tested on `ESPx` and `STM32` only)
11. Overall task timeout
12. Static and dynamic callback method binding
13. CPU load / idle statistics for time critical applications
14. Scheduling options with priority for original schedule (with and without catchup) and interval
15. Ability to pause/resume and enable/disable scheduling
15. Thread-safe scheduling while running under preemptive scheduler (i. e., FreeRTOS)
Scheduling overhead: between `15` and `18` microseconds per scheduling pass (Arduino UNO rev 3 @ `16MHz` clock, single scheduler w/o prioritization)
**TaskScheduler** was tested on the following platforms:
* Arduino Uno R3
* Arduino Nano
* Arduino Micro
* ATtiny85
* ESP8266 (Node MCU v2.0)
* ESP32
* Teensy (tested on Teensy 3.5)
* nRF52 (tested on nRF52832)
* STM32 (tested on Mini USB STM32F103RCBT6 ARM Cortex-M3 leaflabs Leaf maple mini module F)
* MSP430 and MSP432 boards
* Raspberry Pi (requires external `Arduino.h` and `millis()` implementation)
**Don't just take my word for it - try it for yourself on [Wokwi](https://wokwi.com/playground/task-scheduler)**
---
![TaskScheduler process diagram](https://github.com/arkhipenko/TaskScheduler/raw/master/extras/TaskScheduler_html.png)
---
### Changelog is located [here.](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog)
#### For detailed functionality overview please refer to TaskScheduler documentation in the 'extras' folder or in the [Wiki page](https://github.com/arkhipenko/TaskScheduler/wiki).
### User Feedback:
"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/)
"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)
"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 Arkhipenkos TaskScheduler. (https://github.com/arkhipenko/TaskScheduler) This should help me isolate any issues between the different control systems while managing the different tasks timing requirements." - [here](https://hackaday.io/project/167479/logs)
"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)
"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)
"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)
### Check out what TaskScheduler can do:
#### Around the world:
* Ninja Timer: Giant 7-Segment Display at Adafruit.com
https://learn.adafruit.com/ninja-timer-giant-7-segment-display/timer-code
* Playing with NeoPixel to create a nice #smartBulb IoT
https://www.zerozone.it/linux-e-open-source/giocare-con-i-neopixel-per-realizzare-un-simpatico-smartbulb-iot/16760
* Adding a timer to XK X6 Transmitter
https://www.elvinplay.com/adding-a-timer-to-xk-x6-transmitter-en/
* Arduino Bluetooth remote control + ultrasonic anti-collision car
https://xie.infoq.cn/article/0f27dbbebcc2b99b35132b262
* WEMOS D1 Mini로 Ad-hoc WIFI network
https://m.blog.naver.com/sonyi/221330334326
* [3 Devo](http://3devo.eu/) - Quality 3D printing filament, now made accessible and affordable
(http://3devo.eu/license-information/)
* [Houston midi](https://github.com/chaffneue/houston) clock project - TaskScheduler with microseconds resolution
>by chaffneue:
>>My first arduino project. It's a multi-master midi controller with a shared clock and
auto count in behaviour.
youtube: https://www.youtube.com/watch?v=QRof550TtXo
* [Hackabot Nano](http://hackarobot.com/) by Funnyvale - Compact Plug and Play Arduino compatible robotic kit
https://www.kickstarter.com/projects/hackarobot/hackabot-nano-compact-plug-and-play-arduino-robot
* Discrete Time Systems Wiki -
https://sistemas-en-tiempo-discreto.fandom.com/es/wiki/Tiempo_Real
#### My projects:
* Interactive "Do Not Disturb" sign in a shape of Minecraft Sword (ESP32)
(https://www.instructables.com/id/Interactive-Minecraft-Do-Not-Enter-SwordSign-ESP32/)
* Interactive Predator Costume with Real-Time Head Tracking Plasma Cannon (Teensy, Arduino Nano)
(https://www.instructables.com/id/Interactive-Predator-Costume-With-Head-Tracking-Pl/)
* IoT APIS v2 - Autonomous IoT-enabled Automated Plant Irrigation System (ESP8266)
(http://www.instructables.com/id/IoT-APIS-V2-Autonomous-IoT-enabled-Automated-Plant/)
* APIS - Automated Plant Irrigation System (Arduino Uno)
(http://www.instructables.com/id/APIS-Automated-Plant-Irrigation-System/)
* Party Lights LEDs music visualization (Leaf Maple Mini)
(https://www.instructables.com/id/Portable-Party-Lights/)
* Arduino Nano based Hexbug Scarab Robotic Spider (Arduino Nano)
(http://www.instructables.com/id/Arduino-Nano-based-Hexbug-Scarab-Robotic-Spider/)
* Wave your hand to control OWI Robotic Arm... no strings attached (Arduino Uno and Nano)
(http://www.instructables.com/id/Wave-your-hand-to-control-OWI-Robotic-Arm-no-strin/)
* Interactive Halloween Pumpkin (Arduino Uno)
(http://www.instructables.com/id/Interactive-Halloween-Pumpkin/)

View File

@@ -0,0 +1,296 @@
/*
Every example set must have a LED blink example
For this one the idea is to have as many ways to blink the LED
as I can think of. So, here we go.
Tested on:
- Arduino Nano
- ESP8266
- ESP32
- STM32 Maple Mini
*/
// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns
#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass
#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer
// #define _TASK_PRIORITY // Support for layered scheduling priority
// #define _TASK_MICRO_RES // Support for microsecond resolution
// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY)
// #define _TASK_DEBUG // Make all methods and variables public for debug purposes
// #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations
// #define _TASK_TIMEOUT // Support for overall task timeout
// #define _TASK_OO_CALLBACKS // Support for dynamic callback method binding
#include <TaskScheduler.h>
// Debug and Test options
#define _DEBUG_
//#define _TEST_
#ifdef _DEBUG_
#define _PP(a) Serial.print(a);
#define _PL(a) Serial.println(a);
#else
#define _PP(a)
#define _PL(a)
#endif
// LED_BUILTIN 13
#if defined( ARDUINO_ARCH_ESP32 )
#define LED_BUILTIN 23 // esp32 dev2 kit does not have LED
#endif
// Scheduler
Scheduler ts;
/*
Approach 1: LED is driven by the boolean variable; false = OFF, true = ON
*/
#define PERIOD1 500
#define DURATION 10000
void blink1CB();
Task tBlink1 ( PERIOD1 * TASK_MILLISECOND, DURATION / PERIOD1, &blink1CB, &ts, true );
/*
Approac 2: two callback methods: one turns ON, another turns OFF
*/
#define PERIOD2 400
void blink2CB_ON();
void blink2CB_OFF();
Task tBlink2 ( PERIOD2 * TASK_MILLISECOND, DURATION / PERIOD2, &blink2CB_ON, &ts, false );
/*
Approach 3: Use RunCounter
*/
#define PERIOD3 300
void blink3CB();
Task tBlink3 (PERIOD3 * TASK_MILLISECOND, DURATION / PERIOD3, &blink3CB, &ts, false);
/*
Approach 4: Use status request objects to pass control from one task to the other
*/
#define PERIOD4 200
bool blink41OE();
void blink41();
void blink42();
void blink42OD();
Task tBlink4On ( PERIOD4 * TASK_MILLISECOND, TASK_ONCE, blink41, &ts, false, &blink41OE );
Task tBlink4Off ( PERIOD4 * TASK_MILLISECOND, TASK_ONCE, blink42, &ts, false, NULL, &blink42OD );
/*
Approach 5: Two interleaving tasks
*/
#define PERIOD5 600
bool blink51OE();
void blink51();
void blink52();
void blink52OD();
Task tBlink5On ( 600 * TASK_MILLISECOND, DURATION / PERIOD5, &blink51, &ts, false, &blink51OE );
Task tBlink5Off ( 600 * TASK_MILLISECOND, DURATION / PERIOD5, &blink52, &ts, false, NULL, &blink52OD );
/*
Approach 6: RunCounter-based with random intervals
*/
#define PERIOD6 300
void blink6CB();
bool blink6OE();
void blink6OD();
Task tBlink6 ( PERIOD6 * TASK_MILLISECOND, DURATION / PERIOD6, &blink6CB, &ts, false, &blink6OE, &blink6OD );
void setup() {
// put your setup code here, to run once:
#if defined(_DEBUG_) || defined(_TEST_)
Serial.begin(115200);
delay(TASK_SECOND);
_PL("TaskScheduler Blink example");
_PL("Blinking for 10 seconds using various techniques\n");
delay(2 * TASK_SECOND);
#endif
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
ts.execute();
}
inline void LEDOn() {
digitalWrite( LED_BUILTIN, HIGH );
}
inline void LEDOff() {
digitalWrite( LED_BUILTIN, LOW );
}
// === 1 =======================================
bool LED_state = false;
void blink1CB() {
if ( tBlink1.isFirstIteration() ) {
_PP(millis());
_PL(": Blink1 - simple flag driven");
LED_state = false;
}
if ( LED_state ) {
LEDOff();
LED_state = false;
}
else {
LEDOn();
LED_state = true;
}
if ( tBlink1.isLastIteration() ) {
tBlink2.restartDelayed( 2 * TASK_SECOND );
LEDOff();
}
}
// === 2 ======================================
void blink2CB_ON() {
if ( tBlink2.isFirstIteration() ) {
_PP(millis());
_PL(": Blink2 - 2 callback methods");
}
LEDOn();
tBlink2.setCallback( &blink2CB_OFF );
if ( tBlink2.isLastIteration() ) {
tBlink3.restartDelayed( 2 * TASK_SECOND );
LEDOff();
}
}
void blink2CB_OFF() {
LEDOff();
tBlink2.setCallback( &blink2CB_ON );
if ( tBlink2.isLastIteration() ) {
tBlink3.restartDelayed( 2 * TASK_SECOND );
LEDOff();
}
}
// === 3 =====================================
void blink3CB() {
if ( tBlink3.isFirstIteration() ) {
_PP(millis());
_PL(": Blink3 - Run Counter driven");
}
if ( tBlink3.getRunCounter() & 1 ) {
LEDOn();
}
else {
LEDOff();
}
if ( tBlink3.isLastIteration() ) {
tBlink4On.setOnEnable( &blink41OE );
tBlink4On.restartDelayed( 2 * TASK_SECOND );
LEDOff();
}
}
// === 4 =============================================
int counter = 0;
bool blink41OE() {
_PP(millis());
_PL(": Blink4 - Internal status request based");
counter = 0;
tBlink4On.setOnEnable( NULL );
return true;
}
void blink41() {
// _PP(millis());
// _PL(": blink41");
LEDOn();
StatusRequest* r = tBlink4On.getInternalStatusRequest();
tBlink4Off.waitForDelayed( r );
counter++;
}
void blink42() {
// _PP(millis());
// _PL(": blink42");
LEDOff();
StatusRequest* r = tBlink4Off.getInternalStatusRequest();
tBlink4On.waitForDelayed( r );
counter++;
}
void blink42OD() {
if ( counter >= DURATION / PERIOD4 ) {
tBlink4On.disable();
tBlink4Off.disable();
tBlink5On.setOnEnable( &blink51OE );
tBlink5On.restartDelayed( 2 * TASK_SECOND );
tBlink5Off.restartDelayed( 2 * TASK_SECOND + PERIOD5 / 2 );
LEDOff();
}
}
// === 5 ==========================================
bool blink51OE() {
_PP(millis());
_PL(": Blink5 - Two interleaving tasks");
tBlink5On.setOnEnable( NULL );
return true;
}
void blink51() {
// _PP(millis());
// _PL(": blink51");
LEDOn();
}
void blink52() {
// _PP(millis());
// _PL(": blink52");
LEDOff();
}
void blink52OD() {
tBlink6.restartDelayed( 2 * TASK_SECOND );
LEDOff();
}
// === 6 ============================================
long interval6 = 0;
bool blink6OE() {
_PP(millis());
_PP(": Blink6 - RunCounter + Random ON interval = ");
interval6 = random( 100, 901 );
tBlink6.setInterval( interval6 );
_PL( interval6 );
tBlink6.delay( 2 * TASK_SECOND );
return true;
}
void blink6CB() {
if ( tBlink6.getRunCounter() & 1 ) {
LEDOn();
tBlink6.setInterval( interval6 );
}
else {
LEDOff();
tBlink6.setInterval( TASK_SECOND - interval6 );
}
}
void blink6OD() {
tBlink1.restartDelayed( 2 * TASK_SECOND );
LEDOff();
}

View File

@@ -0,0 +1,84 @@
/**
* TaskScheduler Test
*
* Initially only tasks 1 and 2 are enabled
* Task1 runs every 2 seconds 10 times and then stops
* Task2 runs every 3 seconds indefinitely
* Task1 enables Task3 at its first run
* Task3 run every 5 seconds
* Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds
* At the end Task2 is the only task running every 1/2 seconds
*/
#include <TaskScheduler.h>
// Callback methods prototypes
void t1Callback();
void t2Callback();
void t3Callback();
//Tasks
Task t4();
Task t1(2000, 10, &t1Callback);
Task t2(3000, TASK_FOREVER, &t2Callback);
Task t3(5000, TASK_FOREVER, &t3Callback);
Scheduler runner;
void t1Callback() {
Serial.print("t1: ");
Serial.println(millis());
if (t1.isFirstIteration()) {
runner.addTask(t3);
t3.enable();
Serial.println("t1: enabled t3 and added to the chain");
}
if (t1.isLastIteration()) {
t3.disable();
runner.deleteTask(t3);
t2.setInterval(500);
Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500");
}
}
void t2Callback() {
Serial.print("t2: ");
Serial.println(millis());
}
void t3Callback() {
Serial.print("t3: ");
Serial.println(millis());
}
void setup () {
Serial.begin(115200);
Serial.println("Scheduler TEST");
runner.init();
Serial.println("Initialized scheduler");
runner.addTask(t1);
Serial.println("added t1");
runner.addTask(t2);
Serial.println("added t2");
delay(5000);
t1.enable();
Serial.println("Enabled t1");
t2.enable();
Serial.println("Enabled t2");
}
void loop () {
runner.execute();
}

View File

@@ -0,0 +1,78 @@
#define _TASK_SLEEP_ON_IDLE_RUN
#include <TaskScheduler.h>
Scheduler runner;
// Callback methods prototypes
void t1Callback();
void t2Callback();
void t3Callback();
// Tasks
Task t4();
Task t1(2000, 10, &t1Callback, &runner, true); //adding task to the chain on creation
Task t2(3000, TASK_FOREVER, &t2Callback, &runner, true); //adding task to the chain on creation
Task t3(5000, TASK_FOREVER, &t3Callback);
// Test
// Initially only tasks 1 and 2 are enabled
// Task1 runs every 2 seconds 10 times and then stops
// Task2 runs every 3 seconds indefinitely
// Task1 enables Task3 at its first run
// Task3 run every 5 seconds
// loop() runs every 1 second (a default scheduler delay, if no shorter tasks' interval is detected)
// Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds
// Because Task2 interval is shorter than Scheduler default tick, loop() executes ecery 1/2 seconds now
// At the end Task2 is the only task running every 1/2 seconds
//
// 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().
void t1Callback() {
Serial.print("t1: ");
Serial.println(millis());
if (t1.isFirstIteration()) {
runner.addTask(t3);
t3.enable();
Serial.println("t1: enabled t3 and added to the chain");
}
if (t1.isLastIteration()) {
t3.disable();
runner.deleteTask(t3);
t2.setInterval(500);
Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500");
}
}
void t2Callback() {
Serial.print("t2: ");
Serial.println(millis());
}
void t3Callback() {
Serial.print("t3: ");
Serial.println(millis());
}
void setup () {
Serial.begin(115200);
delay(5000);
Serial.println("Scheduler TEST");
runner.startNow(); // set point-in-time for scheduling start
}
void loop () {
runner.execute();
// Serial.println("Loop ticks at: ");
// Serial.println(millis());
}

View File

@@ -0,0 +1,80 @@
/**
* TaskScheduler Test of OnEnable and OnDisable methods and illustration of using wrapper tasks for timout purposes
*
* A wrapper task runs every 10 seconds and initiates the test case
* Another task is run once for 5 seconds, and serves as a LED blinking timeout - 5 seconds
* Finally, a dedicated task which controls LED is running periodically until stopped, and makes the LED blink with 0.5 to 1 second interval.
*
*/
#define _TASK_SLEEP_ON_IDLE_RUN
#include <TaskScheduler.h>
#ifndef LED_BUILTIN
#define LED_BUILTIN 13 // define appropriate pin for your board
#endif
Scheduler ts;
// Callback methods prototypes
void WrapperCallback();
bool BlinkOnEnable();
void BlinkOnDisable();
void LEDOn();
void LEDOff();
// Tasks
Task tWrapper(10000L, TASK_FOREVER, &WrapperCallback, &ts, true);
Task tBlink(5000, TASK_ONCE, NULL, &ts, false, &BlinkOnEnable, &BlinkOnDisable);
Task tLED(0, TASK_FOREVER, NULL, &ts, false, NULL, &LEDOff);
void WrapperCallback() {
tBlink.restartDelayed(); // LED blinking is initiated
//every 30 seconds for 5 seconds
}
// Upon being enabled, tBlink will define the parameters
// and enable LED blinking task, which actually controls
// the hardware (LED in this example)
bool BlinkOnEnable() {
tLED.setInterval( 200 + random(801) );
tLED.setCallback( &LEDOn);
tLED.enable();
return true; // Task should be enabled
}
// tBlink does not really need a callback function
// since it just waits for 5 seconds for the first
// and only iteration to occur. Once the iteration
// takes place, tBlink is disabled by the Scheduler,
// thus executing its OnDisable method below.
void BlinkOnDisable() {
tLED.disable();
}
void LEDOn () {
digitalWrite(LED_BUILTIN , HIGH);
tLED.setCallback( &LEDOff);
}
void LEDOff () {
digitalWrite(LED_BUILTIN , LOW);
tLED.setCallback( &LEDOn);
}
// Note that LEDOff method serves as OnDisable method
// to make sure the LED is turned off when the tBlink
// task finishes (or disabled ahead of time)
void setup() {
// put your setup code here, to run once:
pinMode(LED_BUILTIN , OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
ts.execute();
}

View File

@@ -0,0 +1,90 @@
/** This test demonstrates interaction between three simple tasks via StatusRequest object.
* Task T1 runs every 5 seconds and signals completion of a status request st.
* Tasks T2 and T3 are waiting on the same request (st)
* Task T3 does not renew its interest in status request st, so it is only invoked once (first iteration)
* Task T2 is invoked every time st completes, because it renews its interest in status of status request object st every iteration of T1
*/
#define _TASK_SLEEP_ON_IDLE_RUN
#define _TASK_STATUS_REQUEST
#include <TaskScheduler.h>
StatusRequest st;
Scheduler ts;
// Callback methods prototypes
void Callback1();
void Disable1();
void Callback2();
void Callback3();
void PrepareStatus();
// Tasks
Task t1(5000, TASK_ONCE, &Callback1, &ts, true, NULL, &Disable1);
Task t2(&Callback2, &ts);
Task t3(&Callback3, &ts);
/** T1 callback
* T1 just signals completion of st every 5 seconds
*/
void Callback1() {
Serial.println("T1: Signaling completion of ST");
st.signalComplete();
}
/** T1 On Disable callback
* This callback renews the status request and restarts T1 delayed to run again in 5 seconds
*/
void Disable1() {
PrepareStatus();
t1.restartDelayed();
}
/** T2 callback
* Invoked when status request st completes
*/
void Callback2() {
Serial.println("T2: Invoked due to completion of ST");
}
/** T3 callback
* Invoked when status request st completes.
* This is only run once since T3 does not renew its interest in the status request st after first iteration
*/
void Callback3() {
Serial.println("T3: Invoked due to completion of ST");
}
/** Prepare Status request st for another iteration
*
*/
void PrepareStatus() {
st.setWaiting(); // set the statusrequest object for waiting
t2.waitFor(&st); // request tasks 1 & 2 to wait on the object st
}
/** Main Arduino code
* Not much to do here. Just init Serial and set the initial status request
*/
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("TaskScheduler: Status Request Test 1. Simple Test.");
ts.startNow();
PrepareStatus();
t3.waitFor(&st);
t1.delay();
}
void loop() {
ts.execute();
}

View File

@@ -0,0 +1,181 @@
/** This test emulates querying 3 sensors once every 10 seconds, each could respond with a different delay
* (ultrasonic sensors for instance) and printing a min value of the three when all three have reported their values.
* The overall timeout of 1 second is setup as well.
* An error message needs to be printed if a timeout occurred instead of a value.
*/
#define _TASK_SLEEP_ON_IDLE_RUN
#define _TASK_STATUS_REQUEST
#include <TaskScheduler.h>
#ifdef ARDUINO_ARCH_STM32F1
#define A0 3
#endif
StatusRequest measure;
Scheduler ts;
// Callback methods prototypes
void CycleCallback();
void MeasureCallback();
bool MeasureEnable();
void MeasureDisable();
void CalcCallback();
void S1Callback(); bool S1Enable();
void S2Callback(); bool S2Enable();
void S3Callback(); bool S3Enable();
// Tasks
Task tCycle(10000, TASK_FOREVER, &CycleCallback, &ts, true);
Task tMeasure(1000, TASK_ONCE, &MeasureCallback, &ts, false, &MeasureEnable, &MeasureDisable);
Task tCalculate(&CalcCallback, &ts);
Task tSensor1(0, TASK_ONCE, &S1Callback, &ts, false, &S1Enable);
Task tSensor2(0, TASK_ONCE, &S2Callback, &ts, false, &S2Enable);
Task tSensor3(0, TASK_ONCE, &S3Callback, &ts, false, &S3Enable);
long distance, d1, d2, d3;
void CycleCallback() {
Serial.println("CycleCallback: Initiating measurement cycle every 10 seconds");
tMeasure.restartDelayed();
}
bool MeasureEnable() {
Serial.println("MeasureEnable: Activating sensors");
distance = 0;
measure.setWaiting(3); // Set the StatusRequest to wait for 3 signals.
tCalculate.waitFor(&measure);
tSensor1.restartDelayed();
tSensor2.restartDelayed();
tSensor3.restartDelayed();
return true;
}
void MeasureCallback() {
Serial.println("MeasureCallback: Invoked by calculate task or one second later");
if (measure.pending()) {
tCalculate.disable();
measure.signalComplete(-1); // signal error
Serial.println("MeasureCallback: Timeout!");
}
else {
Serial.print("MeasureCallback: Min distance=");Serial.println(distance);
}
}
void MeasureDisable() {
Serial.println("MeasureDisable: Cleaning up");
tSensor1.disable();
tSensor2.disable();
tSensor3.disable();
}
void CalcCallback() {
Serial.println("CalcCallback: calculating");
distance = -1;
if ( measure.getStatus() >= 0) { // only calculate if statusrequest ended successfully
distance = d1 < d2 ? d1 : d2;
distance = d3 < distance ? d3 : distance;
tMeasure.forceNextIteration();
}
}
/** Simulation code for sensor 1
* ----------------------------
*/
bool S1Enable() {
Serial.print("S1Enable: Triggering sensor1. Delay=");
tSensor1.setInterval( random(1200) ); // Simulating sensor delay, which could go over 1 second and cause timeout
d1 = 0;
Serial.println( tSensor1.getInterval() );
return true;
}
void S1Callback() {
Serial.print("S1Callback: Emulating measurement. d1=");
d1 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement
measure.signal();
Serial.println(d1);
}
/** Simulation code for sensor 2
* ----------------------------
*/
bool S2Enable() {
Serial.print("S2Enable: Triggering sensor2. Delay=");
tSensor2.setInterval( random(1200) ); // Simulating sensor delay, which could go over 1 second and cause timeout
d2 = 0;
Serial.println( tSensor2.getInterval() );
return true;
}
void S2Callback() {
Serial.print("S2Callback: Emulating measurement. d2=");
d2 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement
measure.signal();
Serial.println(d2);
}
/** Simulation code for sensor 3
* ----------------------------
*/
bool S3Enable() {
Serial.print("S3Enable: Triggering sensor3. Delay=");
tSensor3.setInterval( random(1200) ); // Simulating sensor delay, which could go over 1 second and cause timeout
d3 = 0;
Serial.println( tSensor3.getInterval() );
return true;
}
void S3Callback() {
Serial.print("S3Callback: Emulating measurement. d3=");
d3 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement
measure.signal();
Serial.println(d3);
}
/** Main Arduino code
* Not much is left here - everything is taken care of by the framework
*/
void setup() {
Serial.begin(115200);
Serial.println("TaskScheduler StatusRequest Sensor Emulation Test. Complex Test.");
#ifdef ARDUINO_ARCH_STM32F1
pinMode(A0, INPUT_ANALOG);
#endif
randomSeed(analogRead(A0)+millis());
}
void loop() {
ts.execute();
}

View File

@@ -0,0 +1,117 @@
/**
* This is a test to prove that processor really goes into IDLE sleep.
* For this setup:
*
*
Task c(10, -1, &Count, &ts);
Task t(10000, 1, NULL, &ts, true, &tOn, &tOff);
The result are:
1): With #define _TASK_SLEEP_ON_IDLE_RUN enabled
On Arduino Uno:
Start
c1=10771 - v2.5.0 (v1.9.0: same)
c2=1001
On Teensy 3.5 (120MHz ARM):
Start
c1=21065
c2=1001
On esp8266 (80 MHz)
Start
c1=10492
c2=1001
On STM32F103RCBT6 (Maple Mini @72 MHz)
Start
c1=21004
c2=1001
and
2): With #define _TASK_SLEEP_ON_IDLE_RUN disabled (commented out)
Arduino Uno:
Start
c1=722426 - v3.0.2
c1=635735 - v2.5.0
c1=551947 - v1.9.0
c2=1001
On Teensy 3.5 (120MHz ARM):
Start
c1=2690322
c2=1001
On esp8266 (80 MHz)
Start
c1=351085 (689833 at 160Mhz)
c2=1001
On STM32F103RCBT6 (Maple Mini @72 MHz)
Start
c1=4665019
c2=1001
C1 in scenario 2) is much higher than in scenario 1) because processor is put to sleep for 1), but not for 2)
*/
/**
* Compile and run once with _TASK_SLEEP_ON_IDLE_RUN enabled, then with _TASK_SLEEP_ON_IDLE_RUN disabled.
* Compare the results.
*/
//#define _TASK_SLEEP_ON_IDLE_RUN
#include <TaskScheduler.h>
Scheduler ts;
// Callback methods prototypes
void Count();
bool tOn(); void tOff();
// Tasks
Task c(10, TASK_FOREVER, &Count, &ts);
Task t(10000, TASK_ONCE, NULL, &ts, true, &tOn, &tOff);
volatile unsigned long c1, c2;
bool tOn() {
c1 = 0;
c2 = 0;
c.enable();
return true;
}
void tOff() {
c.disable();
Serial.print("c1=");Serial.println(c1);
Serial.print("c2=");Serial.println(c2);
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
delay(1000);
Serial.println("Start");
ts.startNow();
t.delay();
}
void Count() {
c2++;
}
void loop() {
// put your main code here, to run repeatedly:
ts.execute();
c1++;
}

View File

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

View File

@@ -0,0 +1,147 @@
/**
* TaskScheduler Test sketch - use of task's Local Task Storage pointer
* Test case:
* Overall test runs for 5 seconds
* A number of calculator tasks run every one second, and update their respective variables using Local Task Storage pointer
* All calculator tasks use the same callback code, which obtains reference to appropriate variables via LTS pointer
* Calculaotr tasks perform simple calculation (as an example):
* adding task id number to itself
* multiplying task id number by 10
*
* Upon completion of the overall test, all results are printed out.
* Test could be repeated with various number of calculator tasks.
* All that needs to change is data definitions - code is completely agnostic of number of tasks
*/
#define _TASK_SLEEP_ON_IDLE_RUN // Compile with support for entering IDLE SLEEP state for 1 ms if not tasks are scheduled to run
#define _TASK_WDT_IDS // Compile with support for Task IDs and Watchdog timer
#define _TASK_LTS_POINTER // Compile with support for Local Task Storage pointer
#include <TaskScheduler.h>
// Overall number of calculator tasks:
#define NO_TASKS 3
Scheduler ts;
// Callback methods prototypes
void Calculate(); bool CalcOn();
bool WrapperOn(); void WrapperOff();
// Tasks
// Calculator tasks.
// Note that all three tasks use the same callback methods
// They will be updating specific variables based on the
// Locat Task Storage pointers
Task t1(TASK_SECOND, TASK_FOREVER, &Calculate, &ts, false, &CalcOn);
Task t2(TASK_SECOND, TASK_FOREVER, &Calculate, &ts, false, &CalcOn);
Task t3(TASK_SECOND, TASK_FOREVER, &Calculate, &ts, false, &CalcOn);
// add more calc tasks here if necessary
Task tWrapper(5*TASK_SECOND, TASK_ONCE, NULL, &ts, false, &WrapperOn, &WrapperOff);
// The below structure is an object referenced by LTS pointer
typedef struct {
unsigned int id;
long sum;
long product;
} task_var;
// These are actual structures which hold tasks specific values
task_var v1;
task_var v2;
task_var v3;
// Arrays below allow indexed access to specific tasks and tasks variables
Task *tasks[] = { &t1, &t2, &t3 };
task_var *vars[] = { &v1, &v2, &v3 };
/**
* This method is called when a wrapper task is enabled
* The purpose is to supply LTS pointers to all the tasks
*/
bool WrapperOn() {
for (int i=0; i < NO_TASKS; i++) {
Task& T = *tasks[i];
T.setLtsPointer( vars[i] );
T.enableDelayed();
}
return true; // Signal that Task could be enabled
}
/**
* This method is called when Wrapper task is disabled (after first and only iteration is executed)
* For each of the calculor tasks the results are printed out.
*/
void WrapperOff() {
Serial.println("Finished processing");
ts.disableAll();
for (int i=0; i < NO_TASKS; i++) {
Serial.print("ID: "); Serial.println(vars[i]->id);
Serial.print("Sum: "); Serial.println(vars[i]->sum);
Serial.print("Product: "); Serial.println(vars[i]->product);
Serial.println();
}
}
/**
* This method is executed when each calculator task is enabled
* The purpose is to initiate all local variables
*/
bool CalcOn() {
Task& T = ts.currentTask();
task_var& var = *((task_var*) T.getLtsPointer());
// Initialize local variables
var.id = T.getId();
var.sum = 0;
var.product = var.id;
return true;
}
/**
* This method performs simple calculations on task's local variables
*/
void Calculate() {
Task& T = ts.currentTask();
// Another way to get to LTS pointer:
task_var& var = *((task_var*) ts.currentLts());
Serial.print("Calculating for task: ");
Serial.print(T.getId());
Serial.print("; Task id per LTS is: ");
Serial.println( var.id );
var.sum += T.getId();
var.product = var.product * 10;
}
/**
* Standard Arduino setup and loop methods
*/
void setup() {
Serial.begin(115200);
randomSeed(analogRead(0)+analogRead(5));
pinMode(13, OUTPUT);
digitalWrite(13, LOW);
Serial.println("Local Task Storage pointer test");
tWrapper.enableDelayed();
}
void loop() {
ts.execute();
}

View File

@@ -0,0 +1,57 @@
/**
* TaskScheduler Test
* Illustration of use of Time Critical Information
*
* Task1 runs every 1 second indefinitely
* On each run it reports how delayed the invokation of the callback method was,
* and what was the scheduling overun.
* Each run task 1 is dealyed randomly for up to 2 seconds, thus simulating scheduling overrun
*/
#define _TASK_TIMECRITICAL
#define _TASK_SLEEP_ON_IDLE_RUN
#include <TaskScheduler.h>
// Callback methods prototypes
void t1Callback();
//Tasks
Task t1(1000, -1, &t1Callback);
Scheduler runner;
void t1Callback() {
Serial.print(millis());
Serial.print(": overrun = ");
Serial.print(t1.getOverrun());
Serial.print(", start delayed by ");
Serial.println(t1.getStartDelay());
int i = random(2000);
Serial.print("Delaying for "); Serial.println(i);
delay(i);
}
void setup () {
Serial.begin(115200);
Serial.println("Scheduler TimeCritical TEST");
runner.init();
Serial.println("Initialized scheduler");
runner.addTask(t1);
Serial.println("added t1. Waiting for 5 seconds.");
delay(5000);
t1.enable();
Serial.println("Enabled t1");
}
void loop () {
runner.execute();
}

View File

@@ -0,0 +1,106 @@
/**
* This is a test to benchmark TaskScheduler execution.
*
* This test executes 1,000,000 cycles of a task with empty callback method
* Compiled with different options, you can assess the impact of each on the size of the Task object
* and the execution overhead of the main execution pass route.
*
* Sample execution times (in milliseconds per 1M iterations) are provided below.
* The test board is Arduino UNO 16MHz processor.
*
TaskScheduler 2.1.0:
No modifiers
Duration=19869
with SLEEP
Duration=20058
with status request:
Duration=20058
with time critical:
Duration=27289
TaskScheduler 1.9.0:
No modifiers
Duration=15656
with SLEEP
Duration=16285
with status request:
Duration=16600
with rollover fix:
Duration=18109
TaskScheduler 1.8.5:
Duration=15719
with SLEEP
Duration=16348
with status request:
Duration=18360
with rollover fix:
Duration=18423
*/
//#define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns
//#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
//#define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
//#define _TASK_LTS_POINTER // Compile with support for local task storage pointer
//#define _TASK_SLEEP_ON_IDLE_RUN
//#define _TASK_MICRO_RES
#include <TaskScheduler.h>
Scheduler ts;
// Callback methods prototypes
bool tOn(); void tOff();
void callback();
// Tasks
Task t(TASK_IMMEDIATE, 1000000, &callback, &ts, false, &tOn, &tOff);
unsigned long c1, c2;
bool tOn() {
c1 = millis();
c2 = 0;
return true;
}
void tOff() {
c2 = millis();
Serial.println("done.");
Serial.print("Tstart =");Serial.println(c1);
Serial.print("Tfinish=");Serial.println(c2);
Serial.print("Duration=");Serial.println(c2-c1);
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.print("Start...");
t.enable();
}
void callback() {
}
void loop() {
// put your main code here, to run repeatedly:
ts.execute();
}

View File

@@ -0,0 +1,105 @@
/**
* This is a test of TaskScheduler layered priority funtionality
*
* Current test employs two priority layers:
* Base scheduler runs tasks t1, t2 and t3
* High priority scheduler runs tasks t4 and t5
*
* Sequence of task scheduling (not execution!) is:
* 4, 5, 1, 4, 5, 2, 4, 5, 3 = one base scheduler pass
*
* Scheduling overhead (at 20 micros per one pass) is: (B + B * H) * T = (3 + 3 * 2) * 18 = 162 micros
* where
* B - number of tasks in the base scheduler's chain
* H - number of tasks in the high priority scheduler's chain
* T - scheduling overhead for 1 pass (~15-18 microseconds)
*
* Actual task execution order:
Scheduler Priority Test
Task: 40: 0 Start delay = 0
Task: 50: 10 Start delay = 10
Task: 1: 21 Start delay = 21
Task: 2: 31 Start delay = 31
Task: 3: 41 Start delay = 41
Task: 40: 500 Start delay = 0
Task: 40: 1000 Start delay = 0
Task: 50: 1010 Start delay = 10
Task: 1: 1021 Start delay = 20
Task: 40: 1500 Start delay = 0
Task: 40: 2000 Start delay = 0
Task: 50: 2011 Start delay = 11
Task: 1: 2022 Start delay = 21
Task: 2: 2032 Start delay = 32
Task: 40: 2500 Start delay = 0
Task: 40: 3000 Start delay = 0
Task: 50: 3010 Start delay = 10
Task: 1: 3021 Start delay = 20
Task: 3: 3032 Start delay = 32
Task: 40: 3500 Start delay = 0
Task: 40: 4000 Start delay = 0
Task: 50: 4011 Start delay = 11
Task: 1: 4022 Start delay = 21
Task: 2: 4032 Start delay = 32
Task: 40: 4500 Start delay = 0
Task: 40: 5000 Start delay = 0
Task: 50: 5010 Start delay = 10
Task: 1: 5021 Start delay = 20
Task: 40: 5500 Start delay = 0
Task: 40: 6000 Start delay = 0
Task: 50: 6010 Start delay = 10
Task: 1: 6022 Start delay = 21
Task: 2: 6032 Start delay = 32
Task: 3: 6043 Start delay = 42
*/
#define _TASK_SLEEP_ON_IDLE_RUN
#define _TASK_PRIORITY
#define _TASK_WDT_IDS
#define _TASK_TIMECRITICAL
#include <TaskScheduler.h>
Scheduler r, hpr;
// Callback methods prototypes
void tCallback();
// Tasks
Task t1(1000, TASK_FOREVER, &tCallback, &r); //adding task to the chain on creation
Task t2(2000, TASK_FOREVER, &tCallback, &r);
Task t3(3000, TASK_FOREVER, &tCallback, &r);
Task t4(500, TASK_FOREVER, &tCallback, &hpr); //adding task to the chain on creation
Task t5(1000, TASK_FOREVER, &tCallback, &hpr); //adding task to the chain on creation
void tCallback() {
Scheduler &s = Scheduler::currentScheduler();
Task &t = s.currentTask();
Serial.print("Task: "); Serial.print(t.getId());Serial.print(":\t");
Serial.print(millis()); Serial.print("\tStart delay = "); Serial.println(t.getStartDelay());
delay(10);
if (t.getId() == 3) Serial.println();
}
void setup () {
Serial.begin(115200);
Serial.println("Scheduler Priority Test");
t4.setId(40);
t5.setId(50);
r.setHighPriorityScheduler(&hpr);
r.enableAll(true); // this will recursively enable the higher priority tasks as well
}
void loop () {
r.execute();
}

View File

@@ -0,0 +1,136 @@
/**
* This is a test of TaskScheduler layered priority funtionality
*
* Current test employs three priority layers:
* Base scheduler runs tasks t1, t2 and t3
* High priority scheduler runs tasks t4 and t5
* Highest priority scheduler runs tasks t6 and t7
*
* Sequence of task scheduling (not execution!) is:
* 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
*
* 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
* where
* B - number of tasks in the base scheduler's chain
* H - number of tasks in the high priority scheduler's chain
* C - number of tasks in the critical priority scheduler's chain
* T - scheduling overhead for 1 pass (~15-18 microseconds)
*
* Actual task execution order:
Scheduler Priority Test
Task: 600: 0 Start delay = 0
Task: 700: 10 Start delay = 10
Task: 40: 21 Start delay = 21
Task: 50: 31 Start delay = 31
Task: 1: 43 Start delay = 41
Task: 2: 53 Start delay = 53
Task: 3: 63 Start delay = 63
Task: 600: 500 Start delay = 0
Task: 40: 510 Start delay = 10
Task: 600: 1000 Start delay = 0
Task: 700: 1010 Start delay = 10
Task: 40: 1021 Start delay = 21
Task: 50: 1032 Start delay = 32
Task: 1: 1043 Start delay = 43
Task: 600: 1500 Start delay = 0
Task: 40: 1510 Start delay = 10
Task: 600: 2000 Start delay = 0
Task: 700: 2011 Start delay = 11
Task: 40: 2022 Start delay = 22
Task: 50: 2032 Start delay = 32
Task: 1: 2043 Start delay = 43
Task: 2: 2054 Start delay = 54
Task: 600: 2500 Start delay = 0
Task: 40: 2510 Start delay = 10
Task: 600: 3000 Start delay = 0
Task: 700: 3010 Start delay = 10
Task: 40: 3021 Start delay = 21
Task: 50: 3032 Start delay = 32
Task: 1: 3043 Start delay = 43
Task: 3: 3053 Start delay = 53
Task: 600: 3500 Start delay = 0
Task: 40: 3510 Start delay = 10
Task: 600: 4000 Start delay = 0
Task: 700: 4011 Start delay = 11
Task: 40: 4022 Start delay = 22
Task: 50: 4032 Start delay = 32
Task: 1: 4043 Start delay = 43
Task: 2: 4054 Start delay = 54
Task: 600: 4500 Start delay = 0
Task: 40: 4510 Start delay = 10
Task: 600: 5000 Start delay = 0
Task: 700: 5010 Start delay = 10
Task: 40: 5021 Start delay = 21
Task: 50: 5031 Start delay = 31
Task: 1: 5043 Start delay = 43
Task: 600: 5500 Start delay = 0
Task: 40: 5511 Start delay = 11
Task: 600: 6000 Start delay = 0
Task: 700: 6010 Start delay = 10
Task: 40: 6022 Start delay = 22
Task: 50: 6032 Start delay = 32
Task: 1: 6043 Start delay = 43
Task: 2: 6053 Start delay = 53
Task: 3: 6065 Start delay = 65
*/
#define _TASK_SLEEP_ON_IDLE_RUN
#define _TASK_PRIORITY
#define _TASK_WDT_IDS
#define _TASK_TIMECRITICAL
#include <TaskScheduler.h>
Scheduler r;
Scheduler hpr;
Scheduler cpr;
// Callback methods prototypes
void tCallback();
// Tasks
Task t1(1000, TASK_FOREVER, &tCallback, &r); //adding task to the chain on creation
Task t2(2000, TASK_FOREVER, &tCallback, &r);
Task t3(3000, TASK_FOREVER, &tCallback, &r);
Task t4(500, TASK_FOREVER, &tCallback, &hpr); //adding task to the chain on creation
Task t5(1000, TASK_FOREVER, &tCallback, &hpr); //adding task to the chain on creation
Task t6(500, TASK_FOREVER, &tCallback, &cpr); //adding task to the chain on creation
Task t7(1000, TASK_FOREVER, &tCallback, &cpr); //adding task to the chain on creation
void tCallback() {
Scheduler &s = Scheduler::currentScheduler();
Task &t = s.currentTask();
Serial.print("Task: "); Serial.print(t.getId());Serial.print(":\t");
Serial.print(millis()); Serial.print("\tStart delay = "); Serial.println(t.getStartDelay());
delay(10);
if (t.getId() == 3) Serial.println();
}
void setup () {
Serial.begin(115200);
Serial.println("Scheduler Priority Test");
t4.setId(40);
t5.setId(50);
t6.setId(600);
t7.setId(700);
r.setHighPriorityScheduler(&hpr);
hpr.setHighPriorityScheduler(&cpr);
r.enableAll(true); // this will recursively enable the higher priority tasks as well
}
void loop () {
r.execute();
}

View File

@@ -0,0 +1,71 @@
/**
* TaskScheduler Test of microsecond scheduling resolution
*
* Task 1 runs starting with 211 microseconds intervals, doubling the interval on every iteration
* until it wraps when interval reaches about 72 minutes mark
*
* Task 2 provides heartbeat at a steady 5 seconds intervals
*
*/
#define _TASK_MICRO_RES
#include <TaskScheduler.h>
#define T1_INIT (211L)
Scheduler runner;
// Callback methods prototypes
void t1Callback();
void t1OnDisable();
void t2Callback();
unsigned long t1_interval = T1_INIT;
// Tasks
Task t1(t1_interval, 1, &t1Callback, &runner, true, NULL, &t1OnDisable); //adding task to the chain on creation
Task t2(5 * TASK_SECOND, TASK_FOREVER, &t2Callback, &runner, true); //adding task to the chain on creation
void t1Callback() {
unsigned long t = micros();
Serial.print("t1: ");
Serial.println(t);
}
void t1OnDisable() {
t1_interval += t1_interval;
if (t1_interval < T1_INIT) t1_interval = T1_INIT;
t1.setInterval(t1_interval);
t1.restartDelayed();
}
void t2Callback() {
unsigned long t = micros();
Serial.print("t2: ");
Serial.print(t);
Serial.println(" heartbeat");
}
void setup () {
Serial.begin(115200);
Serial.println("Scheduler TEST Microsecond Resolution");
Serial.println("5 seconds delay");
delay(5000);
runner.startNow(); // This creates a new scheduling starting point for all ACTIVE tasks.
// PLEASE NOTE - THIS METHOD DOES NOT ACTIVATE TASKS, JUST RESETS THE START TIME
t1.delay(); // Tasks which need to start delayed, need to be delayed again after startNow();
// Alternatively, tasks should be just enabled at the bottom of setup() method
// runner.enableAll();
// t1.delay();
}
void loop () {
runner.execute();
}

View File

@@ -0,0 +1,339 @@
/**
This test illustrates the use if yield methods and internal StatusRequest objects
THIS TEST HAS BEEN TESTED ON NODEMCU V.2 (ESP8266)
The WiFi initialization and NTP update is executed in parallel to blinking the onboard LED
and an external LED connected to D2 (GPIO04)
Try running with and without correct WiFi parameters to observe the difference in behaviour
*/
#define _TASK_SLEEP_ON_IDLE_RUN
#define _TASK_STATUS_REQUEST
#include <TaskScheduler.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
Scheduler ts;
// Callback methods prototypes
void connectInit();
void ledCallback();
bool ledOnEnable();
void ledOnDisable();
void ledOn();
void ledOff();
void ntpUpdateInit();
// Tasks
Task tConnect (TASK_SECOND, TASK_FOREVER, &connectInit, &ts, true);
Task tLED (TASK_IMMEDIATE, TASK_FOREVER, &ledCallback, &ts, false, &ledOnEnable, &ledOnDisable);
// Tasks running on events
Task tNtpUpdate (&ntpUpdateInit, &ts);
// Replace with WiFi parameters of your Access Point/Router:
const char *ssid = "wifi_network";
const char *pwd = "wifi_password";
long ledDelayOn, ledDelayOff;
#define LEDPIN D0 // Onboard LED pin - linked to WiFi
#define LEDPIN2 D2 // External LED
#define CONNECT_TIMEOUT 30 // Seconds
#define CONNECT_OK 0 // Status of successful connection to WiFi
#define CONNECT_FAILED (-99) // Status of failed connection to WiFi
// NTP Related Definitions
#define NTP_PACKET_SIZE 48 // NTP time stamp is in the first 48 bytes of the message
IPAddress timeServerIP; // time.nist.gov NTP server address
const char* ntpServerName = "time.nist.gov";
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
unsigned long epoch;
WiFiUDP udp; // A UDP instance to let us send and receive packets over UDP
#define LOCAL_NTP_PORT 2390 // Local UDP port for NTP update
void setup() {
Serial.begin(74880);
Serial.println(F("TaskScheduler test #14 - Yield and internal StatusRequests"));
Serial.println(F("=========================================================="));
Serial.println();
pinMode (LEDPIN, OUTPUT);
pinMode (LEDPIN2, OUTPUT);
tNtpUpdate.waitFor( tConnect.getInternalStatusRequest() ); // NTP Task will start only after connection is made
}
void loop() {
ts.execute(); // Only Scheduler should be executed in the loop
}
/**
Initiate connection to the WiFi network
*/
void connectInit() {
Serial.print(millis());
Serial.println(F(": connectInit."));
Serial.println(F("WiFi parameters: "));
Serial.print(F("SSID: ")); Serial.println(ssid);
Serial.print(F("PWD : ")); Serial.println(pwd);
WiFi.mode(WIFI_STA);
WiFi.hostname("esp8266");
WiFi.begin(ssid, pwd);
yield();
ledDelayOn = TASK_SECOND / 2;
ledDelayOff = TASK_SECOND / 4;
tLED.enable();
tConnect.yield(&connectCheck); // This will pass control back to Scheduler and then continue with connection checking
}
/**
Periodically check if connected to WiFi
Re-request connection every 5 seconds
Stop trying after a timeout
*/
void connectCheck() {
Serial.print(millis());
Serial.println(F(": connectCheck."));
if (WiFi.status() == WL_CONNECTED) { // Connection established
Serial.print(millis());
Serial.print(F(": Connected to AP. Local ip: "));
Serial.println(WiFi.localIP());
tConnect.disable();
}
else {
if (tConnect.getRunCounter() % 5 == 0) { // re-request connection every 5 seconds
Serial.print(millis());
Serial.println(F(": Re-requesting connection to AP..."));
WiFi.disconnect(true);
yield(); // This is an esp8266 standard yield to allow linux wifi stack run
WiFi.hostname("esp8266");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, pwd);
yield(); // This is an esp8266 standard yield to allow linux wifi stack run
}
if (tConnect.getRunCounter() == CONNECT_TIMEOUT) { // Connection Timeout
tConnect.getInternalStatusRequest()->signal(CONNECT_FAILED); // Signal unsuccessful completion
tConnect.disable();
Serial.print(millis());
Serial.println(F(": connectOnDisable."));
Serial.print(millis());
Serial.println(F(": Unable to connect to WiFi."));
ledDelayOn = TASK_SECOND / 16; // Blink LEDs quickly due to error
ledDelayOff = TASK_SECOND / 16;
tLED.enable();
}
}
}
/**
Initiate NTP update if connection was established
*/
void ntpUpdateInit() {
Serial.print(millis());
Serial.println(F(": ntpUpdateInit."));
if ( tConnect.getInternalStatusRequest()->getStatus() != CONNECT_OK ) { // Check status of the Connect Task
Serial.print(millis());
Serial.println(F(": cannot update NTP - not connected."));
return;
}
udp.begin(LOCAL_NTP_PORT);
if ( WiFi.hostByName(ntpServerName, timeServerIP) ) { //get a random server from the pool
Serial.print(millis());
Serial.print(F(": timeServerIP = "));
Serial.println(timeServerIP);
sendNTPpacket(timeServerIP); // send an NTP packet to a time server
}
else {
Serial.print(millis());
Serial.println(F(": NTP server address lookup failed."));
tLED.disable();
udp.stop();
tNtpUpdate.disable();
return;
}
ledDelayOn = TASK_SECOND / 8;
ledDelayOff = TASK_SECOND / 8;
tLED.enable();
tNtpUpdate.set( TASK_SECOND, CONNECT_TIMEOUT, &ntpCheck );
tNtpUpdate.enableDelayed();
}
/**
* Check if NTP packet was received
* Re-request every 5 seconds
* Stop trying after a timeout
*/
void ntpCheck() {
Serial.print(millis());
Serial.println(F(": ntpCheck."));
if ( tNtpUpdate.getRunCounter() % 5 == 0) {
Serial.print(millis());
Serial.println(F(": Re-requesting NTP update..."));
udp.stop();
yield();
udp.begin(LOCAL_NTP_PORT);
sendNTPpacket(timeServerIP);
return;
}
if ( doNtpUpdateCheck()) {
Serial.print(millis());
Serial.println(F(": NTP Update successful"));
Serial.print(millis());
Serial.print(F(": Unix time = "));
Serial.println(epoch);
tLED.disable();
tNtpUpdate.disable();
udp.stop();
}
else {
if ( tNtpUpdate.isLastIteration() ) {
Serial.print(millis());
Serial.println(F(": NTP Update failed"));
tLED.disable();
udp.stop();
}
}
}
/**
* Send NTP packet to NTP server
*/
unsigned long sendNTPpacket(IPAddress & address)
{
Serial.print(millis());
Serial.println(F(": sendNTPpacket."));
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
udp.beginPacket(address, 123); //NTP requests are to port 123
udp.write(packetBuffer, NTP_PACKET_SIZE);
udp.endPacket();
yield();
}
/**
* Check if a packet was recieved.
* Process NTP information if yes
*/
bool doNtpUpdateCheck() {
Serial.print(millis());
Serial.println(F(": doNtpUpdateCheck."));
yield();
int cb = udp.parsePacket();
if (cb) {
// We've received a packet, read the data from it
udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
//the timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. First, esxtract the two words:
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
// now convert NTP time into everyday time:
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
// subtract seventy years:
epoch = secsSince1900 - seventyYears;
return (epoch != 0);
}
return false;
}
/**
* Flip the LED state based on the current state
*/
bool ledState;
void ledCallback() {
if ( ledState ) ledOff();
else ledOn();
}
/**
* Make sure the LED starts lit
*/
bool ledOnEnable() {
ledOn();
return true;
}
/**
* Make sure LED ends dimmed
*/
void ledOnDisable() {
ledOff();
}
/**
* Turn LEDs on.
* Set appropriate delay.
* PLEASE NOTE: NodeMCU onbaord LED is active-low
*/
void ledOn() {
ledState = true;
digitalWrite(LEDPIN, LOW);
digitalWrite(LEDPIN2, HIGH);
tLED.delay( ledDelayOn );
}
/**
* Turn LEDs off.
* Set appropriate delay.
* PLEASE NOTE: NodeMCU onbaord LED is active-low
*/
void ledOff() {
ledState = false;
digitalWrite(LEDPIN, HIGH);
digitalWrite(LEDPIN2, LOW);
tLED.delay( ledDelayOff );
}

View File

@@ -0,0 +1,53 @@
/**
* TaskScheduler Test sketch - Showing how to use std::function
* to get acces to variables from within the task callback function
*
* Support for std::function is only available for ESP8266 architecture
*/
#define _TASK_SLEEP_ON_IDLE_RUN
#define _TASK_STD_FUNCTION // Compile with support for std::function
#include <TaskScheduler.h>
Scheduler ts;
int counter = 0;
class Calculator {
public:
int cumSum = 0; // cumulative sum
Calculator(int b) {
// Pass the this pointer, so that we get access to this->cumSum
// Also pass a copy of b
calculateTask.set(TASK_SECOND, TASK_FOREVER, [this, b]() {
counter++;
Serial.printf("%u. %u: cumSum = %u + %u\t", counter, millis(), cumSum, b);
cumSum += b;
Serial.printf("Resulting cumulative sum: %u\n", cumSum);
});
ts.addTask(calculateTask);
calculateTask.enable();
}
Task calculateTask;
};
Calculator calc1(2);
Calculator calc2(4);
Calculator calc3(8);
// Disable tasks after 10 seconds
Task tWrapper(10*TASK_SECOND, TASK_ONCE, []() {
ts.disableAll();
}, &ts);
/**
* Standard Arduino setup and loop methods
*/
void setup() {
Serial.begin(74880);
Serial.println("std::function test");
tWrapper.enableDelayed();
}
void loop() {
ts.execute();
}

View File

@@ -0,0 +1,18 @@
//This file is intentionally left blank.
//
//Arduino IDE plays some dirty tricks on the main sketch .ino file:
//it rearranges #includes, blindly creates forward definitions,
//includes every file in the project that does not have .c or .cpp
//file extension.
//
//Usually it all turns well if you have only one source file and you are either
//inexperienced or really expert C++ Arduino programmer.
//For the folks with the middle ground skills level, when you want
//to split your code into several .cpp files, it is best to leave
//this main sketch empty.
//
//It doesn't matter where you define the void loop() and void setup().
//Just make sure there is exactly one definition of each.
//
//And if you want to use standard Arduino functions
//like digitalWrite or the Serial object - just add #include<Arduino.h>.

View File

@@ -0,0 +1,31 @@
#include <Arduino.h>
#include "header.hpp"
//Declare the functions we want to use before we are ready to define them
void t1Callback();
// Tasks
Task t1(2000, 10, &t1Callback, &runner, true); //adding task to the chain on creation
Task t3(5000, TASK_FOREVER, &t3Callback);
void t1Callback() {
Serial.print("t1: ");
Serial.println(millis());
if (t1.isFirstIteration()) {
runner.addTask(t3);
t3.enable();
Serial.println("t1: enabled t3 and added to the chain");
}
if (t1.isLastIteration()) {
t3.disable();
runner.deleteTask(t3);
t2.setInterval(500);
Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500");
}
}

View File

@@ -0,0 +1,55 @@
// Test the same as example#2:
// Initially only tasks 1 and 2 are enabled
// Task1 runs every 2 seconds 10 times and then stops
// Task2 runs every 3 seconds indefinitely
// Task1 enables Task3 at its first run
// Task3 run every 5 seconds
// loop() runs every 1 second (a default scheduler delay, if no shorter tasks' interval is detected)
// Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds
// Because Task2 interval is shorter than Scheduler default tick, loop() executes ecery 1/2 seconds now
// At the end Task2 is the only task running every 1/2 seconds
//Header that declares all shared objects between .cpp files
#include "header.hpp"
#include <Arduino.h> //for Serial and delay
Scheduler runner; //Let the scheduler live here, in the main file, ok?
//Pretend, that the t2 task is a special task,
//that needs to live in file2 object file.
void t2Callback() {
Serial.print("t2: ");
Serial.println(millis());
}
Task t2(3000, TASK_FOREVER, &t2Callback, &runner, true);
//Lets define t3Callback here. We are going to use it in file1
//for Task 1.
void t3Callback() {
Serial.print("t3: ");
Serial.println(millis());
}
void setup () {
Serial.begin(115200);
delay(5000);
Serial.println("Scheduler TEST (multi-tab)");
runner.startNow(); // set point-in-time for scheduling start
}
void loop () {
runner.execute();
// Serial.println("Loop ticks at: ");
// Serial.println(millis());
}

View File

@@ -0,0 +1,15 @@
//This is the place to declare every single function
//and global variable that is going to be reused between cpp files.
//We are going to use the TaskScheduler, but only the declarations part.
//Remember to put customization macros before the #include:
#define _TASK_SLEEP_ON_IDLE_RUN
#include <TaskSchedulerDeclarations.h>
//Let the runner object be a global, single instance shared between object files.
extern Scheduler runner;
extern Task t2; //the t2 is defined in file2, but we need to access it from file1.
//This function needs to be shared (between file2 and file1).
void t3Callback();

View File

@@ -0,0 +1,20 @@
//This is the only .cpp file that gets the #include<TaskScheduler.h>.
//Without it, the linker would not find necessary TaskScheduler's compiled code.
//
//Remember to put customization macros here as well.
//
//And don't import any common headers (here: header.hpp)
//
//Really. This file needs to be short. All stuff is in TaskScheduler.h.
// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns
#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass
// #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer
// #define _TASK_PRIORITY // Support for layered scheduling priority
// #define _TASK_MICRO_RES // Support for microsecond resolution
// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY)
// #define _TASK_DEBUG // Make all methods and variables public for debug purposes
#include <TaskScheduler.h>

View File

@@ -0,0 +1,90 @@
/*
This eaxmple illustrates the use of overall Task timeout functionality:
Task 1 - runs every 1 seconds and times out in 10 seconds
Task 2 - runs every 5 seconds and resets the timeout every run, so runs continuosly even though the timeout is set to 10 seconds
*/
// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns
#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass
//#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer
// #define _TASK_PRIORITY // Support for layered scheduling priority
// #define _TASK_MICRO_RES // Support for microsecond resolution
// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY)
// #define _TASK_DEBUG // Make all methods and variables public for debug purposes
// #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations
#define _TASK_TIMEOUT
#include <TaskScheduler.h>
Scheduler ts;
void task1Callback();
void task1OnDisable();
void task2Callback();
void task2OnDisable();
Task t1(1 * TASK_SECOND, TASK_FOREVER, &task1Callback, &ts, false, NULL, &task1OnDisable);
Task t2(5 * TASK_SECOND, TASK_FOREVER, &task2Callback, &ts, false, NULL, &task2OnDisable);
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.println("TaskScheduler Timeout example");
Serial.println("=============================");
t1.setTimeout(10 * TASK_SECOND);
t2.setTimeout(10 * TASK_SECOND);
ts.enableAll();
}
void loop() {
// put your main code here, to run repeatedly:
ts.execute();
}
void task1Callback() {
Serial.print("Task 1:\t");
Serial.print(millis());
Serial.print(": t/out=");
Serial.print(t1.getTimeout());
Serial.print("\tms until t/out=");
Serial.println( t1.untilTimeout());
}
void task1OnDisable() {
if (t1.timedOut()) {
Serial.println("Task 1 has timed out. Restarting");
t1.setInterval(1 * TASK_SECOND);
t1.setIterations(15);
t1.setTimeout(TASK_NOTIMEOUT);
t1.enable();
}
else {
Serial.println("Task 1 has been disabled");
}
}
void task2Callback() {
Serial.print("Task 2:\t");
Serial.print(millis());
Serial.print(": t/out=");
Serial.print(t2.getTimeout());
Serial.print("\tms until t/out=");
Serial.println( t2.untilTimeout());
t2.resetTimeout();
}
void task2OnDisable() {
if (t2.timedOut()) {
Serial.println("Task 2 has timed out");
}
else {
Serial.println("Task 2 has been disabled");
}
}

View File

@@ -0,0 +1,172 @@
/**
This is example 5 rewritten with Timeout, LTS and WDT functioanlity:
- 1 second timeout is set for the main calculation task
- LTS is used to address individual array elements for each sensor sinlce the callback code is shared
- WDT is used to set the Task ID and use that as an index for array of distances (alternative to LTS)
Original description:
====================
This test emulates querying 3 sensors once every 10 seconds, each could respond with a different delay
(ultrasonic sensors for instance) and printing a min value of the three when all three have reported their values.
The overall timeout of 1 second is setup as well.
An error message needs to be printed if a timeout occurred instead of a value.
Example5:
Sketch uses 6066 bytes (18%) of program storage space. Maximum is 32256 bytes.
Global variables use 1039 bytes (50%) of dynamic memory, leaving 1009 bytes for local variables. Maximum is 2048 bytes.
Example 18:
Sketch uses 5142 bytes (15%) of program storage space. Maximum is 32256 bytes.
Global variables use 878 bytes (42%) of dynamic memory, leaving 1170 bytes for local variables. Maximum is 2048 bytes.
*/
// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns
#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass
#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
#define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
#define _TASK_LTS_POINTER // Compile with support for local task storage pointer
#define _TASK_PRIORITY // Support for layered scheduling priority
// #define _TASK_MICRO_RES // Support for microsecond resolution
// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY)
#define _TASK_DEBUG // Make all methods and variables public for debug purposes
#define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations
#define _TASK_TIMEOUT // Support for overall task timeout
#include <TaskScheduler.h>
StatusRequest measure;
Scheduler ts, hts;
// Callback methods prototypes
void CycleCallback();
void CalcCallback();
bool CalcEnable();
void CalcDisable();
void SCallback(); bool SEnable();
// Tasks
Task tSensor1(0, TASK_ONCE, &SCallback, &ts, false, &SEnable); // task ID = 1
Task tSensor2(0, TASK_ONCE, &SCallback, &ts, false, &SEnable); // task ID = 2
Task tSensor3(0, TASK_ONCE, &SCallback, &ts, false, &SEnable); // task ID = 3
Task tCycle(10000, TASK_FOREVER, &CycleCallback, &hts);
Task tCalculate(TASK_IMMEDIATE , TASK_ONCE, &CalcCallback, &hts, false, &CalcEnable, &CalcDisable);
#define NO_OF_SENSORS 3
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
void CycleCallback() {
Serial.println();
Serial.print(millis()); Serial.print(":\t");
Serial.println("CycleCallback: Initiating measurement cycle every 10 seconds");
distance = 0;
measure.setWaiting(NO_OF_SENSORS); // Set the StatusRequest to wait for 3 signals.
tCalculate.waitFor(&measure);
}
bool CalcEnable() {
Serial.print(millis()); Serial.print(":\t");
Serial.println("CalcEnable: OnEnable");
Serial.println("Activating sensors and setting timeout");
tSensor1.restartDelayed();
tSensor2.restartDelayed();
tSensor3.restartDelayed();
return true;
}
void CalcDisable() {
if (tCalculate.timedOut()) {
measure.signalComplete(-1); // signal error
Serial.print(millis()); Serial.print(":\t");
Serial.println("MeasureCallback: ***** Timeout *****");
// tSensor1.disable();
// tSensor2.disable();
// tSensor3.disable();
}
}
void CalcCallback() {
Serial.print(millis()); Serial.print(":\t");
Serial.println("CalcCallback: calculating");
distance = -1;
if ( measure.getStatus() >= 0) { // only calculate if statusrequest ended successfully
distance = d[1] < d[2] ? d[1] : d[2];
distance = d[3] < distance ? d[3] : distance;
Serial.print("CalcCallback: Min distance="); Serial.println(distance);
Serial.println();
}
}
/** Simulation code for all sensors
-------------------------------
*/
bool SEnable() {
Task &t = ts.currentTask();
int i = t.getId();
Serial.print(millis()); Serial.print(":\t");
Serial.print("SEnable: TaskID=");
Serial.println(i);
Serial.print("Triggering sensor. Delay=");
t.setInterval( random(1200) ); // Simulating sensor delay, which could go over 1 second and cause timeout
// One way to update the 3 distances with one codebase - use task id as an index
d[i] = 0;
// Another way to update the 3 distances with one codebase - use LTS pointers
int *pd = (int*) t.getLtsPointer();
*pd = 0;
Serial.println( t.getInterval() );
return true;
}
void SCallback() {
Task &t = ts.currentTask();
int i = t.getId();
Serial.print(millis()); Serial.print(":\t");
Serial.print("SCallback: TaskID=");
Serial.println(i);
Serial.print("Emulating measurement. d=");
d[i] = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement
int *pd = (int*) t.getLtsPointer();
*pd = d[i];
measure.signal();
Serial.print(d[i]);
Serial.print("\t");
Serial.println(*pd);
}
/** Main Arduino code
Not much is left here - everything is taken care of by the framework
*/
void setup() {
Serial.begin(115200);
Serial.println("TaskScheduler StatusRequest Sensor Emulation Test. Complex Test.");
randomSeed(analogRead(A0) + millis());
tSensor1.setLtsPointer(&d_lts[0]);
tSensor2.setLtsPointer(&d_lts[1]);
tSensor3.setLtsPointer(&d_lts[2]);
ts.setHighPriorityScheduler(&hts);
tCalculate.setTimeout(1 * TASK_SECOND);
tCycle.enable();
}
void loop() {
ts.execute();
}

View File

@@ -0,0 +1,132 @@
/**
TaskScheduler Test sketch - test of Task destructor
Test case:
Main task runs every 100 milliseconds 100 times and in 50% cases generates a task object
which runs 1 to 10 times with 100 ms to 5 s interval, and then destroyed.
Garbage collection deletes all the tasks which have finished (enabled in their respective
OnDisable methods)
This sketch uses the following libraries:
- FreeMemory library: https://github.com/McNeight/MemoryFree
- QueueArray library: https://playground.arduino.cc/Code/QueueArray/
*/
#define _TASK_WDT_IDS // To enable task unique IDs
#define _TASK_SLEEP_ON_IDLE_RUN // Compile with support for entering IDLE SLEEP state for 1 ms if not tasks are scheduled to run
#define _TASK_LTS_POINTER // Compile with support for Local Task Storage pointer
#include <TaskScheduler.h>
#include <QueueArray.h>
#if defined (ARDUINO_ARCH_AVR)
#include <MemoryFree.h>
#elif defined(__arm__)
extern "C" char* sbrk(int incr);
static int freeMemory() {
char top = 't';
return &top - reinterpret_cast<char*>(sbrk(0));
}
#else
int freeMemory(); // supply your own
#endif
Scheduler ts;
// Callback methods prototypes
void MainLoop();
void GC();
// Statis task
Task tMain(100 * TASK_MILLISECOND, 100, &MainLoop, &ts, true);
Task tGarbageCollection(200 * TASK_MILLISECOND, TASK_FOREVER, &GC, &ts, false);
void Iteration();
bool OnEnable();
void OnDisable();
int noOfTasks = 0;
QueueArray <Task*> toDelete;
void MainLoop() {
Serial.print(millis()); Serial.print("\t");
Serial.print("MainLoop run: ");
int i = tMain.getRunCounter();
Serial.print(i); Serial.print(F(".\t"));
if ( random(0, 101) > 50 ) { // generate a new task only in 50% of cases
// Generating another task
long p = random(100, 5001); // from 100 ms to 5 seconds
long j = random(1, 11); // from 1 to 10 iterations)
Task *t = new Task(p, j, Iteration, &ts, false, OnEnable, OnDisable);
Serial.print(F("Generated a new task:\t")); Serial.print(t->getId()); Serial.print(F("\tInt, Iter = \t"));
Serial.print(p); Serial.print(", "); Serial.print(j);
Serial.print(F("\tFree mem=")); Serial.print(freeMemory());
Serial.print(F("\tNo of tasks=")); Serial.println(++noOfTasks);
t->enable();
}
else {
Serial.println(F("Skipped generating a task"));
}
}
void Iteration() {
Task &t = ts.currentTask();
Serial.print(millis()); Serial.print("\t");
Serial.print("Task N"); Serial.print(t.getId()); Serial.print(F("\tcurrent iteration: "));
int i = t.getRunCounter();
Serial.println(i);
}
bool OnEnable() {
// to-do: think of something to put in here.
return true;
}
void OnDisable() {
Task *t = &ts.currentTask();
unsigned int tid = t->getId();
toDelete.push(t);
tGarbageCollection.enableIfNot();
Serial.print(millis()); Serial.print("\t");
Serial.print("Task N"); Serial.print(tid); Serial.println(F("\tfinished"));
}
/**
Standard Arduino setup and loop methods
*/
void setup() {
Serial.begin(115200);
randomSeed(analogRead(0) + analogRead(5));
noOfTasks = 0;
Serial.println(F("Dynamic Task Creation/Destruction Example"));
Serial.println();
Serial.print(F("Free mem=")); Serial.print(freeMemory());
Serial.print(F("\tNo of tasks=")); Serial.println(noOfTasks);
Serial.println();
}
void GC() {
if ( toDelete.isEmpty() ) {
tGarbageCollection.disable();
return;
}
Task *t = toDelete.pop();
Serial.print(millis()); Serial.print("\t");
Serial.print("Task N"); Serial.print(t->getId()); Serial.println(F("\tdestroyed"));
Serial.print("Free mem="); Serial.print(freeMemory());
Serial.print(F("\tNo of tasks=")); Serial.println(--noOfTasks);
delete t;
}
void loop() {
ts.execute();
}

View File

@@ -0,0 +1,185 @@
/**
This is example 5 rewritten with Timeout, LTS, WDT functioanlity + multitab and extra classes
- 1 second timeout is set for the main calculation task
- LTS is used to address task-specific sensor class object
- WDT is used to set the Task ID and use that for identifying the tasks (debug)
Original description:
====================
This test emulates querying 1 to 10 sensors once every 10 seconds, each could respond with a different delay
(ultrasonic sensors for instance) and printing a max value of them when all have reported their values.
The overall timeout of 1 second is setup as well.
An error message needs to be printed if a timeout occurred instead of a distance value.
Task and SuperSensor objects are dynamically created and destroyed as needed every 10 seconds
*/
// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns
#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass
#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
#define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
#define _TASK_LTS_POINTER // Compile with support for local task storage pointer
#define _TASK_PRIORITY // Support for layered scheduling priority
// #define _TASK_MICRO_RES // Support for microsecond resolution
// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY)
#define _TASK_DEBUG // Make all methods and variables public for debug purposes
#define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations
#define _TASK_TIMEOUT // Support for overall task timeout
#include <TaskScheduler.h>
#include "SuperSensor.h"
StatusRequest measure;
Scheduler ts, hts;
// Callback methods prototypes
void CycleCallback();
void CalcCallback();
bool CalcEnable();
void CalcDisable();
void SCallback();
bool SEnable();
void SDisable();
// Tasks
Task tCycle(10000, TASK_FOREVER, &CycleCallback, &hts);
Task tCalculate(TASK_IMMEDIATE , TASK_ONCE, &CalcCallback, &hts, false, &CalcEnable, &CalcDisable);
int numberSensors;
long distance;
int pins[] = { 1, 9, 3, 7, 5, 6, 4, 8, 2, 10 };
void CycleCallback() {
Serial.println();Serial.println();Serial.println();
Serial.print(millis()); Serial.print(":\t");
Serial.println("CycleCallback: Initiating measurement cycle every 10 seconds");
Serial.print("Number of sensors=");
numberSensors = random(1, 11); // 1 to 10 sensors, randomly
distance = 0;
Serial.println(numberSensors);
measure.setWaiting(numberSensors); // Set the StatusRequest to wait for 3 signals.
tCalculate.waitFor(&measure);
tCalculate.setTimeout(1000 * TASK_MILLISECOND);
}
bool CalcEnable() {
Serial.print(millis()); Serial.print(":\t");
Serial.println("CalcEnable: OnEnable");
Serial.println("Activating sensors");
for (int i = 0; i < numberSensors; i++) {
Task *t = new Task(TASK_MILLISECOND, TASK_FOREVER, &SCallback, &ts, false, &SEnable, &SDisable);
SuperSensor *s = new SuperSensor( pins[i] );
t->setLtsPointer( (void*) s);
t->setId(i + 1);
s->begin();
t->restartDelayed();
}
return true;
}
void CalcDisable() {
if (tCalculate.timedOut()) {
measure.signalComplete(-1); // signal error
Serial.print(millis()); Serial.print(":\t");
Serial.println("MeasureCallback: ***** Timeout *****");
}
ts.disableAll(false); // only disable tasks in the ts scheduler
}
void CalcCallback() {
Serial.print(millis()); Serial.print(":\t");
Serial.println("CalcCallback: calculating");
if ( measure.getStatus() >= 0) { // only calculate if statusrequest ended successfully
Serial.print("CalcCallback: Max distance="); Serial.println(distance);
Serial.println();
}
}
/** Simulation code for all sensors
-------------------------------
*/
bool SEnable() {
Task &t = ts.currentTask();
int i = t.getId();
Serial.print(millis()); Serial.print(":\t");
Serial.print("SEnable: TaskID=");
Serial.println(i);
Serial.print("Triggering sensor. Delay=");
// Another way to update the distances with one codebase - use LTS pointers
SuperSensor *s = (SuperSensor*) t.getLtsPointer();
long dly = s->trigger();
Serial.println( dly );
return true;
}
void SCallback() {
Task &t = ts.currentTask();
SuperSensor *s = (SuperSensor*) t.getLtsPointer();
if ( s->measurementReady() ) {
int i = t.getId();
Serial.print(millis()); Serial.print(":\t");
Serial.print("SCallback: TaskID=");
Serial.println(i);
Serial.print("Emulating measurement. d=");
long d = s->value();
if ( d > distance ) distance = d;
Serial.println(d);
measure.signal();
t.disable();
}
}
void SDisable() {
Task &t = ts.currentTask();
int i = t.getId();
Serial.print(millis()); Serial.print(":\t");
Serial.print("SDisable: TaskID=");
Serial.println(i);
SuperSensor *s = (SuperSensor*) ts.currentLts();
s->stop();
delete s;
delete &t;
}
/** Main Arduino code
Not much is left here - everything is taken care of by the framework
*/
void setup() {
Serial.begin(115200);
Serial.println("TaskScheduler StatusRequest Sensor Emulation Test. Complex Test.");
randomSeed(analogRead(A0) + millis());
ts.setHighPriorityScheduler(&hts);
tCalculate.setTimeout(1 * TASK_SECOND);
tCycle.enable();
}
void loop() {
ts.execute();
}

View File

@@ -0,0 +1,36 @@
#include "SuperSensor.h"
SuperSensor::SuperSensor(int aPin) {
iPin = aPin;
}
SuperSensor::~SuperSensor() {
iValue = -1;
}
void SuperSensor::begin() {
iDelay = random(300, 1500);
iValue = -1;
}
void SuperSensor::stop() {
//nothing to do
}
long SuperSensor::trigger() {
iStart = millis();
return iDelay;
}
bool SuperSensor::measurementReady() {
if ( millis() - iStart > iDelay ) {
iValue = random(501);
return true;
}
return false;
}
long SuperSensor::value() {
return iValue;
}

View File

@@ -0,0 +1,25 @@
#ifndef _SUPER_SENSOR_H
#define _SUPER_SENSOR_H
#include "Arduino.h"
#include <TaskSchedulerDeclarations.h>
class SuperSensor {
public:
SuperSensor(int aPin);
~SuperSensor();
void begin();
void stop();
long trigger();
bool measurementReady();
long value();
private:
long iDelay;
long iValue;
int iPin;
unsigned long iStart;
};
#endif // _SUPER_SENSOR_H

View File

@@ -0,0 +1,66 @@
#include "Calculator.h"
#include "SuperSensor.h"
#if defined (ARDUINO_ARCH_AVR)
#include <MemoryFree.h>
#endif
#if defined(__arm__)
extern "C" char* sbrk(int incr);
static int freeMemory() {
char top = 't';
return &top - reinterpret_cast<char*>(sbrk(0));
}
#endif
Calculator::Calculator( Scheduler* aS, Scheduler* aSensors) : Task(aS) {
iS = aSensors;
setTimeout(1000 * TASK_MILLISECOND);
}
bool Calculator::Callback() {
Serial.print(millis()); Serial.print(":\t");
Serial.println("CalcCallback: calculating");
if ( getStatusRequest()->getStatus() >= 0) { // only calculate if statusrequest ended successfully
Serial.print("CalcCallback: Max distance="); Serial.println(distance);
}
return false;
}
extern int pins[];
bool Calculator::OnEnable() {
Serial.print(millis()); Serial.print(":\t");
Serial.println("CalcEnable: OnEnable");
Serial.println("Activating sensors");
StatusRequest* sr = getStatusRequest();
iNS = sr->getCount();
distance = 0;
for (int i = 0; i < iNS; i++) {
SuperSensor *s = new SuperSensor( iS, pins[i], this, sr);
s->setId(i + 1);
s->begin();
s->restartDelayed();
}
return true;
}
void Calculator::OnDisable() {
if ( timedOut() ) {
getStatusRequest()->signalComplete(-1); // signal error
Serial.print(millis()); Serial.print(":\t");
Serial.println("MeasureCallback: ***** Timeout *****");
}
iS->disableAll(false); // only disable tasks in the ts scheduler
#if defined (ARDUINO_ARCH_AVR) || defined(__arm__)
Serial.print("Free mem = "); Serial.println(freeMemory()); Serial.println();
#endif
}
void Calculator::reportDistance(long aD) {
if (distance < aD) distance = aD;
}

View File

@@ -0,0 +1,34 @@
#ifndef _CALCULATOR_H
#define _CALCULATOR_H
#include "Arduino.h"
#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass
#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
#define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
#define _TASK_PRIORITY // Support for layered scheduling priority
#define _TASK_TIMEOUT // Support for overall task timeout
#define _TASK_OO_CALLBACKS // Support for dynamic callback method binding
#include <TaskSchedulerDeclarations.h>
class Calculator : public Task {
public:
Calculator( Scheduler* aS, Scheduler* aSensors);
void reportDistance(long aD);
bool Callback();
bool OnEnable();
void OnDisable();
private:
Scheduler* iS;
long distance;
int iNS;
};
#endif // _CALCULATOR_H

View File

@@ -0,0 +1,76 @@
/**
This is example 5 rewritten with dynamic binding of callback methods
- 1 second timeout is set for the main calculation task
- LTS is used to address task-specific sensor class object
- WDT is used to set the Task ID and use that for identifying the tasks (debug)
Original description:
====================
This test emulates querying 1 to 10 sensors once every 10 seconds, each could respond with a different delay
(ultrasonic sensors for instance) and printing a max value of them when all have reported their values.
The overall timeout of 1 second is setup as well.
An error message needs to be printed if a timeout occurred instead of a distance value.
Task and SuperSensor objects are dynamically created and destroyed as needed every 10 seconds
This sketch uses a FreeMemory library: https://github.com/McNeight/MemoryFree
FreeMemory for ARM32 boards is based on: http://www.stm32duino.com/viewtopic.php?f=18&t=2065
*/
#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass
#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
#define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
#define _TASK_PRIORITY // Support for layered scheduling priority
#define _TASK_TIMEOUT // Support for overall task timeout
#define _TASK_OO_CALLBACKS
#include <TaskScheduler.h>
#include "SuperSensor.h"
#include "Calculator.h"
#include "Ticker.h"
StatusRequest measure;
Scheduler ts, hts;
// Tasks
Calculator* tCalculate;
Ticker* tCycle;
int pins[] = { 1, 9, 3, 7, 5, 6, 4, 8, 2, 10 };
#ifdef ARDUINO_ARCH_STM32F1
#define A0 3
#endif
/** Main Arduino code
Not much is left here - everything is taken care of by the framework
*/
void setup() {
Serial.begin(115200);
delay(1000);
while (!Serial) {}
Serial.println("TaskScheduler StatusRequest Sensor Emulation Test. Complex Test.");
#ifdef ARDUINO_ARCH_STM32F1
pinMode(A0, INPUT_ANALOG);
#endif
randomSeed(analogRead(A0) + millis());
ts.setHighPriorityScheduler(&hts);
tCalculate = new Calculator (&hts, &ts);
tCycle = new Ticker (&hts, (Task*) tCalculate, &measure);
tCalculate->setTimeout(1 * TASK_SECOND);
tCycle->enable();
}
void loop() {
ts.execute();
}

View File

@@ -0,0 +1,83 @@
#include "SuperSensor.h"
SuperSensor::SuperSensor(Scheduler* aScheduler, int aPin, Calculator* aC, StatusRequest* aS) : Task(TASK_MILLISECOND, TASK_FOREVER, aScheduler, false) {
iPin = aPin;
iC = aC;
iS = aS;
}
SuperSensor::~SuperSensor() {
iValue = -1;
}
void SuperSensor::begin() {
iDelay = random(300, 1500);
iValue = -1;
}
void SuperSensor::stop() {
//nothing to do
}
long SuperSensor::trigger() {
iStart = millis();
return iDelay;
}
bool SuperSensor::measurementReady() {
if ( millis() - iStart > iDelay ) {
iValue = random(501);
return true;
}
return false;
}
long SuperSensor::value() {
return iValue;
}
bool SuperSensor::OnEnable() {
int i = getId();
Serial.print(millis()); Serial.print(":\t");
Serial.print("SEnable: TaskID=");
Serial.println(i);
Serial.print("Triggering sensor. Delay=");
long dly = trigger();
Serial.println( dly );
return true;
}
bool SuperSensor::Callback() {
if ( measurementReady() ) {
int i = getId();
Serial.print(millis()); Serial.print(":\t");
Serial.print("SCallback: TaskID=");
Serial.println(i);
Serial.print("Emulating measurement. d=");
long d = value();
iC->reportDistance(d);
Serial.println(d);
iS->signal();
disable();
delete this;
return true;
}
return false;
}
void SuperSensor::OnDisable() {
int i = getId();
Serial.print(millis()); Serial.print(":\t");
Serial.print("SDisable: TaskID=");
Serial.println(i);
stop();
}

View File

@@ -0,0 +1,44 @@
#ifndef _SUPER_SENSOR_H
#define _SUPER_SENSOR_H
#include "Arduino.h"
#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass
#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
#define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
#define _TASK_PRIORITY // Support for layered scheduling priority
#define _TASK_TIMEOUT // Support for overall task timeout
#define _TASK_OO_CALLBACKS
#include <TaskSchedulerDeclarations.h>
#include "Calculator.h"
//class Calculator;
class SuperSensor : public Task {
public:
SuperSensor(Scheduler* aScheduler, int aPin, Calculator* aC, StatusRequest* aS);
~SuperSensor();
void begin();
void stop();
long trigger();
bool measurementReady();
long value();
bool Callback();
bool OnEnable();
void OnDisable();
private:
long iDelay;
long iValue;
int iPin;
unsigned long iStart;
Calculator* iC;
StatusRequest* iS;
};
#endif // _SUPER_SENSOR_H

View File

@@ -0,0 +1,20 @@
#include "Ticker.h"
Ticker::Ticker(Scheduler* aS, Task* aCalc, StatusRequest* aM) : Task(10000, TASK_FOREVER, aS, false) {
iCalc = aCalc;
iMeasure = aM;
}
bool Ticker::Callback() {
Serial.println(); Serial.println(); Serial.println();
Serial.print(millis()); Serial.print(":\t");
Serial.println("CycleCallback: Initiating measurement cycle every 10 seconds");
int numberSensors = random(1, 11); // 1 to 10 sensors, randomly
Serial.print("Number of sensors=");
Serial.println(numberSensors);
iMeasure->setWaiting(numberSensors); // Set the StatusRequest to wait for 1 to 10 signals.
iCalc->waitFor(iMeasure);
}

View File

@@ -0,0 +1,28 @@
#ifndef _TICKER_H
#define _TICKER_H
#include "Arduino.h"
#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass
#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
#define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
#define _TASK_PRIORITY // Support for layered scheduling priority
#define _TASK_TIMEOUT // Support for overall task timeout
#define _TASK_OO_CALLBACKS
#include <TaskSchedulerDeclarations.h>
class Ticker : public Task {
public:
Ticker(Scheduler* aS, Task* aCalc, StatusRequest* aM);
~Ticker() {};
bool Callback();
private:
Task *iCalc;
StatusRequest* iMeasure;
};
#endif

View File

@@ -0,0 +1,157 @@
/*
An example of using scheduler's custom sleep callback method
An empty loop is executed for 10 seconds with a 10 ms. interval
The first time it is excuted with an empty sleep callback, and
the second time with a 1 ms delay in the custom callback
RESULTS:
Arduino Nano:
=================================
Testing empty sleep callback...
cEmptyCallback=1001
cEmptyTotal=423866
Testing 1 ms delayed sleep callback...
cDelayCallback=1001
cDelayTotal=10669
ESP8266 at 80MHz
=================================
Testing empty sleep callback...
cEmptyCallback=1001
cEmptyTotal=278101
Testing 1 ms delayed sleep callback...
cDelayCallback=1001
cDelayTotal=10493
ESP8266 at 160MHz
=================================
Testing empty sleep callback...
cEmptyCallback=1001
cEmptyTotal=546041
Testing 1 ms delayed sleep callback...
cDelayCallback=1001
cDelayTotal=10746
Maple Mini STM32 board at 70MHz -O3 code optimization
==================================
Testing empty sleep callback...
cEmptyCallback=1001
cEmptyTotal=2689973
Testing 1 ms delayed sleep callback...
cDelayCallback=1001
cDelayTotal=10958
esp32 at 240MHz
==================================
Testing empty sleep callback...
cEmptyCallback=1001
cEmptyTotal=492851
Testing 1 ms delayed sleep callback...
cDelayCallback=1001
cDelayTotal=11002
*/
#define _TASK_SLEEP_ON_IDLE_RUN
#include <TaskScheduler.h>
Scheduler ts;
// Callback methods prototypes
void Count();
bool tEmptyOn();
void tEmptyOff();
bool tDelayOn();
void tDelayOff();
// Sleep methods prototypes
void sEmpty(unsigned long aT);
void sDelay(unsigned long aT);
// Tasks
Task tCount ( 10, TASK_FOREVER, &Count, &ts, false );
Task tEmpty ( 10000, TASK_ONCE, NULL, &ts, false, &tEmptyOn, &tEmptyOff );
Task tDelay ( 10000, TASK_ONCE, NULL, &ts, false, &tDelayOn, &tDelayOff );
volatile unsigned long cEmptyCallback, cEmptyTotal, cDelayCallback, cDelayTotal;
volatile unsigned long *cCB, *cTL;
void setup() {
Serial.begin(115200);
delay(5000);
Serial.println("Start counting...");
ts.setSleepMethod( &sEmpty );
tEmpty.restartDelayed();
}
void sEmpty(unsigned long aT) {
}
void sDelay(unsigned long aT) {
delay(1);
}
bool tEmptyOn() {
Serial.println("Testing empty sleep callback...");
cCB = &cEmptyCallback;
cTL = &cEmptyTotal;
*cCB = 0;
*cTL = 0;
tCount.restart();
return true;
}
void tEmptyOff() {
tCount.disable();
Serial.print("cEmptyCallback="); Serial.println(*cCB);
Serial.print("cEmptyTotal="); Serial.println(*cTL);
ts.setSleepMethod( &sDelay );
tDelay.restartDelayed();
}
bool tDelayOn() {
Serial.println("Testing 1 ms delayed sleep callback...");
cCB = &cDelayCallback;
cTL = &cDelayTotal;
*cCB = 0;
*cTL = 0;
tCount.restart();
return true;
}
void tDelayOff() {
tCount.disable();
Serial.print("cDelayCallback="); Serial.println(*cCB);
Serial.print("cDelayTotal="); Serial.println(*cTL);
}
void Count() {
(*cCB)++;
}
void loop() {
// put your main code here, to run repeatedly:
ts.execute();
(*cTL)++;
}

View File

@@ -0,0 +1,87 @@
/**
This sketch collects scheduling overhead and CPU Idle Sleep information.
A task is invoked every 10 milliseconds for 10 seconds.
CPU statistics are collected
Compile and run once with _TASK_SLEEP_ON_IDLE_RUN enabled, then with _TASK_SLEEP_ON_IDLE_RUN disabled.
Compare the results.
*/
#define _TASK_ESP8266_DLY_THRESHOLD 50L
#define _TASK_ESP32_DLY_THRESHOLD 40L
#define _TASK_SLEEP_ON_IDLE_RUN
#define _TASK_TIMECRITICAL
#include <TaskScheduler.h>
Scheduler ts;
// Callback methods prototypes
void Count();
bool tOn(); void tOff();
// Tasks
Task c(10, TASK_FOREVER, &Count, &ts);
Task t(10000, TASK_ONCE, NULL, &ts, true, &tOn, &tOff);
volatile unsigned long c1, c2;
bool tOn() {
c1 = 0;
c2 = 0;
c.enable();
return true;
}
void tOff() {
c.disable();
unsigned long cpuTot = ts.getCpuLoadTotal();
unsigned long cpuCyc = ts.getCpuLoadCycle();
unsigned long cpuIdl = ts.getCpuLoadIdle();
Serial.print("Loop counts c1="); Serial.println(c1);
Serial.print("Task counts c2="); Serial.println(c2);
Serial.print("Total CPU time="); Serial.print(cpuTot); Serial.println(" micros");
Serial.print("Scheduling Overhead CPU time="); Serial.print(cpuCyc); Serial.println(" micros");
Serial.print("Idle Sleep CPU time="); Serial.print(cpuIdl); Serial.println(" micros");
Serial.print("Productive work CPU time="); Serial.print(cpuTot - cpuIdl - cpuCyc); Serial.println(" micros");
Serial.println();
float idle = (float)cpuIdl / (float)cpuTot * 100;
Serial.print("CPU Idle Sleep "); Serial.print(idle); Serial.println(" % of time.");
float prod = (float)(cpuIdl + cpuCyc) / (float)cpuTot * 100;
Serial.print("Productive work (not idle, not scheduling)"); Serial.print(100.00 - prod); Serial.println(" % of time.");
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
delay(1000);
Serial.println("CPU Time Measurement");
Serial.println("Start");
ts.startNow();
t.delay();
ts.cpuLoadReset();
}
void Count() {
c2++; // number of task callback invocations
// Try different delay intervals to see CPU statistics change
// delay(1);
// delay(5);
// delay(10);
// delay(20);
}
void loop() {
// put your main code here, to run repeatedly:
ts.execute();
c1++; // number of loop() cycles
}

View File

@@ -0,0 +1,127 @@
/*
TaskScheduler Example #25
Create 10 random Tasks. Prints the entire chain.
Then executes chain once printing the remaining chain from currently active task
Deletes all tasks afterwards
*/
// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns
#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass
// #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
#define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer
// #define _TASK_PRIORITY // Support for layered scheduling priority
// #define _TASK_MICRO_RES // Support for microsecond resolution
// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY)
// #define _TASK_DEBUG // Make all methods and variables public for debug purposes
// #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations
// #define _TASK_TIMEOUT // Support for overall task timeout
// #define _TASK_OO_CALLBACKS // Support for dynamic callback method binding
// #define _TASK_DEFINE_MILLIS // Force forward declaration of millis() and micros() "C" style
#define _TASK_EXPOSE_CHAIN // Methods to access tasks in the task chain
#include <TaskScheduler.h>
#include <QueueArray.h>
// Debug and Test options
#define _DEBUG_
//#define _TEST_
#ifdef _DEBUG_
#define _PP(a) Serial.print(a);
#define _PL(a) Serial.println(a);
#else
#define _PP(a)
#define _PL(a)
#endif
// Scheduler
Scheduler ts;
void taskCallback();
void cleanUp();
Task tManager(6000, TASK_ONCE, &cleanUp, &ts, false);
/*
Scheduling defines:
TASK_MILLISECOND
TASK_SECOND
TASK_MINUTE
TASK_HOUR
TASK_IMMEDIATE
TASK_FOREVER
TASK_ONCE
TASK_NOTIMEOUT
*/
int noOfTasks = 10;
QueueArray <Task*> toDelete;
void setup() {
// put your setup code here, to run once:
#if defined(_DEBUG_) || defined(_TEST_)
Serial.begin(115200);
delay(2000);
_PL("Scheduler Example: Expose Scheduler Task Chain"); _PL();
#endif
_PL("Generating a random chain of tasks");
for (int i = 0; i < noOfTasks; i++) {
long p = random(100, 5001); // from 100 ms to 5 seconds
long j = random(1, 11); // from 1 to 10 iterations)
Task *t = new Task(p, j, &taskCallback, &ts, false);
_PP(F("Generated a new task:\t")); _PP(t->getId()); _PP(F("\tInt, Iter = \t"));
_PP(p); Serial.print(", "); _PL(j);
if ( random(1, 100) > 50 ) {
t->enable();
}
else {
t->enableDelayed();
}
toDelete.push(t);
}
_PL();
_PL("Printing the entire chain");
Task* f = ts.getFirstTask();
Task* l = ts.getLastTask();
while (f) {
_PP("Task #"); _PL(f->getId());
f = f->getNextTask();
}
_PL();
tManager.enableDelayed();
}
void loop() {
ts.execute();
}
void taskCallback() {
_PP(millis());
_PP(": taskCallback() of task #");
Task* t = ts.getCurrentTask();
t->disable();
_PL(t->getId());
Task* l = ts.getLastTask();
while (t) {
_PP("#"); _PP(t->getId());
if (t->getNextTask() != NULL) _PP("->");
t = t->getNextTask();
}
_PL(); _PL();
}
void cleanUp() {
_PL("Deleting tasks:");
do {
Task* t = toDelete.pop();
_PP("Deleting task #"); _PL(t->getId());
delete t;
} while (!toDelete.isEmpty());
for (;;) ;
}

View File

@@ -0,0 +1,181 @@
/*
Example of scheduling options:
t1 - is a default option with priority given to schedule, i.e., scheduler tries
maintain original schedule and performs task "catch up" to ensure the number
of iterations that were supposed to happen do happen.
t2 - is an option with priority given to schedule, but without "catch up"
the scheduler will try to maintain original schedule, but next task invocation
is always scheduled to happen in the future
t3 - is a option with priority given to interval. Task are scheduled always in the
future from the point of their current invocation start using task's interval.
*/
// ==== DEFINES ===================================================================================
// ==== Debug and Test options ==================
#define _DEBUG_
//#define _TEST_
//===== Debugging macros ========================
#ifdef _DEBUG_
#define SerialD Serial
#define _PM(a) SerialD.print(millis()); SerialD.print(": "); SerialD.println(a)
#define _PP(a) SerialD.print(a)
#define _PL(a) SerialD.println(a)
#define _PX(a) SerialD.println(a, HEX)
#else
#define _PM(a)
#define _PP(a)
#define _PL(a)
#define _PX(a)
#endif
// ==== INCLUDES ==================================================================================
// ==== Uncomment desired compile options =================================
// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns
// #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass
// #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer
// #define _TASK_PRIORITY // Support for layered scheduling priority
// #define _TASK_MICRO_RES // Support for microsecond resolution
// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY)
// #define _TASK_DEBUG // Make all methods and variables public for debug purposes
// #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations
// #define _TASK_TIMEOUT // Support for overall task timeout
// #define _TASK_OO_CALLBACKS // Support for dynamic callback method binding
// #define _TASK_DEFINE_MILLIS // Force forward declaration of millis() and micros() "C" style
// #define _TASK_EXPOSE_CHAIN // Methods to access tasks in the task chain
#define _TASK_SCHEDULING_OPTIONS // Support for multiple scheduling options
#include <TaskScheduler.h>
// ==== GLOBALS ===================================================================================
// ==== Scheduler ==============================
Scheduler ts;
void t1CB();
void t2CB();
void t3CB();
// ==== Scheduling defines (cheat sheet) =====================
/*
TASK_MILLISECOND
TASK_SECOND
TASK_MINUTE
TASK_HOUR
TASK_IMMEDIATE
TASK_FOREVER
TASK_ONCE
TASK_NOTIMEOUT
TASK_SCHEDULE - schedule is a priority, with "catch up" (default)
TASK_SCHEDULE_NC - schedule is a priority, without "catch up"
TASK_INTERVAL - interval is a priority, without "catch up"
*/
// ==== Task definitions ========================
Task t1_schedule (100 * TASK_MILLISECOND, 10, &t1CB, &ts);
Task t2_schedule_nc (100 * TASK_MILLISECOND, 10, &t2CB, &ts);
Task t3_interval (100 * TASK_MILLISECOND, 10, &t3CB, &ts);
// ==== CODE ======================================================================================
/**************************************************************************/
/*!
@brief Standard Arduino SETUP method - initialize sketch
@param none
@returns none
*/
/**************************************************************************/
void setup() {
// put your setup code here, to run once:
#if defined(_DEBUG_) || defined(_TEST_)
Serial.begin(115200);
delay(1000);
_PL("Scheduling Options: setup()");
#endif
t2_schedule_nc.setSchedulingOption(TASK_SCHEDULE_NC);
t3_interval.setSchedulingOption(TASK_INTERVAL);
_PM("t1 start time");
t1_schedule.enable();
delay(10);
_PM("t2 start time");
t2_schedule_nc.enable();
delay(10);
_PM("t3 start time");
t3_interval.enable();
delay(333);
_PM("333 ms delay ended");
}
/**************************************************************************/
/*!
@brief Standard Arduino LOOP method - using with TaskScheduler there
should be nothing here but ts.execute()
@param none
@returns none
*/
/**************************************************************************/
void loop() {
ts.execute();
}
/**************************************************************************/
/*!
@brief Callback method of task1 - explain
@param none
@returns none
*/
/**************************************************************************/
void t1CB() {
_PM("t1CB()");
// task code
delay(10);
}
/**************************************************************************/
/*!
@brief Callback method of task2 - explain
@param none
@returns none
*/
/**************************************************************************/
void t2CB() {
_PM("t2CB()");
// task code
delay(10);
}
/**************************************************************************/
/*!
@brief Callback method of task3 - explain
@param none
@returns none
*/
/**************************************************************************/
void t3CB() {
_PM("t3CB()");
// task code
delay(10);
}

View File

@@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View File

@@ -0,0 +1,7 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
]
}

View File

@@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

View File

@@ -0,0 +1,12 @@
#ifndef _TS27_LED_H
#define _TS27_LED_H
// LED_BUILTIN 13
#if defined( ARDUINO_ARCH_ESP32 )
#define LED_BUILTIN 23 // esp32 dev2 kit does not have LED
#endif
void LEDOff();
void LEDOn();
#endif // _TS27_LED_H

View File

@@ -0,0 +1,27 @@
#ifndef _TS27_MAIN_H
#define _TS27_MAIN_H
#include <Arduino.h>
#ifdef _DEBUG_
#define _PP(a) Serial.print(a);
#define _PL(a) Serial.println(a);
#else
#define _PP(a)
#define _PL(a)
#endif
#define PERIOD1 500
#define DURATION 10000
#define PERIOD2 400
#define PERIOD3 300
#define PERIOD4 200
#define PERIOD5 600
#define PERIOD6 300
#endif

View File

@@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

View File

@@ -0,0 +1,38 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
[env]
lib_deps =
arkhipenko/TaskScheduler @ ^3.4.0
build_flags =
; -D _TASK_TIMECRITICAL
-D _TASK_SLEEP_ON_IDLE_RUN
-D _TASK_STATUS_REQUEST
; -D _TASK_WDT_IDS
; -D _TASK_LTS_POINTER
; -D _TASK_PRIORITY
; -D _TASK_MICRO_RES
; -D _TASK_STD_FUNCTION
; -D _TASK_DEBUG
; -D _TASK_INLINE
; -D _TASK_TIMEOUT
; -D _TASK_OO_CALLBACKS
; -D _TASK_EXPOSE_CHAIN
; -D _TASK_SCHEDULING_OPTIONS
; -D _TASK_DEFINE_MILLIS
; -D _TASK_EXTERNAL_TIME
-D _DEBUG_
; -D _TEST_

View File

@@ -0,0 +1,11 @@
#include <Arduino.h>
#include "led.h"
#include "main.h"
void LEDOn() {
digitalWrite( LED_BUILTIN, HIGH );
}
void LEDOff() {
digitalWrite( LED_BUILTIN, LOW );
}

View File

@@ -0,0 +1,254 @@
/*
Every example set must have a LED blink example
For this one the idea is to have as many ways to blink the LED
as I can think of. So, here we go.
Tested on:
- Arduino Nano
- ESP8266
- ESP32
- STM32 Maple Mini
*/
#include <TaskSchedulerDeclarations.h>
#include "main.h"
#include "led.h"
// Scheduler
Scheduler ts;
/*
Approach 1: LED is driven by the boolean variable; false = OFF, true = ON
*/
void blink1CB();
Task tBlink1 ( PERIOD1 * TASK_MILLISECOND, DURATION / PERIOD1, &blink1CB, &ts, true );
/*
Approach 2: two callback methods: one turns ON, another turns OFF
*/
void blink2CB_ON();
void blink2CB_OFF();
Task tBlink2 ( PERIOD2 * TASK_MILLISECOND, DURATION / PERIOD2, &blink2CB_ON, &ts, false );
/*
Approach 3: Use RunCounter
*/
void blink3CB();
Task tBlink3 (PERIOD3 * TASK_MILLISECOND, DURATION / PERIOD3, &blink3CB, &ts, false);
/*
Approach 4: Use status request objects to pass control from one task to the other
*/
bool blink41OE();
void blink41();
void blink42();
void blink42OD();
Task tBlink4On ( PERIOD4 * TASK_MILLISECOND, TASK_ONCE, blink41, &ts, false, &blink41OE );
Task tBlink4Off ( PERIOD4 * TASK_MILLISECOND, TASK_ONCE, blink42, &ts, false, NULL, &blink42OD );
/*
Approach 5: Two interleaving tasks
*/
bool blink51OE();
void blink51();
void blink52();
void blink52OD();
Task tBlink5On ( 600 * TASK_MILLISECOND, DURATION / PERIOD5, &blink51, &ts, false, &blink51OE );
Task tBlink5Off ( 600 * TASK_MILLISECOND, DURATION / PERIOD5, &blink52, &ts, false, NULL, &blink52OD );
/*
Approach 6: RunCounter-based with random intervals
*/
void blink6CB();
bool blink6OE();
void blink6OD();
Task tBlink6 ( PERIOD6 * TASK_MILLISECOND, DURATION / PERIOD6, &blink6CB, &ts, false, &blink6OE, &blink6OD );
void setup() {
// put your setup code here, to run once:
#if defined(_DEBUG_) || defined(_TEST_)
Serial.begin(115200);
delay(TASK_SECOND);
_PL("TaskScheduler Blink example");
_PL("Blinking for 10 seconds using various techniques\n");
delay(2 * TASK_SECOND);
#endif
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
ts.execute();
}
// === 1 =======================================
bool LED_state = false;
void blink1CB() {
if ( tBlink1.isFirstIteration() ) {
_PP(millis());
_PL(": Blink1 - simple flag driven");
LED_state = false;
}
if ( LED_state ) {
LEDOff();
LED_state = false;
}
else {
LEDOn();
LED_state = true;
}
if ( tBlink1.isLastIteration() ) {
tBlink2.restartDelayed( 2 * TASK_SECOND );
LEDOff();
}
}
// === 2 ======================================
void blink2CB_ON() {
if ( tBlink2.isFirstIteration() ) {
_PP(millis());
_PL(": Blink2 - 2 callback methods");
}
LEDOn();
tBlink2.setCallback( &blink2CB_OFF );
if ( tBlink2.isLastIteration() ) {
tBlink3.restartDelayed( 2 * TASK_SECOND );
LEDOff();
}
}
void blink2CB_OFF() {
LEDOff();
tBlink2.setCallback( &blink2CB_ON );
if ( tBlink2.isLastIteration() ) {
tBlink3.restartDelayed( 2 * TASK_SECOND );
LEDOff();
}
}
// === 3 =====================================
void blink3CB() {
if ( tBlink3.isFirstIteration() ) {
_PP(millis());
_PL(": Blink3 - Run Counter driven");
}
if ( tBlink3.getRunCounter() & 1 ) {
LEDOn();
}
else {
LEDOff();
}
if ( tBlink3.isLastIteration() ) {
tBlink4On.setOnEnable( &blink41OE );
tBlink4On.restartDelayed( 2 * TASK_SECOND );
LEDOff();
}
}
// === 4 =============================================
int counter = 0;
bool blink41OE() {
_PP(millis());
_PL(": Blink4 - Internal status request based");
counter = 0;
tBlink4On.setOnEnable( NULL );
return true;
}
void blink41() {
// _PP(millis());
// _PL(": blink41");
LEDOn();
StatusRequest* r = tBlink4On.getInternalStatusRequest();
tBlink4Off.waitForDelayed( r );
counter++;
}
void blink42() {
// _PP(millis());
// _PL(": blink42");
LEDOff();
StatusRequest* r = tBlink4Off.getInternalStatusRequest();
tBlink4On.waitForDelayed( r );
counter++;
}
void blink42OD() {
if ( counter >= DURATION / PERIOD4 ) {
tBlink4On.disable();
tBlink4Off.disable();
tBlink5On.setOnEnable( &blink51OE );
tBlink5On.restartDelayed( 2 * TASK_SECOND );
tBlink5Off.restartDelayed( 2 * TASK_SECOND + PERIOD5 / 2 );
LEDOff();
}
}
// === 5 ==========================================
bool blink51OE() {
_PP(millis());
_PL(": Blink5 - Two interleaving tasks");
tBlink5On.setOnEnable( NULL );
return true;
}
void blink51() {
// _PP(millis());
// _PL(": blink51");
LEDOn();
}
void blink52() {
// _PP(millis());
// _PL(": blink52");
LEDOff();
}
void blink52OD() {
tBlink6.restartDelayed( 2 * TASK_SECOND );
LEDOff();
}
// === 6 ============================================
long interval6 = 0;
bool blink6OE() {
_PP(millis());
_PP(": Blink6 - RunCounter + Random ON interval = ");
interval6 = random( 100, 901 );
tBlink6.setInterval( interval6 );
_PL( interval6 );
tBlink6.delay( 2 * TASK_SECOND );
return true;
}
void blink6CB() {
if ( tBlink6.getRunCounter() & 1 ) {
LEDOn();
tBlink6.setInterval( interval6 );
}
else {
LEDOff();
tBlink6.setInterval( TASK_SECOND - interval6 );
}
}
void blink6OD() {
tBlink1.restartDelayed( 2 * TASK_SECOND );
LEDOff();
}

View File

@@ -0,0 +1,11 @@
This directory is intended for PlatformIO Unit Testing and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/page/plus/unit-testing.html

View File

@@ -0,0 +1,148 @@
/*
Your code description here
(c) you, 20XX
All rights reserved.
Functionality:
Version log:
20XX-MM-DD:
v1.0.0 - Initial release
*/
// ==== DEFINES ===================================================================================
// ==== Debug and Test options ==================
#define _DEBUG_
//#define _TEST_
//===== Debugging macros ========================
#ifdef _DEBUG_
#define SerialD Serial
#define _PM(a) SerialD.print(millis()); SerialD.print(": "); SerialD.println(a)
#define _PP(a) SerialD.print(a)
#define _PL(a) SerialD.println(a)
#define _PX(a) SerialD.println(a, HEX)
#else
#define _PM(a)
#define _PP(a)
#define _PL(a)
#define _PX(a)
#endif
// ==== INCLUDES ==================================================================================
// ==== Uncomment desired compile options =================================
// #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass
// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns
// #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer
// #define _TASK_PRIORITY // Support for layered scheduling priority
// #define _TASK_MICRO_RES // Support for microsecond resolution
// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY)
// #define _TASK_DEBUG // Make all methods and variables public for debug purposes
// #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations
// #define _TASK_TIMEOUT // Support for overall task timeout
// #define _TASK_OO_CALLBACKS // Support for dynamic callback method binding
// #define _TASK_DEFINE_MILLIS // Force forward declaration of millis() and micros() "C" style
// #define _TASK_EXPOSE_CHAIN // Methods to access tasks in the task chain
// #define _TASK_SCHEDULING_OPTIONS // Support for multiple scheduling options
#include <TaskScheduler.h>
// ==== GLOBALS ===================================================================================
// ==== Scheduler ==============================
Scheduler ts;
void task1Callback();
void task2Callback();
// ==== Scheduling defines (cheat sheet) =====================
/*
TASK_MILLISECOND
TASK_SECOND
TASK_MINUTE
TASK_HOUR
TASK_IMMEDIATE
TASK_FOREVER
TASK_ONCE
TASK_NOTIMEOUT
TASK_SCHEDULE - schedule is a priority, with "catch up" (default)
TASK_SCHEDULE_NC - schedule is a priority, without "catch up"
TASK_INTERVAL - interval is a priority, without "catch up"
*/
// ==== Task definitions ========================
Task t1 (100 * TASK_MILLISECOND, TASK_FOREVER, &task1Callback, &ts, true);
Task t2 (TASK_IMMEDIATE, 100, &task2Callback, &ts, true);
// ==== CODE ======================================================================================
/**************************************************************************/
/*!
@brief Standard Arduino SETUP method - initialize sketch
@param none
@returns none
*/
/**************************************************************************/
void setup() {
// put your setup code here, to run once:
#if defined(_DEBUG_) || defined(_TEST_)
Serial.begin(115200);
delay(2000);
_PL("Scheduler Template: setup()");
#endif
}
/**************************************************************************/
/*!
@brief Standard Arduino LOOP method - using with TaskScheduler there
should be nothing here but ts.execute()
@param none
@returns none
*/
/**************************************************************************/
void loop() {
ts.execute();
}
/**************************************************************************/
/*!
@brief Callback method of task1 - explain
@param none
@returns none
*/
/**************************************************************************/
void task1Callback() {
_PM("task1Callback()");
// task code
}
/**************************************************************************/
/*!
@brief Callback method of task2 - explain
@param none
@returns none
*/
/**************************************************************************/
void task2Callback() {
_PM("task2Callback()");
// task code
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -0,0 +1,3 @@
As of May 2020 all offline documents are deprecated.
For the latest documentation of TaskScheduler please visit:
https://github.com/arkhipenko/TaskScheduler/wiki

View File

@@ -0,0 +1,140 @@
#######################################
# Syntax Coloring Map For TaskManager
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
Scheduler KEYWORD1
StatusRequest KEYWORD1
Task KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
abort KEYWORD2
addTask KEYWORD2
adjust KEYWORD2
allowSleep KEYWORD2
Callback KEYWORD2
cancel KEYWORD2
canceled KEYWORD2
completed KEYWORD2
cpuLoadReset KEYWORD2
currentLts KEYWORD2
delay KEYWORD2
deleteTask KEYWORD2
disable KEYWORD2
disableAll KEYWORD2
disableOnLastIteration KEYWORD2
enable KEYWORD2
enableAll KEYWORD2
enableDelayed KEYWORD2
enableIfNot KEYWORD2
execute KEYWORD2
forceNextIteration KEYWORD2
getControlPoint KEYWORD2
getCount KEYWORD2
getCount KEYWORD2
getCpuLoadCycle KEYWORD2
getCpuLoadIdle KEYWORD2
getCpuLoadTotal KEYWORD2
getCurrentTask KEYWORD2
getFirstTask KEYWORD2
getId KEYWORD2
getInternalStatusRequest KEYWORD2
getInterval KEYWORD2
getIterations KEYWORD2
getLastTask KEYWORD2
getLtsPointer KEYWORD2
getNextTask KEYWORD2
getOverrun KEYWORD2
getPreviousTask KEYWORD2
getRunCounter KEYWORD2
getSchedulingOption KEYWORD2
getStartDelay KEYWORD2
getStatus KEYWORD2
getStatusRequest KEYWORD2
getTimeout KEYWORD2
init KEYWORD2
isEnabled KEYWORD2
isFirstIteration KEYWORD2
isLastIteration KEYWORD2
isOverrun KEYWORD2
OnDisable KEYWORD2
OnEnable KEYWORD2
pause KEYWORD2
pending KEYWORD2
resetTimeout KEYWORD2
restart KEYWORD2
restartDelayed KEYWORD2
resume KEYWORD2
set KEYWORD2
setCallback KEYWORD2
setControlPoint KEYWORD2
setHighPriorityScheduler KEYWORD2
setId KEYWORD2
setInterval KEYWORD2
setIterations KEYWORD2
setLtsPointer KEYWORD2
setOnDisable KEYWORD2
setOnEnable KEYWORD2
setSchedulingOption KEYWORD2
setSleepMethod KEYWORD2
setTimeout KEYWORD2
setWaiting KEYWORD2
signal KEYWORD2
signalComplete KEYWORD2
startNow KEYWORD2
timedOut KEYWORD2
timeUntilNextIteration KEYWORD2
untilTimeout KEYWORD2
waitFor KEYWORD2
waitForDelayed KEYWORD2
yield KEYWORD2
yieldOnce KEYWORD2
#######################################
# Constants (LITERAL1)
_TASK_DEBUG LITERAL1
_TASK_INLINE LITERAL1
_TASK_LTS_POINTER LITERAL1
_TASK_MICRO_RES LITERAL1
_TASK_OO_CALLBACKS LITERAL1
_TASK_PRIORITY LITERAL1
_TASK_SLEEP_ON_IDLE_RUN LITERAL1
_TASK_STATUS_REQUEST LITERAL1
_TASK_STD_FUNCTION LITERAL1
_TASK_TIMECRITICAL LITERAL1
_TASK_TIMEOUT LITERAL1
_TASK_WDT_IDS LITERAL1
_TASK_EXPOSE_CHAIN LITERAL1
_TASK_DEFINE_MILLIS LITERAL1
_TASK_SCHEDULING_OPTIONS LITERAL1
_TASK_DEFINE_MILLIS LITERAL1
_TASK_EXTERNAL_TIME LITERAL1
_TASK_THREAD_SAFE LITERAL1
SleepCallback LITERAL1
TASK_FOREVER LITERAL1
TASK_HOUR LITERAL1
TASK_IMMEDIATE LITERAL1
TASK_MILLISECOND LITERAL1
TASK_MINUTE LITERAL1
TASK_NOTIMEOUT LITERAL1
TASK_ONCE LITERAL1
TASK_SECOND LITERAL1
TaskCallback LITERAL1
TaskOnDisable LITERAL1
TaskOnEnable LITERAL1
TASK_SCHEDULE LITERAL1
TASK_SCHEDULE_NC LITERAL1
TASK_INTERVAL LITERAL1
TASK_SR_OK LITERAL1
TASK_SR_ERROR LITERAL1
TASK_SR_TIMEOUT LITERAL1
#######################################

View File

@@ -0,0 +1,22 @@
{
"name": "TaskScheduler",
"keywords": "multitasking, cooperative, event, task, taskscheduler, scheduling",
"description": "Cooperative multitasking for Arduino, ESPx, STM32 and other microcontrollers",
"repository":
{
"type": "git",
"url": "https://github.com/arkhipenko/TaskScheduler"
},
"authors":
[
{
"name": "Anatoli Arkhipenko",
"email": "arkhipenko@hotmail.com",
"url": "https://github.com/arkhipenko",
"maintainer": true
}
],
"version": "3.6.0",
"frameworks": "arduino",
"platforms": "*"
}

View File

@@ -0,0 +1,9 @@
name=TaskScheduler
version=3.6.0
author=Anatoli Arkhipenko <arkhipenko@hotmail.com>
maintainer=Anatoli Arkhipenko <arkhipenko@hotmail.com>
sentence=Cooperative multitasking for Arduino, ESPx, STM32 and other microcontrollers.
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 (esp8266, esp32 only), overall task timeout, static and dynamic callback method binding.
category=Timing
url=https://github.com/arkhipenko/TaskScheduler.git
architectures=*

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,393 @@
// Cooperative multitasking library for Arduino
// Copyright (c) 2015-2019 Anatoli Arkhipenko
#include <stddef.h>
#include <stdint.h>
#ifndef _TASKSCHEDULERDECLARATIONS_H_
#define _TASKSCHEDULERDECLARATIONS_H_
// ----------------------------------------
// The following "defines" control library functionality at compile time,
// and should be used in the main sketch depending on the functionality required
//
// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns
// #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between runs if no callback methods were invoked during the pass
// #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer
// #define _TASK_PRIORITY // Support for layered scheduling priority
// #define _TASK_MICRO_RES // Support for microsecond resolution
// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY)
// #define _TASK_DEBUG // Make all methods and variables public for debug purposes
// #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations
// #define _TASK_TIMEOUT // Support for overall task timeout
// #define _TASK_OO_CALLBACKS // Support for callbacks via inheritance
// #define _TASK_EXPOSE_CHAIN // Methods to access tasks in the task chain
// #define _TASK_SCHEDULING_OPTIONS // Support for multiple scheduling options
// #define _TASK_DEFINE_MILLIS // Force forward declaration of millis() and micros() "C" style
// #define _TASK_EXTERNAL_TIME // Custom millis() and micros() methods
// #define _TASK_THREAD_SAFE // Enable additional checking for thread safety
class Scheduler;
#define TASK_SCHEDULE 0 // default
#define TASK_SCHEDULE_NC 1 // schedule + no catch-ups (always in the future)
#define TASK_INTERVAL 2 // interval (always in the future)
#ifdef _TASK_DEBUG
#define _TASK_SCOPE public
#else
#define _TASK_SCOPE private
#endif
#define TASK_IMMEDIATE 0
#define TASK_FOREVER (-1)
#define TASK_ONCE 1
#ifdef _TASK_TIMEOUT
#define TASK_NOTIMEOUT 0
#endif
#ifdef _TASK_PRIORITY
extern Scheduler* iCurrentScheduler;
#endif // _TASK_PRIORITY
#ifdef _TASK_INLINE
#define INLINE inline
#else
#define INLINE
#endif
#ifdef _TASK_EXTERNAL_TIME
#define _task_millis() external_millis()
#define _task_micros() external_micros()
#endif // _TASK_EXTERNAL_TIME
#ifndef _TASK_MICRO_RES
#define TASK_MILLISECOND 1UL
#define TASK_SECOND 1000UL
#define TASK_MINUTE 60000UL
#define TASK_HOUR 3600000UL
#else
#define TASK_MILLISECOND 1000UL
#define TASK_SECOND 1000000UL
#define TASK_MINUTE 60000000UL
#define TASK_HOUR 3600000000UL
#endif // _TASK_MICRO_RES
#ifdef _TASK_STATUS_REQUEST
#define TASK_SR_OK 0
#define TASK_SR_ERROR (-1)
#define TASK_SR_TIMEOUT (-99)
#define _TASK_SR_NODELAY 1
#define _TASK_SR_DELAY 2
class Scheduler;
class StatusRequest {
friend class Scheduler;
public:
INLINE StatusRequest();
INLINE void setWaiting(unsigned int aCount = 1);
INLINE bool signal(int aStatus = 0);
INLINE void signalComplete(int aStatus = 0);
INLINE bool pending();
INLINE bool completed();
INLINE int getStatus();
INLINE int getCount();
#ifdef _TASK_TIMEOUT
INLINE void setTimeout(unsigned long aTimeout) { iTimeout = aTimeout; };
INLINE unsigned long getTimeout() { return iTimeout; };
INLINE void resetTimeout();
INLINE long untilTimeout();
#endif
_TASK_SCOPE:
unsigned int iCount; // number of statuses to wait for. waiting for more that 65000 events seems unreasonable: unsigned int should be sufficient
int iStatus; // status of the last completed request. negative = error; zero = OK; positive = OK with a specific status
#ifdef _TASK_TIMEOUT
unsigned long iTimeout; // Task overall timeout
unsigned long iStarttime; // millis at task start time
#endif // _TASK_TIMEOUT
};
#endif // _TASK_STATUS_REQUEST
#ifdef _TASK_STD_FUNCTION
#include <functional>
typedef std::function<void()> TaskCallback;
typedef std::function<void()> TaskOnDisable;
typedef std::function<bool()> TaskOnEnable;
#else
typedef void (*TaskCallback)();
typedef void (*TaskOnDisable)();
typedef bool (*TaskOnEnable)();
#endif // _TASK_STD_FUNCTION
#ifdef _TASK_SLEEP_ON_IDLE_RUN
typedef void (*SleepCallback)( unsigned long aDuration );
extern Scheduler* iSleepScheduler;
extern SleepCallback iSleepMethod;
#endif // _TASK_SLEEP_ON_IDLE_RUN
typedef struct {
bool enabled : 1; // indicates that task is enabled or not.
bool inonenable : 1; // indicates that task execution is inside OnEnable method (preventing infinite loops)
bool canceled : 1; // indication that tast has been canceled prior to normal end of all iterations or regular call to disable()
#ifdef _TASK_STATUS_REQUEST
uint8_t waiting : 2; // indication if task is waiting on the status request
#endif
#ifdef _TASK_TIMEOUT
bool timeout : 1; // indication if task timed out
#endif
} __task_status;
class Task {
friend class Scheduler;
public:
#ifdef _TASK_OO_CALLBACKS
INLINE Task(unsigned long aInterval=0, long aIterations=0, Scheduler* aScheduler=NULL, bool aEnable=false);
#else
INLINE Task(unsigned long aInterval=0, long aIterations=0, TaskCallback aCallback=NULL, Scheduler* aScheduler=NULL, bool aEnable=false, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL);
#endif // _TASK_OO_CALLBACKS
#ifdef _TASK_STATUS_REQUEST
#ifdef _TASK_OO_CALLBACKS
// INLINE Task(Scheduler* aScheduler=NULL);
INLINE Task(Scheduler* aScheduler);
#else
// INLINE Task(TaskCallback aCallback=NULL, Scheduler* aScheduler=NULL, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL);
INLINE Task(TaskCallback aCallback, Scheduler* aScheduler, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL);
#endif // _TASK_OO_CALLBACKS
#endif // _TASK_STATUS_REQUEST
virtual INLINE ~Task();
#ifdef _TASK_TIMEOUT
INLINE void setTimeout(unsigned long aTimeout, bool aReset=false);
INLINE void resetTimeout();
INLINE unsigned long getTimeout();
INLINE long untilTimeout();
INLINE bool timedOut();
#endif
INLINE bool enable();
INLINE bool enableIfNot();
INLINE bool enableDelayed(unsigned long aDelay=0);
INLINE bool restart();
INLINE bool restartDelayed(unsigned long aDelay=0);
INLINE void delay(unsigned long aDelay=0);
INLINE void adjust(long aInterval);
INLINE void forceNextIteration();
INLINE bool disable();
INLINE void abort();
INLINE void cancel();
INLINE bool isEnabled();
INLINE bool canceled();
#ifdef _TASK_SCHEDULING_OPTIONS
INLINE unsigned int getSchedulingOption() { return iOption; }
INLINE void setSchedulingOption(unsigned int aOption) { iOption = aOption; }
#endif //_TASK_SCHEDULING_OPTIONS
#ifdef _TASK_OO_CALLBACKS
INLINE void set(unsigned long aInterval, long aIterations);
#else
INLINE void set(unsigned long aInterval, long aIterations, TaskCallback aCallback,TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL);
#endif // _TASK_OO_CALLBACKS
INLINE void setInterval(unsigned long aInterval);
INLINE unsigned long getInterval();
INLINE void setIterations(long aIterations);
INLINE long getIterations();
INLINE unsigned long getRunCounter() ;
#ifdef _TASK_OO_CALLBACKS
virtual INLINE bool Callback() =0; // return true if run was "productive - this will disable sleep on the idle run for next pass
virtual INLINE bool OnEnable(); // return true if task should be enabled, false if it should remain disabled
virtual INLINE void OnDisable();
#else
INLINE void setCallback(TaskCallback aCallback) ;
INLINE void setOnEnable(TaskOnEnable aCallback) ;
INLINE void setOnDisable(TaskOnDisable aCallback) ;
INLINE void yield(TaskCallback aCallback);
INLINE void yieldOnce(TaskCallback aCallback);
#endif // _TASK_OO_CALLBACKS
INLINE bool isFirstIteration() ;
INLINE bool isLastIteration() ;
#ifdef _TASK_TIMECRITICAL
INLINE long getOverrun() ;
INLINE long getStartDelay() ;
#endif // _TASK_TIMECRITICAL
#ifdef _TASK_STATUS_REQUEST
INLINE bool waitFor(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1);
INLINE bool waitForDelayed(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1);
INLINE StatusRequest* getStatusRequest() ;
INLINE StatusRequest* getInternalStatusRequest() ;
#endif // _TASK_STATUS_REQUEST
#ifdef _TASK_WDT_IDS
INLINE void setId(unsigned int aID) ;
INLINE unsigned int getId() ;
INLINE void setControlPoint(unsigned int aPoint) ;
INLINE unsigned int getControlPoint() ;
#endif // _TASK_WDT_IDS
#ifdef _TASK_LTS_POINTER
INLINE void setLtsPointer(void *aPtr) ;
INLINE void* getLtsPointer() ;
#endif // _TASK_LTS_POINTER
#ifdef _TASK_EXPOSE_CHAIN
INLINE Task* getPreviousTask() { return iPrev; }; // pointer to the previous task in the chain, NULL if first or not set
INLINE Task* getNextTask() { return iNext; }; // pointer to the next task in the chain, NULL if last or not set
#endif // _TASK_EXPOSE_CHAIN
_TASK_SCOPE:
INLINE void reset();
volatile __task_status iStatus;
volatile unsigned long iInterval; // execution interval in milliseconds (or microseconds). 0 - immediate
volatile unsigned long iDelay; // actual delay until next execution (usually equal iInterval)
volatile unsigned long iPreviousMillis; // previous invocation time (millis). Next invocation = iPreviousMillis + iInterval. Delayed tasks will "catch up"
#ifdef _TASK_SCHEDULING_OPTIONS
unsigned int iOption; // scheduling option
#endif // _TASK_SCHEDULING_OPTIONS
#ifdef _TASK_TIMECRITICAL
volatile long iOverrun; // negative if task is "catching up" to it's schedule (next invocation time is already in the past)
volatile long iStartDelay; // actual execution of the task's callback method was delayed by this number of millis
#endif // _TASK_TIMECRITICAL
volatile long iIterations; // number of iterations left. 0 - last iteration. -1 - infinite iterations
long iSetIterations; // number of iterations originally requested (for restarts)
unsigned long iRunCounter; // current number of iteration (starting with 1). Resets on enable.
#ifndef _TASK_OO_CALLBACKS
TaskCallback iCallback; // pointer to the void callback method
TaskOnEnable iOnEnable; // pointer to the bool OnEnable callback method
TaskOnDisable iOnDisable; // pointer to the void OnDisable method
#endif // _TASK_OO_CALLBACKS
Task *iPrev, *iNext; // pointers to the previous and next tasks in the chain
Scheduler *iScheduler; // pointer to the current scheduler
#ifdef _TASK_STATUS_REQUEST
StatusRequest *iStatusRequest; // pointer to the status request task is or was waiting on
StatusRequest iMyStatusRequest; // internal Status request to let other tasks know of completion
#endif // _TASK_STATUS_REQUEST
#ifdef _TASK_WDT_IDS
unsigned int iTaskID; // task ID (for debugging and watchdog identification)
unsigned int iControlPoint; // current control point within the callback method. Reset to 0 by scheduler at the beginning of each pass
#endif // _TASK_WDT_IDS
#ifdef _TASK_LTS_POINTER
void *iLTS; // pointer to task's local storage. Needs to be recast to appropriate type (usually a struct).
#endif // _TASK_LTS_POINTER
#ifdef _TASK_TIMEOUT
unsigned long iTimeout; // Task overall timeout
unsigned long iStarttime; // millis at task start time
#endif // _TASK_TIMEOUT
#ifdef _TASK_THREAD_SAFE
volatile uint8_t iMutex; // a mutex to pause scheduling during chages to the task
#endif
};
class Scheduler {
friend class Task;
public:
INLINE Scheduler();
// ~Scheduler();
INLINE void init();
INLINE void addTask(Task& aTask);
INLINE void deleteTask(Task& aTask);
INLINE void pause() { iPaused = true; };
INLINE void resume() { iPaused = false; };
INLINE void enable() { iEnabled = true; };
INLINE void disable() { iEnabled = false; };
#ifdef _TASK_PRIORITY
INLINE void disableAll(bool aRecursive = true);
INLINE void enableAll(bool aRecursive = true);
INLINE void startNow(bool aRecursive = true); // reset ALL active tasks to immediate execution NOW.
#else
INLINE void disableAll();
INLINE void enableAll();
INLINE void startNow(); // reset ALL active tasks to immediate execution NOW.
#endif
INLINE bool execute(); // Returns true if none of the tasks' callback methods was invoked (true = idle run)
INLINE Task& currentTask() ; // DEPRICATED
INLINE Task* getCurrentTask() ; // Returns pointer to the currently active task
INLINE long timeUntilNextIteration(Task& aTask); // return number of ms until next iteration of a given Task
#ifdef _TASK_SLEEP_ON_IDLE_RUN
INLINE void allowSleep(bool aState = true);
INLINE void setSleepMethod( SleepCallback aCallback );
#endif // _TASK_SLEEP_ON_IDLE_RUN
#ifdef _TASK_LTS_POINTER
INLINE void* currentLts();
#endif // _TASK_LTS_POINTER
#ifdef _TASK_TIMECRITICAL
INLINE bool isOverrun();
INLINE void cpuLoadReset();
INLINE unsigned long getCpuLoadCycle(){ return iCPUCycle; };
INLINE unsigned long getCpuLoadIdle() { return iCPUIdle; };
INLINE unsigned long getCpuLoadTotal();
#endif // _TASK_TIMECRITICAL
#ifdef _TASK_PRIORITY
INLINE void setHighPriorityScheduler(Scheduler* aScheduler);
INLINE static Scheduler& currentScheduler() { return *(iCurrentScheduler); };
#endif // _TASK_PRIORITY
#ifdef _TASK_EXPOSE_CHAIN
INLINE Task* getFirstTask() { return iFirst; }; // pointer to the previous task in the chain, NULL if first or not set
INLINE Task* getLastTask() { return iLast; }; // pointer to the next task in the chain, NULL if last or not set
#endif // _TASK_EXPOSE_CHAIN
_TASK_SCOPE:
Task *iFirst, *iLast, *iCurrent; // pointers to first, last and current tasks in the chain
volatile bool iPaused, iEnabled;
#ifdef _TASK_SLEEP_ON_IDLE_RUN
bool iAllowSleep; // indication if putting MC to IDLE_SLEEP mode is allowed by the program at this time.
#endif // _TASK_SLEEP_ON_IDLE_RUN
#ifdef _TASK_PRIORITY
Scheduler* iHighPriority; // Pointer to a higher priority scheduler
#endif // _TASK_PRIORITY
#ifdef _TASK_TIMECRITICAL
unsigned long iCPUStart;
unsigned long iCPUCycle;
unsigned long iCPUIdle;
#endif // _TASK_TIMECRITICAL
};
#endif /* _TASKSCHEDULERDECLARATIONS_H_ */

View File

@@ -0,0 +1,30 @@
// Cooperative multitasking library for Arduino
// Copyright (c) 2015-2019 Anatoli Arkhipenko
#ifndef _TASKSCHEDULERSLEEPMETHODS_H_
#define _TASKSCHEDULERSLEEPMETHODS_H_
#if defined( ARDUINO_ARCH_AVR ) // Could be used only for AVR-based boards.
#include <avr/sleep.h>
#include <avr/power.h>
void SleepMethod( unsigned long aDuration ) {
set_sleep_mode(SLEEP_MODE_IDLE);
sleep_enable();
/* Now enter sleep mode. */
sleep_mode();
/* The program will continue from here after the timer timeout ~1 ms */
sleep_disable(); /* First thing to do is disable sleep. */
}
// ARDUINO_ARCH_AVR
#else
void SleepMethod( unsigned long aDuration ) {
}
#endif // SLEEP METHODS
#endif // _TASKSCHEDULERSLEEPMETHODS_H_