RTC 或实时时钟是电子和嵌入式设备中最常用的模块,用于跟踪时间。但是 RTC 的问题是计算机中的微芯片不是那么准确,它们只能提供本地设备的时间。另一方面,使用互联网从 NTP 服务器获取时间是获取时间的更好解决方案,因为它更准确并且可以提供世界上任何地理区域的时间。我们只需要一个 Wi-Fi 模块并访问互联网即可通过使用 NTP 服务器获取世界上任何位置的时间。在本教程中,我们将使用 ESP8266 NodeMCU 从 NTP 服务器获取当前时间和日期,并将其显示在 OLED 显示屏上。
网络时间协议 (NTP)
NTP 是用于在计算机网络之间同步时钟的最古老的网络 Internet 协议 (IP) 之一。它由特拉华大学的 David L. Mills 于 1981 年设计。该协议可用于在几毫秒内将许多网络与协调世界时 (UTC)同步。UTC 是世界调节时钟和时间的主要时间标准。UTC 不会因不同的地理位置而改变和变化。NTP 使用 UTC 作为时间参考,并通过 Internet 提供准确和同步的时间。
NTP 工作在分层的客户端-服务器模型上。顶级型号具有称为“stratum0”的参考时钟,如原子钟、无线电波、GPS、GSM,可从卫星接收时间。从stratum0接收时间的服务器称为“stratum1”,从stratum1接收时间的服务器称为“stratum2”,依此类推。这种情况继续下去,时间的准确性在每个阶段之后都在下降。NTP 自动选择几个可用时间源中最好的一个进行同步,这使其具有容错能力。
所以在这个项目中,我们使用 ESP8266 NodeMCU 从 NTP 服务器获取时间并将其显示在 OLED 显示器上。
ESP8266 可以使用互联网访问 NTP 服务器以获取准确的时间。这里 NTP 工作在客户端-服务器模式,ESP8266 作为客户端设备使用 UDP(用户数据报协议)与NTP 服务器连接。客户端向 NTP 服务器发送一个请求包,然后 NTP 发送一个时间戳包,其中包含准确度、时区、UNIX 时间戳等信息。然后客户端将日期和时间详细信息分离出来,可以根据需要在应用程序中进一步使用。
所需组件
单色 7 针 SSD1306 0.96” OLED 显示屏
ESP8266 NodeMCU
微型 USB 数据线
面包板
公对公跳线
电路图和连接
这款 7 针 OLED 显示器使用 SPI 协议与 ESP8266 模块通信,下面是连接 OLED SPI 针与 NodeMCU 以显示互联网时间的电路图和连接表。
代码说明
首先,我们必须将 NTP 库下载并安装到 ESP8266 中。有许多可用于 NTP 客户端的库。您可以从 Arduino IDE 安装其中的任何一个。在本教程中,我安装了Taranais 的 NTPClient 库,因为它易于使用并且具有从 NTP 服务器获取日期和时间的功能。
要安装 NTP 库,首先使用上面的链接下载库,然后使用 Arduino IDE 安装它。要安装它,请转到Sketch 》 Include Library 》 Add .ZIP Library,然后通过转到您下载 zip 文件夹的位置打开 Zip 文件夹并重新启动 Arduino IDE。
NTPClient 库附带示例。打开 Arduino IDE 并转到示例 》 NTPClient 》 高级。此草图中给出的代码在串行监视器上显示来自 NTP 服务器的时间。我们将使用此草图在 OLED 显示屏上显示当前时间和日期。
本教程末尾提供了完整的代码,这里我解释了代码的几个重要部分。
ESP8266WiFi 库提供 ESP8266 特定的 Wi-Fi 例程以连接到网络。WiFiUDP.h处理发送和接收 UDP 包。由于我们使用 SPI 协议将 OLED 与 NodeMCU 连接,因此我们将导入“SPI.h”库。而“Adafruit_GFX.h”和“Adafruit_SSD1306.h”用于OLED显示。
#include#include // 提供我们调用的 ESP8266 特定 Wi-Fi 例程以连接到网络 #include // 处理 UDP 包的发送和接收 #include // 用于将 OLED 与 NodeMCU 连接的 SPI #include #include
我们的 OLED 尺寸为 128x64,因此我们将屏幕宽度和高度分别设置为 128 和 64。因此,为连接到 NodeMCU 的 OLED 引脚定义变量以进行 SPI 通信。
#define SCREEN_WIDTH 128 // OLED 显示宽度,以像素为单位 #define SCREEN_HEIGHT 64 // OLED 显示高度,以像素为单位 // 使用软件 SPI 连接 SSD1306 显示器的声明(默认情况): #define OLED_MOSI D7 #define OLED_CLK D5 #define OLED_DC D2 #define OLED_CS D8 #define OLED_RESET D3
Adafruit_SSD1306 显示器(SCREEN_WIDTH、SCREEN_HEIGHT、OLED_MOSI、OLED_CLK、OLED_DC、OLED_RESET、OLED_CS);
在下面的代码行中,将“your_ssid”和“your_password”替换为您的 Wi-Fi SSID 和密码。
const char *ssid = "your_ssid"; const char *password = "your_password";
通过向WiFi.begin函数提供 SSID 和密码来设置 WI-Fi 连接。ESP8266 的连接需要一些时间才能连接到 NodeMCU,所以我们必须等到它连接上。
WiFi.begin(ssid, 密码); 而(WiFi.status()!= WL_CONNECTED){ 延迟(500); Serial.print("."); }
要请求日期和时间,请使用 NTP 服务器的地址初始化时间客户端。为了获得更好的准确性,请选择靠近您的地理区域的 NTP 服务器的地址。在这里,我们使用“ pool.ntp.org ”,它提供来自世界各地的服务器。如果您想从亚洲选择服务器,您可以使用“ asia.pool.ntp.org ”。timeClient还采用您的时区的 UTC 时间偏移量(以毫秒为单位)。例如,印度的 UTC 偏移量为 +5:30,因此我们以毫秒为单位转换此偏移量,等于 5*60*60+30*60 = 19800。
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 19800,60000);
SSD1306_SWITCHCAPVCC在内部产生 3.3V 电压来初始化显示。当 OLED 启动时,它会显示“ WELCOME TO CIRCUIT DIGEST ”,文字大小为 2,颜色为蓝色,持续 3 秒。
if(!display.begin(SSD1306_SWITCHCAPVCC)) { Serial.println(F("SSD1306 分配失败")); 为了(;;); // 不要继续,永远循环 } display.clearDisplay(); display.setTextSize(2); // 绘制 2X 比例的文本 display.setTextColor(BLUE); display.setCursor(5, 2); display.println("欢迎来到"); display.println("电路"); display.println("摘要"); 显示.显示(); 延迟(3000);
NTP 客户端使用begin()函数初始化 ,以从 NTP 服务器设置日期和时间。
timeClient.begin();
Update()函数用于在我们向 NTP 服务器请求时接收日期和时间。
timeClient.update();
波特率设置为 115200 以在串行监视器上打印时间。
序列号.开始(115200); Serial.println(timeClient.getFormattedTime());
getHours()、getMinutes()、getSeconds()、getDay是库函数,并从 NTP 服务器提供当前的小时、分钟、秒和天。下面的代码用于区分 AM 和 PM 之间的时间。如果我们使用getHours()获得的小时数大于 12,那么我们将该时间设置为 PM,否则设置为 AM。
int hh = timeClient.getHours(); int mm = timeClient.getMinutes(); int ss = timeClient.getSeconds(); int day = timeClient.getDay(); 如果(hh>12) { hh=hh-12; 显示.打印(hh); display.print(":"); 显示.打印(毫米); display.print(":"); 显示.打印(ss); display.println("PM"); } 其他 { display.print(hh); display.print(":"); 显示.打印(毫米); display.print(":"); 显示.打印(ss); display.println("AM"); } int day = timeClient.getDay(); display.println("'"+arr_days[天]+"'");
getFormattedDate()用于从 NTP 服务器获取“yyyy-mm-dd”格式的日期。此函数以“yyyy-mm-dd T hh:mm:ss格式给出日期和时间。但是我们只需要日期,因此我们必须将这个以date_time格式存储的字符串拆分为由substring()函数完成的“T”,然后将日期存储在“date”变量中。
date_time = timeClient.getFormattedDate(); int index_date = date_time.indexOf("T"); 字符串日期 = date_time.substring(0, index_date); 序列号.println(日期); display.println(日期); 显示.显示();
这就是OLED 互联网时钟最终的样子:
#include
#include
#include
#include
#include
#include
#define SCREEN_WIDTH 128 // OLED 显示宽度,以像素为单位
#define SCREEN_HEIGHT 64 // OLED 显示高度,以像素为单位
// 使用软件 SPI 连接的 SSD1306 显示器的声明(默认情况):
#define OLED_MOSI D7
#define OLED_CLK D5
#define OLED_DC D2
#define OLED_CS D8
#define OLED_RESET D3
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT,OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);
const char *ssid = "CircuitLoop";
const char *password = "circuitdigest101";
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 19800,60000);
字符串 arr_days[]={"星期日","星期一","星期二","星期三","星期四","星期五","星期六"};
字符串日期时间;
// 您可以指定时间服务器池和偏移量(以秒为单位,
// 稍后可以使用 setTimeOffset() 更改)。另外,您可以指定
// 更新间隔(以毫秒为单位,可以使用 setUpdateInterval() 更改)。
无效设置(){
Serial.begin(115200);
WiFi.begin(ssid, 密码);
而(WiFi.status()!= WL_CONNECTED){
延迟(500);
Serial.print(".");
}
if(!display.begin(SSD1306_SWITCHCAPVCC))
{
Serial.println(F("SSD1306 分配失败"));
为了(;;); // 不要继续,永远循环
}
display.clearDisplay();
display.setTextSize(2); // 绘制 2X 比例的文本
display.setTextColor(WHITE);
display.setCursor(5, 2);
display.println("欢迎来到");
display.println("电路");
display.println("摘要");
显示.显示();
延迟(3000);
timeClient.begin();
}
无效循环(){
timeClient.update();
display.clearDisplay();
Serial.println(timeClient.getFormattedTime());
display.setTextSize(2); // 绘制 2X 比例的文本
display.setTextColor(BLUE);
display.setCursor(0, 2);
int hh = timeClient.getHours();
int mm = timeClient.getMinutes();
int ss = timeClient.getSeconds();
如果(hh>12)
{
hh=hh-12;
显示.打印(hh);
display.print(":");
显示.打印(毫米);
display.print(":");
显示.打印(ss);
display.println("PM");
}
其他
{
display.print(hh);
display.print(":");
显示.打印(毫米);
display.print(":");
显示.打印(ss);
display.println("AM");
}
int day = timeClient.getDay();
display.println("'"+arr_days[天]+"'");
date_time = timeClient.getFormattedDate();
int index_date = date_time.indexOf("T");
字符串日期 = date_time.substring(0, index_date);
序列号.println(日期);
display.println(日期);
显示.显示();// 显示初始文本
}
全部0条评论
快来发表一下你的评论吧 !