multiple interval / durations

Hello, I have a project that requires 24 outputs to be able to be turned on for a time and then go off after the duration period. They have to be able to be turned on multiple times during the interval period at different interval times..

Its like a clock period that is cut up into, say 20 second, slots and the 24 outputs can be turned on during any number of the slots.

ANY help would be appreciated

This is what I have tried and failed at, due to no set period or slots…

[code]----CONSTANTS
//SETTING UP THE OUTPUT PINS

byte outputPins[24] = {22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45};

//SETTING UP THE INTERVAL CONSTANTS Ie: How long You want the output to go HIGH
//....................................//1     //2     //3     //4     //5       //6     //7     //8       //9     //10     //11     //12    //13      //14    //15      //16    //17      //18      //19     //20   //21    //22      //23  //24
unsigned long outputIntervals[24] = {5000UL, 3000UL, 4000UL, 4000UL, 20000UL, 5000UL, 60000UL, 20000UL, 20000UL, 20000UL, 5000UL, 40000UL, 20000UL, 20000UL, 20000UL, 10000UL, 10000UL, 10000UL, 30000UL, 30000UL, 20000UL, 20000UL, 500UL, 550UL};


//SETTING THE "ON" TIME Ie: How Often You Want It To Happen
//....................................//1      //2       //3       //4        //5      //6       //7       //8        //9       //10      //11     //12      //13     //14       //15       //16      /17       //18    //19    //20      //21      //22      //23      //24
unsigned long blinkDurations[24] = {240000UL, 245000UL, 247500UL, 248500UL, 249000UL, 269500UL, 245000UL, 295000UL, 245000UL, 245000UL, 275000UL, 245000UL, 275000UL, 265000UL, 280000UL, 290000UL, 290000UL, 290000, 180000, 180000UL, 170000UL, 160000UL, 240000UL, 200000UL};


//SETTING THE OUTPUT PINS LOW

byte outputStates[24] = {0};   // initialize all elements to 0 == LOW


//SETTING THE MILLIS TO 0 TO START FRESH
unsigned long currentMillis = 0;    // stores the value of millis() in each iteration of loop()

unsigned long previousMillis[24] = {0};

//SETUP AREA

//SETTING THE MONITOR FUNCTION TO 9600 AND PRINT THE LED_A_STATE
void setup() {
Serial.begin(9600);

// SETTING THE OUTPUT PINS TO OUTPUT MODE
for (int i = 0; i < 24; i++) {
 pinMode(outputPins[i], OUTPUT);

}

}

//LOOP AREA

void loop() {


currentMillis = millis();   // capture the latest value of millis()


for (int i = 0; i < 24; i++) {
 updateOutputState(i);
}


switchOutputs();
}



//SETTING LED n IN ACTION
void updateOutputState(int n) {
if (outputStates[n] == LOW) {
 if (currentMillis - previousMillis[n] >= outputIntervals[n]) {
   outputStates[n] = HIGH;
   previousMillis[n] += outputIntervals[n];
 }
}
else {
 if (currentMillis - previousMillis[n] >= blinkDurations[n]) {
   outputStates[n] = LOW;
   previousMillis[n] += blinkDurations[n];

 }
}
}

//SWITCH THE LED STATE
void switchOutputs() {
// this is the code that actually switches the LEDs on and off
for (int i = 0; i < 24; i++) {
 digitalWrite(outputPins[i], outputStates[i]);
 Serial.println(outputStates[21]);
}

}

//=====END

Please use the code button </> so your code looks like this

    //CONSTANTS
    //SETTING UP THE OUTPUT PINS

byte outputPins[24] = {22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45};

    //SETTING UP THE INTERVAL CONSTANTS Ie: How long You want the output to go HIGH
