espressif / arduino-esp32

Arduino core for the ESP32
GNU Lesser General Public License v2.1
13.54k stars 7.39k forks source link

How to make use of ESP32 internal RTC to setup Epoch time? #3641

Closed ghost closed 4 years ago

ghost commented 4 years ago

As mentioned in the datasheet of ESP32, I was trying to make use of the internal RTC. But not able to find any example or APIs related to it. Is there any way to access the RTC?

lbernstone commented 4 years ago

The default timer system uses both the RTC and the APB timers to track the internal time. It is possible to force the system to use only the RTC for time keeping (config). This requires recompiling the libraries, but I am not even sure arduino would function if you did so. Why don't you explain what you would like to achieve by doing so? Perhaps just this?

chegewara commented 4 years ago

setTime(epoch) epoch = now()

everslick commented 4 years ago

maybe a misunderstanding? RTC != real time clock like DS3231 !!!

chegewara commented 4 years ago

This is code i am using to get time from NTP and set local time (internal RTC) and DS1307:

const char* ntpServer = "pool.ntp.org";
  const long  gmtOffset_sec = 3600;
  const int   daylightOffset_sec = 3600;

  // get NTP time every time connect to wifi, not necessary but wont hurts
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  struct tm timeinfo;

  if (getLocalTime(&timeinfo)) {
    timeAdjusted = true;
    setTime(timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec,
            timeinfo.tm_mday, timeinfo.tm_mon+1,timeinfo.tm_year - 100); // <--- set internal RTC

    RTC.set(now()); // <--- read internal RTC (epoch) with now() and set DS1307
  }
a-c-sreedhar-reddy commented 4 years ago

Hi @chegewara. can you try this

if (getLocalTime(&timeinfo)) {
    timeAdjusted = true;
    time_t now;
    time(&now);
    RTC.set(now); // <--- read internal RTC (epoch) with now() and set DS1307
  }

Got it from https://github.com/espressif/arduino-esp32/issues/935#issuecomment-353094810

ghost commented 4 years ago

I have seen in the library, the now() function actually runs millis() to increment count. Using the time.h library and now() function, I kept my ESP 32 over night after once getting EPOCH from NTP server. After getting the EPOCH once I turned the wifi off and in the morning after about 12 hrs, I found that the time has actually elapsed by 30 minutes. So what I can see now is there is no such internal RTC which works like an external RTC(like DS3231 !!!) generally which runs on battery, as said by "everslick".

a-c-sreedhar-reddy commented 4 years ago

What I do is update for every 10 loops. I am not sure whats the correct way to fix this.

lbernstone commented 4 years ago

As I mentioned, the time keeping is based on the APB clock. This, in turn, is based on an external oscillator. I think the ones used by espressif are probably +-1%, which is 864 seconds over a day. If you want accuracy, use a purpose built ASIC. The device was built around network connectivity, and it is simple to sync your time over the network.

chegewara commented 4 years ago

Hi @chegewara. can you try this

if (getLocalTime(&timeinfo)) {
    timeAdjusted = true;
    time_t now;
    time(&now);
    RTC.set(now); // <--- read internal RTC (epoch) with now() and set DS1307
  }

Got it from #935 (comment)

I dont have issues with my code, it works exactly as intended. It was exaampple to show how to use internal RTC with now() to get epoch and how to set internal RTC with NTP call.

chegewara commented 4 years ago

@Gopal123456789 You asked how to use internal RTC, you got the answer. Now about accuracy: https://www.esp32.com/viewtopic.php?t=3715#p17038

stickbreaker commented 4 years ago

@Gopal123456789 The RTC need an external 32.768KHz crystal. ESP32 Hardware doc esp32 rtc circuit

Chuck.

ghost commented 4 years ago

Thank you all of you for your help. @chegewara, Can you please tell me which library do you use for the example code you have given, for the below lines? Or give a link to the library?

