Toggle Button Timeout Using Millis()

Hello,

Please pardon my newbie level and my question that may seem trivial. I am completely new to this business, and I have been searching for over a week for an answer to my question, with no luck.

I am building a program for a specific project, which controls multiple window rolling shutters. My goal to to set 4 buttons per rolling shutter, where two are momentary up and down (where as long as I am holding the button(s), the shutters are rolling), and the other two are toggle up and down (where I push once and release, then the corresponding shutter will roll up or down for a specific period of time).

I was successful building the sketch, but I am unable to use millis() function for the toggle switches. When I use delay(), it works as intended, but it blocks the micro-controller from controlling other variables. Thus, I know I have to use millis()

What I am particularly interested in, is program the toggle button to run the corresponding if statement for a specified period of time (1000ms) using millis().

Here is my sketch (primitive, I know ... but I am trying to learn as much as I could ... I certainly appreciate any help, advice, or guidance)

int relay1 = 0;
int relay2 = 1;
int relay3 = 2;
int relay4 = 3;
int button1 = 4;
int togglebutton1 = 5;
int button2 = 6;
int togglebutton2 = 7;


void setup() {
 pinMode(relay1, OUTPUT);
 pinMode(relay2, OUTPUT);
 pinMode(relay3, OUTPUT);
 pinMode(relay4, OUTPUT);
 pinMode (button1, INPUT_PULLUP);
 pinMode (togglebutton1, INPUT_PULLUP);
 pinMode (button2, INPUT_PULLUP);
 pinMode (togglebutton2, INPUT_PULLUP);

}

void loop() {
 
 if ((digitalRead(button1) == LOW)) {
   digitalWrite(relay1, LOW);
   digitalWrite(relay2, LOW);
 }

 if ((digitalRead(togglebutton1) == LOW)) {

   digitalWrite(relay1, LOW);
   digitalWrite(relay2, LOW);
   delay(10000);
 }
 
 if
 ((digitalRead(button2) == LOW)) {
   digitalWrite(relay1, LOW);
   digitalWrite(relay2, LOW);
   digitalWrite(relay3, LOW);
   digitalWrite(relay4, LOW);
 }
 if ((digitalRead(togglebutton2) == LOW)) {
   digitalWrite(relay1, LOW);
   digitalWrite(relay2, LOW);
   digitalWrite(relay3, LOW);
   digitalWrite(relay4, LOW);
   delay(10000);
 }
 else {
   digitalWrite(relay1, HIGH);
   digitalWrite(relay2, HIGH);
   digitalWrite(relay3, HIGH);
   digitalWrite(relay4, HIGH);
 }

}

Please edit your post and add
** **[code]** **
before your code and
** **[/code]** **
after your code. So it looks like below; that makes is easier to read and copy.

your code here

Have you read and understood Using millis() for timing. A beginners guide ?

Here's some example code using millis() for run time out after the togglebutton press.

Your code is perhaps best rewritten as a state machine, but I have used your conditional logic structure and added boolean control variables to the "if" conditional statements.

There are control variables and timing variables to start/finish the time out period after a togglebutton press. There are also control variables to turn the relays off when the untimed buttons are released. You cannot do the untimed turn offs with a simple "else" conditions because relays 1 and 2 are controlled by all 4 buttons.

I used the indicator led on pin 13 to help show the timing.

int relay1 = 0;
int relay2 = 1;
int relay3 = 2;
int relay4 = 3;
int button1 = 4;
int togglebutton1 = 5;
int button2 = 6;
int togglebutton2 = 7;

//variables to control state and timeout after toggle button press
unsigned long currentTime;
unsigned long startTime1;
unsigned long startTime2;
boolean timing1 = false;
boolean timing2 = false;
unsigned long runTime1 = 10000;
unsigned long runTime2 = 10000;

//variables to control on/off after button press and hold
boolean holding1 = false;
boolean holding2 = false;


void setup() {
  pinMode(relay1, OUTPUT);
  pinMode(relay2, OUTPUT);
  pinMode(relay3, OUTPUT);
  pinMode(relay4, OUTPUT);
  pinMode (button1, INPUT_PULLUP);
  pinMode (togglebutton1, INPUT_PULLUP);
  pinMode (button2, INPUT_PULLUP);
  pinMode (togglebutton2, INPUT_PULLUP);
  //Test led for timing check
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);

}