//....................................//1     //2     //3     //4     //5       //6     //7     //8       //9     //10     //11     //12    //13      //14    //15      //16    //17      //18      //19     //20   //21    //22      //23  //24
unsigned long outputIntervals[24] = {5000UL, 3000UL, 4000UL, 4000UL, 20000UL, 5000UL, 60000UL, 20000UL, 20000UL, 20000UL, 5000UL, 40000UL, 20000UL, 20000UL, 20000UL, 10000UL, 10000UL, 10000UL, 30000UL, 30000UL, 20000UL, 20000UL, 500UL, 550UL};


    //SETTING THE "ON" TIME Ie: How Often You Want It To Happen
//....................................//1      //2       //3       //4        //5      //6       //7       //8        //9       //10      //11     //12      //13     //14       //15       //16      /17       //18    //19    //20      //21      //22      //23      //24
unsigned long blinkDurations[24] = {240000UL, 245000UL, 247500UL, 248500UL, 249000UL, 269500UL, 245000UL, 295000UL, 245000UL, 245000UL, 275000UL, 245000UL, 275000UL, 265000UL, 280000UL, 290000UL, 290000UL, 290000, 180000, 180000UL, 170000UL, 160000UL, 240000UL, 200000UL};


    //SETTING THE OUTPUT PINS LOW

byte outputStates[24] = {0};   // initialize all elements to 0 == LOW


    //SETTING THE MILLIS TO 0 TO START FRESH
unsigned long currentMillis = 0;    // stores the value of millis() in each iteration of loop()

unsigned long previousMillis[24] = {0};

    //SETUP AREA
    //SETTING THE MONITOR FUNCTION TO 9600 AND PRINT THE LED_A_STATE
void setup() {
    Serial.begin(9600);

        // SETTING THE OUTPUT PINS TO OUTPUT MODE
    for (int i = 0; i < 24; i++) {
        pinMode(outputPins, OUTPUT);
    }

}

    //LOOP AREA

void loop() {
    currentMillis = millis();   // capture the latest value of millis()
    
    for (int i = 0; i < 24; i++) {
        updateOutputState(i);
    }
    switchOutputs();
}



    //SETTING LED n IN ACTION
void updateOutputState(int n) {
    if (outputStates[n] == LOW) {
        if (currentMillis - previousMillis[n] >= outputIntervals[n]) {
            outputStates[n] = HIGH;
            previousMillis[n] += outputIntervals[n];
        }
    }
    else {
        if (currentMillis - previousMillis[n] >= blinkDurations[n]) {
            outputStates[n] = LOW;
            previousMillis[n] += blinkDurations[n];

        }
    }
}

    //SWITCH THE LED STATE
void switchOutputs() {
        // this is the code that actually switches the LEDs on and off
    for (int i = 0; i < 24; i++) {
        digitalWrite(outputPins, outputStates);
        Serial.println(outputStates[21]);
    }

}

//=====END

The code in updateOutputState() seems strange insofar as the blinking seems only to happen if the outputState[] is low and also because you are using the same variable previousMillis[] for managing the time for output states and blinks.

Can you describe more clearly the sequence of events you are trying to achieve.

…R

update, some small mods esp to show code more slow.

//SETTING UP THE OUTPUT PINS

// int dummy = 42;

byte outputPins[24] = {22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45};

//SETTING UP THE INTERVAL CONSTANTS Ie: How long You want the output to go HIGH
unsigned long outputIntervals[24] = {
  5000UL, 3000UL, 4000UL, 4000UL, 20000UL,
  5000UL, 60000UL, 20000UL, 20000UL, 20000UL,
  5000UL, 40000UL, 20000UL, 20000UL, 20000UL,
  10000UL, 10000UL, 10000UL, 30000UL, 30000UL,
  20000UL, 20000UL, 500UL, 550UL
};


//SETTING THE "ON" TIME Ie: How Often You Want It To Happen
unsigned long blinkDurations[24] = {
  240000UL, 245000UL, 247500UL, 248500UL, 249000UL,
  269500UL, 245000UL, 295000UL, 245000UL, 245000UL,
  275000UL, 245000UL, 275000UL, 265000UL, 280000UL,
  290000UL, 290000UL, 290000UL, 180000UL, 180000UL,
  170000UL, 160000UL, 240000UL, 200000UL
};

