4

I have three funtion which describe the brightness of three LEDs. I want the seconde loop run 1 second after the first one and the third 1 second after the second one. One loop needs approximately 5 seconds. So basically I need to run three loops separated. How is this possible?

Update: So I have this code running which I can compile but does not work:

int LED = 3;  
int LED2 =10;
int LED3 = 9;

bool loop1state, loop2state, loop3state; // True if running, false if off
int  loop1index, loop2index, loop3index; // Iterator values
int  periodIndex = 0; // starting with 0, increasing every 10 ms


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

void loop()
{
// Update loop enablers.
switch(periodIndex)
{
case 0:
 loop1state = true;
 loop1index = 0;
 break;

case 1000:
 loop2state = true;
 loop2index = 0;
 break;

case 1500:
 loop3state = true;
 loop3index = 0;
 break;

case 4000:
 periodIndex = 0;
 break;
}

if (loop1state)
{
 for (loop1index = 0; loop1index<255; loop1index++){
  analogWrite(LED,loop1index);
 }

 loop1state = loop1index < 256;
 }

 if (loop2state)
 {
 for(loop2index = 0; loop2index<255; loop2index++)
 {
  analogWrite(LED2,loop2index);
 }

 loop2state = loop2index < 256;
}

 if (loop3state)
 {
 for(loop3index = 0; loop3index<255; loop3index++)
 {
  analogWrite(LED3,loop3index);
 }
 loop3state = loop3index < 256;
 }
 delay(10);
 }
2

4 Answers 4

1

No, but there is a solution to put the action in one loop (main loop).

If I understand you well, you have the following scheme:

Time[s] 0..1 Loop 1 1..2 Nothing 2..3 Loop 2 3..4 Nothing 4..5 Loop 3

Define a variable that keeps track of the time. Check against the current time (millis) and when within the correct 'range' perform an action. After 5 seconds start over again.

uint32_t lastTime = -1;

void loop()
{
    if (lastTime == -1)
    {
        lastTime = currentTime; // Initialization, can be done in setup too
    }

    uint32_t diffTime = millis() - lastTime;

    if (diffTime < 1000) // 0..1 sec
    {
        DoLoop1();
    }
    else if (diffTime < 2000)
    {
         // Do nothing();
    }
    else if (diffTime < 3000)
    {
       DoLoop2();
    }
    else if (diffTime < 4000)
    {
        // Do nothing
    }
    else if (diffTime < 5000)
    {
       DoLoop3();
    }
    else // diffTime >= 5000
    {
         lastTime == currentTime; // Reset after 6 s
    }
}

Update

Since you need all three loops together (and mixed), it's easier to define some variables.

bool loop1state, loop2state, loop3state; // True if running, false if off
int  loop1index, loop2index, loop3index; // Iterator values
int  periodIndex = 0; // starting with 0, increasing every 10 ms

Too bad I don't know the conditions when a loop starts, but let's say each iteration is 10 ms, and loop 1 starts immediately, loop 2 after 1000 ms, loop 3 after 1500 ms. After 3000 ms it starts all over.

void loop()
{
  // Update loop enablers.
  switch(periodIndex)
  {
  case 0:
     loop1state = true;
     loop1index = 0;
     break;

  case 1000:
     loop2state = true;
     loop2index = 0;
     break;

  case 1500:
     loop3state = true;
     loop3index = 0;
     break;

  case 4000:
     periodIndex = 0;
     break;
  }

  if (loop1state)
  {
     DoLoop1(loop1index++);
     loop1state = loop1index < 256;
  }

  if (loop2state)
  {
     DoLoop2(loop2index++);
     loop2state = loop2index < 256;
  }

  if (loop3state)
  {
     DoLoop3(loop3index++);
     loop3state = loop3index < 256;
  }

  delay(10); // 10 ms
}

Note, not tested, but the idea shold be clear

New update

You should not do a full loop during each switch, but only one iteration of the loop.

I did not run the sketch, just out of my head.

Some tips: print out the variables during each loop, so you get a clear idea what is happening (so all loop states and indices, and the periodIndex).

int LED = 3;  
int LED2 =10;
int LED3 = 9;

