Top

本文章為舊版Blogger內容轉移,目前暫停維護

使用Nodemcu連接NTP伺服器更新內部時鐘並製成html網頁

一.使用器材、前情提要

二.程式碼部分

  • 函式庫請至GitHub下載最新版本即可
  • 如有註解不當或缺失歡迎留言更正

宣告部分

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
#include <TimeLib.h> #include <ESP8266WiFi.h> #include <WiFiUdp.h> #define NTP_PACKET_SIZE 48 #define update_delay 70 #define ntp_correct 2 //緩存區大小 訊息的前48個字節為NTP時間戳記 //每計數一次之等待秒數 //NTP延遲校正(單位s) const char* ssid="Home Wifi"; const char* password="12345678"; const char* ntpServerName="time.nist.gov"; //Wifi SSID //Wifi PassWord //NTP 伺服器名稱 byte packetBuffer[NTP_PACKET_SIZE]; int update_count = 99; String http_sent =""; String http_get =""; String w[]={"","Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; unsigned long GMT = 2208988800; //宣告緩存區 //更新時間用計數器 每100次loop更新一次時間 //http傳送字串 //http接收字串 //設定代號[註1] //接收到之時間 IPAddress ntpServerIP; WiFiUDP udp; WiFiServer server(80); //宣告一個IPAddress物件來存放NTP伺服器的IP //宣告一個UDP物件來傳送和接收封包 //宣告一個伺服器來接收埠上的訊息

函式宣告

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
unsigned long getUnixTime() {//取得NTP時間 WiFi.hostByName(ntpServerName, ntpServerIP); //獲取一個隨機的伺服器 Serial.print("Sending NTP packet..."); memset(packetBuffer, 0, NTP_PACKET_SIZE); //將緩存區每個byte設為0(清理緩衝區) //初始化請求所需的值 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; udp.beginPacket(ntpServerIP, 123); //埠123為網路時間協定通訊埠 udp.write(packetBuffer, NTP_PACKET_SIZE); //向NTP伺服器發送請求 udp.endPacket(); //NTP packet END delay(1500); //等待 int cb=udp.parsePacket(); //回傳接收到之位元數 unsigned long unix_time=0; //預設傳回 0, 表示未收到 NTP 回應 if (cb>0) { udp.read(packetBuffer, NTP_PACKET_SIZE); unsigned long highWord=word(packetBuffer[40], packetBuffer[41]); //將封包讀入緩存區 時間戳從接收到的封包的字節40開始 unsigned long lowWord=word(packetBuffer[42], packetBuffer[43]); //是四個字節或兩個字長 unsigned long secsSince1900=highWord << 16 | lowWord; //將四個字節組合成一個長整數 NTP時間(1900年1月1日以來的秒數) unix_time=secsSince1900 - 2208988800UL; //更新 unix_time(減掉70年的秒數 即1970年1月1日以來的秒數) } return unix_time; //回傳1970年1月1日以來的秒數 }

初始化

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
void setup() { Serial.begin(115200); Serial.println("\nConnecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { //等待連線 delay(500); Serial.print("."); } Serial.print("\nWiFi connected\nIP address: "); Serial.print("IP address: "); Serial.println(WiFi.localIP()); Serial.println("Starting UDP"); udp.begin(2390); //本地使用埠2390監聽UDP數據包 Serial.print("Local port: "); Serial.println(udp.localPort()); server.begin(); Serial.println("Web server started"); pinMode(LED_BUILTIN, OUTPUT); }

迴圈部分

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
void loop() { if (WiFi.status() != WL_CONNECTED) { //檢查連線 delay(2000); return; } WiFiClient client = server.available(); if (!client) { //未接收到http訊息要做的事 update_count+=1; if(update_count==100){update_count=0;} //計數達100次向伺服器獲取時間並歸零計數器 else{ delay(update_delay); return; } digitalWrite(LED_BUILTIN, LOW); //led on[註2] GMT=getUnixTime(); if(GMT!=0){ //檢查是否正確獲取時間 GMT=GMT + 28800 + ntp_correct; //GMT+8及延遲校正 Serial.print("Successed\nUnix time=" ); Serial.println(GMT); setTime(GMT); //以NTP時間更新內部時鐘 Serial.print((String)year() + "-" + (String)month() + "-" + (String)day()+ " "); Serial.print(w[weekday()] + " "); Serial.println((String)hour() + ":" + (String)minute() + ":" + (String)second()); digitalWrite(LED_BUILTIN, HIGH);//led off }else{ Serial.println("Failed"); } delay(100); return; } while(!client.available()){ delay(1); } //接收到資料要做的事 String http_get = client.readStringUntil('\r'); Serial.println(http_get); client.flush(); client.print("HTTP/1.1 200 OK\r\nContent-Type: text/ html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\n" + (String)year() + "-" + (String)month() + "-" + (String)day() + " " + w[weekday()] + " " + (String)hour() + ":" + (String)minute() + ":" + (String)second() + "< /html >\n"); delay(10); }
[註1]星期日為1依此類推
[註2]Nodemcu板上之LED燈為 High時滅燈 LOW時亮燈

實際執行結果如下

Connecting to 
Home Wifi
...
WiFi connected
IP address: IP address: 192.168.50.147
Starting UDP
Local port: 2390
Web server started
Sending NTP packet...Successed
Unix time=1518909722
2018-2-17 Sat 23:22:2
Sending NTP packet...Successed
Unix time=1518909731
2018-2-17 Sat 23:22:11
Sending NTP packet...Failed

三.結語

  • (本項待確認)ESP8266 內建 RTC 每 7 個小時 45 分會超出型態範圍,因此 7 個小時45分內一定要再跟 NTP 伺服器同步一次確保時間的準確性
  • 在NTP取得時間時有時會失敗,不知為何(等待不夠久?)
    • 在經過多次測試後發現按下Nodemcu板上方的Flash按鈕後有明顯改善可能是記憶體的問題(不確定)
  • 如果有先進知道可能的原因請不吝留言補充

再次感謝您的閱讀 ❤❤❤

沒有留言 :

張貼留言