Want Relay with Button and Time Limit, not sure if loop will work correctly...

Essentially what I am trying to do is:

Press Button > Relay ON, Start timer from 60 minutes > Turn Relay off. If the button is pressed again before time expires, turn relay off.

Here's my code so far,

//Arduino Timed Button
const int buttonPin = 2;    // the number of the pushbutton pin
const int relayPin =  8;      // the number of the Realy pin
int intButtonState = 0;
 
void setup() {
   // initialize the Relay pin as an output:
  pinMode(relayPin, OUTPUT);
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);

}
 
void loop()
{
  //
  // Read the state of the pushbutton value.
  //
  intButtonState = digitalRead(buttonPin);

  //
  // Check to see the pushbutton is pressed down.
  //   IF it is, the intButtonState is HIGH
  //      - turn on the relay
  //
  if (intButtonState == HIGH)
  {     
    // Turn Relay on.    
    digitalWrite(relayPin, HIGH);
    
    //Does intButtonState need to be zeroed here? How do I do this?
    
    // If button is pressed again...
    if (intButtonState == HIGH)
  {     
    // Turn Relay off (Early)   
    digitalWrite(relayPin, LOW);
    } 
    else
    {
  
     // otherwise, wait for an hour
    delay(3600000);
    // Turn Relay off.
    digitalWrite(relayPin, LOW);
    
    }
 } 
  else
  {
    // Turn Relay off.
    digitalWrite(relayPin, LOW); 
  }
}

You need to investigate the famouse blink without delay.

It uses the time to keep track for the moment something has to be done.

In the loop() function, you check the button and check the time. Do that over and over again.

You should also look at the state change detection example. You need to be concerned about when the switch becomes pressed, not is pressed, or becomes released, not is released.

Ok, so I investigated some examples, and the revised post is what I cam up with, but it doesn't seem to be doing what I want... Any suggestions would be great! :slight_smile:

Also, I get the general idea of the blink without delay example, but I am unsure how to apply it to my problem... I still need some input, thanks so much for the advice. :slight_smile:

and the revised post is what I cam up

So, how are we now supposed to figure out what you changed? Never do that again!

but it doesn't seem to be doing what I want...

It does something. You didn't bother describing what it does.
You want it to do something. I'm not into guessing games this morning.

Any suggestions would be great

I'm willing to bet that something has occurred to you by now.

Also, I get the general idea of the blink without delay example, but I am unsure how to apply it to my problem...

Start by explaining what you think the blink without delay example is doing.

It's really pretty simple. An event of interest happens. You record when that event happens. At some later time, you see that enough time has passed that another event should happen. That sounds an awful like what you said you wanted to do.

Put names and variables to the "event of interest", the "enough time has passed", and the "another event", from the example, and you should see how to apply it to your situation.

The delay freezes the program.

The none delay works like someone using a stop watch, so you get the millisecond count and at some point you get it again, you compare the two time stamps (oldtime-newtime) gives how long it's been running for, be 5 seconds or 5 hours.

But where's the code?

OK. I did manage to get my button to turn the relay ON/OFF with each press. :slight_smile:

/*
  Arduino Timed Button
  
 My Setup:
 * pushbutton from pin 2 to +5V
 * 10K resistor from pin 2 to ground
 * Relay on pin 13 and ground
*/
const int  buttonPin = 2;    // button pin (input)
const int relayPin = 13;       // relay pin (output)
int buttonPushCounter = 0;   // button counter
int buttonState = 0;         // state of button
int prevButtonState = 0;     // previous state of button

void setup() {
  // initialize the button as an input:
  pinMode(buttonPin, INPUT);
  // initialize the relay as an output:
  pinMode(relayPin, OUTPUT);
  // initialize serial communication:
  Serial.begin(9600);
}

void loop() {
  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  // compare the buttonState to its previous state
  if (buttonState != prevButtonState) {
    // if state has changed, increment counter
    if (buttonState == LOW) {
      // if the current state is LOW then the button
      // went from on to off:
      buttonPushCounter++;
      Serial.println("off");
      Serial.print("number of button pushes:  ");
      Serial.println(buttonPushCounter);
    } 
    else {
//if current state is HIGH then button went from off to on
 Serial.println("on"); 
 
    }
  }
  // save the current state as the previous state, 
  //for next time through loop
  prevButtonState = buttonState;

  
  // turns off the LED every 2 button pushes by 
  // checking the modulo of the button push counter.
  // the modulo function gives you the remainder of 
  // the division of two numbers:
  if (buttonPushCounter % 2 == 0) {
    digitalWrite(relayPin, LOW);
  } else {
   digitalWrite(relayPin, HIGH);
  }
  
}
int buttonState = 0;         // state of button
int prevButtonState = 0;     // previous state of button

I think that you'll find that names like currButtonState and prevButtonState work better, in the long run. It's quite obvious when comparing current and previous values that that is what you are doing when the names say so.

