Replacing Delay with Millis on my solenoid function

Hello, for sometime I've been trying to understand how to use millis. I've been to many sites, read forums, and gone in circles trying to figure out how to use millis in a way that one part of my code runs and finishes before another begins. I have a garden project that uses 10 hygrometers, 10 solenoids, 3 DHT22 sensors, and a handful of other types of sensors connected to a Mega 2560. I have succeeded in using millis to call certain functions at different times.Right now I'm just wanting to remove the delay from my Solenoid function and replace it with millis. I am using Serialprint just to be able to view the steps of my code as they run. I would like the code and the output to run/look like....

Now Testing Solenoids

Watering Section 1 // turns solenoid 1 on for 5 seconds
Section 1 is OFF // turns solenoid 1 off
wait before moving to next solenoid //Waits 2 seconds before going to the next solenoid

Watering Section 2 // turns solenoid 2 on for 5 seconds
Section 2 is OFF // turns solenoid 2 off
wait before moving to next solenoid //Waits 2 seconds before going to the next solenoid

Watering Section 3 // turns solenoid 3 on for 5 seconds
Section 3 is OFF // turns solenoid 3 off
wait before moving to next solenoid //Waits 2 seconds before going to the next solenoid

.....and so on.....

void Solenoid()
{
  Serial.println ("Now Testing Solenoids");
  unsigned long previousMillis10 = 0; // millis() returns an unsigned long.( )
  unsigned long interval10 = 5000 ; //  = 5 sec ( )
  int period = 2000;
  int period2 = 5000;
  unsigned long time_now = 0;
  unsigned long currentMillis = millis(); // grab current time

  if (Value1 > 300)
  {
    if (millis() > time_now + period)
    {
      digitalWrite(solenoidRelay1, LOW);  //  turns on
      Serial.println ("Watering Section 1");
    }
    if (millis() > period2) // check if "interval10" time has passed (  5 sec )
    {
      digitalWrite(solenoidRelay1, HIGH); //  turns off
      Serial.println (" Section 1 is OFF");
    }
    if (millis() > period2 && digitalWrite == HIGH) {
      Serial.println("wait before moving to next solenoid");
    }
  }
  if (Value2 > 300)
  {
    if (millis() > time_now + period)
    {
      digitalWrite(solenoidRelay2, LOW);  //  turns on
      Serial.println ("Watering Section 2");
    }
    if (millis() > time_now + period2) // check if "interval10" time has passed (  5 sec )
          {
      digitalWrite(solenoidRelay2, HIGH); //  turns off
      Serial.println (" Section 2 is OFF");
    }
    if (millis() > time_now + period) {
      Serial.println("wait before moving to next solenoid");
    }
  }
  if (Value3 > 300)
  {
    if (millis() > time_now + period)
    {
      digitalWrite(solenoidRelay3, LOW);  //  turns on
      Serial.println ("Watering Section 3");
    }
    if (millis() > time_now >= interval10) // check if "interval10" time has passed (  5 sec )
    {
      digitalWrite(solenoidRelay3, HIGH); //  turns off
      Serial.println (" Section 3 is OFF");
    }
    if (millis() > time_now + period) {
      Serial.println("wait before moving to next solenoid");
    }
  }
  if (Value4 > 300)
  {
    if (millis() > time_now + period)
    {
      digitalWrite(solenoidRelay4, LOW);  //  turns on
      Serial.println ("Watering Section 4");
    }
    if (millis() > time_now >= interval10) // check if "interval10" time has passed (  5 sec )
    {
      digitalWrite(solenoidRelay4, HIGH); //  turns off
      Serial.println (" Section 4 is OFF");
    }
    if (millis() > time_now + period) {
      Serial.println("wait before moving to next solenoid");
    }
  }
  if (Value5 > 300)
  {
    if (millis() > time_now + period)
    {
      digitalWrite(solenoidRelay5, LOW);  //  turns on
      Serial.println ("Watering Section 5");
    }
    if (millis() > time_now >= interval10) // check if "interval10" time has passed (  5 sec )
      //if ((unsigned long)(currentMillis - time_now) >= interval10) // check if "interval10" time has passed (  5 sec )
    {
      digitalWrite(solenoidRelay5, HIGH); //  turns off
      Serial.println (" Section 5 is OFF");
    }
    if (millis() > time_now + period) {

      Serial.println("wait before moving to next solenoid");
    }
  }
  if (Value6 > 300)
  {
    if (millis() > time_now + period)
    {
      digitalWrite(solenoidRelay6, LOW);  //  turns on
      Serial.println ("Watering Section 6");
    }
    if (millis() > time_now >= interval10) // check if "interval10" time has passed (  5 sec )
    {
      digitalWrite(solenoidRelay6, HIGH); //  turns off
      Serial.println (" Section 6 is OFF");
    }
    if (millis() > time_now + period) {

      Serial.println("wait before moving to next solenoid");
    }
  }
  if (Value7 > 300)
  {
    if (millis() > time_now + period)
    {
      digitalWrite(solenoidRelay7, LOW);  //  turns on
      Serial.println ("Watering Section 7");
    }
    if (millis() > time_now >= interval10) // check if "interval10" time has passed (  5 sec )
    {
      digitalWrite(solenoidRelay7, HIGH); //  turns off
      Serial.println (" Section 7 is OFF");
    }
    if (millis() > time_now + period) {

      Serial.println("wait before moving to next solenoid");
    }
  }
  if (Value8 > 300)
  {
    if (millis() > time_now + period)
    {
      digitalWrite(solenoidRelay8, LOW);  //  turns on
      Serial.println ("Watering Section 8");
    }
    if (millis() > time_now >= interval10) // check if "interval10" time has passed (  5 sec )
    {
      digitalWrite(solenoidRelay8, HIGH); //  turns off
      Serial.println (" Section 8 is OFF");
    }
    if (millis() > time_now + period) {
      Serial.println("wait before moving to next solenoid");
    }
  }
  if (Value9 > 300)
  {
    if (millis() > time_now + period)
    {
      digitalWrite(solenoidRelay9, LOW);  //  turns on
      Serial.println ("Watering Section 9");
    }
    if (millis() > time_now >= interval10) // check if "interval10" time has passed (  5 sec )
    {
      digitalWrite(solenoidRelay9, HIGH); //  turns off
      Serial.println (" Section 9 is OFF");
    }
    if (millis() > time_now + period) {
      Serial.println("wait before moving to next solenoid");
    }
  }
  if (Value10 > 300)
  {
    if (millis() > time_now + period)
    {
      digitalWrite(solenoidRelay10, LOW);  //  turns on
      Serial.println ("Watering Section 10");
    }
    if (millis() > time_now >= interval10) // check if "interval10" time has passed (  5 sec )
      //if ((unsigned long)(currentMillis - time_now) >= interval10) // check if "interval10" time has passed (  5 sec )
    {
      digitalWrite(solenoidRelay10, HIGH); //  turns off
      Serial.println (" Section 10 is OFF");
    }
    if (millis() > time_now + period) {
      Serial.println("wait before moving to next solenoid");
    }
  }
  /////////////////////////////////////////////////////////////////////////////////////////////////


}