setTime(timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, timeinfo.tm_mday, timeinfo.tm_mon+1,timeinfo.tm_year - 100); // <--- set internal RTC

RTC.set(now()); // <--- read internal RTC (epoch) with now() and set DS1307
chegewara commented 4 years ago

https://github.com/PaulStoffregen/Time https://github.com/PaulStoffregen/DS1307RTC.git

PS in Time library is Time.h header file, which is causing conflict on esp32, i had to rename it

ghost commented 4 years ago

Thank you chegewara.

stale[bot] commented 4 years ago

[STALE_SET] This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions.

stale[bot] commented 4 years ago

[STALE_DEL] This stale issue has been automatically closed. Thank you for your contributions.

mohsendianati commented 3 years ago

@Gopal123456789 The RTC need an external 32.768KHz crystal. ESP32 Hardware doc esp32 rtc circuit

Chuck.

Using external xtal is not mandatory. It increases both accuracy and current consumption. It's possible to configure internal clock for RTC.

gitcnd commented 2 years ago

My code that works:-

/* Constructor for the RTC on the logging shield. */
DFRobot_DS1307 DS1307;
char daynames[]="SUN\0MON\0TUE\0WED\0THU\0FRI\0SAT\0";

// Set the ESP32 time from the RTC.
bool StartRTC() { // See http://www.rinkydinkelectronics.com/resource/DS1307/DS1307.pdf
  if( !(DS1307.begin()) ){
    Serial.println(F("Communication with DS1307 RTC device failed"));
    return 1;
  }
  struct dsTimeStruct {
    uint16_t tm_sec;         /* seconds,  range 0 to 59          */
    uint16_t tm_min;         /* minutes, range 0 to 59           */
    uint16_t tm_hour;        /* hours, range 0 to 23             */
    uint16_t tm_wday;        /* day of the week, range 0 to 6    */               
    uint16_t tm_mday;        /* day of the month, range 1 to 31  */
    uint16_t tm_mon;         /* month, range 1 to 12 (not 0 to 11!) */
    uint16_t tm_year;        /* The number of years since 1900   */
    uint16_t tm_yday;        /* day in the year, range 0 to 365  */
    uint16_t tm_isdst;       /* daylight saving time             */ 
  } dstm ={0};

  DS1307.getTime((uint16_t *)&dstm); //dstm.tm_mon--; // The month on the DS1307 is 1-12.

  Serial.printf("DS1307 time: %s %4d/%02d/%02d %02d:%02d:%02d UTC\r\n", // DS1307 time: SUN 2022/07/17 13:06:10 UTC
  &daynames[dstm.tm_wday*4], //Weekday  &daynames[(getTimeBuff[3])*4], //Weekday  [3]
            dstm.tm_year, // Year [6]
            dstm.tm_mon,  // Month [5]
            dstm.tm_mday, // Day [4]
            dstm.tm_hour, // Hours  dstm.dsRaw[2]
            dstm.tm_min,  // Mins   dstm.dsRaw[1]
            dstm.tm_sec   // seconds dstm.dsRaw[0]
            ); 

  time_t seconds;
  time(&seconds); // Put the time into seconds
  struct tm *esptm;
  esptm=gmtime(&seconds);
  Serial.printf("ESP32 internal time: %s",asctime(esptm)); // ESP32 internal time: Thu Jan  1 00:00:00 1970

  //DS1307 format is not same as linux ( they put the tm_wday in the middle, and don't have yday and isdst).
  struct tm newdstm;  newdstm.tm_sec =dstm.tm_sec; newdstm.tm_min =dstm.tm_min; newdstm.tm_hour=dstm.tm_hour;
  newdstm.tm_wday=dstm.tm_wday; newdstm.tm_mday=dstm.tm_mday; newdstm.tm_mon =dstm.tm_mon-1; newdstm.tm_year=dstm.tm_year-1900; 
  // Serial.printf("newdstm time: %s",asctime(&newdstm)); // newdstm time: Mon Jul 17 13:09:32 2022
  struct timeval dstv = { .tv_sec = mktime(&newdstm), .tv_usec = 0}; // &dstm.tm_ds
  settimeofday(&dstv, NULL);  

  time(&seconds); // Put the updated-above time into seconds
  esptm=gmtime(&seconds); // Convert epoc-based to D/M/Y...
  Serial.printf("new ESP32 internal time: %s",asctime(esptm)); // new ESP32 internal time: Sun Jul 17 13:14:29 2022

  return 0;
} // StartRTC
Gendiaaa commented 2 years ago

