很多时候,我们使用ESP32的时候,没有RTC时钟芯片辅助,那么就需要一个外部时钟源,来提供准确的时间,以便进行对时。
经过一番了解,可以使用NTP服务器来进行对时。
最终完成的实际代码如下:
/* 基于NTP服务器的互联网时钟
*/
#include <Arduino.h>
#include <stdlib.h>
// 时间库
#include <TimeLib.h>
// WiFi库
#include <WiFi.h>
#include <WiFiMulti.h>
#include <WiFiUdp.h>
#include <Wire.h>
// OLED库
#include <Adafruit_SSD1306.h>
#include <U8g2_for_Adafruit_GFX.h>
// OLED定义
// 屏幕大小定义
#define SCREEN_WIDTH 128 // 宽度
#define SCREEN_HEIGHT 64 // 高度
// OLED I2C定义
#define OLED_RESET -1 // RESET
#define SCREEN_ADDRESS 0x3C // I2C地址
// Adafruit_SSD1306显示对象
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// U8G2_FOR_ADAFRUIT_GFX显示对象
U8G2_FOR_ADAFRUIT_GFX u8g2_for_adafruit_gfx;
// WiFi定义
#define WIFI_SSID "OpenBSD"
#define WIFI_PASS "13581882013"
WiFiMulti WiFiMulti;
// NTP定义
static const char ntpServerName[] = "ntp.aliyun.com";
const int timeZone = 8; // 时区
// 通讯数据包定义
const int NTP_PACKET_SIZE = 48;
byte packetBuffer[NTP_PACKET_SIZE];
// UDP定义
WiFiUDP Udp;
unsigned int localPort = 8888;
// NTP方法和对象定义
time_t getNtpTime();
// 时间显示状态
time_t prevDisplay = 0;
/**
* showtime 显示时间
**/
void showtime() {
int Hour = hour();
int Min = minute();
int Sec = second();
int HourHigh, HourLow, MinHigh, MinLow, SecHigh, SecLow;
char buff[50];
sprintf(buff, "%02d:%02d:%02d", Hour, Min, Sec);
u8g2_for_adafruit_gfx.setFont(u8g2_font_fur20_tn); // 中文字体
u8g2_for_adafruit_gfx.setCursor(8, 52); // start writing at this position
u8g2_for_adafruit_gfx.print(buff);
}
/**
* getNtpTime NTP对时
**/
time_t getNtpTime() {
IPAddress ntpServerIP; // NTP 服务器地址
while (Udp.parsePacket() > 0)
; // 丢弃任何先前接收的数据包
// 发起NTP请求
Serial.println("Transmit NTP Request");
WiFi.hostByName(ntpServerName, ntpServerIP); // 获取IP
Serial.print(ntpServerName);
Serial.print(": ");
Serial.println(ntpServerIP);
sendNTPpacket(ntpServerIP); // 发送数据包
uint32_t beginWait = millis();
while (millis() - beginWait < 1500) {
int size = Udp.parsePacket();
if (size >= NTP_PACKET_SIZE) {
Serial.println("Receive NTP Response");
Udp.read(packetBuffer, NTP_PACKET_SIZE); // 读取数据包
unsigned long secsSince1900;
// 转换数据
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
secsSince1900 |= (unsigned long)packetBuffer[43];
// 返回数据
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
}
}
Serial.println("No NTP Response :-(");
return 0;
}
/**
* sendNTPpacket 发送NTP数据包
**/
void sendNTPpacket(IPAddress &address) {
// 清理buff
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// 生成请求数据
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// 发送NTP数据包
Udp.beginPacket(address, 123);
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
}
void setup() {
// 串口初始化
Serial.begin(115200);
// 通讯初始化
Wire.begin();
// OLED初始化
Serial.println(F("SSD1306 init:"));
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;)
; // 循环
} else {
Serial.println(F("SSD1306 allocation ok"));
}
// 显示默认 Adafruit splash screen
display.display();
delay(1000);
// 关联u8g8到Adafruit GFX
u8g2_for_adafruit_gfx.begin(display);
// WiFi连接处理
WiFiMulti.addAP(WIFI_SSID, WIFI_PASS);
while (WiFiMulti.run() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
// 开启UDP
Serial.println("Starting UDP");
Udp.begin(localPort);
Serial.print("Local port: ");
Serial.println(localPort);
Serial.println("waiting for sync");
// 设置NTP对时
setSyncProvider(getNtpTime);
setSyncInterval(300);
// 延时
delay(500);
}
void loop() {
// 显示时间
if (timeStatus() != timeNotSet) { // 检查更新最新时间
if (now() != prevDisplay) {
prevDisplay = now();
// 清除显示缓存
display.clearDisplay();
// 画框
display.drawRoundRect(0, 0, display.width() - 1, 16, 1, SSD1306_WHITE); // 状态栏
display.drawRoundRect(0, 16, display.width() - 1, display.height() - 16, 1, SSD1306_WHITE); // 内容区
u8g2_for_adafruit_gfx.setFontDirection(0); // 显示方向:从左到右(默认)
u8g2_for_adafruit_gfx.setForegroundColor(WHITE); // 应用Adafruit GFX颜色
u8g2_for_adafruit_gfx.setFont(u8g2_font_unifont_t_chinese2); // 中文字体
u8g2_for_adafruit_gfx.setCursor(25, 12); // 显示坐标
u8g2_for_adafruit_gfx.print("Net Clock");
showtime(); // 显示时间
// 显示
display.display();
}
}
// 延时
delay(50);
}
以上代码在Arduino中进行开发;
在上述代码中:
在setup()初始化阶段:
在loop()主循环阶段:
另外,几个主要的函数功能:
getNtpTime()函数,用于NTP对时
sendNTPpacket()函数,用于发送NTP数据包
实际测试验证中,使用了一块ESP32-E的开发板,一块SSD 1306 128x64 的OLED,具体效果如下:
视频效果如下:
Leader Qiao is niubility
详细