Skip to content

GyverLibs/GyverNTP

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

51 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

latest PIO Foo Foo Foo

Foo

GyverNTP

Библиотека для получения точного времени с NTP сервера для esp8266/esp32

  • Работает на стандартном интерфейсе Udp.h
  • Учёт времени ответа сервера и задержки соединения
  • Получение времени с точностью до миллисекунд
  • Интеграция с библиотекой Stamp для распаковки unix в часы, минуты итд
  • Автоматическая синхронизация
  • Поддержание хода времени на базе millis() между синхронизациями
  • Секундный таймер для удобства автоматизации
  • Обработка ошибок
  • Асинхронный режим
  • Поддержка внешнего RTC
  • Кеширование DNS, стабильная работа и возможность проверки наличия Интернет

Совместимость

Все платформы

Зависимости

Содержание

Инициализация

GyverNTP;                 // параметры по умолчанию (gmt 0, период 3600 секунд (1 час))
GyverNTP(gmt);            // часовой пояс в часах (например Москва 3)
GyverNTP(gmt, period);    // часовой пояс в часах и период обновления в секундах

Начиная с версии 2.1.0 доступен глобальный объект NTP, свой создавать не нужно

Использование

// установить часовой пояс в часах или минутах (глобально для Stamp)
void setGMT(int16_t gmt);

// установить период обновления в секундах
void setPeriod(uint16_t prd);

// включить асинхронный режим (по умолч. true)
void asyncMode(bool async);

// установить хост (умолч. "pool.ntp.org")
void setHost(const String& host);

// установить хост IP
void setHost(const IPAddress& host);

// установить порт (умолч. 123)
void setPort(uint16_t port);

// получить пинг NTP сервера, мс
int16_t ping();

// вернёт true при изменении статуса online
bool statusChanged();

// не учитывать пинг соединения (умолч. false)
void ignorePing(bool ignore);

// подключить RTC
void attachRTC(VirtualRTC& rtc);

// отключить RTC
void detachRTC();

// подключить обработчик ошибки
void onError(ErrorCallback cb);

// получить последнюю ошибку
Error getError();

// получить последнюю ошибку
const __FlashStringHelper* readError();

// есть ошибка
bool hasError();

// вернёт true, если tick ожидает ответа сервера в асинхронном режиме
bool busy();

// true - есть соединение с Интернет
bool online();

// запустить
bool begin();

// запустить с указанием часового пояса в часах или минутах (глобально для Stamp)
bool begin(int16_t gmt);

// выключить NTP
void end();

// синхронно обновить время с сервера. true при успехе
bool updateNow();

// тикер, вызывать в loop. Вернёт true каждую секунду, если синхронизирован. Синхронизируется по таймеру
bool tick();

Особенности

  • GyverNTP работает с WiFi UDP для esp8266/esp32, но может использоваться любой другой UDP клиент (см. ниже)
  • Существует глобальный объект NTP (как Serial, Wire И проч.)
  • Нужно вызывать tick() в главном цикле программы loop(), он синхронизирует время с сервера по своему таймеру и обеспечивает работу секундного таймера
  • Если основной цикл программы сильно загружен, а время нужно получать с максимальной точностью (несколько мс), то можно выключить асинхронный режим asyncMode(false)
  • Библиотека продолжает считать время после пропадания синхронизации. По моим тестам esp "уходит" на ~1.7 секунды за сутки, поэтому стандартный период синхронизации выбран 1 час
  • Наследуется класс StampKeeper, который обеспечивает счёт времени, работу секундного таймера и удобную конвертацию времени

Часовой пояс

Часовой пояс задаётся для всех операций со Stamp/Datime в программе! Установка часового пояса в объекте NTP равносильна вызову setStampZone() - установка глобального часового пояса для библиотеки Stamp:

void setup() {
    // подключить к WiFi
    NTP.begin(3); // запустить и указать часовой пояс
}

Минимальный пример

#include <GyverNTP.h>

void setup() {
    // подключить к WiFi
    NTP.begin(3); // запустить и указать часовой пояс
}

void loop() {
    NTP.tick();   // вызывать тикер в loop
}

Секундный таймер

Для удобства автоматизации событий по таймеру в библиотеку встроен секундный таймер, он срабатывает в 0 миллисекунд каждой секунды. По условию таймера NTP гарантированно синхронизирован и выдаёт корректное время:

void loop() {
  if (NTP.tick()) {
    // новая секунда!
    Serial.println(NTP.toString());
  }

  // или так
  // NTP.tick();
  // if (NTP.newSecond()) { }
}