bool loop1state, loop2state, loop3state; // True if running, false if off
int  loop1index, loop2index, loop3index; // Iterator values
int  periodIndex = 0; // starting with 0, increasing every 10 ms


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

void loop()
{
  // Update loop enablers.
  switch(periodIndex)
  {
  case 0:
    loop1state = true;
    loop1index = 0;
    break;

  case 1000:
    loop2state = true;
    loop2index = 0;
    break;

  case 1500:
    loop3state = true;
    loop3index = 0;
    break;

  case 4000:
     periodIndex = 0;
     break;
  }

  if (loop1state)
  {
    analogWrite(LED,loop1index++);
    loop1state = loop1index < 256;
  }

  if (loop2state)
  {
    analogWrite(LED2,loop2index++);
    loop2state = loop2index < 256;
  }

  if (loop3state)
  {
    analogWrite(LED3,loop3index++);
    loop3state = loop3index < 256;
  }

  periodIndex++;
  delay(10);
}
10
  • @ChrisStratton Updated my answer according to the recalling millis() problem. Delay is possible too indeed (with your remark when possible to use). Commented Jul 23, 2017 at 21:26
  • @ChrisStratton True, with some addition for 'no-action'parts Commented Jul 23, 2017 at 21:32
  • Oh,you're absolutely right, sorry about that! Feel free to change it back if you prefer the way you previously had it. Commented Jul 23, 2017 at 21:43
  • 1
    I just wanted to try your sketch but I get an error at the bottom of the code at the last else loop: "currentTime was not declared in this scope" However, I'm not quite sure if this code will work the way I was thinking of. The wirst loop should start an while x is increasing at the first loop, the second one should start. Time[s]:0..1:Loop 1 starts, 1...2: Loop2 starts while Loop 1 is increasing, 2...3: Loop 3 starts while Loop 1 and 2 are increasing,3...5:all Loops are increasing until 255. In that order Loop 1 reaches 255 first, Loop 2 is second and Loop 3 third.
    – gamma
    Commented Jul 24, 2017 at 8:21
  • 1
    Thanks for your help. Unfortunately it doesn't work. I'm still a beginner in coding so I think I misunderstood something. This is how I try to run my first loop: if (loop1state) { for (loop1index = 0; loop1index<255; loop1index++){ analogWrite(LED,loop1index); } loop1state = loop1index < 256; } I can however compile it but it does not work. I also wanted to ask you how I can change my delay of 10ms?
    – gamma
    Commented Jul 24, 2017 at 20:33
1

Let me see if I understand correctly what you want... You want to ramp up the brightnesses of 3 LEDs, one step every roughly 10 ms, and you want the brightness ramps to start at different times. If this is the case, I would start by writing a function that handles one LED, increases its brightness by one step, and tells the caller whether the ramp is done or not:

// Update the LED.
// Returns true if there are brightness steps left,
// false if we have finished the brightness ramp.
bool update_led_1()
{
    static uint8_t level = 0;  // brightness level
    analogWrite(LED1, level);
    level++;  // wraps to zero when we are done
    return level != 0;
}

This stores the brightness level as a single byte, rather than a two-byte int, because I am cheap. ;-) Notice that on the last step of the ramp, the value 255 is sent to the LED, then level++ wraps the level to zero, then the function returns false to tell us the ramp is finished. At this point, since level is zero, the function is ready for the next ramp.

You can copy and paste this function three times, for each of the three LEDs. Name them update_led_1(), update_led_2() and update_led_3(). The main loop can then go along these lines:

void loop()
{
    static int periodIndex = 0;  // increasing every 10 ms

    static bool led1active = false;
    if (periodIndex == 0)
        led1active = true;
    if (led1active)
        led1active = update_led_1();

    static bool led2active = false;
    if (periodIndex == 1000)
        led2active = true;
    if (led2active)
        led2active = update_led_2();

    static bool led3active = false;
    if (periodIndex == 1000)
        led3active = true;
    if (led3active)
        led3active = update_led_3();

    if (++periodIndex == 4000)
        periodIndex = 0;
    delay(10);
}

Notice the idiom if (led1active) led1active = update_led_1();. This will be executed on every loop iteration, it will increase the LED brightness if the LED is supposed to be active, and it will set led1active to false when the brightness ramp is complete.

