Water Show With Timing

You could just get over it, zero is a perfectly good number and there are reasons that ppl number things 0 through N - 1, unIike normal humans.

When you print anything about it, lie if you must and just add one th whatever it would have said

  Serial.print("launching effect # ");
  Serial.println(theEffect + 1);

Happens alla time, you just never see it!

Or, you could put something in slot 0 and just ignore it. Start your loops at 1, make your arrays one bigger. Not a large deal to waste a tiny bit of memory.

If you want the specific help the rest of your post might be seeking, you should… post the code you are working with, one hopes it functions but does not yet do X or is doing Y what it should not.

a7

I did make a direct connection from the battery to the solenoids, because they do have a hig peak of a milisecond when turning on it will take to much ampere. It is via a relais on the breadboard. so it receives a signal from the solenoid, but the power from the accu.

Burning wires are replacebale and if you are lucky it gives a nice sound effect :wink:

See reply by @alto777.

The Mega does not have 18 analogue pins, only 16 (A0..A15). But you can use these as general purpose digital IO pins (input or output)

Please draw a schematic/wiring diagram of your setup. Every single wire (including power and GND)

Please post your code (please don't forget to use code tags)

One possible reason might be the bootloader on the Mega. While the bootloader is active, all pins are inputs (exception are the Tx pin and pin 13); your relay module might pick up noise and react on that. If your relays are activated when you provide a high signal, you can add pull-down resistors; if your relays are activated when you provide a low signal, you can add pull-up resistors

You can use the random() function to pick a number from 0 to 11 to activate a relay.

No idea, no knowledge about that.

I'm not much of an electronics engineer but charging at the same time might not be healthy. The charging voltage is higher than 12V (or whatever you use) and your solenoids might not like that.


Since i have only tested the first 12 solenoids this is what i got. I have another 16 ch relay for the other 6 solenoids and they are wired the same way but dont know what pins im supposed to use but pretty sure its pins 12-17? Also i know their is no power connected to the arduino but this was working fine, i will connect it directly to the distribution blocks when im done. I will also disconnect the ground wire from the relays to the arduino as they say the ground messes with the arduinos functions? But again this is what i was using at that time.

The code im using is the code you posted above a while back i just need you to tweak it a bit. This code is what i changed it to for a simple test to activate the first 12 solenoids for a few seconds.

// macro to calculate the number of elements in any array
#define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))
// sprinkler will be on if pin is set HIGH
#define SPRINKLER_ON HIGH
// button wired between pin and GND; use INPUT_PULLUP
#define ISPRESSED LOW

// the sprinkler pins
const uint8_t sprinklerPins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};

// a struct holding relevant information
struct SPRINKLER
{
  const uint32_t relStartTime;  // relative start time
  const uint8_t pinIndex;       // index in sprinklerPins array
  uint8_t pinStatus;            // on or off
  bool started;                 // flag indicating that pattern entry is started
  uint32_t absStartTime;        // absolute start time
};

SPRINKLER sprinklerPattern[] = {
  {5000UL, 0, SPRINKLER_ON, false, 0},
  {10000UL, 0, !SPRINKLER_ON, false, 0},
  {10000UL, 1, SPRINKLER_ON, false, 0},
  {15000UL, 1, !SPRINKLER_ON, false, 0},
  {15000UL, 2, SPRINKLER_ON, false, 0},
  {20000UL, 2, !SPRINKLER_ON, false, 0},
  {20000UL, 3, SPRINKLER_ON, false, 0},
  {25000UL, 3, !SPRINKLER_ON, false, 0},
  {25000UL, 4, SPRINKLER_ON, false, 0},
  {30000UL, 4, !SPRINKLER_ON, false, 0},
  {30000UL, 5, SPRINKLER_ON, false, 0},
  {35000UL, 5, !SPRINKLER_ON, false, 0},
  {35000UL, 6, SPRINKLER_ON, false, 0},
  {40000UL, 6, !SPRINKLER_ON, false, 0},
  {40000UL, 7, SPRINKLER_ON, false, 0},
  {45000UL, 7, !SPRINKLER_ON, false, 0},
  {45000UL, 8, SPRINKLER_ON, false, 0},
  {50000UL, 8, !SPRINKLER_ON, false, 0},
  {50000UL, 9, SPRINKLER_ON, false, 0},
  {55000UL, 9, !SPRINKLER_ON, false, 0},
  {55000UL, 10, SPRINKLER_ON, false, 0},
  {60000UL, 10, !SPRINKLER_ON, false, 0},
  {60000UL, 11, SPRINKLER_ON, false, 0},
  {65000UL, 11, !SPRINKLER_ON, false, 0},
};

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Sprinkler Demo"));

  // validate configuration
  if (validateSprinklerPattern() == false)
  {
    // hang forever
    for (;;) {}
  }

  // for debugging
  printSprinklerPins();
  printSprinklerPattern();

  // set sprinkler pins to output
  for (uint8_t cnt = 0; cnt < NUMELEMENTS(sprinklerPins); cnt++)
  {
    Serial.print(F("Setting sprinkler pin "));
    Serial.print(sprinklerPins[cnt]);
    Serial.println(F(" to output"));
    digitalWrite(sprinklerPins[cnt], !SPRINKLER_ON);
    pinMode(sprinklerPins[cnt], OUTPUT);
  }

  //delay(3000);
  // reset the pattern
  resetSprinklerPattern();
}

