Arduino Minimal Task Scheduler Example?

Thread Starter

Adamelli

Joined Sep 14, 2020
66
Scheduler_example00_Blink is ~300 lines. For just an Arduino Uno, what is a minimal example?

It's hard to know what belongs and what doesn't. This example has six different approaches, and I'm just trying to extract one of them.

This website allows compilation and run of the program: wokwi.com/playground/task-scheduler
(I tried deleting sections of the program to reduce it down to one method, but get different errors each time.)
 

djsfantasi

Joined Apr 11, 2010
9,163
The Scheduler_example_blink is a lot of code to digest... Is there a member here who has used it?

Plus, if you “get different errors” each time, don’t you think it would be helpful if you told us what they were? My crystal ball is in the shop.

If we knew what you changed and the errors (in detail) that occurred, perhaps we’d have a chance to help.
 

Thread Starter

Adamelli

Joined Sep 14, 2020
66
Plus, if you “get different errors” each time, don’t you think it would be helpful if you told us what they were?
Variables. It seems sections are somehow dependent on each other.
I'm not sure if each "approach" corresponds to "// === # ==========================================" for each number.

The link has the full program, so you can see errors or "Build failed!" when taking out sections 2-6 or just 6 and various combinations. I could list all the "Build failed!" and errors for each specific case, but I don't think that'd be useful.
 

Thread Starter

Adamelli

Joined Sep 14, 2020
66
Here is one error. I took out approaches 2-6, and what I assume to be the corresponding sections below void loop, I took out 2-6:
Code:
'tBlink2' was not declared in this scope
suggested alternative: 'tBlink1'
This is a variable in approach 2. So I put that approach back in, and it says "Build failed!" I can run it through the real Arduino IDE, but I think I did that earlier today, and it's just a cascade of errors until most of the original program is back... which defeats the whole purpose of minimizing it. I just want to get one method, alone, to work. This way I can use one of the methods in a separate program.
 

Ya’akov

Joined Jan 27, 2019
9,150
Here is one error. I took out approaches 2-6, and what I assume to be the corresponding sections below void loop, I took out 2-6:
Code:
'tBlink2' was not declared in this scope
suggested alternative: 'tBlink1'
This is a variable in approach 2. So I put that approach back in, and it says "Build failed!" I can run it through the real Arduino IDE, but I think I did that earlier today, and it's just a cascade of errors until most of the original program is back... which defeats the whole purpose of minimizing it. I just want to get one method, alone, to work. This way I can use one of the methods in a separate program.
“Cargo cult” programming is a very bad approach. Copy-pasting bits of code that “work” is sure to make the program you are writing unstable. Instead of blindly cutting away code, you really need to dig into it and understand how and why it works.

I can’t tell you how much unstable spaghetti code I’ve had to deal with, some of it written by me!

There is a principle when commenting code: don’t write comments about what code is doing, the code tells you that, comment on why you wrote the code that way. This is called being kind to the maintenance programmer, who, a very large percentage of the time, will be you!

In the same vein, cargo cult coding means that future you will be unable to untangle the progress that past you spent a considerable time sawing, filing, gluing, and taping together from parts you didn’t understand even then.

Work through the code example and when you can separately use one of the methods without (much) mystery, add it to your program, comment it, and enjoy the splendor of dong The Right Thing®.

[EDIT] For those not familiar with the "cargo cult" and how it relates to programming, Wikipedia has a pretty good write-up: https://en.wikipedia.org/wiki/Cargo_cult_programming
 
Last edited:

djsfantasi

Joined Apr 11, 2010
9,163
Here is one error. I took out approaches 2-6, and what I assume to be the corresponding sections below void loop, I took out 2-6:
Code:
'tBlink2' was not declared in this scope
suggested alternative: 'tBlink1'
This is a variable in approach 2. So I put that approach back in, and it says "Build failed!" I can run it through the real Arduino IDE, but I think I did that earlier today, and it's just a cascade of errors until most of the original program is back... which defeats the whole purpose of minimizing it. I just want to get one method, alone, to work. This way I can use one of the methods in a separate program.
Excellent article from @Yaakov I must admit I was gobsmacked by your response.

