How to trigger actions to even hours?

I have been struggling with this problem for a couple of days without finding a good solution, can someone advice?

In my sketch for an Adafruit Huzzah breakout device using ESP-12 I have a number of things that will happen on a schedule with varying time intervals. It has been done in the recommended way, i.e. to have this construct in the loop() function:

unsigned long lastdbreport = 0;

void loop()
{
  unsigned long now = millis();

  if ((now - lastdbreport) > DB_UPDATE_INTERVAL) // This is 36000000, i.e. 1 hour
  {
      lastdbreport = now;
      ReportMeterData();
  }
//Other similar tasks are also defined here.
}

This has the effect of executing the report with a constant interval (within the millis() accuracy) of 1 hour .
I also have NTP time function running in my sketch via the time.h include.
So I can get a current local timestamp using this function in an utils.cpp file:

time_t now;
struct tm * timeinfo;

void getLocalTime(char buf[], int buflen)
{
   time(&now);        // read the current time
   timeinfo = localtime(&now); //Convert to local
   strftime(buf, buflen, "%Y-%m-%d %H:%M:%S", timeinfo);
}

What I want is to make sure that the execution of my ReportMeterData() function happens at the very beginning of the hour, i.e. it should be starting at the time the new hour starts.
But I cannot wrap my head around how this can be done in loop()...

In fact the only thing needed is to be able to once sync the execution to the hour switch because from then on my loop using millis() will work just fine.
But the millis() is hooked to the start/reset of the device, which is random with respect to calendar time.

Any suggestions or web links welcome!

set "lastbreport" to the current millis() minus the # of msec since the last hour

Upon start up get the time of the hour in minutes. Convert that minutes to milli seconds. Use that millis as your first time out.

Say its 11:45, 45 minutes is 2700000 millis. An hour is 3600000 thus, the first time out millis value is 3600000 - 2700000

unsigned long timeoutvalue = millis() - ( millis of the current hours minutes - total millis in an hour

I assume you mean here?

unsigned long lastdbreport = 0; <= calculate this before loop starts?
loop();
if ((now - lastdbreport) > DB_UPDATE_INTERVAL)

But at that time the time system has not yet synced to a NTP server so HOW can I know the calendar time?

You don't get NTP time in setup()?

1 Like

... then when you do get NTP, reset lastdbreport

code snippets suck

...........................................................................
Help us help you.

Please follow the advice given in the link below when posting code, in particular the section entitled 'Posting code and common code problems'

Use code tags (the </> icon above the compose window) to make it easier to read and copy for examination

Use the IDE autoformat tool (ctrl-t or Tools, Auto format) before posting code in code tags.

Include the entire error message. It is easy to do. There is a button (lower right of the IDE window) called "copy error message". Copy the error and paste into a post in code tags. Paraphrasing the error message leaves out important information.

Post a schematic.

Post an image of your project.

Which Micro Controller are you using?

Is this simulator code?

Please describe the problem better then you just did.

......................................................................

If you are using a 9V battery like this.


Don't.

.................................................................

Info about multi things

.......................................................................................................................

I2C scanner

https://www.arduino.cc/reference/en/libraries/i2cscanner/

What did the I2C scanner report?

.........................................................................................
Bread board power rail split


...............................................................................................................................................
Serial Input basics.

................................................................................................................

The search box is a useful tool.


For instance using the words "9v battery" in the search box one could find lots of reasons why this may be a bad idea.

Other search words include "level shifter",
.........................................................................................................

code merging

http://www.thebox.myzen.co.uk/Tutorial/Merging_Code.html

..........................................................................................................

disclaimer

The events portrayed in this post are not all true. The names are not real names of real people and real organizations.
..............................................................................

i don't believe this is necessary for this problem -- this appears to be a math problem

i believe he described is adequately

I just went to the doctor for 2 hours and meanwhile @idahowalker stepped in and posted a giant "contribution" NOT dealing with my problem!
WHY?

My code snippets were posted with code tags, so why the attack!
And what use are these images showing batteries and breadbords etc?

No, at millis 11516 there is no time sync. Time is back in 1970.
Here are a few log lines:

1970-01-01 01:00:04  millis: 11516
WiFi signal strength: -51
WiFi:  IPaddr=192.168.119.200; MACaddr=A4:E5:7C:2D:36:DC2022-11-16 11:40:01 millis: 16424

So NTP cannot get time until WiFi is connected, which happens about 15-16 seconds after millis start counting.
At that time loop() is running already.

The DB_UPDATE_INTERVAL value is a define in the setup.h file, but I guess that if I make it a long instead then the value could be changed to something that will trigger the first run on an even hour and then I can return 3600000 to it for future use...
But I cannot do that until the WiFi is connected and NTP has had time to sync.

good luck.

So now I have solved the issue as follows:

  1. I have created a function that calculates the millis when next hour starts
  2. I have put in a delay in setup() such that the WiFi is online when I reach the end of setup() it should be on line.
  3. I have created a new variable db_update_interval which is set to the define as a default.
  4. After the delay in setup() I set the interval to the millis for the next hour.
  5. In loop I am resetting the test value to the define the first time I reach the function so the next interval will be exactly 3600000 ms.

With this in place I started the device at 18:02 and it reported its data at 19:00:00 and the next time at 20:00:00! :grinning: :grin:

Code snippets:
The new function:

	//Get the offset from UTC hour start to millis()
	unsigned long MillisAtNextHour()
	{
		time(&now);        // read the current time
		timeinfo = localtime(&now); //Convert to local
		if (timeinfo->tm_year < 100) return(0); //if WiFi cannot connect

		unsigned long justnow = millis();
		unsigned long millisecsinutc = ((timeinfo->tm_min * 60 + timeinfo->tm_sec)) * 1000; //milliseconds into UTC hour
		unsigned long nexthourmillis = justnow + 3600000 - millisecsinutc;

		return (nexthourmillis);
	}

At the end of setup():

    db_update_interval = MillisAtNextHour();
    if (db_update_interval == 0) {db_update_interval = DB_UPDATE_INTERVAL; //If NTP time could not be read
    Serial.print("Setting db_update_interval = ");
    Serial.println(db_update_interval);

Inside loop()

    //Check if we should send meter data to the database
    #ifdef DB_UPDATE_INTERVAL
        if ((now - lastdbreport) > db_update_interval)
        {
            lastdbreport = now;
            printLocalTime();
            ReportMeterData();
            db_update_interval = DB_UPDATE_INTERVAL;
        }
    #endif

in vague generic terms, without writing actual code: divide the hour by two and see if there is a remainder

when minutes = 00;
get the time;
float eventhour = ( hour / 2 );
if eventhour hour == round (eventhour); // if there is a remainder it's an odd number; no remainder is an even number
{
do the thing
}
else
move on to what comes next

You misunderstood me here...
When I wrote even hours I meant time ending in 00 seconds, not that the hours should be even like in (0-2-4-6...).

Right now my sketch has been running for 18 hours and the timestamps in the database is 4 seconds past the hour on all 18 entries.
In my debug window I can see that the millis() when the call is sent to the database is within one count for all 18 of them.
So:

  • My code works to sync up the database updates.
  • There is probably a short delay when the database is updated via the web call or else the database server clock is off a bit in order to account for the 4s difference.
  • And the ESP-12 clock seems to be pretty accurate

Note: I have yet to add a follow-up sync down the line if the times would drift due to the inaccuracy of he millis() clock.
Right now it is OK for me. But if needed I could insert a sync at the 30s mark some days or weeks into the future.
But I really do not need that kind of accuracy on the readings.

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