void loop() {

  currentTime = millis();

  if ((digitalRead(button1) == LOW) && (holding1 == false)) //button1 pressed
  {
    digitalWrite(relay1, LOW);
    digitalWrite(relay2, LOW);
    digitalWrite (13, LOW);
    holding1 = true;
  }
  if (holding1 == true && digitalRead(button1) == HIGH) //button1 released
  {
    digitalWrite(relay1, HIGH);
    digitalWrite(relay2, HIGH);
    digitalWrite (13, HIGH);
    holding1 = false;
  }

  if ((digitalRead(togglebutton1) == LOW) && (timing1 == false) ) //togglebutton1 pressed
  {
    digitalWrite(relay1, LOW);
    digitalWrite(relay2, LOW);
    digitalWrite(13, LOW);
    timing1 = true;
    startTime1 = currentTime;
    // delay(10000);
  }

  if (timing1 == true && (currentTime - startTime1 >= runTime1))//togglebutton1 timed out
  {
    digitalWrite(relay1, HIGH);
    digitalWrite(relay2, HIGH);
    digitalWrite(13, HIGH);
    timing1 = false;
  }

  if ((digitalRead(button2) == LOW) && (holding2 == false)) //button2 pressed
  {
    digitalWrite(relay1, LOW);
    digitalWrite(relay2, LOW);
    digitalWrite(relay3, LOW);
    digitalWrite(relay4, LOW);
    digitalWrite(13, LOW);
    holding2 = true;
  }
  if ((digitalRead(button2) == HIGH) && (holding2 == true)) //button2 released
  {
    digitalWrite(relay1, HIGH);
    digitalWrite(relay2, HIGH);
    digitalWrite(relay3, HIGH);
    digitalWrite(relay4, HIGH);
    digitalWrite(13, HIGH);
    holding2 = false;
  }

  if ((digitalRead(togglebutton2) == LOW) && (timing2 == false)) //togglebutton2 pressed
  {
    digitalWrite(relay1, LOW);
    digitalWrite(relay2, LOW);
    digitalWrite(relay3, LOW);
    digitalWrite(relay4, LOW);
    timing2 = true;
    startTime2 = currentTime;
    digitalWrite(13, LOW);
    //delay(10000);
  }

  if (timing2 == true && (currentTime - startTime2 >= runTime2))//togglebutton2 timed out
  {
    digitalWrite(relay1, HIGH);
    digitalWrite(relay2, HIGH);
    digitalWrite(relay3, HIGH);
    digitalWrite(relay4, HIGH);
    digitalWrite(13, HIGH);
    timing2 = false;
  }
}

UKHeliBob:
Have you read and understood Using millis() for timing. A beginners guide ?

Thank you for referring me to this document. I did read it and I learned a lot about millis(). I do appreciate directing me into the right direction

sterretje:
Please edit your post and add

 before your code and  after your code. So it looks like below; that makes is easier to read and copy.

Thank you for this wonderful trick, I was wondering how to include the sketch into an easier to scroll and copy format. I have updated my script.

cattledog:
Here's some example code using millis() for run time out after the togglebutton press.

Your code is perhaps best rewritten as a state machine, but I have used your conditional logic structure and added boolean control variables to the "if" conditional statements.

There are control variables and timing variables to start/finish the time out period after a togglebutton press. There are also control variables to turn the relays off when the untimed buttons are released. You cannot do the untimed turn offs with a simple "else" conditions because relays 1 and 2 are controlled by all 4 buttons.

I used the indicator led on pin 13 to help show the timing.

int relay1 = 0;

int relay2 = 1;
int relay3 = 2;
int relay4 = 3;
int button1 = 4;
int togglebutton1 = 5;
int button2 = 6;
int togglebutton2 = 7;

//variables to control state and timeout after toggle button press
unsigned long currentTime;
unsigned long startTime1;
unsigned long startTime2;
boolean timing1 = false;
boolean timing2 = false;
unsigned long runTime1 = 10000;
unsigned long runTime2 = 10000;

//variables to control on/off after button press and hold
boolean holding1 = false;
boolean holding2 = false;

void setup() {
  pinMode(relay1, OUTPUT);
  pinMode(relay2, OUTPUT);
  pinMode(relay3, OUTPUT);
  pinMode(relay4, OUTPUT);
  pinMode (button1, INPUT_PULLUP);
  pinMode (togglebutton1, INPUT_PULLUP);
  pinMode (button2, INPUT_PULLUP);
  pinMode (togglebutton2, INPUT_PULLUP);
  //Test led for timing check
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);

}

