Multitasking code issue

Hello world,
I am just having some trouble with a code, that is like a multitasking process, well, i want a 4 led sequence to turn on one by one until the last one and return the same way, the speed sequence can be changed by a potentiometer, the real issue appears when i try to temporize that action due to it has to do the sequence (described above) for only 20 seconds and then start a new action for other 5 seconds, which consists en in simply blink the 4 leds, I was able to get that working but not in the proper way, due to on the sequence led you can see the last led gets stock for a few secs more than expected, and I don’t really know why, to be honest i am getting frustrated. I would really appreciate the help
thanks a bunch.
code is bellow.

 unsigned long interval1 = 20000;
unsigned long interval2 = 2000;
unsigned long previousMillis1;
unsigned long previousMillis2;
int leds[4] = {8, 9, 10, 11};
unsigned long previousMillis = 0;
int velocidad;
int led1=8;
int led2=9;
int led3=10;
int led4=11;
int pot;
unsigned long currentMillis;
void setup() {
  Serial.begin(115200);
   previousMillis1 = millis();
   previousMillis2 = millis();
   for (int i = 0; i < 4; i++)
  {
    pinMode(leds[i], OUTPUT);
    digitalWrite(leds[i], LOW);
  }
}

void loop() {
  pot=analogRead(A0);
 velocidad=pot;
  unsigned long currentMillis = millis();
   // Gestionar el desbordamiento
   if ((unsigned long)(currentMillis - previousMillis1) >= interval1)
   {
      action1();
      previousMillis1 = millis();
   }
   if ((unsigned long)(currentMillis - previousMillis2) >= interval2)
   {
      action2();
      previousMillis2 = millis();
   } 
}
void action2(){ 
   
   for (int i = 0; i < 4; i++){
    if (i > 0){
      digitalWrite(leds[i - 1], LOW);
    }
    digitalWrite(leds[i], HIGH);
    delay(velocidad);
  }
  digitalWrite(leds[3], LOW);
  for (int i = 3; i >= 0; i--){
    if (i < 4){
      digitalWrite(leds[i + 1], LOW);
    }
    digitalWrite(leds[i], HIGH);
    delay(velocidad);
  }
  digitalWrite(leds[0], 1);
}
void action1(){
  
    for(int i=0; i<=4; i++){
  digitalWrite(led1, 1);
  digitalWrite(led2, 1);
  digitalWrite(led3, 1);
  digitalWrite(led4, 1);
  delay(200);
  digitalWrite(led1, 0);
  digitalWrite(led2, 0);
  digitalWrite(led3, 0);
  digitalWrite(led4, 0);
  delay(200);
 }
}

Well you started out right using millis to handle the timing. But then your action1 and action2 routines are blocking and use delay and don't return until the entire sequence is done.

You should restructure so that those functions are also non-blocking and rely on being called over and over and over to constantly check their state and see if it is time to do something.

Not that it will help solve your issue but

pot=analogRead(A0);
 velocidad=pot;

could be simplified to just

velocidad=analogRead(A0);

Delta_G:
Well you started out right using millis to handle the timing. But then your action1 and action2 routines are blocking and use delay and don't return until the entire sequence is done.

You should restructure so that those functions are also non-blocking and rely on being called over and over and over to constantly check their state and see if it is time to do something.

I get your point, but not shure how to implement the non-blocking on the action1 and action2.

Have a look at the demo Several Things at a Time

...R

Action 1 is pretty easy. Every 200 milliseconds, toggle between HIGH and LOW.

void action1()
{
  static bool currentState = LOW;
  static unsigned long previousMillis = 0;
  const unsigned long interval = 200;
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval)
  {
    previousMillis = currentMillis;
    currentState = !currentState;  // Toggle between HIGH and LOW

    digitalWrite(led1, currentState);
    digitalWrite(led2, currentState);
    digitalWrite(led3, currentState);
    digitalWrite(led4, currentState);
  }
}

The rest wasn’t too bad. I made many of the global variables local and passed an argument to action2() instead of using a global. Now it should run action1() for 20 seconds and then action2() for two seconds and then back to action1(). Each action has its own timer and keeps track of its own state.

const unsigned long Interval1 = 20000;
const unsigned long Interval2 = 2000;
const byte LEDPins[4] = {8, 9, 10, 11};


void setup()
{
  Serial.begin(115200);
  for (int i = 0; i < 4; i++)
  {
    pinMode(LEDPins[i], OUTPUT);
    digitalWrite(LEDPins[i], LOW);
  }
}


