How to do schedule tasks using Cyclic Executive?

Hello, I am looking for some ideas on how to design a system that executes several periodic tasks, with specified timings using a Cyclic Executive.
to keep it simple, let's say I have 3 tasks and would like to execute them at the following rates:

  • task 1 - 2HZ (i.e. execute task 1 every 0.5 seconds)
  • task 2 - 5HZ
  • task 3 - 6HZ

Any help will be appreciated. Thanks!

millis() already known?

yes

Run a timer at 30Hz. Every 5 periods, run the 6Hz task. Every 6 periods, run the 5Hz task. Every 15 periods, run the 2Hz task.

You can also try generalising it all.

3 Likes

If using a ESP32 the built in OS, freeRTOS, will easily handle such timings and task switching.

1 Like

thanks for your answer. I simplified my problem to just two tasks:

  • task 1 - turn on the red LED for 1 second - execute every 4 seconds
  • task 2 - turn on green LED for 1 second - execute every 10 seconds
    and I wrote this code:
/*
task 1 - turn on red LED - execute every 4 seconds
task 2 - turn on green LED - execute every 10 seconds
*/
const long eventTime_Red = 4000;    //execute every 10 seconds
const long eventTime_Green = 10000; //execute every 10 seconds

unsigned long previousTime_Red = 0;
unsigned long previousTime_Green = 0;

const byte LED_Red=15;    //LED red connected to pin 15
const byte LED_Green=21;  //LED green connected to pin 21

void setup() {
  Serial.begin(9600);

  pinMode(LED_Red, OUTPUT);  //configure pin 15 to behave as an output
  pinMode(LED_Green, OUTPUT); //configure pin 21 to behave as an output

  digitalWrite(LED_Red, LOW);  //have the red LED initially off
  digitalWrite(LED_Green, LOW); //have the green LED initially off
}