I don’t want to underestimate you. Can you describe your programming background?

I feel that you don’t understand what this example is doing. And that is leading you to incorrect assumptions.

You cannot just snip out a piece of code from a larger program without understanding each line of code and what all of the variables purpose, definition and scope. That leads to the kind of errors you’ve posted. And, without the details I requested and with a 300 line sketch, there is no way to address the issues in a meaningful way. That’s why I’ve been silent for so long.

So, the source of all your errors is an inability to understand what the code is, it’s structure and “Data, data, data”. The later is a personal quote based on my belief that “The data is the code”.
 

Thread Starter

Adamelli

Joined Sep 14, 2020
66
It's nice that you're trying to help, but has anyone looked at the program? (It's much easier to say general things rather than get into the dirty details of the program itself.) Also, I'm not doing cargo cult programming. I'm trying to get a minimal example to work from the multi-example program. That way, I would understand how to integrate the Task Scheduler library into a different program (to replace delay).

It looks and the error messages suggest that each approach is linked together. However, if I comment out the linked part, undefined reference errors result:

C++:
// === 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;
  }
// Commented out linked part, where tBlink2 is for approach 2:
//  if ( tBlink1.isLastIteration() ) {
//    tBlink2.restartDelayed( 2 * TASK_SECOND );
//    LEDOff();
//  }
}
Errors:

