Millis logic not working properly

I have the following sketch. The requirement is if pin2 is connected to GND pin and pin1 is not connected to GND pin then the relay should trigger.

While relay is ON if I disconnect PIN2 from GND then the relay should OFF and then the device should be in suspended state for 20 seconds. Within this 20 seconds all input pins should not work but other output pins should work as usual like blink led. I tried delay it is working fine but entire sketch is freezed for 20 seconds but I dont want to freeze entire code. Please help me on this.

#include <digitalWriteFast.h>

#define PIN1       2
#define PIN2       3
#define RELAY_PIN  4
#define BLINK_LED  5

#define OPENED     1  
#define CLOSED     0  

const byte opened = HIGH;
const byte closed = LOW;

unsigned long previousMillis = 0;  
const long blinkInterval = 500;    
bool blinkState = LOW;            

unsigned long relayStartMillis = 0;  // Timer for relay
const unsigned long delayTime = 20000;  // 20 seconds
bool timerStarted = false;  // Flag to start the timer only once

void setup() {
  digitalWriteFast(RELAY_PIN, opened); 

  pinMode(RELAY_PIN, OUTPUT);
  pinMode(PIN1, INPUT_PULLUP);
  pinMode(PIN2, INPUT_PULLUP);
  pinMode(BLINK_LED, OUTPUT);
}

