Arduino ESP32 获取网络时间方法

本文详细介绍了如何在ArduinoESP32上获取网络时间,并同步到RTC时钟。通过使用内建的库函数,连接WiFi并调用configTime,确保在获取时间时避免内存溢出问题。示例代码展示了从阿里云NTP服务器获取时间并进行格式化输出的方法,同时提供了一个网络同步到RTC的完整示例。

Arduino ESP32 获取网络时间方法


✨在 ArduinoESP32核心支持库当中已经包含相关的获取时间的库,所有获取网络时间,只需要连接好网络,调用相关的库函数即可实现NTP时间的获取,免去的额外加载扩展库的头文件。

  • 最容易掉坑的地方:
  • ✨在获取本地时间的时候,一定要先判断一下getLocalTime()布尔类型函数的返回值:
struct tm timeInfo; //声明一个结构体
  if (!getLocalTime(&timeInfo))
  { //一定要加这个条件判断,否则内存溢出
    Serial.println("Failed to obtain time");
    return;
  }
  • 🔖这一点和ESP8266库的类似API有写差异,只要申请了全局的时间结构体的变量,就随便可以直接调用本地时间。
  • 🌿如果想将访问到的本地时间,来作为OLED显示输出的话,读取本地时间特别要注意,很容易造成内存溢出,导致系统重启,所以操作相关的时间函数时,特别要小心。

📓示例程序代码一

#include <Arduino.h>
#include <WiFi.h>

#define NTP1 "ntp1.aliyun.com"
#define NTP2 "ntp2.aliyun.com"
#define NTP3 "ntp3.aliyun.com"
//填写WIFI入网信息
const char *ssid = "MERCURY_D268G";                                                                                // WIFI账户
const char *password = "pba5ayzk";                                                                                 // WIFI密码
const String WDAY_NAMES[] = {"星期天", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};                //星期
const String MONTH_NAMES[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; //月份

//time_t now; //实例化时间
void setClock()
{

  struct tm timeInfo; //声明一个结构体
  if (!getLocalTime(&timeInfo))
  { //一定要加这个条件判断,否则内存溢出
    Serial.println("Failed to obtain time");
    return;
  }
  //Serial.print(asctime(&timeInfo)); //默认打印格式:Mon Oct 25 11:13:29 2021
  String date = WDAY_NAMES[timeInfo.tm_wday];
  Serial.println(date.c_str());
  // sprintf_P(buff1, PSTR("%04d-%02d-%02d %s"), timeInfo.tm_year + 1900, timeInfo.tm_mon + 1, timeInfo.tm_mday, WDAY_NAMES[timeInfo.tm_wday].c_str());
  String shuju = String(timeInfo.tm_year + 1900); //年
  shuju += "-";
  shuju += timeInfo.tm_mon + 1; //月
  shuju += "-";
  shuju += timeInfo.tm_mday; //日
  shuju += " ";
  shuju += timeInfo.tm_hour; //时
  shuju += ":";
  shuju += timeInfo.tm_min;
  shuju += ":";
  shuju += timeInfo.tm_sec;
  shuju += " ";
  shuju += WDAY_NAMES[timeInfo.tm_wday].c_str(); //星期
  Serial.println(shuju.c_str());
}

void setup()
{
  Serial.begin(115200);
  Serial.println();
  //设置ESP32工作模式为无线终端模式
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi connected!");
  configTime(8 * 3600, 0, NTP1, NTP2, NTP3);
}

void loop()
{
  Serial.println("Waiting 10s before the next round...");
  delay(10000);
  setClock();
  // printLocalTime();
}
  • 📜串口打印输出
    在这里插入图片描述

📝示例程序代码二

  • 🔖来自固件自带例程的时间同步,最终都是通过调用configTime函数实现。
#include <WiFi.h>
#include "time.h"//固件自带
#include "sntp.h"//固件自带

const char* ssid       = "#######";//填写个人WIFI信息
const char* password   = "*******";

const char* ntpServer1 = "ntp.aliyun.com";
const char* ntpServer2 = "ntp2.aliyun.com";
const char* ntpServer3 = "time.nist.gov";
const long  gmtOffset_sec = 28800;//时区偏移:8*60*60
const int   daylightOffset_sec = 0;

const char* time_zone = "CET-1CEST,M3.5.0,M10.5.0/3";  // TimeZone rule for Europe/Rome including daylight adjustment rules (optional)

void printLocalTime()
{
  struct tm timeinfo;
  if(!getLocalTime(&timeinfo)){// 获取本地时间
    Serial.println("No time available (yet)");
    return;
  }
  Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}

//回调函数(通过NTP调整时间时调用)
void timeavailable(struct timeval *t)
{
  Serial.println("Got time adjustment from NTP!");
  printLocalTime();
}

void setup()
{
  Serial.begin(115200);

  // 设置回调功能
  sntp_set_time_sync_notification_cb( timeavailable );

  /**
   * NTP server address could be aquired via DHCP,
   *
   * NOTE: This call should be made BEFORE esp32 aquires IP address via DHCP,
   * otherwise SNTP option 42 would be rejected by default.
   * NOTE: configTime() function call if made AFTER DHCP-client run
   * will OVERRIDE aquired NTP server address
   */
  sntp_servermode_dhcp(1);    // (optional)

  /**
   * This will set configured ntp servers and constant TimeZone/daylightOffset
   * should be OK if your time zone does not need to adjust daylightOffset twice a year,
   * in such a case time adjustment won't be handled automagicaly.
   */
 // configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1, ntpServer2);
 configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1, ntpServer2,ntpServer3);
  /**
   * A more convenient approach to handle TimeZones with daylightOffset 
   * would be to specify a environmnet variable with TimeZone definition including daylight adjustmnet rules.
   * A list of rules for your zone could be obtained from https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h
   */
  //configTzTime(time_zone, ntpServer1, ntpServer2);

  //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");

}