byte outputStates[24];

unsigned long currentMillis;

unsigned long previousMillis[24];

//SETUP AREA

//SETTING THE MONITOR FUNCTION TO 9600 AND PRINT THE LED_A_STATE
void setup() {
  Serial.begin(115200);

  // SETTING THE OUTPUT PINS TO OUTPUT MODE
  for (int i = 0; i < 24; i++) {
    // pinMode(outputPins[i], OUTPUT);
    outputStates[i] = LOW;
    previousMillis[i] = 0;
  }
}

void loop() {
  currentMillis = millis();   // capture the latest value of millis()
  int sumChanged = 0;
  for (int i = 0; i < 24; i++) {
    sumChanged += updateOutputState(i);
  }
  if (sumChanged > 0) switchOutputs();
}

int updateOutputState(int n) {
  if (outputStates[n] == LOW) {
    if (currentMillis - previousMillis[n] >= outputIntervals[n]) {
      outputStates[n] = HIGH;
      previousMillis[n] = currentMillis;
      return 1;
    }
  }
  else {
    if (currentMillis - previousMillis[n] >= blinkDurations[n]) {
      outputStates[n] = LOW;
      previousMillis[n] = currentMillis;
      return 1;
    }
  }
  return 0;
}


//SWITCH THE LED STATE
void switchOutputs() {
  // this is the code that actually switches the LEDs on and off
  Serial.print(currentMillis);
  for (int i = 0; i < 24; i++) {
    // digitalWrite(outputPins[i], outputStates[i]);
    Serial.print(" ");
    Serial.print(outputStates[i]);
  }
  Serial.println();

}

//=====END

the values in blinkDurations[24] are very long

would it not be easier to define per pin HIGH time and the LOW time?

The best I can describe this project is:

I have a time period of 240 seconds (cycle)
I have 24 relays driven by outputs 22-45 (LOW on active)

I wish to be able to have the relays come on at different intervals thru out the “cycle”… IE: relay 1 on for 10 seconds, off for 180 seconds and back on for 15 seconds, off till the end of the cycle…

I need this functionality for all 24 relays

I hope this gives you enough insight as to my project…

I apologize for the code not being proper… I used the button… A little help here would be appreciated as well

Ok..did I ask for too much when I described my project?

I posted some code here to turn relays on and off based on predetermined intervals and start times.

It could be expanded to 24 devices relatively easily. Just add additional values to the arrays and change the value of “RELAYS_IN_USE”.

dcr_inc: I hope this gives you enough insight as to my project.. ....snip.... A little help here would be appreciated as well

You have not responded to my comments about your code in the penultimate paragraph in Reply #2. Have you considered whether thay may have a bearing on the problem?

...R

@Robin2.. The outputs are LOW active due to the relay boards I have.. They use LOW logic for ON..

As for "also because you are using the same variable previousMillis[] for managing the time for output states and blinks."]

I used existing code and made the modifications I thought I needed to create the desired outcome..

I don't know of a different way to do this.. Your insight please..

I do apologize for not being a C++ programmer, I realize as most of the responders are, that is the reason I come asking for guidance and maybe a little help with example code.

Thanks

You still haven't made your code easily accessible by adding code tags, as requested in reply #2.

another attempt at describing my project…

Time Relay On
20 X X X X X X X - - - - - X X X X - - - - - - - -
40 - - - -X X X - X - X - X - - - - - - - - - - - -
60 X X X X X X - - - - - X X X X X X X X X X X
80
100
120 ETC ETC ETC
140
160
180
200
220
240

X = Relay ON -= Relay OFF

AT 240, all relays clear and the cycle starts again

I’m not sure what tags are missing? I have it tagged as to what each section does… Is there something specific that I can TRY to explain?

The code tags make the code look

like this