void loop() {

currentTime = millis();

if ((digitalRead(button1) == LOW) && (holding1 == false)) //button1 pressed
  {
    digitalWrite(relay1, LOW);
    digitalWrite(relay2, LOW);
    digitalWrite (13, LOW);
    holding1 = true;
  }
  if (holding1 == true && digitalRead(button1) == HIGH) //button1 released
  {
    digitalWrite(relay1, HIGH);
    digitalWrite(relay2, HIGH);
    digitalWrite (13, HIGH);
    holding1 = false;
  }

if ((digitalRead(togglebutton1) == LOW) && (timing1 == false) ) //togglebutton1 pressed
  {
    digitalWrite(relay1, LOW);
    digitalWrite(relay2, LOW);
    digitalWrite(13, LOW);
    timing1 = true;
    startTime1 = currentTime;
    // delay(10000);
  }

if (timing1 == true && (currentTime - startTime1 >= runTime1))//togglebutton1 timed out
  {
    digitalWrite(relay1, HIGH);
    digitalWrite(relay2, HIGH);
    digitalWrite(13, HIGH);
    timing1 = false;
  }

if ((digitalRead(button2) == LOW) && (holding2 == false)) //button2 pressed
  {
    digitalWrite(relay1, LOW);
    digitalWrite(relay2, LOW);
    digitalWrite(relay3, LOW);
    digitalWrite(relay4, LOW);
    digitalWrite(13, LOW);
    holding2 = true;
  }
  if ((digitalRead(button2) == HIGH) && (holding2 == true)) //button2 released
  {
    digitalWrite(relay1, HIGH);
    digitalWrite(relay2, HIGH);
    digitalWrite(relay3, HIGH);
    digitalWrite(relay4, HIGH);
    digitalWrite(13, HIGH);
    holding2 = false;
  }

if ((digitalRead(togglebutton2) == LOW) && (timing2 == false)) //togglebutton2 pressed
  {
    digitalWrite(relay1, LOW);
    digitalWrite(relay2, LOW);
    digitalWrite(relay3, LOW);
    digitalWrite(relay4, LOW);
    timing2 = true;
    startTime2 = currentTime;
    digitalWrite(13, LOW);
    //delay(10000);
  }

if (timing2 == true && (currentTime - startTime2 >= runTime2))//togglebutton2 timed out
  {
    digitalWrite(relay1, HIGH);
    digitalWrite(relay2, HIGH);
    digitalWrite(relay3, HIGH);
    digitalWrite(relay4, HIGH);
    digitalWrite(13, HIGH);
    timing2 = false;
  }
}

This is very helpful, I am so thankful for you and for your time and effort helping me out. While this code works perfect for the toggle switch and momentary switch, I ran into another problem. The toggle switch in my case is NOT a physical momentay switch, but rather is a relay itself. Here is the situation: I am adding a LUTRON Wireless light switch where I can control from a dedicated app the system. I am planning to use a 120V coil relay connected to the lutron wireless light switch, and use the normally open and the normally closed sides of that relay as my toggle buttons for the UP and DOWN. when I turn the wireless switch on, it would trigger the attached relay, thus closing the toggle circuit, initiating the toggle the Roller UP function, when I turn the wireless off, the relay will set back to the other position, where it will also toggle the ROLLER DOWN function.

The problem that I am running into, is how to make the toggle part of the sketch expire and not continuously run when the toggle button or function is still pressed (a relay in the normal open position for roller up, or normal closed position for the roller down)?

In other words, I want the toggle button function to timeout in the specified interval even if the toggle button continues to be pushed....

too picky I am I guess .... My wife says that I am too OCD and crazy enough. I think she is right....

In other words, I want the toggle button function to timeout in the specified interval even if the toggle button continues to be pushed....

I think there is a simple modification to the code which will allow for an unconditional time out of the togglebuttons. By making the switch between timing = true and timing = false dependent upon the switch release, we can keep the code from reentry into the block which turns the relays on. Calling the variable timing is not quite accurate for its new function, but I can't quite think of a better name.

I thought there were some issues with switch bounce on togglebutton release with this new code, so I added some debounce delay and repeated readings.

See if this version does what you want.