void loop()
{
  // loop through the patterns
  for (uint16_t patternIndex = 0; patternIndex < NUMELEMENTS(sprinklerPattern); patternIndex++)
  {
    // if the pattern entry was not started
    if (sprinklerPattern[patternIndex].started == false)
    {
      // if it is time
      if (millis() >= sprinklerPattern[patternIndex].absStartTime)
      {
        // set the output
        digitalWrite(sprinklerPins[sprinklerPattern[patternIndex].pinIndex], sprinklerPattern[patternIndex].pinStatus);
        // update 'started' status
        sprinklerPattern[patternIndex].started = true;
        // debugging
        Serial.print(F("Sprinkler pin "));
        Serial.print(sprinklerPins[sprinklerPattern[patternIndex].pinIndex]);
        Serial.print(" set to ");
        Serial.print(sprinklerPattern[patternIndex].pinStatus == SPRINKLER_ON ? "ON" : "OFF");
        Serial.print(F(" at "));
        Serial.println(millis());
      }
    }
  }
}

/*
  display information about the sprinkler pins
*/
void printSprinklerPins()
{
  Serial.print(F("There are "));
  Serial.print(NUMELEMENTS(sprinklerPins));
  Serial.println(F(" sprinklers"));
  for (uint8_t cnt = 0; cnt < NUMELEMENTS(sprinklerPins); cnt++)
  {
    Serial.print(F("Sprinkler "));
    Serial.print(cnt);
    Serial.print(F(" on pin "));
    Serial.println(sprinklerPins[cnt]);
  }
}

/*
  display information about the pattern entries
*/
void printSprinklerPattern()
{
  Serial.print(F("The pattern contains "));
  Serial.print(NUMELEMENTS(sprinklerPattern));
  Serial.println(F(" entries"));
  for (uint16_t cnt = 0; cnt < NUMELEMENTS(sprinklerPattern); cnt++)
  {
    Serial.print(F("Entry "));
    Serial.print(cnt);
    Serial.print(F(",  pin "));
    Serial.print(sprinklerPins[sprinklerPattern[cnt].pinIndex]);
    Serial.print(F(" will change at "));
    Serial.print(sprinklerPattern[cnt].relStartTime);
    Serial.print(F(" to "));
    Serial.println(sprinklerPattern[cnt].pinStatus == SPRINKLER_ON ? "ON" : "OFF");
  }
}

/*
  validate sprinkler pattern pin indices
  Returs:
    false on error, else true
*/
bool validateSprinklerPattern()
{
  bool isValid = true;
  for (uint16_t cnt = 0; cnt < NUMELEMENTS(sprinklerPattern); cnt++)
  {
    if(sprinklerPattern[cnt].pinIndex >= NUMELEMENTS(sprinklerPins))
    {
      Serial.print(F("Invalid pinIndex for pattern["));
      Serial.print(cnt);
      Serial.println(F("]"));
      isValid = false;
    }
  }
  
  return isValid;
}

/*
  reset sprinkler pattern variables
*/
void resetSprinklerPattern()
{
  // hold the current time
  uint32_t startTime = millis();
  // loop through all entries in sprinklerPattern
  for (uint16_t cnt = 0; cnt < NUMELEMENTS(sprinklerPattern); cnt++)
  {
    // clear the flag
    sprinklerPattern[cnt].started = false;
    // set a new absolute start time
    sprinklerPattern[cnt].absStartTime = startTime + sprinklerPattern[cnt].relStartTime;
  }
}

You said their arent that many analog pins on this mega to work with and your right i just looked at this mega and the digital pins actually go to 53 so i guess we dont need to mess with that. But im also confused because the pins im using is 0-11 and can actually go to 13. then 14-21 are communication pins and then the rest say digital. So can i use pins 14-21 with out changing the code? Like i said im still new to arduino.

For the resistors do i really need them? they are only being activated for a short time when the arduino is connected and then it runs smooth.

For the random activation of the first 12 solenoids can you add that to the script please?

In order for this show to work i need background music and thats the only way i can get the music to play at the same time as the code. Unless you know another way to play a large .way audio file? The only other way i know is using vixen and arduino together, but im still trying to figure that out. That is actually the easiest route as i can time music and solenoids from that program. I just need coding for it. You mess with vixen? Other than that, i need to code that audio player with arduino.

