初始化提交

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,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
}