RTC Timer Relay issue

I'm making a project using an Arduino Nano, DS3231 RTC module and a 4 channel 5V Relay module. I want to run some appliances when a certain time is set with the relays. Everything is hooked up and the hardware setup works fine. My problem is with the programming side. I've set the time and the serial monitor confirms that the RTC works ok. Then I changed the program to set the timer to work when relay wants to trigger ( 18:00 to ON and 23:00 to OFF ). The RTC keeps memory of it times. The relay works ON when 18:00 Trigger On and at 23:00 hours, the relay goes OFF. But the weird thing is that after half an hour the relay goes back to ON. I don't know why.

my code is


//SPECIFIC TIME TRIGGER RELAY
#include <DS3231.h>

int RelayA = 12;    // Pin Number for Relay
int RelayB = 11;    // Pin Number for Relay
int RelayC = 10;    // Pin Number for Relay
int RelayD = 9;     // Pin Number for Relay

DS3231  rtc(SDA, SCL);
Time t;

const int OnHour = 18;  //SET TIME TO ON RELAY (24 HOUR FORMAT)
const int OffHour = 23; //SET TIME TO OFF RELAY


void setup() {
  Serial.begin(115200);
  rtc.begin();
  pinMode(RelayA, OUTPUT);     // Set Pinmode of OUTPUT
  pinMode(RelayB, OUTPUT);     // Set Pinmode of OUTPUT
  pinMode(RelayC, OUTPUT);     // Set Pinmode of OUTPUT
  pinMode(RelayD, OUTPUT);     // Set Pinmode of OUTPUT
  digitalWrite(RelayA, HIGH);  // High triggers OFF
  digitalWrite(RelayB, HIGH);  // High triggers OFF
  digitalWrite(RelayC, HIGH);  // High triggers OFF
  digitalWrite(RelayD, HIGH);  // High triggers OFF
}

void loop() {
  t = rtc.getTime();
  Serial.println(rtc.getTimeStr());
  delay (1000);
  
  if(t.hour >= OnHour && t.hour <= OffHour)  // If 18:00 >= & 23:00<= then Relay is ON
  {
    digitalWrite(RelayA,LOW);
    digitalWrite(RelayB,LOW);
    digitalWrite(RelayC,LOW);
    digitalWrite(RelayD,LOW);
    Serial.println("LIGHT ON");
    }
    
    else       // Else Relay if OFF
    {
      digitalWrite(RelayA,HIGH);
      digitalWrite(RelayB,HIGH);
      digitalWrite(RelayC,HIGH);
      digitalWrite(RelayD,HIGH);
      Serial.println("LIGHT OFF");
    }
}

The library is Library: DS3231 from Rinky-Dink Electronics

Your issue is likely caused by the logic in this condition:

if(t.hour >= OnHour && t.hour <= OffHour)  

Write it the following way instead:

void loop() {
  t = rtc.getTime();
  Serial.println(rtc.getTimeStr());
  delay (1000);
  
  if (t.hour >= OnHour && t.hour < OffHour)  // Now OffHour is exclusive
  {
    digitalWrite(RelayA, LOW);
    digitalWrite(RelayB, LOW);
    digitalWrite(RelayC, LOW);
    digitalWrite(RelayD, LOW);
    Serial.println("LIGHT ON");
  }
  else
  {
    digitalWrite(RelayA, HIGH);
    digitalWrite(RelayB, HIGH);
    digitalWrite(RelayC, HIGH);
    digitalWrite(RelayD, HIGH);
    Serial.println("LIGHT OFF");
  }
}

2 Likes

So, my logic is like
if 18:00 is greater than or equals - and- 23:00 smaller than or equals

The new logic is
if 18:00 is greater than or equals - and- 23:00 smaller than

Is this correct?

1 Like

Either way, the logic is primitive. There's no need to do the digitalWrite() calls on every pass through loop. The hour only changes once per hour, you're calling digitalWrite() 3600 times per hour. Instead of checking when the hour IS some value, check when it BECOMES that value. The concept is similar to that in the StateChangeDetection example that's built into the Arduino IDE.

