gyb001
gyb001

Reputation: 39

How to resync time from NTP server in esp-idf?

I use an ESP32 with esp-idf. I need correct time, therefore i'm trying resync time with NTP server. I use this example.[1]: https://github.com/espressif/esp-idf/tree/master/examples/protocols/sntp

When i call again the obtain_time() method the device is rebooting.

What do i wrong? I didn't find anything which help.

I (2259) initialise_wifi: Setting WiFi configuration SSID OpenWrt                                                                                                                                           
I (2359) syncTime: I'm runing :)                                                                                                                                        
I (2369) getTimeNow: Time is not set yet. Connecting to WiFi and getting time over NTP.                                                                                                    
I (2389) initialize_sntp: Initializing SNTP                                                                                                                                                
I (2389) obtain_time: Waiting for system time to be set... (1/10)                                                                                                                          
...                                                                                                                   
I (18389) obtain_time: Waiting for system time to be set... (9/10)  
-----The time is correct, but when i'm trying resync with NTP                                                                                                                        
I (20639) getTimeNow: Time is not set yet. Connecting to WiFi and getting time over NTP.                                                                                                   
I (20639) initialize_sntp: Initializing SNTP                                                                                                                                               
assertion "Operating mode must not be set while SNTP client is running" failed: file "/home/lenovov510/esp/esp-idf/components/lwip/lwip/src/apps/sntp/sntp.c", line 600, function: sntp_s
etoperatingmode                                                                                                                                                                            
abort() was called at PC 0x400d2c6b on core 1                                                                                                                                              

ELF file SHA256: 145d1f5e047670ed10c462ae090b3e64db1c5aa158a9988417a513b2ee801051                                                                                                          

Backtrace: 0x4008623c:0x3ffc7e00 0x40086489:0x3ffc7e20 0x400d2c6b:0x3ffc7e40 0x4011e251:0x3ffc7e70 0x400d28b4:0x3ffc7e90 0x400d28c7:0x3ffc7eb0 0x400d2aff:0x3ffc7f10 0x400d2bcd:0x3ffc7fa0 
0x4008b569:0x3ffc7fc0                                                                                                                                                                      

Rebooting...   

There is my methods:

This give back the timestamp.
void getDateTime(char *dateTime)
{
  char *TAG = "getDateTime";
  time_t now;
  struct tm timeinfo;
  time(&now);
  localtime_r(&now, &timeinfo);
  char strftime_buf[64];
  setenv("TZ", "GTM-2", 1);
  tzset();
  localtime_r(&now, &timeinfo);
  strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
  sprintf(dateTime, "20%d-%d-%d+%d:%d:%d", timeinfo.tm_year - 100, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
}

This method trying to update time. 
void syncTime()
{
  char *TAG = "syncTime";
  obtain_time();

}

static void obtain_time(void)
{
  static const char *TAG = "obtain_time";
  initialize_sntp();
  time_t now = 0;
  struct tm timeinfo = {0};
  int retry = 0;
  const int retry_count = 10;
  while (retry!=retry_count)// timeinfo.tm_year < (2016 - 1900) && ++retry < retry_count )
  {
    ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count);
    vTaskDelay(2000 / portTICK_PERIOD_MS);
    time(&now);
    localtime_r(&now, &timeinfo);
  }
}
//----
static void initialize_sntp(void)
{
  static const char *TAG = "initialize_sntp";
  ESP_LOGI(TAG, "Initializing SNTP");
  sntp_setoperatingmode(SNTP_OPMODE_POLL);
  sntp_setservername(0, "pool.ntp.org");
  sntp_init();

...

//Update the timeInSec and Datettime variable
void updateTimeVariables(void *pvParameter)
{
  char *TAG = "updateTimeVariables";
  while (1 == 1)
  {
    getDateTime(dateTime);
    timeInSec = getTimeNow();
    vTaskDelay(500 / portTICK_PERIOD_MS);
  }

  vTaskDelete(NULL);
}

//Sync NTP server.
void updateTime(void *pvParameter)
{
  char *TAG = "updateTime";
  while (1 == 1)
  {
    syncTime();
    vTaskDelay(10000 / portTICK_PERIOD_MS);//1800000 / portTICK_PERIOD_MS);
  }
  vTaskDelete(NULL);
}

...
 xTaskCreate(&updateTime, "updateTime", 4000, NULL, 6, NULL);
  xTaskCreate(&updateTimeVariables, "updateTimeVariables", 4000, NULL, 0, NULL);

Upvotes: 0

Views: 9115

Answers (4)

yml33t
yml33t

Reputation: 21

Through issue 4386 the SNTP documentation has been updated with the following:

An application with this initialization code will periodically synchronize the time. The time synchronization period is determined by CONFIG_LWIP_SNTP_UPDATE_DELAY (default value is one hour). To modify the variable, set CONFIG_LWIP_SNTP_UPDATE_DELAY in project configuration.

All you need is to use the below code in your application:

sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, "pool.ntp.org");
sntp_init();

