Arduino Multithreading error

Hello Guys,

I have been using a Arduino Uno, and I have some problem with Multithreading.

It’s my first time using Arduino

Could you help me please :slight_smile:

My code as bellow:

#include <EasyScheduler.h>

int valvula_0 = 19; //CLM_25
int valvula_1 = 0; //PFH
int valvula_2 = 1; //GSM_GIRO
int valvula_3 = 2; //GSM_GRIPPER
int valvula_4 = 3; //SRU GRIPPER
int valvula_5 = 4; //SRU
int valvula_6 = 5; //SRU
int valvula_7 = 6; //SRU
int microvalvula = 7; //MICROVALVULA
int valvula_8 = 8; //SRH
int valvula_9 = 9; //SRH GRIPPER PGN
int valvula_10 = 10; //SRH GRIPPER PZN
int valvula_11 = 11; //DPG
int valvula_12 = 12;
int valvula_13 = 13;

Schedular Task1;
Schedular Task2;
Schedular Task3;
Schedular Task4;
Schedular Task5;
Schedular Task6;
Schedular Task7;

void setup() {

pinMode(valvula_0, OUTPUT);
pinMode(valvula_1, OUTPUT);
pinMode(valvula_2, OUTPUT);
pinMode(valvula_3, OUTPUT);
pinMode(valvula_4, OUTPUT);
pinMode(valvula_5, OUTPUT);
pinMode(valvula_6, OUTPUT);
pinMode(valvula_7, OUTPUT);
pinMode(valvula_8, OUTPUT);
pinMode(valvula_9, OUTPUT);
pinMode(valvula_10, OUTPUT);
pinMode(valvula_11, OUTPUT);
pinMode(valvula_12, OUTPUT);
pinMode(valvula_13, OUTPUT);
pinMode(microvalvula, OUTPUT);

Task1.start();
Task2.start();
Task3.start();
Task4.start();
Task5.start();
Task6.start();
Task7.start();
}

void loop() {

Task1.check(SRU,3000);
Task2.check(microvalve,1500);
Task3.check(CLM,3000);
Task4.check(PFH,3000);
Task5.check(GSM_R,3000);
Task6.check(SRH,3000);
Task7.check(DPG,3000);
}

//*************************************************************************************

void SRU()

{
digitalWrite(valvula_6, HIGH);//0
digitalWrite(valvula_5, LOW);//0
delay(3000);//0

for(int a = 0; a < 3; a++){
digitalWrite(valvula_4, HIGH);//gripper
delay(1500);
digitalWrite(valvula_4, LOW);//gripper
delay(1500);
}

digitalWrite(valvula_7, HIGH);//180
digitalWrite(valvula_5, LOW);//180
delay(3000);//180

for(int a = 0; a < 3; a++){
digitalWrite(valvula_4, HIGH);//gripper
delay(1500);
digitalWrite(valvula_4, LOW);//gripper
delay(1500);
}

digitalWrite(valvula_6, HIGH);//0
digitalWrite(valvula_5, LOW);//0
delay(3000);//0

for(int a = 0; a < 3; a++){
digitalWrite(valvula_4, HIGH);//gripper
delay(1500);
digitalWrite(valvula_4, LOW);//gripper
delay(1500);
}

digitalWrite(valvula_6, LOW);//90
digitalWrite(valvula_5, HIGH);//90
delay(3000);//90

for(int a = 0; a < 3; a++){
digitalWrite(valvula_4, HIGH);//gripper
delay(1500);
digitalWrite(valvula_4, LOW);//gripper
delay(1500);
}

digitalWrite(valvula_7, HIGH);//180
digitalWrite(valvula_5, LOW);//180
delay(3000);//180

for(int a = 0; a < 3; a++){
digitalWrite(valvula_4, HIGH);//gripper
delay(1500);
digitalWrite(valvula_4, LOW);//gripper
delay(1500);
}

digitalWrite(valvula_6, LOW);//90
digitalWrite(valvula_5, HIGH);//90
delay(3000);//90

for(int a = 0; a < 3; a++){
digitalWrite(valvula_4, HIGH);//gripper
delay(1500);
digitalWrite(valvula_4, LOW);//gripper
delay(1500);
}

digitalWrite(valvula_6, HIGH);//0
digitalWrite(valvula_5, LOW);//0
delay(3000);//0

for(int a = 0; a < 3; a++){
digitalWrite(valvula_4, HIGH);//gripper
delay(1500);
digitalWrite(valvula_4, LOW);//gripper
delay(1500);
}

digitalWrite(valvula_6, LOW);//90
digitalWrite(valvula_5, HIGH);//90
delay(3000);//90

for(int a = 0; a < 3; a++){
digitalWrite(valvula_4, HIGH);//gripper
delay(1500);
digitalWrite(valvula_4, LOW);//gripper
delay(1500);
}

digitalWrite(valvula_6, HIGH);//0
digitalWrite(valvula_5, LOW);//0
delay(3000);//0

for(int a = 0; a < 3; a++){
digitalWrite(valvula_4, HIGH);//gripper
delay(1500);
digitalWrite(valvula_4, LOW);//gripper
delay(1500);
}

digitalWrite(valvula_7, HIGH);//180
digitalWrite(valvula_5, LOW);//180
delay(3000);//180

for(int a = 0; a < 3; a++){
digitalWrite(valvula_4, HIGH);//gripper
delay(1500);
digitalWrite(valvula_4, LOW);//gripper
delay(1500);
}

digitalWrite(valvula_6, LOW);//90
digitalWrite(valvula_5, HIGH);//90
delay(3000);//90

for(int a = 0; a < 3; a++){
digitalWrite(valvula_4, HIGH);//gripper
delay(1500);
digitalWrite(valvula_4, LOW);//gripper
delay(1500);
}

digitalWrite(valvula_7, HIGH);//180
digitalWrite(valvula_5, LOW);//180
delay(3000);//180

for(int a = 0; a < 3; a++){
digitalWrite(valvula_4, HIGH);//gripper
delay(1500);
digitalWrite(valvula_4, LOW);//gripper
delay(1500);
}
}