You still need to deal with the possibility of the switch bouncing. The simplest way is to simply add a small delay after seeing that the state changed. delay(10); will have negligible impact on the code, since it happens only when the code detects that the switch just became pressed or just became released.

The more complicated way involves recording when the last valid state change happened, and comparing then to now (when this state change to pressed happens), and ignoring two changes too close together.

I am trying to now introduce the time element, testing with 10 seconds. With this new code introduced, the ON/OFF with the button is still working. However, I am still missing something as far as the timed shutoff. I feel I'm missing something obvious... My concern is that I have created a redundancy with my int relayState

Thank you everyone with the previous input.

/*
  Arduino Timed Button
  
 My Setup:
 * pushbutton from pin 2 to +5V
 * 10K resistor from pin 2 to ground
 * Relay on pin 13 and ground
*/

long previousMillis = 0;        // will store last time relay was updated
int buttonState = 0;         // state of button
int prevButtonState = 0;     // previous state of button
const int  buttonPin = 2;    // button pin (input)
const int relayPin = 13;       // relay pin (output)
int buttonPushCounter = 0;   // button counter
int relayState = LOW;          // state of relay

// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long interval = 10000;           // 10 second interval at which to stay on (milliseconds)

void setup() {
  // initialize the button as an input:
  pinMode(buttonPin, INPUT);
  // initialize the relay as an output:
  pinMode(relayPin, OUTPUT);
  // initialize serial communication:
  Serial.begin(9600);  
}

void loop()
{
   // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  // compare the buttonState to its previous state
  if (buttonState != prevButtonState) {
    // if state has changed, increment counter
    if (buttonState == LOW) {
      // if the current state is LOW then the button
      // went from on to off:
      buttonPushCounter++;
      Serial.println("off");
      Serial.print("number of button pushes:  ");
      Serial.println(buttonPushCounter);
    } 
    else {
//if current state is HIGH then button went from off to on
 Serial.println("on"); 
 
    }
  }
  // save the current state as the previous state, 
  //for next time through loop
  prevButtonState = buttonState;

  
  // turns off the Relay every 2 button pushes by 
  // checking the modulo of the button push counter.
  // the modulo function gives you the remainder of 
  // the division of two numbers:
  if (buttonPushCounter % 2 == 0) {
    digitalWrite(relayPin, LOW);
    digitalWrite(relayState, LOW);
  } else {
   digitalWrite(relayPin, HIGH);
   digitalWrite(relayState, HIGH);
  }

  // check to see if it's time to switch the relay; that is, if the 
  // difference between the current time and last time you switched 
  // the relay is bigger than the interval at which you want to 
  // switch the relay.
  unsigned long currentMillis = millis();

    if(currentMillis - previousMillis > interval) {
    // save the last time you switched the relay 
    previousMillis = currentMillis;   

    // if the relay is on turn it off and vice-versa:
    if (relayState == HIGH)
      relayState = LOW;
    else
      relayState = LOW;

    // set the relay with the relayState of the variable:
    digitalWrite(relayPin, relayState);
  }
  }

Think about this for a minute. What do currentMillis and previousMillis represent?

Would names like now and whenTheRelayWasTurnedOn make the situation clearer? Where do you record when the relay was turned on? Where are you comparing that time to now?

OK, I have decided to go a different direction, this new code currently works for:
press button > relay on > wait 1 hour > relay off.
What I need to get it to do is get the button to halt the countdown and turn the relay off manually. I have been having trouble getting it to do this...

/*
  Arduino Timed Button
  
 My Setup:
 * pushbutton from pin 2 to +5V
 * 10K resistor from pin 2 to ground
 * Relay on pin 13 and ground
*/



void setup()
{
  pinMode(13,OUTPUT); // Relay output
  pinMode(2,INPUT); // Button input
   Serial.begin(9600);
}
void loop()
{
  static unsigned char relayState = LOW;
  static unsigned char buttonState = LOW;
  static unsigned char lastButtonState = LOW;
  static unsigned long relayCameOn = 0;
  
  // If the LED has been on for at least 5 seconds then turn it off.
  if(relayState == HIGH)
  {
    if(millis()-relayCameOn > 3600000)
    {
      digitalWrite(13,LOW);
      relayState = LOW;
      Serial.println("off");
    }
  }

  // If the button's state has changed, then turn the LED on IF it is not on already.
  buttonState = digitalRead(2);
  if(buttonState != lastButtonState)
  {
    lastButtonState = buttonState;
    if((buttonState == HIGH) && (relayState == LOW))
    {
      digitalWrite(13,HIGH);
      relayState = HIGH;
      relayCameOn = millis();
      Serial.println("on"); 
    }
  }
}
  // If the button's state has changed, then turn the LED on IF it is not on already.

Is that what the code really does?

    if((buttonState == HIGH) && (relayState == LOW))

