Help using RTC to do tasks at different times of day

I have an Arduino Pro Mini ATmega328P 3.3V 8M with a DS3231 RTC. My project consists of a transistor triggering a button at 10 second intervals at certain times of the day. I have originally been using the delay function as I have no coding experience and just wanted to come up with something simple while I try to figure out how to use the RTC.

void setup() 
{
  pinMode(2, OUTPUT);
}

void loop() {
  for (int i=0; i<4320; i++) 
    {
    digitalWrite(2, HIGH);  // 7pm - 7am Loop Enabled
    delay(1000);
    digitalWrite(2, LOW);
    delay(9000);
    }
  delay(3600000);  // 7am - 8am Loop Disabled
  for (int i=4320; i<5040; i++)
  {
    digitalWrite(2, HIGH);  // 8am - 10am Loop Enabled
    delay(1000);
    digitalWrite(2, LOW);
    delay(9000);
  }
  delay(7200000);  // 10am - 12pm Loop Disabled
  for (int i=5040; i<6480; i++)
  {
    digitalWrite(2, HIGH);  // 12pm - 4pm Loop Enabled
    delay(1000);
    digitalWrite(2, LOW);
    delay(9000);
  }
  delay(10800000);  // 4pm - 7pm Loop Disabled
  for (int i=6480; i<8280; i++)
}

Like I mentioned earlier, I have no coding experience and I can't figure out which library to use for the RTC and what functions to use to set the pin output to high.

To be clear, the schedule I want is:
7pm-6:59am (Loop Enabled)
7am-7:59am (Loop Disabled)
8am-9:59am (Loop Enabled)
10am-11:59pm (Loop Disabled)
12pm-3:59pm (Loop Enabled)
4pm-6:59pm (Loop Disabled)

Please ask any questions for clarification. Any help is greatly appreciated!

Did you do some googling with keywords like "Arduino DS3231"?

Are you prepared to learn a completely different way about timing?

Do you know what non-blocking timing is?

https://www.google.com/search?as_q=arduino+DS3231+democode&as_epq=&as_oq=&as_eq=&as_nlo=&as_nhi=&lr=&cr=&as_qdr=all&as_sitesearch=&as_occt=any&as_filetype=&tbs=

calculating minuteOfDay makes if-conditions much easier

13:59
minuteOfDay = 13 * 60 + 59

Did you already read this tutorial about non-blocking timing?

best regards Stefan

1 Like

I use one of the alarms to set the time for the next operation. When the time matches the alarm an interrupt is raised. In the main loop check for the interrupt and set which schedule you are on. For a way over kill example look at my Garagerator topic.

For timer operation you can ignore most of the code, of interest are lines at approximately
100-138 for definitions
550-564 for timer set up
619 for testing the interrupt in loop()
651-665 for setting up the next interval
(lines 642-649 are probably redundant from before there was a setNextSampleTime() function)

look this over

output


   1:00 
   2:00 
   3:00 
   4:00 
   5:00 
   6:00 
   7:00  LoopDis
   8:00  LoopEn
   9:00 
  10:00  LoopDis
  11:00 
  12:00  LoopEn
  13:00 
  14:00 
  15:00 
  16:00  LoopDis
  17:00 
  18:00 
  19:00  LoopEn
  20:00 
  21:00 
  22:00 
  23:00 
  24:00 
   1:00 
   2:00 
   3:00 
   4:00 
   5:00 
   6:00 
   7:00  LoopDis
   8:00  LoopEn
   9:00 
  10:00  LoopDis
  11:00 
  12:00  LoopEn
  13:00 

// enum defines symbols with sequential values
enum { LoopEn, LoopDis };
enum { InAct,  Act     };

// user defined structure containing sub-fields
struct Alarm {
    int  hr;
    int  min;
    int  action;
    int  state;
};

// statically define values in table of Alarm
Alarm alarm [] = {
   // hr min  actionId
    {  7,  0, LoopDis },
    {  8,  0, LoopEn  },
    { 10,  0, LoopDis },
    { 12,  0, LoopEn  },
    { 16,  0, LoopDis },
    { 19,  0, LoopEn  },
};

// determine # of entries in table
const int Nalarm = sizeof(alarm) / sizeof(Alarm);

// define time variables
unsigned long MsecPeriod = 10;
unsigned long msecLast;

// define clock values
int  hr;
int  min;

// define char string for use with sprintf()
char s [80];

// -----------------------------------------------------------------------------
void loop ()
{
    unsigned long msec = millis ();
    
    // simulate RTC -- MsecPeriod can be small to quickly simulate 24 hrs
    if (msec - msecLast > MsecPeriod) {
        msecLast = msec;

        // increament clock mins each tic
        if (60 <= ++min)  {
            min = 0;
            if (24 < ++hr)
                hr = 1;
        }

        // display clock each hr
        if (0 == min) {
            sprintf (s, "\n  %2d:%02d ",  hr, min);
            Serial.print (s);
        }
    }

    // monitor alarms
    for (int n = 0; n < Nalarm; n++)  {
        // check if time matches alarm
        if (hr == alarm [n].hr && min == alarm [n].min)  {
            if (InAct == alarm [n].state)  {    // do action if alarm no active
                alarm [n].state = Act;

                switch (alarm [n].action)  {    // perform alarm action
                case LoopDis:
                    Serial.print (" LoopDis");
                    break;

                case LoopEn:
                    Serial.print (" LoopEn");
                    break;
                }
            }
        }
        else if (Act == alarm [n].state)        // reset alarm
            alarm [n].state = InAct;
    }
}

