Иногда NTP возвращает неправильное время

У меня есть Uno с Wi-Fi-экраном CC3000, и я пытаюсь использовать библиотеку sntp.h, чтобы получить время NTP. Я экспериментировал с модифицированной версией кода ntpTest из библиотеки, которая считывает время каждую минуту. В последовательном мониторе я вижу правильное время БОЛЬШИНСТВО времени. Однако через случайные промежутки времени время будет отображаться на 3 часа и 14 минут раньше текущего времени MST.

Конструктор клиента SNTP выглядит следующим образом:

sntp mysntp = sntp(NULL,"time.nist.gov", (short)(-7 * 60),(short)(-7 * 60), true);

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

ИЗМЕНИТЬ Вот урезанный код из примера Adafruit CC3000 WIFI Shield ntpTest. Обратите внимание: единственная причина, по которой я так часто находил время, заключалась в том, что я просто учился тому, как это работает, и я заметил это странное поведение. В мои намерения не входило создание приложения, которое в конечном итоге могло бы обращаться к серверам NTP чаще одного раза в день.

 /****************************************** ********
Это пример Adafruit CC3000 Wifi Breakout & Щит

Разработан специально для работы с продуктами Adafruit WiFi:
----> https://www.adafruit.com/products/1469

Adafruit инвестирует время и ресурсы, предоставляя этот открытый исходный код.
пожалуйста, поддержите Adafruit и оборудование с открытым исходным кодом, купив
продукция от Adafruit!

Автор Кевин Таунсенд и amp; Лимор Фрид & Рик Лесняк из Adafruit Industries.
Лицензия BSD, весь текст выше должен быть включен в любое распространение.
************************************************* **/

/*

В этом примере выполняется проверка клиента SNTP (Simple Network Time Protocol):
* Инициализация
* Сканирование SSID
* Точка доступа
* Распечатка DHCP
* Синхронизация времени SNTP
* Извлечение и печать информации о текущем времени и дате.

*/

#include <Adafruit_CC3000.h>
#include <ccspi.h>
#include <SPI.h>
//#include "utility/NetTime.h"
#include <string.h>
#include "utility/debug.h"
#include "sntp.h"

// Это контакты прерывания и управления
#define ADAFRUIT_CC3000_IRQ   3  // ДОЛЖЕН быть контактом прерывания!
// Это могут быть любые два контакта
#define ADAFRUIT_CC3000_VBAT  5
#define ADAFRUIT_CC3000_CS    10
// Используем аппаратный SPI для остальных контактов
// В UNO SCK = 13, MISO = 12 и MOSI = 11
Adafruit_CC3000 cc3000 = Adafruit_CC3000(ADAFRUIT_CC3000_CS, ADAFRUIT_CC3000_IRQ, ADAFRUIT_CC3000_VBAT,
                                         SPI_CLOCK_DIVIDER); // вы можете изменить эту тактовую частоту, но DI



#define WLAN_SSID       "CompsciWifi"   // не может быть длиннее 32 символов!
#define WLAN_PASS       "005276DA"
// Безопасность может быть WLAN_SEC_UNSEC, WLAN_SEC_WEP, WLAN_SEC_WPA или WLAN_SEC_WPA2.
#define WLAN_SECURITY   WLAN_SEC_WPA2

//Аргументы конструктора клиента SNTP:
// 1 - URL основного сетевого сервера времени (может быть NULL)
// 2 - URL вторичного сетевого сервера времени (также может быть NULL)
// 3 — локальное смещение UTC в минутах (восточное время США — UTC — 5:00).
// 4 — локальное смещение UTC в минутах для летнего времени (восточное летнее время США — UTC — 4:00).
// 5 - Включить настройку летнего времени (пока не реализовано)
//
//sntp mysntp = sntp(NULL, "time.nist.gov", (short)(-7 * 60), (short)(-7 * 60), true);
sntp mysntp = sntp(NULL, "time-a.timefreq.bldrdoc.gov", (short)(-7 * 60), (short)(-7 * 60), true);

// Тип SNTP_Timestamp — это 64-битное время NTP. Старшие 32-битные разряды — это секунды, прошедшие с 01.01.1900.
// Младшие 32-битные значения составляют доли секунды
SNTP_Timestamp_t now;

// Тип NetTime_t содержит время NTP, разбитое на человекоориентированные значения:
// uint16_t миллис; ///< Миллисекунды после второго (0..999)
// uint8_t сек; ///< Секунды после минуты (0..59)
// uint8_t мин; ///< Минуты после часа (0..59)
// uint8_t час; ///< Часов с полуночи (0..23)
// uint8_t mday; ///< День месяца (1..31)
// uint8_t мон; ///< Месяцы с января (0..11)
// uint16_t год; ///< Год.
// uint8_t wday; ///< Дней с воскресенья (0..6)
// uint8_t yday; ///< Дней с 1 января (0..365)
// bool isdst; ///< Флаг перехода на летнее время, в настоящее время не поддерживается.
NetTime_t timeExtract;

#define pF(string_pointer) (reinterpret_cast<const __FlashStringHelper *>(pgm_read_word(string_pointer)))

const prog_char   janStr[] PROGMEM = "January";
const prog_char   febStr[] PROGMEM = "February";
const prog_char   marStr[] PROGMEM = "March";
const prog_char   aprStr[] PROGMEM = "April";
const prog_char   mayStr[] PROGMEM = "May";
const prog_char   junStr[] PROGMEM = "June";
const prog_char   julStr[] PROGMEM = "July";
const prog_char   augStr[] PROGMEM = "August";
const prog_char   sepStr[] PROGMEM = "September";
const prog_char   octStr[] PROGMEM = "October";
const prog_char   novStr[] PROGMEM = "November";
const prog_char   decStr[] PROGMEM = "December"; 

PROGMEM const char* const monthStrs[] = { janStr, febStr, marStr, aprStr, mayStr, junStr,
                                          julStr, augStr, sepStr, octStr, novStr, decStr}; 

const prog_char   sunStr[] PROGMEM = "Sunday";
const prog_char   monStr[] PROGMEM = "Monday";
const prog_char   tueStr[] PROGMEM = "Tuesday";
const prog_char   wedStr[] PROGMEM = "Wednesday";
const prog_char   thuStr[] PROGMEM = "Thursday";
const prog_char   friStr[] PROGMEM = "Friday";
const prog_char   satStr[] PROGMEM = "Saturday"; 

PROGMEM const char* const dayStrs[] = { sunStr, monStr, tueStr,  wedStr,
                                        thuStr, friStr, satStr};


/**************************************************************************/
/*!
    @brief  Sets up the HW and the CC3000 module (called automatically
            on startup)
*/
/**************************************************************************/
void setup(void)
{
  Serial.begin(115200);
  Serial.println(F("Hello, CC3000!\n")); 

  //Serial.print("Свободная оперативная память: "); Serial.println(getFreeRam(), DEC);


  /* Initialise the module */
  Serial.println(F("\nInitialising the CC3000 ..."));
  if (!cc3000.begin())
  {
    Serial.println(F("Unable to initialise the CC3000! Check your wiring?"));
    while(1);
  }

  /* Optional: Update the Mac Address to a known value */
/*
  uint8_t macAddress[6] = { 0x08, 0x00, 0x28, 0x01, 0x79, 0xB7 };
   if (!cc3000.setMacAddress(macAddress))
   {
     Serial.println(F("Failed trying to update the MAC address"));
     while(1);
   }
*/


  /* Delete any old connection data on the module */
  Serial.println(F("\nDeleting old connection profiles"));
  if (!cc3000.deleteProfiles()) {
    Serial.println(F("Failed!"));
    while(1);
  }

  /* Attempt to connect to an access point */
  char *ssid = WLAN_SSID;             /* Max 32 chars */
  Serial.print(F("\nAttempting to connect to ")); Serial.println(ssid);

  /* NOTE: Secure connections are not available in 'Tiny' mode! */
  if (!cc3000.connectToAP(WLAN_SSID, WLAN_PASS, WLAN_SECURITY)) {
    Serial.println(F("Failed!"));
    while(1);
  }

  Serial.println(F("Connected!"));

  /* Wait for DHCP to complete */
  Serial.println(F("Request DHCP"));
  while (!cc3000.checkDHCP())
  {
    delay(100); // Задача: добавить тайм-аут DHCP!
  }  

  /*Serial.println(F("UpdateNTPTime"));
  if (mysntp.UpdateNTPTime())
  {
    Serial.println(F("Current local time is:"));
    mysntp.ExtractNTPTime(mysntp.NTPGetTime(&now, true), &timeExtract);*/

    /*Serial.print(timeExtract.hour); Serial.print(F(":")); Serial.print(timeExtract.min); Serial.print(F(":"));Serial.print(timeExtract.sec); Serial.print(F("."));Serial.println(timeExtract.millis);
    Serial.print(pF(&dayStrs[timeExtract.wday])); Serial.print(F(", ")); Serial.print(pF(&monthStrs[timeExtract.mon])); Serial.print(F(" ")); Serial.print(timeExtract.mday); Serial.print(F(", "));Serial.println(timeExtract.year);
    Serial.println(timeExtract.yday + 1); 
  }

  /* You need to make sure to clean up after yourself or the CC3000 can freak out */
  /* the next time you try to connect ... */
  /*Serial.println(F("\n\nClosing the connection"));
  cc3000.disconnect(); */

    mysntp.UpdateNTPTime();
    mysntp.ExtractNTPTime(mysntp.NTPGetTime(&now, true), &timeExtract);
    Serial.print(pF(&dayStrs[timeExtract.wday])); Serial.print(F(", ")); Serial.print(pF(&monthStrs[timeExtract.mon])); Serial.print(F(" ")); Serial.print(timeExtract.mday); Serial.print(F(", "));Serial.println(timeExtract.year);
}


void loop(void)
{
    mysntp.UpdateNTPTime();
    mysntp.ExtractNTPTime(mysntp.NTPGetTime(&now, true), &timeExtract);
    Serial.print(timeExtract.hour); Serial.print(F(":")); Serial.print(timeExtract.min);

    delay(60000);
    Serial.println();
}

, 👍1

Обсуждение

Попытайтесь выяснить, повреждены ли данные или произошло математическое переполнение. 3 часа 14 минут — не такое интересное число, как четыре часа 15 минут, но все же может быть интригующим. Вы уверены, что интервалы действительно случайны? Было бы здорово, если бы вы могли каким-то образом отслеживать данные непосредственно с экрана Wi-Fi (или еще лучше, если бы вы могли настроить поддельный сервер, чтобы он неоднократно циклически проходил одни и те же моменты времени и смотрел, повторяется ли это. Но реальным решением может стать тщательная проверка всей цепочки операций., @Chris Stratton

Вот что я предлагаю вам сделать: сделайте копию своего кода и постепенно удаляйте любой код, не связанный напрямую с вашей проблемой sntp. Есть два возможных результата: либо проблема исчезнет, и в этом случае нужно найти минимальное изменение, чтобы проблема исчезла. Если проблема не исчезнет, опубликуйте оставшийся код здесь - я ожидаю, что у вас останется около 10-20 строк (если вы зайдете так далеко)., @AMADANON Inc.

PS — вам, вероятно, не следует слишком часто опрашивать серверы времени — если ваше устройство не выключится, вам следует держать локальные часы. NTP предназначен для предотвращения смещения ваших часов, а не для того, чтобы избавить вас от необходимости держать локальные часы. Я бы не ожидал, что Arduino потребуется опрашивать NTP более одного раза в день. Сохранив локальные часы, вы также можете проверить, выключены ли часы — они не должны смещаться на 3 часа между опросами., @AMADANON Inc.

@AMADANON-inc Я думаю, что код уже довольно минимален, но я попробую удалить все, что смогу, и посмотреть, что произойдет. Я согласен, что конечный продукт должен использовать NTP по назначению. Это был просто странный феномен, который я наблюдал, изучая использование Wi-Fi-экрана. Это заставило меня забеспокоиться, что я, должно быть, упускаю что-то важное, когда в конечном итоге буду использовать NTP для исправления отклонения времени., @techkilljoy

@ChrisStratton это звучит немного выше моих навыков. Возможно, было бы неплохо попробовать, но сначала я попробую исключить другие возможности. Спасибо за понимание., @techkilljoy

@techkilljoy, как только у вас будет минимальный код, опубликуйте его здесь, чтобы мы могли попытаться воспроизвести проблему. Обычно в подобных случаях возникает небольшая проблема с кодом; когда вы работаете над кодом, часто трудно понять, что именно не так., @AMADANON Inc.

@AMADANON-inc, извините за задержку, вызванную праздниками, но вот урезанный код, указанный под строкой РЕДАКТИРОВАНИЯ выше., @techkilljoy

Возможно, будет проще начать с создания минимального приложения NTP и подтверждения его работы. Используйте только библиотеку NTP и образец эскиза или пример из библиотеки NTP. Начните с чего-то максимально простого или заведомо хорошего., @dlu


1 ответ


1

Обновленный тестовый набросок NTP предполагает, что связь с сервером NTP не прервется, но поскольку протокол NTP основан на UDP, ответный пакет может быть потерян. Возможным исправлением обновленного наброска является проверка возвращаемого значения из UpdateNTPTime().

void loop(void)
{
    if (mysntp.UpdateNTPTime()) {
        mysntp.ExtractNTPTime(mysntp.NTPGetTime(&now, true), &timeExtract);
        Serial.print(timeExtract.hour); 
        Serial.print(F(":")); 
        Serial.println(timeExtract.min);
        delay(60000);
        Serial.println();
    }
}

Ура!

,