void loop() {

  // Read the current time
  unsigned long currentMillis = millis();

  // LED Blinking Logic
  if (currentMillis - previousMillis >= blinkInterval) {
    previousMillis = currentMillis;  
    blinkState = !blinkState;        
    digitalWrite(BLINK_LED, blinkState ? HIGH : LOW); 
  }

 
  int pin1State = digitalReadFast(PIN1);
  int pin2State = digitalReadFast(PIN2);

  if (digitalRead(RELAY_PIN) == closed) {
    if (pin1State == closed) {
      digitalWriteFast(RELAY_PIN, opened); // Turn off relay
      relayStartMillis = 0;
    }
  else {
    if (pin2State == opened) {
      // Keep relay off during the wait period
      digitalWrite(RELAY_PIN, LOW);  // Relay OFF

      // Start the timer only once
      if (!timerStarted) {
        relayStartMillis = millis();  // Record the start time
        timerStarted = true;  // Set the flag indicating the timer has started
      }

      // Wait for the 20-second delay to pass before activating the relay
      if (millis() - relayStartMillis >= delayTime) {
        // After 20 seconds, activate the relay
        digitalWrite(RELAY_PIN, HIGH);  // Relay ON

        // Reset the timer for the next cycle
        relayStartMillis = 0;  // Reset the timer
        timerStarted = false;  // Reset the timer flag
      }
    }
  }


  if (pin1State == opened && pin2State == closed) {
    if (relayStartMillis == 0) {
      relayStartMillis = millis(); 
    }
    
    if (millis() - relayStartMillis >= 3000) {
      digitalWrite(RELAY_PIN, closed); // turn on the relay
      relayStartMillis = 0;  // Reset the timer to allow re-triggering the delay
    }
  }
}
  • Hi Chandu.

  • Always show us a good schematic of your proposed circuit.
    Show us good images of your ‘actual’ wiring.

  • Always get into the habit of using de-bounce logic when it comes to switch inputs.

  • When setting up outputs in setup( ), explicitly turn the relays ON/OFF.

Give this a go. You code wasn't compiling correctly for me.

//#include <digitalWriteFast.h>

#define PIN1 2
#define PIN2 3
#define RELAY_PIN 4
#define BLINK_LED 5

#define OPENED 1
#define CLOSED 0

const byte opened = HIGH;
const byte closed = LOW;

unsigned long previousMillis = 0;
const long blinkInterval = 500;
bool blinkState = LOW;

unsigned long relayStartMillis = 0;     // Timer for relay
const unsigned long delayTime = 20000;  // 20 seconds
bool timerStarted = false;              // Flag to start the timer only once

void setup() {
  digitalWriteFast(RELAY_PIN, opened);

  pinMode(RELAY_PIN, OUTPUT);
  pinMode(PIN1, INPUT_PULLUP);
  pinMode(PIN2, INPUT_PULLUP);
  pinMode(BLINK_LED, OUTPUT);
}

void loop() {

  // Read the current time
  unsigned long currentMillis = millis();

  // LED Blinking Logic
  if (currentMillis - previousMillis >= blinkInterval) {
    previousMillis = currentMillis;
    blinkState = !blinkState;
    digitalWrite(BLINK_LED, blinkState ? HIGH : LOW);
  }


  int pin1State = digitalReadFast(PIN1);
  int pin2State = digitalReadFast(PIN2);

  if (digitalRead(RELAY_PIN) == closed) {
    if (pin1State == closed) {
      digitalWriteFast(RELAY_PIN, opened);  // Turn off relay
      relayStartMillis = 0;
    }
  } else { // *** This part was tripping up my compiler because it thought the else belongs to the above "if (pin1State == closed)" instead of the "if (digitalRead(RELAY_PIN) == closed)"
    if (pin2State == opened) {
      // Keep relay off during the wait period
      digitalWrite(RELAY_PIN, LOW);  // Relay OFF

      // Start the timer only once
      if (!timerStarted) {
        relayStartMillis = millis();  // Record the start time
        timerStarted = true;          // Set the flag indicating the timer has started
      }

      // Wait for the 20-second delay to pass before activating the relay
      if (millis() - relayStartMillis >= delayTime) {
        // After 20 seconds, activate the relay
        digitalWrite(RELAY_PIN, HIGH);  // Relay ON

        // Reset the timer for the next cycle
        relayStartMillis = 0;  // Reset the timer
        timerStarted = false;  // Reset the timer flag
      }
    }
  }


  if (pin1State == opened && pin2State == closed) {
    if (relayStartMillis == 0) {
      relayStartMillis = millis();
    }

    if (millis() - relayStartMillis >= 3000) {
      digitalWrite(RELAY_PIN, closed);  // turn on the relay
      relayStartMillis = 0;             // Reset the timer to allow re-triggering the delay
    }
  }
}

This code is compiling for me. Please try again.


//#include <digitalWriteFast.h>

#define PIN1 2
#define PIN2 3
#define RELAY_PIN 4
#define BLINK_LED 5

#define OPENED 1
#define CLOSED 0

const byte opened = HIGH;
const byte closed = LOW;

unsigned long previousMillis = 0;
const long blinkInterval = 500;
bool blinkState = LOW;

unsigned long relayStartMillis = 0;     // Timer for relay
const unsigned long delayTime = 20000;  // 20 seconds
bool timerStarted = false;              // Flag to start the timer only once

void setup() {
  digitalWrite(RELAY_PIN, opened);

  pinMode(RELAY_PIN, OUTPUT);
  pinMode(PIN1, INPUT_PULLUP);
  pinMode(PIN2, INPUT_PULLUP);
  pinMode(BLINK_LED, OUTPUT);
}

void loop() {

  // Read the current time
  unsigned long currentMillis = millis();

  // LED Blinking Logic
  if (currentMillis - previousMillis >= blinkInterval) {
    previousMillis = currentMillis;
    blinkState = !blinkState;
    digitalWrite(BLINK_LED, blinkState ? HIGH : LOW);
  }


  int pin1State = digitalRead(PIN1);
  int pin2State = digitalRead(PIN2);

  if (digitalRead(RELAY_PIN) == closed) {
    if (pin1State == closed) {
      digitalWrite(RELAY_PIN, opened);  // Turn off relay
      relayStartMillis = 0;
    }
  } else { // *** This part was tripping up my compiler because it thought the else belongs to the above "if (pin1State == closed)" instead of the "if (digitalRead(RELAY_PIN) == closed)"
    if (pin2State == opened) {
      // Keep relay off during the wait period
      digitalWrite(RELAY_PIN, LOW);  // Relay OFF

      // Start the timer only once
      if (!timerStarted) {
        relayStartMillis = millis();  // Record the start time
        timerStarted = true;          // Set the flag indicating the timer has started
      }

      // Wait for the 20-second delay to pass before activating the relay
      if (millis() - relayStartMillis >= delayTime) {
        // Reset the timer for the next cycle
        relayStartMillis = 0;  // Reset the timer
        timerStarted = false;  // Reset the timer flag
      }
    }
  }


  if (pin1State == opened && pin2State == closed) {
    if (relayStartMillis == 0) {
      relayStartMillis = millis();
    }

    if (millis() - relayStartMillis >= 3000) {
      digitalWrite(RELAY_PIN, closed);  // turn on the relay
      relayStartMillis = 0;             // Reset the timer to allow re-triggering the delay
    }
  }
}

  • What is this doing ?
    if (pin1State == opened && pin2State == closed)
    {
      if (relayStartMillis == 0)
      {
        relayStartMillis = millis();
      }

      if (millis() - relayStartMillis >= 3000)
      {
        digitalWrite(RELAY_PIN, closed); // turn on the relay
        relayStartMillis = 0;  // Reset the timer to allow re-triggering the delay
      }
    }
1 Like

What does this do?

Should it be

digitalWrite(RELAY_PIN, CLOSED);  // Relay OFF

?

EDIT:
This will depend on your relay and your wiring. Which we do not have a clear understanding

Replying to: mancera1979

const byte closed = LOW;

LOW and closed are the same, so that doesn't really matter, what does matter is consistency and readability.

1 Like
  • Please confirm this preamble, it does not agree with your sketch.
At power up time, the relay is opened
I have the following sketch.
If pin2 is closed, pin1 is open, and relay is opened, then the relay should close.

While relay is closed, if pin2 opens, the relay should open
and then the device should be in suspended state for 20 seconds.

Within this 20 seconds all input pins should not work but other output pins should work as usual like blink led.

  • Try the sketch attached below.

  • I used the preamble shown in post #8.

  • I always start off a new sketch with a skeleton as seen in the code below.
    I then modify it to do what my preamble says.

  • The sketch below uses a class, makeTIMER and a class, makeInput.

  • Classes might make code look complicated, however, classes make your code organized and documented.

Example make a TIMER object:

//========================
makeTIMER relayDelayTIMER =
{
  //.TimerType, .Interval, .TimerFlag, .Restart, .SpeedAdjustPin
  MILLIS, 20 * 1000ul, DISABLED, NO, 0
};

Example, make an Input object:

//============  GPIO 2
makeInput switch1 =
{
  //.pin, .lastState
  2, OPENED
};
  • BTW, in the future, you must be consistent in your code, always use named variables with unique names; don’t use HIGH/LOW, true/false.
//================================================^================================================
//
//  https://forum.arduino.cc/t/millis-logic-not-working-properly/1363091
//
//  Millis Problem
//
//  Version    YY/MM/DD    Comments
//  =======    ========    ========================================================================
//  1.00       25/03/12    Running code
//
//
//
// Notes:
// I have the following sketch.
// At power up time, the relay is opended
// If pin2 is closed, pin1 is open, and relay is opened, then the relay should close.
//
// While relay is closed, if pin2 opens, the relay should go OFF
// and then the device should be in suspended state for 20 seconds.
//
// Within this 20 seconds all input pins should not work but other output pins should work as usual like blink led.
//


#include <digitalWriteFast.h>


//================================================
#define LEDon              HIGH   //PIN---[220R]---A[LED]K---GND
#define LEDoff             LOW

#define PRESSED            LOW    //+5V---[Internal 50k]---PIN---[Switch]---GND
#define RELEASED           HIGH

#define OPENED             HIGH  //+5V---[Internal 50k]---PIN---[Switch]---GND
#define CLOSED             LOW

#define ENABLED            true
#define DISABLED           false

#define RELAYopened        HIGH
#define RELAYclosed         LOW



//                          millis() / micros()   B a s e d   T I M E R S
//================================================^================================================
//
/*
  //========================
  makeTIMER toggleLED =
  {
     //.TimerType, .Interval, .TimerFlag, .Restart, .SpeedAdjustPin
     MILLIS/MICROS, 500ul, ENABLED/DISABLED, YES/NO, A0-A5

     //.SpeedAdjustPin defaults to 0 i.e. no speed adjustment is used
     //if .SpeedAdjustPin = A0-A5, a potentiometer on this pin adjusts the TIMER's speed (for diagnostics)
     //class static flag "makeTIMER::normalFlag" can be used to ENABLE/DISABLE adjustable TIMER speed,
     //ENABLE = normal speed, DISABLED = potentiometer controls TIMER speed
  };

  TIMER functions we can access:
  toggleLED.checkTIMER();
  toggleLED.enableRestartTIMER();
  toggleLED.restartTIMER()
  toggleLED.disableTIMER();
  toggleLED.expireTimer();
  toggleLED.setInterval(100ul);

  makeTIMER::normalFlag = ENABLED/DISABLED
*/


//These TIMER objects are non-blocking
class makeTIMER
{
#define MILLIS             0
#define MICROS             1

#define ENABLED            true
#define DISABLED           false

#define YES                true
#define NO                 false

#define STILLtiming        0
#define EXPIRED            1
#define TIMERdisabled      2


  private:

  public:

    static bool              normalFlag;      //when ENABLED, adjustable TIMERs run at normal speed

    unsigned long            Time;            //when the TIMER started

    //these "members" are needed to define a TIMER
    byte                     TimerType;       //what kind of TIMER is this? MILLIS/MICROS
    unsigned long            Interval;        //delay time which we are looking for
    bool                     TimerFlag;       //is the TIMER enabled ? ENABLED/DISABLED
    bool                     Restart;         //do we restart this TIMER   ? YES/NO
    byte                     SpeedAdjustPin;  //a potentiometer on this pin, A0-A5, adjusts TIMER speed

    //================================================
    //constructor with no parameters
    makeTIMER()
    {
      TimerType = MILLIS;
      Interval = 1000ul;
      TimerFlag = ENABLED;
      Restart = YES;
      SpeedAdjustPin = 0;

      Time = 0;
    }

    //================================================
    //constructor with parameters
    makeTIMER(byte _TimerType, unsigned long _Interval,
              bool _TimerFlag, bool _Restart, byte _SpeedAdjustPin = 0)
    {
      TimerType = _TimerType;
      Interval = _Interval;
      TimerFlag = _TimerFlag;
      Restart = _Restart;
      SpeedAdjustPin = _SpeedAdjustPin;

      Time = 0;
    }


    //================================================
    //condition returned: STILLtiming (0), EXPIRED (1) or TIMERdisabled (2)
    //function to check the state of our TIMER  ex: if(myTimer.checkTIMER() == EXPIRED);
    byte checkTIMER()
    {
      //========================
      //is this TIMER enabled ?
      if (TimerFlag == ENABLED)
      {
        //============
        //is this an adjustable TIMER OR is the "normalSpeed" switch closed ?
        if (SpeedAdjustPin == 0 || normalFlag == ENABLED)
        {
          //TIMER is not speed adjustable
          //has it expired ?
          if (getTime() - Time >= Interval)
          {
            //============
            //should this TIMER restart again?
            if (Restart == YES)
            {
              //restart this TIMER
              Time = getTime();
            }

            //this TIMER has expired
            return EXPIRED;
          }
        }

        //============
        //we are using a potentiometer to adjust the TIMER speed
        else
        {
          //for diagnostics, use a potentiometer to adjust TIMER speed
          //has it expired ?
          if (getTime() - Time >= Interval / adjustInterval())
          {
            //============
            //should this TIMER restart again?
            if (Restart == YES)
            {
              //restart this TIMER
              Time = getTime();
            }

            //this TIMER has expired
            return EXPIRED;
          }
        }

        return STILLtiming;

      } //END of   if (TimerFlag == ENABLED)

      //========================
      else
      {
        //this TIMER is disabled
        return TIMERdisabled;
      }

    } //END of   checkTime()

    //================================================
    //function to enable and restart this TIMER  ex: myTimer.enableRestartTIMER();
    void enableRestartTIMER()
    {
      TimerFlag = ENABLED;

      //restart this TIMER
      Time = getTime();

    } //END of   enableRestartTIMER()

    //================================================
    //function to disable this TIMER  ex: myTimer.disableTIMER();
    void disableTIMER()
    {
      TimerFlag = DISABLED;

    } //END of    disableTIMER()

    //================================================
    //function to restart this TIMER  ex: myTimer.restartTIMER();
    void restartTIMER()
    {
      Time = getTime();

    } //END of    restartTIMER()

    //================================================
    //function to force this TIMER to expire ex: myTimer.expireTimer();
    void expireTimer()
    {
      //force this TIMER to expire
      Time = getTime() - Interval;

    } //END of   expireTimer()

    //================================================
    //function to set the Interval for this TIMER ex: myTimer.setInterval(100);
    void setInterval(unsigned long value)
    {
      //set the Interval
      Interval = value;

    } //END of   setInterval()

    //================================================
    //function to return the current time
    unsigned long getTime()
    {
      //return the time             i.e. millis() or micros()
      //========================
      if (TimerType == MILLIS)
      {
        return millis();
      }

      //========================
      else
      {
        return micros();
      }

    } //END of   getTime()


    //================================================
    //for diagnostics, a potentiometer on an analog pin is used to adjust TIMER speed, thanks alto777
    float adjustInterval()
    {
      int Speed = analogRead(SpeedAdjustPin);
      float fSpeed = 1.0 + Speed * 12.0 / 1023.0;

      return fSpeed;

    } //END of   adjustInterval()

}; //END of   class makeTIMER

//create/initialize static "normalFlag" of class makeTIMER
bool makeTIMER::normalFlag = DISABLED;


//================================================^================================================

//                             D e f i n e   a l l   o u r   T I M E R S
//================================================^================================================
/*example

  //========================
  makeTIMER toggleLED =
  {
     //.TimerType, .Interval, .TimerFlag, .Restart, .SpeedAdjustPin
     MILLIS/MICROS, 500ul, ENABLED/DISABLED, YES/NO, A0-A5

     //.SpeedAdjustPin defaults to 0 i.e. no speed adjustment is used
     //if .SpeedAdjustPin = A0-A5, a potentiometer on this pin adjusts the TIMER's speed (for diagnostics)
     //class static flag "makeTIMER::normalFlag" can be used to ENABLE/DISABLE adjustable TIMER speed,
     //ENABLE = normal speed, DISABLED = potentiometer controls TIMER speed
  };

  TIMER functions we can access:
  toggleLED.checkTIMER();
  toggleLED.enableRestartTIMER();
  toggleLED.restartTIMER()
  toggleLED.disableTIMER();
  toggleLED.expireTimer();
  toggleLED.setInterval(100ul);

  static variable access
  makeTIMER::normalFlag = ENABLED/DISABLED */

//========================
//example: uses default library values
//makeTIMER testTIMER{};

//========================
makeTIMER heartbeatTIMER =
{
  //.TimerType, .Interval, .TimerFlag, .Restart, .SpeedAdjustPin
  MILLIS, 500ul, ENABLED, YES, 0
};

//========================
makeTIMER switchesTIMER =
{
  //.TimerType, .Interval, .TimerFlag, .Restart, .SpeedAdjustPin
  MILLIS, 5ul, ENABLED, YES, 0
};

//========================
makeTIMER relayDelayTIMER =
{
  //.TimerType, .Interval, .TimerFlag, .Restart, .SpeedAdjustPin
  MILLIS, 1000ul, DISABLED, NO, 0
};



//                                  c l a s s    m a k e I n p u t
//================================================^================================================
//a class to define input objects, switches or sensors
//
#define NOTvalidated       0
#define VALIDATED          1
#define NOchange           2

//================================================
class makeInput
{
  private:

  public:

    static byte filter;
    //say the above validating "filter" variable is set to 10
    //if we scan "inputs" every 5ms
    //i.e. we sample our inputs every 5ms looking for a change in state.
    //5ms * 10 = 50ms is needed to validate a switch change in state.
    //i.e. A switch change in state is valid "only after" 10 identical changes are detected.
    //This technique is used to filter out EMI (spikes), noise, etc.
    //i.e. we ignore switch changes in state that are less than 50ms.

    byte pin;                       //the digital input pin number
    byte lastState;                 //the state the input was last in

    unsigned long switchTime;       //the time the switch was closed
    byte counter;                   //a counter used to validate a switch change in state

    //================================================
    //constructor with parameters
    makeInput(byte _pin, byte _lastState)
    {
      pin = _pin;
      lastState = _lastState;

      switchTime = 0;
      counter = 0;

      pinMode(pin, INPUT_PULLUP);
    }


    //================================================
    //condition returned: NOTvalidated (0), VALIDATED (1) or NOchange (2)
    //check to see if the input object has had a valid state change
    byte validChange()
    {
      byte pinState = digitalRead(pin);

      //===================================
      //has there been an input change in state ?
      if (lastState != pinState)
      {
        counter++;

        //is this "change in state" stable ?
        if (counter >= filter)
        {
          //an input change has been validated
          //get ready for the next sequence
          counter = 0;

          //update to this new state
          lastState = pinState;

          return VALIDATED;
        }

        return NOTvalidated;
      }

      //===================================
      //there has not been an input change in state
      counter = 0;

      return NOchange;

    } //END of   validChange()

}; //END of   class makeInput

//===================================
//initialize the validating filter
byte makeInput::filter = 10;


//                              G P I O s   A n d   V a r i a b l e s
//================================================^================================================
//

//INPUTS
//================================================
//

//============  GPIO 2
makeInput switch1 =
{
  //.pin, .lastState
  2, OPENED
};

//============  GPIO 3
makeInput switch2 =
{
  //.pin, .lastState
  3, OPENED
};




//OUTPUTS
//================================================
const byte relayPin               = 4;
const byte heartbeatLED           = 5;


//VARIABLES
//================================================
//
//unsigned long suspenedInterval  = 10 * 1000ul;  //10 seconds
unsigned long suspenedInterval    = 20 * 1000ul;  //20 seconds


//                                           s e t u p ( )
//================================================^================================================
//
void setup()
{
  Serial.begin(115200);

  digitalWriteFast(heartbeatLED, LEDoff);
  pinMode(heartbeatLED, OUTPUT);

  //at power up time, the relay is opended
  digitalWriteFast(relayPin, RELAYopened);
  pinMode(relayPin, OUTPUT);

} //END of   setup()


//                                            l o o p ( )
//================================================^================================================
//
void loop()
{
  //================================================
  //Print the time it takes to return to this same spot.
  //comment the next 3 lines when no longer needed
  //static unsigned long startTime;
  //Serial.println(micros() - startTime);
  //startTime = micros();

  //================================================
  //PULSE62D13;
  //PULSE62D13;


  //========================================================================  T I M E R  heartbeatLED
  //condition returned: STILLtiming, EXPIRED or TIMERdisabled
  //is it time to toggle the heartbeat LED ?
  if (heartbeatTIMER.checkTIMER() == EXPIRED)
  {
    //toggle the heartbeat LED
    digitalWriteFast(heartbeatLED, digitalRead(heartbeatLED) == HIGH ? LOW : HIGH);
  }

  //========================================================================  T I M E R  switches
  //condition returned: STILLtiming, EXPIRED or TIMERdisabled
  //is it time to check our switches ?
  if (switchesTIMER.checkTIMER() == EXPIRED)
  {
    checkSwitches();
  }

  //========================================================================  T I M E R  relayDelay
  //condition returned: STILLtiming, EXPIRED or TIMERdisabled
  //has this TIMER expired ?
  if (relayDelayTIMER.checkTIMER() == EXPIRED)
  {
    //we are finished with this TIMER
    relayDelayTIMER.disableTIMER();
  }


  //================================================
  //       Other non blocking code goes here
  //================================================


} //END of   loop()


//                                   c h e c k S w i t c h e s ( )
//================================================^================================================
//object.validChange()    - checks to see if there was a valid state change
//object.pin              - hardware pin number
//object.lastState        - the state the switch is in right now
//object.switchTime       - an unsigned long variable where we can save millis()

void checkSwitches()
{
  //========================================================================  switch1
  //was there a validated input change ?
  if (switch1.validChange() == VALIDATED)
  {
    //========================
    //did this switch go closed ?
    if (switch1.lastState == CLOSED)
    {
    }

    //========================
    //this switch went opened
    else
    {
    }

  } //END of mySwitch1


  //========================================================================  switch2
  //was there a validated input change ?
  if (switch2.validChange() == VALIDATED)
  {
    //========================
    //did this switch go closed ?
    if (switch2.lastState == CLOSED)
    {
      //condition returned: STILLtiming, EXPIRED or TIMERdisabled
      //make sure we are not timing
      if (relayDelayTIMER.checkTIMER() == TIMERdisabled)
      {
        if (digitalRead(relayPin) == RELAYopened)
        {
          if (switch1.lastState == OPENED)
          {
            digitalWriteFast(relayPin, RELAYclosed);
          }
        }
      }
    }

    //========================
    //this switch went opened
    else
    {
      if (digitalRead(relayPin) == RELAYclosed)
      {
        digitalWriteFast(relayPin, RELAYopened);
        
        //set TIMER interval
        relayDelayTIMER.setInterval(suspenedInterval);

        //Start timing
        relayDelayTIMER.enableRestartTIMER();
      }
    }

  } //END of switch2

} //END of   checkSwitches()


//================================================^================================================

Try out the above sketch, if you have questions, ask them.

EDIT

Updated sketch

Hello Larry,

I am using Attiny2313 microcontroller as I am using very few pins for my application. The sketch you provided is bit big one for MC. I am trying to reduce the code and test.

Have I already asked what the programme is for in real life?

I am going to use in simple home automation project

i get the following warning/errors

C:\stuff\SW\Arduino\_Others\Tst\Tst.ino:3:0: warning: "PIN1" redefined
 #define PIN1       2
 
In file included from c:\users\lenovo\appdata\local\arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7\avr\include\avr\io.h:715:0,
                 from c:\users\lenovo\appdata\local\arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7\avr\include\avr\pgmspace.h:90,
                 from C:\Users\lenovo\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\cores\arduino/Arduino.h:28,
                 from sketch\Tst.ino.cpp:1:
c:\users\lenovo\appdata\local\arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7\avr\include\avr\portpins.h:71:0: note: this is the location of the previous definition
 #define    PIN1         1
 
C:\stuff\SW\Arduino\_Others\Tst\Tst.ino:4:0: warning: "PIN2" redefined
 #define PIN2       3
 
In file included from c:\users\lenovo\appdata\local\arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7\avr\include\avr\io.h:715:0,
                 from c:\users\lenovo\appdata\local\arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7\avr\include\avr\pgmspace.h:90,
                 from C:\Users\lenovo\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\cores\arduino/Arduino.h:28,
                 from sketch\Tst.ino.cpp:1:
c:\users\lenovo\appdata\local\arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7\avr\include\avr\portpins.h:70:0: note: this is the location of the previous definition
 #define    PIN2         2
 
C:\stuff\SW\Arduino\_Others\Tst\Tst.ino: In function 'void loop()':
Tst:85:5: error: expected '}' at end of input
     }
     ^