For the battery charging its only charging the battery and shouldn't mess with the solenoids? The charger is running off the outlet from the house. Ive been testing it and i have had no issues. The reason i need a battery is that i will have audio stuff connected to the battery as well.

So to recap. I have a flip switch button, please code that into the script. When its off/flipped down and the arduino is still on, it plays the random code for the first 12 solenoids/pins. When its on/flipped up it plays the script for the show. Is that something you can tweak for me? Thanks.

So you were not using USB with this setup to power the Mega? If so, the Mega is powered from the relay shield. And you will need the GND between the shield and the Mega.

Or did I misunderstand?

Based on the schematic you seem to be using pins 0 and 1; note that use of those pins can interfere with uploads and serial receive/print will have side effects on your solenoids.

Do you have a link to (preferably datasheet) of the relay shield?

Yeah i didnt connect the usb. I will connect the power when i continue testing today maybe. And should i start at pins 2 and go upward to 19?

Not sure what you mean relay shield but this is the link on amazon where i got it.

Quick glance at your code shows that a sprinkler is on if you make the pin HIGH.

You can do some testing to determine the cause of the 5 seconds that the sprinklers turn on.

Keep the schematic as is but disconnect one of the wires at the Mega side (e.g. pin 12) and connect it to GND; if the problem for that specific sprinkler disappears (it does not switch on at startup) I suggest that you use pull-down resisters to get rid of the problem.

So you actually want me to connect my relay pin to the mega ground? Welp lets see how that goes. But to be honest the arduino switches the relays lights for 5 seconds anyways, i heard that thats how long it takes for the arduino to set up at start up. But i will try the ground thing and maybe test with a resistor to see if that helps too. But for now can you code the other things please?

Or the GND on the relay board.

This is the schematic for the relay board for those that are interested

16 relay 2.pdf (55.1 KB)

Add the following just before the setup() function.

/*
  for random sprinkler
*/
uint8_t pinButton = 99;              // button pin to switch between normal pattern and random pattern
const uint32_t interval = 180000UL;  // every 3 minutes
const uint32_t duration = 3000;      // on for 3 seconds

Change the button pin to the pin that you want to use; it needs to be connected between pin and GND.

Add the following to setup()

  pinMode(pinButton, INPUT_PULLUP);

The below code will activate a random sprinkler every 3 minutes (plus 3 seconds) for 3 seconds.

enum RANDOMSTATES
{
  WAIT,
  RUN,
};

void runRandom()
{
  //Serial.println("runRandom()");
  static uint32_t startTime;
  static uint8_t pin = 255;
  static RANDOMSTATES currentState = WAIT;

  switch (currentState)
  {
    case WAIT:
      if (millis() - startTime >= interval)
      {
        // pick a random sprinkler
        uint8_t randomNumber = random(NUMELEMENTS(sprinklerPins));
        pin = sprinklerPins[randomNumber];

        // switch selected sprintkler on
        digitalWrite(pin, SPRINKLER_ON);
        // remember the start time
        startTime = millis();
        // go to next state
        currentState = RUN;
        // some debug
        Serial.print(F("Pin "));
        Serial.print(pin);
        Serial.println(F(" switched on"));
      }
      break;
    case RUN:
      if (millis() - startTime >= duration)
      {
        // switch selected sprintkler off
        digitalWrite(pin, !SPRINKLER_ON);
        // remember the start time
        startTime = millis();
        // go to next state
        currentState = WAIT;
        // some debug
        Serial.print(F("Pin "));
        Serial.print(pin);
        Serial.println(F(" switched off"));
      }

      break;
  }
}

It makes use of a so-called finite state machine (FSM). There are two states; in the first state (WAIT) the function waits till 3 minutes are passed.

  1. It will then pick a random number that acts as the index into the sprinklerPins array to select a sprinkler.
  2. It will activate the selected sprinkler.
  3. It will remember the time that the sprinkler was activated.
  4. It will switch to the RUN state.

In the run state, there is a check if the 3 seconds have passed. If so

  1. The sprinkler will be switched off.
  2. The time that this happened will be remembered as the start time for the next 3 minute wait time.
  3. The state will be switch to the next state (which in this case is the first state, WAIT).

Process repeats.

You need to call this function continuously from loop(); e.g.

void loop()
{
  runRandom();
}

Note that this just runs a random sequence; button is not implemented yet, it might depend on your needs.
Do you have a button or are you using a switch to select between running of the pattern and the running of the random sprinkler.

Do you want to run the random sprinkler once or start the random sprinkler again after the pattern is finished?

Do you have a user manual for DY-HV20T 12V/24V Power Supply10W/20W Voice Playback Module Supporting Micro-SD Card MP3? I have no experience with it so I do not know if I can help you with that.

