Controlling Relay by push button

Hi,

I'm new to Arduino and i'm trying to switch two lamps with couple of relays controlled by a push button.

The idea is when i push the button once, it toggles an LED to HIGH and send signal to the relays to turn on for couple of seconds then turn off the first one and turn the second one for longer time then STOPS till I push the button again.

The problem is when I upload the code, it waits my push, I push it then LED turns on and relays start to work fine according to delays but the problem that they keep looping forever even I pushed the button again to set them LOW again. Seems the looping process is preventing the code to read my second push to turn it off.

here is the code:

const int valveRelay1 = 7;
const int valveRelay2 = 6;
// constants won't change. They're used here to
// set pin numbers:
const int buttonPin = 2; // the number of the pushbutton pin
const int ledPin = 13; // the number of the LED pin

// Variables will change:
int ledState = LOW; // the current state of the output pin
int reading;
int buttonState; // the current reading from the input pin
int lastButtonState = LOW; // the previous reading from the input pin

// the following variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long lastDebounceTime = 0; // the last time the output pin was toggled
long debounceDelay = 50; // the debounce time; increase if the output flickers

void setup() {
pinMode(buttonPin, INPUT);
pinMode(ledPin, OUTPUT);
pinMode(valveRelay1, OUTPUT);
pinMode(valveRelay2, OUTPUT);
Serial.begin(9600);
}

void loop() {
// read the state of the switch into a local variable:
reading = digitalRead(buttonPin);
Serial.println(reading);
// check to see if you just pressed the button
// (i.e. the input went from LOW to HIGH), and you've waited
// long enough since the last press to ignore any noise:

// If the switch changed, due to noise or pressing:
if (reading != lastButtonState) {
// reset the debouncing timer
lastDebounceTime = millis();

// this is all that's new to the code
// toggles the ledState variable each time the button is pressed
if (buttonState == HIGH) {
ledState = !ledState;
Serial.println(ledState);
}
}

if ((millis() - lastDebounceTime) > debounceDelay) {
// whatever the reading is at, it's been there for longer
// than the debounce delay, so take it as the actual current state:
buttonState = reading;
}

digitalWrite(ledPin, ledState);
lastButtonState = reading;
switch (ledState) {
case 1:
digitalWrite(valveRelay1, HIGH);
delay(1000);
digitalWrite(valveRelay1, LOW);
digitalWrite(valveRelay2, HIGH);
delay(3000);
digitalWrite(valveRelay2, LOW);
break;
case 0:
digitalWrite(valveRelay1, LOW);
digitalWrite(valveRelay2, LOW);
break;
}
}

First break down loop () into several smaller pieces of code. You'll be
able to read it then...

For instance you are not setting lastButtonstate when you should because all the code is spread
about in loop().

Write one function to handle the button, perform debouncing and detect state change.
Have that function change the state appropriately. Have loop() call that and
another function that implements the state machine. So roughly like this:

void loop ()  // loop is just a set of calls to tasks...
{
  check_button () ;
  handle_state_machine () ;
}

void check_button ()  // keep all the detail of button handling in one place
{
  boolean buttonState = digitalRead (buttonPin) ;
  if (buttonState != lastButtonState && millis () - lastDebounceTime > debounceDelay)
  {
    lastButtonState = buttonState ;
    lastDebounceTime = millis () ;
    if (buttonState == HIGH)
      ledState = !ledState
  }
}

void handle_state_machine ()  // ditto for business logic
{
  digitalWrite(ledPin, ledState);
  switch (ledState) {
    case 1:
      digitalWrite(valveRelay1, HIGH);
      delay(1000);      // proper state machine would have states that timeout, not call delay() and block...
      digitalWrite(valveRelay1, LOW);
      digitalWrite(valveRelay2, HIGH);
      delay(3000);
      digitalWrite(valveRelay2, LOW);
      break;
    case 0:
      digitalWrite(valveRelay1, LOW);
      digitalWrite(valveRelay2, LOW);
      break;
  }
}