when posting source code files. It makes it easier to read, and can be copied with a single mouse click. Also, if you don’t do it, some of the character sequences in the code can be misinterpred by the forum code as italics or funny emoticons.
If you have already posted without using code tags, open your message and select “modify” from the pull down menu labelled, “More”, at the lower left corner of the message. Highlight your code by selecting it (it turns blue), and then click on the “</>” icon at the upper left hand corner. Click on the “Save” button. Code tags can also be inserted manually in the forum text using the code and /code metatags.

Please read these two posts:

How to use this forum - please read.
and
Read this before posting a programming question …

OK… I re-read both articles…

Issue is, If I knew how to do what I am asking for help on, I wouldn’t waste anyone’s time or intelligence asking…

At 57 YO, I believe I know when to and when not to ask for help… I have read as many books, articles and what not to grasp as much knowledge that I can on my own. A lot of it sinks in and is useful without help or guidance while some of the information just doesn’t make sense by reading it alone…

It is a shame that this forum, usually turns into someone being belittled or bashed by the “Experts” when the one asking, truly needs help and guidance…

As many “askers” are not programmers by trade or full time hobby, they, as in myself, ask what the “experts” see as stupid, elementary level questions… but to the asker, it is a legitimate question…

Someday, I hope to be the “expert” and you are the one asking for help…

RANT OFF…

A sincere thanks to those who have guided me along the way, You have made a positive difference in my journey.

dcr_inc: It is a shame that this forum, usually turns into someone being belittled or bashed by the "Experts" when the one asking, truly needs help and guidance..

Back off a bit. (And I am older than you are - age is irrelevant).

I made some comments in Reply #2 on the assumption that they would be meaningful to you when you thought about them and studied your code again carefully.

I am perfectly happy to answer questions if you say there are specific things in my comments that you don't understand. But your response

I used existing code and made the modifications I thought I needed to create the desired outcome..

I don't know of a different way to do this.. Your insight please..

does not give me any guidance about how to help you next - apart from actually writing the code for you.

The fact that you are not a C++ programmer has nothing to do with this.

And the only questions that I consider stupid are the ones that people are afraid to ask (they are usually the best questions).

...R

dcr_inc:
It is a shame that this forum, usually turns into someone being belittled or bashed by the “Experts” when the one asking, truly needs help and guidance…

As many “askers” are not programmers by trade or full time hobby, they, as in myself, ask what the “experts” see as stupid, elementary level questions… but to the asker, it is a legitimate question…

Someday, I hope to be the “expert” and you are the one asking for help…

I’m not sure what brought on the above. I agree some “bashing” occurs but I sure didn’t see any in this thread.

I think the vast majority of questions are just answered.

You should be able to use the array “RELAYS_ON_BITS_BYTE_TABLE” to set which relay you want on when. The Arduino IDE won’t allow (it might be C not allowing it) variables larger than a byte to be expressed in binary.

The program flows a lot better if I can hold these bits in a single variable so the bits get moved to “relayBits” in the setup function.

The program compiles but I haven’t tested it.

Edit: This version of the code has severe bugs. See reply #19 for the fixed code.

const unsigned long MILLISECONDS_PER_SECOND = 1000;

const unsigned long INTERVAL_BETWEEN_CHANGES = 20 * MILLISECONDS_PER_SECOND;
const byte TIMES_TO_CHECK_RELAYS = 12;

// We can only use binary notation with bytes.
// So the relay on bits need to be byte sized variables.
// Fill these bits with the relays you want on during the 
// various intervals.
const byte RELAYS_ON_BITS_BYTE_TABLE[TIMES_TO_CHECK_RELAYS * 3] = {
/*  
 *     2            1
   43210987   65432109   87654321 */
  B11111110, B00001111, B00001100, //20
  B00000000, B00000000, B00000000, 
  B00000000, B00000000, B00000000, //60
  B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000, //120
  B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000, //180
  B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000  //240
  };

  //We'll move the byte dato to longs in the setup function.
unsigned long relayBits[TIMES_TO_CHECK_RELAYS];

const byte RELAYS_IN_USE = 24;
const byte BYTES_OF_RELAYS_IN_USE = (RELAYS_IN_USE + 7) / 8; // This should equal 3

