从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。现在,在您发送到服务器的任何数据包中,您可以添加时间戳。

参考文献

广告