//*************************************************************************************

void microvalve()
{
digitalWrite(microvalvula, HIGH);
delay(10);
digitalWrite(microvalvula, LOW);
}

//*************************************************************************************

void CLM()
{
digitalWrite(valvula_0, HIGH);
delay(10);
digitalWrite(valvula_0, LOW);
}

//*************************************************************************************

void PFH()
{
digitalWrite(valvula_1, HIGH);
delay(10);
digitalWrite(valvula_1, LOW);
}

//*************************************************************************************

void GSM_R()
{
digitalWrite(valvula_2, HIGH);

delay(3000);

for(int b = 0; b < 3; b++){
digitalWrite(valvula_3, HIGH);//gripper
delay(1500);
digitalWrite(valvula_3, LOW);//gripper
delay(1500);
}

digitalWrite(valvula_2, LOW);

delay(3000);

for(int b = 0; b < 3; b++){
digitalWrite(valvula_3, HIGH);//gripper
delay(1500);
digitalWrite(valvula_3, LOW);//gripper
delay(1500);
}
}

//************************************************************************************

void SRH()
{
digitalWrite(valvula_8, HIGH);

delay(3000);

for(int c = 0; c < 3; c++){
digitalWrite(valvula_9, HIGH);//gripper
delay(1500);
digitalWrite(valvula_9, LOW);//gripper
delay(1500);
}

digitalWrite(valvula_8, LOW);

delay(3000);

for(int c = 0; c < 3; c++){
digitalWrite(valvula_10, HIGH);//gripper
delay(1500);
digitalWrite(valvula_10, LOW);//gripper
delay(1500);
}
}

//*************************************************************************************

void DPG()
{
digitalWrite(valvula_11, HIGH);
delay(10);
digitalWrite(valvula_11, LOW);
}

//*************************************************************************************

Since Arduino processor only has one core, there’s no such thing as multithreading. All of those delay calls are going to cause one process to hog the available processor time until that process is done.

If you want multiple things to appear to happen at the same time, then you need non-blocking code. That definitely means no delay calls.

Check out the “Blink Without Delay” example that comes with the IDE for an example of how to do that.

Hello Delta_G

Thanks for you attention.

I'm new on Arduino.

If i change the Delay () for example the code as bellow coul be work ?

if(currentMillis - previousMillis > interval) {
previousMillis = currentMillis;

if (ledState == LOW)
ledState = HIGH;
else
ledState = LOW;

digitalWrite(ledPin, ledState);
}
}