Something like:

void loop() {
  t = rtc.getTime();
  static uint8_t previousHour {t.hour};
  Serial.println(rtc.getTimeStr());
  delay (1000);

  uint8_t currentHour {t.hour};
  if (currentHour != previousHour) {
    previousHour = currentHour;
    if (currentHour == OnHour) {
      // Turn on Relays here
    } else if (currentHour == Off Hour) {
      // Turn off Relays here
    }
  }
}
2 Likes

So what happens if the response is corrupted?

Better to use else if like @gfvalvo has on the code above.

You propose changing the logic of

  if (t.hour >= OnHour && t.hour <= OffHour) {
  }
  else {
  }

to

  if (t.hour >= OnHour && t.hour < OffHour) {
  }
  else {
  }

I don’t see that explaining or fixing the OP’s claim that "after half an hour the relay goes back to ON"

It would mean that the switching time was 11 PM as opposed to midnight.

I might do the logic differently as has been variously suggested. But none of that addresses the half hour thing.

a7

1 Like

The only way I see the happening is if you get a bad response from the RTC. Unfortunately there is not much error checking on the I2C. What time did Arduino print before the relay turned on again?

How is this project powered and which Arduino board are you using ?

The power supply comes from 4x LiFePO batteries and step downs with a LM2596 Buck converter module t0 5V. Arduino board is a Nano.

I tried the code from @ahsrabrifat and the project works for now. I guess it's a logical issue.

And my guess is that whatever was really wrong will be back one day wrong as ever.

Or maybe not. Are you lucky?

a7

So, is it a hardware fault? How can I find what's actually wrong?

It's not necessarily "fault". Just unexpected response is enough to turn off your relay.
Read posts # 4,5,6,7.

In your original code you have

if(t.hour >= OnHour && t.hour <= OffHour)

as the condition for eveything to be ON. The '<=' means this runs from OnHour through OffHour.

Yet you say it turned off at 23:00. I read it as turning off at 0:00.

That's one thing, maybe you were reporting it wrong or I am reading the code wrong or you changed it since. No matter.

But you also report that "after half an hour the relay goes back to ON." I am sure there is nothing in the code that would make anything happen other than very near to exactly on the hour, whatever the hour may turn out to be.

Since it now works but didn't, and I do not believe @ahsrabrifat's change is meaningful other than fixing running through the 23rd hour rather than stopping at that time, we are compelled (!) to look elsewhere, rather than guess and move on.

You haven't posted a schematic or any pictures. Perhaps there is a loose connection or a marginal power supply issue or... a few things that are not the code.

Pro tip: whilst testing, use seconds not hours and fix the logic to, say, be on from 10 seconds to 42 seconds. This will make finding or seeing faults much quicker. How much quicker, Mr. Spock? :expressionless:

a7

1 Like

Here is my schematics

I looked at the code from @gfvalvo and I don't know will this work in case of a power outage. Like example, if I had a power outage at 19:00 and gets power back at 20:00, will this still work? I used that > and < in case of power outage.

No, but you could resolve that by checking the time in setup() which runs once when arduino starts up.

Anyway, alto777 posted above, there's nothing in your code that could turn relays ON after half an hour.

I think that's right. If you see only the hour change as an opportunity to turn on, or off, the stuff it will not handle a power outrage.

You could create more code somewhere to know and handle this, or leave the logic you arrived at.

If turning on something that is already on, or off something already off offends or is not a good idea because of whatever it is you are turning on and off, that can be handled very simple.

Of course there is no reason and no harm writing HIGH to a bit that is already. But if turning stuff on meant some kind of startup that should only be done once, you can track it with a boolean variable like

bool stuffBeOn;

And later to turn something on but not again write

  if (!stuffBeOn) {

// code to start and turn on something

    stuffBeOn = true;
  }

and obvsly I hope to turn stuff off

  if (stuffBeOn) {

// code to shut down and turn off something

    stuffBeOn = false;
  }

