├── .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 | [](https://github.com/GyverLibs/GyverNTP/releases/latest/download/GyverNTP.zip)
2 | [](https://registry.platformio.org/libraries/gyverlibs/GyverNTP)
3 | [](https://alexgyver.ru/)
4 | [](https://alexgyver.ru/support_alex/)
5 | [](https://github-com.translate.goog/GyverLibs/GyverNTP?_x_tr_sl=ru&_x_tr_tl=en)
6 |
7 | [](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 | }
--------------------------------------------------------------------------------