While I was typing the above, you provided the link to the relay board. I did extract the schematic from the manual (posted in post #32) but did not have a close look.

As far as I understand it, you can power the Mega from the 5V from the relay board; as said, you will need the ground connection between the relay board and the Mega, else you don't have a closed circuit for it. Be aware that if you power the Mega from the 5V of the relay board, you should not connect the Mega to your PC; something might give in.

Next the schematic indicates that the relays are activated with a LOW signal; if you don't connect the inputs, the LEDs should be off. A non-connected input INx is the same as a connected input to a pin of the Mega while the Mega is in bootloader mode or if the Mega is running an empty sketch.

I think that your 5 second problem comes from the fact that you, by the looks of it, have wired your solenoids to the wrong terminal of the screw connector.

Each relay has three contacts, COMMON, NC and NO.

  • NC stands for normally connected; if the relay is not activated and the solenoid is connected to NC, you are actually activating the solenoid.
  • NO stand for normally open; if the relay is not activated the solenoid will not get power.
  • COMMON is the centre contact in the group of three.

If I'm right, you will need to move the GND of the battery to the screw terminal in each block of three that is currently not used.

You will also need to change (in the code)

// sprinkler will be on if pin is set HIGH
#define SPRINKLER_ON HIGH

to

// sprinkler will be on if pin is set LOW
#define SPRINKLER_ON LOW

Note
COMMON is usually connected to the the live wire (in your case that would be 12V) and not to GND.

The arduino will be on the whole time, the button is just to switch between the random sprinkler activation and the show. So when the button is off/flipped down it will just play the random sprinkler event and it will continuously keep choosing a random sprinkler every 3 minutes till i flip the switch up/on and that will play the show code and activate the sound card to play the .wav file for the show lets just call that file "SHOW.wav". Then when the show is over i just flip the switch down and itl go back to the random sprinkler code and of course the music should cut off too. And if i want to play the show again i can do that by flipping the switch up again. And sorry no their is no schematics on the sound card that i know of.

Ill just be using a basic flip switch for this.

So my wiring is wrong? why does it work then lol? In this picture which one is right? Left or right?

Yes and no. It is correct in the sense that it will work, it really doesn't matter.

It is wrong because it is unconventional. And if you have N relays, they all ought also to be wired identicammaly, just for sanity.

It's like using red wire for positive and black for negative.

@sterretje is aware of a convention I had not known, or used.

I used chatGPT, interesting conversation, the last exchange of which was

Me:

ok, so can we say by convention current is arranged so as to flow from COM to NO no matter it be on the high side or low side of the load?

and ChatGPT:

Yes, exactly! You can say that by convention, in both high-side (COM connected to positive) and low-side (COM connected to the load, which connects to positive) configurations, current is arranged to flow from the Common (COM) terminal to the Normally Open (NO) terminal when the relay is energized.

and I would appreciate any confirmation of this result, as I always feel like I have exceeded chatGPT's patience (even though it complimented me on mine) and that it just starts agreeing with me so I go away.

a7

A suggestion for next drawing: use parallel lines for connecting wires.

Well i just need it to be wired in a way where the solenoids arent drawing current when they are not being activated by the code and draining the battery. Not sure what happened today but my battery got super hot, not sure if it was a bad battery but i will have to test again on saturday with a different one. None of my cables were hot so i think it might have just been the battery.

Got it.

This is from the schematic

image

I can not say what is what on your board; I'm used to relay boards where pin 2 on Jx is COMMON but looking at the schematic this board is different. But I would connect pin 1 in the schematic to the 12V.

If the board is powered but the input pins (that you connect to the Mega) are not connected, there will be a connection between either 1 and 2 or between 1 and 3; the pin that is now connected is the NC pin. The LED of all relays should be off.

If you have a multimeter, you can measure the resistance between the pins on e.g. J1; the board can be powered on while doing that.

Next you can connect one of the input pins to the GND on the relay board; the LED associated with that pin should turn on and the connection from pin 1 should switch. So if without an input pin connected the connection is between 1 and 2, the connection will change to 1 and 3. In which case you need to connect your solenoid to pin 3.

To prevent your 5 second problem when the Mega starts up, you need to connect the relay to the pin that is normally not connected (NO); in the above description pin 3.

In short, you need to connect the solenoid in such a way that of the relay board is not connected to the Mega, the solenoid is not activated and if you short an INx pin, the associated relay/solenoid is activated.

I will give you an example how to implement the random functionality.

The first step is to move the current content of your loop to a function like below. The main reason is to keep your loop() clean so the program is easier to follow.

void runShow()
{
  // loop through the patterns
  for (uint16_t patternIndex = 0; patternIndex < NUMELEMENTS(sprinklerPattern); patternIndex++)
  {
    // if the pattern entry was not started
    if (sprinklerPattern[patternIndex].started == false)
    {
      // if it is time
      if (millis() >= sprinklerPattern[patternIndex].absStartTime)
      {
        // set the output
        digitalWrite(sprinklerPins[sprinklerPattern[patternIndex].pinIndex], sprinklerPattern[patternIndex].pinStatus);
        // update 'started' status
        sprinklerPattern[patternIndex].started = true;
        // debugging
        Serial.print(F("Sprinkler pin "));
        Serial.print(sprinklerPins[sprinklerPattern[patternIndex].pinIndex]);
        Serial.print(" set to ");
        Serial.print(sprinklerPattern[patternIndex].pinStatus == SPRINKLER_ON ? "ON" : "OFF");
        Serial.print(F(" at "));
        Serial.println(millis());
      }
    }
  }
}

And call that function from loop().

void loop()
{
  runShow();
}

This function needs a modification so it can tell loop() when the pattern is finished. If you don't do that, the show will go on forever.

/*
  run the show
  Returns:
    true if the show is completed, else false
*/
bool runShow()
{
  // loop through the patterns
  for (uint16_t patternIndex = 0; patternIndex < NUMELEMENTS(sprinklerPattern); patternIndex++)
  {
    // if the pattern entry was not started
    if (sprinklerPattern[patternIndex].started == false)
    {
      // if it is time
      if (millis() >= sprinklerPattern[patternIndex].absStartTime)
      {
        // set the output
        digitalWrite(sprinklerPins[sprinklerPattern[patternIndex].pinIndex], sprinklerPattern[patternIndex].pinStatus);
        // update 'started' status
        sprinklerPattern[patternIndex].started = true;
        // debugging
        Serial.print(F("[show]Sprinkler pin "));
        Serial.print(sprinklerPins[sprinklerPattern[patternIndex].pinIndex]);
        Serial.print(" set to ");
        Serial.print(sprinklerPattern[patternIndex].pinStatus == SPRINKLER_ON ? "ON" : "OFF");
        Serial.print(F(" at "));
        Serial.println(millis());
      }
    }
  }

  // the show is completed if each step (element) in sprinklerPattern is 'started
  // assume it's completed
  bool isCompleted = true;
  for (uint16_t patternIndex = 0; patternIndex < NUMELEMENTS(sprinklerPattern); patternIndex++)
  {
	// if step was not done yet
    if (sprinklerPattern[patternIndex].started == false)
    {
	  // clear the flag
      isCompleted = false;
    }
  }

  return isCompleted;
}

The function now returns a boolean variable which will be false if the show is not completed yet and true if the show is completed. Compared to the previous function, there is an additional for-loop at the end to check if all elements of the sprinklerPattern are processed (started).

For demonstration (it's not what you want but to demonstrate)

void loop()
{
  static bool showCompleted = false;
  
  if (showCompleted == false)
  {
    showCompleted = runShow();
  }
}

The above will run the show only once.

So it's time to implement the switch. You will need to wire the switch between a pin and GND; the below code uses pin 2 and you need to change it to a pin that suites you and in setup() use INPUT_PULLUP. Note that I had to remove pin 2 from the sprinklerPins (and pin 1 was already removed). For that reason I also had to shorten the sprinklerPattern as well.

Below the updated version of the first part of the code (up to loop()).

// macro to calculate the number of elements in any array
#define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))
// sprinkler will be on if pin is set LOW
#define SPRINKLER_ON LOW
// button wired between pin and GND; use INPUT_PULLUP
#define SWITCH_ON LOW

// the sprinkler pins
//const uint8_t sprinklerPins[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
const uint8_t sprinklerPins[] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };

// a struct holding relevant information
struct SPRINKLER
{
  const uint32_t relStartTime;  // relative start time
  const uint8_t pinIndex;       // index in sprinklerPins array
  uint8_t pinStatus;            // on or off
  bool started;                 // flag indicating that pattern entry is started
  uint32_t absStartTime;        // absolute start time
};

SPRINKLER sprinklerPattern[] = {
  { 5000UL, 0, SPRINKLER_ON, false, 0 },
  { 7000UL, 1, SPRINKLER_ON, false, 0 },
  { 10000UL, 0, !SPRINKLER_ON, false, 0 },
  { 15000UL, 1, !SPRINKLER_ON, false, 0 },
  { 15000UL, 2, SPRINKLER_ON, false, 0 },
  { 20000UL, 2, !SPRINKLER_ON, false, 0 },
  { 20000UL, 3, SPRINKLER_ON, false, 0 },
  { 25000UL, 3, !SPRINKLER_ON, false, 0 },
  { 25000UL, 4, SPRINKLER_ON, false, 0 },
  { 30000UL, 4, !SPRINKLER_ON, false, 0 },
  { 30000UL, 5, SPRINKLER_ON, false, 0 },
  { 35000UL, 5, !SPRINKLER_ON, false, 0 },
  { 35000UL, 6, SPRINKLER_ON, false, 0 },
  { 40000UL, 6, !SPRINKLER_ON, false, 0 },
  { 40000UL, 7, SPRINKLER_ON, false, 0 },
  { 45000UL, 7, !SPRINKLER_ON, false, 0 },
  { 45000UL, 8, SPRINKLER_ON, false, 0 },
  { 50000UL, 8, !SPRINKLER_ON, false, 0 },
  { 50000UL, 9, SPRINKLER_ON, false, 0 },
  { 55000UL, 9, !SPRINKLER_ON, false, 0 },
  //{ 55000UL, 10, SPRINKLER_ON, false, 0 },
  //{ 60000UL, 10, !SPRINKLER_ON, false, 0 },
  //{ 60000UL, 11, SPRINKLER_ON, false, 0 },
  //{ 65000UL, 11, !SPRINKLER_ON, false, 0 },
};

/*
  for random sprinkler
*/
uint8_t pinSwitch = 2;               // switch pin to switch between normal pattern and random pattern
const uint32_t interval = 180000UL;  // every 3 minutes
const uint32_t duration = 3000;      // on for 3 seconds

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Sprinkler Demo"));

  pinMode(pinSwitch, INPUT_PULLUP);

  // validate configuration
  if (validateSprinklerPattern() == false)
  {
    // hang forever
    for (;;) {}
  }

  // for debugging
  //printSprinklerPins();
  //printSprinklerPattern();

  // set sprinkler pins to output
  for (uint8_t cnt = 0; cnt < NUMELEMENTS(sprinklerPins); cnt++)
  {
    //Serial.print(F("Setting sprinkler pin "));
    //Serial.print(sprinklerPins[cnt]);
    //Serial.println(F(" to output"));
    digitalWrite(sprinklerPins[cnt], !SPRINKLER_ON);
    pinMode(sprinklerPins[cnt], OUTPUT);
  }

  // reset the pattern
  resetSprinklerPattern();
}