And, what if the switch is pressed and the relay is HIGH? You don't have any code for that case. Isn't that where you want to turn the relay off?

Is that what the code really does?

From what I can tell... Yes. :~

And, what if the switch is pressed and the relay is HIGH? You don't have any code for that case. Isn't that where you want to turn the relay off?

This currently 'works', however, it doesn't seem to be working every button press... It seems to take from 3 to 7 presses to get it to cycle...

/*
Arduino Timed Button
My Setup:
* pushbutton from pin 2 to +5V
* 10K resistor from pin 2 to ground
* Relay on pin 13 and ground
*/
void setup()
  {
    pinMode(13,OUTPUT); // Relay output
    pinMode(2,INPUT); // Button input
    Serial.begin(9600);
  }
void loop()
  {
    static unsigned char relayState = LOW;
    static unsigned char buttonState = LOW;
    static unsigned char lastButtonState = LOW;
    static unsigned long relayCameOn = 0;
  // If the Relay has been on for at least 7 seconds then turn it off.
    if(relayState == HIGH)
      {
        if(millis()-relayCameOn > 7000)
        {
          digitalWrite(13,LOW);
          relayState = LOW;
          Serial.println("off - time expired");
         }
       }
// If the button's state has changed, then turn the Relay on IF it is not on already.
    buttonState = digitalRead(2);
      if(buttonState != lastButtonState)
        {
          lastButtonState = buttonState;
          if((buttonState == HIGH) && (relayState == LOW))
            {
              digitalWrite(13,HIGH);
              relayState = HIGH;
              relayCameOn = millis();
              Serial.println("on - button press");
            }
         }
         // If the button's state has changed, then turn the Relay off IF it is not off already.
      buttonState = digitalRead(2);
      if(buttonState != lastButtonState)
        {
          lastButtonState = buttonState;
          if((buttonState == HIGH) && (relayState == HIGH))
            {
              digitalWrite(13,LOW);
              relayState = LOW;
              relayCameOn = millis();
              Serial.println("off - button press"); 
            }
         }
  }

Quite frustrating... :0

What am I missing??

Thank you everyone for all you help so far, it's been quite constructive.

From what I can tell... Yes.

Really?

/*
Arduino Timed Button
My Setup:
* pushbutton from pin 2 to +5V
* 10K resistor from pin 2 to ground
* Relay on pin 13 and ground
*/

You seem to be confusing a relay and an LED, then.

No wonder the program doesn't do what you want.

You really need to do a better job of describing how the switch is wired. Switches have two legs. You've described one of them.

I really don't understand why you want the complexity of an external resistor. Get rid of it. Connect one leg of the switch to the digital pin (2). Connect the other leg to ground. Change the pin mode to INPUT_PULLUP.

Then, the switch will be LOW when pressed and HIGH when released.

Thanks for the advice on the resistor, I took it out. :slight_smile:

However, the button is still not responding to every press... Do I want to set my button or relay states between different parts of the program? I'm not sure how to fix it.

Do I want to set my button or relay states between different parts of the program? I'm not sure how to fix it.

What you want to do is post the code you have now, and summarize the issues you are having. At various times in this thread, you say that everything is working, and then later you say stuff still doesn't work.

Well, maybe that isn't what you want to do, but it is what you need to do.

This is current code:

/*
Arduino Timed Button
My Setup:
* pushbutton from pin 2 to +5V
* 10K resistor from pin 2 to ground
* Relay on pin 13 and ground
*/
void setup()
  {
    pinMode(13,OUTPUT); // Relay output
    pinMode(2,INPUT_PULLUP); // Button input
    Serial.begin(9600);
  }
void loop()
  {
    static unsigned char relayState = LOW;
    static unsigned char buttonState = LOW;
    static unsigned char lastButtonState = LOW;
    static unsigned long relayCameOn = 0;
  // If the Relay has been on for at least 7 seconds then turn it off.
    if(relayState == HIGH)
      {
        if(millis()-relayCameOn > 7000)
        {
          digitalWrite(13,LOW);
          relayState = LOW;
          Serial.println("off - time expired");
         }
       }
// If the button's state has changed, then turn the Relay on IF it is not on already.
    buttonState = digitalRead(2);
      if(buttonState != lastButtonState)
        {
          lastButtonState = buttonState;
          if((buttonState == HIGH) && (relayState == LOW))
            {
              digitalWrite(13,HIGH);
              relayState = HIGH;
              relayCameOn = millis();
              Serial.println("on - button press");
            }
         }
         // If the button's state has changed, then turn the Relay off IF it is not off already.
      buttonState = digitalRead(2);
      if(buttonState != lastButtonState)
        {
          lastButtonState = buttonState;
          if((buttonState == HIGH) && (relayState == HIGH))
            {
              digitalWrite(13,LOW);
              relayState = LOW;
              relayCameOn = millis();
              Serial.println("off - button press"); 
            }
         }
  }