exit status 1
expected '}' at end of input

when i more correctly format your code, i don't see how any processing occurs when the RELAY_PIN is NOT closed. are you missing an else

#include <digitalWriteFast.h>

#define PIN1       2
#define PIN2       3
#define RELAY_PIN  4
#define BLINK_LED  5

#define OPENED     1
#define CLOSED     0

const byte opened = HIGH;
const byte closed = LOW;

unsigned long previousMillis = 0;
const long blinkInterval = 500;
bool blinkState = LOW;

unsigned long relayStartMillis = 0;  // Timer for relay
const unsigned long delayTime = 20000;  // 20 seconds
bool timerStarted = false;  // Flag to start the timer only once

void setup () {
    digitalWriteFast (RELAY_PIN, opened);

    pinMode (RELAY_PIN, OUTPUT);
    pinMode (PIN1, INPUT_PULLUP);
    pinMode (PIN2, INPUT_PULLUP);
    pinMode (BLINK_LED, OUTPUT);
}


void loop () {

    // Read the current time
    unsigned long currentMillis = millis ();

    // LED Blinking Logic
    if (currentMillis - previousMillis >= blinkInterval) {
        previousMillis = currentMillis;
        blinkState = !blinkState;
        digitalWrite (BLINK_LED, blinkState ? HIGH : LOW);
    }

    int pin1State = digitalReadFast (PIN1);
    int pin2State = digitalReadFast (PIN2);

    if (digitalRead(RELAY_PIN) == closed) {
        if (pin1State == closed) {
            digitalWriteFast (RELAY_PIN, opened); // Turn off relay
            relayStartMillis = 0;
        }
        else {
            if (pin2State == opened) {
                // Keep relay off during the wait period
                digitalWrite (RELAY_PIN, LOW);  // Relay OFF

                // Start the timer only once
                if (!timerStarted) {
                    relayStartMillis = millis ();  // Record the start time
                    timerStarted = true;  // Set the flag indicating the timer has started
                }

                // Wait for the 20-second delay to pass before activating the relay
                if (millis() - relayStartMillis >= delayTime) {
                    // After 20 seconds, activate the relay
                    digitalWrite (RELAY_PIN, HIGH);  // Relay ON

                    // Reset the timer for the next cycle
                    relayStartMillis = 0;  // Reset the timer
                    timerStarted = false;  // Reset the timer flag
                }
            }
        }

        if (pin1State == opened && pin2State == closed) {
            if (relayStartMillis == 0) {
                relayStartMillis = millis ();
            }

            if (millis() - relayStartMillis >= 3000) {
                digitalWrite (RELAY_PIN, closed); // turn on the relay
                relayStartMillis = 0;  // Reset the timer to allow re-triggering the delay
            }
        }
    }
}

