├── .gitattributes ├── .github └── workflows │ └── tg-send.yml ├── LICENSE ├── README.md ├── README_EN.md ├── examples ├── customClient │ └── customClient.ino ├── demo │ └── demo.ino ├── rtc │ └── rtc.ino └── testBlink │ └── testBlink.ino ├── keywords.txt ├── library.properties └── src ├── GyverNTP.cpp ├── GyverNTP.h ├── GyverNTPClient.h └── requestNTP.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/tg-send.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Telegram Message 3 | on: 4 | release: 5 | types: [published] 6 | jobs: 7 | build: 8 | name: Send Message 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: send telegram message on push 12 | uses: appleboy/telegram-action@master 13 | with: 14 | to: ${{ secrets.TELEGRAM_TO }} 15 | token: ${{ secrets.TELEGRAM_TOKEN }} 16 | disable_web_page_preview: true 17 | message: | 18 | ${{ github.event.repository.name }} v${{ github.event.release.tag_name }} 19 | ${{ github.event.release.body }} 20 | https://github.com/${{ github.repository }} 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 AlexGyver 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![latest](https://img.shields.io/github/v/release/GyverLibs/GyverNTP.svg?color=brightgreen)](https://github.com/GyverLibs/GyverNTP/releases/latest/download/GyverNTP.zip) 2 | [![PIO](https://badges.registry.platformio.org/packages/gyverlibs/library/GyverNTP.svg)](https://registry.platformio.org/libraries/gyverlibs/GyverNTP) 3 | [![Foo](https://img.shields.io/badge/Website-AlexGyver.ru-blue.svg?style=flat-square)](https://alexgyver.ru/) 4 | [![Foo](https://img.shields.io/badge/%E2%82%BD%24%E2%82%AC%20%D0%9F%D0%BE%D0%B4%D0%B4%D0%B5%D1%80%D0%B6%D0%B0%D1%82%D1%8C-%D0%B0%D0%B2%D1%82%D0%BE%D1%80%D0%B0-orange.svg?style=flat-square)](https://alexgyver.ru/support_alex/) 5 | [![Foo](https://img.shields.io/badge/README-ENGLISH-blueviolet.svg?style=flat-square)](https://github-com.translate.goog/GyverLibs/GyverNTP?_x_tr_sl=ru&_x_tr_tl=en) 6 | 7 | [![Foo](https://img.shields.io/badge/ПОДПИСАТЬСЯ-НА%20ОБНОВЛЕНИЯ-brightgreen.svg?style=social&logo=telegram&color=blue)](https://t.me/GyverLibs) 8 | 9 | # GyverNTP 10 | Библиотека для получения точного времени с NTP сервера для esp8266/esp32 11 | - Работает на стандартном интерфейсе Udp.h 12 | - Учёт времени ответа сервера и задержки соединения 13 | - Получение времени с точностью до миллисекунд 14 | - Интеграция с библиотекой [Stamp](https://github.com/GyverLibs/Stamp) для распаковки unix в часы, минуты итд 15 | - Автоматическая синхронизация 16 | - Поддержание хода времени на базе millis() между синхронизациями 17 | - Секундный таймер для удобства автоматизации 18 | - Обработка ошибок 19 | - Асинхронный режим 20 | - Поддержка внешнего RTC 21 | - Кеширование DNS, стабильная работа и возможность проверки наличия Интернет 22 | 23 | ### Совместимость 24 | Все платформы 25 | 26 | ### Зависимости 27 | - [Stamp](https://github.com/GyverLibs/Stamp) v1.4.0+ 28 | 29 | ## Содержание 30 | - [Инициализация](#init) 31 | - [Использование](#usage) 32 | - [Пример](#example) 33 | - [Версии](#versions) 34 | - [Установка](#install) 35 | - [Баги и обратная связь](#feedback) 36 | 37 | 38 | 39 | ## Инициализация 40 | ```cpp 41 | GyverNTP; // параметры по умолчанию (gmt 0, период 3600 секунд (1 час)) 42 | GyverNTP(gmt); // часовой пояс в часах (например Москва 3) 43 | GyverNTP(gmt, period); // часовой пояс в часах и период обновления в секундах 44 | ``` 45 | 46 | > Начиная с версии 2.1.0 доступен глобальный объект `NTP`, свой создавать не нужно 47 | 48 | 49 | 50 | ## Использование 51 | ```cpp 52 | // установить часовой пояс в часах или минутах (глобально для Stamp) 53 | void setGMT(int16_t gmt); 54 | 55 | // установить период обновления в секундах 56 | void setPeriod(uint16_t prd); 57 | 58 | // включить асинхронный режим (по умолч. true) 59 | void asyncMode(bool async); 60 | 61 | // установить хост (умолч. "pool.ntp.org") 62 | void setHost(const String& host); 63 | 64 | // установить хост IP 65 | void setHost(const IPAddress& host); 66 | 67 | // установить порт (умолч. 123) 68 | void setPort(uint16_t port); 69 | 70 | // получить пинг NTP сервера, мс 71 | int16_t ping(); 72 | 73 | // вернёт true при изменении статуса online 74 | bool statusChanged(); 75 | 76 | // не учитывать пинг соединения (умолч. false) 77 | void ignorePing(bool ignore) { 78 | _usePing = ignore; 79 | } 80 | 81 | // подключить RTC 82 | void attachRTC(VirtualRTC& rtc); 83 | 84 | // отключить RTC 85 | void detachRTC(); 86 | 87 | // подключить обработчик ошибки 88 | void onError(ErrorCallback cb); 89 | 90 | // получить последнюю ошибку 91 | Error getError(); 92 | 93 | // получить последнюю ошибку 94 | const __FlashStringHelper* readError(); 95 | 96 | // есть ошибка 97 | bool hasError(); 98 | 99 | // вернёт true, если tick ожидает ответа сервера в асинхронном режиме 100 | bool busy(); 101 | 102 | // true - есть соединение с Интернет 103 | bool online(); 104 | 105 | // запустить 106 | bool begin(); 107 | 108 | // запустить с указанием часового пояса в часах или минутах (глобально для Stamp) 109 | bool begin(int16_t gmt); 110 | 111 | // выключить NTP 112 | void end(); 113 | 114 | // синхронно обновить время с сервера. true при успехе 115 | bool updateNow(); 116 | 117 | // тикер, вызывать в loop. Вернёт true каждую секунду, если синхронизирован. Синхронизируется по таймеру 118 | bool tick(); 119 | ``` 120 | 121 | ## Особенности 122 | - GyverNTP работает с WiFi UDP для esp8266/esp32, но может использоваться любой другой UDP клиент (см. ниже) 123 | - Существует глобальный объект `NTP` (как `Serial`, `Wire` И проч.) 124 | - Нужно вызывать `tick()` в главном цикле программы `loop()`, он синхронизирует время с сервера по своему таймеру и обеспечивает работу секундного таймера 125 | - Если основной цикл программы сильно загружен, а время нужно получать с максимальной точностью (несколько мс), то можно выключить асинхронный режим `asyncMode(false)` 126 | - Библиотека продолжает считать время после пропадания синхронизации. По моим тестам esp "уходит" на ~1.7 секунды за сутки, поэтому стандартный период синхронизации выбран 1 час 127 | - Наследуется класс `StampKeeper`, который обеспечивает счёт времени, работу секундного таймера и удобную конвертацию времени 128 | 129 | ### Часовой пояс 130 | Часовой пояс задаётся для всех операций со Stamp/Datime в программе! Установка часового пояса в объекте NTP равносильна вызову `setStampZone()` - установка **глобального** часового пояса для библиотеки Stamp: 131 | 132 | ```cpp 133 | void setup() { 134 | // подключить к WiFi 135 | NTP.begin(3); // запустить и указать часовой пояс 136 | } 137 | ``` 138 | 139 | ### Минимальный пример 140 | ```cpp 141 | #include 142 | 143 | void setup() { 144 | // подключить к WiFi 145 | NTP.begin(3); // запустить и указать часовой пояс 146 | } 147 | 148 | void loop() { 149 | NTP.tick(); // вызывать тикер в loop 150 | } 151 | ``` 152 | 153 | ### Секундный таймер 154 | Для удобства автоматизации событий по таймеру в библиотеку встроен секундный таймер, он срабатывает в 0 миллисекунд каждой секунды. По условию таймера NTP гарантированно синхронизирован и выдаёт корректное время: 155 | 156 | ```cpp 157 | void loop() { 158 | if (NTP.tick()) { 159 | // новая секунда! 160 | Serial.println(NTP.toString()); 161 | } 162 | 163 | // или так 164 | // NTP.tick(); 165 | // if (NTP.newSecond()) { } 166 | } 167 | ``` 168 | 169 | Также можно подключить обработчик на секунду: 170 | ```cpp 171 | void newSecond() { 172 | // ваш код 173 | } 174 | 175 | void setup() { 176 | // подключить к WiFi 177 | NTP.begin(3); // запустить и указать часовой пояс 178 | NTP.onSecond(newSecond); 179 | 180 | // или так 181 | NTP.onSecond([](){ 182 | // ваш код 183 | }); 184 | } 185 | 186 | void loop() { 187 | NTP.tick(); 188 | } 189 | ``` 190 | 191 | ### Получение времени 192 | GyverNTP наследует [StampConvert](https://github.com/GyverLibs/Stamp), то есть получать время можно множеством способов: 193 | 194 | ```cpp 195 | // каждую секунду 196 | if (NTP.tick()) { 197 | // вывод даты и времени строкой 198 | Serial.print(NTP.toString()); // NTP.timeToString(), NTP.dateToString() 199 | Serial.print(':'); 200 | Serial.println(NTP.ms()); // + миллисекунды текущей секунды. Внутри tick всегда равно 0 201 | 202 | // вывод в Datime 203 | Datime dt = NTP; // или Datime dt(NTP) 204 | dt.year; 205 | dt.second; 206 | dt.hour; 207 | dt.weekDay; 208 | dt.yearDay; 209 | // ... и прочие методы и переменные Datime 210 | 211 | // чтение напрямую, медленнее чем вывод в Datime 212 | NTP.second(); 213 | NTP.minute(); 214 | NTP.year(); 215 | // ... и прочие методы StampConvert 216 | 217 | // сравнение 218 | NTP == DaySeconds(12, 35, 0); // сравнение с DaySeconds (время равно 12:35:00) 219 | NTP == 1738237474; // сравнение с unix 220 | NTP == Datime(2025, 1, 30, 14, 14, 30); // сравнение с Datime 221 | } 222 | ``` 223 | 224 | ### Режим синхронизации 225 | GyverNTP может использоваться и **без тикера** - нужно вручную вызвать `updateNow` для синхронизации времени. В этом случае время будет считаться просто с момента последней синхронизации, обработчик секунд не будет работать: 226 | 227 | ```cpp 228 | void setup() { 229 | // подключить к WiFi 230 | NTP.begin(3); // запустить и указать часовой пояс 231 | NTP.updateNow(); // синхронизировать 232 | } 233 | 234 | void loop() { 235 | Serial.println(NTP.toString()); 236 | delay(1000); 237 | } 238 | ``` 239 | 240 | ### Рассинхронизация 241 | Если период синхронизации очень большой или в системе надолго пропадает связь, часы рассинхронизируются и будут синхронизированы при следующем обращении к серверу. Если время ушло больше, чем на 1 секунду, то поведение будет следующим: 242 | - Если внутренние часы "спешат" - секундный таймер перестанет срабатывать, пока реальное время не догонит внутреннее 243 | - Если внутренние часы "отстают" - таймер будет вызываться каждую итерацию loop с прибавлением времени, пока внутреннее время не догонит реальное 244 | 245 | Это сделано для того, чтобы при синхронизации не потерялись секунды - библиотека обработает каждую секунду и не будет повторяться, что очень важно для алгоритмов автоматизации. 246 | 247 | ### Проверка онлайна 248 | NTP работает по UDP - очень легковесному и "дешёвому" протоколу связи, обращение к серверу практически не занимает времени. Благодаря этому NTP можно использовать для проверки связи с Интернет - там, где стандартный TCP клиент зависнет на несколько секунд, NTP асинхронно сообщит о потере связи. В рамках GyverNTP это можно использовать так: 249 | ```cpp 250 | void setup() { 251 | // подключить к WiFi 252 | NTP.begin(3); 253 | NTP.setPeriod(5); // синхронизация каждые 5 секунд 254 | } 255 | 256 | void loop() { 257 | NTP.tick(); 258 | 259 | // вернёт true при смене статуса 260 | if (NTP.statusChanged()) { 261 | Serial.println(NTP.online()); 262 | // здесь флаг online можно использовать для передачи в другие библиотеки 263 | // например FastBot2 264 | // bot.setOnline(NTP.online()); 265 | } 266 | } 267 | ``` 268 | 269 | ### Подключение RTC 270 | Библиотека поддерживает подключение внешнего RTC для синхронизации времени: 271 | - Если пришло время синхронизироваться с NTP, но возникла ошибка - будет синхронизировано время с RTC 272 | - Если успешно синхронизирован с NTP, то в RTC также будет записано актуальное время (не чаще чем `GNTP_RTC_WRITE_PERIOD`, по умолч. 1 час) 273 | 274 | Объект RTC должен являться экземпляром класса `VirtualRTC`, из готовых например `GyverDS3231Min`. Можно написать и свой класс для подключения любого источника реального времени. 275 | 276 | 277 | 278 | ## Пример 279 | ### Полное демо 280 | ```cpp 281 | #include 282 | #include 283 | 284 | void setup() { 285 | Serial.begin(115200); 286 | WiFi.begin("WIFI_SSID", "WIFI_PASS"); 287 | while (WiFi.status() != WL_CONNECTED) delay(100); 288 | Serial.println("Connected"); 289 | 290 | // обработчик ошибок 291 | NTP.onError([]() { 292 | Serial.println(NTP.readError()); 293 | Serial.print("online: "); 294 | Serial.println(NTP.online()); 295 | }); 296 | 297 | // обработчик секунды (вызывается из тикера) 298 | NTP.onSecond([]() { 299 | Serial.println("new second!"); 300 | }); 301 | 302 | // обработчик синхронизации (вызывается из sync) 303 | // NTP.onSync([](uint32_t unix) { 304 | // Serial.println("sync: "); 305 | // Serial.print(unix); 306 | // }); 307 | 308 | NTP.begin(3); // запустить и указать часовой пояс 309 | // NTP.setPeriod(30); // период синхронизации в секундах 310 | // NTP.setHost("ntp1.stratum2.ru"); // установить другой хост 311 | // NTP.setHost(IPAddress(1, 2, 3, 4)); // установить другой хост 312 | // NTP.asyncMode(false); // выключить асинхронный режим 313 | // NTP.ignorePing(true); // не учитывать пинг до сервера 314 | // NTP.updateNow(); // обновить прямо сейчас 315 | } 316 | 317 | void loop() { 318 | // тикер вернёт true каждую секунду в 0 мс секунды, если время синхронизировано 319 | if (NTP.tick()) { 320 | // вывод даты и времени строкой 321 | Serial.print(NTP.toString()); // NTP.timeToString(), NTP.dateToString() 322 | Serial.print(':'); 323 | Serial.println(NTP.ms()); // + миллисекунды текущей секунды. Внутри tick всегда равно 0 324 | 325 | // вывод в Datime 326 | Datime dt = NTP; // или Datime dt(NTP) 327 | dt.year; 328 | dt.second; 329 | dt.hour; 330 | dt.weekDay; 331 | dt.yearDay; 332 | // ... и прочие методы и переменные Datime 333 | 334 | // чтение напрямую, медленнее чем вывод в Datime 335 | NTP.second(); 336 | NTP.minute(); 337 | NTP.year(); 338 | // ... и прочие методы StampConvert 339 | 340 | // сравнение 341 | NTP == DaySeconds(12, 35, 0); // сравнение с DaySeconds (время равно 12:35:00) 342 | NTP == 1738237474; // сравнение с unix 343 | NTP == Datime(2025, 1, 30, 14, 14, 30); // сравнение с Datime 344 | } 345 | 346 | if (NTP.newSecond()) { 347 | // новую секунду можно поймать и здесь 348 | } 349 | 350 | // изменился онлайн-статус 351 | if (NTP.statusChanged()) { 352 | Serial.print("STATUS: "); 353 | Serial.println(NTP.online()); 354 | } 355 | } 356 | ``` 357 | 358 | ### RTC 359 | 360 | ```cpp 361 | #include 362 | #include 363 | 364 | // GyverDS3231 поддерживает работу с GyverNTP 365 | #include 366 | GyverDS3231Min rtc; 367 | 368 | // можно написать свой класс и использовать любой другой RTC 369 | class RTC : public VirtualRTC { 370 | public: 371 | void setUnix(uint32_t unix) { 372 | Serial.print("SET RTC: "); 373 | Serial.println(unix); 374 | } 375 | uint32_t getUnix() { 376 | return 1738015299ul; 377 | } 378 | }; 379 | RTC vrtc; 380 | 381 | void setup() { 382 | Serial.begin(115200); 383 | WiFi.begin("WIFI_SSID", "WIFI_PASS"); 384 | // while (WiFi.status() != WL_CONNECTED) delay(100); 385 | Serial.println("Connected"); 386 | 387 | // GyverDS3231 388 | Wire.begin(); 389 | rtc.begin(); 390 | 391 | NTP.begin(3); // запустить и указать часовой пояс 392 | 393 | // подключить RTC 394 | // NTP.attachRTC(vrtc); 395 | NTP.attachRTC(rtc); 396 | } 397 | 398 | void loop() { 399 | if (NTP.tick()) { 400 | Serial.println(NTP.toString()); 401 | } 402 | } 403 | ``` 404 | 405 | 406 | 407 | ## Версии 408 | - v1.0 409 | - v1.1 - мелкие улучшения и gmt в минутах 410 | - v1.2 - оптимизация, улучшена стабильность, добавлен асинхронный режим 411 | - v1.2.1 - изменён стандартный период обновления 412 | - v1.3 - ускорена синхронизация при запуске в асинхронном режиме 413 | - v1.3.1 - заинклудил WiFi библиотеку в файл 414 | - v2.0 - добавлена зависимость от Stamp, больше возможностей, проверка онлайна для других библиотек 415 | - v2.1 - добавлен глобальный объект NTP 416 | - v2.2.0 - более стабильная работа, новые возможности 417 | 418 | 419 | 420 | ## Установка 421 | - Библиотеку можно найти по названию **GyverNTP** и установить через менеджер библиотек в: 422 | - Arduino IDE 423 | - Arduino IDE v2 424 | - PlatformIO 425 | - [Скачать библиотеку](https://github.com/GyverLibs/GyverNTP/archive/refs/heads/main.zip) .zip архивом для ручной установки: 426 | - Распаковать и положить в *C:\Program Files (x86)\Arduino\libraries* (Windows x64) 427 | - Распаковать и положить в *C:\Program Files\Arduino\libraries* (Windows x32) 428 | - Распаковать и положить в *Документы/Arduino/libraries/* 429 | - (Arduino IDE) автоматическая установка из .zip: *Скетч/Подключить библиотеку/Добавить .ZIP библиотеку…* и указать скачанный архив 430 | - Читай более подробную инструкцию по установке библиотек [здесь](https://alexgyver.ru/arduino-first/#%D0%A3%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0_%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA) 431 | ### Обновление 432 | - Рекомендую всегда обновлять библиотеку: в новых версиях исправляются ошибки и баги, а также проводится оптимизация и добавляются новые фичи 433 | - Через менеджер библиотек IDE: найти библиотеку как при установке и нажать "Обновить" 434 | - Вручную: **удалить папку со старой версией**, а затем положить на её место новую. "Замену" делать нельзя: иногда в новых версиях удаляются файлы, которые останутся при замене и могут привести к ошибкам! 435 | 436 | 437 | 438 | ## Баги и обратная связь 439 | При нахождении багов создавайте **Issue**, а лучше сразу пишите на почту [alex@alexgyver.ru](mailto:alex@alexgyver.ru) 440 | Библиотека открыта для доработки и ваших **Pull Request**'ов! 441 | 442 | 443 | При сообщении о багах или некорректной работе библиотеки нужно обязательно указывать: 444 | - Версия библиотеки 445 | - Какой используется МК 446 | - Версия SDK (для ESP) 447 | - Версия Arduino IDE 448 | - Корректно ли работают ли встроенные примеры, в которых используются функции и конструкции, приводящие к багу в вашем коде 449 | - Какой код загружался, какая работа от него ожидалась и как он работает в реальности 450 | - В идеале приложить минимальный код, в котором наблюдается баг. Не полотно из тысячи строк, а минимальный код 451 | -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | This is an automatic translation, may be incorrect in some places. See sources and examples! 2 | 3 | # Gyverntp 4 | Library for receiving the exact time with the NTP server for ESP8266/ESP32 5 | - works at the standard library wifiudp.h 6 | - taking into account the time of the server response and the delay of the connection 7 | - Getting time up to several milliseconds 8 | - receipt of unix time, as well as milliseconds, seconds, minutes, hours, day, month, year and day of the week 9 | - Synchronization by timer 10 | - Error processing 11 | - Asynchronous regime 12 | 13 | ## compatibility 14 | ESP8266, ESP32 15 | 16 | ## Content 17 | - [installation] (# Install) 18 | - [initialization] (#init) 19 | - [use] (#usage) 20 | - [Example] (# Example) 21 | - [versions] (#varsions) 22 | - [bugs and feedback] (#fedback) 23 | 24 | 25 | ## Installation 26 | - The library can be found by the name ** gyverntp ** and installed through the library manager in: 27 | - Arduino ide 28 | - Arduino ide v2 29 | - Platformio 30 | - [download the library] (https://github.com/gyverlibs/gyverntp/archive/refs/heads/main.zip) .Zip archive for manual installation: 31 | - unpack and put in * C: \ Program Files (X86) \ Arduino \ Libraries * (Windows X64) 32 | - unpack and put in * C: \ Program Files \ Arduino \ Libraries * (Windows X32) 33 | - unpack and put in *documents/arduino/libraries/ * 34 | - (Arduino id) Automatic installation from. Zip: * sketch/connect the library/add .Zip library ... * and specify downloaded archive 35 | - Read more detailed instructions for installing libraries [here] (https://alexgyver.ru/arduino-first/#%D0%A3%D1%81%D1%82%D0%B0%BD%D0%BE%BE%BE%BED0%B2%D0%BA%D0%B0_%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA) 36 | ### Update 37 | - I recommend always updating the library: errors and bugs are corrected in the new versions, as well as optimization and new features are added 38 | - through the IDE library manager: find the library how to install and click "update" 39 | - Manually: ** remove the folder with the old version **, and then put a new one in its place.“Replacement” cannot be done: sometimes in new versions, files that remain when replacing are deleted and can lead to errors! 40 | 41 | 42 | 43 | ## initialization 44 | `` `CPP 45 | Gyverntp NTP;// default parameters (GMT 0, period 3600 seconds (1 hour)) 46 | Gyverntp NTP (GMT);// hourly belt in watches (for example, Moscow 3) 47 | Gyverntp NTP (GMT, Period);// Tax belt in the clock and the renewal period in seconds 48 | `` ` 49 | 50 | 51 | ## Usage 52 | `` `CPP 53 | Bool Begin ();// Launch 54 | VOID end ();// Stop 55 | 56 | VOID Setgmtminute (Int16_T GMT);// Install an hourly belt in minutes 57 | VOID Setgmt (Int8_T GMT);// Install an hourly waist in the watch 58 | VOID Setperiod (Uint16_T PRD);// set the update period in seconds 59 | VOID Sethost (Char* Host);// Install the host (by default "" Pool.ntp.org ") 60 | Void asyncmode (Bool F);// asynchronous regime (on the silence, turned on, true) 61 | VOID ignoreping (bool f);// Do not take into account the ping of the connection (silence. FALSE) 62 | 63 | uint8_t tick ();// Tiker, updates the time by its timer.Will return True if there is an attempt to update 64 | uint8_t updatatenow ();// Manually request and update the time from the server.Will return the status (see below) 65 | uint32_t unix ();// Unix Time 66 | 67 | uint16_t ms ();// milliseconds of the current second 68 | uint8_t second ();// Get second 69 | uint8_t minute ();// Get minutes 70 | uint8_t hor ();// Get a clock 71 | uint8_t day ();// Get the day of the month 72 | uint8_t month ();// Get a month 73 | uint16_t year ();// Get a year 74 | uint8_t Dayweek ();// Get the day of the week (Mon .. BC = 1 .. 7) 75 | 76 | String Timestring ();// Get a line of time of the format of hhch: mm: ss 77 | String Datestring ();// Get the line of the date of the format DD.MM.YYYY 78 | 79 | Bool Synced ();// Get the status of the current time, True - synchronized 80 | Bool Busy ();// will return True if Tick expects a server response in asynchronous mode 81 | int16_t ping ();// Get server ping 82 | uint8_t status ();// Get the status of the system 83 | 84 | // 0 - everything is ok 85 | // 1 - not launched UDP 86 | // 2 - not connected to wifi 87 | // 3 - error error to the server 88 | // 4 - error sending a package 89 | // 5 - server response timeout 90 | // 6 - the incorrect server response was obtained 91 | `` ` 92 | 93 | ### Peculiarities 94 | - We need to call `tick ()` in the main cycle of the `loop ()` program, it synchronizes the time according to its timer 95 | - if the main cycle of the program is strongly loaded, and the time must be obtained with maximum accuracy (several MS), then you can turn off the asynchronous mode `asyncmode (false)` ` 96 | - The library continues to count time even after the loss of synchronization 97 | - according to my tests, ESP "goes" for ~ 1.7 seconds per day (without synchronization).Therefore, the standard period of synchronization is chosen 1 hour 98 | 99 | 100 | ## Example 101 | `` `CPP 102 | // An example takes time every second 103 | // And also twice a second flashes LED 104 | // can be flashed on several boards - they will blink synchronously 105 | 106 | #include // ESP8266 107 | //#include // ESP32 108 | 109 | #include 110 | Gyverntp NTP (3); 111 | 112 | // List of servers if "Pool.ntp.org" does not work 113 | //"ontp1.stratum2.ru " 114 | //"ontp2.stratum2.ru " 115 | //"ontp.msk-ig.ru " 116 | 117 | VOID setup () { 118 | Pinmode (LED_BUILTIN, OUTPUT); 119 | Serial.Begin (115200); 120 | Wifi.begin ("wifi_ssid", "wifi_pass"); 121 | While (wifi.status ()! = Wl_ConNECTED) DELAY (100); 122 | Serial.println ("connected"); 123 | 124 | ntp.begin (); 125 | //NTP.ASYNCMODE(False);// turn off the asynchronous regime 126 | //ntp.ignoreping(true);// not to take into account the ping before the server 127 | } 128 | 129 | VOID loop () { 130 | ntp.tick (); 131 | 132 | if (ntp.ms () == 0) { 133 | DELAY (1); 134 | digitalWrite (LED_BUILTIN, 1); 135 | } 136 | if (ntp.ms () == 500) { 137 | DELAY (1); 138 | digitalWrite (LED_BUILTIN, 0); 139 | Serial.println (ntp.timestring ()); 140 | Serial.println (ntp.datestestring ()); 141 | Serial.println (); 142 | } 143 | } 144 | `` ` 145 | 146 | 147 | ## versions 148 | - V1.0 149 | - V1.1 - Small improvements and GMT in minutes 150 | - V1.2 - optimization, improved stability, added asynchronous regime 151 | - v1.2.1 - The standard renewal period has been changed 152 | - V1.3 - Synchronization is accelerated when launched in asynchronous mode 153 | - v1.3.1 - struck the WiFi library to the file 154 | 155 | 156 | ## bugs and feedback 157 | Create ** Issue ** when you find the bugs, and better immediately write to the mail [alex@alexgyver.ru] (mailto: alex@alexgyver.ru) 158 | The library is open for refinement and your ** pull Request ** 'ow! 159 | 160 | 161 | When reporting about bugs or incorrect work of the library, it is necessary to indicate: 162 | - The version of the library 163 | - What is MK used 164 | - SDK version (for ESP) 165 | - version of Arduino ide 166 | - whether the built -in examples work correctly, in which the functions and designs are used, leading to a bug in your code 167 | - what code has been loaded, what work was expected from it and how it works in reality 168 | - Ideally, attach the minimum code in which the bug is observed.Not a canvas of a thousand lines, but a minimum code -------------------------------------------------------------------------------- /examples/customClient/customClient.ino: -------------------------------------------------------------------------------- 1 | // пример использования другого UDP клиента 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | WiFiUDP udp; 9 | GyverNTPClient ntp(udp); 10 | 11 | void setup() { 12 | Serial.begin(115200); 13 | WiFi.begin("WIFI_SSID", "WIFI_PASS"); 14 | while (WiFi.status() != WL_CONNECTED) delay(100); 15 | Serial.println("Connected"); 16 | 17 | ntp.begin(3); // часовой пояс 18 | } 19 | 20 | void loop() { 21 | if (ntp.tick()) { 22 | Serial.println(ntp.toString()); 23 | } 24 | } -------------------------------------------------------------------------------- /examples/demo/demo.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void setup() { 5 | Serial.begin(115200); 6 | WiFi.begin("WIFI_SSID", "WIFI_PASS"); 7 | while (WiFi.status() != WL_CONNECTED) delay(100); 8 | Serial.println("Connected"); 9 | 10 | // обработчик ошибок 11 | NTP.onError([]() { 12 | Serial.println(NTP.readError()); 13 | Serial.print("online: "); 14 | Serial.println(NTP.online()); 15 | }); 16 | 17 | // обработчик секунды (вызывается из тикера) 18 | NTP.onSecond([]() { 19 | Serial.println("new second!"); 20 | }); 21 | 22 | // обработчик синхронизации (вызывается из sync) 23 | // NTP.onSync([](uint32_t unix) { 24 | // Serial.println("sync: "); 25 | // Serial.print(unix); 26 | // }); 27 | 28 | NTP.begin(3); // запустить и указать часовой пояс 29 | // NTP.setPeriod(30); // период синхронизации в секундах 30 | // NTP.setHost("ntp1.stratum2.ru"); // установить другой хост 31 | // NTP.setHost(IPAddress(1, 2, 3, 4)); // установить другой хост 32 | // NTP.asyncMode(false); // выключить асинхронный режим 33 | // NTP.ignorePing(true); // не учитывать пинг до сервера 34 | // NTP.updateNow(); // обновить прямо сейчас 35 | } 36 | 37 | void loop() { 38 | // тикер вернёт true каждую секунду в 0 мс секунды, если время синхронизировано 39 | if (NTP.tick()) { 40 | // вывод даты и времени строкой 41 | Serial.print(NTP.toString()); // NTP.timeToString(), NTP.dateToString() 42 | Serial.print(':'); 43 | Serial.println(NTP.ms()); // + миллисекунды текущей секунды. Внутри tick всегда равно 0 44 | 45 | // вывод в Datime 46 | Datime dt = NTP; // или Datime dt(NTP) 47 | dt.year; 48 | dt.second; 49 | dt.hour; 50 | dt.weekDay; 51 | dt.yearDay; 52 | // ... и прочие методы и переменные Datime 53 | 54 | // чтение напрямую, медленнее чем вывод в Datime 55 | NTP.second(); 56 | NTP.minute(); 57 | NTP.year(); 58 | // ... и прочие методы StampConvert 59 | 60 | // сравнение 61 | NTP == DaySeconds(12, 35, 0); // сравнение с DaySeconds (время равно 12:35:00) 62 | NTP == 1738237474; // сравнение с unix 63 | NTP == Datime(2025, 1, 30, 14, 14, 30); // сравнение с Datime 64 | } 65 | 66 | if (NTP.newSecond()) { 67 | // новую секунду можно поймать и здесь 68 | } 69 | 70 | // изменился онлайн-статус 71 | if (NTP.statusChanged()) { 72 | Serial.print("STATUS: "); 73 | Serial.println(NTP.online()); 74 | } 75 | } -------------------------------------------------------------------------------- /examples/rtc/rtc.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // GyverDS3231 поддерживает работу с GyverNTP 5 | #include 6 | GyverDS3231Min rtc; 7 | 8 | // можно написать свой класс и использовать любой другой RTC 9 | class RTC : public VirtualRTC { 10 | public: 11 | void setUnix(uint32_t unix) { 12 | Serial.print("SET RTC: "); 13 | Serial.println(unix); 14 | } 15 | uint32_t getUnix() { 16 | return 1738015299ul; 17 | } 18 | }; 19 | RTC vrtc; 20 | 21 | void setup() { 22 | Serial.begin(115200); 23 | WiFi.begin("WIFI_SSID", "WIFI_PASS"); 24 | // while (WiFi.status() != WL_CONNECTED) delay(100); 25 | Serial.println("Connected"); 26 | 27 | // GyverDS3231 28 | Wire.begin(); 29 | rtc.begin(); 30 | 31 | NTP.begin(3); // запустить и указать часовой пояс 32 | 33 | // подключить RTC 34 | // NTP.attachRTC(vrtc); 35 | NTP.attachRTC(rtc); 36 | } 37 | 38 | void loop() { 39 | if (NTP.tick()) { 40 | Serial.println(NTP.toString()); 41 | } 42 | } -------------------------------------------------------------------------------- /examples/testBlink/testBlink.ino: -------------------------------------------------------------------------------- 1 | // пример раз в секунду мигает светодиодом 2 | // можно прошить на несколько плат - они будут мигать синхронно 3 | 4 | #include 5 | #include 6 | 7 | void setup() { 8 | Serial.begin(115200); 9 | WiFi.begin("WIFI_SSID", "WIFI_PASS"); 10 | while (WiFi.status() != WL_CONNECTED) delay(100); 11 | Serial.println("Connected"); 12 | 13 | NTP.begin(3); // запустить и указать часовой пояс 14 | 15 | pinMode(LED_BUILTIN, OUTPUT); 16 | } 17 | 18 | void loop() { 19 | NTP.tick(); 20 | 21 | if (NTP.ms() == 0) { 22 | delay(1); 23 | digitalWrite(LED_BUILTIN, 1); 24 | } 25 | 26 | if (NTP.ms() == 500) { 27 | delay(1); 28 | digitalWrite(LED_BUILTIN, 0); 29 | } 30 | } -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For GyverNTP 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | GyverNTPClient KEYWORD1 10 | GyverNTP KEYWORD1 11 | NTP KEYWORD1 12 | 13 | ####################################### 14 | # Methods and Functions (KEYWORD2) 15 | ####################################### 16 | 17 | setGMTminute KEYWORD2 18 | setGMT KEYWORD2 19 | setPeriod KEYWORD2 20 | setHost KEYWORD2 21 | asyncMode KEYWORD2 22 | begin KEYWORD2 23 | end KEYWORD2 24 | tick KEYWORD2 25 | busy KEYWORD2 26 | updateNow KEYWORD2 27 | unix KEYWORD2 28 | ms KEYWORD2 29 | second KEYWORD2 30 | minute KEYWORD2 31 | hour KEYWORD2 32 | day KEYWORD2 33 | month KEYWORD2 34 | year KEYWORD2 35 | dayWeek KEYWORD2 36 | timeString KEYWORD2 37 | dateString KEYWORD2 38 | ping KEYWORD2 39 | status KEYWORD2 40 | synced KEYWORD2 41 | ignorePing KEYWORD2 42 | statusChanged KEYWORD2 43 | attachStatus KEYWORD2 44 | detachStatus KEYWORD2 45 | attachSync KEYWORD2 46 | detachSync KEYWORD2 47 | 48 | ####################################### 49 | # Constants (LITERAL1) 50 | ####################################### 51 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=GyverNTP 2 | version=2.2.2 3 | author=AlexGyver 4 | maintainer=AlexGyver 5 | sentence=Library for async receiving precise time from NTP server 6 | paragraph=Library for async receiving precise time from NTP server 7 | category=Timing 8 | url=https://github.com/GyverLibs/GyverNTP 9 | architectures=* 10 | depends=Stamp -------------------------------------------------------------------------------- /src/GyverNTP.cpp: -------------------------------------------------------------------------------- 1 | #if defined(ESP8266) || defined(ESP32) 2 | 3 | #include "GyverNTP.h" 4 | 5 | GyverNTP NTP; 6 | 7 | #endif -------------------------------------------------------------------------------- /src/GyverNTP.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(ESP8266) 4 | #include 5 | #elif defined(ESP32) 6 | #include 7 | #else 8 | #error "No implementation for given platform" 9 | #endif 10 | 11 | #include 12 | 13 | #include "GyverNTPClient.h" 14 | 15 | class GyverNTP : public GyverNTPClient { 16 | public: 17 | GyverNTP(int16_t gmt = 0, uint16_t prd = 3600) : GyverNTPClient(udp, gmt, prd) {} 18 | 19 | protected: 20 | bool connected() { 21 | return WiFi.status() == WL_CONNECTED; 22 | } 23 | 24 | IPAddress getHostIP(const char* host) { 25 | IPAddress ip; 26 | WiFi.hostByName(host, ip); 27 | return ip; 28 | } 29 | 30 | private: 31 | WiFiUDP udp; 32 | }; 33 | 34 | extern GyverNTP NTP; -------------------------------------------------------------------------------- /src/GyverNTPClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #define GNTP_MIN_PERIOD (5ul * 1000) // минимальный период синхронизации 7 | #define GNTP_DNS_PERIOD (10ul * 60 * 1000) // период обновления DNS 8 | #define GNTP_OFFLINE_DNS_PERIOD (15 * 1000ul) // период опроса при первом оффлайн статусе, мс 9 | #define GNTP_OFFLINE_PERIOD (4 * 1000ul) // период опроса при оффлайн статусе, мс 10 | #define GNTP_RTC_WRITE_PERIOD (60ul * 60 * 1000) // период записи в RTC, мс 11 | #define GNTP_NTP_TIMEOUT (2500ul) // таймаут ожидания ответа от NTP сервера, мс 12 | #define GNTP_LOCAL_PORT (1234) // локальный udp порт 13 | #define GNTP_NTP_PORT 123 // ntp порт по умолчанию 14 | #define GNTP_NTP_HOST "pool.ntp.org" // ntp хост по умолчанию 15 | 16 | class GyverNTPClient : public StampKeeper { 17 | typedef std::function ErrorCallback; 18 | 19 | enum class State : uint8_t { 20 | Disabled, 21 | Idle, 22 | WaitPacket, 23 | }; 24 | 25 | public: 26 | enum class Error : uint8_t { 27 | None, 28 | WiFi, 29 | Timeout, 30 | Host, 31 | Begin, 32 | BeginPacket, 33 | SendPacket, 34 | ParsePacket, 35 | }; 36 | 37 | // конструктор (часовой пояс в часах или минутах, период обновления в секундах) 38 | GyverNTPClient(UDP& udp, int16_t gmt = 0, uint16_t prd = 3600) : udp(udp) { 39 | setGMT(gmt); 40 | setPeriod(prd); 41 | } 42 | 43 | // установить часовой пояс в часах или минутах (глобально для Stamp) 44 | void setGMT(int16_t gmt) { 45 | setStampZone(gmt); 46 | } 47 | 48 | // установить период обновления в секундах 49 | void setPeriod(uint16_t prd) { 50 | _prd = prd * 1000ul; 51 | if (_prd < GNTP_MIN_PERIOD) _prd = GNTP_MIN_PERIOD; 52 | } 53 | 54 | // включить асинхронный режим (по умолч. true) 55 | void asyncMode(bool async) { 56 | _async = async; 57 | } 58 | 59 | // установить хост (умолч. "pool.ntp.org") 60 | void setHost(const String& host) { 61 | _host = host; 62 | _host_ip = INADDR_NONE; 63 | } 64 | 65 | // установить хост IP 66 | void setHost(const IPAddress& host) { 67 | _host = ""; 68 | _host_ip = host; 69 | } 70 | 71 | // установить порт (умолч. 123) 72 | void setPort(uint16_t port) { 73 | _port = port; 74 | } 75 | 76 | // получить пинг NTP сервера, мс 77 | int16_t ping() { 78 | return _ping; 79 | } 80 | 81 | // вернёт true при изменении статуса online 82 | bool statusChanged() { 83 | return _changed; 84 | } 85 | 86 | // не учитывать пинг соединения (умолч. false) 87 | void ignorePing(bool ignore) { 88 | _usePing = ignore; 89 | } 90 | 91 | // подключить RTC 92 | void attachRTC(VirtualRTC& rtc) { 93 | _rtc = &rtc; 94 | } 95 | 96 | // отключить RTC 97 | void detachRTC() { 98 | _rtc = nullptr; 99 | } 100 | 101 | // подключить обработчик ошибки 102 | void onError(ErrorCallback cb) { 103 | _state_cb = cb; 104 | } 105 | 106 | // отключить обработчик ошибки 107 | void detachError() { 108 | _state_cb = nullptr; 109 | } 110 | 111 | // получить последнюю ошибку 112 | Error getError() { 113 | return _error; 114 | } 115 | 116 | // получить последнюю ошибку 117 | const __FlashStringHelper* readError() { 118 | switch (_error) { 119 | case Error::None: return F("None"); 120 | case Error::WiFi: return F("WiFi"); 121 | case Error::Timeout: return F("Timeout"); 122 | case Error::Host: return F("Host"); 123 | case Error::Begin: return F("Begin"); 124 | case Error::BeginPacket: return F("BeginPacket"); 125 | case Error::SendPacket: return F("SendPacket"); 126 | case Error::ParsePacket: return F("ParsePacket"); 127 | } 128 | return F(""); 129 | } 130 | 131 | // есть ошибка 132 | bool hasError() { 133 | return _error != Error::None; 134 | } 135 | 136 | // вернёт true, если tick ожидает ответа сервера в асинхронном режиме 137 | bool busy() { 138 | return _state == State::WaitPacket; 139 | } 140 | 141 | // true - есть соединение с Интернет 142 | bool online() { 143 | return _online; 144 | } 145 | 146 | // запустить 147 | bool begin() { 148 | if (_state == State::Disabled) { 149 | if (!udp.begin(GNTP_LOCAL_PORT)) return _setError(Error::Begin); 150 | 151 | _state = State::Idle; 152 | _error = Error::None; 153 | _ping = 0; 154 | _changed = false; 155 | _online = false; 156 | _first = true; 157 | // _rtc_synced = false; 158 | _sync_tmr.reset(); 159 | _dns_tmr.reset(); 160 | _rtc_r_tmr.reset(); 161 | _rtc_w_tmr.reset(); 162 | if (_host.length()) _host_ip = INADDR_NONE; 163 | if (_state_cb) _state_cb(); 164 | StampKeeper::reset(); 165 | return true; 166 | } 167 | return false; 168 | } 169 | 170 | // запустить с указанием часового пояса в часах или минутах (глобально для Stamp) 171 | bool begin(int16_t gmt) { 172 | setGMT(gmt); 173 | return begin(); 174 | } 175 | 176 | // выключить NTP 177 | void end() { 178 | if (_state != State::Disabled) { 179 | _setError(Error::None); 180 | _state = State::Disabled; 181 | udp.stop(); 182 | } 183 | } 184 | 185 | // синхронно обновить время с сервера. true при успехе 186 | bool updateNow() { 187 | if (_state == State::Disabled) return false; 188 | 189 | if (!_sendPacket()) return false; 190 | 191 | while (!_available()) { 192 | if (millis() - _rtt >= GNTP_NTP_TIMEOUT) return _setError(Error::Timeout); 193 | delay(1); 194 | } 195 | 196 | return _readPacket(); 197 | } 198 | 199 | // тикер, вызывать в loop. Вернёт true каждую секунду, если синхронизирован. Синхронизируется по таймеру 200 | bool tick() { 201 | if (_changed) _changed = false; 202 | if (_state != State::Disabled) { 203 | if (_first && connected()) { 204 | _first = false; 205 | _sync_tmr.reset(); 206 | } 207 | if (_async) { 208 | switch (_state) { 209 | case State::Idle: 210 | if (_timeToSync()) _sendPacket(); 211 | break; 212 | 213 | case State::WaitPacket: 214 | if (connected()) { 215 | if (_available()) _readPacket(); 216 | else if (millis() - _rtt > GNTP_NTP_TIMEOUT) _setError(Error::Timeout); 217 | } else { 218 | _setError(Error::WiFi); 219 | } 220 | break; 221 | 222 | default: break; 223 | } 224 | } else { 225 | if (_timeToSync()) updateNow(); 226 | } 227 | } else if (_rtc && _rtc_r_tmr.elapsed(_prd)) { 228 | StampKeeper::sync(_rtc->getUnix()); 229 | } 230 | return StampKeeper::tick(); 231 | } 232 | 233 | protected: 234 | virtual bool connected() = 0; 235 | virtual IPAddress getHostIP(const char* host) = 0; 236 | 237 | private: 238 | class Timer { 239 | public: 240 | bool elapsed(uint32_t prd) { 241 | if (!_tmr || millis() - _tmr >= prd) { 242 | restart(); 243 | return true; 244 | } 245 | return false; 246 | } 247 | void restart() { 248 | _tmr = millis(); 249 | if (!_tmr) --_tmr; 250 | } 251 | void reset() { 252 | _tmr = 0; 253 | } 254 | 255 | private: 256 | uint32_t _tmr = 0; 257 | }; 258 | 259 | UDP& udp; 260 | VirtualRTC* _rtc = nullptr; 261 | uint32_t _prd = 0; 262 | uint32_t _rtt = 0; 263 | Timer _sync_tmr; 264 | Timer _dns_tmr; 265 | Timer _rtc_r_tmr; 266 | Timer _rtc_w_tmr; 267 | ErrorCallback _state_cb = nullptr; 268 | String _host = GNTP_NTP_HOST; 269 | IPAddress _host_ip; 270 | uint16_t _port = GNTP_NTP_PORT; 271 | int16_t _ping = 0; 272 | State _state = State::Disabled; 273 | Error _error = Error::None; 274 | bool _usePing = true; 275 | bool _online = false; 276 | bool _async = true; 277 | bool _changed = false; 278 | bool _first = true; 279 | // bool _rtc_synced = false; 280 | 281 | bool _timeToSync() { 282 | return (_sync_tmr.elapsed(_host_ip ? (_online ? _prd : GNTP_OFFLINE_PERIOD) : GNTP_OFFLINE_DNS_PERIOD)); 283 | } 284 | 285 | bool _getHost() { 286 | if (_host.length()) { 287 | if (!_host_ip || _dns_tmr.elapsed(GNTP_DNS_PERIOD)) { 288 | _dns_tmr.restart(); 289 | IPAddress host = getHostIP(_host.c_str()); 290 | if (host) _host_ip = host; 291 | } 292 | } 293 | return _host_ip; 294 | } 295 | 296 | bool _sendPacket() { 297 | uint8_t buf[48] = {0b11100011}; 298 | if (!connected()) return _setError(Error::WiFi); 299 | if (!_getHost()) return _setError(Error::Host); 300 | if (!udp.beginPacket(_host_ip, _port)) return _setError(Error::BeginPacket); 301 | if (!(udp.write(buf, 48) == 48 && udp.endPacket())) return _setError(Error::SendPacket); 302 | 303 | _state = State::WaitPacket; 304 | _rtt = millis(); 305 | return true; 306 | } 307 | 308 | bool _available() { 309 | return (udp.parsePacket() == 48 && udp.remotePort() == _port); 310 | } 311 | 312 | bool _readPacket() { 313 | _rtt = millis() - _rtt; // время между запросом и ответом 314 | uint8_t buf[48]; 315 | if (!(udp.read(buf, 48) == 48 && buf[40])) return _setError(Error::ParsePacket); 316 | 317 | uint16_t a_ms = (_merge(buf + 44) * 1000) >> 16; // мс ответа сервера 318 | if (_usePing) { // расчёт пинга 319 | uint16_t r_ms = (_merge(buf + 36) * 1000) >> 16; // мс запроса клиента 320 | int16_t err = a_ms - r_ms; // время обработки сервером 321 | if (err < 0) err += 1000; // переход через секунду 322 | _rtt = (_rtt - err) / 2; // текущий пинг 323 | _ping = _ping ? (_ping + _rtt) / 2 : _rtt; // бегущее среднее по двум 324 | a_ms += _ping; 325 | } 326 | uint32_t unix = ((_merge(buf + 40) << 16) | _merge(buf + 42)) - 2208988800ul; // 1900 to unix 327 | 328 | // пропускать тики если подключен и не синхронизирован rtc 329 | StampKeeper::sync(unix, a_ms /*, (_rtc && !_rtc_synced)*/); 330 | 331 | if (!_online) _changed = true; 332 | _online = true; 333 | 334 | _state = State::Idle; 335 | _error = Error::None; 336 | if (_state_cb) _state_cb(); 337 | if (_rtc && _rtc_w_tmr.elapsed(GNTP_RTC_WRITE_PERIOD)) { 338 | _rtc->setUnix(unix); 339 | // _rtc_synced = true; 340 | } 341 | return true; 342 | } 343 | 344 | inline uint32_t _merge(uint8_t* buf) { 345 | return (buf[0] << 8) | buf[1]; 346 | } 347 | 348 | bool _setError(Error error) { 349 | if (_online && error == Error::Timeout) _dns_tmr.reset(); 350 | 351 | if (_online) _changed = true; 352 | _online = false; 353 | 354 | if (_rtc && _rtc_r_tmr.elapsed(_prd)) { 355 | // пропускать тики если не синхронизирован rtc 356 | StampKeeper::sync(_rtc->getUnix() /*, 0, !_rtc_synced*/); 357 | } 358 | _error = error; 359 | _state = State::Idle; 360 | if (_state_cb) _state_cb(); 361 | return false; 362 | } 363 | }; -------------------------------------------------------------------------------- /src/requestNTP.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(ESP8266) 4 | #include 5 | #elif defined(ESP32) 6 | #include 7 | #endif 8 | #include 9 | 10 | static uint32_t requestNTP(const char* host = "pool.ntp.org", uint16_t port = 123, uint16_t tout = 2000) { 11 | WiFiUDP udp; 12 | if (!udp.begin(1234)) return 0; 13 | if (!udp.beginPacket(host, port)) return 0; 14 | 15 | uint8_t buf[48] = {0b11100011}; 16 | if (udp.write(buf, 48) != 48) return 0; 17 | if (!udp.endPacket()) return 0; 18 | 19 | while (udp.parsePacket() != 48) { 20 | if (!--tout) return 0; 21 | delay(1); 22 | } 23 | if (udp.remotePort() != port) return 0; 24 | if (udp.read(buf, 48) != 48) return 0; 25 | if (!buf[40]) return 0; 26 | return (((buf[40] << 8 | buf[41]) << 16) | (buf[42] << 8 | buf[43])) - 2208988800ul; 27 | } --------------------------------------------------------------------------------