Upvotes: 1

Brownstone
Brownstone

Reputation: 103

Luckily the sntp_stop() function doesn't remove previous settings (including servers) so you can use this:

sntp_stop();
sntp_init();

On the first run the sync takes ~30s and subsequent runs ~500ms.

I place this in a FreeRTOS task:

#include "esp_sntp.h"
#include "freertos/task.h"

void update(void* pvParameters) {
  while (true) {
    sntp_stop();
    sntp_init();
    vTaskDelay(pdMS_TO_TICKS(60 * 60 * 1000));
  }
}

void setup(void) {
  // Add your SNTP setup code here

  xTaskCreate(update, "NtpUpdate", 2048, NULL, tskIDLE_PRIORITY,
              &updateHandle);
}

Upvotes: 3

Jerry
Jerry

Reputation: 1

To solve this problem, you need to do a couple of things. 1.) Modify the sntp.c file in users/[username]/.platformio/packages/framework-espidf/components/lwip/lwip/src/apps/sntp/sntp.c and the users/[username]/.platformio/packages/framework-espidf/components/lwip/lwip/include/lwip/apps/sntp.h files and change the following:

a.) in the sntp.c file - change the static void sntp_request(void *arg) to " void sntp_request(void *arg) to make the function available to other modules. It's somewhere around line 490 in the source file. Also, at line number 160, remove the word "static" to prevent compiler errors.

b.) In the sntp.h header file, add the statement void sntp_request(void *) to make the function prototype available to your code.

Below is my code with modifications to allow calling sntp_request() as needed. I call mine every 30 minutes or so, but you could wait a longer time, maybe one a day, would be sufficient to keep the clock reasonably stable.

bool sntp_1st_init = true;                      // 1st init call allowed
static void obtain_time(void)
{
    if(sntp_1st_init)                           // doing this again?
        {
        sntp_setoperatingmode(SNTP_OPMODE_POLL);
        sntp_setservername(0, "north-america.pool.ntp.org");
        ESP_LOGI(TAG, "Initializing SNTP");
        sntp_1st_init = false;                  // don't call again
        sntp_init();                            // init and set time
        }
    else
        {
        ESP_LOGI(TAG, "Syncing System Time");
        sntp_request(NULL);                     // sync time again
        }

    // wait for System time to be set by monitoring Date changes
    int retry = 0;
    const int retry_count = 15;

    while(timeinfo.tm_year < (2016 - 1900) && ++retry < retry_count)
        {
        ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)",
                retry, retry_count);
        vTaskDelay((1 * ONEsec) / portTICK_PERIOD_MS);
        time(&now);
        localtime_r(&now, &timeinfo);
        }
}

The bool "sntp_1st_init" is set true on program startup and set false after 1st sntp init takes place. The call to "sntp_setoperatingmode(SNTP_OPMODE_POLL)" can only be performed one time, so must be placed in the sntp_1st_init code section.

I have confirmed that these changes work by altering the system time to something other than the correct time and observing that the time gets corrected as expected.

The original authors limited the functionality of the sntp code by making the sntp_request() function static, preventing the user from making additional sntp corrections to the computer clock time. Even the best oscillator drifts over time, and if your going to bother to use sntp, you might as well allow for clock drift corrections as well.

Hope this helps.

Jerry

JWM Engineering Group

Upvotes: 0

Vlad A.
Vlad A.

Reputation: 31

Looks like you are trying to initialize sntp every time you update time.

Watch for second line of obtain_time function:

static const char *TAG = "obtain_time";
initialize_sntp();                     //   <<<< THIS ONE.
time_t now = 0;
struct tm timeinfo = {0};
//.....

You have to change you code in a way initialize_sntp would be called only once.

Upvotes: 2

Related Questions