// The pins are in the opposite order of the on bits.
const byte RELAY_PIN[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25};
const boolean ACTIVE_STATE = LOW;
const boolean INACTIVE_STATE = HIGH; 

void setup()
{
  for (int relayIndex = 0; relayIndex < RELAYS_IN_USE; relayIndex++)
  {
    digitalWrite(RELAY_PIN[relayIndex], INACTIVE_STATE);
    pinMode(RELAY_PIN[relayIndex], OUTPUT);
  }

  byte byteIndex = 0;
  for (int intervalIndex = 0; intervalIndex < RELAYS_IN_USE; intervalIndex++)
  {
    for (int i = 0; i < 3; i++)
    {
      relayBits[intervalIndex] <<= 8;
      relayBits[intervalIndex] |= RELAYS_ON_BITS_BYTE_TABLE[byteIndex++];
    }
  }
}

void loop()
{ 
  for (int intervalIndex; intervalIndex < TIMES_TO_CHECK_RELAYS; intervalIndex++)
  {
    unsigned long bitsToShift = relayBits[intervalIndex];
    for (int relayIndex = 0; relayIndex < RELAYS_IN_USE; relayIndex++)
    {
      
      if (bitsToShift & 1)
      {
        digitalWrite(RELAY_PIN[relayIndex], ACTIVE_STATE);
      }
      else
      {
        digitalWrite(RELAY_PIN[relayIndex], INACTIVE_STATE);
      }
      bitsToShift >>= 1;
    }
    delay(INTERVAL_BETWEEN_CHANGES);
  }
}

Edit: I didn’t have 24 I/O pins in the pin array. Fixed.

Folks.. I do APPRECIATE every bit of help you provide.. I am struggling with what I don't know and have not been able to comprehend..

Thank you

The code in reply #14 should do what you want.

Just place an one in the bit if you want that relay to turn on for that interval.

/*  
 *     2            1
   43210987   65432109   87654321 */
  B11111110, B00001111, B00001100, //20
  B00000000, B00000000, B00000000, 
  B00000000, B00000000, B00000000, //60
  B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000, //120
  B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000, //180
  B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000,
  B00000000, B00000000, B00000000  //240
  };

I put some ones in the first interval as an example.

The bits are numbered from right to left.

The relay I/O pins are contained in this array.

// The pins are in the opposite order of the on bits.
const byte RELAY_PIN[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25};

These are numbered from left to right.

We can figure out which pins will be set low (active) during the first 20 second interval by using these two sets of numbers.

The active pins. An one indicates the relay is active.
" B11111110, B00001111, B00001100,"

The right most bit corresponds with the left most I/O pin in this next list.

2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25

So in the above example, the following pins will be set low during the first 20 second interval.
4, 5, 10, 11, 12, 13, 19, 20, 21, 22, 23, 24, 25

As the code is presently written, all the relays will be turned off for the remainder of the four loop interval. You’ll need to add additional ones to indicate which relays are to be turned on.

Edit: I’m not aware of any bugs presently. I haven’t tested the code so there may be bugs I’m not aware of. Let me know if the program doesn’t behave as expected.

Thanks a million..

The code in post #14 is exactly what I needed to accomplish my project.. And I am referring to it as the code I'm using

I do need help getting the relay status ( whether active or not) on the monitor..

I added Serial.begin(9600) in the setup and attempted to get something to display using Serial.println( tried RELAY_PIN, etc) in the loop and had no luck..

Thanks

I just added some debug statements and tried running the program. There is obviously a bug in the program. It's not working correctly right now.

I'll see if I can fix it and uploaded a fixed version.

The debug statements made many of the bugs in the original code very obvious (one reason why it’s good to use debug statements).

I think I’ve got rid of the bugs.

const unsigned long MILLISECONDS_PER_SECOND = 1000;

const unsigned long INTERVAL_BETWEEN_CHANGES = 20 * MILLISECONDS_PER_SECOND;
const byte TIMES_TO_CHECK_RELAYS = 12;