( NOT tested, NOT complete, just the flavour of the structure you should have - the delay()
calls could be done as a proper statemachine for instance, and button presses would then
be detectable during the relay operating sequence )

I'm new to Arduino...

#7 below:

http://forum.arduino.cc/index.php/topic,148850.0.html

Thank you MarkT

I cleaned up my code to be the following:

const int valveRelay1 = 7;
const int valveRelay2 = 6;

const int buttonPin = 2;     // the number of the pushbutton pin
const int ledPin =  13;      // the number of the LED pin

// Variables will change:
int ledState = LOW;         // the current state of the output pin
int reading;
int buttonState;             // the current reading from the input pin
int lastButtonState = LOW;   // the previous reading from the input pin

// the following variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers

void setup() {
  pinMode(buttonPin, INPUT);
  pinMode(ledPin, OUTPUT);
  pinMode(valveRelay1, OUTPUT);
  pinMode(valveRelay2, OUTPUT);
  Serial.begin(9600);
}

void loop ()  // loop is just a set of calls to tasks...
{
  check_button () ;
  handle_state_machine () ;
}

void check_button ()  // keep all the detail of button handling in one place
{
  boolean buttonState = digitalRead (buttonPin) ;
  if (buttonState != lastButtonState && millis () - lastDebounceTime > debounceDelay)
  {
    lastButtonState = buttonState ;
    lastDebounceTime = millis () ;
    if (buttonState == HIGH) {
      ledState = !ledState;
    }
  }
}

void handle_state_machine ()  // ditto for business logic
{
  digitalWrite(ledPin, ledState);
  switch (ledState) {
    case 1:
      digitalWrite(valveRelay1, HIGH);
      delay(1000);      // proper state machine would have states that timeout, not call delay() and block...
      digitalWrite(valveRelay1, LOW);
      digitalWrite(valveRelay2, HIGH);
      delay(3000);
      digitalWrite(valveRelay2, LOW);
      break;
    case 0:
      digitalWrite(valveRelay1, LOW);
      digitalWrite(valveRelay2, LOW);
      break;
  }
}

I'm still having the same problem, I push the button and the sequence work fine but it keeps looping without stop even I try to push again to toggle it off, it won't work unless the push occurs exactly when the sequence ends in a matter of milliseconds

Here is a video of my setup: - YouTube

Thank you zoomkat for the embedding code tip :slight_smile:

You have to get rid of all those instances of the delay() function.

aarg: but how to set the timing sequence of turning on / off relays ?

Get rid of the delay()s
Whilst they are happening the program cannot read the button state. You need to embrace the principle in the BlinkWithoutDelay example in the IDE.

Save the start time of the action from millis() and check each time through loop() whether it is time to stop the action.

If yes, then do it and start the next action, if there is one, saving the new start time.
If no then go round loop() again reading inputs each time and acting on them as appropriate.

I got rid of delays

It works ok for switching but still couldn't achive the main goal of performing timed sequence.

What i want is to make Relay_1 turn on for 4 seconds then turn off then Relay_2 turns on for 10 seconds then stop everything till i push the button off then on again.

Here is he code i made:

const int valveRelay1 = 7;
const int valveRelay2 = 6;

const int buttonPin = 2;
const int ledPin =  13;

int ledState = 0;
int reading;
int buttonState = 0;
int lastButtonState = 0;

long lastDebounceTime = 0;
long debounceDelay = 50;

long prevMillis1=0;
int interval1 = 4000;
long prevMillis2=0;
int interval2 = 10000;

void setup() {
  pinMode(buttonPin, INPUT);
  pinMode(ledPin, OUTPUT);
  pinMode(valveRelay1, OUTPUT);
  pinMode(valveRelay2, OUTPUT);
  Serial.begin(9600);
  
  digitalWrite(valveRelay1, 1);
  digitalWrite(valveRelay2, 1);
}

