Timing sequence help

i have a 4 relay board. i want to use a timer to control the state of the relay. I prefer to use millis as its easier for me to understand. Im struggling to think of how code the behavior.

there will be 4 timers each has its own on period and off period. for example timer 1 turns on relay 1 for a set period of time after the on time expires it should not come on until the off time expires.

however its gets more complicated i would like it to work like this,

the sequence is not important. i need 4 different timers to have their own on off times but i dont want to more than 1 relay active at a time. i want to run timer 1-4’s on times then wait until their off duration expire before they come back on and because of the individual off times timer 1 may not come on after timer 4 because timer 2’s off time may expire before timer 1 and come on first.

does this make sense at all? i might be over thinking it. can someone please help me

lets say

timer1Ontime = 10000
timer2Ontime = 10000
timer3Ontime = 10000
timer4Ontime = 10000

timer1Offtime = 5000
timer2Offtime = 2500
timer3Offtime = 1000
timer4Offtime = 30000


timer 1 should run for 10 seconds then after 10 seconds expire timer 2 runs for 10 second then timer 3 runs then timer 4 runs. now they should all be off untill the first off time expires then that timers relay will be active again.

as soon as the first timer shuts off its offtime start counting down. it should continues counting down if/while another timer is running.

if the time are effected by a few seconds here and there because of blocking code this is okay.

Put the data for each relay such as pin number, on time and period in a struct and create an array of the struct

Iterate through the array of structs and use the BWOD principle for each of them in turn

what is the “BWOD principle”?

1 Like

how can i handle this situation with millis. in the future i could assign every timer parameter to an array entry but for now i would like to stick with millis so i can easier visualize the logic of how it should work.

im not looking for a complete program that compiles. if someone could show me sorta how this would work in millis that would be wonderful. i get most lost when i start thinking about how only 1 relay can be active at a time and where do i update my millis compare variable for off duration

Using millis() the right way has strict rules, but it is also flexible. You can adjust the interval or use a table with a sequence.
I have made a few examples, maybe those will help: Fun with millis.

right now this is what i have. this code operates timers 1-4 one after another waiting to move on to the next timer after the previous timers off time expires. However i dont want to wait for the previous timers off time to expire before moving onto the next timer. i want it function how i explained above. but i cant wrap my head around it,


this code will skip certain timers if they are disabled constantly going in the 1-2-3-4 sequence

so i want to make this into timerMode 2, so starting at if (timerMode==2) this is all i have. turn on relay1, run it untill the on duration expires. but then what? i want to turn on relay 2 after relay 1 goes off and i want relay 1 off duration to start counting down while relay 2 is on etc. does this make sense?

  if (_EEPROM.timerMode == 2) {
    handleTimers2(0, 1); //turn on t1
    
    if (millis() - timer1onMillis >= _EEPROM.timerTimes[0][0]) {
      handleTimers2(0, 0); //turn off t1
    }

  }

consider

enum { Off = HIGH, On = LOW };

byte pinsOut [] = { 10, 11, 12, 13 };
#define N_OUT   sizeof(pinsOut)

struct Relay_s {
    byte            pin;
    unsigned long   msecOn;
    unsigned long   msecOff;
    unsigned long   msecLst;
    unsigned long   interval;
} relays [] = {
    { 10, 1000,  500, Off },
    { 11, 1000,  250, Off },
    { 12, 1000,  100, Off },
    { 13, 1000,  300, Off },
};

void
setup (void)
{
    for (unsigned n = 0; n < N_OUT; n++)  {
        digitalWrite (pinsOut [n], Off);
        pinMode      (pinsOut [n], OUTPUT);
    }

    for (unsigned n = 0; n < N_OUT; n++)  {
        digitalWrite (relays [n].pin, On);
        relays [n].interval = relays [n].msecOn;
    }
}


void
loop (void)
{
    unsigned long msec = millis ();

    for (unsigned n = 0; n < N_OUT; n++)  {
        Relay_s *r = & relays [n];

        if ( (msec - r->msecLst) > r->interval)  {
            r->msecLst = msec;

            if (Off == digitalRead (r->pin))  {
                digitalWrite (r->pin, On);
                r->interval = r->msecOn;
            }
            else {
                digitalWrite (r->pin, Off);
                r->interval = r->msecOff;
            }
        }
    }
}

BlinkWithOutDelay

for simplicity sake on my end. im going to try to use my struct of variables just because its going to take a lot of modification of other nodes if they change.

anyway i barley understand the for loop of function pointers. I will consider this in the end when i rewrite the whole program. in the mean time i would really like someone to complete a little bit more of the code in my previous post with millis. i know it probably goes against most of your programming knowledge in practice but it would be easiest for me to visualize right now.

or maybe you could also explain how your code works i may be able to replace variables with mine. thanks

i interpreted what you want are four relays turning on/off independent of one another.

each iteration of loop() services each relay. an array of structures is used to maintain the state of each relay – its pin, on/off times, the timestamp (msecLst) the relay was last switched and the current interval.

whenever the time has exceeded the interval for a relay, it is toggled and its interval is set to the on or off time.

i used a pointer to make the code easier to read, otherwise every “r->” is “relays [n].”

How bad would the blocking action of this code be using the for loop? keep in mind this device sends a struct of all variables to a master node every few seconds and it has a webserver waiting for input commands that are sent from the master. would the for loop like this cause the program to lock up?

it doesn’t block

does this mean that in 4 iterations that all 4 “relays” and their statuses are checked? here are a few of the lines that throw me off,

Relay_s *r = & relays [n];

r->msecLst = msec;

r->interval = r->msecOn;

digitalWrite (r->pin, Off);

#define N_OUT   sizeof(pinsOut)

sorry, by statuses i mean in 4 iterations are all 4 timers millis compared ontime/offtime?

im running your code and trying to understand it. but i have another question. how can i make sure that only 1 relay is on at a time?

not clear what you want.

so the 4 relays control 4 water solenoids. the supply to the solenoid can only carry so much volume. all 4 solenoids inputs share the same supply line. if 2 solenoid were open at a single time it would cause low pressure. You could think of all 4 on times as a single cycle.

solenoid 1 comes on, then off, then 2 comes on then off, then 3 etc. after the 4th solenoid is on then off now they are now all idle waiting for the first off duration to expire. does this make more clear at all?

if solenoid 1 was 10 seconds late to turn on because solenoid 4 was still active this would be okay

my mistake, these sound contradictory, but it’s not clear what you want.

seems to me you could turn each on for the specified on period and then turn the next one on once that period has ended. so after all 4 relays have sequentially been turned on/off, how long should the code wait until restarting the sequence?

added comments that may help

    // capture current timestamp
    unsigned long msec = millis ();

    // for each of the 4 relays
    for (unsigned n = 0; n < N_OUT; n++)  {
        // get pointer to the relay structure being processed
        Relay_s *r = & relays [n];

        // check if the interval for the relay has expired
        if ( (msec - r->msecLst) > r->interval)  {
            // update the timestamp for the next interval
            r->msecLst = msec;

            // determine the state of the relay
            if (Off == digitalRead (r->pin))  {
                // turn relay on and use msecOn as the interval
                digitalWrite (r->pin, On);
                r->interval = r->msecOn;
            }
            else {
                // turn relay off and use msecOff as the interval
                digitalWrite (r->pin, Off);
                r->interval = r->msecOff;
            }
        }
    }
}
1 Like