// We can only use binary notation with bytes.
// So the relay on bits need to be byte sized variables.
// Fill these bits with the relays you want on during the
// various intervals.
const byte RELAYS_ON_BITS_BYTE_TABLE[] = {
  /*
   *     2            1
     43210987   65432109   87654321 */
  B11111110, B00001111, B00001100, //20
  B10000000, B01000000, B00100000,
  B01000000, B00100000, B00010000, //60
  B00100000, B00010000, B00001000,
  B00010000, B00001000, B00000100,
  B00001000, B00000100, B00000010, //120
  B00000100, B00000010, B00000001,
  B00000010, B00000001, B10000000,
  B00000001, B10000000, B01000000, //180
  B10100000, B01010000, B00101000,
  B01010000, B00101000, B00010100,
  B00101000, B00010100, B00001010  //240
};

//We'll move the byte dato to longs in the setup function.
unsigned long relayBits[TIMES_TO_CHECK_RELAYS];

const byte RELAYS_IN_USE = 24;

// The pins are in the opposite order of the on bits.
const byte RELAY_PIN[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25};
const boolean ACTIVE_STATE = LOW;
const boolean INACTIVE_STATE = HIGH;
const byte RELAYS_PER_ROW = 4;

void setup()
{
  Serial.begin(9600);
  for (int relayIndex = 0; relayIndex < RELAYS_IN_USE; relayIndex++)
  {
    digitalWrite(RELAY_PIN[relayIndex], INACTIVE_STATE);
    pinMode(RELAY_PIN[relayIndex], OUTPUT);
  }
  delay(100);
  byte byteIndex = 0;
  for (int intervalIndex = 0; intervalIndex < TIMES_TO_CHECK_RELAYS; intervalIndex++)
  {
//    Serial.print(F("intervalIndex = "));
//    Serial.print(intervalIndex, DEC);
    relayBits[intervalIndex] = 0;
    for (int i = 0; i < 3; i++)
    {
//      Serial.print(F("i = "));
//      Serial.print(i, DEC);
//      Serial.print(F(", byteIndex = "));
//      Serial.print(byteIndex, DEC);
//      Serial.print(F(", RELAYS_ON_BITS_BYTE_TABLE["));
//      Serial.print(byteIndex, DEC);
//      Serial.print(F("] = B"));
//      Serial.println(RELAYS_ON_BITS_BYTE_TABLE[byteIndex], BIN);
      relayBits[intervalIndex] <<= 8;
      relayBits[intervalIndex] |= RELAYS_ON_BITS_BYTE_TABLE[byteIndex++];
//      Serial.print(F(", relayBits["));
//      Serial.print(intervalIndex, DEC);
//      Serial.print(F("] = B"));
//      Serial.println(relayBits[intervalIndex], BIN);
    }
    Serial.print(F("relayBits["));
    Serial.print(intervalIndex, DEC);
    Serial.print(F("] = B"));
    Serial.println(relayBits[intervalIndex], BIN);
  }
  Serial.println();
  Serial.println(F("***** End of Setup *****"));
  Serial.println();
  delay(100);
}

