I am trying to run a do loop which will operate a motor for a given length of time. I am using RTClib.h so I can compare actual times. I read in the current time add a length of time in seconds to it to get an end time and start the loop. The problem is the end time is never being reached and the do while loop is ending. I don't want to use a delay because this value is not consistent.
#include <Wire.h>
#include <RTClib.h>
RTC_DS1307 rtc;
//Inputs & Outputs
int Dirty=5; //Pressure Transducer N/O
int Filter=9; //Filter Motor Advance Output
//Time Vars
DateTime Now; //Current time read in
DateTime FilterAdv; //Time to stop advancing filter (calculated)
//Filter advance time in seconds
int FilterAdd = 30;
//showDate necessary only for output to Serial for checking code
// use: ex. [showDate("dt", dt);] to see calculated time in unix
void showDate(const char* txt, const DateTime& dt) {
Serial.print(txt);
Serial.print(' ');
Serial.print(dt.year(), DEC);
Serial.print('/');
Serial.print(dt.month(), DEC);
Serial.print('/');
Serial.print(dt.day(), DEC);
Serial.print(' ');
Serial.print(dt.hour(), DEC);
Serial.print(':');
Serial.print(dt.minute(), DEC);
Serial.print(':');
Serial.print(dt.second(), DEC);
Serial.print(" = ");
Serial.print(dt.unixtime());
Serial.print("s / ");
Serial.print(dt.unixtime() / 86400L);
Serial.print("d since 1970");
Serial.println();
}
void setup()
{
pinMode(Dirty, INPUT_PULLUP);
pinMode(Filter, OUTPUT);
digitalWrite(Filter, LOW);
Serial.begin(9600);
//Initialize DS3231 clock
rtc.begin();
}
void loop()
{
if (digitalRead(Dirty) == LOW)
{
Serial.println("Advance Paper");
AdvancePaper();
}
}
void AdvancePaper()
{
Now = rtc.now();
FilterAdv = (Now.unixtime() + FilterAdd);
showDate("Now", Now.unixtime());
showDate("FilterAdv", FilterAdv.unixtime());
do
{
//Serial.println("Looping");
digitalWrite(Filter, HIGH);
Now = rtc.now();
} while (Now.unixtime() < FilterAdv.unixtime());
Serial.println("Leave While Loop");
digitalWrite(Filter, LOW);
showDate("Now", Now.unixtime());
}
if you don't want blocking code, then you need to take a different approach. you might benefit from studying state machines. Here is a small introduction to the topic: Yet another Finite State Machine introduction
If you care about the time being exacly 30 seconds, you'll do better with delay() - the code that works for us (THX for the link @DaveX) but not for you (?) will enter the seconds somewhere in the middle, so you'll get a varying period of 29 .. 30 seconds. Or 30 .. 31, I'm too lazy but it will average N + 0.5 seconds.
Using delay will give you very close to 30 seconds every time. And be off by the same insignificant anount.
When I run the code this is what happens between the Arduino and Serial Monitor:
08:09:12.268 -> Advance Paper
08:09:12.268 -> Now 2025/3/12 8:8:53 = 1741766933s / 20159d since 1970
08:09:12.388 -> FilterAdv 2025/3/12 8:9:23 = 1741766963s / 20159d since 1970
08:09:12.388 -> Leave While Loop
08:09:12.388 -> Now 2025/3/12 8:8:53 = 1741766933s / 20159d since 1970
J-M-L : Thank you for pointing out the digitalWrite(Filter, HIGH); inside the do loop I have moved it out just before the do so now I have this:
digitalWrite(Filter, HIGH);
do
{
//Serial.println("Looping");
Now = rtc.now();
} while (Now.unixtime() < FilterAdv.unixtime());
The reason I need to get this figured out with RTC is I am running another motor in another portion of the code that needs to run for 2 hours after the last time motion has been detected, so the shut off time has to be dynamic.
I am currently just using the Serial Monitor for trouble shooting the code ...
Even if I modify the do loop to introduce a counter and delay it still stops short of the goal:
digitalWrite(Filter, HIGH);
int count = 0;
do
{
//Serial.println("Looping");
count++;
delay(1000ul);
Now = rtc.now();
} while (Now.unixtime() < FilterAdv.unixtime() && (count <= 30));
Serial.println("Leave While Loop");
Serial.println(count);
Now = rtc.now();
showDate("Now", Now.unixtime());
digitalWrite(Filter, LOW);
08:56:31.662 -> Advance Paper
08:56:31.662 -> Now 2025/3/12 8:56:12 = 1741769772s / 20159d since 1970
08:56:31.744 -> FilterAdv 2025/3/12 8:56:42 = 1741769802s / 20159d since 1970
08:56:52.755 -> Leave While Loop
08:56:52.825 -> 21
08:56:52.825 -> Now 2027/3/12 8:56:33 = 1804841793s / 20889d since 1970
DaveX, example scripts from RTClib for DS1307 appear to be working with consistent results. I did notice an improvement with my loop after inserting delay(1000ul); in the middle to slow it down. I ordered some DS3231, which should arrive the first of next week, to see if it may be the DS1307 that was causing my issues. I find it interesting that the code works in the simulator that you shared, but not on my hardware. Which makes me wonder if it isn't in the components.
I appreciate all of the quick responses and suggestions and will post what I find after receiving the replacement parts.
That still doesn't look like it really needs the RTC do loop. It seems like it could be done with a FSM. So far, it sounds like the system is either Waiting for the DirtyFilter signal, running MotorA for 30 seconds, or running MotorB for 2 hours. If the system needs to be paying attention to other things while a delay(30000) or delay(7200000) is happening, (such as counting down or displaying the time, or acting on other signals, integrating Pasteurization Units, or monitoring millis() accuracy with the RTC), then millis()-based timing should be able to enable doing several things at the same time.
What it does mean it's that you can't use the do-loop you are trying to work with. If the 2 hours are up while this loop is running, the Arduino won't realise and shut off the motor.
The built-in millis() function should be accurate to less than a second over these periods. An RTC is only actuate to 1 second. Forget the RTC and use millis() instead.
After receiving and testing a different RTC module, I think that was the problem on my end.
This was why I was not getting proper results back from the code.
After looking into DaveX advice and going down the FSM and millis() path of code this is what I came up with:
#include <Wire.h>
#include <RTClib.h>
RTC_DS3231 rtc;
//Inputs & Outputs
int Dirty=5; //Pressure Transducer N/O
int Filter=9; //Filter Motor Advance Output
//Time Vars
DateTime Now; //Current time read in
DateTime FilterAdv; //Time to stop advancing filter (calculated)
//Filter advance time in seconds
int FilterAdd = 30;
//Vars
bool FilterOn = false;
bool FilterDirty = false;
//showDate necessary only for output to Serial for checking code
// use: ex. [showDate("dt", dt);] to see calculated time in unix
void showDate(const char* txt, const DateTime& dt) {
Serial.print(txt);
Serial.print(' ');
Serial.print(dt.year(), DEC);
Serial.print('/');
Serial.print(dt.month(), DEC);
Serial.print('/');
Serial.print(dt.day(), DEC);
Serial.print(' ');
Serial.print(dt.hour(), DEC);
Serial.print(':');
Serial.print(dt.minute(), DEC);
Serial.print(':');
Serial.print(dt.second(), DEC);
Serial.print(" = ");
Serial.print(dt.unixtime());
Serial.print("s / ");
Serial.print(dt.unixtime() / 86400L);
Serial.print("d since 1970");
Serial.println();
}
void setup()
{
pinMode(Dirty, INPUT_PULLUP);
pinMode(Filter, OUTPUT);
digitalWrite(Filter, LOW);
Serial.begin(9600);
//Initialize DS3231 clock
rtc.begin();
}
void loop()
{
Now = rtc.now();
if ((digitalRead(Dirty) == LOW) && (FilterDirty == false))
{
FilterDirty = true;
Serial.println("Advance Paper");
}
if (FilterDirty == true)
{
AdvancePaper();
}
}
void AdvancePaper()
{
if (FilterOn == false)
{
FilterAdv = (Now.unixtime() + FilterAdd);
showDate("Start", Now.unixtime());
showDate("FilterAdv", FilterAdv.unixtime());
digitalWrite(Filter, HIGH);
FilterOn = true;
}
if (Now.unixtime() >= FilterAdv.unixtime())
{
showDate("Stop", Now.unixtime());
digitalWrite(Filter, LOW);
FilterOn = false;
FilterDirty = false;
}
}
I agree by removing the do...while loop I will be able to monitor for any other problems and be able to act upon it without being stuck in the loop. I may use millis() in my final code but I haven't fully decided on that yet. I do see the benefit and it would be one less component to tuck into a control box.
If instead of comparing uint32_t values directly, you subtract the start time from both sides, you could then compare intervals and you won't have weird issues when it rolls over in Y2038:
#include <Wire.h>
#include <RTClib.h>
RTC_DS3231 rtc;
//Inputs & Outputs
int Dirty=5; //Pressure Transducer N/O
int Filter=9; //Filter Motor Advance Output
//Time Vars
DateTime Now; //Current time read in
DateTime FilterAdvStart; //Time to Start advancing filter (calculated)
//Filter advance time in seconds
int FilterAdd = 30;
//Vars
bool FilterOn = false;
bool FilterDirty = false;
//showDate necessary only for output to Serial for checking code
// use: ex. [showDate("dt", dt);] to see calculated time in unix
void showDate(const char* txt, const DateTime& dt) {
Serial.print(txt);
Serial.print(' ');
Serial.print(dt.year(), DEC);
Serial.print('/');
Serial.print(dt.month(), DEC);
Serial.print('/');
Serial.print(dt.day(), DEC);
Serial.print(' ');
Serial.print(dt.hour(), DEC);
Serial.print(':');
Serial.print(dt.minute(), DEC);
Serial.print(':');
Serial.print(dt.second(), DEC);
Serial.print(" = ");
Serial.print(dt.unixtime());
Serial.print("s / ");
Serial.print(dt.unixtime() / 86400L);
Serial.print("d since 1970");
Serial.println();
}
void setup()
{
pinMode(Dirty, INPUT_PULLUP);
pinMode(Filter, OUTPUT);
digitalWrite(Filter, LOW);
Serial.begin(9600);
//Initialize DS3231 clock
rtc.begin();
}
void loop()
{
Now = rtc.now();
if ((digitalRead(Dirty) == LOW) && (FilterDirty == false))
{
FilterDirty = true;
Serial.println("Advance Paper");
}
if (FilterDirty == true)
{
AdvancePaper();
}
}
void AdvancePaper()
{
if (FilterOn == false)
{
FilterAdvStart = Now.unixtime() ;
showDate("Start", Now.unixtime());
showDate("FilterAdvStart", FilterAdvStart.unixtime());
showDate("FilterAdvStop", FilterAdvStart.unixtime()+FilterAdd);
digitalWrite(Filter, HIGH);
FilterOn = true;
}
if (Now.unixtime() - FilterAdvStart.unixtime() >= FilterAdd)
{
showDate("Stop", Now.unixtime());
digitalWrite(Filter, LOW);
FilterOn = false;
FilterDirty = false;
}
}