int relay1 = 0;
int relay2 = 1;
int relay3 = 2;
int relay4 = 3;
int button1 = 4;
int togglebutton1 = 5;
int button2 = 6;
int togglebutton2 = 7;

//variables to control state and timeout after toggle button press
unsigned long currentTime;
unsigned long startTime1;
unsigned long startTime2;
boolean timing1 = false;
boolean timing2 = false;
unsigned long runTime1 = 5000;
unsigned long runTime2 = 5000;

//variables to control on/off after button press and hold
boolean holding1 = false;
boolean holding2 = false;


void setup() {
  pinMode(relay1, OUTPUT);
  pinMode(relay2, OUTPUT);
  pinMode(relay3, OUTPUT);
  pinMode(relay4, OUTPUT);
  pinMode (button1, INPUT_PULLUP);
  pinMode (togglebutton1, INPUT_PULLUP);
  pinMode (button2, INPUT_PULLUP);
  pinMode (togglebutton2, INPUT_PULLUP);
  //Test led for timing check
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);

}

void loop() {

  currentTime = millis();

  if ((digitalRead(button1) == LOW) && (holding1 == false)) //button1 pressed
  {
    digitalWrite(relay1, LOW);
    digitalWrite(relay2, LOW);
    digitalWrite (13, LOW);
    holding1 = true;
  }
  if (holding1 == true && digitalRead(button1) == HIGH) //button1 released
  {
    digitalWrite(relay1, HIGH);
    digitalWrite(relay2, HIGH);
    digitalWrite (13, HIGH);
    holding1 = false;
  }

  if ((digitalRead(togglebutton1) == LOW) && (timing1 == false) ) //togglebutton1 pressed
  {
    digitalWrite(relay1, LOW);
    digitalWrite(relay2, LOW);
    digitalWrite(13, LOW);
    timing1 = true;
    startTime1 = currentTime;
  }

  if (timing1 == true && (currentTime - startTime1 >= runTime1))//togglebutton1 timed out
  {
    digitalWrite(relay1, HIGH);
    digitalWrite(relay2, HIGH);
    digitalWrite(13, HIGH);
    if (digitalRead(togglebutton1) == HIGH) //button was released
    {
      delay(50);//debounce delay
      if (digitalRead(togglebutton1) == HIGH) //reading is solid
      {
        timing1 = false; //reenables the togglebutton1
      }
    }
  }

  if ((digitalRead(button2) == LOW) && (holding2 == false)) //button2 pressed
  {
    digitalWrite(relay1, LOW);
    digitalWrite(relay2, LOW);
    digitalWrite(relay3, LOW);
    digitalWrite(relay4, LOW);
    digitalWrite(13, LOW);
    holding2 = true;
  }
  if ((digitalRead(button2) == HIGH) && (holding2 == true)) //button2 released
  {
    digitalWrite(relay1, HIGH);
    digitalWrite(relay2, HIGH);
    digitalWrite(relay3, HIGH);
    digitalWrite(relay4, HIGH);
    digitalWrite(13, HIGH);
    holding2 = false;
  }

  if ((digitalRead(togglebutton2) == LOW) && (timing2 == false)) //togglebutton2 pressed
  {
    digitalWrite(relay1, LOW);
    digitalWrite(relay2, LOW);
    digitalWrite(relay3, LOW);
    digitalWrite(relay4, LOW);
    digitalWrite(13, LOW);
    timing2 = true;
    startTime2 = currentTime;
  }

  if (timing2 == true && (currentTime - startTime2 >= runTime2))//togglebutton2 timed out
  {
    digitalWrite(relay1, HIGH);
    digitalWrite(relay2, HIGH);
    digitalWrite(relay3, HIGH);
    digitalWrite(relay4, HIGH);
    digitalWrite(13, HIGH);
    if (digitalRead(togglebutton2) == HIGH) //button was released
    {
      delay(50);//debounce delay
      if (digitalRead(togglebutton2) == HIGH) //reading is solid
      {
        timing2 = false; //reenables the togglebutton2
      }
    }
  }
}

cattledog:
I think there is a simple modification to the code which will allow for an unconditional time out of the togglebuttons. By making the switch between timing = true and timing = false dependent upon the switch release, we can keep the code from reentry into the block which turns the relays on. Calling the variable timing is not quite accurate for its new function, but I can't quite think of a better name.

I thought there were some issues with switch bounce on togglebutton release with this new code, so I added some debounce delay and repeated readings.

See if this version does what you want.

