1

I'd like to put my ESP8266 into deep sleep and then wake it up at at specific date and time, [roughly] to the second.

Edit: Unfortunately, ESP8266 deep sleep doesn't support values greater than about 3.5 hours. I tried sleeping for an hour at a time until the desired time is reached, but found that it's not very accurate; often dozens seconds of drift on each wake, adding up to many minutes of drift after several hours.

Edit: I want to wake every 24 hours at a specific time. A few seconds per week drift is more than acceptable, as I'll be re-syncing the clock every 24 hours or so.

Edit: I'm using WAKE_RF_DISABLED to disable WiFi to save power on wake, so when it wakes up, there is usually no internet connection to check the time (I want to connect to WiFi only once every 24h).

8
  • Over what period of time do you expect one-second accuracy? This is important. Dec 2, 2022 at 19:17
  • Every 24 hours at a specific time, but I'd like a solution that I can use flexibly, so perhaps wake every few days. Dec 2, 2022 at 19:28
  • You need 1ppm or better if you want 1 second accuracy over a period on 1 week. That's not going to be easy or cheap. For a cheap RTC the accuracy will be limited by the crystal itself, not by the chip that counts the clock cycles. Dec 2, 2022 at 19:32
  • Are you sure? "The [DS3231 at ±3.5ppm] maintains the RTC to within ±2 minutes per year accuracy from -40°C to +85°C" (Source: datasheets.maximintegrated.com/en/ds/DS3231.pdf) Dec 2, 2022 at 19:34
  • Two minutes per year is 120 seconds over 52 weeks...worse than 2 seconds per week drift. I'm pretty sure. Dec 2, 2022 at 19:38

1 Answer 1

3

Since the ESP8266 doesn't have a real onboard RTC (i.e. it's not capable of keeping very accurate time while in deep sleep) and is limited to a short time period, one solution is to use an external RTC (real time clock) but there is a way of doing it without.

Below are 3 approaches, the first two involve a DS3231 RTC (quite easy to get as a module). For low power, I prefer DS3231 over DS1307, since DS3231 modules seem to work fine at 2.6V (whereas DS1307 modules seem to require at least 3.3V... or more like 4.5V according to the datasheet). The 3rd approach is useful if you don't want to add an external RTC.

"The DS3231 is a low-cost, extremely accurate I2C real-time clock (RTC)". The DS3231 "maintains the RTC to within ±2 minutes per year accuracy from -40°C to +85°C"

Solution 1: Periodically wake the MCU and check the external RTC

