Define time period with RTC DS3231

I am trying to define a time period with the DS3231.

I want some lights to be on from 8:00 until 10:00 (in my code this is defined as morning)

and then some other lights should come on from 10:00 until 14:00 but the first lights should be off (this is defined as noon)

bool ItsMorning(const DateTime &checkTime) //morning == 8:00-10:00
{
  return checkTime.hour() >= 8 && checkTime.hour() < 10;
}

bool ItsNoon(const DateTime &checkTime) //noon == 10:00-14:00
{
  return checkTime.hour() >= 10 && (checkTime.hour() < 14;
}

By connecting the time with && the arduino isn’t able to deal with the comparison. Can anyone tell me how to code this correctly?

Thank you.

By connecting the time with && the arduino isn’t able to deal with the comparison.

What does that mean? Do you get an error message?

return checkTime.hour() >= 10 && (checkTime.hour() < 14;

There is a syntax error in this line.

Hello Pylon

yes, true about the syntax error. Must have happened when I copied it.

No error message. But the programm doesnt set "ItsMorning" if I combine both times with &&. If I only put hour >=8 && minutes >= 0 it works.

if ( checkTime.hour() >= 8 && checkTime.minute() > =0 )

The programm changes to ItsMorning, but as this condition is always true and there is no end statement it will always stay morning.

I tried to put the end statement before the start statement (the time setting is just for testing):

bool ItsMorning(const DateTime &checkTime) //morning == 8:00-10:00
{
  bool result = 0;
  if ( checkTime.hour() > 17 && checkTime.minute() > 5 )
      result = false;
  else if ( checkTime.hour() >= 17 && checkTime.minute() >= 4 )
      result = true;
                return result;
}

In this case ItsMorning will be set at 17:04 but for a reason I dont understand ItsMorning will not be turned off at 17:05.

In this case ItsMorning will be set at 17:04 but for a reason I dont understand ItsMorning will not be turned off at 17:05.

I would expect it to start at 17:04 and end at 18:06.

But the programm doesnt set "ItsMorning" if I combine both times with &&. If I only put hour >=8 && minutes >= 0 it works.

So you should post the definition of DateTime. It must change the value returned by the hour() method between calls because otherwise the code should return the correct result.

It is much easier to use “minutes past midnight” to define on/off times and time intervals during the day.

This function checks whether a given time of day, in minutes past midnight, is with the interval defined by two endpoints, even if those endpoints span midnight.

// 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
}

Easily extended to seconds past midnight, if you need more accuracy.

Thank you both!

@pylon:

I would expect it to start at 17:04 and end at 18:06.

How come 18:06?

you should post the definition of DateTime

Isn't it the class for RTClib for the rtc.now() function? I wouldn't have a definition of my own.

This is what I use to call the function:

/*For debugging*/
if ( ItsDawn(dateTimenow) )
{
  Serial.println("ItsDawn");
}

This is how I define dateTimenow:

/*Definition of dateTimenow and Initialization of rtc object*/
DateTime dateTimenow = 0;
RTC_DS3231 rtc; //Initialize RTC object

/*Getting the time in loop()*/
dateTimenow = rtc.now();

@jremington This seems like a very good idea and handy to apply!

even if those endpoints span midnight.

How will it know, when we pass midnight?

//multiply minutes per day by two for safety

Why for safety?

I will try this out. Thank you very much. At first glance I think this will make it much easier.

How will it know, when we pass midnight?

The function does not know about the passage of time. It simply decides if the time point is within the start and end times. The modulus operator (%) takes care of wrap around at midnight, like compass wrap at 360.

Why for safety?

It might help to make certain that the result is positive. The actual multiplication factor doesn't matter, as long as it is an integer, 1 to 4 inclusive.

Thank you again. I realized that there is still one thing unclear. Where does Arduino get the amount of minutes for the now variable from?

From where ever the Arduino gets the time of day.

In 24 hour time mode, minutes past midnight = hours*60+minutes.

Ok. So I would add some math to calculate the "now-time"?

How come 18:06?

Because for the first check you used “<” and not “<=”.

Isn’t it the class for RTClib for the rtc.now() function? I wouldn’t have a definition of my own.

You didn’t tell us yet that you’re using that library.

pylon:
Because for the first check you used “<” and not “<=”.

You didn’t tell us yet that you’re using that library.

Ok. Thank you. But why 18:06? And not any other time?

if ( checkTime.hour() > 17 && checkTime.minute() > 5 )

The first time that's true is at 18:06 (hour is 18, so > 17 and minute is 6, so > 5). And it stops being true at midnight.

Ok Thank you wvmarle!

Actually the minutes after midnight strategy was great! Thank you jremington! That really helped a lot!

One of my projects uses minutes of the week, starting counting at Sunday midnight. That as alarms are operating on a weekly basis in that one. To calculate that I'm using the day of the week (0-6) plus current time. Makes life a lot easier that way :-)

Key of the strategy: reduce the time to a single number, that's always easiest to work with.

Thank you wvmarle I will try to stick to that.

I have come across another question. I took one time let’s say 8:00 as starting time of an action (start a sunrise simulation) and gave it the duration of 1min. My goal was just to kick the action off. And not to wait for a minute. My code waits however for the time of the duration (if I put 2min for duration it waits 2mins). Is there any way to reduce this delay to a minimum? I tried to somehow reduce it below a minute by taking 0.5 for example, but this doesnt seem to work.

There is the corresponding code bit:

unsigned long timeNow;
static byte idxLED = 0;

if ( grLEDControl[idxLED].valPWM == grLEDControl[idxLED].minPWM
    && ItsDawn(DAWN_START, DAWN_DURATION, nowMinutes) )
{
    grLEDControl[idxLED].state = SUNRISEPREP;
    Serial.println ("SUNRISEPREP"); //for debugging
}
timeNow = millis();

switch( grLEDControl[idxLED].state )
    {

    case SUNRISEPREP:
        //grLEDControl[idxLED].timerLED = timeNow;

        grLEDControl[idxLED].timeDelay = grLEDControl[idxLED].updatePeriod + grLEDControl[idxLED].initialPauseSR;

        grLEDControl[idxLED].state = SUNRISE;
        Serial.println ("SUNRISE"); //for debugging
    break;
    
    case SUNRISE:
        timeNow = millis();
        if ( (timeNow - grLEDControl[idxLED].timerLED) >= grLEDControl[idxLED].timeDelay )
        {
            grLEDControl[idxLED].timerLED = timeNow;
            grLEDControl[idxLED].timeDelay = grLEDControl[idxLED].updatePeriod;
            if ( grLEDControl[idxLED].valPWM < grLEDControl[idxLED].sunupLowPWM )
            {
                grLEDControl[idxLED].valPWM++;
            }//if
            else
            {
                grLEDControl[idxLED].state = SUNUPLOW_AM;
                Serial.println ("SUNUPLOW_AM"); //for debugging
            }
        analogWrite( grLEDControl[idxLED].pin, grLEDControl[idxLED].valPWM );
        }//if
    break;

Thank you

Less than 1 minute won't work because the ItsDawn() function will still return true: it's still that first minute.

Possible solution: set a bool value that the action has taken place, and check for that before setting the action. Then at a later time clear that flag to allow for the next day.

Thank you wvmarle

I will try that.

I changed the approach a bit by starting the action at a defined minute:

#define SR_START 480 //8:00 start // defining the starting minute

if ( grLEDControl[idxLED].valPWM == grLEDControl[idxLED].minPWM
    && StartSunrise(SR_START, nowMinutes) )  // calling the function StartSunrise

byte StartSunrise(const unsigned int &start, const unsigned int &now) the StartSunrise function
{
  if (start == now) return 1;
  return 0;
}

The states change all the way to SUNRISE. After that there is a constant pause of 1 minute. I cannot understand or find where this pause comes from. Is it may be based on the same problem as before, with the statement being true for the whole minute?:

if ( grLEDControl[idxLED].valPWM == grLEDControl[idxLED].minPWM
    //&& ItsDawn(DAWN_START, DAWN_DURATION, nowMinutes) )
    && StartSunrise(SR_START, nowMinutes) )
    //&& (now.hour() == 22 && now.minute() == 55 && now.second() == 0) )
{
    grLEDControl[idxLED].state = SUNRISEPREP;
    Serial.println ("SUNRISEPREP"); //for debugging
}
timeNow = millis();

switch( grLEDControl[idxLED].state )
    {

    case SUNRISEPREP:
        grLEDControl[idxLED].timerLED = timeNow;

        grLEDControl[idxLED].timeDelay = grLEDControl[idxLED].initialPauseSR + grLEDControl[idxLED].updatePeriod;

        grLEDControl[idxLED].state = SUNRISE;
        Serial.println ("SUNRISE"); //for debugging
    break;
    
    case SUNRISE:
        if ( (timeNow - grLEDControl[idxLED].timerLED) >= grLEDControl[idxLED].timeDelay )
        {
            grLEDControl[idxLED].timerLED = timeNow;
            grLEDControl[idxLED].timeDelay = grLEDControl[idxLED].updatePeriod;
            if ( grLEDControl[idxLED].valPWM < grLEDControl[idxLED].sunupLowPWM )
                grLEDControl[idxLED].valPWM++;
            else
            {
                grLEDControl[idxLED].state = SUNUPLOW_AM;
                Serial.println ("SUNUPLOW_AM"); //for debugging
            }
        analogWrite( grLEDControl[idxLED].pin, grLEDControl[idxLED].valPWM );
        }//if
    break;

This is the led struct:

typedef struct
{
    const byte          pin;
    const byte          sunupLowPWM;         //sun level low (morning / afternoon)
    const byte          sunupHighPWM;         //sun level high (noon)
    const byte          minPWM;         //lamp off
    const unsigned long updatePeriod;   //time/step increase (fade in/10 min)
    const unsigned long initialPauseSR; //pause on sunrise (hot pin)
    const unsigned long initialPauseSS; //pause on sunset (ambient pin)
    unsigned long       timeDelay;      //== updatePeriod + initialPause
    unsigned long       timerLED;       //== millis()
    byte                valPWM;         //
    byte                state;          //
}structLEDControl;

structLEDControl grLEDControl[] = 
{
    {
        //ambient
        .pin =          pinAmbient, //blue led for testing
        .sunupLowPWM =       128,        //50% PWM Max (255)
        .sunupHighPWM =       204,        //80% PWM Max (255)
        .minPWM =       0,          //0% (off)
        .updatePeriod =  4687, //4687mS/step (128 steps 50%) / 2941mS/step (204 steps 80%)
        .initialPauseSR = 0,        //0 sec pause for ambient on sunrise
        .initialPauseSS = 120000,     //2 min pause for sunset
        .timeDelay =    0,
        .timerLED =     0,
        .valPWM =       0,
        .state =        7
        
        
    },//ambient
    {
        //hot
        .pin =          pinHot, // red led for testing
        .sunupLowPWM =       204,        //80% PWM Max (255)
        .sunupHighPWM =      255,        //100% PWM Max (255)
        .minPWM =       0,          //0% (off)
        .updatePeriod = 2941,        //204 steps in 10 minutes == 294 mS/step
        .initialPauseSR = 120000,     //2 min pause for sunrise
        .initialPauseSS = 0,        //0-sec pause for sunset
        .timeDelay =    0,
        .timerLED =     0,
        .valPWM =       0,
        .state =        7
        
    },//hot
    {
        //neon
        //yellow is handled in the statemachine differently than the other two
        .pin =          pinNeon, // yellow led for testing
        .sunupLowPWM =       255,        //100%
        .sunupHighPWM =       255,        //100%
        .minPWM =       0,          //0% (off)
        .updatePeriod = 0,          //no update period for neon
        .initialPauseSR = 0,        //0-sec
        .initialPauseSS = 0,        //0-sec
        .timeDelay =    0,          //no delay
        .timerLED =     0,
        .valPWM =       0,
        .state =        7
    }//neon
};

In SUNRISEPREP you seem to set some kind of time delay, then indeed immediately go to SUNRISE where you sit out that delay before doing anything else.

Please post a complete sketch, with snippets more often than not the problem is in the part you didn't post. If too long, create a minimal sketch that demonstrates the problem. Fair chance when doing so you'll discover the problem yourself, so that's anyway a good strategy.