Thank you :wink:

By the way: Did you see my PM to you on May 28th? There is something that I would love if you consider.

the sketch is better and getting closer to what I intended it to be. However, I ran into another smaller obstacle (those obstacles seems to become smaller and smaller as I explore and talk to great people like you over the forum).

When ''togglebutton1'' is activated and held in activation, (via a relay in my case), it expires according to the interval specified, but it won't be able to execute an input from ''button1'' as long as the
''togglebutton1'' is activated. However, it will still be able to execute the if statement associated with ''button2'', and ''togglebutton2''. On the other hand, when ''togglebutton2'' is activated and held in position (due to the same relay set back to the other NO or NC status), it still expires after the specified time, and it will accept "togglebutton1" input only , but it WILL NOT execute "button1" or "button2" input during the hold. I spent like 2 hours of trial and error but without luck. Obviously I am missing the solid fundamentals of arduino, because my exposure is so little.

what I am trying to achieve, is, if the roller shutters were put all the way up or all the way down using the relay as trigger to "toggglebutton1" or "togglebutton2", to still be controllable either way using the momentary up or down buttons, without the toggle relay being disconnected. I wanted to make "button1", and "button2" responsive all the time, regardless of whether "toggle1" or "toggle2" activation status is.

I am amazed and humbled for the amount of knowledge and truth out there, and moreover, to the level of Karma and kindness some people have to offer. Thank you again.

I think I understand your requirements, I think they are best met by restructuring the program to work on button state transitions where a button becomes pressed or become released rather than the button state (pressed or released) with a lot of logical conditions pasted on. You should be able to work the press and hold button independently of the timeout toggle button. Give me a day or so to work on this.

I see something like this

if button1 becomes pressed turn stuff on
if button1 becomes released turn stuff off

if togglebutton1 becomes pressed turn stuff on with timeout
if togglebutton1 becomes released do nothing

I'm not certain about the need for debounce, but I will probably use a button library (Bounce2) which simplifies reading the debounced transitions.

You could use a library like TDuino for convenience:

#include <TDuino.h>

#define BUTTON1 4
#define TOGGLE1 5
#define BUTTON2 6
#define TOGGLE2 7
#define RELAY1 0
#define RELAY2 1
#define RELAY3 2
#define RELAY4 3
#define TOGGLE_TIMEOUT 10000

void timerCallback(byte timerId);

TButton btn1(BUTTON1), btn2(BUTTON2);
TButton tgl1(TOGGLE1), tgl2(TOGGLE2);
TTimer timer(timerCallback, 2);

void setRelays(byte state, bool all)
{
  digitalWrite(RELAY1, state);
  digitalWrite(RELAY2, state);
  if (all)
  {
    digitalWrite(RELAY3, state);
    digitalWrite(RELAY4, state);
  }
}

void buttonPress(byte pin, int state)
{
  if (pin == BUTTON1) setRelays(LOW, false);
  else if (pin == TOGGLE1)
  {
    setRelays(LOW, false);
    timer.set(0, TOGGLE_TIMEOUT, 1);
  }
  else if (pin == BUTTON2) setRelays(LOW, true);
  else if (pin == TOGGLE2)
  {
    setRelays(LOW, true);
    timer.set(1, TOGGLE_TIMEOUT, 1);
  }
}

void buttonRelease(byte pin, int state)
{
  setRelays(HIGH, pin == BUTTON2);
}
  
void timerCallback(byte timerId)
{
  setRelays(HIGH, timerId == 1);
}

void setup()
{
  btn1.setup();
  btn2.setup();
  tgl1.setup();
  tgl2.setup();
  timer.setup();
  btn1.onPress(buttonPress);
  btn2.onPress(buttonPress);
  tgl1.onPress(buttonPress);
  tgl2.onPress(buttonPress);
  btn1.onRelease(buttonRelease);
  btn2.onRelease(buttonRelease);
  //tgl1.onRelease(buttonRelease);
  //tgl2.onRelease(buttonRelease);
  pinMode(RELAY1, OUTPUT);
  pinMode(RELAY2, OUTPUT);
  pinMode(RELAY3, OUTPUT);
  pinMode(RELAY4, OUTPUT);
}

void loop()
{
  btn1.loop();
  btn2.loop();
  tgl1.loop();
  tgl2.loop();
  timer.loop();
}

Code is untested and may not work at all :slight_smile:

EDIT: Code simplified…