Также можно подключить обработчик на секунду:

void newSecond() {
  // ваш код
}

void setup() {
  // подключить к WiFi
  NTP.begin(3); // запустить и указать часовой пояс
  NTP.onSecond(newSecond);

  // или так
  NTP.onSecond([](){
    // ваш код
  });
}

void loop() {
  NTP.tick();
}

Получение времени

GyverNTP наследует StampConvert, то есть получать время можно множеством способов:

// каждую секунду
if (NTP.tick()) {
  // вывод даты и времени строкой
  Serial.print(NTP.toString());  // NTP.timeToString(), NTP.dateToString()
  Serial.print(':');
  Serial.println(NTP.ms());  // + миллисекунды текущей секунды. Внутри tick всегда равно 0

  // вывод в Datime
  Datime dt = NTP;  // или Datime dt(NTP)
  dt.year;
  dt.second;
  dt.hour;
  dt.weekDay();
  dt.yearDay();
  // ... и прочие методы и переменные Datime

  // чтение напрямую, медленнее чем вывод в Datime
  NTP.second();
  NTP.minute();
  NTP.year();
  // ... и прочие методы StampConvert

  // сравнение
  NTP == DaySeconds(12, 35, 0);            // сравнение с DaySeconds (время равно 12:35:00)
  NTP == 1738237474;                       // сравнение с unix
  NTP == Datime(2025, 1, 30, 14, 14, 30);  // сравнение с Datime
}

Режим синхронизации

GyverNTP может использоваться и без тикера - нужно вручную вызвать updateNow для синхронизации времени. В этом случае время будет считаться просто с момента последней синхронизации, обработчик секунд не будет работать:

void setup() {
  // подключить к WiFi
  NTP.begin(3);     // запустить и указать часовой пояс
  NTP.updateNow();  // синхронизировать
}

void loop() {
  Serial.println(NTP.toString());
  delay(1000);
}

Рассинхронизация

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

  • Если внутренние часы "спешат" - секундный таймер перестанет срабатывать, пока реальное время не догонит внутреннее
  • Если внутренние часы "отстают" - таймер будет вызываться каждую итерацию loop с прибавлением времени, пока внутреннее время не догонит реальное

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

Проверка онлайна

NTP работает по UDP - очень легковесному и "дешёвому" протоколу связи, обращение к серверу практически не занимает времени. Благодаря этому NTP можно использовать для проверки связи с Интернет - там, где стандартный TCP клиент зависнет на несколько секунд, NTP асинхронно сообщит о потере связи. В рамках GyverNTP это можно использовать так:

void setup() {
  // подключить к WiFi
  NTP.begin(3);
  NTP.setPeriod(5); // синхронизация каждые 5 секунд
}

void loop() {
  NTP.tick();

  // вернёт true при смене статуса
  if (NTP.statusChanged()) {
    Serial.println(NTP.online());
    // здесь флаг online можно использовать для передачи в другие библиотеки
    // например FastBot2
    // bot.setOnline(NTP.online());
  }
}

Подключение RTC

Библиотека поддерживает подключение внешнего RTC для синхронизации времени:

  • Если пришло время синхронизироваться с NTP, но возникла ошибка - будет синхронизировано время с RTC
  • Если успешно синхронизирован с NTP, то в RTC также будет записано актуальное время (не чаще чем GNTP_RTC_WRITE_PERIOD, по умолч. 1 час)

Объект RTC должен являться экземпляром класса VirtualRTC, из готовых например GyverDS3231Min. Можно написать и свой класс для подключения любого источника реального времени.

Пример

Полное демо

#include <Arduino.h>
#include <GyverNTP.h>

void setup() {
    Serial.begin(115200);
    WiFi.begin("WIFI_SSID", "WIFI_PASS");
    while (WiFi.status() != WL_CONNECTED) delay(100);
    Serial.println("Connected");

    // обработчик ошибок
    NTP.onError([]() {
        Serial.println(NTP.readError());
        Serial.print("online: ");
        Serial.println(NTP.online());
    });

    // обработчик секунды (вызывается из тикера)
    NTP.onSecond([]() {
        Serial.println("new second!");
    });

    // обработчик синхронизации (вызывается из sync)
    // NTP.onSync([](uint32_t unix) {
    //     Serial.println("sync: ");
    //     Serial.print(unix);
    // });

    NTP.begin(3);                           // запустить и указать часовой пояс
    // NTP.setPeriod(30);                   // период синхронизации в секундах
    // NTP.setHost("ntp1.stratum2.ru");     // установить другой хост
    // NTP.setHost(IPAddress(1, 2, 3, 4));  // установить другой хост
    // NTP.asyncMode(false);                // выключить асинхронный режим
    // NTP.ignorePing(true);                // не учитывать пинг до сервера
    // NTP.updateNow();                     // обновить прямо сейчас
}