void loop()
{
  delay(5000);
  printLocalTime();     // it will take some time to sync time :)
}

在这里插入图片描述

📘查询本地时间,如果获取失败,就从网络NTP服务器获取时间并存储到本地。
  • 🍁常用代码实现框架:
#include <WiFi.h>
#include <time.h>

void setup() {
  Serial.begin(115200);

  // 连接到WiFi网络
  WiFi.begin("your_SSID", "your_PASSWORD");
  while (WiFi.status()!= WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }

  // 配置NTP服务器
  configTime(0, 0, "pool.ntp.org", "time.nist.gov");//可以替换其他NTP服务器地址

  // 获取本地时间
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
  }

  // 打印本地时间
  Serial.print("Local time: ");
  Serial.print(asctime(&timeinfo));
}

void loop() {
  // 主循环中不需要做任何事情,因为时间已经在setup中获取并打印
}

  • 测试代码:
#include <WiFi.h>

#define NTP  "ntp.aliyun.com"	

//填写自己的WIFI信息
const char *ssid = "#######";
const char *password = "********";

void wifi_init(){
  
  WiFi.mode(WIFI_STA);//配置ESP32 工作模式
  WiFi.begin(ssid, password);
  Serial.println("正在连接 WiFi.");
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi connected!");

}

void time_init() {
  struct tm timeinfo;  // 定义时间信息
  //如果获取失败,就开启联网模式,获取时间
  if (!getLocalTime(&timeinfo)){// 获取本地时间
    Serial.println("获取时间失败");
    //开启网络  
    wifi_init();
    // 从网络时间服务器上获取并设置时间
    configTime(8 * 3600, 0, NTP);//时区,夏令时,NTP地址
    return;
  }
  // 格式化输出:2021-10-24 23:00:44 Sunday
  Serial.println(&timeinfo, "%F %T %A"); 
  //   WiFi.disconnect(true);//在不需要开启网络的情况下,可以主动断开网络连接。
}

void setup(){
  Serial.begin(115200);
  wifi_init();

}

void loop()
{
  time_init();
  delay(1000);
}

📓示例程序代码三


#ifdef ESP32
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>
//  #include <WiFiClient.h>//3.0.2新增
//  #include <ESP8266HTTPClient.h>
#endif
// 获取网络时间相关库
#include <NTPClient.h>//需要自行搜索并安装此库
#include <WiFiUdp.h>//固件自带

const char* ssid       = "########";//填写个人WIFI信息
const char* password   = "********";

const char* ntpServer = "ntp.aliyun.com";
const char* ntpServer2 = "ntp2.aliyun.com";
const char* ntpServer3 = "time.nist.gov";
const long gmtOffset = 28800; // 如果需要校准时区,可以根据需要进行调整
const int daylightOffset = 0;

uint32_t targetTime = 0;  // for next 1 second timeout

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, ntpServer, gmtOffset, daylightOffset);

void setup() {
  // put your setup code here, to run once:
// 连接WiFi网络
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  
  // 启动NTP客户端
  timeClient.begin();
  targetTime = millis() + 1000;
}

void loop() {
  // put your main code here, to run repeatedly:
    if (targetTime < millis()) {
    targetTime = millis() + 1000;
// 获取NTP时间
  timeClient.update();
  // 获取NTP时间的小时、分钟和秒
  int hours = timeClient.getHours();
  int minutes = timeClient.getMinutes();
  int seconds = timeClient.getSeconds();
  printf("%02d:%02d:%02d\r\n",hours,minutes,seconds);
    }
 
}

