DS3231 RTC on hour/off hour question

Hi there,

I have a small project working with a ds3231 rtc using the ds3231 library to schedule some relays.

Everything is working, but after tinkering and getting everything up, I realized that the triggering event for the relays is the exact minute I've set in the onhour/off hour constants. If I restart the system after the onhour time, there is no trigger to initiate the relay until the onhour cycles around again tomorrow morning.

Is there a way to say that the onhour is between onhour and offhour so that the relay is triggered at any point during the on period?

Ultimately, this is not a make or break problem, but just one of those cool little problems that help illustrate more of the inner workings of the logic. Happy to have this problem :slight_smile:

Please read Read this before posting a programming question then post your code using code tags

You could save the on/off state in EEPROM so that when the project is started you can set the on/off state as it was. Save the state whenever it changes and read and set the state in setup().


I'm not sure if that will help, but instead of working with hours it's easier to work with seconds, this way you can easily check if the current time is within the on or off period

time_t getSecond( uint8_t hour, uint8_t min, uint8_t sec )
	return ( hour * 3600UL ) + ( min * 60UL ) + sec;

Here is an example, it's a modified part of an actual working code so it won't compile for you, but you get the idea:

time_t photoperiodStart = getSecond(  6, 0, 0 ); // light relay starts at 6h00
time_t photoperiodEnd   = getSecond( 18, 0, 0 ); // light relay stops at 18h00

Then in loop()

time_t now = rtc.get();
static time_t past = now;

if ( now != past ) // every second
	past = now;
	nowLocal = tz.toLocal( now );

	if ( photoperiodStart != photoperiodEnd )
		time_t todaySeconds = getSecond( hour( nowLocal ), minute( nowLocal ), second( nowLocal ) );
		bool inverted = photoperiodEnd < photoperiodStart;
		if ( todaySeconds >= _min( photoperiodStart, photoperiodEnd ) && todaySeconds <= _max( photoperiodStart, photoperiodEnd ) )
			mcp23s17.digitalWrite( lightRelayPin, inverted ? LOW : HIGH );
			mcp23s17.digitalWrite( lightRelayPin, inverted ? HIGH : LOW );

guix - this looks very nice. I 'll try it out when I have some time to tinker.

Thank you both for your suggestions.


:slight_smile: I've just realized that I could simplify my code like this

if ( todaySeconds >= ( inverted ? photoperiodEnd : photoperiodStart ) && todaySeconds <= ( inverted ? photoperiodStart : photoperiodEnd ) )

It is easy to use "minutes since midnight" for timed tasks.

The following function determines whether a specific time is within an interval, and works if the interval spans midnight (e.g. if you want a lamp on between 11 PM and 1 AM the next day).

// this function determines whether the current time in minutes past midnight 
// is within the timed interval start and start+duration, also in minutes past midnight
// range may span midnight
#define MIN_PER_DAY 1440
byte state(unsigned int start, unsigned int duration, unsigned int now) {
  unsigned int time_on = (now - start + 2*MIN_PER_DAY) % MIN_PER_DAY;  //multiply minutes per day by two for safety
  if (time_on < duration) return 1;  //within interval
  return 0;  //not within interval