Well, it's not a simple drop in replacement. It's a different way of thinking about how to handle the timing.

Could you give some example please ?

It's difficult for my first time on arduino world :slight_smile:

I will use your example and make by myself :slight_smile:

Can you edit your first post and add the code tags you forgot?

Hi,

Can you please post a copy of your sketch, using code tags?
Please use code tags.. See section 7 http://forum.arduino.cc/index.php/topic,148850.0.html

Tom...... :slight_smile:

juliodv:
Could you give some example please ?

It's difficult for my first time on arduino world :slight_smile:

I will use your example and make by myself :slight_smile:

Check Response #1` for examples. They are also found in the examples section, under learning at the top of this page.

Try those.

Delta_G:
Since Arduino processor only has one core, there's no such thing as multithreading. All of those delay calls are going to cause one process to hog the available processor time until that process is done.

That is likely not true. I'm not familiar with this particular task switcher, but several of the ones I have seen will use a delay() call to do a task switch, and put the current task to sleep until the delay period has expired. So, using delay()will NOT block other tasks. A task switcher without such ability would be a very limited value.

Regards,
Ray L.

If you just use millis() to manage timing as illustrated in several things at a time there is no library to cause confusion. If something does not work the cause of the problem will be right in front of you.

There will be no time wasted trying to figure out how a library works.

...R

Hi,
What programming experience do you have?
This will help us to explain to you how to modify your sketch.

Tom....... :slight_smile:

RayLivingston:
That is likely not true. I'm not familiar with this particular task switcher, but several of the ones I have seen will use a delay() call to do a task switch, and put the current task to sleep until the delay period has expired. So, using delay()will NOT block other tasks. A task switcher without such ability would be a very limited value.

Regards,
Ray L.

Oh, but it will. Let's look at just the first one:

(Who moved the damn code tags up there? )

Task1.check(SRU,3000);

So call this SRU function every 3000 milliseconds. Let's see how long the SRU function takes to run though.

digitalWrite(valvula_6, HIGH);//0
  digitalWrite(valvula_5, LOW);//0
  delay(3000);//0     << 3000ms
  
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500); 
          }  //   3000 ms * 3 is 9000ms so  we're at 12000

  digitalWrite(valvula_7, HIGH);//180
  digitalWrite(valvula_5, LOW);//180
  delay(3000);//180    //  Now 15000
  
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500); 
          }   //  Now 24000
          
  digitalWrite(valvula_6, HIGH);//0
  digitalWrite(valvula_5, LOW);//0
  delay(3000);//0   //   THis makes 27000
  
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500); 
          }   ///  Now we're at 36000

  digitalWrite(valvula_6, LOW);//90
  digitalWrite(valvula_5, HIGH);//90
  delay(3000);//90   // 39000
  
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500); 
          }   //  48000
  
  digitalWrite(valvula_7, HIGH);//180
  digitalWrite(valvula_5, LOW);//180
  delay(3000);//180   // 51000
  
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500); 
          }  //60000
          
  digitalWrite(valvula_6, LOW);//90
  digitalWrite(valvula_5, HIGH);//90
  delay(3000);//90  // 63000
  
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500); 
          } /// 72000
          
  digitalWrite(valvula_6, HIGH);//0
  digitalWrite(valvula_5, LOW);//0
  delay(3000);//0   //75000
  
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500); 
          } // 84000

  digitalWrite(valvula_6, LOW);//90
  digitalWrite(valvula_5, HIGH);//90
  delay(3000);//90 // 87000
  
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500); 
          } // 96000
          
  digitalWrite(valvula_6, HIGH);//0
  digitalWrite(valvula_5, LOW);//0
  delay(3000);//0 // 99000
  
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500); 
          }         108000

  digitalWrite(valvula_7, HIGH);//180
  digitalWrite(valvula_5, LOW);//180
  delay(3000);//180  110000
  
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500); 
          } // 120000
          
  digitalWrite(valvula_6, LOW);//90
  digitalWrite(valvula_5, HIGH);//90
  delay(3000);//90  // 123000
  
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500); 
          }   // 132000
          
  digitalWrite(valvula_7, HIGH);//180
  digitalWrite(valvula_5, LOW);//180
  delay(3000);//180  //135000
  
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500); 
          }          //  And this bring us up to 144000 ms just counting the delays.
}

