3

I'm trying to get my Lilygo T5 4.7" epaper to deep sleep for 12 hours. But I only seem to be able to get about half an hour (2100s) of deepsleep on it. If I set the timer for longer, it just reboots right away.

#include <Arduino.h>
#include "epd_driver.h"

long DEEP_SLEEP_TIME_SEC = 43200;

void start_deep_sleep()
{
    epd_poweroff_all();
    esp_sleep_enable_timer_wakeup(1000000L * DEEP_SLEEP_TIME_SEC);
    Serial.println("Starting deep-sleep period...");
    esp_deep_sleep_start();
}

void setup() {  
  Serial.begin(115200); 
  Serial.println("\n- - - - - - - - -INITIALIZING - - - - - - - - - -");
  delay(10000);
  start_deep_sleep();    
}

void loop() {
}

The sbove code gives the following:

16:44:25.104 -> - - - - - - - - -INITIALIZING - - - - - - - - - -

16:44:35.105 -> Starting deep-sleep period...

16:44:35.105 -> ets Jul 29 2019 12:21:46
  • and it just continues to reboot..

Maybe I'm using the wrong variables in some way?

Regards Kasper

5
  • refer the ESP32 datasheet for the longest deep sleep available
    – jsotola
    Dec 17, 2022 at 17:40
  • I will try Ul tommorrow, but searching the datasheet for timer and sleep didn't help me learn how long it will go to sleep for..
    – Engberg
    Dec 17, 2022 at 22:00
  • I don't know what the max duration is, but the function you're calling seems to accept uint64_t, in which case you're probably looking for 1000000ULL rather than 1000000UL. Or uint64_t(1000000) or (uint64_t)1000000 or static_cast or ... whatever. In other words, the same thing Juraj appears to be saying, only more so.
    – timemage
    Dec 18, 2022 at 0:35
  • alright, so I changed this esp_sleep_enable_timer_wakeup(1000000ULL * DEEP_SLEEP_TIME_SEC); and tried for 1 hour (3600s) and that worked (+- 15 seconds). Could you post an answer @timemage, so that I can close the question? Still unsure as to how long it will work for.. I don't seem to have the problem @hcheung suggests..
    – Engberg
    Dec 18, 2022 at 9:28
  • I can, I kind of figured Juraj had dropped an L and was otherwise thinking this, but I see their comment is gone.
    – timemage
    Dec 18, 2022 at 14:29

1 Answer 1

3

So, formalizing this into an answer:

long DEEP_SLEEP_TIME_SEC = 43200;
// ...
esp_sleep_enable_timer_wakeup(1000000L * DEEP_SLEEP_TIME_SEC);

This is attempting to calculate the result of 1000000L * DEEP_SLEEP_TIME_SEC as a long type because both operands are of long type. The language does not consider the values of the operands (your constant and variable) of an operator (the multiplication) when determining the type of the result (the product) nor what you do with the result after that (passing it to the function).

The long type on ESP32 has a maximum value of 2147483647 which is as you said, "about half an hour" (not quite 36 minutes) worth of microseconds.

According to the documentation esp_sleep_enable_timer_wakeup accepts a uint64_t type for its requested duration parameter. I don't know what the valid maximum value to call with is, but the original poster confirmed it was large enough for 12 hours.

An unsigned long long is 64-bit on the ESP32 and the ULL suffix on 1000000ULL forces the compiler to only consider types which are unsigned and which are not smaller than a long long. In practice it means make me an unsigned long long with value 1000000. In

esp_sleep_enable_timer_wakeup(1000000ULL * DEEP_SLEEP_TIME_SEC);

the compiler follows something called the usual arithmetic conversions which will cause it to upconvert the long type of DEEP_SLEEP_TIME_SEC to match the unsigned long long that it is finding in 1000000ULL. The multiplication is therefore carried out in the unsigned long long type, so that the calculation itself accommodates more microseconds than you'll ever know what to do with. What the actual maximum is for esp_sleep_enable_timer_wakeup is still unclear. If I find out I will update with that information.


The ESP32-Arduino support comes with a fairly complete C++ standard library. So, if you want to get fancy you can use the <chrono> header. Just to give an example of what that could look like in your context:

#include <chrono>
// ...
static constexpr auto DEEP_SLEEP_TIME = std::chrono::hours{12};
// ...
esp_sleep_enable_timer_wakeup(
    std::chrono::duration_cast<std::chrono::microseconds>(
        DEEP_SLEEP_TIME
    ).count()    
);

I'm being very explicit in the above. But you can import parts of the chrono namespace so you don't have to name everything fully. You can also import chrono literals support so that you can use time constants that look more like natural language(not yet apparently; there's still no C++14 support in Arduino-ESP32 I guess). You can control the underlying integer type used to represent the unit. It is also possible to create and name your own duration types to represent things like bit durations in a serial protocol etc. The default underlying type for chrono::microseconds is large enough for what you're doing; the default widths of underlying types are generally pretty sensible. Different time units are different std::chrono types so you can't mix them up accidentally. duration_cast is a means of explicit conversion of time units. The .count() in the above is just a way to get the raw integer value out of the chrono type for when you need to do that, like calling this function.

If you wrap the function with your own taking std::chrono::milliseconds the unit conversion could just happen automatically in the call to your own wrapper:

static constexpr auto DEEP_SLEEP_TIME = std::chrono::hours{12};
//...
esp_err_t esp_sleep_enable_timer_wakeup_using_chrono(
  std::chrono::milliseconds duration
) {
  return esp_sleep_enable_timer_wakeup(
    duration.count()
  );
}
// ...
esp_sleep_enable_timer_wakeup_using_chrono(DEEP_SLEEP_TIME);

The way durations work in chrono is such that you don't need to duration_cast when you're going to type that can represent the same values as the source type. The the prior example the point of the duration_cast was just so that .count() was getting a count of microseconds. In other words, the duration_cast performed the multiplication and would have selected a more appropriate underlying integer size. Here that conversion takes place during the call. Anyway, it may be worth some effort to learn about as it tends to help avoid problems with unit conversions and integer ranges.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.