void loop()
{
  for (int intervalIndex; intervalIndex < TIMES_TO_CHECK_RELAYS; intervalIndex++)
  {
    unsigned long bitsToShift = relayBits[intervalIndex];
    Serial.print(F("intervalIndex = "));
    Serial.print(intervalIndex, DEC);
    Serial.print(F(", interval time = "));
    Serial.print(intervalIndex * INTERVAL_BETWEEN_CHANGES / MILLISECONDS_PER_SECOND, DEC);
    Serial.print(F(" seconds, bitsToShift = B"));
    Serial.println(bitsToShift, BIN);
    Serial.print(F("relay states this interval:"));

    for (int relayIndex = 0; relayIndex < RELAYS_IN_USE; relayIndex++)
    {
      if (relayIndex % RELAYS_PER_ROW == 0)
      {
        Serial.println();
      }
      else
      {
        Serial.print(F(", "));
      }
      Serial.print(F("relay #"));
      Serial.print(relayIndex, DEC);
      Serial.print(F(", pin #"));
      Serial.print(RELAY_PIN[relayIndex], DEC);
 
      if (bitsToShift & 1 == 1)
      {
        digitalWrite(RELAY_PIN[relayIndex], ACTIVE_STATE);
        Serial.print(F(" ACTIVE"));
      }
      else
      {
        digitalWrite(RELAY_PIN[relayIndex], INACTIVE_STATE);
        Serial.print(F(" INACTIVE"));
      }
      bitsToShift >>= 1;
    }
    Serial.println();
    Serial.println();
    Serial.print(F("***** Waiting for Next Interval *****"));
    Serial.println();
    Serial.println();
    delay(INTERVAL_BETWEEN_CHANGES);   
  }
  Serial.println();
  Serial.println();
  Serial.print(F("***** End of Loop *****"));
  Serial.println();
  Serial.println();
}

I changed the dummy relay bit values so the “relayBits” weren’t all zeros.

Here’s the output of the first couple intervals.

relayBits[0] = B111111100000111100001100
relayBits[1] = B100000000100000000100000
relayBits[2] = B10000000010000000010000
relayBits[3] = B1000000001000000001000
relayBits[4] = B100000000100000000100
relayBits[5] = B10000000010000000010
relayBits[6] = B1000000001000000001
relayBits[7] = B100000000110000000
relayBits[8] = B11000000001000000
relayBits[9] = B101000000101000000101000
relayBits[10] = B10100000010100000010100
relayBits[11] = B1010000001010000001010

***** End of Setup *****

intervalIndex = 0, interval time = 0 seconds, bitsToShift = B111111100000111100001100
relay states this interval:
relay #0, pin #2 INACTIVE, relay #1, pin #3 INACTIVE, relay #2, pin #4 ACTIVE, relay #3, pin #5 ACTIVE
relay #4, pin #6 INACTIVE, relay #5, pin #7 INACTIVE, relay #6, pin #8 INACTIVE, relay #7, pin #9 INACTIVE
relay #8, pin #10 ACTIVE, relay #9, pin #11 ACTIVE, relay #10, pin #12 ACTIVE, relay #11, pin #13 ACTIVE
relay #12, pin #14 INACTIVE, relay #13, pin #15 INACTIVE, relay #14, pin #16 INACTIVE, relay #15, pin #17 INACTIVE
relay #16, pin #18 INACTIVE, relay #17, pin #19 ACTIVE, relay #18, pin #20 ACTIVE, relay #19, pin #21 ACTIVE
relay #20, pin #22 ACTIVE, relay #21, pin #23 ACTIVE, relay #22, pin #24 ACTIVE, relay #23, pin #25 ACTIVE

***** Waiting for Next Interval *****

intervalIndex = 1, interval time = 20 seconds, bitsToShift = B100000000100000000100000
relay states this interval:
relay #0, pin #2 INACTIVE, relay #1, pin #3 INACTIVE, relay #2, pin #4 INACTIVE, relay #3, pin #5 INACTIVE
relay #4, pin #6 INACTIVE, relay #5, pin #7 ACTIVE, relay #6, pin #8 INACTIVE, relay #7, pin #9 INACTIVE
relay #8, pin #10 INACTIVE, relay #9, pin #11 INACTIVE, relay #10, pin #12 INACTIVE, relay #11, pin #13 INACTIVE
relay #12, pin #14 INACTIVE, relay #13, pin #15 INACTIVE, relay #14, pin #16 ACTIVE, relay #15, pin #17 INACTIVE
relay #16, pin #18 INACTIVE, relay #17, pin #19 INACTIVE, relay #18, pin #20 INACTIVE, relay #19, pin #21 INACTIVE
relay #20, pin #22 INACTIVE, relay #21, pin #23 INACTIVE, relay #22, pin #24 INACTIVE, relay #23, pin #25 ACTIVE

***** Waiting for Next Interval *****