Here is the state change version of the code using the Bounce2 library for debounced button transitions. The library is available through the library manager. The press and hold buttons work when the toggle buttons have timed out.

I’m sure the code can be tidied up with use of arrays.

byte relay1 = 0;
byte relay2 = 1;
byte relay3 = 2;
byte relay4 = 3;

byte button_1 = 4;
byte togglebutton_1 = 5;
byte button_2 = 6;
byte togglebutton_2 = 7;

//variables to control state and timeout after toggle button press
boolean timing1 = false;
boolean timing2 = false;
unsigned long startTime1;
unsigned long startTime2;
unsigned long runTime1 = 5000;
unsigned long runTime2 = 5000;

#include <Bounce2.h> //available through ide library manager
//create 4 Bounce objects
Bounce button1 = Bounce();
Bounce togglebutton1 = Bounce();
Bounce button2 = Bounce();
Bounce togglebutton2 = Bounce();

void setup() {
  pinMode(relay1, OUTPUT);
  pinMode(relay2, OUTPUT);
  pinMode(relay3, OUTPUT);
  pinMode(relay4, OUTPUT);
  //Test led for timing check of relay outputs
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);

  //set up button pin Bounce objects
  pinMode (button_1, INPUT_PULLUP);
  button1.attach(button_1);
  button1.interval(50);//debounce interval

  pinMode (togglebutton_1, INPUT_PULLUP);
  togglebutton1.attach(togglebutton_1);
  togglebutton1.interval(50);//debounce interval

  pinMode (button_2, INPUT_PULLUP);
  button2.attach(button_2);
  button2.interval(50);//debounce interval

  pinMode (togglebutton_2, INPUT_PULLUP);
  togglebutton2.attach(togglebutton_2 );
  togglebutton2.interval(50);//debounce interval
}

void loop() {
  //.update() key function of Bounce2 library that checks for debounced state of pins and any transitions
  button1.update();
  togglebutton1.update();
  button2.update();
  togglebutton2.update();

  if (button1.fell())//state change to pressed
  {
    digitalWrite(relay1, LOW);
    digitalWrite(relay2, LOW);
    digitalWrite (13, LOW);
  }

  if (button1.rose())//state change to released
  {
    digitalWrite(relay1, HIGH);
    digitalWrite(relay2, HIGH);
    digitalWrite (13, HIGH);
  }

  if (togglebutton1.fell())
  {
    digitalWrite(relay1, LOW);
    digitalWrite(relay2, LOW);
    digitalWrite(13, LOW);
    timing1 = true;
    startTime1 = millis();
  }

  if (timing1 == true && millis() - startTime1 >= runTime1) //togglebutton1 timed out
  {
    digitalWrite(relay1, HIGH);
    digitalWrite(relay2, HIGH);
    digitalWrite(13, HIGH);
    timing1 = false;
  }

  if (button2.fell())
  {
    digitalWrite(relay1, LOW);
    digitalWrite(relay2, LOW);
    digitalWrite(relay3, LOW);
    digitalWrite(relay4, LOW);
    digitalWrite(13, LOW);
  }

  if (button2.rose())
  {
    digitalWrite(relay1, HIGH);
    digitalWrite(relay2, HIGH);
    digitalWrite(relay3, HIGH);
    digitalWrite(relay4, HIGH);
    digitalWrite(13, HIGH);
  }

  if (togglebutton2.fell())
  {
    digitalWrite(relay1, LOW);
    digitalWrite(relay2, LOW);
    digitalWrite(relay3, LOW);
    digitalWrite(relay4, LOW);
    digitalWrite(13, LOW);
    timing2 = true;
    startTime2 = millis();
  }

  if (timing2 == true && millis() - startTime2 >= runTime2)
  {
    digitalWrite(relay1, HIGH);
    digitalWrite(relay2, HIGH);
    digitalWrite(relay3, HIGH);
    digitalWrite(relay4, HIGH);
    digitalWrite(13, HIGH);
    timing2 = false;
  }
}

cattledog:
Here is the state change version of the code using the Bounce2 library for debounced button transitions. The library is available through the library manager. The press and hold buttons work when the toggle buttons have timed out.

I'm sure the code can be tidied up with use of arrays.

Gosh you are an artist! it worked exactly as I wanted it to work. You have relieved a long time agony and suffering... works flawlessly now! thank you thank you thank you :slight_smile: :slight_smile: :).