在这里插入图片描述

### Arduino ESP32 使用 NTP 协议获取网络时间的示例代码 以下是基于 Arduino 平台实现 ESP32 使用 NTP 协议同步网络时间的一个完整示例代码: ```cpp #include <WiFi.h> #include <WiFiUdp.h> const char* ssid = "your_SSID"; // 替换为您的 Wi-Fi SSID const char* password = "your_PASSWORD"; // 替换为您的 Wi-Fi 密码 unsigned int localPort = 2390; // UDP 端口号用于接收来自服务器的时间戳 IPAddress timeServerIP; // 时间服务器 IP 地址变量 const char* ntpServer = "pool.ntp.org"; // NTP 服务器地址 const int timeZone = 8; // 设置时区 (UTC+8) WiFiUDP udp; void setup() { Serial.begin(115200); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { // 连接至 Wi-Fi 网络 delay(500); Serial.print("."); } Serial.println(""); Serial.println("Connected to the WiFi network"); Serial.println(WiFi.localIP()); getNTPTime(); // 获取当前网络时间并打印到串口监视器 } void loop() { delay(10000); // 每隔 10 秒重新请求一次时间 getNTPTime(); } // 函数:发送 NTP 请求包给指定的 NTP 服务器 void sendNTPpacket(IPAddress& address) { byte packetBuffer[48]; // 创建缓冲区存储数据包 memset(packetBuffer, 0, 48); // 清零缓冲区 // 填充 NTP 数据包头部字段 packetBuffer[0] = 0b11100011; // LI, Version, Mode 字段设置 packetBuffer[1] = 0; // Stratum 字段清零 packetBuffer[2] = 6; // Poll Interval 字段设置 packetBuffer[3] = 0xEC; // Peer Clock Precision 字段设置 (-2^14) // 将本机时间填充到第 40 到 47 字节位置(仅作为占位符) unsigned long localEpoch = millis() / 1000; packetBuffer[40] = 0; packetBuffer[41] = 0; packetBuffer[42] = 0; packetBuffer[43] = 0; packetBuffer[44] = 0; packetBuffer[45] = 0; packetBuffer[46] = 0; packetBuffer[47] = 0; udp.beginPacket(address, 123); // 向目标地址端口 123 发送数据包 udp.write(packetBuffer, 48); udp.endPacket(); } // 函数:解析返回的数据包并计算 UTC 时间 String processNTPResponse(unsigned long highWord, unsigned long lowWord) { const unsigned long seventyYears = 2208988800UL; // 自 1900-01-01 至 1970-01-01 的秒数差 unsigned long epoch = ((highWord << 16) | lowWord) - seventyYears; return String(epoch + timeZone * 3600); // 考虑时区偏移量后的 Unix 时间戳字符串表示形式 } // 主要功能函数:通过 NTP 协议获取当前时间 void getNTPTime() { udp.begin(localPort); // 开启本地 UDP 端口监听 WiFi.hostByName(ntpServer, timeServerIP); // 解析域名为主机 IP 地址 sendNTPpacket(timeServerIP); // 向时间服务器发送请求包 uint32_t beginWait = millis(); while (millis() - beginWait < 1500) { // 等待最多 1.5 秒钟响应 int cb = udp.parsePacket(); if (!cb) continue; IPAddress remoteIp = udp.remoteIP(); // 接收远程主机发回的消息 Serial.printf("Received %d bytes from %s\n", cb, remoteIp.toString().c_str()); udp.read((byte*)&buffer, sizeof(buffer)); // 读取消息内容 unsigned long secsSince1900; memcpy(&secsSince1900, buffer + 40, sizeof(secsSince1900)); secsSince1900 &= 0xffffffff; // 取低 32 位有效部分 Serial.println(processNTPResponse(secsSince1900 >> 16, secsSince1900 & 0xffff), DEC); break; } } ``` 上述代码实现了以下功能: - 配置 Wi-Fi 参数以便连接互联网。 - 定义了一个 `sendNTPpacket` 方法来构建标准 NTP 请求报文[^1]。 - 实现了 `processNTPResponse` 来处理从 NTP 服务器收到的时间戳,并将其转换成可理解的形式。 #### 注意事项 为了使程序正常运行,需替换掉代码中的 `"your_SSID"` 和 `"your_PASSWORD"` 为您实际使用的无线路由器名称及其密码。此外,在某些地区可能需要调整 `timeZone` 数值以匹配当地的标准时间偏差。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值