Help would be greatly appreciated. Thank you in advance.
Also, I hope I have all of the necessary information for someone to be able to help.

I suggest to absorb this great tutorial.

When you've been through that and understand it completely, cast a critical eye on your code, fix what you can of the many errors and get back to us.

Maybe you can get something from this:

const int pinSol1 = 2;
const int pinSol2 = 3;
const int pinSol3 = 4;
const int pinSol4 = 5;
const int pinSol5 = 6;
const int pinSol6 = 7;
const int pinSol7 = 8;
const int pinSol8 = 9;
const int pinSol9 = 10;
const int pinSol10 = 11;

#define NUM_SOLS    10
#define SOL_ON      LOW
#define SOL_OFF     HIGH

const byte grSols[NUM_SOLS] = 
{
    pinSol1, pinSol2, pinSol3,
    pinSol4, pinSol5, pinSol6,
    pinSol7, pinSol8, pinSol9,
    pinSol10
    
};

bool
    bSolenoidTestComplete;

void setup( void )
{
    Serial.begin(9600);
    for( int i=0; i<NUM_SOLS; i++ )
    {
        pinMode( grSols[i], OUTPUT );
        digitalWrite( grSols[i], SOL_OFF );
        
    }//for
    
    bSolenoidTestComplete = false;
    //
    //...other setup stuff
    //
    
}//setup