Probably, the most energy efficient solution (compared to using the alarm solution below) would be to wake the ESP8266 from deep sleep periodically to check the time on the external RTC and depending on desired accuracy, deep sleep for shorter periods as you approach the target wake time (e.g. wake and check the RTC every minute once you're about 10 mins away from the target time).

To save power, before sleeping the ESP8266, you can simply use a transistor connected to GPIO to turn off the Vcc power supply on the DS3231 module (it'll use the onboard 2032 battery while no power is supplied). Don't forget to turn off the I2C pull-ups too, as they can provide parasite power to the RTC. You can also just disconnect GND.

If restarted with WiFi off (ESP.deepSleep(1e6, WAKE_RF_DISABLED)), the ESP8266 only needs to wake for about 200 ms consuming 20 mA to check the DS3231 if it's time to wake up fully. This is probably the most energy efficient you can get with wanting to keep accurate time.

Solution 2: Set an alarm on the external RTC to wake the MCU

Using the DS3231, the SQW pin can be connected to the ESP8266 RST pin via a monostable circuit. The monostable circuit is important because the RST pin can't stay low, as it won't boot. The monostable circuit creates a pulse on the RST pin when the DS3231 SQW pin goes low.

A note on power consumption: The ESP8266 should consume only 20 µA in deep sleep, but DS3231 modules actually tend to use about 1 mA when powered on (which is required to trigger the alarm), probably because of the EEPROM on the module. That power consumption is significant if you're designing a low power system. This is why this 2nd solution is less energy efficient compared to the 1st solution.

The following code will set an alarm to a specific time, and when the RST pin is pulled low (based on the SQW output of the DS3231), the ESP8266 then wakes up. Make sure you uncomment SLEEP_DT and set a date and time to wake at a specific date and time (of course, you can set this programmatically to any date and time), otherwise it'll wake up every n seconds (value of SLEEP_SECONDS) but that might also be useful (ESP8266 deep sleep time isn't very accurate).

#include <ESP8266WiFi.h>

// TODO: NTPClient is redundant; replace with native
// ESP8266 NTP functions (configTime and localtime)
#include <NTPClient.h>
#include <WiFiUdp.h>

#include <RTClib.h>

#define WIFI_SSID "Your WiFi SSID"
#define WIFI_PASS "Your WiFi password"

// uncomment to wake up at specific date and time
//#define SLEEP_DT
#define SLEEP_DT_YEAR 2022
#define SLEEP_DT_MONTH 12
#define SLEEP_DT_DAY 02
#define SLEEP_DT_HOUR 15
#define SLEEP_DT_MINUTE 50
#define SLEEP_DT_SECOND 25

#define SLEEP_SECONDS 10 // easier for testing

RTC_DS3231 rtc;
WiFiUDP udp;
NTPClient ntp(udp);

void print_date(const DateTime& dt);

void setup() {
  Serial.begin(9600);
  while (!Serial) { delay(1); }
  
  Serial.println('\n');
  Serial.println("Hello RTC wake deep sleep");
  
  Serial.println("Init DS3231");
  if(!rtc.begin()) {
    Serial.println("DS3231 failed");
    while (true) { delay(1); }
  }

  // not needed
  rtc.disable32K();

  // not used
  rtc.disableAlarm(2);

  // apparently can lead to problems if not cleared
  rtc.clearAlarm(1);
  rtc.clearAlarm(2);

  // stop oscillating; allows alarm to work
  rtc.writeSqwPinMode(DS3231_OFF);
  
  Serial.println("Init WiFi");
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  
  Serial.println("Connecting to " WIFI_SSID);
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
  }
  Serial.print("Connected, IP: ");
  Serial.println(WiFi.localIP());
  
  ntp.begin();
  bool ntpOk = ntp.update();

  if (rtc.lostPower()) {
    Serial.println("RTC lost power, setting time");
    if (ntpOk) {
      Serial.println("Using time from NTP client");
      rtc.adjust(DateTime(ntp.getEpochTime()));
    }
    else {
      // untested code
      Serial.println("NTP unavailable, using time of build");
      rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    }
  }
  
  Serial.print("Date and time set to: ");
  print_datetime(rtc.now());

#ifdef SLEEP_DT

  DateTime alarm(
    SLEEP_DT_YEAR,
    SLEEP_DT_MONTH,
    SLEEP_DT_DAY,
    SLEEP_DT_HOUR,
    SLEEP_DT_MINUTE,
    SLEEP_DT_SECOND);

  Serial.print("Setting alarm for date: ");
  print_datetime(alarm);
    
  if (!rtc.setAlarm1(alarm, DS3231_A1_Date)) {
    Serial.println("Failed to set alarm");
  }

#else

  // sleep for set seconds for the purposes of testing
  Serial.print("Setting alarm for seconds: ");
  Serial.print(SLEEP_SECONDS);
  Serial.println(" seconds");
  if (!rtc.setAlarm1(rtc.now() + TimeSpan(SLEEP_SECONDS), DS3231_A1_Second)) {
    Serial.println("Failed to set alarm");
  }
  
#endif // SLEEP_DT

  Serial.println("Going into deep sleep (wake on RST low)");
  Serial.flush();
  ESP.deepSleep(0);
}

void print_datetime(const DateTime& dt) {
  char df[] = "YYYY-MM-DD hh:mm:ss";
  Serial.println(dt.toString(df));
}

void loop() { }

Here it is in action...

Arduino IDE and output

Note: The time jumps because I chopped out a few frames to save you from being bored (no smoke and mirrors, I promise).

Solution 3: Use only the internal RTC

Keeping with the low power theme: to use the internal RTC in deep sleep, you have to keep sleeping for n seconds until desired seconds elapse. You can keep track of how many seconds have elapsed by using the RTC memory. This approach allows you to implement time keeping without having to add an external RTC, and without having to reconnect connect to WiFi every hour. However, I have found the drift to be quite substantial (many seconds per hour), so this is probably useful only when accuracy is not a major concern.

16
  • the ESP8266 has an onboard RTC capable of keeping time while in deep sleep
    – Juraj
    Dec 2, 2022 at 17:03
  • "Despite being named RTC, the RTC is not really a Real Time Clock in the normal sense of the word. While it does keep a counter ticking while the module is sleeping, the accuracy with which it does so is highly dependent on the temperature of the chip.": nodemcu.readthedocs.io/en/dev/modules/rtctime Dec 2, 2022 at 17:05
  • "number of microseconds to sleep for. Maxmium value is 4294967295us, or ~71 minutes.": nodemcu.readthedocs.io/en/dev/modules/rtctime/#rtctimedsleep Dec 2, 2022 at 17:06
  • @Juraj Updated answer, thanks. Dec 2, 2022 at 17:10
  • 1
    of course it is redundant
    – Juraj
    Dec 5, 2022 at 19:06

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.