Relay, Pushbutton, Timing

Hello Group,

I have one push button, one relay, using a switch case statement. Now what IS working is one press turns the relay on and another press turns the relay off. However. Each time the button is pressed the relay needs to do the opposite. If it is on it needs to go off after a certain amount of time has passed and vice versa. I tried adding a delay(50); in the case statement that did not work. The delays are needed to turn the relay off as to not burn up the solenoid that is attached to the relay. Code Below without the delay and thanks in advanced!

int buttonPin  = 9;
int relay      = 10;
int led1       = 3;
int led2       = 4;

int buttonPushCounter = 0;   
int buttonState       = 0;         
int lastButtonState   = 0;    

void setup(){
Serial.begin(9600);
pinMode(buttonPin, INPUT_PULLUP);
pinMode(led1,            OUTPUT);
pinMode(led2,            OUTPUT);
pinMode(relay,           OUTPUT);
  
digitalWrite(led1,  HIGH);
digitalWrite(led2,   LOW);
digitalWrite(relay, LOW);
delay(1000);
digitalWrite(led1,  LOW);
digitalWrite(led2, HIGH);
digitalWrite(relay, HIGH);
delay(1000);
digitalWrite(led1,  HIGH); // LED1 OFF
digitalWrite(led2,  HIGH); // LED2 OFF
digitalWrite(relay, LOW); // SET OFF
} // END SETUP

void loop() {
// read the pushbutton input pin:
buttonState = digitalRead(buttonPin);
// compare the buttonState to its previous state
if (buttonState != lastButtonState) {

// if the state has changed, increment the counter

if (buttonState == HIGH) {

// if the current state is HIGH then the button
// went from off to on:
buttonPushCounter++;
if(buttonPushCounter == 3){ 
buttonPushCounter = 1;
}
}
}

// save the current state as the last state, 
//for next time through the loop

lastButtonState = buttonState;
switch(buttonPushCounter) {
case 1:
digitalWrite(led1,  HIGH);
digitalWrite(led2,   LOW);
digitalWrite(relay,  HIGH);
break;

case 2:
digitalWrite(led1,   LOW);
digitalWrite(led2,  HIGH);
digitalWrite(relay, LOW);
break;

} // END CASE
} // END LOOP

It's crude, but what if you do

     digitalWrite(relay,  HIGH);
     delay(someValue);
     digitalWrite(relay, LOW);

and

     digitalWrite(relay,  LOW);
     delay(someValue);
     digitalWrite(relay, HIGH);

There are two methods, first you could use "switch(buttonPushCOunter % 2)" and check for "case 0" and "case 1". The other method is to introduce a "bool isOn" and simply toggle the value for each press with "isOn != isOn".

EDIT: Oh, you probably just need to debounce the button:

unsigned long lastButtonPress = 0; //global
...
if ((buttonState != lastButtonState) && (millis() - lastButtonPress > 100))
{
  lastButtonPress = millis();
  ...
}

@UKHeliBob: adding the delay to the case statement:

case 1:
digitalWrite(led1,  HIGH);
digitalWrite(led2,   LOW);
digitalWrite(relay,  HIGH);
delay(50);
digitalWrite(relay, LOW);
break;

Does not work. You think it should but it does not.

So, whenever the relay is turned on it has to turn off again after a period of time? If that is the case, add a global variable "unsigned long relayOnMillis = 0". Add line "relayOnMillis = millis()" below any line with "digitalWrite(relay, HIGH)". As the first line in "loop()" add "if ((digitalRead(relay) == HIGH) && (millis() - relayOnMillis >= 1000)) digitalWrite(relay, LOW)". This will ensure that the relay is always turned off 1 second after it was turned on.

delay(50);50 milliseconds is quite a short period. Are you sure that it is not working ? As an experiment make the delay() 1000 milliseconds then you will definitely be able to tell whether it is working.

@UKHeliBob: Positive its not working...the LEDS change with each button press and so does the relay...however the relay needs to shut off after 1 or 2 seconds...this keeps the solenoid from burning up

the relay needs to shut off after 1 or 2 seconds

But your code only puts the relay in it's other state for 50 milliseconds. Are you using the state of the LEDs as an indication of the state of the relay because you do not change the state of the LEDs at the end of the delay() anyway.

@UKHeliBob: I changed the delay from 50 to delay(1000); and it does not go off…the indicator light on the relay board itself does not shut off…in the setup part of the code using the delay it does shut off. pressing the button again shuts it off…however when a button is pressed the relay needs to come on and go off and so on.

Danois90:
So, whenever the relay is turned on it has to turn off again after a period of time? If that is the case, add a global variable "unsigned long relayOnMillis = 0". Add line "relayOnMillis = millis()" below any line with "digitalWrite(relay, HIGH)". As the first line in "loop()" add "if ((digitalRead(relay) == HIGH) && (millis() - relayOnMillis >= 1000)) digitalWrite(relay, LOW)". This will ensure that the relay is always turned off 1 second after it was turned on.