void loop ()
{
  check_button();
  if (ledState == HIGH) {
    progOneOn();
  } else {
    progOneOff();
  }
}

void check_button ()
{
  boolean buttonState = digitalRead (buttonPin) ;
  if (buttonState != lastButtonState && millis () - lastDebounceTime > debounceDelay)
  {
    lastButtonState = buttonState ;
    lastDebounceTime = millis () ;
    if (buttonState == HIGH) {
      ledState = !ledState;
    }
  }
}

void progOneOn()
{
  Serial.println("progOneOn processed ...");
  digitalWrite(ledPin, 1);
  timerFunc(interval1,interval2,valveRelay1,valveRelay2);
}

void progOneOff()
{
  Serial.println("progOneOff processed ...");
  digitalWrite(ledPin, 0);
  digitalWrite(valveRelay1, 1);
  digitalWrite(valveRelay2, 1);
}

int timerFunc(int i1,int i2,int p1,int p2) // i = interval , p = pin
{
  digitalWrite(p1, 0);
  unsigned long currMillis1=millis();
  if ((unsigned long)(currMillis1 - prevMillis1) >= i1) {
      prevMillis1 = currMillis1;
      digitalWrite(p1, 1);
      digitalWrite(p2, 0);
      unsigned long currMillis2=millis();
      if ((unsigned long)(currMillis2 - prevMillis2) >= i2) {
        prevMillis2 = currMillis2;
        digitalWrite(p2, 1);
      }
  }
}

Now what happens that when i push the button first time Relay_1 turns on for 4 seconds but won't turn off then Relay_2 starts for 10 seconds then turns off successfully. The problem that Relay_1 stays on all the time.

Second issue, the whole sequence keeps looping, I need to stop it after it completes the first sequence.

Try this. You will need to adjust pin numbers and the logic may be inverted compared to your wiring but you should get the idea

const byte ledPin = 13;
const byte relayPin1 = 10;
const byte relayPin2 = 11;
const byte buttonPin = A1;
byte buttonState = HIGH;
unsigned long currentTime;
unsigned long startTime;
unsigned long period;
byte state = 0;

void setup()
{
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);
  pinMode(relayPin1, OUTPUT);
  pinMode(relayPin2, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
  digitalWrite(relayPin1, HIGH);
  digitalWrite(relayPin2, HIGH);
  digitalWrite(ledPin, HIGH);
}

void loop()
{
  switch (state)
  {
    case 0:  //waiting for button press
      buttonState = digitalRead(buttonPin);
      if (buttonState == LOW)  //button has been or is pressed
      {
        digitalWrite(ledPin, LOW);  //turn on the LED
        digitalWrite(relayPin1, LOW);  //turn on relay1
        period = 4000;
        startTime = millis();
        state = 1;
      }
      break;

    case 1:  //waiting for 4 seconds to elapse
      currentTime = millis();
      if (currentTime - startTime >= period)  //time's up
      {
        digitalWrite(relayPin1, HIGH);  //turn off relay 1
        digitalWrite(relayPin2, LOW);  //turn on relay 2
        startTime = currentTime;
        period = 10000;
        state = 2;
      }
      break;

    case 2:  //waiting for 10 seconds to elapse
      currentTime = millis();
      if (currentTime - startTime >= period)  //time's up
      {
        digitalWrite(relayPin2, HIGH);  //turn off relay 2
        digitalWrite(ledPin, HIGH);  //turn off LED
        state = 0;
      }
      break;
  }
}

Thank you UKHeliBob :slight_smile:

The code worked fine, the only little issue is the push button sometimes doesn't respond. works after few tries sometimes not works just fine. Any suggestion to tweak the push button?

The button will only work when the program is waiting for it to be pressed.
Check the wiring and connections to the pushbutton. How exactly is it wired ?
Remove the pushbutton and ground the input pin to the USB connector using a patch wire as a temporary substitute for the pushbutton.

By the way, do you understand the program and how it works ?