This is called once per loop it sets flags so that every function called after it will have the flag be true then it will be turned off again the next loop
Im calling this every loop but it should only do something at the top of the day 00:00, or whenever the Time.day() actually changes
void runDaily()
{
if (isNewDay())
{
Particle.syncTime(); // Sync time
}
}
I also have this function that runs hourly, but maybe do different logic depending on if the loop is also a new day
void runHourly()
{
if (isNewHour()) // Check if it's time to run the hourly report
{
if(isNewDay()){
publish(true,"New Day","%d",dayTracker);
}
Log.info("Hourly report: %d:00", Time.hour());
on_The_Hour(isNewDay()); // forces unthrottled publish if new day
powerReport();
}
}
I am not sure why but its not only running my new hour check twice but also it is new day for both of those loops as well (It also jumped to 25 then back to 24?), im not sure if this has to do with me running Particle.syncTime() at the top of the day, so I tried to account for it with the Particle.syncTimeDone() which didnt seem to fix my issue.
I can't tell exactly what's wrong from briefly looking at the code, but there are several things that need to be taken into account:
Checking for equality of seconds is dangerous because if the loop is blocked or the time jumps because of synchronization, you're likely to miss the exact second.
Timezones can be tricky.
If you are in a location with daylight saving, that's even worse.
It may be overkill for your use case, but the LocalTimeRK library is designed to make scheduling things based on clock time easy.
@rickkas7 so i looked into your library you recommended, and im not sure if what i did was the wrong way to implement it but i seem to still be getting a similar issue
currently isNew____ is just returning the bool that is set ex: newMonth
but i still seem to be getting double publishes? but now its not just at midnight
Im not sure if that has anything to do with how im implementing those if statements to check for the scheduled time or not, also since the midnight double publish is consistent could that be related to this?
I also cannot tell what is wrong with your code, but I think the order of the "if" statements feels strange to me. An "if-else" tree like this forms a priority encoder and things at the top of the tree happen first, while things below are skipped. The priority order of those statements matters.
There are a few concepts you need to keep in mind here:
You probably do not want to compare to the exact second because sometimes things in the user thread are delayed. You want to check if the current time is "greater than" the alarm time.
You should plan that your code can be called many times with the hh:mm:ss equal to the alarm time. You need a flag that tells you "this alarm has yet to go off" in one state, and "this alarm has already been handled and no action is required" in the other. Resetting this flag should be done only after you are sure you will not re-trigger the alarm.
There are complicated factors as others have said above, like changing time zones, daylight savings time (Summer time), etc. These complexities mostly affect when you reset the flag in point 2 above to look for the next alarm and prevent re-triggering, but they can effect the first triggering as well, particularly when the change moves the time forward. That's why point 1 above of checking for "greater than" the alarm time can help.
Could you copy and paste more of the code? It's not possible to tell if there's a logic error in the screenshot of the code above, and presumably there is one somewhere because it failed both ways.
This code does catch-up, which is to say if you run it the first time between intervals it will run the code in the off-interval. To only run on actual intervals, only do your activity when the variable such as nextDaily is non-zero.
The daily check is for 00:00:00 not 23:59:59 because you should never check for an exact second, because that second could be missed.
If you need to know the date of the actual end of the time period (yesterday), use something like this:
conv.withTime(nextDaily - 1).convert();
Log.info("for day ending: %s", conv.timeStr().c_str());
The logs look like this. Time are in the local time zone with daylight saving, but calculations are done at UTC.
0000489083 [app] INFO: minutely current=Thu Aug 1 05:58:00 2024 next=Thu Aug 1 05:59:00 2024
0000549143 [app] INFO: minutely current=Thu Aug 1 05:59:00 2024 next=Thu Aug 1 06:00:00 2024
0000609203 [app] INFO: minutely current=Thu Aug 1 06:00:00 2024 next=Thu Aug 1 06:01:00 2024
0000609228 [app] INFO: hourly current=Thu Aug 1 06:00:00 2024 next=Thu Aug 1 07:00:00 2024
@rickkas7 is there a reason you only check every second, rather than just every loop through. also i beleive your solution probably still is affected by the same problem I was having before, when I did particle sync time for some reason the date from Time would jump forward or back a day, since i was using the greater than like you are here
if (!nextMinutely || nextMinutely <= now) {
it would trigger it twice in a row, once when it was actually the new day and once when the timing was being synced (I think). I see that youre using the Time.valid in the check before testing times, however i tried to implment that and it didnt seem to make a difference to what happened
You can check every loop, but since the real time clock is second-based, the only thing it would do is lower the latency slightly.
There is always the possibility of events occurring twice when the time synchronizes. This is probably only a real concern for minutely. The solution is to remember the time_t value for the last minutely check and if it occurs again, skip it. There are weird edge cases that it still misses, but it fixes the most common one easily.
How does this example account for minutes / hour / etc swapping back to 0
Like if it goes 58 -> 59 that looks like it works fine but then would 59 -> 0 immediately trigger after a second since 0<= 59
I see now it comparing time not the direct minute sorry for the confusion
On second thought, the easiest solution is to do sync time when it's unlikely to cause harm, such as at 03:30:30. Use a separate sync time variable and conv.nextDay(LocalTimeHMS("03:30:30"))
I would use nextDayOfNextMonth(1). This will get you midnight on the first of the next month, but you can then subtract to get the correct month you are generating the data for.