void loop() {
    // тикер вернёт true каждую секунду в 0 мс секунды, если время синхронизировано
    if (NTP.tick()) {
        // вывод даты и времени строкой
        Serial.print(NTP.toString());  // NTP.timeToString(), NTP.dateToString()
        Serial.print(':');
        Serial.println(NTP.ms());  // + миллисекунды текущей секунды. Внутри tick всегда равно 0
    }

    if (NTP.newSecond()) {
        // новую секунду можно поймать и здесь
    }

    // изменился онлайн-статус
    if (NTP.statusChanged()) {
        Serial.print("STATUS: ");
        Serial.println(NTP.online());
    }
}

RTC

#include <Arduino.h>
#include <GyverNTP.h>

// GyverDS3231 поддерживает работу с GyverNTP
#include <GyverDS3231Min.h>
GyverDS3231Min rtc;

// можно написать свой класс и использовать любой другой RTC
class RTC : public VirtualRTC {
   public:
    void setUnix(uint32_t unix) {
        Serial.print("SET RTC: ");
        Serial.println(unix);
    }
    uint32_t getUnix() {
        return 1738015299ul;
    }
};
RTC vrtc;

void setup() {
    Serial.begin(115200);
    WiFi.begin("WIFI_SSID", "WIFI_PASS");
    // while (WiFi.status() != WL_CONNECTED) delay(100);
    Serial.println("Connected");

    // GyverDS3231
    Wire.begin();
    rtc.begin();

    NTP.begin(3);  // запустить и указать часовой пояс

    // подключить RTC
    // NTP.attachRTC(vrtc);
    NTP.attachRTC(rtc);
}

void loop() {
    if (NTP.tick()) {
        Serial.println(NTP.toString());
    }
}

Версии

  • v1.0
  • v1.1 - мелкие улучшения и gmt в минутах
  • v1.2 - оптимизация, улучшена стабильность, добавлен асинхронный режим
  • v1.2.1 - изменён стандартный период обновления
  • v1.3 - ускорена синхронизация при запуске в асинхронном режиме
  • v1.3.1 - заинклудил WiFi библиотеку в файл
  • v2.0 - добавлена зависимость от Stamp, больше возможностей, проверка онлайна для других библиотек
  • v2.1 - добавлен глобальный объект NTP
  • v2.2.0 - более стабильная работа, новые возможности

Установка

  • Библиотеку можно найти по названию GyverNTP и установить через менеджер библиотек в:
    • Arduino IDE
    • Arduino IDE v2
    • PlatformIO
  • Скачать библиотеку .zip архивом для ручной установки:
    • Распаковать и положить в C:\Program Files (x86)\Arduino\libraries (Windows x64)
    • Распаковать и положить в C:\Program Files\Arduino\libraries (Windows x32)
    • Распаковать и положить в Документы/Arduino/libraries/
    • (Arduino IDE) автоматическая установка из .zip: Скетч/Подключить библиотеку/Добавить .ZIP библиотеку… и указать скачанный архив
  • Читай более подробную инструкцию по установке библиотек здесь

Обновление

  • Рекомендую всегда обновлять библиотеку: в новых версиях исправляются ошибки и баги, а также проводится оптимизация и добавляются новые фичи
  • Через менеджер библиотек IDE: найти библиотеку как при установке и нажать "Обновить"
  • Вручную: удалить папку со старой версией, а затем положить на её место новую. "Замену" делать нельзя: иногда в новых версиях удаляются файлы, которые останутся при замене и могут привести к ошибкам!

Баги и обратная связь

При нахождении багов создавайте Issue, а лучше сразу пишите на почту alex@alexgyver.ru
Библиотека открыта для доработки и ваших Pull Request'ов!

При сообщении о багах или некорректной работе библиотеки нужно обязательно указывать:

  • Версия библиотеки
  • Какой используется МК
  • Версия SDK (для ESP)
  • Версия Arduino IDE
  • Корректно ли работают ли встроенные примеры, в которых используются функции и конструкции, приводящие к багу в вашем коде
  • Какой код загружался, какая работа от него ожидалась и как он работает в реальности
  • В идеале приложить минимальный код, в котором наблюдается баг. Не полотно из тысячи строк, а минимальный код

About

Библиотека для получения точного времени с NTP сервера для esp8266/esp32

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors