Have System.sleep SLEEP_MODE_DEEP wakeup at the same time everday

I have the code segment below that reads sensors between the hours of 13 and 24. Everything works fine looping around every hour. The problem aries at the end of the day because as the day progressed with the built in delays each hour the code is running a few minutes later in time so if I set the sleep at the XXXX point in the code for 13 hours the unit will not wake up exactly at 13 but the number of minutes that got delayed from the previous day. Eventually over a few days the unit is waking up later and later. If you look at a snap shot of my log for one period of time the unit came online at 10:35:19 and went offline at 10:36:27.


The next iteration the unit will come on two minutes later and so on and so on.

What I am looking for is something similar to in the electric imp API server.sleepuntil(13, 0) where I am guaranteed that the
unit will wakeup at hour 13.

if ((Time.hour() >= 13) && (Time.hour()  <= 24) )
{
.
.
. 
 
 delay(10000); //  to get ready for sensors 

  .
  .
  .
  
	
 
  delay(10000);  // Let sensors read  values
  Serial.println("Read Sensors...");
  
  .
  .
  .
  
    delay(10000);
       .
       .
       .
                      // Contact IFTTT
    delay(10000);

	.
	.
	.

System.sleep(SLEEP_MODE_DEEP,3600);  // Sleep for one hour
  //*********************************
	.
	.
	.
	// Start process all over

else 
{

 **System.sleep(SLEEP_MODE_DEEP,XXX);  // I would Like XXX to be a sleep until and wakeup at the start time of 13**

}//time loop

A quick solution might be to do 24*3600-(combined delay time) then it should wake at the same time. Alternatively, you could calculate the time difference between the current time and the time of the next wakeup, and use that to set the sleep time.

1 Like

Thanks. I will give both options a try.

I’d definetly go with @Moors7’s aproach as this is the easiest and already for everybody doable way, with enough precision.

But just for completeness, if you had a battery backup for the RTC there would even be a wake-on-RTC possible for the STM32 µC, but it’s not (yet) baked into a nice and easy API.

1 Like

The wake on RTC is how the sleep for X seconds is implemented. For now, calculating the difference between your desired wake up time and the current time is the simplest approach.

In future, we might have an API like:

System.sleepUntil(time);

But this would just be syntactic sugar for calling System.sleep() with a duration.

Thanks for all of the input.

How can I determine the integer value for a particular hour. For example in the documentation:
// Print the hour for the given time, in this case: 4
Serial.println(Time.hour(1400647897));

What would the integer value be for hour 13 or 14? What is the formula to determine the integer value of a particular hour?

The “UNIX epoch time” used here is a count of seconds since midnight 1/1/1970 (with no account for leap seconds).

There are some C functions (like localtime and its relatives) you could use, or you do the relative maths with the functions you get with the Time library (e.g. (Time.now()) - (Time.now() % 86400) + (86400) + (14*3600) = “current second” - “seconds since midnight” + “seconds of one day” + “seconds of fourteen hours”)

If you want to wake at a given time (hh:mm:ss) on the following day, you would do something like this

  //               (      till midnight       ) and (   another x seconds    ) 
  int secs2sleep = (86400 - Time.now() % 86400)  +  (hh * 3600 + mm * 60 + ss); 
  System.sleep(SLEEP_MODE_DEEP, secs2sleep);
3 Likes

Thanks.

1 Like

I would use UTC or Epoch which is seconds based and while the numbers can be large the math is simple. Here is some untested code similar to something I did in one of my apps. I am sure there are more elegant ways but hopefully this helps.

uint32_t wakeUpUTC;
uint32_t wakeUpSecs;

//When you first enter your testing period at 1300
void setWakeup(){
wakeUpUTC = Time.now() + 24*3600;
}

//When you exit the last check at 2300
void calcWakeupSecs(){
wakeUpSecs = wakeUpUTC - Time.now();
}

else 
{

 System.sleep(SLEEP_MODE_DEEP,wakeUpSecs);  // I would Like XXX to be a sleep until and wakeup at the start time of 13**

}//time loop

1 Like

You won’t need long long (int64_t) - at least not till 18/1/2038 :wink:

Thanks…corrected.

1 Like

Hi @ScruffR ,

I have tried the following code segment the last two days but your suggestion:

//               (      till midnight       ) and (   another x seconds    ) 
  int secs2sleep = (86400 - Time.now() % 86400)  +  (hh * 3600 + mm * 60 + ss); 
  System.sleep(SLEEP_MODE_DEEP, secs2sleep);

is not working.

Here is what I have:

int secs2sleep = 0;

//--------------------------------
void setup() {
.
.
.
}

void loop(){

if ((Time.hour() >= 13) && (Time.hour()  <= 23) && (Time.weekday() != 1 ) 

// read sensors
.
.
.

System.sleep(SLEEP_MODE_DEEP,3600); //3600 1 hour
else if  (Time.weekday() != 1) 
{

 //calcWakeupSecs();

 secs2sleep = (86400 - Time.now() % 86400)  +  (12 * 3600 + 00 * 60 + 00); 
 
 Particle.publish("After Hours - Store Closed", String(Time.second(secs2sleep)));
 
 System.sleep(SLEEP_MODE_DEEP, secs2sleep);

}//time loop
.
.
.
}

I am getting an erroneous result and consequently the Photon is not waking up at 13 (8:00 am local time).
Last night secs2sleep returned the following:

/

The previous night I did not use “String(Time.second” and the result was a large number of seconds which when divided by 60 multiple times came out to the number 35.

What I am I doing wrong and what needs to be done to return the the correct number of sleeps seconds to wakeup at 13?

I’m not sure what time zone you are in, but while Time.hh() returns the hour of your local zone set in setup() Time.now() returns the epoch time relative to UTC/GMT (zone 0).
And the whole setup only works after your RTC has at least synced once before.

I am in the Eastern time zone - US. I assume since the original setup of the Photon that RTC has at least synced once. How can I force a sync?
This morning when I removed power to the Photon, after it never woke up, the code started executing where it recognized that the time was >= 13. What do suggest I do?

@ScruffR I think I got it to work in trial runs. I will know for sure tomorrow morning if it wakes up at the proper time in an actual run.
Thanks for all of your help and insight.

Good to hear you’re getting somewhere :+1:

Just one not on the code you’ve given above

  Particle.publish("After Hours - Store Closed", String(Time.second(secs2sleep));

If you wanted to know how many seconds your device will sleep, you’d rather write

  Particle.publish("After Hours - Store Closed", String(secs2sleep));

The way you did it you’d just get the seconds part of the time that is secs2sleepseconds past midnight 1/1/1970 which would be the same as secs2sleep % 60.

If you wanted to publish the time string your device will wake you’d something like this

float timeZone = +5.0;  // your local time offset (without DST)
void setup()
{
  ...
  Time.zone(timeZone); 
  if (Time.year() <= 1970) Particle.syncTime();
}

void loop()
{
  uint32_t _now = Time.now();
  //                     ( till midnight UTC  ) and (  time to wake but UTC  ) and (  make local   )
  int      _secs2sleep = (86400 - _now % 86400)  +  (12 * 3600 + 00 * 60 + 00)  +  (timeZone * 3600); 
  
  Particle.publish("After Hours - Store Closed", Time.format(_now + _secs2sleep));
 
  System.sleep(SLEEP_MODE_DEEP, _secs2sleep);
}

Thanks for the update.

One thing to be aware of.
Since the “midnight finding” is done on the UTC side, you might find that the above will not wake on the first day if you do this after midnight UTC (so after 7pm your time).
To deal with that you’d have to add the time zone correction to _now but might have to correct other bits of above code accordingly.

Yes I started getting strange results after 7:00pm local time. Earlier in the day when I said I was close I was using your earlier code:

secs2sleep = (86400 - Time.now() % 86400) + (12 * 3600 + 00 * 60 + 00);
Particle.publish(“After Hours - Store Closed”, String(Time.second(secs2sleep)));
System.sleep(SLEEP_MODE_DEEP, secs2sleep);

and I had everything theoretically working ok where the seconds returned corresponded to a store opening at the right time the next day.

When I added the new code you suggested later in the day where the time zone was set:

float timeZone = +5.0; // your local time offset (without DST)
void setup()
{
…
Time.zone(timeZone);
if (Time.year() <= 1970) Particle.syncTime();
}
.
.
.

I got really strange results in seconds returned. Results of a store opening a few days in the future were returned!

I am going play with it more today and see if I can something going.

Again thanks for all of your input.

@autolib, timeZone should be an INT, not a float :wink: