從NTP伺服器獲取當前時間



在物聯網裝置中,時間戳成為裝置和伺服器之間交換的資料包的重要屬性。因此,始終在裝置上擁有正確的時間是必要的。一種方法是使用與ESP32連線的RTC(即時時鐘)。您甚至可以使用ESP32的內部RTC。一旦給定參考時間,它就可以正確輸出未來的時間戳。但是您將如何獲取參考時間呢?一種方法是在程式設計ESP32時硬編碼當前時間。但這並不是一個簡潔的方法。其次,RTC容易漂移,定期向其提供參考時間戳是一個好主意。在本章中,我們將瞭解如何從NTP伺服器獲取當前時間,將其一次性饋送到ESP32的內部RTC,並列印未來的時間戳。

關於NTP的簡要說明

NTP代表網路時間協議。它是一種用於計算機系統之間時鐘同步的協議。通俗地說,有一個伺服器位於某個地方,它精確地維護時間。每當客戶端向NTP伺服器請求當前時間時,它都會發送精確到100毫秒的時間。您可以在此處閱讀更多關於NTP的資訊。對於ESP32,有一個內建的time庫來處理與NTP伺服器的所有通訊。讓我們在下面的程式碼演練中探索該庫的使用。

程式碼演練

我們將使用一個內建示例進行此演練。它可以在檔案 -> 示例 -> ESP32 -> 時間 -> SimpleTime中找到。它也可以在GitHub上找到。

我們首先包含WiFi和時間庫。

#include <WiFi.h>
#include "time.h"

接下來,我們定義一些全域性變數。將WiFi SSID和密碼替換為您WiFi的相應值。接下來,我們定義了NTP伺服器的URL。gmtOffset_sec指的是您的時區與GMT或密切相關的UTC的秒數偏移。例如,在印度,時區比UTC提前5小時30分鐘,gmtOffset_sec將為(5+0.5)*3600 = 19800。

daylightOffset_sec與實行夏令時的國家相關。在其他國家,可以簡單地將其設定為0。

const char* ssid       = "YOUR_SSID";
const char* password   = "YOUR_PASS";

const char* ntpServer = "pool.ntp.org";
const long  gmtOffset_sec = 3600;
const int   daylightOffset_sec = 3600;

接下來,您可以看到一個函式printLocalTime()。它只是從內部RTC獲取本地時間並將其列印到序列埠。

void printLocalTime()
{
   struct tm timeinfo;
   if(!getLocalTime(&timeinfo)){
      Serial.println("Failed to obtain time");
      return;
   }
   Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}

您可能在這裡有三個問題:

  • struct tm在哪裡定義?
  • getLocalTime()函式在哪裡定義?
  • %A、%B等格式化程式是什麼?

struct tm在我們在頂部包含的time.h檔案中定義。事實上,時間庫不是ESP32特有的庫。它是一個與ESP32相容的AVR庫。您可以在此處找到原始碼。如果您檢視time.h檔案,您將看到struct tm。

struct tm {
   int8_t   tm_sec; /**< seconds after the minute - [ 0 to 59 ] */
   int8_t   tm_min; /**< minutes after the hour - [ 0 to 59 ] */
   int8_t   tm_hour; /**< hours since midnight - [ 0 to 23 ] */
   int8_t   tm_mday; /**< day of the month - [ 1 to 31 ] */
   int8_t   tm_wday; /**< days since Sunday - [ 0 to 6 ] */
   int8_t   tm_mon; /**< months since January - [ 0 to 11 ] */
   int16_t  tm_year; /**< years since 1900 */
   int16_t  tm_yday; /**< days since January 1 - [ 0 to 365 ] */
   int16_t  tm_isdst; /**< Daylight Saving Time flag */
};

現在,getLocalTime函式是ESP32特有的。它在esp32-hal-time.c檔案中定義。它是ESP32的Arduino核心的一部分,不需要在Arduino中單獨包含。您可以在此處檢視原始碼。

現在,格式化程式的含義如下:

/*
   %a Abbreviated weekday name
   %A Full weekday name
   %b Abbreviated month name
   %B Full month name
   %c Date and time representation for your locale
   %d Day of month as a decimal number (01−31)
   %H Hour in 24-hour format (00−23)
   %I Hour in 12-hour format (01−12)
   %j Day of year as decimal number (001−366)
   %m Month as decimal number (01−12)
   %M Minute as decimal number (00−59)
   %p Current locale's A.M./P.M. indicator for 12−hour clock
   %S Second as decimal number (00−59)
   %U Week of year as decimal number,  Sunday as first day of week (00−51)
   %w Weekday as decimal number (0−6; Sunday is 0)
   %W Week of year as decimal number, Monday as first day of week (00−51)
   %x Date representation for current locale
   %X Time representation for current locale
   %y Year without century, as decimal number (00−99)
   %Y Year with century, as decimal number
   %z %Z Time-zone name or abbreviation, (no characters if time zone is unknown)
   %% Percent sign
   You can include text literals (such as spaces and colons) to make a neater display or for padding between adjoining columns.
   You can suppress the display of leading zeroes  by using the "#" character  (%#d, %#H, %#I, %#j, %#m, %#M, %#S, %#U, %#w, %#W, %#y, %#Y)
*/

因此,使用我們的%A、%B %d %Y %H:%M:%S格式方案,我們可以預期輸出類似於以下內容:星期日,2020年11月15日14:51:30。

現在,讓我們來看setup和loop。在setup中,我們初始化Serial,使用我們的WiFi連線到網際網路,並使用configTime()函式配置ESP32的內部RTC。正如您所看到的,該函式接受三個引數:gmtOffset、daylightOffset和ntpServer。它將從ntpServer獲取UTC時間,在本地應用gmtOffset和daylightOffset,並返回輸出時間。此函式與getLocalTime一樣,在esp32-hal-time.c檔案中定義。正如您從檔案中看到的,TCP/IP協議用於從NTP伺服器獲取時間。

一旦我們從NTP伺服器獲得時間並將其饋送到ESP32的內部RTC,我們就不再需要WiFi了。因此,我們斷開WiFi連線,並在迴圈中每秒保持列印時間。您可以在序列埠監視器上看到時間每列印一次就會增加一秒。這是因為ESP32的內部RTC在獲得參考時間後會維護時間。

void setup()
{
   Serial.begin(115200);
  
   //connect to WiFi
   Serial.printf("Connecting to %s ", ssid);
   WiFi.begin(ssid, password);
   while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
   }
   Serial.println(" CONNECTED");
  
   //init and get the time
   configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
   printLocalTime();

   //disconnect WiFi as it's no longer needed
   WiFi.disconnect(true);
   WiFi.mode(WIFI_OFF);
}
void loop() {
  delay(1000);
  printLocalTime();
}

序列埠監視器輸出將如下所示:

ESP32 NTP Sketch Output

就是這樣。您已經學習瞭如何從NTP伺服器獲取正確的時間並配置ESP32的內部RTC。現在,在您傳送到伺服器的任何資料包中,您可以新增時間戳。

參考文獻

廣告