Don't miss the '!' logical negation operator in the turning on if condition: "If the thing is not on, turn it on and remember that you did."

a7

1 Like

there's nothing in your code that could turn relays ON after half an hour.

That's still a mystery. Maybe the Library is corrupt or half baked. I saw many posts in this forum saying this particular library is not recommended.

2 days on and this works without any issues. I think @ahsrabrifat logic works but the code for @gfvalvo is the good one.

Print the time out inside the if statement clauses that the turn the relays on / off. That will tell you why execution entered the clause.

As already mentioned, that is properly handled in the setup() function which will run once (and only once) after power up.

Your loop code is more properly handled using the state changed detection model as I've stated. The way you have it doesn't lend itself to adding new features. Also, you should ditch the delay() statement in favor of a millis() timer to handle periodic tasked. That will make your code non-blocking and again more conducive to moving beyond its current, primitive functionality.

will this works?

#include <DS3231.h>

int RelayA = 12;    // Pin Number for Relay A
int RelayB = 11;    // Pin Number for Relay B
int RelayC = 10;    // Pin Number for Relay C
int RelayD = 9;     // Pin Number for Relay D

DS3231 rtc(SDA, SCL);
Time t;

const int OnHour = 18;  // Time to turn ON relays (24-hour format)
const int OffHour = 23; // Time to turn OFF relays (24-hour format)

int lastHour = -1;  // Variable to store the previous hour to detect change
bool relayState = false; // Track relay state: false = OFF, true = ON

void setup() {
  Serial.begin(115200);
  rtc.begin();

  // Initialize each relay pin and set them to OFF (HIGH for active-high relays)
  pinMode(RelayA, OUTPUT);
  pinMode(RelayB, OUTPUT);
  pinMode(RelayC, OUTPUT);
  pinMode(RelayD, OUTPUT);

  // Set all relays to OFF initially (HIGH means OFF for active-high relays)
  digitalWrite(RelayA, HIGH);
  digitalWrite(RelayB, HIGH);
  digitalWrite(RelayC, HIGH);
  digitalWrite(RelayD, HIGH);

  // Check the current time immediately after power-up
  t = rtc.getTime();
  
  // If the current time is within the ON period (18:00 to before 23:00), turn the relays ON
  if (t.hour >= OnHour && t.hour < OffHour) {
    digitalWrite(RelayA, LOW);
    digitalWrite(RelayB, LOW);
    digitalWrite(RelayC, LOW);
    digitalWrite(RelayD, LOW);
    Serial.println("LIGHT ON");
    relayState = true;  // Set relay state to ON
  } else {
    // If the current time is outside the ON period, keep the relays OFF
    Serial.println("LIGHT OFF");
    relayState = false; // Set relay state to OFF
  }
}

void loop() {
  t = rtc.getTime();
  
  // Check if the hour has changed
  if (t.hour != lastHour) {
    // Update the last hour to the current hour
    lastHour = t.hour;

    // Check if the current time is within the ON period (18:00 to before 23:00)
    if (t.hour >= OnHour && t.hour < OffHour && !relayState) {
      // Turn ON all relays (set to LOW to trigger the relays ON)
      digitalWrite(RelayA, LOW);
      digitalWrite(RelayB, LOW);
      digitalWrite(RelayC, LOW);
      digitalWrite(RelayD, LOW);
      Serial.println("LIGHT ON");
      relayState = true;  // Update the state to ON
    }
    // Check if the current time is outside the ON period (after 23:00 or before 18:00)
    else if ((t.hour >= OffHour || t.hour < OnHour) && relayState) {
      // Turn OFF all relays (set to HIGH to trigger the relays OFF)
      digitalWrite(RelayA, HIGH);
      digitalWrite(RelayB, HIGH);
      digitalWrite(RelayC, HIGH);
      digitalWrite(RelayD, HIGH);
      Serial.println("LIGHT OFF");
      relayState = false;  // Update the state to OFF
    }
  }

  delay(1000); // Check the time every second, but only update on hour change
}