Help with nested millis

I am working on my first RGB LED Christmas scene. We normally have an artificial tree in the front yard lit up with several strings of green tree lights. That’s not changing this year. Behind this tree, I have a convenient branch of a pine tree where I plan to hang a few RGB LED strings. These strings will be normally all blue, but at random, a white “drip” will start at the top and proceed to the bottom.

I sort of have this working, but because my drip functions have blocking parts (for loop and delays), the drip is not as random as I would like. Ideally, the strings would drip at random times, sometimes while another string is dripping.

Every time I try to make the drip functions non-blocking, I break something else. Any suggestions how to make the drip functions non-blocking would be appreciated.

/*
   fastLED_Drip3b.ino
*/

#include <FastLED.h>

#define NUM_LEDS 26     // Per string
#define DATA_PIN1 D1
#define DATA_PIN2 D2
#define DATA_PIN3 D3
#define BRIGHTNESS 255
#define LED_TYPE WS2811
#define COLOR_ORDER RGB


CRGB leds_1[NUM_LEDS];        // This is an array of leds.  One item for each led in your strip.
CRGB leds_1_Saved;            // Save the LED color here (for the drip function)
CRGB leds_2[NUM_LEDS];
CRGB leds_2_Saved;
CRGB leds_3[NUM_LEDS];
CRGB leds_3_Saved;


// Variable holding the timer value so far. One for each string of LEDs
unsigned long leds_1_timer;
unsigned long leds_2_timer;
unsigned long leds_3_timer;

// Time periods of drips in milliseconds
unsigned long leds_1_Interval = 400;
unsigned long leds_2_Interval = 1000;
unsigned long leds_3_Interval = 1000;



// =========================== setup() ===========================
void setup() {
  Serial.begin(115200);
  Serial.println();

  // The initial interval between drips in milliseconds
  leds_1_Interval = random(250, 1000);
  leds_2_Interval = random(250, 1000);
  leds_3_Interval = random(250, 1000);

  // sanity check delay - allows reprogramming if accidently blowing power w/leds
  delay(2000);

  // This tells the library that there's a strand of LED_TYPE on pin DATA_PIN1 to DATA_PIN3,
  // and those leds will use the led array leds_1 to leds_3, and there are NUM_LEDS of them.
  FastLED.addLeds<LED_TYPE, DATA_PIN1, COLOR_ORDER>(leds_1, NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.addLeds<LED_TYPE, DATA_PIN2, COLOR_ORDER>(leds_2, NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.addLeds<LED_TYPE, DATA_PIN3, COLOR_ORDER>(leds_3, NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);


  // Start with all LEDs off.
  oneColor(CRGB::Black);
  delay(2000);

  // Curtain color- the LEDs normal color
  oneColor(CRGB::Blue);

  leds_1_timer = millis ();
  leds_2_timer = millis ();
  leds_3_timer = millis ();
}


// =================== loop() ===================
void loop() {

  // First string.
  if ( (millis () - leds_1_timer) >= leds_1_Interval)
    drip_1();

  // Second string.
  if ( (millis () - leds_2_timer) >= leds_2_Interval)
    drip_2();

  // Third string.
  if ( (millis () - leds_3_timer) >= leds_3_Interval)
    drip_3();

  EVERY_N_SECONDS(5) {
    // Every few seconds, change the drip intervals.
    leds_1_Interval = random(250, 1500);
    leds_2_Interval = random(500, 1500);
    leds_3_Interval = random(750, 900);
  }
}



// =========================== oneColor() ===========================
void oneColor(CRGB myColor) {
  // Light the leds one at a time. Leave them on.
  for (int ledNum = 0; ledNum < NUM_LEDS; ledNum = ledNum + 1) {
    leds_1[ledNum] = myColor;
    leds_2[ledNum] = myColor;
    leds_3[ledNum] = myColor;
    FastLED.show();
    delay(15);        // Not needed, but you can see the leds light up sequentially.
  }
}


// =========================== drip_1() ===========================
// Drip a white LED along the string.
void drip_1() {
  // For each LED
  // save the current color, set the LED to white, delay, then restore the color.
  for (int i = 0; i <= NUM_LEDS; i++) {
    leds_1_Saved = leds_1[i];
    leds_1[i] = CRGB::White;
    FastLED.show();
    delay(random(5, 20));           // Speed of the drip.  Lower is faster
    leds_1[i] = leds_1_Saved;
    FastLED.show();


    // remember when we dripped
    leds_1_timer = millis ();
  }
  delay(random(100, 500));          // Random delay between drips
  leds_1_Interval = random(250, 1500);

}


// =========================== drip_2() ===========================
// Drip a white LED along the string.
void drip_2() {
  // For each LED
  // save the current color, set the LED to white, delay, then restore the color.
  for (int i = 0; i <= NUM_LEDS; i++) {
    leds_2_Saved = leds_2[i];
    leds_2[i] = CRGB::White;
    FastLED.show();
    delay(random(5, 20));           // Speed of the drip.  Lower is faster
    leds_2[i] = leds_2_Saved;
    FastLED.show();

    // remember when we dripped
    leds_2_timer = millis ();
  }
  delay(random(1, 500));           //Random delay between drips
  leds_2_Interval = random(500, 1500);

}



// =========================== drip_3() ===========================
// Drip a white LED along the string.
void drip_3() {
  // For each LED
  // save the current color, set the LED to white, delay, then restore the color.
  for (int i = 0; i <= NUM_LEDS; i++) {
    leds_3_Saved = leds_3[i];
    leds_3[i] = CRGB::White;
    FastLED.show();
    delay(random(5, 20));           // Speed of the drip.  Lower is faster
    leds_3[i] = leds_3_Saved;
    FastLED.show();

    // remember when we dripped
    leds_3_timer = millis ();
  }
  delay(random(100, 1000));         //Random delay between drips
  leds_3_Interval = random(750, 900);
}

I just picked this function as an example

// Drip a white LED along the string.
void drip_1() {
  // For each LED
  // save the current color, set the LED to white, delay, then restore the color.
  for (int i = 0; i <= NUM_LEDS; i++) {
    leds_1_Saved = leds_1[i];
    leds_1[i] = CRGB::White;
    FastLED.show();
    delay(random(5, 20));           // Speed of the drip.  Lower is faster
    leds_1[i] = leds_1_Saved;
    FastLED.show();


    // remember when we dripped
    leds_1_timer = millis ();
  }
  delay(random(100, 500));          // Random delay between drips
  leds_1_Interval = random(250, 1500);

}

To get a responsive program this will need a major overhaul.

First, don’t use a FOR loop - just implement a counter and allow loop() to do the repetition. You could have a variable that gets changed (somewhere) that tell this function whether it is its turn to operate - something like

void drip_1() {
  if (dripNumber != 1) { // do nothing if it is not my turn
     return;
  ]

Then use millis() in the same style as you have used elsewhere to do the timing. You may also need another variable to indicate whether this iteration is the coloured version or the white version - something like

if (showWhite == true) {
    leds_1_Saved = leds_1[i];
    leds_1[i] = CRGB::White;
    showWhite = false;
}
else {
    leds_1[i] = leds_1_Saved;
}
FastLED.show();

…R

I will try again today. After more than 12 hours, my brain was getting fuzzy.

Making significant progress. I will post final code when it works (for anyone who searches for the same problem later).