There is something I do not like in the solution I am suggesting here: the copy-and-paste programming paradigm. If I am allowed to use C++ constructs, then I would encapsulate the ramp logic in a C++ class:

class RampingLed {
    const uint8_t pin;
    bool active;
    uint8_t level;
public:
    RampingLed(uint8_t pin)
        : pin(pin), active(false), level(0) {}
    void start() {
        active = true;
        level = 0;
    }
    void update() {
        if (!active) return;
        analogWrite(pin, level++);
        if (level == 0)
            active = false;
    }
};

Then the logic of the main program would appear more straightforward:

RampingLed led1(LED1), led2(LED2), led3(LED3);

void loop()
{
    static int periodIndex = 0;  // increasing every 10 ms

    // Start the LEDs at the appropriate times.
    if (periodIndex == 0)
        led1.start();
    if (periodIndex == 1000)
        led2.start();
    if (periodIndex == 1500)
        led3.start();

    // Update every LED.
    led1.update();
    led2.update();
    led3.update();

    if (++periodIndex == 4000)
        periodIndex = 0;
    delay(10);
}
-1

If your three described for loops are the only thing the Arduino needs to do, then you can simply use blocking delay() calls. However, this scheme will not be extensible to also satisfying other needs at the same time - should you anticipate those, then you should use a mechanism of time comparisons instead.

void loop() {
    int i;
    /* First loop */
    for (i=0; i < 255 ; i++) {
        your_led_function(LED_1, i);
        delay(20); //256 * 20 = 5.12 seconds
    }

    /* I want the second loop run 1 second after the first one */
    delay(1000);

    /* Second loop */
    for (i=0; i < 255 ; i++) {
        your_led_function(LED_2, i);
        delay(20);
    }

    /* and the third 1 second after the second one */
    delay(1000);

    /* Third loop */
    for (i=0; i < 255 ; i++) {
        your_led_function(LED_3, i);
        delay(20);
    }
}

Ultimately which type of solution is best depends on your goals - you can do things in a simple but limited way, or a more complicated but less limited one. For every 3d printer firmware that cleverly squeezes every last drop of coordinated capability out of an Arduino, there are also those that spend their days sitting in a blocking delay() waiting for the next trivial bit toggle task.

-1

So I have this code running which I can compile but does not work:

because it is wrong.

try something like this instead:

  time_elapsed = millis() / 1000 - start_time;  //time past start time, in seconds
       if (time_elapsed < LOOP1_ENDTIME) loop1();
  else if (time_elapsed < LOOP2_STARTTIME) do_nothing();
  else if (time_elapsed < LOOP2_ENDTIME) loop2();
  else if (time_elapsed < LOOP3_STARTTIME) do_nothing();
  ...
  else start_time+=TOTAL_TIME; //update start time

edit 2:

so here is the above code in action.

enter image description here

three simple tasks: loop1() flips D12, loop2() flips D13 and loop3() flips D8.

The time duration + gaps for those tasks are specified by the following macros:

#define LOOP1_DURATION  5  //time in x100 ms for easier simulation
#define LOOP1_GAP       1
#define LOOP2_DURATION  4
#define LOOP2_GAP       2
#define LOOP3_DURATION  3
#define LOOP3_GAP       6

from those macros, I built the ending and starting time used in the code above.

the code is long-term error free and will never lose track of time.

As you can see, it is essentially a fancy way of executing the following:

  do {loop1();} while (loop1's time isn't up);
  do {nothing();} while (loop1's gap time isn't up);
  do {loop2();} while (loop2's time isn't up);
  do {nothing();} while (loop2's gap time isn't up);
  ...

the advantage with my implementation is that each loop run is very quick and you can execute other tasks as well.

But the general idea is the same.

1
  • I notice a number (maybe most?) of your posts do not have properly capitalized sentences. Please slow down and type proper English. A sentence starts with a capital letter. An example here. Your answers look good, now just take a bit of time to format them properly. Proper formatting makes the site in general look better. Thanks for your understanding and cooperation!
    – Nick Gammon
    Commented Jul 26, 2017 at 0:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.