In loop() we first declare two static variables. Static variables are only known inside the function but will be remembered when the function (loop() in this case) finishes.

void loop()
{
  // keep track of the last position of the switch
  static uint8_t lastSwitchState = !SWITCH_ON;
  // keep track if the show is completed
  static bool showCompleted = false;
  
  ...
  ...

Next we read the switch

  uint8_t switchState = digitalRead(pinSwitch);

Because switches and buttons might bounce a short delay is implemented when if the switch goes from on to off or vice versa

  // if the switch state changed
  if (lastSwitchState != switchState)
  {
    // a crude debounce timing
    Serial.println(F("Debounce"));
    delay(50);
  }

If the switch is off, we run the random pattern. We also set showCompleted to false so if you flip the switch runShow() will be started (again).

  // if the switch is off
  if (switchState == !SWITCH_ON)
  {
    // indicate that a show is no longer completed
    showCompleted = false;
    // run a random pattern
    runRandom();
  }

If the switch is on, we check if the switch changed and reset the sprinkler pattern variables to initial states.

  else
  {
    // if the switch changed (from off to on)
    if (lastSwitchState != switchState)
    {
      // reset timing and output state of sprinkler pins
      resetSprinklerPattern();
    }
	
	...
	...

Next we check if the show is completed; if not, we run the show.

    // if the show is not completed
    if (showCompleted == false)
    {
      // run the show
      showCompleted = runShow();
      if (showCompleted == true)
      {
        Serial.println(F("Show completed"));
        // reset the output state of the sprinkler pins
        resetSprinklerPattern();
      }
    }

The last part here is the call to resetSprinklerPatten() to make sure that when you throw the switch to off all solenoids are deactivated for starters.

And lastly we remember the state of the switch

  // remember the last state of the switch
  lastSwitchState = switchState;
}

Full code below

// macro to calculate the number of elements in any array
#define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))
// sprinkler will be on if pin is set LOW
#define SPRINKLER_ON LOW
// button wired between pin and GND; use INPUT_PULLUP
#define SWITCH_ON LOW

// the sprinkler pins
//const uint8_t sprinklerPins[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
const uint8_t sprinklerPins[] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };

// a struct holding relevant information
struct SPRINKLER
{
  const uint32_t relStartTime;  // relative start time
  const uint8_t pinIndex;       // index in sprinklerPins array
  uint8_t pinStatus;            // on or off
  bool started;                 // flag indicating that pattern entry is started
  uint32_t absStartTime;        // absolute start time
};

SPRINKLER sprinklerPattern[] = {
  { 5000UL, 0, SPRINKLER_ON, false, 0 },
  { 7000UL, 1, SPRINKLER_ON, false, 0 },
  { 10000UL, 0, !SPRINKLER_ON, false, 0 },
  { 15000UL, 1, !SPRINKLER_ON, false, 0 },
  { 15000UL, 2, SPRINKLER_ON, false, 0 },
  { 20000UL, 2, !SPRINKLER_ON, false, 0 },
  { 20000UL, 3, SPRINKLER_ON, false, 0 },
  { 25000UL, 3, !SPRINKLER_ON, false, 0 },
  { 25000UL, 4, SPRINKLER_ON, false, 0 },
  { 30000UL, 4, !SPRINKLER_ON, false, 0 },
  { 30000UL, 5, SPRINKLER_ON, false, 0 },
  { 35000UL, 5, !SPRINKLER_ON, false, 0 },
  { 35000UL, 6, SPRINKLER_ON, false, 0 },
  { 40000UL, 6, !SPRINKLER_ON, false, 0 },
  { 40000UL, 7, SPRINKLER_ON, false, 0 },
  { 45000UL, 7, !SPRINKLER_ON, false, 0 },
  { 45000UL, 8, SPRINKLER_ON, false, 0 },
  { 50000UL, 8, !SPRINKLER_ON, false, 0 },
  { 50000UL, 9, SPRINKLER_ON, false, 0 },
  { 55000UL, 9, !SPRINKLER_ON, false, 0 },
  //{ 55000UL, 10, SPRINKLER_ON, false, 0 },
  //{ 60000UL, 10, !SPRINKLER_ON, false, 0 },
  //{ 60000UL, 11, SPRINKLER_ON, false, 0 },
  //{ 65000UL, 11, !SPRINKLER_ON, false, 0 },
};