My code that works:-

/* Constructor for the RTC on the logging shield. */
DFRobot_DS1307 DS1307;
char daynames[]="SUN\0MON\0TUE\0WED\0THU\0FRI\0SAT\0";

// Set the ESP32 time from the RTC.
bool StartRTC() { // See http://www.rinkydinkelectronics.com/resource/DS1307/DS1307.pdf
  if( !(DS1307.begin()) ){
    Serial.println(F("Communication with DS1307 RTC device failed"));
    return 1;
  }
  struct dsTimeStruct {
    uint16_t tm_sec;         /* seconds,  range 0 to 59          */
    uint16_t tm_min;         /* minutes, range 0 to 59           */
    uint16_t tm_hour;        /* hours, range 0 to 23             */
    uint16_t tm_wday;        /* day of the week, range 0 to 6    */               
    uint16_t tm_mday;        /* day of the month, range 1 to 31  */
    uint16_t tm_mon;         /* month, range 1 to 12 (not 0 to 11!) */
    uint16_t tm_year;        /* The number of years since 1900   */
    uint16_t tm_yday;        /* day in the year, range 0 to 365  */
    uint16_t tm_isdst;       /* daylight saving time             */ 
  } dstm ={0};

  DS1307.getTime((uint16_t *)&dstm); //dstm.tm_mon--; // The month on the DS1307 is 1-12.

  Serial.printf("DS1307 time: %s %4d/%02d/%02d %02d:%02d:%02d UTC\r\n", // DS1307 time: SUN 2022/07/17 13:06:10 UTC
  &daynames[dstm.tm_wday*4], //Weekday  &daynames[(getTimeBuff[3])*4], //Weekday  [3]
            dstm.tm_year, // Year [6]
            dstm.tm_mon,  // Month [5]
            dstm.tm_mday, // Day [4]
            dstm.tm_hour, // Hours  dstm.dsRaw[2]
            dstm.tm_min,  // Mins   dstm.dsRaw[1]
            dstm.tm_sec   // seconds dstm.dsRaw[0]
            ); 

  time_t seconds;
  time(&seconds); // Put the time into seconds
  struct tm *esptm;
  esptm=gmtime(&seconds);
  Serial.printf("ESP32 internal time: %s",asctime(esptm)); // ESP32 internal time: Thu Jan  1 00:00:00 1970

  //DS1307 format is not same as linux ( they put the tm_wday in the middle, and don't have yday and isdst).
  struct tm newdstm;  newdstm.tm_sec =dstm.tm_sec; newdstm.tm_min =dstm.tm_min; newdstm.tm_hour=dstm.tm_hour;
  newdstm.tm_wday=dstm.tm_wday; newdstm.tm_mday=dstm.tm_mday; newdstm.tm_mon =dstm.tm_mon-1; newdstm.tm_year=dstm.tm_year-1900; 
  // Serial.printf("newdstm time: %s",asctime(&newdstm)); // newdstm time: Mon Jul 17 13:09:32 2022
  struct timeval dstv = { .tv_sec = mktime(&newdstm), .tv_usec = 0}; // &dstm.tm_ds
  settimeofday(&dstv, NULL);  

  time(&seconds); // Put the updated-above time into seconds
  esptm=gmtime(&seconds); // Convert epoc-based to D/M/Y...
  Serial.printf("new ESP32 internal time: %s",asctime(esptm)); // new ESP32 internal time: Sun Jul 17 13:14:29 2022

  return 0;
} // StartRTC