I added the code in each spot you suggested....however this line "if ((digitalRead(relay) == HIGH) && (millis() - relayOnMillis >= 1000)) digitalWrite(relay, LOW)". Seems to have no effect on anything?

PilotinControl:
@UKHeliBob: I changed the delay from 50 to delay(1000); and it does not go off....the indicator light on the relay board itself does not shut off.....in the setup part of the code using the delay it does shut off. pressing the button again shuts it off...however when a button is pressed the relay needs to come on and go off and so on.

Please post the complete program that you tried with the 1000 millisecond delay()

@UKHeliBob: below is the entire code including the 1000 delay when button is pressed

int buttonPin  = 9;
int relay      = 10;
int led1       = 3;
int led2       = 4;

int buttonPushCounter = 0;   
int buttonState       = 0;         
int lastButtonState   = 0;    

void setup(){
Serial.begin(9600);
pinMode(buttonPin, INPUT_PULLUP);
pinMode(led1,            OUTPUT);
pinMode(led2,            OUTPUT);
pinMode(relay,           OUTPUT);
  
digitalWrite(led1,  HIGH);
digitalWrite(led2,   LOW);
digitalWrite(relay, LOW);
delay(1000);
digitalWrite(led1,  LOW);
digitalWrite(led2, HIGH);
digitalWrite(relay, HIGH);
delay(1000);
digitalWrite(led1,  HIGH); // LED1 OFF
digitalWrite(led2,  HIGH); // LED2 OFF
digitalWrite(relay, LOW); // SET OFF
} // END SETUP

void loop() {
buttonState = digitalRead(buttonPin);
if (buttonState != lastButtonState) {
if (buttonState == HIGH) {
buttonPushCounter++;
if(buttonPushCounter == 3){ 
buttonPushCounter = 1;
}
}  
}

lastButtonState = buttonState;
switch(buttonPushCounter) { 
case 1:
digitalWrite(led1,  HIGH);
digitalWrite(led2,   LOW);
digitalWrite(relay, HIGH);
delay(1000);
digitalWrite(relay, LOW);
break;
case 2:
digitalWrite(led1,   LOW);
digitalWrite(led2,  HIGH);
digitalWrite(relay,  HIGH);
delay(1000);
digitalWrite(relay, LOW);
break;
} // END CASE

} // END LOOP

PilotinControl:
I added the code in each spot you suggested....however this line "if ((digitalRead(relay) == HIGH) && (millis() - relayOnMillis >= 1000)) digitalWrite(relay, LOW)". Seems to have no effect on anything?

Then you must have borked it somehow, please post the code where it is integrated.

@UKHeliBob: below is the entire code including the 1000 delay when button is pressed

In your code what will the value of buttonPushCounter be after the button has been pressed and released once ?

As I read it the value will be 1 so case 1 will be executed. Next time through loop() buttonPushCounter will still be 1 so the case 1 code will be executed again. Is that what you intend ? If so the the relay pin will go

HIGH for 1 second
LOW
then immediately HIGH for 1 second
LOW
then immediately HIGH for 1 second

and so on. Effectively it will not go LOW for any measurable period.

To prove it, put a delay(1000) before the break in each case

@UKHeliBob: You are right. One of them "}" should move to after switch statement :wink:

@Danois90: The code below with your additions…it does work had to re-arrange some things…now for that button debounce…I want to get rid of the delay(200); As I will be adding more buttons with more relays…this was just the start with one…I assume changing the variables for each button will work…I will have 4 buttons and 4 relays. Thanks for the help.

#define button 9
#define relay 10
#define led1 3
#define led2 4

int buttonState = 0;
int flag = 0;
unsigned long relayOnMillis   = 0;
unsigned long lastButtonPress = 0;

void setup() {
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(button, INPUT_PULLUP);
pinMode(relay, OUTPUT);
digitalWrite(relay, LOW); // Keep Relay Off
digitalWrite(led1, HIGH);
digitalWrite(led2, HIGH);
} // END SETUP

void loop() {
buttonState  = digitalRead(button);
if ((digitalRead(relay) == HIGH) && (millis() - relayOnMillis >= 800)) {
digitalWrite(relay, LOW);
}

if (buttonState == HIGH) {
if ( flag == 0){
digitalWrite(led1, HIGH);
digitalWrite(led2,  LOW);
digitalWrite(relay, HIGH); // ON
relayOnMillis = millis();
flag=1; //change flag variable
}
//...twice, turn led off!
else if ( flag == 1){
digitalWrite(led1,  LOW);
digitalWrite(led2, HIGH);
digitalWrite(relay, HIGH); // ON
relayOnMillis = millis();
flag=0; //change flag variable again 
} // END FLAG
} // END IF
delay(200);      //   <------ I want to remove this and add millis debounce for buttons
} // END LOOP