So just counting the delays and nothing else that's 144000ms to run this one function.

And he want's this function to be called every 3000 ms. So this function will interrupt itself 48 times before it can finish the first iteration of itself.

That's 48 calls to this one function sitting on the stack in recursive interruptions.

Add to that the other functions with the same problem and the issue gets even worse.

Delta_G:
Oh, but it will. Let’s look at just the first one:

(Who moved the damn code tags up there? )

Task1.check(SRU,3000);

So call this SRU function every 3000 milliseconds. Let’s see how long the SRU function takes to run though.

digitalWrite(valvula_6, HIGH);//0

digitalWrite(valvula_5, LOW);//0
  delay(3000);//0    << 3000ms
 
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500);
          }  //  3000 ms * 3 is 9000ms so  we’re at 12000

digitalWrite(valvula_7, HIGH);//180
  digitalWrite(valvula_5, LOW);//180
  delay(3000);//180    //  Now 15000
 
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500);
          }  //  Now 24000
         
  digitalWrite(valvula_6, HIGH);//0
  digitalWrite(valvula_5, LOW);//0
  delay(3000);//0  //  THis makes 27000
 
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500);
          }  ///  Now we’re at 36000

digitalWrite(valvula_6, LOW);//90
  digitalWrite(valvula_5, HIGH);//90
  delay(3000);//90  // 39000
 
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500);
          }  //  48000
 
  digitalWrite(valvula_7, HIGH);//180
  digitalWrite(valvula_5, LOW);//180
  delay(3000);//180  // 51000
 
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500);
          }  //60000
         
  digitalWrite(valvula_6, LOW);//90
  digitalWrite(valvula_5, HIGH);//90
  delay(3000);//90  // 63000
 
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500);
          } /// 72000
         
  digitalWrite(valvula_6, HIGH);//0
  digitalWrite(valvula_5, LOW);//0
  delay(3000);//0  //75000
 
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500);
          } // 84000

digitalWrite(valvula_6, LOW);//90
  digitalWrite(valvula_5, HIGH);//90
  delay(3000);//90 // 87000
 
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500);
          } // 96000
         
  digitalWrite(valvula_6, HIGH);//0
  digitalWrite(valvula_5, LOW);//0
  delay(3000);//0 // 99000
 
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500);
          }        108000

digitalWrite(valvula_7, HIGH);//180
  digitalWrite(valvula_5, LOW);//180
  delay(3000);//180  110000
 
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500);
          } // 120000
         
  digitalWrite(valvula_6, LOW);//90
  digitalWrite(valvula_5, HIGH);//90
  delay(3000);//90  // 123000
 
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500);
          }  // 132000
         
  digitalWrite(valvula_7, HIGH);//180
  digitalWrite(valvula_5, LOW);//180
  delay(3000);//180  //135000
 
    for(int a = 0; a < 3; a++){
                digitalWrite(valvula_4, HIGH);//gripper
                delay(1500);
                digitalWrite(valvula_4, LOW);//gripper
                delay(1500);
          }          //  And this bring us up to 144000 ms just counting the delays.
}





So just counting the delays and nothing else that's 144000ms to run this one function.

And he want's this function to be called every 3000 ms. So this function will interrupt itself 48 times before it can finish the first iteration of itself. 

That's 48 calls to this one function sitting on the stack in recursive interruptions. 

Add to that the other functions with the same problem and the issue gets even worse.

Again, I think you are making assumptions that are may well not be valid. You have to look at EXACTLY how the specific task switcher being used here operates. Very likely, the delays() in each task have essentially ZERO impact on execution of other threads, or their own threads. Any call to delay() will likely cause a task switch, with the calling task put to sleep until the specified delay has passed. IF that is not the case, then it is a very poorly written task switched indeed. Scheduling a task to run at some interval that is shorter than the runtime of that task will NOT necessarily, and really SHOULD NOT, cause the task to recurse. In fact, I’d say the task switcher is a useless, very badly written POS if it DOES attempt to do that. More likely, the behavior is: A task is invoked. Unless and until that task completes, it will NOT be invoked again. So, no matter how long that task takes to run and no matter how short the interval specified for it to run, it will run to completion each time it is invoked, without recursing, and the interval will be re-started ONLY after the task has completed. So in this case, the SRU task will be invoked roughly 3 seconds after the program starts running. It will take roughly 144000ms to run to completion. 3 seconds AFTER that, it will be invoked for the second time. Lather, rinse, repeat. Again, without looking at the internals of EasyScheduler, we can’t know for sure what the behavior is, but I’d bet that what I’ve described is what most task switchers would do.