No. I did not miss anything. I tried the logic as per my requirement but failed.

i guess you know best. sorry for trying to help

2 Likes

If it is just 2 input pins and a relay plus a status blinker,

I would run the blinker as one task in loop() and a 3-bit switch-case inside of a timer as another task in loop() with the 3 bits determined just before the switch-case runs.

How I would do the latter ---

In the pins & relay task function, first is a timer that returns if millis() - start < interval else sets interval = 0.
As long as the interval > 0, reading the pins and relay and changing anything does not happen.

Then read all 3 before the switch-case as state;
bit 0 = relay state open/closed,
bit 1 = pin 1 state LOW/HIGH,
bit 2 = pin 2 state LOW/HIGH.

Then switch ( state );
Case 0B100 (pin 2 HIGH, pin 1 LOW, relay open) should close the relay.
break;
Case 0B101 (pin 2 HIGH, pin 1 LOW, relay closed) changes nothing.
break;
Case 0B001 (pin 2 LOW, pin 1 LOW, relay closed)
open the relay
set timer interval to 20 seconds, the switch-case will not run for 20 seconds, pins 1 and 2 will be LOW and the relay open.
break;
Case 0B000 (pin 2 LOW, pin 1 LOW, relay open) wait time from case 0B001 is over, so set pin 1 HIGH or what?
break;

