Как повторно синхронизировать время с NTP-сервера в esp-idf?

Я использую ESP32 с esp-idf. Мне нужно правильное время, поэтому я пытаюсь повторно синхронизировать время с NTP-сервером. Я использую этот пример. [1]: https://github.com/espressif/esp-idf/tree/master/examples/protocols/sntp

Когда я снова вызываю метод receive_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);

0 ответов

Похоже, вы пытаетесь инициализировать sntp каждый раз, когда обновляете время.

Следите за второй строкой функции receive_time:

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

Вы должны изменить свой код таким образом, чтобы initialize_sntp вызывалась только один раз.

К счастью sntp_stop() функция не удаляет предыдущие настройки (включая серверы), поэтому вы можете использовать это:

sntp_stop();
sntp_init();

При первом запуске синхронизация занимает ~30 с, а при последующих запусках ~500 мс.

Я помещаю это в задачу 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);
}

Чтобы решить эту проблему, вам нужно сделать пару вещей. 1.) Измените файл sntp.c вusers/[username]/.platformio/packages/framework-espidf/components/lwip/lwip/src/apps/sntp/sntp.c и users/[username]/.platformio/packages/framework-espidf/components/lwip/lwip/include/lwip/apps/sntp.h файлы и измените следующее:

а.) в файле sntp.c - измените static void sntp_request(void *arg) к "void sntp_request(void *arg)чтобы сделать эту функцию доступной для других модулей. Это где-то около строки 490 в исходном файле. Кроме того, в строке номер 160 удалите слово "статический", чтобы предотвратить ошибки компилятора.

б.) В заголовочный файл 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"устанавливается в истину при запуске программы и устанавливается в ложь после выполнения 1-й инициализации sntp. Вызов"sntp_setoperatingmode(SNTP_OPMODE_POLL)"можно выполнить только один раз, поэтому его необходимо поместить в sntp_1st_init раздел кода.

Я подтвердил, что эти изменения работают, изменяя системное время на что-то иное, чем правильное время, и наблюдая, что время корректируется, как ожидалось.

Первоначальные авторы ограничили функциональность кода sntp, сделав sntp_request()функция static, не позволяющая пользователю вносить дополнительные sntp-исправления во время часов компьютера. Даже самый лучший осциллятор со временем дрейфует, и если вы собираетесь использовать sntp, вы также можете разрешить коррекцию дрейфа часов.

Надеюсь это поможет.

Джерри

JWM Engineering Group

Путем выпуска 4386 документация SNTP была обновлена со следующим:

Приложение с этим кодом инициализации будет периодически синхронизировать время. Период синхронизации времени определяется параметром CONFIG_LWIP_SNTP_UPDATE_DELAY (значение по умолчанию - один час). Чтобы изменить переменную, установите CONFIG_LWIP_SNTP_UPDATE_DELAY в конфигурации проекта.

Все, что вам нужно, это использовать в своем приложении приведенный ниже код:

sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, "pool.ntp.org");
sntp_init();
Другие вопросы по тегам