/*
  for random sprinkler
*/
uint8_t pinSwitch = 2;             // switch pin to switch between normal pattern and random pattern
const uint32_t interval = 3000UL;  // every 3 minutes
const uint32_t duration = 3000;    // on for 3 seconds

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Sprinkler Demo"));

  pinMode(pinSwitch, INPUT_PULLUP);

  // validate configuration
  if (validateSprinklerPattern() == false)
  {
    // hang forever
    for (;;) {}
  }

  // for debugging
  //printSprinklerPins();
  //printSprinklerPattern();

  // set sprinkler pins to output
  for (uint8_t cnt = 0; cnt < NUMELEMENTS(sprinklerPins); cnt++)
  {
    //Serial.print(F("Setting sprinkler pin "));
    //Serial.print(sprinklerPins[cnt]);
    //Serial.println(F(" to output"));
    digitalWrite(sprinklerPins[cnt], !SPRINKLER_ON);
    pinMode(sprinklerPins[cnt], OUTPUT);
  }

  //delay(3000);
  // reset the pattern
  resetSprinklerPattern();
}

void loop()
{
  // keep track of the last position of the switch
  static uint8_t lastSwitchState = !SWITCH_ON;
  // keep track if the show is completed
  static bool showCompleted = false;

  // check the switch
  uint8_t switchState = digitalRead(pinSwitch);

  // if the switch state changed
  if (lastSwitchState != switchState)
  {
    // a crude debounce timing
    Serial.println(F("Debounce"));
    delay(50);
  }

  // if the switch is off
  if (switchState == !SWITCH_ON)
  {
    // indicate that a show is no longer completed
    showCompleted = false;
    // run a random pattern
    runRandom();
  }
  else
  {
    // if the switch changed (from off to on)
    if (lastSwitchState != switchState)
    {
      // reset timing and output state of sprinkler pins
      resetSprinklerPattern();
    }
    // if the show is not completed
    if (showCompleted == false)
    {
      // run the show
      showCompleted = runShow();
      if (showCompleted == true)
      {
        Serial.println(F("Show completed"));
        // reset the output state of the sprinkler pins
        resetSprinklerPattern();
      }
    }
  }

  // remember the last state of the switch
  lastSwitchState = switchState;
}

enum RANDOMSTATES
{
  WAIT,
  RUN,
};

void runRandom()
{
  static uint32_t startTime;
  static uint8_t pin = 255;
  static RANDOMSTATES currentState = WAIT;

  switch (currentState)
  {
    case WAIT:
      if (millis() - startTime >= interval)
      {
        // pick a random sprinkler
        uint8_t randomNumber = random(NUMELEMENTS(sprinklerPins));
        pin = sprinklerPins[randomNumber];

        // switch selected sprintkler on
        digitalWrite(pin, SPRINKLER_ON);
        // remember the start time
        startTime = millis();
        // go to next state
        currentState = RUN;
        // some debug
        Serial.print(F("[random]Pin "));
        Serial.print(pin);
        Serial.println(F(" switched on"));
      }
      break;
    case RUN:
      if (millis() - startTime >= duration)
      {
        // switch selected sprintkler off
        digitalWrite(pin, !SPRINKLER_ON);
        // remember the start time
        startTime = millis();
        // go to next state
        currentState = WAIT;
        // some debug
        Serial.print(F("Pin "));
        Serial.print(pin);
        Serial.println(F(" switched off"));
      }

      break;
  }
}