// -----------------------------------------------------------------------------
void setup ()
{
    Serial.begin (9600);
}
1 Like

You will probably find Stoffgren's TimeAlarms library very useful.

2 Likes

This RTC simulation shows how to make a schedule... add it to your project.

1 Like

Yes, I have actually figured out how to set the time & date with that exact video. As for non-blocking timing, I have tried to understand how to use millis function over delay but I still can't wrap my head around it and get some sample code working with my scenario.

However, I have managed to use the TimeAlarms library and edit the example sketch. Although, I after a few hours of trial and error I wasn't able to have the alarms sync with the RTC even after including the DS3231 library in the sketch. So instead, I continued to modify the example sketch and am still going off of the Arduino's time. Here is my modified sketch in a simulation. All that's left is trying to implement the RTC

Thank you! I did not know this existed and should be very helpful for future projects

This library was actually extremely useful although I wasn't able to have the alarms trigger from the RTC time. Instead, it still goes off of the Arduino's time so I left it out for now. I modified the example sketch and this is what I was able to come up with in a simulation.

Here is also the sketch:

#include <TimeLib.h>
#include <TimeAlarms.h>

AlarmId id;

void setup() {
  pinMode(2, OUTPUT);
  Serial.begin(9600);
  while (!Serial) ; // wait for Arduino Serial Monitor

  setTime(7,59,45,18,10,23); // set time to Wednesday 7:59:45am October 18 2023

  // create the alarms, to trigger at specific times
  Alarm.alarmRepeat(19,0,0,SevenPMToSevenAM);  // 7pm - 7am
  Alarm.alarmRepeat(8,0,0,EightAMToTenAM);  // 8am - 10am
  Alarm.alarmRepeat(12,0,0,TwelvePMToFourPM);  // 12pm - 4pm

  // create timers, to trigger relative to when they're created
  //Alarm.timerRepeat(15, Repeats);           // timer for every 15 seconds
  //id = Alarm.timerRepeat(2, Repeats2);      // timer for every 2 seconds
  //Alarm.timerOnce(10, OnceOnly);            // called once after 10 seconds
}

void loop() {
  digitalClockDisplay();
  Alarm.delay(1000); // wait one second between clock display
}

// functions to be called when an alarm triggers:
void SevenPMToSevenAM() {
  Serial.println("Alarm: - 7pm-7am Active");
  for (int i=0; i<4320; i++) 
    {
    digitalWrite(2, HIGH);  // 7pm - 7am Loop Enabled
    Alarm.delay(1000);
    digitalWrite(2, LOW);
    Alarm.delay(9000);
    }
}

void EightAMToTenAM() {
  Serial.println("Alarm: - 8am-10am Active");
  for (int i=4320; i<5040; i++)
  {
    digitalWrite(2, HIGH);  // 8am - 10am Loop Enabled
    Alarm.delay(1000);
    digitalWrite(2, LOW);
    Alarm.delay(9000);
  }
}

void TwelvePMToFourPM() {
  Serial.println("Alarm: - 12pm-4pm Active");
  for (int i=5040; i<6480; i++)
  {
    digitalWrite(2, HIGH);  // 12pm - 4pm Loop Enabled
    Alarm.delay(1000);
    digitalWrite(2, LOW);
    Alarm.delay(9000);
  }
}
void digitalClockDisplay()
{
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println(); 
}

void printDigits(int digits)
{
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}
  1. Establish a timeOut (for example, every 2000 ms / every 2 seconds )
  2. Store a "startTimerNow" time by reading the system clock (for example, startTimerNow = millis(); )
  3. Compare timeOut (2 seconds) to startTimerNow
  4. If startTimerNow is larger than timeOut, (a) set new startTimerNow to count toward a new event and (b) make timeOut event happen (call the user defined function)

With this millis() timer you can set multiple events/timeouts as well as keeping mcp is free to do other tasks (not blocked, not tied to a "loop" like delay())

There is a main and very fundamental difference between delay() and non-blocking timing.

The main difference is:

delay() = linear thinking

non-blocking timing is circular thinking

A straight line is very very different from a circle.
Can you let sink in this fact into your mind for a moment:

A straight line is very very different from a circle.

Next point: loop() is looping.
A looping is a circular thing.

You repeat the loop ten-thousands of times
and you do ten-thousands of times
a comparing
1st loop has the right time come? No !
2nd loop has the right time come? No !
3rd loop has the right time come? No !
4th loop has the right time come? No !
5th loop has the right time come? No !
....
10000th loop has the right time come? YES ! ==> time for timed action

as pseudo code

void loop()
  Check_if_the_right_time_has_come();
  when right time has come ==> do timed action

another analogy

