RTC 或實(shí)時(shí)時(shí)鐘是電子和嵌入式設(shè)備中最常用的模塊,用于跟蹤時(shí)間。但是 RTC 的問(wèn)題是計(jì)算機(jī)中的微芯片不是那么準(zhǔn)確,它們只能提供本地設(shè)備的時(shí)間。另一方面,使用互聯(lián)網(wǎng)從 NTP 服務(wù)器獲取時(shí)間是獲取時(shí)間的更好解決方案,因?yàn)樗鼫?zhǔn)確并且可以提供世界上任何地理區(qū)域的時(shí)間。我們只需要一個(gè) Wi-Fi 模塊并訪問(wèn)互聯(lián)網(wǎng)即可通過(guò)使用 NTP 服務(wù)器獲取世界上任何位置的時(shí)間。在本教程中,我們將使用 ESP8266 NodeMCU 從 NTP 服務(wù)器獲取當(dāng)前時(shí)間和日期,并將其顯示在 OLED 顯示屏上。
網(wǎng)絡(luò)時(shí)間協(xié)議 (NTP)
NTP 是用于在計(jì)算機(jī)網(wǎng)絡(luò)之間同步時(shí)鐘的最古老的網(wǎng)絡(luò) Internet 協(xié)議 (IP) 之一。它由特拉華大學(xué)的 David L. Mills 于 1981 年設(shè)計(jì)。該協(xié)議可用于在幾毫秒內(nèi)將許多網(wǎng)絡(luò)與協(xié)調(diào)世界時(shí) (UTC)同步。UTC 是世界調(diào)節(jié)時(shí)鐘和時(shí)間的主要時(shí)間標(biāo)準(zhǔn)。UTC 不會(huì)因不同的地理位置而改變和變化。NTP 使用 UTC 作為時(shí)間參考,并通過(guò) Internet 提供準(zhǔn)確和同步的時(shí)間。
NTP 工作在分層的客戶端-服務(wù)器模型上。頂級(jí)型號(hào)具有稱為“stratum0”的參考時(shí)鐘,如原子鐘、無(wú)線電波、GPS、GSM,可從衛(wèi)星接收時(shí)間。從stratum0接收時(shí)間的服務(wù)器稱為“stratum1”,從stratum1接收時(shí)間的服務(wù)器稱為“stratum2”,依此類推。這種情況繼續(xù)下去,時(shí)間的準(zhǔn)確性在每個(gè)階段之后都在下降。NTP 自動(dòng)選擇幾個(gè)可用時(shí)間源中最好的一個(gè)進(jìn)行同步,這使其具有容錯(cuò)能力。
所以在這個(gè)項(xiàng)目中,我們使用 ESP8266 NodeMCU 從 NTP 服務(wù)器獲取時(shí)間并將其顯示在 OLED 顯示器上。
ESP8266 可以使用互聯(lián)網(wǎng)訪問(wèn) NTP 服務(wù)器以獲取準(zhǔn)確的時(shí)間。這里 NTP 工作在客戶端-服務(wù)器模式,ESP8266 作為客戶端設(shè)備使用 UDP(用戶數(shù)據(jù)報(bào)協(xié)議)與NTP 服務(wù)器連接。客戶端向 NTP 服務(wù)器發(fā)送一個(gè)請(qǐng)求包,然后 NTP 發(fā)送一個(gè)時(shí)間戳包,其中包含準(zhǔn)確度、時(shí)區(qū)、UNIX 時(shí)間戳等信息。然后客戶端將日期和時(shí)間詳細(xì)信息分離出來(lái),可以根據(jù)需要在應(yīng)用程序中進(jìn)一步使用。
所需組件
單色 7 針 SSD1306 0.96” OLED 顯示屏
ESP8266 NodeMCU
微型 USB 數(shù)據(jù)線
面包板
公對(duì)公跳線
電路圖和連接
這款 7 針 OLED 顯示器使用 SPI 協(xié)議與 ESP8266 模塊通信,下面是連接 OLED SPI 針與 NodeMCU 以顯示互聯(lián)網(wǎng)時(shí)間的電路圖和連接表。
代碼說(shuō)明
首先,我們必須將 NTP 庫(kù)下載并安裝到 ESP8266 中。有許多可用于 NTP 客戶端的庫(kù)。您可以從 Arduino IDE 安裝其中的任何一個(gè)。在本教程中,我安裝了Taranais 的 NTPClient 庫(kù),因?yàn)樗子谑褂貌⑶揖哂袕?NTP 服務(wù)器獲取日期和時(shí)間的功能。
要安裝 NTP 庫(kù),首先使用上面的鏈接下載庫(kù),然后使用 Arduino IDE 安裝它。要安裝它,請(qǐng)轉(zhuǎn)到Sketch 》 Include Library 》 Add .ZIP Library,然后通過(guò)轉(zhuǎn)到您下載 zip 文件夾的位置打開(kāi) Zip 文件夾并重新啟動(dòng) Arduino IDE。
NTPClient 庫(kù)附帶示例。打開(kāi) Arduino IDE 并轉(zhuǎn)到示例 》 NTPClient 》 高級(jí)。此草圖中給出的代碼在串行監(jiān)視器上顯示來(lái)自 NTP 服務(wù)器的時(shí)間。我們將使用此草圖在 OLED 顯示屏上顯示當(dāng)前時(shí)間和日期。
本教程末尾提供了完整的代碼,這里我解釋了代碼的幾個(gè)重要部分。
ESP8266WiFi 庫(kù)提供 ESP8266 特定的 Wi-Fi 例程以連接到網(wǎng)絡(luò)。WiFiUDP.h處理發(fā)送和接收 UDP 包。由于我們使用 SPI 協(xié)議將 OLED 與 NodeMCU 連接,因此我們將導(dǎo)入“SPI.h”庫(kù)。而“Adafruit_GFX.h”和“Adafruit_SSD1306.h”用于OLED顯示。
#include#include // 提供我們調(diào)用的 ESP8266 特定 Wi-Fi 例程以連接到網(wǎng)絡(luò) #include // 處理 UDP 包的發(fā)送和接收 #include // 用于將 OLED 與 NodeMCU 連接的 SPI #include #include
我們的 OLED 尺寸為 128x64,因此我們將屏幕寬度和高度分別設(shè)置為 128 和 64。因此,為連接到 NodeMCU 的 OLED 引腳定義變量以進(jìn)行 SPI 通信。
#define SCREEN_WIDTH 128 // OLED 顯示寬度,以像素為單位 #define SCREEN_HEIGHT 64 // OLED 顯示高度,以像素為單位 // 使用軟件 SPI 連接 SSD1306 顯示器的聲明(默認(rèn)情況): #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";
通過(guò)向WiFi.begin函數(shù)提供 SSID 和密碼來(lái)設(shè)置 WI-Fi 連接。ESP8266 的連接需要一些時(shí)間才能連接到 NodeMCU,所以我們必須等到它連接上。
WiFi.begin(ssid, 密碼); 而(WiFi.status()!= WL_CONNECTED){ 延遲(500); Serial.print("."); }
要請(qǐng)求日期和時(shí)間,請(qǐng)使用 NTP 服務(wù)器的地址初始化時(shí)間客戶端。為了獲得更好的準(zhǔn)確性,請(qǐng)選擇靠近您的地理區(qū)域的 NTP 服務(wù)器的地址。在這里,我們使用“ pool.ntp.org ”,它提供來(lái)自世界各地的服務(wù)器。如果您想從亞洲選擇服務(wù)器,您可以使用“ asia.pool.ntp.org ”。timeClient還采用您的時(shí)區(qū)的 UTC 時(shí)間偏移量(以毫秒為單位)。例如,印度的 UTC 偏移量為 +5:30,因此我們以毫秒為單位轉(zhuǎn)換此偏移量,等于 5*60*60+30*60 = 19800。
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 19800,60000);
SSD1306_SWITCHCAPVCC在內(nèi)部產(chǎn)生 3.3V 電壓來(lái)初始化顯示。當(dāng) OLED 啟動(dòng)時(shí),它會(huì)顯示“WELCOME TO CIRCUIT DIGEST”,文字大小為 2,顏色為藍(lán)色,持續(xù) 3 秒。
if(!display.begin(SSD1306_SWITCHCAPVCC)) { Serial.println(F("SSD1306 分配失敗")); 為了(;;); // 不要繼續(xù),永遠(yuǎn)循環(huán) } display.clearDisplay(); display.setTextSize(2); // 繪制 2X 比例的文本 display.setTextColor(BLUE); display.setCursor(5, 2); display.println("歡迎來(lái)到"); display.println("電路"); display.println("摘要"); 顯示.顯示(); 延遲(3000);
NTP 客戶端使用begin()函數(shù)初始化,以從 NTP 服務(wù)器設(shè)置日期和時(shí)間。
timeClient.begin();
Update()函數(shù)用于在我們向 NTP 服務(wù)器請(qǐng)求時(shí)接收日期和時(shí)間。
timeClient.update();
波特率設(shè)置為 115200 以在串行監(jiān)視器上打印時(shí)間。
序列號(hào).開(kāi)始(115200); Serial.println(timeClient.getFormattedTime());
getHours()、getMinutes()、getSeconds()、getDay是庫(kù)函數(shù),并從 NTP 服務(wù)器提供當(dāng)前的小時(shí)、分鐘、秒和天。下面的代碼用于區(qū)分 AM 和 PM 之間的時(shí)間。如果我們使用getHours()獲得的小時(shí)數(shù)大于 12,那么我們將該時(shí)間設(shè)置為 PM,否則設(shè)置為 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 服務(wù)器獲取“yyyy-mm-dd”格式的日期。此函數(shù)以“yyyy-mm-dd T hh:mm:ss格式給出日期和時(shí)間。但是我們只需要日期,因此我們必須將這個(gè)以date_time格式存儲(chǔ)的字符串拆分為由substring()函數(shù)完成的“T”,然后將日期存儲(chǔ)在“date”變量中。
date_time = timeClient.getFormattedDate(); int index_date = date_time.indexOf("T"); 字符串日期 = date_time.substring(0, index_date); 序列號(hào).println(日期); display.println(日期); 顯示.顯示();
這就是OLED 互聯(lián)網(wǎng)時(shí)鐘最終的樣子:
#include
#include
#include
#include
#include
#include
#define SCREEN_WIDTH 128 // OLED 顯示寬度,以像素為單位
#define SCREEN_HEIGHT 64 // OLED 顯示高度,以像素為單位
// 使用軟件 SPI 連接的 SSD1306 顯示器的聲明(默認(rèn)情況):
#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[]={"星期日","星期一","星期二","星期三","星期四","星期五","星期六"};
字符串日期時(shí)間;
// 您可以指定時(shí)間服務(wù)器池和偏移量(以秒為單位,
// 稍后可以使用 setTimeOffset() 更改)。另外,您可以指定
// 更新間隔(以毫秒為單位,可以使用 setUpdateInterval() 更改)。
無(wú)效設(shè)置(){
Serial.begin(115200);
WiFi.begin(ssid, 密碼);
而(WiFi.status()!= WL_CONNECTED){
延遲(500);
Serial.print(".");
}
if(!display.begin(SSD1306_SWITCHCAPVCC))
{
Serial.println(F("SSD1306 分配失敗"));
為了(;;); // 不要繼續(xù),永遠(yuǎn)循環(huán)
}
display.clearDisplay();
display.setTextSize(2); // 繪制 2X 比例的文本
display.setTextColor(WHITE);
display.setCursor(5, 2);
display.println("歡迎來(lái)到");
display.println("電路");
display.println("摘要");
顯示.顯示();
延遲(3000);
timeClient.begin();
}
無(wú)效循環(huán)(){
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);
序列號(hào).println(日期);
display.println(日期);
顯示.顯示();// 顯示初始文本
}
-
OLED
+關(guān)注
關(guān)注
119文章
6202瀏覽量
224279 -
NTP
+關(guān)注
關(guān)注
1文章
170瀏覽量
13907 -
ESP8266
+關(guān)注
關(guān)注
50文章
962瀏覽量
45064
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論