void loop() {
unsigned long presentTime = millis();

//for task 1
if (presentTime-previousTime_Red >= eventTime_Red){
  while (presentTime
  digitalWrite(LED_Red, HIGH);
  
  delay(1000); //keep Red on for 1 sec
  Serial.print(presentTime/1000);
  Serial.println("Red ON");
  digitalWrite(LED_Red, LOW); 
  previousTime_Red = presentTime;
}

//for task 2
if (presentTime-previousTime_Green >= eventTime_Green){
  digitalWrite(LED_Green, HIGH);
  delay(1000); //keep Red on for 1 sec
    Serial.print(presentTime/1000);
  Serial.println("Green ON");
  digitalWrite(LED_Green, LOW); 
  previousTime_Green = presentTime;
}
}

is this sort of what you meant?..obviously with different numbers.

And it works the way I want but there is just one problem: since I am using delay() to control how long LEDs are on, other instructions are blocked. This is problematic during instances where the red led and the green led must turn on simultaneously. I know I probably have to use millis() to control the delays but I don't know how.

Yes, I am using ESP32, but I was hoping to use Cyclic Executive to bypass the coding of the real-time system. I've never used freeRTOS before, but I think I won't get the chance to exhibit Cyclic Executive coding with it...

freeRTOs is the ESP32's built in OS and Cyclic Executive code will run on top of the OS.

Actually, I think I figured it out. So I first defined how long each LED will be on for, e.g. for the red LED:

const long red_period = 1000; //keep red LED on for 1 second

Then I nested an if statement inside the if statement for each task, so for task 1:

//for task 1
  if (presentTime - previousTime_Red >= eventTime_Red) {

    if (presentTime >= (previousTime_Red + red_period)) {
      //keep Red on for 1 sec
      digitalWrite(LED_Red, HIGH);
      Serial.print(presentTime / 1000);
      Serial.println("Red ON");
    }
    digitalWrite(LED_Red, LOW);
    previousTime_Red = presentTime;
  }

Now the problem is that the LEDs flash so fast I can't even see them. The timings look accurate though when I analysed them on the serial monitor...so I'm confused .... I'll be back with an update

I'm sure you'll get there.

It is nice, incidentally, to switch the led on only once and off only once per cycle.
Especially so if you write a message to the serial console on each switch because this would otherwise get clogged up with all the messages.

Something like (pseudo code):

if ( <led is off> && <led should be on> ) switch led ON ;
if ( <led is on> && <led should be off> ) switch led OFF ;

Have you read this page?

no but looks useful. I'll definitely read it. cheers!

you need a state variable to store states of each led. if red goes on for 1 second or 100, the other timer must not wait for end of this time.

I don't know how storing the state lets the other timer know? I thought using millis() would let me do things simultaneously.. :face_with_diagonal_mouth:

Hello cinnamonroll
Take view to the sketch below. The sketch is using the BWOD example of the IDE as mother of all Arduino timers with an added function caller for the timed scheduled functions calls.

/* BLOCK COMMENT
  ATTENTION: This Sketch contains elements of C++.
  https://www.learncpp.com/cpp-tutorial/
  Many thanks to LarryD
  https://europe1.discourse-cdn.com/arduino/original/4X/7/e/0/7e0ee1e51f1df32e30893550c85f0dd33244fb0e.jpeg
  https://forum.arduino.cc/t/how-to-do-schedule-tasks-using-cyclic-executive/962598
*/
#define ProjectName "How to do schedule tasks using Cyclic Executive?"
// HARDWARE AND TIMER SETTINGS
// YOU MAY NEED TO CHANGE THESE CONSTANTS TO YOUR HARDWARE AND NEEDS
constexpr byte LedPins[] {9, 10, 11};    // portPin o-------[LED]-----|220ohm|---GND
// CONSTANT DEFINITION
enum {One, Two, Three};
// VARIABLE DECLARATION AND DEFINITION
unsigned long currentTime;
//_____ information block for task timer
void task1();
void task2();
void task3();
struct TASKTIMER {              // has the following members
  void (*task)();               // task name
  unsigned long duration;       // memory for interval time
  unsigned long stamp;          // memory for actual time
} taskTimers [] {
  {task1, 500, 0 }, // task1 @ 2Hz
  {task2, 200, 0 }, // task2 @ 5Hz
  {task3, 167, 0 }, // task2 @ 6Hz
};
//______________ scheduled task ____________
void task1() {
  Serial.println(__func__);
  toggleLed(LedPins[One]);
}
void task2() {
  Serial.println(__func__);
  toggleLed(LedPins[Two]);
}
void task3() {
  Serial.println(__func__);
  toggleLed(LedPins[Two]);
}
//__________________________________________

void  toggleLed(int pin) {
  digitalWrite(pin, !digitalRead(pin));
}
// -------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  Serial.println(F("."));
  Serial.print(F("File   : ")), Serial.println(__FILE__);
  Serial.print(F("Date   : ")), Serial.println(__DATE__);
  Serial.print(F("Project: ")), Serial.println(ProjectName);
  pinMode (LED_BUILTIN, OUTPUT);  // used as heartbeat indicator
  //  https://www.learncpp.com/cpp-tutorial/for-each-loops/
  for (auto Output_ : LedPins) pinMode(Output_, OUTPUT);
}
void loop () {
  currentTime = millis();
  digitalWrite(LED_BUILTIN, (currentTime / 500) % 2);
  //__________________ task timer
  for (auto &taskTimer : taskTimers) {
    //______________ based on BWOD with attached function call
    if (currentTime - taskTimer.stamp > taskTimer.duration) {
      taskTimer.stamp = currentTime;
      taskTimer.task();
    }
  }
}

Have a nice day and enjoy coding in C++.
1 Like

Thank you for the code. It's slightly too complex for my level but I sort of see what's going on. I'd appreciate if you (or anyone else) can clarify the purpose of

digitalWrite(LED_BUILTIN, (currentTime / 500) % 2);

I don't understand how the built-in LED is involved with the 3 tasks,.