delay() is like
a classic "analog" clock is stopped = pins stand still no more ticking pins

non-blocking timing is
check each and every single second if pins have reached the position of 5 o ' clock pm
Only in case the pins have REALLY reached the position of 5 o ' clock pm

==> do the timed action

I assume that you know what a for-loop is

for (int myCounter = 0; myCounter <= 100; myCounter++) {
  if (myCounter == 90) {
    doAction();
  }
}

This for-loop makes 100 "loopings" = 100 iterations.
Only in case the variable myCounter has value 90 doAction() is executed

For all other numbers the if-condition

  if (myCounter == 90) 

is false and doAction() is not executed

1
2
3
......
88
89
90 doAction
91
92
...
100

The same principle is used with non-blocking timing.

The difference is:
The myCounter-example uses a for-loop for explaining the looping

In non-blocking timing

void loop()

does the looping

void setup() {
  myCounter = 0;
}

void loop() {
  1. execute_a_lot_of_things();
  2. myCounter++
  3. if (myCounter == 90) {
    3bdoAction();
  }
  4. execute_a_lot_of_OTHER_things();
}

with delay() you would code

void loop() {
  1. execute_ONE_SINGLE_time_Blabla()
  2. freeze_microcontroller=stop_code_execution_completely  (which in fact is delay())
  3. doAction()
  4. execute_ONE_SINGLE_time_Blublublu

Can you see the difference?

With delay()
the lines
1.
2.
3.
4.
is run down
ONE SINGLE time

With non-blocking timing the lines
1.
2.
3.
3b
}
4.

are repeated 100 times.
1st-run
1.
2.
3.
3b
}
4.

2nd-run
1.
2.
3.
3b
}
4.
....
90-run ==> doAction
...
100-run

But only if myCounter is 90
doAction is executed

best regards Stefan

#include <RTClib.h> // Include the RTC library

RTC_DS3231 rtc; // Create an RTC object

void setup() {
  pinMode(2, OUTPUT);
  rtc.begin(); // Initialize the RTC

  // Fix 1: Set the initial time
  rtc.adjust(DateTime(2023, 1, 1, 19, 0, 0)); // Use your actual date and time
}

void loop() {
  DateTime now = rtc.now(); // Get the current time

  // Fix 2: Check the current time and enable/disable the loop
  if ((now.hour() >= 19 && now.hour() < 24) || (now.hour() >= 0 && now.hour() < 7) || (now.hour() >= 12 && now.hour() < 16)) {
    // Enable the loop (7pm-6:59am and 12pm-3:59pm)
    digitalWrite(2, HIGH);

    // Fix 3: Delay for 10 seconds
    delay(10000);

    digitalWrite(2, LOW);

    // Fix 4: Delay for 90 seconds
    delay(90000);
  } else {
    // Disable the loop
    digitalWrite(2, LOW);

    // Fix 5: Delay for 10 minutes (7am-12pm and 4pm-7pm)
    delay(600000);
  }
}

Explanation of fixes:

  1. Set the initial time: In the rtc.adjust() function, I've set the initial time to a specific date and time. You should replace this with your actual date and time. It's crucial to initialize the RTC with the correct time, as the code relies on it to determine when to enable or disable the loop.

  2. Check the current time: I've adjusted the time comparisons in the if statement to account for the schedule you provided. The logic now considers three time ranges: 7pm-11:59pm, 12am-6:59am, and 12pm-3:59pm, during which the loop is enabled.

  3. Delay for 10 seconds: The delay function is used to create a 10-second interval when the loop is enabled.

  4. Delay for 90 seconds: After the loop is enabled, there's a 90-second delay, which adds up to the 10-second interval and aligns with your schedule.

  5. Delay for 10 minutes: When the loop is disabled, a 10-minute delay is used to match the schedule during which the loop should not be active (7am-12pm and 4pm-7pm).

1 Like

Thank you so much, I was able to play around and understand all the functions used in the sketch! After a quick google search, I figured out the difference between Serial.print() Serial.println() and it was driving me crazy in the serial monitor

#include <RTClib.h>

RTC_DS3231 rtc;

void setup() {
  Serial.begin(9600);
  if(!rtc.begin()) {
    Serial.println("Couldn't find RTC!");
    Serial.flush();
    while (1) delay(10);
  }

  pinMode(2, OUTPUT);
  rtc.begin();
  //rtc.adjust(DateTime(2023, 10, 18, 11, 59, 45)); // Use your actual date and time
}

void loop() {
  DateTime now = rtc.now();
  char date[20] = "MM/DD/YYYY hh:mm:ss";
    rtc.now().toString(date);
    Serial.println(date);

  if ((now.hour() >= 19 && now.hour() < 24) || (now.hour() >= 0 && now.hour() < 7) || (now.hour() >= 8 && now.hour() < 10) || (now.hour() >= 12 && now.hour() < 16)) {
    Serial.println("Alarm: Active");
    digitalWrite(2, HIGH);
    delay(1000);
    digitalWrite(2, LOW);
    delay(9000);

  } else {
    digitalWrite(2, LOW);
    delay(1000);
  }
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.