如何在esp-idf中从NTP服务器重新同步时间?

时间:2019-05-07 15:10:08

标签: c time ntp esp-idf

我使用带有esp-idf的ESP32。我需要正确的时间,因此我正在尝试与NTP服务器重新同步时间。 我使用此示例。[1]:https://github.com/espressif/esp-idf/tree/master/examples/protocols/sntp

当我再次调用gain_time()方法时,设备正在重新引导。

我怎么了?我没有找到任何帮助。

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...   

有我的方法:

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);

4 个答案:

答案 0 :(得分:1)

看起来您每次更新时间都在尝试初始化sntp。

注意gain_time函数的第二行:

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

您必须以只调用一次 initialize_sntp 的方式更改代码。

答案 1 :(得分:0)

要解决此问题,您需要做几件事。 1.)修改users/[username]/.platformio/packages/framework-espidf/components/lwip/lwip/src/apps/sntp/sntp.cusers/[username]/.platformio/packages/framework-espidf/components/lwip/lwip/include/lwip/apps/sntp.h文件中的sntp.c文件并更改以下内容:

a。)在sntp.c文件中-将static void sntp_request(void *arg)更改为“ void sntp_request(void *arg)使该功能可用于其他模块。它位于源文件中第490行附近。另外,在第160行,删除单词“ static”以防止编译器错误。

b。)在sntp.h头文件中,添加语句void sntp_request(void *),以使函数原型可用于您的代码。

下面是我的代码,并进行了修改以允许根据需要调用sntp_request()。我每30分钟左右打电话给我,但是您可以等待更长的时间(也许一天一次),足以使时钟保持合理的稳定。

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);
        }
}

布尔“ sntp_1st_init”在程序启动时设置为true,在第一次sntp初始化之后设置为false。对“ sntp_setoperatingmode(SNTP_OPMODE_POLL)”的调用只能执行一次,因此必须放在sntp_1st_init代码部分。

我已经确认可以通过将系统时间更改为除正确时间之外的其他时间并观察到时间已按预期得到纠正的方式来进行这些更改。

原始作者通过使sntp_request()函数为静态函数来限制sntp代码的功能,从而阻止用户对计算机时钟时间进行其他sntp校正。甚至最好的振荡器也会随时间漂移,如果您不愿意使用sntp,则最好也进行时钟漂移校正。

希望这会有所帮助。

杰里

JWM工程集团

答案 2 :(得分:0)

幸运的是,sntp_stop()函数不会删除以前的设置(包括服务器),因此您可以使用此功能:

sntp_stop();
sntp_init();

在第一次运行时,同步大约需要30秒,而随后的运行大约需要500ms。

我将其放置在FreeRTOS任务中:

#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);
}

答案 3 :(得分:0)

通过问题4386 SNTP documentation已进行以下更新:

具有此初始化代码的应用程序将定期 同步时间。时间同步周期由 CONFIG_LWIP_SNTP_UPDATE_DELAY(默认值为一小时)。修改 在项目中设置变量CONFIG_LWIP_SNTP_UPDATE_DELAY 配置。

您需要做的就是在您的应用程序中使用以下代码:

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