There should be more cases but those 4 are set by:

If pin2 is closed, pin1 is open, and relay is opened, then the relay should close.

While relay is closed, if pin2 opens, the relay should open
and then the device should be in suspended state for 20 seconds.

Within this 20 seconds all input pins should not work but other output pins should work as usual like blink led.

The blink task runs itself. Pins 1, 2 and relay are only changed in the the switch-case. Pin 1 is not used for serial on this ATtiny, right?

What cases need to run concerning pin 1? How does it get changed and what actions does pin 1 HIGH change?
There are 4 more cases possible.
Every case is a response to the immediate states of pin 1, pin 2 and the relay. Change nothing is a permissible response.

Be aware that if either pin is connected to a contact switch that there needs to be a task to debounce the switch and that the switch-case (state machine) above run not on pin state but on debounced switch status instead.... not a biggie, I have a function for that and 999 other members here have their way to do it.

Any details ?

Try this. If this still doesn't work, then I am tapping out.

//#include <digitalWriteFast.h>

#define PIN1 2
#define PIN2 3
#define RELAY_PIN 4
#define BLINK_LED 5

#define ON HIGH
#define OFF LOW

#define BLINK_INTERVAL 500UL
#define SUSPENDED_INTERVAL 20000UL

bool blinkState = LOW;
bool pin1State = LOW;
bool pin2State = LOW;
bool relayState = LOW;