#define SOL_INIT        0
#define SOL_RUN         1
#define SOL_ON_TIME     5000ul
#define SOL_OFF_TIME    2000ul

void SolTest_StateMachine( void )
{
    static byte
        stateSol = SOL_INIT,
        idxSol;
    static bool
        bSolState = false;
    static unsigned
        timeSolDelay,
        timeSolenoid = 0;
    unsigned long
        timeNow;

    switch( stateSol )
    {
        case    SOL_INIT:
            //initialize the test by setting the array index to zero,
            //turning on the first solenoid and setting the state flag to true (on)
            Serial.println( "Now Testing Solenoids" );
            idxSol = 0;
            bSolState = true;
            digitalWrite( grSols[idxSol], SOL_ON );
            Serial.print( "Turned on solenoid " );Serial.println( idxSol+1 );
            //grab the millis count now and set the delay time to the "on" millis count
            timeSolenoid = millis();
            timeSolDelay = SOL_ON_TIME;
            //and move to the run state
            stateSol = SOL_RUN;
            
        break;

        case    SOL_RUN:
            //get the millis count...
            timeNow = millis();
            //if the on/off time has passed...
            if( (timeNow - timeSolenoid) >= timeSolDelay )
            {
                //set the delay timer to "now"
                timeSolenoid = timeNow;

                //if the solenoid is on now...
                if( bSolState == true )
                {
                    //turn it off
                    digitalWrite( grSols[idxSol], SOL_OFF );
                    //indicate internally the solenoid is off
                    Serial.print( "Turned off solenoid " );Serial.println( idxSol+1 );
                    bSolState = false;
                    //set the off time
                    timeSolDelay = SOL_OFF_TIME;
                    //and bump to the next index
                    idxSol++;
                    //if we've done the last one (it just turned off), set the
                    //flag to indicate test is complete
                    if( idxSol == NUM_SOLS )
                    {
                        Serial.println( "Testing complete." );
                        bSolenoidTestComplete = true;
                        
                    }//if                        
                    
                }//if
                else
                {
                    //solenoid is off now, so turn it on
                    digitalWrite( grSols[idxSol], SOL_ON );
                    //indicate...
                    bSolState = true;
                    Serial.print( "Turned on solenoid " );Serial.println( idxSol+1 );
                    //set the on-time
                    timeSolDelay = SOL_ON_TIME;                    
                    
                }//else
                
            }//if

        break;

    }//switch
    
}//SolTest_StateMachine

void loop( void )
{
    //run the solenoid test state machine as long as it doesn't indicate complete
    if( !bSolenoidTestComplete )
        SolTest_StateMachine();

    // do other stuff while solenoid test is running...
    //.
    //.
    //.
    
}//loop

jremington:
I suggest to absorb this great tutorial.

When you've been through that and understand it completely, cast a critical eye on your code, fix what you can of the many errors and get back to us.

I have been through that a few times it is what helped me in the first place with calling other functions at different times. Unfortunately I don't get it when it comes to using it for my solenoids in a step by step process. I understand that my code is crap compared to what most people write but it's an attempt and I am learning. I have tried and I am asking for pointers.

Blackfin:
Maybe you can get something from this:

Thank you. Most of my variables are in another file connected to my main code. My solenoid function is called from my main code once a day. My solenoid function checks to see if a hygrometer in a specific section of my garden reads that the soil is dry then it triggers a relay connected to that solenoid in the specific location of the garden to come on for X amount of time depending on the plant and it's need for water. I've been using delay for 2 years now and it holds up the whole program every time it is used. I started this project as a personal learning experience with coding and Arduino. I would like to learn how to use millis in a simple way so that I can eventually use it in other areas of my program. I am an amateur at programming and I'm sure that it's obvious in my code. Thank you for attempting to help but I feel that what you offered is above my comprehension.

My solenoid function is called from my main code once a day

There's your problem. How can the solenoid function turn off the solenoid 2 seconds or 2 minutes later if it never gets called at the right time?

The main loop should take at most a few milliseconds to run. So it can check all the inputs and all the different timers hundreds or thousands of times per second.

One possible solution that will minimise your re-write is to have the solenoid function only turn it on and record the time in a global variable. Then add another function which is called relatively rapidly that is able to look at the state of the solenoid and the time to turn it off at the right time. Fortunately the Arduino never gets bored asking "Are we there yet?" thousands of times per second.