Despite what seems to be “conventional wisdon” here, a lightweight task-switcher, properly used, can be a very useful tool for managing complex tasks, even on small processors. It allows each task to become what nearly amounts to a separate, stand-alone program. But, this comes at a cost - some increased overhead (very little with a well-written switcher), and the potential for some really nasty bugs when it becomes necessary for tasks to communicate or coordinate with each other. The “Blink without delay” model is fine for simple applications, but is hardly a universal solution to all problems. It becomes quite quite cumbersome and difficult to manage for more complex applications. There is also a broad range of other potential solutions between the two extremes.

Regards,
Ray L.

You can hypothesize all you want. I have the code. What do you think will happen?

#include <easyscheduler.h>
#include <Arduino.h>

Schedular::Schedular()
{
  lastRun = 0;
  timesRun = 0;
}

// Check if task needs to run 
void Schedular::check(TaskFunctionCallback MethodeToCall,int Interval)
{
  if(lastRun > 0)
  {
    if(lastRun+Interval < millis() && (timesRun < timesToRun || timesToRun == 0))
    {
      MethodeToCall();
      lastRun = millis();
	  timesRun++;
    }
	
	
  };
}

// Start as soon as the the function fires
void Schedular::start()
{
  lastRun = millis()+1;
}

// Delays start by 'delayStartBy' milli seconds
void Schedular::start(int delayStartBy)
{
  lastRun = millis()+delayStartBy;
}

// Stops Task
void Schedular::stop()
{
  lastRun = 0;
  timesRun = 0;
}
typedef void  (*TaskFunctionCallback)  ();

class Schedular 
{
  private:
    unsigned long lastRun;
    int timesRun;	
  public:
    // public methods
    Schedular();
    void start();
    void start(int delayStartBy); 
    void stop();                     
    void check(TaskFunctionCallback MethodeToCall,int Interval);
	void pause();
	int timesToRun;
	
};

Delays in the loop wouldn't be any problem. Delays in the code being called will. Arduino has no way to split the function up and call one line from this function and then one line from that function and switch back and forth. At some point, code has to execute.

RayLivingston:
Again, I think you are making assumptions that are may well not be valid. You have to look at EXACTLY how the specific task switcher being used here operates.

Good advice. And if you follow it you'll find the EasyScheduler used here is nothing more than a thinly disguised "Blink without delay".

Delta_G:
You can hypothesize all you want. I have the code. What do you think will happen?

#include <easyscheduler.h>

#include <Arduino.h>

Schedular::Schedular()
{
 lastRun = 0;
 timesRun = 0;
}

// Check if task needs to run
void Schedular::check(TaskFunctionCallback MethodeToCall,int Interval)
{
 if(lastRun > 0)
 {
   if(lastRun+Interval < millis() && (timesRun < timesToRun || timesToRun == 0))
   {
     MethodeToCall();
     lastRun = millis();
 timesRun++;
   }

};
}

// Start as soon as the the function fires
void Schedular::start()
{
 lastRun = millis()+1;
}

// Delays start by 'delayStartBy' milli seconds
void Schedular::start(int delayStartBy)
{
 lastRun = millis()+delayStartBy;
}

// Stops Task
void Schedular::stop()
{
 lastRun = 0;
 timesRun = 0;
}

THAT is one completely useless piece of code that should be sent straight to the "round file". However, the "tasks" (and they are anything but....) will NOT ever recurse. They will run sequentially. 1.5 seconds after the program is launched, Task2 will run, and it will run to completion, delays and all, no matter how long it takes, without anything else happening. Once it completes, its anybody's guess which one will run next, and then all remaining ones will run, one after the other, in no easily determined order.

A completely useless POS. Silly me, thinking something called "Scheduler" would actually be doing, you know, some kind of useful scheduling....

Regards,
Ray L.

It would be useful if the code it was scheduling wasn't blocking. If all it did was turn an LED on and off, then it would be a decent way to hide the BWoD paradigm.

The problem is that there's no way on the Arduino to interleave two blocking functions. If you think there is then show us some code.