/*
  run the show
  Returns:
    true if the show is completed, else false
*/
bool runShow()
{
  // loop through the patterns
  for (uint16_t patternIndex = 0; patternIndex < NUMELEMENTS(sprinklerPattern); patternIndex++)
  {
    // if the pattern entry was not started
    if (sprinklerPattern[patternIndex].started == false)
    {
      // if it is time
      if (millis() >= sprinklerPattern[patternIndex].absStartTime)
      {
        // set the output
        digitalWrite(sprinklerPins[sprinklerPattern[patternIndex].pinIndex], sprinklerPattern[patternIndex].pinStatus);
        // update 'started' status
        sprinklerPattern[patternIndex].started = true;
        // debugging
        Serial.print(F("[show]Sprinkler pin "));
        Serial.print(sprinklerPins[sprinklerPattern[patternIndex].pinIndex]);
        Serial.print(" set to ");
        Serial.print(sprinklerPattern[patternIndex].pinStatus == SPRINKLER_ON ? "ON" : "OFF");
        Serial.print(F(" at "));
        Serial.println(millis());
      }
    }
  }

  // the show is completed if each step (element) in sprinklerPattern is 'started
  // assume it's completed
  for (uint16_t patternIndex = 0; patternIndex < NUMELEMENTS(sprinklerPattern); patternIndex++)
  {
    // if step was not started
    if (sprinklerPattern[patternIndex].started == false)
    {
      isCompleted = false;
    }
  }

  return isCompleted;
}

/*
  display information about the sprinkler pins
*/
void printSprinklerPins()
{
  Serial.print(F("There are "));
  Serial.print(NUMELEMENTS(sprinklerPins));
  Serial.println(F(" sprinklers"));
  for (uint8_t cnt = 0; cnt < NUMELEMENTS(sprinklerPins); cnt++)
  {
    Serial.print(F("Sprinkler "));
    Serial.print(cnt);
    Serial.print(F(" on pin "));
    Serial.println(sprinklerPins[cnt]);
  }
}

/*
  display information about the pattern entries
*/
void printSprinklerPattern()
{
  Serial.print(F("The pattern contains "));
  Serial.print(NUMELEMENTS(sprinklerPattern));
  Serial.println(F(" entries"));
  for (uint16_t cnt = 0; cnt < NUMELEMENTS(sprinklerPattern); cnt++)
  {
    Serial.print(F("Entry "));
    Serial.print(cnt);
    Serial.print(F(",  pin "));
    Serial.print(sprinklerPins[sprinklerPattern[cnt].pinIndex]);
    Serial.print(F(" will change at "));
    Serial.print(sprinklerPattern[cnt].relStartTime);
    Serial.print(F(" to "));
    Serial.println(sprinklerPattern[cnt].pinStatus == SPRINKLER_ON ? "ON" : "OFF");
  }
}

/*
  validate sprinkler pattern pin indices
  Returs:
    false on error, else true
*/
bool validateSprinklerPattern()
{
  bool isValid = true;
  for (uint16_t cnt = 0; cnt < NUMELEMENTS(sprinklerPattern); cnt++)
  {
    if (sprinklerPattern[cnt].pinIndex >= NUMELEMENTS(sprinklerPins))
    {
      Serial.print(F("Invalid pinIndex for pattern["));
      Serial.print(cnt);
      Serial.println(F("]"));
      isValid = false;
    }
  }

  return isValid;
}

/*
  reset sprinkler pattern variables and deactivate solenoids
*/
void resetSprinklerPattern()
{
  // hold the current time
  uint32_t startTime = millis();
  Serial.print(F("resetSprinklerPattern; time = "));
  Serial.println(startTime);
  // loop through all entries in sprinklerPattern
  for (uint16_t cnt = 0; cnt < NUMELEMENTS(sprinklerPattern); cnt++)
  {
    // clear the flag
    sprinklerPattern[cnt].started = false;
    // set a new absolute start time
    sprinklerPattern[cnt].absStartTime = startTime + sprinklerPattern[cnt].relStartTime;
    // make sure the outputs are off
    digitalWrite(sprinklerPins[sprinklerPattern[cnt].pinIndex], !SPRINKLER_ON);
  }
}

Notes:

  1. I've modiefied the sprinklerPattern slightly in the beginning to demonstrate that you can have two sprinklers on at the same time; in this case they partially overlap.
  2. Once the show is over, it will not be repeated and the code will not automatically fall back to a random pattern; you have to put the switch in the off position to run the random functionality.

Request:
Please do not write long pieces of text; seperate into paragraphs where needed as it makes it a lot easier to read and to find specifics back.