bool isSuspended = false;

void setup() {
  digitalWriteFast(RELAY_PIN, OFF);

  pinMode(RELAY_PIN, OUTPUT);
  pinMode(PIN1, INPUT_PULLUP);
  pinMode(PIN2, INPUT_PULLUP);
  pinMode(BLINK_LED, OUTPUT);
}

void loop() {

  // Read the current time
  unsigned long currentMillis = millis();
  static unsigned long previousMillis = 0;
  static unsigned long previousSuspensionMillis = 0;

  pin1State = digitalReadFast(PIN1);
  pin2State = digitalReadFast(PIN2);
  relayState = digitalReadFast(RELAY_PIN);

  // LED Blinking Logic
  if ((currentMillis - previousMillis) >= BLINK_INTERVAL) {
    previousMillis = currentMillis;
    blinkState = !blinkState;
    digitalWrite(BLINK_LED, blinkState);
  }

  if (isSuspended == false) {
    // Relay and Button logic
    switch (relayState) {
      case OFF:
        if (pin1State == HIGH && pin2State == LOW) {
          digitalWrite(RELAY_PIN, ON);
        }
        break;

      case ON:
        if (pin2State == HIGH) {
          digitalWrite(RELAY_PIN, OFF);
          previousSuspensionMillis = currentMillis;
          isSuspended = true;
        }
        break;
    }
  } else { // if isSuspended == true
    if ((currentMillis - previousSuspensionMillis) >= SUSPENDED_INTERVAL) {
      isSuspended = false;
    }
  }
}

So the suspension portion overrides the first requirement.

Logic-wise, it sounds as if the relay is only doing three different things:

SuspendingInput
DrivingRelay
AwaitingActivation

If you had a variable tracking where the system was in doing those things, it could help simplify the logic, versus keeping track of the several separate binary variables.

Another trick for keeping track of the logic is naming things well. For example, @HazardsMind's isSuspended is a better name than timerStarted because it is descriptive of behavior the system, rather than of the state of a subcomponent.

  • Did you try it ?

  • An Arduino Pro Mini is physically small. :thinking: