Программирование ARM ESP-IDF System Time Tue, April 23 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

ESP-IDF System Time Печать
Добавил(а) microsin   

ESP32-C3 использует 2 аппаратных таймера для отслеживания системного времени, RTC timer (далее сокращенно RTC) и High-resolution timer (сокращенно HRT). Системное время может отсчитываться либо одним из них, либо сразу двумя - в зависимости от требований к приложению и необходимой точности отсчета. Эти 2 аппаратных таймера следующие:

RTC: этот таймер позволяет отслеживать время в различных режимах пониженного энергопотребления (sleep modes), и может также отслеживать время между любыми типами сброса за исключением сброса по питанию (power-on reset), который сбросит таймер RTC. Девиация частоты зависит от настроенного источника тактирования (RTC Timer Clock Source) и влияет на точность только в режимах пониженного энергопотребления, в которых время может измеряться с дискретностью 6.6667 мкс.

HRT: этот таймер недоступен в режимах пониженного энергопотребления, и не сохранит свое значение после сброса, но у него точность выше. Этот таймер использует источник тактов APB_CLK (типовое значение 80 МГц), что дает девиацию частоту менее ±10 ppm. Время будет измеряться с точностью 1 мкс.

Ниже перечислены возможные комбинации аппаратных таймеров для отслеживания системного времени:

RTC + HRT (по умолчанию, в menuconfig это вариант "RTC and high-resolution timer")
RTC
HRT
None (ни один из аппаратных таймеров для системного времени не используется)

Рекомендуется придерживаться варианта по умолчанию, поскольку он обеспечивает самую высокую точность. Однако пользователи могут выбрать другую настройку опцией конфигурации CONFIG_NEWLIB_TIME_SYSCALL (idf.py menuconfig → Component config → Newlib → Timers used for gettimeofday function).

menuconfig System Time default

[Тактирование RTC]

Таймер RTC может тактироваться от следующих источников:

• Внутренний RC-генератор 136 кГц (по умолчанию): обеспечивает самое низкое энергопотребление в режиме (Deep-sleep) и отсутствие зависимости от каких-либо внешних компонентов. Однако на стабильность частоты этого источника влияет изменение температуры, и частота может меняться в режимах Deep-sleep и Light-sleep.

• Внешний кварц 32 кГц: требует подключения снаружи кварцевого резонатора на 32 кГц к выводам XTAL_32K_P (вход) и XTAL_32K_N (выход). Этот источник дает лучшую стабильность частоты и немного выше потребление тока (на 1 мкА) в режиме Deep-sleep.

• Внешний генератор 32 кГц на выводе XTAL_32K_P: позволяет использовать частоту 32 кГц, генерируемую внешней схемой. Амплитуда сигнала, подаваемого на вывод XTAL_32K_P, должна быть меньше 1.2V для синусоидальной формы и меньше 1V для прямоугольной формы сигнала. Напряжение относительно земли (Vcm) должно быть в диапазоне 0.1 < Vcm < 0.5 x Vamp, где Vamp означает амплитуду сигнала. В этом случае вывод XTAL_32K_P нельзя использовать как порт GPIO.

• Внутренний генератор 17.5 МГц, частота которого поделена на 256 (~68 кГц): дает лучшую стабильность частоты в сравнении с внутренним RC-генератором 136 кГц ценой увеличения тока потребления (на 5 мкА) в режиме Deep-sleep. Этот вариант также не требует внешних компонентов.

Выбор зависит от требований для вашей системы к точности отсчета времени и к энергопотреблению в режимах сна (sleep modes). Чтобы изменить источник тактирования RTC, установите CONFIG_RTC_CLK_SRC в конфигурации проекта (idf.py menuconfig → Component config → Hardware Settings → RTC Clock Config → RTC clock source).

Информацию о требованиях к разводке цепей для внешнего кварца или внешнего генератора см. в разделе "2.4 Clock Source" руководства [2].

[Получение текущего времени]

Чтобы узнать текущее время, используйте POSIX-функция gettimeofday(). Дополнительно вы можете использовать следующие стандартные библиотечные функции языка C для получения времени и для манипуляции его значением:

int gettimeofday(struct timeval *, struct timezone *);

Функция gettimeofday() определена в заголовочном файле sys/time.h. Как многие другие функции с префиксом get, у неё есть комплементарная функция settimeofday().

Параметры:

timeval Это указатель на структуру, к которой содержатся 2 поля: tv_sec (тип time_t) и tv_usec (тип suseconds_t). В поле tv_sec присутствует абсолютное количество секунд, прошедших с момента 1 января 1970 года. В поле tv_usec содержит добавочное количество микросекунд, которое не включено в значение time_t tv_sec.

struct timeval {
   time_t      tv_sec;  /* количество секунд */
   suseconds_t tv_usec; /* и микросекунд */
};

timezone Это указатель на структуру, где содержатся 2 поля, соответствующие информации временной зоны: int tz_minuteswest, значение в минутах западнее от GMT для текущей зоны времени, и поле int tz_dsttime, где указано количество часов для подстройки коррекции летнего времени (daylight saving time). Эти подробности недоступны в других функциях обработки времени.

struct timezone {
   int tz_minuteswest;  /* сколько минут к западу от Гринвича (GMT) */
   int tz_dsttime;      /* тип коррекции летнего времени */
};

Возвращаемое значение: gettimeofday() вернет 0 в случае успеха, и -1 в случае ошибки, и в этом случае будет установлена переменная errno.

Пример программы для тестирования функции gettimeofday():

#include < stdio.h>
#include < sys/time.h>
 
int main()
{
   struct timeval tv;
   struct timezone tz;
 
   gettimeofday(&tv,&tz);
 
   printf("Сколько секунд прошло от 1/1/1970: %lu\n", tv.tv_sec);
   printf("... и микросекунд: %d\n", tv.tv_usec);
   printf("Минут западнее Гринвича: %d\n", tz.tz_minuteswest);
   printf("Коррекция летнего времени: %d час\n",tz.tz_dsttime);
 
   return(0);
}

В качестве временного хранилища вычисляемых значений здесь определены локальные переменные tv и tz. Если декларировать их как указатели, то нужно гарантировать, что им присвоены адреса на выделенные области в памяти. В функцию gettimeofday можно передать и нулевые указатели (NULL), в этом случае функция не будет вычислять и заполнять значения для timeval и timezone.

Пример вывода этой программы:

Сколько секунд прошло от 1/1/1970: 1685491200
... и микросекунд: 613564
Минут западнее Гринвича: 480
Коррекция летнего времени: 1 час

Очевидно, результаты такого вывода не всегда полезны для практического применения. Усовершенствованный вариант кода:

#include < stdio.h>
#include < sys/time.h>
#include < time.h>
 
int main()
{
   struct timeval tv;
   struct timezone tz;
   struct tm *today;
   int zone;
 
   gettimeofday(&tv,&tz);
 
   /* Отображение времени в удобном формате: */
   today = localtime(&tv.tv_sec);
   printf("%d:%0d:%0d.%d\n",
            today->tm_hour,
            today->tm_min,
            today->tm_sec,
            tv.tv_usec);
   /* Установка временной зоны на часы, не минуты: */
   zone = tz.tz_minuteswest/60;
   /* Вычисление для зон восточнее GMT: */
   if( zone > 12 )
      zone -= 24;
   printf("Зона времени: GMT %+d\n", zone);
   printf("Коррекция летнего времени: %d час\n", tz.tz_dsttime);
 
   return(0);
}

Здесь добавлена функция localtime(), чтобы обработать значение time_t из tv.tv_sec. Это значение транслируется в формат hh:mm:ss, и также выводится добавочное значение микросекунд.

Временная зона из tz.tz_minuteswest сохраняется в переменную zone, и корректируется с учетом случая, когда зона находится восточнее Гринвича. This result is output at Line 27. Формат %+d обеспечивает вывод для значения зоны префикса знака + или –.

Пример вывода этого кода:

10:15:23.952229
Зона времени: GMT +8
Коррекция летнего времени: 1

#include < time.h>
 
time_t time(time_t *t);

Возвращает время в секундах, прошедшее с начала этой эпохи (00:00:00 UTC, 1 Января 1970 года). Если t не равно NULL, то возвращаемое значение будет также сохранено в памяти структуры t. При ошибке возвращается max(time_t)-1, а в переменной errno код ошибки.

#include < time.h>
 
char *asctime(const struct tm *tm);
char *asctime_r(const struct tm *tm, char *buf);
char *ctime_r(const time_t *timep, char *buf);
char *ctime(const time_t *timep);
struct tm *gmtime(const time_t *timep);
struct tm *gmtime_r(const time_t *timep, struct tm *result);
struct tm *localtime(const time_t *timep);
struct tm *localtime_r(const time_t *timep, struct tm *result);
time_t mktime(struct tm *tm);

Функции ctime(), gmtime() и localtime() в качестве аргумента используют тип данных time_t , представляющий собой календарное время. Функции интерпретируют его как абсолютное время, содержащее количество секунд, прошедших с 00:00:00 1 января 1970 года времени UTC.

Функции asctime() и mktime() используют в качестве аргумента локальное время в календарном представлении, то есть разделенное на год, месяц, день и т.д. Время в календарном представлении хранится в структуре tm, которая определена в файле time.h следующим образом:

struct tm {
   int tm_sec;         /* секунды */
   int tm_min;         /* минуты */
   int tm_hour;        /* часы */
   int tm_mday;        /* день месяца */
   int tm_mon;         /* месяц */
   int tm_year;        /* год */
   int tm_wday;        /* день недели */
   int tm_yday;        /* день года */
   int tm_isdst;       /* летнее время */
};

Члены структуры tm:

tm_sec Число секунд, прошедших после минуты, обычно в диапазоне от 0 до 59; но для того, чтобы установить високосную секунду, используются числа до 61.

tm_min Число минут, прошедших после часа, от 0 до 59.

tm_hour Количество прошедших часов после полуночи, от 0 до 23.

tm_mday День месяца, от 1 до 31.

tm_mon Число месяцев, прошедших с января, от 0 до 11.

tm_year Число лет, прошедших с 1900.

tm_wday Число дней, прошедших с воскресенья, от 0 до 6.

tm_yday Количество дней, прошедших с 1 января, от 0 до 365.

tm_isdst Этот флаг показывает, в действительности ли "летнее" время учтено в этой структуре. Значение флага положительно, если "летнее" время учитывается, 0, если нет, и отрицательно, если информация недоступна.

Функция ctime(t) является эквивалентом asctime(localtime(t)). Она преобразует календарное время t в строку формата:

"Wed Jun 30 21:49:08 1993\n"

Аббревиатуры дней недели: `Sun', `Mon', `Tue', `Wed', `Thu', `Fri' и `Sat'. Аббревиатуры месяцев: `Jan', `Feb', `Mar', `Apr', `May', `Jun', `Jul', `Aug', `Sep', `Oct', `Nov' и `Dec'. Возвращаемое значение указывает на статически размещенную строку, которая может быть заново записана с помощью последовательности вызовов любых функций даты и времени. Функция также устанавливает значение внешней переменной tzname (см. tzset()) равным значению текущей временной зоны. Реентрантная версия ctime_r() делает то же самое, но заносит строку в буфер, предоставляемый пользователем. Длина буфера должна быть не менее 26 байт. Устанавливать tzname необязательно.

Функция gmtime() преобразует календарное время в локальное представление времени, определенное во всеобщем скоординированном времени (UTC). Но она может вернуть значение NULL, если год не может быть описан типом int. Возвращаемое значение указывает на статически выделенную структуру, содержимое которой может быть перезаписано последующими вызовами любых функций, работающих с датой и временем. Функция gmtime_r() делает то же самое, но помещает данные в структуру, предоставленную пользователем.

Функция localtime() преобразует календарное время в локальное, определенное в соответствии с заданной пользователем временной зоной. Функция работает так, как будто она вызывает tzset(), и устанавливает внешние переменные: tzname в значение текущей временной зоны, timezone в значение разницы в секундах между всеобщим скоординированным временем (UTC) и локальным стандартом времени и daylight в ненулевое значение, если используются стандартные правила "летнего" времени. Возвращаемое значение указывает на статически выделенную структуру, содержимое которой может быть перезаписано последующими вызовами любых функций, работающих с датой и временем. Функция localtime_r() делает то же самое, но помещает данные в структуру, предоставленную пользователем. Она не нуждается в установке tzname.

Функция asctime() преобразует значение локального времени tm в строку того же формата, что и функция ctime(). Возвращаемое значение указывает на статическую строку, которая может быть перезаписана последовательностью вызовов любых функций даты и времени. Функция asctime_r() делает то же самое, но заносит строку в буфер, предоставленный пользователем. Длина буфера должна быть не менее 26 байт.

Функция mktime() преобразует время в календарном представлении в абсолютное время. Функция игнорирует содержимое полей структуры tm_wday и tm_yday и пересчитывает их значения в соответствии с другими элементами структуры. Если значения элементов структуры выходят за пределы допустимых значений, то они "нормализуются" (так, например, 40 октября превращается в 9 ноября). Вызов mktime() также присваивает внешней переменной tzname значение текущей временной зоны. Если локальное время не может быть представлено как календарное (число секунд с начала века), то mktime() возвращает значение max(time_t)-1 и не изменяет значения членов структуры локального времени tm_wday и tm_yday.

Возвращаемое значение: в случае успеха все вышеперечисленные функции возвращают описанное значение, или NULL (-1 для mktime()) при возникновении ошибки.

#include < time.h>clock_t clock(void);

Функция clock() возвращает суммарное процессорное время, использованное программой. Возвращаемое значение - это время, затраченное процессором на выполнение программы, представленное типом clock_t. Чтобы узнать количество затраченных на выполнение секунд, разделите возвращенное значение на CLOCKS_PER_SEC. Если затраченное процессором время недоступно или если его значение не может быть определено, то функция возвращает значение max(clock_t)-1.

#include < time.h>double difftime (time_t time1 time_t time0);

Возвратит разницу в секундах между двумя абсолютными метками времени (time1 - time0).

size_t strftime (char *buf, size_t maxsize, const char *fmt, const struct tm *tm);

Функция берет информацию из структуры tm, и преобразует её в текст и помещает в буфер buf. Для преобразования используется формат, указанный параметром fmt. Формат состоит из 0 или большего количества спецификаторов формата и обычных символов. Все обычные функции копируются напрямую в выходной буфер buf. Спецификаторы преобразования состоят из знака процента (%) и одного из символов формата (см. список форматов далее).

В выходной буфер не может быть помещено больше чем maxsize символов. Если общее количество символов выходного результата, включая завершающий '\0', не превышает maxsize, то strftime() вернет количество полезных символов в этом массиве, не включая завершающий '\0'. Иначе будет возвращен 0, и состояние выходного буфера будет неопределенное.

Спецификаторы формата преобразования:

%A Заменяется национальным представлением полного имени дня недели.

%a Заменяется аббревиатурой национального представления имени дня недели.

%B Заменяется национальным представлением полного имени месяца.

%b Заменяется аббревиатурой национального представления имени месяца.

%C Заменяется на (год / 100) в виде десятичного числа; одиночным цифрам предшествует 0.

%c Заменяется национальным представлением времени и даты.

%D Эквивалент "%m/%d/%y".

%d Заменяется днем месяцы в виде десятичного числа (01 .. 31).

%E*%O* Расширения локали POSIX. Поддерживаются последовательности %Ec %EC %Ex %EX %Ey %EY %Od %Oe %OH %OI %Om %OM %OS %Ou %OU %OV %Ow %OW %Oy для альтернативных представлений преобразования. Дополнительно реализовано %OB для представления альтернативных названий месяцев (используется автономно, без указания дня).

%e Заменяется днем месяца в виде десятичного числа (1 .. 31); одиночным цифрам предшествует пробел.

%F Эквивалентно "%Y-%m-%d".

%G Заменяется на год в виде десятичного числа с веком (4 десятичные цифры). Значение года содержит большую часть недели (понедельник как первый день недели).

%g Заменяется таким же годом, как и с использованием %G, но без десятичого числа века (00 .. 99).

%H Заменяется на значение часа (24-часовое представление) в виде десятичного числа (00 .. 23).

%h То же самое, что и %b.

%I Заменяется на значение часа (12-часовое представление) в виде десятичного числа (01 .. 12).

%j Заменяется на день года (001 .. 366).

%k Заменяется на значение часа (24-часовое представление) в виде десятичного числа (0 .. 23); одиночным цифрам предшествует пробел.

%l Заменяется на значение часа (12-часовое представление) в виде десятичного числа (1 .. 12); одиночным цифрам предшествует пробел.

%M Заменяется на значение минут (00 .. 59).

%m Заменяется на значение месяца (01 .. 12).

%n Заменяется на перевод строки.

%O* То же самое, что и %E*.

%p Заменяется национальным представлением либо "ante meridiem" (AM), либо "post meridiem" (PM), в зависимости от обстоятельств.

%R Эквивалент "%H:%M".

%r Эквивалент "%I:%M:%S %p".

%S Заменяется значением секунд (00 .. 60).

%s Заменяется на значение секунд, прошедших от Epoch, UTC (см. описание mktime).

%T Эквивалент "%H:%M:%S".

%t Заменяется символом табуляции.

%U Заменяется на номер недели года (воскресенье Sunday как первый день недели) в виде десятичного числа (00 .. 53).

%u Заменяется днем недели (понедельник Monday как первый день недели) в виде десятичного числа (1 .. 7).

%V Заменяется на номер недели года понедельник Monday как первый день недели) в виде десятичного числа (01 .. 53). Если неделя, содержащая 1 января, имеет 4 или большее количество дней в новом году, то эта неделя 1; иначе это последняя неделя предыдущего года, и следующая неделя будет неделей 1.

%v Эквивалент "%e-%b-%Y".

%W Заменяется на номер недели кода (понедельник Monday как первый день недели) в виде десятичного числа (00 .. 53).

%w Заменяется на день недели (воскресенье Sunday как первый день недели) в виде десятичного числа (0 .. 6).

%X Заменяется на национальное представление времени.

%x Заменяется на национальное представление даты.

%Y Заменяется на год с сотнями лет в виде десятичного числа из 4 цифр.

%y Заменяется на год без указания сотен лет в виде десятичного числа (00 .. 99).

%Z Заменяется на имя зоны времени.

%z Заменяется на смещение зоны времени от UTC; начальный знак плюса обозначает смещение на восток от UTC, а минус на запад от UTC. Значения часов и минут далее представляются 2 цифрами каждое, без разделителя между ними (общая форма для заголовков даты RFC, RFC 822 date headers).

%+ Заменяется национальным представлением даты и времени (формат подобен тому, как его генерирует date).

%-* Расширение GNU libc. Не делать никакое дополнение слева, когда выполняется любой числовой вывод.

%_* Расширение GNU libc. Явно указывает пробел для дополнения слева.

%0* Расширение GNU libc. Явно указывает 0 для дополнения слева.

%% Заменяет символ процента (%). 

#include < sys/time.h>

int adjtime (const struct timeval *delta, struct timeval *olddelta);

Корректирует время, чтобы позволить синхронизацию системных часов.

Системный вызов, выполняющий небольшую коррекцию системного времени, которое возвращается из gettimeofday(). Происходит замедление или ускорение времени, как это указано параметром timeval *delta. Если *delta отрицательное, то часы замедляются путем более медленного инкремента, пока не завершится коррекция. Если *delta положительное, то нормально используется увеличенный инкремент. Шаг, используемый для коррекции, обычно составляет доли процента. Таким образом, время всегда является монотонно возрастающей функцией. Коррекция времени из предыдущего вызова adjtime() не может быть завешено, когда adjtime() была вызвана повторно. Если olddelta не NULL, то структура, на которую указывает olddelta, на выходе будет заполнена количеством микросекунд, которое предстоит поправить из предыдущего вызова.

Функция adjtime может использоваться серверами времени, которые синхронизируют часы компьютеров локальной сети. Такие серверы времени замедляли бы часы одних машин и ускоряли бы часы других, чтобы подкорректировать их до среднего сетевого времени.

Чтобы остановить уточненную подстройку времени и напрямую установить текущее время, используйте POSIX-функцию settimeofday().

Если вам нужно получить время с точностью 1 секунда, то используйте такой кусок кода:

time_t now;
char strftime_buf[64];
struct tm timeinfo;
 
time(&now);
// Установка зоны времени на China Standard Time:
setenv("TZ", "CST-8", 1);
tzset();
 
localtime_r(&now, &timeinfo);
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
ESP_LOGI(TAG, "Текущие дата/время в Шанхае: %s", strftime_buf);

Если надо получить время с разрешающей способностью 1 мкс:

struct timeval tv_now;
gettimeofday(&tv_now, NULL);
int64_t time_us = (int64_t)tv_now.tv_sec * 1000000L + (int64_t)tv_now.tv_usec;

[Синхронизация времени SNTP]

Установить текущее время вы можете POSIX-функциями settimeofday() и adjtime(). Они используются внутри библиотеки lwIP SNTP для установки текущего времени, когда принят ответ от сервера NTP. Эти функции можно также использовать отдельно от библиотеки lwIP SNTP.

Некоторые API-функции lwIP, включая функции SNTP, не являются потокобезопасными (not thread safe), так что рекомендуется использовать компонент esp_netif для взаимодействия с модулем SNTP.

Для инициализации определенного сервера SNTP, а также запуска службы SNTP, просто создайте конфигурацию сервера SNTP по умолчанию с определенным именем сервера, затем вызовите esp_netif_sntp_init() для регистрации сервера и запуска службы SNTP.

esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG("pool.ntp.org");
esp_netif_sntp_init(&config);

Этот код автоматически выполняет синхронизацию времени при получении ответа от сервера SNTP. Иногда полезно ждать, пока не произйдет синхронизация времени, для этой цели можно использовать esp_netif_sntp_sync_wait():

if (esp_netif_sntp_sync_wait(pdMS_TO_TICKS(10000)) != ESP_OK) {
   printf("Failed to update system time within 10s timeout");
}

Чтобы сконфигурировать несколько серверов NTP (или использовать более продвинутые настройки, такие как DHCP от серверов NTP), см. подробное описание SNTP API в документации на библиотеку esp_netif [3].

Библиотека lwIP SNTP может работать в одном из следующих режимов синхронизации:

SNTP_SYNC_MODE_IMMED (по умолчанию): обновит системное время сразу после приема ответа от сервера SNTP, после использования settimeofday().

SNTP_SYNC_MODE_SMOOTH: обновляет время плавно путем постепенной коррекции ошибки отсчета времени с помощью функции adjtime(). Если разница между ответом SNTP и системным временем будет больше 35 минут, то системное время обновится немедленно с помощью вызова settimeofday().

Если вы хотите выбрать режим SNTP_SYNC_MODE_SMOOTH, то установите поле esp_sntp_config::smooth в true структуры конфигурации SNTP. Иначе (и по умолчанию) будет использоваться режим SNTP_SYNC_MODE_IMMED.

Для настройки callback-функции, которая будет вызвана в момент, когда произошла синхронизация времени, используйте поле esp_sntp_config::sync_cb в структуре конфигурации SNTP.

Приложение с этим кодом инициализации будет периодически синхронизировать время. Период синхронизации определяется опцией CONFIG_LWIP_SNTP_UPDATE_DELAY (значение по умолчанию 1 час). Для изменения установите макрос CONFIG_LWIP_SNTP_UPDATE_DELAY в конфигурации проекта (idf.py menuconfig → Component config → LWIP → SNTP → Request interval to update time (ms)).

В директории protocols/sntp среды разработки ESP-IDF предоставлен пример кода, демонстрирующий реализацию синхронизацию времени на основе библиотеки lwIP SNTP.

Обратите внимание, что также можно напрямую использовать lwIP API, однако надо позаботиться о безопасности потоков (thread safety). Список thread-safe API-функций, предоставленных разработчиками ESP-IDF:

• sntp_set_time_sync_notification_cb() может использоваться для установки callback-функции, которая будет оповещать о процессе синхронизации времени.

• sntp_get_sync_status() и sntp_set_sync_status() могут использоваться для получения/установки состояния синхронизации времени.

• sntp_set_sync_mode() может использоваться для установки режима синхронизации.

• esp_sntp_setoperatingmode() установит предпочтительный режим работы. :cpp:enumerator:ESP_SNTP_OPMODE_POLL и esp_sntp_init() инициализируют модуль SNTP.

• esp_sntp_setservername() конфигурирует сервер SNTP.

[Зоны времени]

Чтобы установить локальную зону времени, используйте следующие POSIX-функции:

1. Вызовите setenv(), чтобы установить переменную окружения TZ на корректное значение, основываясь на текущем месте расположения устройства. Формат строки времени такой же, как описан в документации GNU libc (хотя реализация отличается).

2. Вызовите tzset() для обновления runtime-данных библиотеки C на новую timezone.

После выполнения этих шагов вызывайте стандартную библиотечную C-функцию localtime(), и она возвратит корректное локальное время с учетом смещения зоны времени (timezone offset) и летнего времени (daylight saving time).

[2036 и 2038]

SNTP/NTP 2036 Overflow. Метки времени SNTP/NTP представлены как 64-разрядные числа без знака с фиксированной запятой, где первые 32 бита представляют целую часть, и последние 32 бита дробную часть. 64-разрядное число без знака с фиксированной точкой представляет количество секунд, прошедшее от 00:00 момента времени 1 января 1900 года, так что время SNTP/NTP переполнится в 2036 году.

Чтобы разобраться с этой проблемой, новым соглашением было сделано расширение времени жизни меток времени SNTP/NTP. При это используется MSB (по соглашению бит 0) целой части, чтобы показать интервалы времени между годами 1968 .. 2104 (подробности см. в RFC2030 [4]). Это соглашение реализовано в библиотечном модуле lwIP SNTP. Таким образом функции ESP-IDF, относящиеся к SNTP, будут работать до 2104 года.

Unix Time 2038 Overflow. Время Unix (тип time_t) был ранее представлен 32-битным числом со знаком, что приведет к переполнению в 2038 году (так называемая проблема Y2K38). Чтобы пофиксить Y2K38, начиная с ESP-IDF версии v5.0 используется 64-разрядное число со знаком, чтобы представить time_t, это откладывает переполнение time_t на следующие 292 миллиард лет.

[Справочник по API системного времени]

Ниже приведен краткий справочник по API-функциям системного времени ESP-IDF. Подробную информацию по параметрам всех этих функций, типам данных и константам см. в документации [1].

Заголовочный файл: components/lwip/include/apps/esp_sntp.h.

sntp_sync_time Обновит системное время, в качестве параметра она принимает указатель на значение переменной timeval (время, полученное от сервера SNMP). Это weak-линкуемая функция. Можно заменить весь относящийся к SNTP функционал, применив собственную реализацию функции sntp_sync_time() в приложении firmware. Если используется реализация по умолчанию, то вызов sntp_set_sync_mode() позволит изменить режим синхронизации на немедленный (instant) или плавный (smooth). Если зарегистрирована callback-функция через sntp_set_time_sync_notification_cb(), то она будет вызвана при последующей синхронизации времени.

Функция Описание
sntp_sync_time Обновит системное время, в качестве параметра она принимает указатель на значение переменной timeval (время, полученное от сервера SNMP). Это weak-линкуемая функция. Можно заменить весь относящийся к SNTP функционал, применив собственную реализацию функции sntp_sync_time() в приложении firmware. Если используется реализация по умолчанию, то вызов sntp_set_sync_mode() позволит изменить режим синхронизации на немедленный (instant) или плавный (smooth). Если зарегистрирована callback-функция через sntp_set_time_sync_notification_cb(), то она будет вызвана при последующей синхронизации времени.
sntp_set_sync_mode Установит режим синхронизации времени на немедленный (SNTP_SYNC_MODE_IMMED) или (SNTP_SYNC_MODE_SMOOTH).
sntp_get_sync_mode Позволяет узнать, какой используется текущий режим синхронизации времени (SNTP_SYNC_MODE_IMMED или SNTP_SYNC_MODE_SMOOTH).
sntp_get_sync_status Позволяет узнать состояние синхронизации времени. После того, как обновление было завершено, будет возвращено состояние SNTP_SYNC_STATUS_COMPLETED. После этого состояние будет сброшено на SNTP_SYNC_STATUS_RESET. Если операция обновления еще не завершена, то состояние будет SNTP_SYNC_STATUS_RESET. Если был выбран плавный режим синхронизации (SNTP_SYNC_MODE_SMOOTH) и синхронизация все еще продолжается (работает adjtime), то будет возвращено состояние SNTP_SYNC_STATUS_IN_PROGRESS.
sntp_set_sync_status Установит состояние синхронизации. Входной параметр: одно из значений SNTP_SYNC_STATUS_RESET, SNTP_SYNC_STATUS_COMPLETED или SNTP_SYNC_STATUS_IN_PROGRESS.
sntp_set_time_sync_notification_cb Установит callback-функцию для оповещения о процессе синхронизации времени.
sntp_set_sync_interval Установит интервал операции синхронизации SNTP. Замечание: SNTPv4 RFC 4330 принудительно устанавливает минимальный интервал синхронизации 15 секунд. Параметр этой функции принимает значение в миллисекундах, и оно не может быть меньше 15 секунд (т. е. > 15000, иначе будет установлен минимальный интервал времени 15 секунд). Этот интервал синхронизации будет использоваться на следующей попытке обновления времени через SNTP. Чтобы сразу применить новый интервал времени, вызовите sntp_restart(), иначе установленный интервал будет применен только после истечения ранее установленного интервала.
sntp_get_sync_interval Извлекает текущий интервал синхронизации SNTP.
sntp_restart Перезапустит SNTP.
esp_sntp_setoperatingmode Установит рабочий режим SNTP. Режим должен быть установлен перед инициализацией.
esp_sntp_init Инициализирует и запускает службу SNTP.
esp_sntp_stop Остановит службу SNTP.
esp_sntp_setserver Установит IP-адрес сервера SNTP.
esp_sntp_setservername Установит имя сервера SNTP.
esp_sntp_getservername Извлечет имя сервера SNTP.
esp_sntp_getserver Извлечет IP-адрес сервера SNTP.
esp_sntp_enabled Проверяет, разрешен ли sntp.

Если была сборка без lwip, то предоставляются статические inline-функции, при этом будет выдаваться предупреждение о thread safety:

static inline void sntp_setoperatingmode(u8_t operating_mode)

static inline void sntp_servermode_dhcp(int set_servers_from_dhcp)

static inline void sntp_setservername(u8_t idx, const char *server)

static inline void sntp_init(void)

static inline const char *sntp_getservername(u8_t idx)

static inline const ip_addr_t *sntp_getserver(u8_t idx) 

[Ссылки]

1. ESP32-C3 System Time site:espressif.com.
2. ESP32-C3 Hardware Design Guidelines site:espressif.com.
3. ESP-NETIF.
4. Request for Comments 2030 site:rfc-editor.org.
5ESP-IDF: получение времени компиляции проекта в формате Unix.

 

Добавить комментарий


Защитный код
Обновить

Top of Page