NTPClient
NTPClient
库为 Arduino 提供了连接 NTP 服务器并获取准确时间的功能。
项目地址:https://github.com/arduino-libraries/NTPClient
UTC时间戳计算工具
https://www.epochconverter.com/
核心函数
构造函数
NTPClient(UDP& udp)
- 功能:创建一个
NTPClient
对象,使用默认的 NTP 服务器pool.ntp.org
,无时间偏移,更新间隔为 60 秒。 - 示例:
#include <WiFiUdp.h>
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);
NTPClient(UDP& udp, long timeOffset)
- 功能:创建一个
NTPClient
对象,指定时间偏移量(以秒为单位)。 - 示例:
#include <WiFiUdp.h>
WiFiUDP ntpUDP;
long offset = 3600; // 偏移1小时
//long offset = 8 * 3600; // 北京时间,东八区,偏移8小时
NTPClient timeClient(ntpUDP, offset);
NTPClient(UDP& udp, const char* poolServerName)
- 功能:创建一个
NTPClient
对象,指定 NTP 服务器名称。 - 示例:
#include <WiFiUdp.h>
WiFiUDP ntpUDP;
const char* serverName = "europe.pool.ntp.org";
NTPClient timeClient(ntpUDP, serverName);
NTPClient(UDP& udp, IPAddress poolServerIP)
- 功能:创建一个
NTPClient
对象,指定 NTP 服务器的 IP 地址。 - 示例:
#include <WiFiUdp.h>
WiFiUDP ntpUDP;
IPAddress serverIP(192, 168, 1, 100);
NTPClient timeClient(ntpUDP, serverIP);
NTPClient(UDP& udp, const char* poolServerName, long timeOffset)
- 功能:创建一个
NTPClient
对象,指定 NTP 服务器名称和时间偏移量。 - 示例:
#include <WiFiUdp.h>
WiFiUDP ntpUDP;
const char* serverName = "europe.pool.ntp.org";
long offset = 3600;
NTPClient timeClient(ntpUDP, serverName, offset);
NTPClient(UDP& udp, IPAddress poolServerIP, long timeOffset)
- 功能:创建一个
NTPClient
对象,指定 NTP 服务器的 IP 地址和时间偏移量。 - 示例:
#include <WiFiUdp.h>
WiFiUDP ntpUDP;
IPAddress serverIP(192, 168, 1, 100);
long offset = 3600;
NTPClient timeClient(ntpUDP, serverIP, offset);
NTPClient(UDP& udp, const char* poolServerName, long timeOffset, unsigned long updateInterval)
- 功能:创建一个
NTPClient
对象,指定 NTP 服务器名称、时间偏移量和更新间隔(以毫秒为单位)。 - 示例:
#include <WiFiUdp.h>
WiFiUDP ntpUDP;
const char* serverName = "europe.pool.ntp.org";
long offset = 3600;
unsigned long interval = 120000; // 2分钟更新一次
NTPClient timeClient(ntpUDP, serverName, offset, interval);
NTPClient(UDP& udp, IPAddress poolServerIP, long timeOffset, unsigned long updateInterval)
- 功能:创建一个
NTPClient
对象,指定 NTP 服务器的 IP 地址、时间偏移量和更新间隔。 - 示例:
#include <WiFiUdp.h>
WiFiUDP ntpUDP;
IPAddress serverIP(192, 168, 1, 100);
long offset = 3600;
unsigned long interval = 120000;
NTPClient timeClient(ntpUDP, serverIP, offset, interval);
成员函数
void begin()
- 功能:启动底层 UDP 客户端,使用默认的本地端口
NTP_DEFAULT_LOCAL_PORT
(1337)。 - 示例:
timeClient.begin();
void begin(unsigned int port)
- 功能:启动底层 UDP 客户端,使用指定的本地端口。
- 示例:
unsigned int port = 1234;
timeClient.begin(port);
bool update()
- 功能:根据更新间隔检查是否需要从 NTP 服务器更新时间。如果需要更新,则进行更新并返回
true
;否则返回false
。 - 示例:
if (timeClient.update()) {
Serial.println("Time updated successfully");
} else {
Serial.println("Time not updated");
}
bool forceUpdate()
- 功能:强制从 NTP 服务器更新时间。更新成功返回
true
,失败返回false
。 - 示例:
if (timeClient.forceUpdate()) {
Serial.println("Forced time update successful");
} else {
Serial.println("Forced time update failed");
}
bool isTimeSet() const
- 功能:检查
NTPClient
是否成功接收 NTP 数据包并设置了时间。如果时间已设置,返回true
;否则返回false
。 - 示例:
if (timeClient.isTimeSet()) {
Serial.println("Time is set");
} else {
Serial.println("Time is not set");
}
int getDay() const
- 功能:获取当前是星期几(0 表示星期日)。
- 示例:
int day = timeClient.getDay();
Serial.print("Day of the week: ");
Serial.println(day);
int getHours() const
- 功能:获取当前小时数(0 - 23)。
- 示例:
int hours = timeClient.getHours();
Serial.print("Hours: ");
Serial.println(hours);
int getMinutes() const
- 功能:获取当前分钟数(0 - 59)。
- 示例:
int minutes = timeClient.getMinutes();
Serial.print("Minutes: ");
Serial.println(minutes);
int getSeconds() const
- 功能:获取当前秒数(0 - 59)。
- 示例:
int seconds = timeClient.getSeconds();
Serial.print("Seconds: ");
Serial.println(seconds);
String getFormattedTime() const
- 功能:返回格式化的时间字符串,格式为
hh:mm:ss
。有设置偏移的话,这个是偏移后的时间。 - 示例:
String formattedTime = timeClient.getFormattedTime();
Serial.println(formattedTime);
unsigned long getEpochTime() const
- 功能:返回自 1970 年 1 月 1 日 00:00:00 UTC 以来的秒数(Unix 时间戳)。如果设置了时间偏移量,该偏移量将被添加到时间戳中。
时间戳(timestamp)是一个绝对的时间表示,通常定义为从1970 年 1 月 1 日 00:00:00
UTC开始经过的秒数。时间戳与时区无关,无论你在哪个时区,同一时刻的时间戳都是相同的。北京时间的时间戳与 UTC 时间戳在同一时刻应该是完全相同的,不需要加上时区偏移。
注意:NTP库函数定义:getEpochTime()返回值 = UTC纪元时间戳 + 时间偏移量
- 示例:
unsigned long epochTime = timeClient.getEpochTime();
Serial.print("Epoch time: ");
Serial.println(epochTime);
UTC纪元时间戳
1.在线工具
https://www.epochconverter.com/
2.python计算
from datetime import datetime, timedelta, timezone
def beijing_time_to_utc_timestamp(date_str, time_str="00:00:00"):
"""
将北京时间(格式:YYYY.M.D)转换为UTC纪元时间戳
参数:
- date_str: 日期字符串,例如 "2025.3.5"
- time_str: 时间字符串,例如 "12:30:45"(默认00:00:00)
返回:
- utc_timestamp: UTC时间戳(秒级)
- utc_time_str: UTC时间字符串(格式:YYYY-MM-DD HH:MM:SS)
"""
try:
# 解析日期和时间
year, month, day = map(int, date_str.split('.'))
hour, minute, second = map(int, time_str.split(':'))
# 创建北京时间的时区对象(UTC+8)
beijing_tz = timezone(timedelta(hours=8))
# 创建带时区的北京时间对象
beijing_time = datetime(year, month, day, hour, minute, second, tzinfo=beijing_tz)
# 转换为UTC时间
utc_time = beijing_time.astimezone(timezone.utc)
# 计算UTC时间戳
utc_timestamp = int(utc_time.timestamp())
# 格式化为字符串
utc_time_str = utc_time.strftime("%Y-%m-%d %H:%M:%S")
return utc_timestamp, utc_time_str
except ValueError as ve:
print(f"输入格式错误: {ve}")
return None, None
except Exception as e:
print(f"发生未知错误: {e}")
return None, None
# 示例使用
if __name__ == "__main__":
# 用户输入案例
input_date = input("STEP 1/2:请输入日期(格式:年.月.日,例如:2025.3.5):")
input_time = input("STEP 2/2:请输入时间(格式:时:分:秒,例如:12:30:45,默认为00:00:00):")
# 处理默认时间
if not input_time.strip():
input_time = "00:00:00"
# 计算UTC时间戳和UTC时间
timestamp, utc_str = beijing_time_to_utc_timestamp(input_date, input_time)
# 打印结果
if timestamp is not None:
print(f"输入北京时间:{input_date} {input_time}")
print(f"对应的UTC时间:{utc_str}")
print(f"UTC纪元时间戳(注意:NTP库getEpochTime() = UTC纪元时间戳 + 时间偏移量):{timestamp}")
void end()
- 功能:停止底层 UDP 客户端。
- 示例:
timeClient.end();
void setTimeOffset(int timeOffset)
- 功能:设置时间偏移量(以秒为单位),可动态更改时区。
- 示例:
int offset = 7200; // 偏移2小时
timeClient.setTimeOffset(offset);
void setUpdateInterval(unsigned long updateInterval)
- 功能:设置时间更新间隔(以毫秒为单位)。
- 示例:
unsigned long interval = 180000; // 3分钟更新一次
timeClient.setUpdateInterval(interval);
void setPoolServerName(const char* poolServerName)
- 功能:设置 NTP 服务器名称。
- 示例:
const char* serverName = "asia.pool.ntp.org";
timeClient.setPoolServerName(serverName);
void setRandomPort(unsigned int minValue, unsigned int maxValue)
- 功能:设置一个随机的本地端口,端口范围在
minValue
到maxValue
之间。 - 示例:
unsigned int minPort = 49152;
unsigned int maxPort = 65535;
timeClient.setRandomPort(minPort, maxPort);
示例
格式化时间1
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
// WiFi网络信息
const char *ssid = "<SSID>";
const char *password = "<PASSWORD>";
// NTP服务器地址
const char* ntpServerName = "ntp.aliyun.com";
// 北京时间所在时区为UTC+8
const long gmtOffset_sec = 8 * 3600;
// 设置NTP更新最小时间间隔,参数单位为毫秒
const int daylightOffset_sec = 60000;
// 创建WiFiUDP对象
WiFiUDP ntpUDP;
// 创建NTPClient对象
NTPClient timeClient(ntpUDP, ntpServerName, gmtOffset_sec, daylightOffset_sec);
void setup() {
Serial.begin(115200);
// 连接 WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi");
// 初始化 NTP 客户端
timeClient.begin();
}
void loop() {
// 更新 NTP 时间
timeClient.update();
// 获取格式化后的时间字符串,HH:MM:SS
String formattedTime = timeClient.getFormattedTime();
// 打印原始格式化时间
Serial.print("Formatted Time: ");
Serial.println(formattedTime);
// 提取小时、分钟和秒
int hour, minute, second;
// 从格式化时间字符串中提取小时
hour = formattedTime.substring(0, 2).toInt();
// 从格式化时间字符串中提取分钟
minute = formattedTime.substring(3, 5).toInt();
// 从格式化时间字符串中提取秒
second = formattedTime.substring(6, 8).toInt();
// 打印提取的时间位
Serial.print("Hour: ");
Serial.print(hour);
Serial.print(", Minute: ");
Serial.print(minute);
Serial.print(", Second: ");
Serial.println(second);
// timeClient.getEpochTime()返回自 1970-01-01 00:00:00 UTC 以来的秒数(即Unix时间戳)
unsigned long epochTime = timeClient.getEpochTime();
Serial.print("Epoch Time: ");
Serial.println(epochTime);
delay(1000);
}
格式化时间2
#include <NTPClient.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
// WiFi网络信息
const char *ssid = "<SSID>";
const char *password = "<PASSWORD>";
// 创建WiFi UDP客户端
WiFiUDP ntpUDP;
// 创建NTPClient对象,使用默认的NTP服务器,东八区(偏移28800秒),更新间隔为60秒
NTPClient timeClient(ntpUDP, "pool.ntp.org", 28800, 60000);
// 定义中文星期数组
const char* weekDays[] = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
// 补零函数
String zeroPad(int num) {
return (num < 10) ? "0" + String(num) : String(num);
}
void setup() {
// 初始化串口通信
Serial.begin(115200);
// 连接WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
// 启动NTPClient
timeClient.begin();
}
void loop() {
// 更新时间
timeClient.update();
// 获取星期、小时、分钟和秒
int day = timeClient.getDay();
int hours = timeClient.getHours();
int minutes = timeClient.getMinutes();
int seconds = timeClient.getSeconds();
// 格式化输出
String formattedTime = weekDays[day] + String(" ") + zeroPad(hours) + ":" + zeroPad(minutes) + ":" + zeroPad(seconds);
Serial.println(formattedTime);
delay(1000); // 每秒更新一次
}
距离目标时间还有多久
#include <NTPClient.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
// WiFi网络信息
const char *ssid = "<SSID>";
const char *password = "<PASSWORD>";
// 创建WiFi UDP客户端
WiFiUDP ntpUDP;
// 创建NTPClient对象,使用默认的NTP服务器,东八区(偏移28800秒),更新间隔为60秒
NTPClient timeClient(ntpUDP, "pool.ntp.org", 28800, 60000);
// 2025年6月14日星期六凌晨12点00分 GMT+08:00
// 用https://www.epochconverter.com/在线工具计算时间戳
// 工具计算出的时间戳为UTC纪元时间戳,getEpochTime() = UTC纪元时间戳 + 时间偏移量
// 所以计算剩余时间,需要加上时区偏移
const unsigned long targetTime = 1749830400;
void setup() {
// 初始化串口通信
Serial.begin(115200);
// 连接WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
// 启动NTPClient
timeClient.begin();
}
void loop() {
// 更新时间
timeClient.update();
Serial.println(timeClient.getFormattedTime());
Serial.println(timeClient.getEpochTime());
// 获取当前时间戳
unsigned long currentTime = timeClient.getEpochTime();
// 计算剩余时间,计算时间差需要加上时区偏移
long remainingTime = targetTime - currentTime + 8 * 3600;
if (remainingTime > 0) {
// 显示剩余时间
Serial.print("Remaining time: ");
unsigned long days = remainingTime / (24 * 3600);
String daysStr = days < 10 ? "0" + String(days) : String(days);
Serial.print(daysStr);
Serial.print("d:");
remainingTime %= (24 * 3600);
unsigned long hours = remainingTime / 3600;
String hoursStr = hours < 10 ? "0" + String(hours) : String(hours);
Serial.print(hoursStr);
Serial.print("h:");
remainingTime %= 3600;
unsigned long minutes = remainingTime / 60;
String minuteStr = minutes < 10 ? "0" + String(minutes) : String(minutes);
Serial.print(minuteStr);
Serial.print("m:");
unsigned long seconds = remainingTime % 60;
String secondStr = seconds < 10 ? "0" + String(seconds) : String(seconds);
Serial.print(secondStr);
Serial.println("s");
} else {
Serial.println("Countdown finished!");
}
delay(1000); // 每秒更新一次
}