I am asking for pointers.

Great!

  1. Carefully study the example code in the blink program tutorial I linked.

  2. Use subtraction, not addition, to check the time.

Instead of this:

    if (millis() > time_now + period)

use this, as done in the tutorial (but see comments below)

    if (millis() - time_now > period)
  1. Look through your code line by line and make sure every step and every variable makes sense.
    In the following, what is the function of the variable "time_now"?
    Why do you set "time_now" equal to zero and never change it again?
    Where do you ever use the variable "currentMillis"?
  unsigned long time_now = 0;
  unsigned long currentMillis = millis(); // grab current time
  1. When studying the blink example, determine the function of the variable called "previousMillis" and think about how that idea might apply to your code.

  2. The following if statement will become true 5 seconds after the Arduino starts up (because "period2" equals 5000), and will remain true for about 59 days of continuous running, until the millisecond counter overflows to zero. Is that what you intended?

if (millis() > period2)
  1. This is not a valid if statement and I have no idea what is intended.
if (millis() > time_now >= interval10)

Why is "interval10" set equal to 5000? Could you think of a better name for "interval10"?
Recall that time_now is always equal to zero.

  1. These mistakes repeat throughout the code.

jremington:
Great!

  1. Carefully study the example code in the blink program tutorial I linked.

  2. Use subtraction, not addition, to check the time.

  3. Look through your code line by line and make sure every step and every variable makes sense.
    In the following, what is the function of the variable time_now?
    Why do you set time_now equal to zero and never change it again?
    Where do you ever use the variable currentMillis?
    ........

Ahhhhh!!!! I see! Thank you so much.
Sorry about the crappy code, I was trying so many different ways that I didn't delete some of the old code that I was using. Yes interval10 is a horrible name. In my mind i wanted every step to have a 2 second pause in between the next step without using delay. Just for testing purposes I wanted the "solenoid" to come on for 5 seconds then off, 2 seconds later go to the next solenoid. I deleted most and got frustrated because I couldn't figure out why it wouldn't click in my head, then I decided to post to a forum for help. Thank you for taking the time to explain.

I'm attaching photos of parts of my original code that I use in the summertime. It checks more frequently because of the heat. One screenshot is from my solenoid code and the other is from the main code calling the solenoid. Not that any of that matters but I wanted to show how I have been doing things in the past and that I'm wanting to learn so I can improve my programming.

What @blackfin has shown is a state machine but I'm thinking the indexing is clouding your view. Using your outline directly would be similar and take more steps but it might bring out the concept.

oibdrew:
Watering Section 1 // turns solenoid 1 on for 5 seconds
Section 1 is OFF // turns solenoid 1 off
wait before moving to next solenoid //Waits 2 seconds before going to the next solenoid

Watering Section 2 // turns solenoid 2 on for 5 seconds
Section 2 is OFF // turns solenoid 2 off
wait before moving to next solenoid //Waits 2 seconds before going to the next solenoid

Watering Section 3 // turns solenoid 3 on for 5 seconds
Section 3 is OFF // turns solenoid 3 off
wait before moving to next solenoid //Waits 2 seconds before going to the next solenoid

.....and so on.....

Using a switch/case to run this 'sequence' your way would look like this:

case 1:
turn on solenoid1
set time_now to current millis // this effectively resets the timer accumulated value to zero
set state variable to 2

// some would include a break; between steps but it's not strictly necessary
// if break is omitted you 'fall through' to the next statement

case 2: // solenoid1 is delivering water
is timer at preset? - That is, is (millis() - time_now >= interval10) true?
if timer not at preset, break // we'll be in state #2 'til timer is done
// this also allows the code to attend to other duties elsewhere instead of being
// locked into waiting for the timer
// otherwise

if timer is at preset - solenoid1 time is ended
turn off solenoid1
set timer to current millis
set timer preset to two seconds
set state variable to three

case 3: // dwell time between steps
is timer at preset? - if (millis() - time_now >= intervalTwo) // two second delay
if timer not at preset, break // as before, we're going to be stuck doing this 'til timer is done
if timer is at preset // dwell time is ended
set timer to current millis
set timer preset to five seconds // solenoid2 on time
turn on solenoid2
set state variable to four

case 4: // solenoid2 delivers water
lather, rinse, repeat

at last solenoid set state to idle