Secondly, I don't understand this instruction at all

  for (auto &taskTimer : taskTimers)

so a description would be great. Thank you!

Just going back here a minute, You not only want to be able to specify the "ON" time for each led. You also want to be able to specify the "OFF" time. Is that correct ?
It is not very clear because originally you appeared to want simply to trigger a task without considering the execution time of the task.
Anyway, if you want to check that its is working correctly, print also to the serial monitor. That will work even if the leds flash too quickly to see.

Incidentally, if this is school homework, no doubt the teacher will be impressed if you are able to manipulate range based for loops as fluently as @paulpaulson

Ok, I want the LEDs to be on for a second, I should've written this:

/*
  task 1: turn on red LED for 1 second, execute every 4 seconds
  task 2: turn on green LED for 1 second, execute every 10 seconds
*/

I made another post about this with more details because I got somewhere with my solution but now have a new problem, so I didn't want to confuse this thread: How to simultaneously execute two tasks?

This is for an assignment but it's not the assignment question, I am trying to get an idea of how to do Cyclic executive using a very simple example so that I can apply it to a complex problem for my assignment

byte state_T1 = 0;
byte state_T2 = 0;
byte state_T3 = 0;

unsigned long T1 = 0;
unsigned long T2 = 0;
unsigned long T3 = 0;

const int T1period = 4; //Seconds
const int T2period = 5;
const int T3period = 6;

const byte LED1_Pin = 15;  //LED red connected to pin 15
const byte LED2_Pin = 21; //LED green connected to pin 21
const byte LED3_Pin = 22;

void setup() {
  pinMode(LED1_Pin, OUTPUT);
  pinMode(LED2_Pin, OUTPUT);
  pinMode(LED3_Pin, OUTPUT);

  T1 = millis() + T1period * 1000;
  T2 = millis() + T2period * 1000;
  T3 = millis() + T3period * 1000;
}

void loop() {
  if (T1 - millis() < (T1period * 1000) && state_T1 == 0) {
    state_T1 = 1;
    digitalWrite(LED1_Pin, HIGH);
    T1 = millis() + 1000;
  }
  if (T1 - millis() < (T1period * 1000) && state_T1 == 1) {
    state_T1 = 0;
    digitalWrite(LED1_Pin, LOW);
    T1 = millis() + T1period ;
  }

  if (T2 - millis() < (T2period * 1000) && state_T2 == 0) {
    state_T2 = 1;
    digitalWrite(LED2_Pin, HIGH);
    T2 = millis() + 1000;
  }
  if (T2 - millis() < (T2period * 1000) && state_T2 == 1) {
    state_T2 = 0;
    digitalWrite(LED2_Pin, LOW);
    T2 = millis() + T2period ;
  }

  if (T3 - millis() < (T3period * 1000) && state_T3 == 0) {
    state_T3 = 1;
    digitalWrite(LED3_Pin, HIGH);
    T1 = millis() + 1000;
  }
  if (T3 - millis() < (T3period * 1000) && state_T3 == 1) {
    state_T3 = 0;
    digitalWrite(LED3_Pin, LOW);
    T3 = millis() + T3period ;
  }
}

or

void loop() {
  if (T1 - millis() < T1period * 1000)
    if ( state_T1 == 0) {
      state_T1 = 1;
      digitalWrite(LED1_Pin, HIGH);
      T1 += 1000;
    }
    else  {
      state_T1 = 0;
      digitalWrite(LED1_Pin, LOW);
      T1 += T1period;
    }

  ...
}

Duplicate topic locked

@cinnamonroll

Do NOT cross post / duplicate as it wastes peoples time and efforts to have more than one post for a single topic.

Continued cross posting could result in a time out from the forum.

Could you also take a few moments to [url=https://forum.arduino.cc/index.php?topic=710766.0]Learn How To Use The Forum[/url].

Other general help and troubleshooting advice can be found here.
It will help you get the best out of the forum in the future.