void loop()
{
  unsigned long currentMillis = millis();
  static unsigned long previousMillis = 0;
  static byte whichPattern = 0;
  unsigned long interval = Interval1;
  byte nextPattern = 0;


  unsigned velocidad = analogRead(A0);


  switch (whichPattern)
  {
    case 0:
      action1();
      interval = Interval1;
      nextPattern = 1;
      break;


    case 1:
      action2(velocidad);
      interval = Interval2;
      nextPattern = 0;
      break;
  }


  // Gestionar el desbordamiento
  if (currentMillis - previousMillis >= interval)
  {
    previousMillis = currentMillis;
    whichPattern = nextPattern;
  }
}


void action2(unsigned long velocidad)
{
  static bool countingUp = true;
  static int count = 0;
  static unsigned long previousMillis = 0;
  unsigned long currentMillis = millis();


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


    // Turn off previously lit LED
    digitalWrite(LEDPins[count], LOW);


    // Count to the next LED:
    if (countingUp)
    {
      // UP
      count++;
      if (count > 3)
      {
        count = 3;
        countingUp = false;
      }
    }
    else
    {
      // Down
      count--;
      if (count < 0)
      {
        count = 0;
        countingUp = true;
      }
    }


    digitalWrite(LEDPins[count], HIGH); // Turn on the next in sequence
  }
}




void action1()
{
  static bool currentState = LOW;
  static unsigned long previousMillis = 0;
  const unsigned long interval = 200;
  unsigned long currentMillis = millis();


  if (currentMillis - previousMillis >= interval)
  {
    previousMillis = currentMillis;
    currentState = !currentState;  // Toggle between ON and OFF


    digitalWrite(LEDPins[0], currentState);
    digitalWrite(LEDPins[1], currentState);
    digitalWrite(LEDPins[2], currentState);
    digitalWrite(LEDPins[3], currentState);
  }
}

johnwasser:
The rest wasn’t too bad. I made many of the global variables local and passed an argument to action2() instead of using a global. Now it should run action1() for 20 seconds and then action2() for two seconds and then back to action1(). Each action has its own timer and keeps track of its own state.

const unsigned long Interval1 = 20000;

const unsigned long Interval2 = 2000;
const byte LEDPins[4] = {8, 9, 10, 11};

void setup()
{
  Serial.begin(115200);
  for (int i = 0; i < 4; i++)
  {
    pinMode(LEDPins[i], OUTPUT);
    digitalWrite(LEDPins[i], LOW);
  }
}

void loop()
{
  unsigned long currentMillis = millis();
  static unsigned long previousMillis = 0;
  static byte whichPattern = 0;
  unsigned long interval = Interval1;
  byte nextPattern = 0;

unsigned velocidad = analogRead(A0);

switch (whichPattern)
  {
    case 0:
      action1();
      interval = Interval1;
      nextPattern = 1;
      break;

case 1:
      action2(velocidad);
      interval = Interval2;
      nextPattern = 0;
      break;
  }

// Gestionar el desbordamiento
  if (currentMillis - previousMillis >= interval)
  {
    previousMillis = currentMillis;
    whichPattern = nextPattern;
  }
}

void action2(unsigned long velocidad)
{
  static bool countingUp = true;
  static int count = 0;
  static unsigned long previousMillis = 0;
  unsigned long currentMillis = millis();

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

// Turn off previously lit LED
    digitalWrite(LEDPins[count], LOW);

// Count to the next LED:
    if (countingUp)
    {
      // UP
      count++;
      if (count > 3)
      {
        count = 3;
        countingUp = false;
      }
    }
    else
    {
      // Down
      count–;
      if (count < 0)
      {
        count = 0;
        countingUp = true;
      }
    }

digitalWrite(LEDPins[count], HIGH); // Turn on the next in sequence
  }
}

void action1()
{
  static bool currentState = LOW;
  static unsigned long previousMillis = 0;
  const unsigned long interval = 200;
  unsigned long currentMillis = millis();

if (currentMillis - previousMillis >= interval)
  {
    previousMillis = currentMillis;
    currentState = !currentState;  // Toggle between ON and OFF

digitalWrite(LEDPins[0], currentState);
    digitalWrite(LEDPins[1], currentState);
    digitalWrite(LEDPins[2], currentState);
    digitalWrite(LEDPins[3], currentState);
  }
}

Bro, you rock!

I was literally frustrated because wasn’t able to get this working properly. you made my day.
Thanks a lot.

j2nm:
Bro, you rock!
I was literally frustrated because wasn't able to get this working properly. you made my day.
Thanks a lot.

Now study the code to understand how it works. If there is a piece you can't figure out, feel free to ask here for an explanation of that piece.
The secret to multitasking is for the task to be called very often and, most of the time, not doing anything because it is not time to do something. When it is time to do something, it does very little. Just enough to get the effect you want.
In your case the 'tasks' are not done at the same time. If you had two sets of LEDs and wanted both effects to run at the same time, each on its own set of LEDs, you would just call action1() and action2() over and over.