Ntp client

Тема в разделе "ESP8266, ESP32", создана пользователем ale, 27 дек 2021.

  1. ale

    ale Нерд

    Доброго всем! Устройство на базе esp нуждается в более-менее точном времени. модуль припаивать не комильфо ведь есть же вайфай. В общем, ищу нормальную реализацию NTP клиента (асинхронную) поскольку устройство занимается пением. Я немного подпилил пример с AsyncUDP клиентом для асинхронного получения "точного" времени, но совсем не Си-гуру и мне нужно, чтобы я мог задавать адрес серевра NTP через его доменное имя, а не IP-адрес. Следующий шаг - завернуть в класс/библу и отдать нуждающимся если таковые есть (а может уже и есть решение, но не нашел). Тогда это была бы законченная реализация, дружественная к конечному пользователю девайса (настройка через web). Мой код ниже (реализацию запроса взял https://github.com/arduino-libraries/NTPClient ) Не поверю, что все кто сталкивался с синхроном по NTP не сталкивались с проблемой тупого GetLocalTime. И да, скажу заранее - опубликованный здесь код уже есть на другом форуме, и он мой, просто там не смогли/не хотели помочь.
    Код (C++):
    #include "Arduino.h"
    #include "WiFi.h"
    #include "AsyncUDP.h"

    #define secs70 2208988800UL
    #define NTP_PACKET_SIZE 48
    #define UPDATE_PERIOD 10 //каждый 10 сек корректируем время

    unsigned long lastUpdate = 0;
    long          gmtOffset = 3600 * 9; //9 - мой часовой пояс
    unsigned long offsetTime = 0;
    unsigned long updateT = 0;

    int retryUpd = 0;


    time_t now; //системное время на устройстве

    const char* ssid = "ssid";
    const char* password = "pass";

    AsyncUDP udp;

    void setup()
    {
        Serial.begin(115200);
        WiFi.mode(WIFI_STA);
        WiFi.begin(ssid, password);
        if (WiFi.waitForConnectResult() != WL_CONNECTED) {
            Serial.println("WiFi Failed");
            while (1) {
                delay(1000);
            }
        }

        now = millis() / 1000;

        if (udp.connect(IPAddress(162, 159, 200, 123), 123)) //time.cloudflare.com......pool.ntp.org 0-й сервер (197,84,150,123)/*
        {
            Serial.println("UDP connected");

            udp.onPacket([](AsyncUDPPacket packet)
                {
                    unsigned long highWord = word(packet.data()[40], packet.data()[41]);
                    unsigned long lowWord = word(packet.data()[42], packet.data()[43]);

                    unsigned long secs1900 = highWord << 16 | lowWord;

                    unsigned long current = secs1900 - secs70 + gmtOffset;

                    offsetTime = (time_t)(current);
                 
                    Serial.println("TIME CORRECTION");
                });    
        }
    }

    void printTime()
    {
        tm tm;
        localtime_r(&now, &tm);

        char buff[30];

        strftime(buff, 256, "%Y-%m-%d  %H:%M:%S", &tm);
        Serial.println(buff);
    }

    void update() {
        byte buff[NTP_PACKET_SIZE]; //48
        memset(buff, 0, NTP_PACKET_SIZE);
     
        buff[0] = 0b11100011;
        buff[1] = 0;
        buff[2] = 6;
        buff[3] = 0xEC;
        buff[12] = 49;
        buff[13] = 0x4E;
        buff[14] = 49;
        buff[15] = 52;

        if (udp.connected())
            udp.write(buff, NTP_PACKET_SIZE);
    }

    void loop()
    {
        now = offsetTime + (millis() - updateT) / 1000;
       
        if (millis() - lastUpdate > 1000)
        {
            Serial.print("Now: ");
            printTime();
            lastUpdate = millis();
            retryUpd++;
            if (retryUpd >= UPDATE_PERIOD)//~каждые 10 секунд корректировка асинхронная
            {
                updateT = millis();
                update();
                retryUpd = 0;
            }
        }
    }
     
  2. GeoShu

    GeoShu Гик

    А чем не подходит ntpclient из репозитория Ардуино Иде?
     
  3. ale

    ale Нерд

    Если речь про это: https://github.com/arduino-libraries/NTPClient
    то вот по чему:
    Код (C++):

    do {
    delay ( 10 );
    cb = this->_udp->parsePacket();
    if (timeout > 100) return false; // timeout after 1000 ms
    timeout++;
    } while (cb == 0);
    фриз в лупе
     
  4. GeoShu

    GeoShu Гик

    Так он по тайм-ауту выходит, если что-то не так.
     
  5. ale

    ale Нерд

    я знаю, но для главного цикла это агония
     
    Последнее редактирование: 29 дек 2021
  6. GeoShu

    GeoShu Гик

    А как правильно выходить из цикла, если нужно либо дождаться ответа, либо прекратить запрос?
    Вот в вашем коде :
    Код (C++):
    if (WiFi.waitForConnectResult() != WL_CONNECTED) {
            Serial.println("WiFi Failed");
            while (1) {
                delay(1000);
            }
        }
    При попытке соединения с сетью вы просто уходите в бесконечный цикл вместо того, чтобы повторить запрос. Так ваше устройство не сможет работать автономно. Допустим, выключили электричество, потом включили. Ардуино включилось сразу, а роутер через N минут. Но Ардуино уже не сможет самостоятельно к нему подключиться. Потребуется ручная перезагрузка. Тут бы вам либо WTD поставить, либо зациклить попытки соединения.
     
  7. SergeiL

    SergeiL Оракул Модератор

    А какая проблема?
    Не сталкивался ни с какими проблемами, использую все стандартное:
    Код (C++):
    #include <ESP8266WiFi.h>
    #include <WiFiUdp.h>
     
    Vovka и GeoShu нравится это.
  8. ale

    ale Нерд

    Всех с прошедшим! Никого не хочу обидеть, но похоже никто не вникает в содержание вопроса.
    Да, я вам про асинхронный запрос, а вы мне про бесконечный цикл :) Речь вообще не об этом.
    А как Вы синхронизируете время по ntp? Прошу показать кусочек кода по отправке запроса и обработке ответа от ntp-сервака, будет забавно, если я на ровном месте нашел проблему.
     
  9. b707

    b707 Гуру

    ну перепишите цикл асинхронно - в чем проблема? Пример "блинк без делей" вам в помощь
     
  10. b707

    b707 Гуру

    как в примере к библиотеке. Асинхронностью кода не заморачивался, так как у меня для NTP отдельный контроллер, основной код на другом МК
     
  11. parovoZZ

    parovoZZ Гуру

    в примерах стандартного SDK есть следующее:
    Код (C++):
    sntp_stop();
    sntp_setservername(0,"ntp.time.in.ua");
    sntp_setservername(1,"pool.ntp.org");
    sntp_set_timezone(2);
    sntp_init();
    и забираем время
    Код (C++):
    uint32 Time = sntp_get_current_timestamp()
     
  12. ale

    ale Нерд

    в примере к библиотеке (еще вопрос какой библиотеки) показан еще один способ как задушить луп. Что касается отдельного МК под время, ну не у всех такие грандиозные проекты и теперь мне капец как интересно узнать какой контроллер вы для этого используете и в каком устройстве.
    От души приветствую! Я натыкался на этот код, но как его прикрутить к своей задаче ума не хватило. Погуглил сегодня еще и вроде кое-что нашел, и там реализованы колбэки. Проверить надо
     
    Последнее редактирование: 4 янв 2022
  13. parovoZZ

    parovoZZ Гуру

    надо юзать стандартный SDK.
     
  14. ale

    ale Нерд

    Согласен, но я водитель, а не механик ). Много быстрее собрать проект из того что уже сделано чем писать самому. :rolleyes:Что касается найденной библы - проверил, работает отлично. Синхронизация в бэкграунде, колбэк, точность 1 мс. https://github.com/gmag11/ESPNtpClient.
    parovoZZ, спасибо за наводку, библу нашел гугля реализацию sntp_. Остальным участникам - фэйспалм;)
     
  15. b707

    b707 Гуру

    с чего бы это? :)
    вы может думали. что все ринутся решать ваши проблемы? - таки нет :)
     
  16. ale

    ale Нерд

    таки зачем тогда вообще отвечать? тем более не в кассу. только по фразе - "ну перепишите цикл асинхронно - в чем проблема?" уже все понятно - гуру по количеству бесполезных реплик :) Посмотрите библу, может уберете лишний мк.
     
  17. b707

    b707 Гуру

    не уберу, меня устраивает :)
    А что до "перепишите" - ну правда смешно, там поменять-то надо пару строк - разбить получение времени на две отдельные процедуры, отправку запроса и получения ответа.
     
  18. SergeiL

    SergeiL Оракул Модератор

    Нужно еще посмотреть на эту асинхронность.
    Хотел протестировать, когда и откуда вызывается обработчик, но пока не до этого,
    восстанавливаю систему на даче.
     
  19. ale

    ale Нерд

    :D:D:D на примере "блинк без делэй". а может лучше на две функции? погуглите - асинхронная реализация
    ну так если "Не сталкивался ни с какими проблемами, использую все стандартное:" конечно упадет:rolleyes:
     
    Последнее редактирование: 6 янв 2022
  20. SergeiL

    SergeiL Оракул Модератор

    Код (C++):
    loop()
    {
       //............................................................
       //............................................................
     
       if( (cur_ms - ms2) > 900000L) && !NTP_answer_timeout && T100_ms_timer==1 && s > 20 && WiFi_Last_State == CONNECTED )
       {
           err_count++;  // увеличим счетчик попыток
           NTP_Request_Send();      
       }
       if(NTP_Answer_Check())
       {
          if( NTP_answer_timeout )
          {
              if (NTP_Answer_Read())
              {
                ms2  = cur_ms;
                err_count = 0;
                NTP_answer_timeout=0;
              }
           }
       }
       //............................................................
       //............................................................
    }

    В часах с NTP, я не использовал <Time.h>, только UDP, время считаю и синхронизирую сам.
    Использую <Ticker.h>, который дергает каждые 100мс функцию (делал и 10мс, разницы глазами не видно), из нее и обновление 4-х разрядного, 7-ми индикатора по SPI.
    Когда делал первые часики с NTP хотелось сделать, чтобы поставленные рядом несколько часов обновлялись бы синхронно (типа как в Apple Watch, все часы в мире ходят абсолютно синхронно).

    Поэтому из приходящего пакета NTP вытаскиваю не только время до секунд, но и доли секунд (44-47, байты пакета), отправляю запрос в начале секунды, учитываю время отправки пакета, время получения ответа.
    Если если ответ пришел c задержкой - игнорирую ответ.
    Если без задержек смотрю на доли секунд, рассчитываю десятые, и если время отличаются - синхронизирую.

    Все это в loop(), при этом часы еще измеряют температуру, влажность, отправляют их по MQTT, получают по MQTT уличную температуру, которую отображают попеременно со временем. Дома таких в разных размерах 5 штук.