Как повторно синхронизировать время с 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();