Code:
C:\Users\user\AppData\Local\Temp\cc39Gvct.ltrans0.ltrans.o: In function `global constructors keyed to 65535_0_21_3_23_taskScheduler.ino.cpp.o.1918':
<artificial>:(.text.startup+0xc0): undefined reference to `blink2CB_ON()'
<artificial>:(.text.startup+0xc4): undefined reference to `blink2CB_ON()'
<artificial>:(.text.startup+0xe0): undefined reference to `blink3CB()'
<artificial>:(.text.startup+0xe4): undefined reference to `blink3CB()'
<artificial>:(.text.startup+0x100): undefined reference to `blink41OE()'
<artificial>:(.text.startup+0x104): undefined reference to `blink41OE()'
<artificial>:(.text.startup+0x108): undefined reference to `blink41()'
<artificial>:(.text.startup+0x10c): undefined reference to `blink41()'
<artificial>:(.text.startup+0x128): undefined reference to `blink42OD()'
<artificial>:(.text.startup+0x12c): undefined reference to `blink42OD()'
<artificial>:(.text.startup+0x134): undefined reference to `blink42()'
<artificial>:(.text.startup+0x138): undefined reference to `blink42()'
<artificial>:(.text.startup+0x158): undefined reference to `blink51OE()'
<artificial>:(.text.startup+0x15c): undefined reference to `blink51OE()'
<artificial>:(.text.startup+0x160): undefined reference to `blink51()'
<artificial>:(.text.startup+0x164): undefined reference to `blink51()'
<artificial>:(.text.startup+0x180): undefined reference to `blink52OD()'
<artificial>:(.text.startup+0x184): undefined reference to `blink52OD()'
<artificial>:(.text.startup+0x18c): undefined reference to `blink52()'
<artificial>:(.text.startup+0x190): undefined reference to `blink52()'
<artificial>:(.text.startup+0x1ac): undefined reference to `blink6OD()'
<artificial>:(.text.startup+0x1b0): undefined reference to `blink6OD()'
<artificial>:(.text.startup+0x1b4): undefined reference to `blink6OE()'
<artificial>:(.text.startup+0x1b8): undefined reference to `blink6OE()'
<artificial>:(.text.startup+0x1bc): undefined reference to `blink6CB()'
<artificial>:(.text.startup+0x1c0): undefined reference to `blink6CB()'
collect2.exe: error: ld returned 1 exit status
exit status 1
Error compiling for board Arduino Uno.
 

Thread Starter

Adamelli

Joined Sep 14, 2020
66
"Work through the code example and when you can separately use one of the methods"--that's part of the problem. That's what I've been trying to do. Each approach is linked to the next. I don't know how to get just one method/approach to work. That is the question I'm asking.
 

Ya’akov

Joined Jan 27, 2019
9,150
It's nice that you're trying to help, but has anyone looked at the program? (It's much easier to say general things rather than get into the dirty details of the program itself.) Also, I'm not doing cargo cult programming. I'm trying to get a minimal example to work from the multi-example program. That way, I would understand how to integrate the Task Scheduler library into a different program (to replace delay).

It looks and the error messages suggest that each approach is linked together. However, if I comment out the linked part, undefined reference errors result:

C++:
// === 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;
  }
// Commented out linked part, where tBlink2 is for approach 2:
//  if ( tBlink1.isLastIteration() ) {
//    tBlink2.restartDelayed( 2 * TASK_SECOND );
//    LEDOff();
//  }
}
Errors:

Code:
C:\Users\user\AppData\Local\Temp\cc39Gvct.ltrans0.ltrans.o: In function `global constructors keyed to 65535_0_21_3_23_taskScheduler.ino.cpp.o.1918':
<artificial>:(.text.startup+0xc0): undefined reference to `blink2CB_ON()'
<artificial>:(.text.startup+0xc4): undefined reference to `blink2CB_ON()'
<artificial>:(.text.startup+0xe0): undefined reference to `blink3CB()'
<artificial>:(.text.startup+0xe4): undefined reference to `blink3CB()'
<artificial>:(.text.startup+0x100): undefined reference to `blink41OE()'
<artificial>:(.text.startup+0x104): undefined reference to `blink41OE()'
<artificial>:(.text.startup+0x108): undefined reference to `blink41()'
<artificial>:(.text.startup+0x10c): undefined reference to `blink41()'
<artificial>:(.text.startup+0x128): undefined reference to `blink42OD()'
<artificial>:(.text.startup+0x12c): undefined reference to `blink42OD()'
<artificial>:(.text.startup+0x134): undefined reference to `blink42()'
<artificial>:(.text.startup+0x138): undefined reference to `blink42()'
<artificial>:(.text.startup+0x158): undefined reference to `blink51OE()'
<artificial>:(.text.startup+0x15c): undefined reference to `blink51OE()'
<artificial>:(.text.startup+0x160): undefined reference to `blink51()'
<artificial>:(.text.startup+0x164): undefined reference to `blink51()'
<artificial>:(.text.startup+0x180): undefined reference to `blink52OD()'
<artificial>:(.text.startup+0x184): undefined reference to `blink52OD()'
<artificial>:(.text.startup+0x18c): undefined reference to `blink52()'
<artificial>:(.text.startup+0x190): undefined reference to `blink52()'
<artificial>:(.text.startup+0x1ac): undefined reference to `blink6OD()'
<artificial>:(.text.startup+0x1b0): undefined reference to `blink6OD()'
<artificial>:(.text.startup+0x1b4): undefined reference to `blink6OE()'
<artificial>:(.text.startup+0x1b8): undefined reference to `blink6OE()'
<artificial>:(.text.startup+0x1bc): undefined reference to `blink6CB()'
<artificial>:(.text.startup+0x1c0): undefined reference to `blink6CB()'
collect2.exe: error: ld returned 1 exit status
exit status 1
Error compiling for board Arduino Uno.
Yes, I looked that the program.
 

Thread Starter

Adamelli

Joined Sep 14, 2020
66
Ok, I figured it out. For some reason, errors resulted from using the program from the wokwi site. If the example program is used from the library, and all the other "approaches" are commented out--including the last if-statement in the approach you want to use--and the declarations, then it compiles.

Example for approach 1:

C++:
/*
   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();
//}
Thanks all for trying to help.
 
Last edited:
Top