is this can be done with DS3231 could you please help to do the same but with ds3231 which the one available here

Gendiaaa commented 2 years ago

My code that works:-

/* Constructor for the RTC on the logging shield. */
DFRobot_DS1307 DS1307;
char daynames[]="SUN\0MON\0TUE\0WED\0THU\0FRI\0SAT\0";

// Set the ESP32 time from the RTC.
bool StartRTC() { // See http://www.rinkydinkelectronics.com/resource/DS1307/DS1307.pdf
  if( !(DS1307.begin()) ){
    Serial.println(F("Communication with DS1307 RTC device failed"));
    return 1;
  }
  struct dsTimeStruct {
    uint16_t tm_sec;         /* seconds,  range 0 to 59          */
    uint16_t tm_min;         /* minutes, range 0 to 59           */
    uint16_t tm_hour;        /* hours, range 0 to 23             */
    uint16_t tm_wday;        /* day of the week, range 0 to 6    */               
    uint16_t tm_mday;        /* day of the month, range 1 to 31  */
    uint16_t tm_mon;         /* month, range 1 to 12 (not 0 to 11!) */
    uint16_t tm_year;        /* The number of years since 1900   */
    uint16_t tm_yday;        /* day in the year, range 0 to 365  */
    uint16_t tm_isdst;       /* daylight saving time             */ 
  } dstm ={0};

  DS1307.getTime((uint16_t *)&dstm); //dstm.tm_mon--; // The month on the DS1307 is 1-12.

  Serial.printf("DS1307 time: %s %4d/%02d/%02d %02d:%02d:%02d UTC\r\n", // DS1307 time: SUN 2022/07/17 13:06:10 UTC
  &daynames[dstm.tm_wday*4], //Weekday  &daynames[(getTimeBuff[3])*4], //Weekday  [3]
            dstm.tm_year, // Year [6]
            dstm.tm_mon,  // Month [5]
            dstm.tm_mday, // Day [4]
            dstm.tm_hour, // Hours  dstm.dsRaw[2]
            dstm.tm_min,  // Mins   dstm.dsRaw[1]
            dstm.tm_sec   // seconds dstm.dsRaw[0]
            ); 

  time_t seconds;
  time(&seconds); // Put the time into seconds
  struct tm *esptm;
  esptm=gmtime(&seconds);
  Serial.printf("ESP32 internal time: %s",asctime(esptm)); // ESP32 internal time: Thu Jan  1 00:00:00 1970

  //DS1307 format is not same as linux ( they put the tm_wday in the middle, and don't have yday and isdst).
  struct tm newdstm;  newdstm.tm_sec =dstm.tm_sec; newdstm.tm_min =dstm.tm_min; newdstm.tm_hour=dstm.tm_hour;
  newdstm.tm_wday=dstm.tm_wday; newdstm.tm_mday=dstm.tm_mday; newdstm.tm_mon =dstm.tm_mon-1; newdstm.tm_year=dstm.tm_year-1900; 
  // Serial.printf("newdstm time: %s",asctime(&newdstm)); // newdstm time: Mon Jul 17 13:09:32 2022
  struct timeval dstv = { .tv_sec = mktime(&newdstm), .tv_usec = 0}; // &dstm.tm_ds
  settimeofday(&dstv, NULL);  

  time(&seconds); // Put the updated-above time into seconds
  esptm=gmtime(&seconds); // Convert epoc-based to D/M/Y...
  Serial.printf("new ESP32 internal time: %s",asctime(esptm)); // new ESP32 internal time: Sun Jul 17 13:14:29 2022

  return 0;
} // StartRTC

your code is not working as it should be for me DS1307 time: MON 2000/01/01 00:53:42 UTC ESP32 internal time: Thu Jan 1 00:00:01 1970 new ESP32 internal time: Sat Jan 1 00:53:42 2000 Arduino IoT Cloud - configuration info