# ArduinoCookbook2E.zip
解開後在 ch15/ch15r14 子目錄下就可以找到範例程式檔. 我將此兩範例檔壓縮在下列 NTP_Ethernet.zip 中 :
# https://www.dropbox.com/s/7qz2v39ph9tv06u/NTP_Ethernet.zip?dl=0
不過, 這個範例使用的是乙太網函式庫 Ethernet.h, 主要是支援以 WZ5100 晶片為主的 Ethernet 網路擴充板, 無法用在 ESP8266 的 WiFi 網路.
在 TCP/IP 模型中, NTP 協定屬於應用層協定, 使用 port 123, 是目前仍在使用中的古老協定之一, 其傳輸層使用 UDP, 這是一個不可靠的非連接性協定, 適用於需要反應較即時的應用. 關於 NTP 可參閱維基與鳥哥的大作 :
# 鳥哥的 Linux 私房菜 : 第十五章、時間伺服器: NTP 伺服器
# Wiki : 網路時間協定
# Wiki : Network Time Protocol
大致摘要一下 NTP 用到的知識 :
- GMT (格林威治時間) 與 UTC (國際標準時間) 雖然都是以格林威治時區為參考, 但準度不同, GMT 是太陽通過格林威治天文臺為基準, 使用於 1880 年以前; 而 UTC 則以原子鐘的銫原子震盪週期來計算, 兩者約有 16 分鐘的誤差.
- 台灣位於東經 120 度北緯 25 度, 時區為 CCT (中原標準時區), GMT+8.
- NTP 伺服器傳回的是 1900/1/1 以來的總秒數, 若要換算成 UNIX 自 1970/1/1 運作以來的秒數, 必須減掉 2208988800 秒.
# NTP Data Packet Structure of theNTP Data packet
在 ESP8266 的 AT 韌體中, UDP 訊息可以用 AT 指令 CIPSTART 來起始, 但這並不是建立連線, 而僅僅是註冊要傳送 IP 封包而已, 傳送資料要用 CIPSEND, 例如 :
AT+CIPSTART="UDP","129.6.15.28",123
AT+CIPSEND=48
但接下來要傳送甚麼資料給 NTP 伺服器呢? 下列這篇文章的範例似乎是要傳送 48 BYTES 的資料給 ESP8266 :
# Arduino ESP8266 and NTP
但我照著此文所述, 在 < 出現後傳送那幾個怪碼 (ãì 1N14) 給 ESP8266, 卻沒收到任何回應, 而作者的卻是回應如下 :
SEND OK
+IPD,48:$ ãACTSØ7×0Æêä
OK
難道這是編碼的問題嗎? 這篇文章也沒下文. 最後, 我在下面這篇找到可資參考範例, 是有回應了, 但還未成功 :
# NTP from ESP8266 via AT commands
此作者是參考 Arduino 論壇另一篇回應文章改寫的 :
# Arduino ESP8266 and NTP
從這兩篇可以進一步了解, 傳送給 NTP 伺服器的資料有 48 個位元組, 其中 Byte 0~3 以及 12~15 須填入特定的值. 而傳回的時間戳記 (time stamp, 1900/1/1 以來之秒數) 是放在回應字元的 Byte 40 (MSB)~43 (LSB).
作者使用 Arduino Mega 當主控, 以 Serial1 與 ESP8266 介接, 但我是用軟體序列埠, 故我把這個程式中的 Serial1 全部改成 sSerial, 並添加 SoftwareSerial sSerial(10,11) 指令, 以便能移植到我的 Nano + ESP8266 實驗麵包板上 :
/*
ESP8266 connected to sSerial
*/
#include <SoftwareSerial.h>
SoftwareSerial sSerial(10,11); //(RX,TX) 與 ESP8266 介接的軟體串列埠
void setup() {
// initialize both serial ports:
Serial.begin(9600);
sSerial.begin(9600);
Serial.println("...");
}
void loop() {
delay(3000);
Serial.println();
Serial.println("Getting time..");
int Epoch = GetTime();
Serial.print("Epoch is: ");
Serial.println(Epoch);
delay(5000);
}
const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
unsigned long GetTime() {
String cmd = "AT+CIPSTART=\"UDP\",\"130.102.128.23\",123"; // NTP server
sSerial.println(cmd);
delay(2000);
if(sSerial.find("Error")) {
Serial.print("RECEIVED: Error");
return 0;
}
int counta = 0;
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
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
packetBuffer[12]=49; //參考 ID (4 bytes)
packetBuffer[13]=0x4E;
packetBuffer[14]=49;
packetBuffer[15]=52;
sSerial.print("AT+CIPSEND=");
sSerial.println(NTP_PACKET_SIZE);
if (sSerial.find(">")) {
for (byte i = 0; i < NTP_PACKET_SIZE; i++) {
sSerial.write(packetBuffer[i]);
delay(5);
}
}
else{
sSerial.println("AT+CIPCLOSE");
return 0;
}
//sSerial.find("+IPD,48:");
int acksize = NTP_PACKET_SIZE + 1 + 2 + 8; // ESP8266 adds a space, a CRLF and starts with "+IPD,48:"
Serial.println("ESP2866 ACK : ");
for (byte i = 0; i < acksize; i++) {
while (sSerial.available() == 0) { // you may have to wait for some bytes
counta += 1;
Serial.print(".");
delay(100);
if (counta == 15) {return 0;}
}
byte ch = sSerial.read();
if (ch < 0x10) {Serial.print('0');}
Serial.print(ch,HEX);
Serial.print(' ');
if ( (((i+1) % 15) == 0) ) { Serial.println(); }
}
Serial.println();
Serial.println();
memset(packetBuffer, 0, NTP_PACKET_SIZE);
Serial.println("Server answer : ");
int i = 0;
while (sSerial.available() > 0) {
byte ch = sSerial.read();
if (i <= NTP_PACKET_SIZE) {
packetBuffer[i] = ch;
}
if (ch < 0x10) {Serial.print('0');}
Serial.print(ch,HEX);
Serial.print(' ');
if ((((i+1) % 15) == 0)) {Serial.println();}
delay(5);
i++;
if ((i < NTP_PACKET_SIZE) && (sSerial.available() == 0)) {
while (sSerial.available() == 0) { // you may have to wait for some bytes
counta += 1;
Serial.print("!");
delay(100);
if (counta == 15) {return 0;}
}
}
}
Serial.println();
Serial.println();
Serial.print(i+1);
Serial.println(" bytes received"); // will be more than 48
Serial.print(packetBuffer[40],HEX);
Serial.print(" ");
Serial.print(packetBuffer[41],HEX);
Serial.print(" ");
Serial.print(packetBuffer[42],HEX);
Serial.print(" ");
Serial.print(packetBuffer[43],HEX);
Serial.print(" = ");
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
Serial.print(secsSince1900,DEC);
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
// subtract seventy years:
unsigned long epoch = secsSince1900 - seventyYears;
unsigned long DST = 60*60*2; // adjust to your GMT+DST
unsigned long timestamp = epoch + DST;
Serial.println();
Serial.print("Epoch : ");
Serial.println(epoch,DEC);
return epoch;
}
此程式執行後, 序列埠監視視窗的輸出如下 :
...
Getting time..
ESP2866 ACK :
C6 00 06 EC 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 .0D 0A 53 45 4E 44 20 4F 4B 0D 0A
Server answer :
1 bytes received
0 0 0 0 = 0
Epoch : 2085978496
Epoch is: -32384
最後一列中小數點後面的資料之 ASCII 解碼如下 :
0D (CR)
0A (LF)
53 (S)
45 (E)
4E (N)
44 (D)
20 (SP)
4F (O)
4B (K)
0D (CR)
0A (LF)
但是從其輸出可知, 似乎沒有從 NTP 伺服器收到回應, 這是 NTP 伺服器的問題嗎? 我更改 IP 為 192.43.244.18 與 192.5.41.40 後有改善, 雖然大部分要求都沒有回應, 但偶而還是會收到伺服器回應, 例如 :
Getting time..
ESP2866 ACK :
C6 00 06 EC 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 ..0D 0A 53 45 4E 44 20 4F 4B 0D 0A
Server answer :
0D 0A 2B 49 50 44 2C 34 38 3A 24 01 06 E3 00
00 00 00 00 00 00 00 41 43 54 53 D9 E4 6E 2D
30 F4 36 DA 00 00 00 00 00 00 00 00 D9 E4 6E
51 80 26 3B F1 D9 E4 6E 51 80 27 8C 0A 4B
60 bytes received
0 0 D9 E4 = 55780
Epoch : 2086034276
Epoch is: 23396
但新的問題是, 此程式就算成功地收到 NTP 伺服器的回應, 其 Epoch 時間值都一樣不變, Why?
參考下列網址 :
# http://www.stdtime.gov.tw/chinese/bulletin/NTP%20promo.txt
# 台灣合用的ntp server
可用的 NTP 伺服器網址收集如下, 但不是每一個都有回應 :
192.43.244.18 time.nist.gov (ACTS)
132.163.135.130 time-A.timefreq.bldrdoc.gov (ACTS)
192.5.41.40 tick.usno.navy.mil
tick.stdtime.gov.tw
tock.stdtime.gov.tw
time.stdtime.gov.tw
clock.stdtime.gov.tw
watch.stdtime.gov.tw
另外, 有一位德國人把支援 NTP AT 指令的 0.9.4 韌體的函式寫成函式庫 :
# Arduino Zeitsynchronisation mit ESP8266 und NTP
其函式庫含範例可從 Git 下載 :
# https://drive.google.com/file/d/0B0j0q4jsSHdjMnVyajZZMXhvNTQ/view
而支援 NTP AT 指令的 0.9.4 版韌體可從下列網址下載 :
# https://drive.google.com/file/d/0B0j0q4jsSHdjMnVyajZZMXhvNTQ/view
如果 0.9.2 版的韌體在 NTP 網路對時上一直無法成功的話, 考慮找一顆 ESP8266 來燒錄 0.9.4 版韌體試試看, 此版本有為了 NTP 新增 AT 指令.
其他參考資料 :
# How do es work?
# 有無人玩開ESP8266 (超平WIFI SOC MODULE)呢?
# teos0009/ESP8266 Arduino Mega (有範例程式)
# Tutorial: IoT Datalogger with ESP8266 WiFi Module and FRDM-KL25Z
# A Guide To Using ESP8266 With TEENSY 3
# 鳥哥的 Linux 私房菜 : 第十五章、時間伺服器: NTP 伺服器
# NTP Request Packet
# Addition of NTP support for 0020000903/0020000904 (0.9.3 版韌體)
# Using an ESP8266 as a time source (Part 1) (0.9.3 版韌體)
# Using an ESP8266 as a time source (Part 2) (使用 NodeMCU)
# Display Internet based Time (NTP)
# UDP NTP CLIENT
# NTP DRIVER FOR ESP8266
# Richard's stuff (ESP8266 剖析)
# A SIMPLE NTP CLIENT FOR ESP8266
# REAL TIME CLOCK (DS1307/DS3231) FOR THE ESP8266
# Low-memory footprint, scheduler-friendly NTP client
# Arduino ESP8266 and NTP (Read 6501 times)
# Network Time Protocol (NTP) Client
# Arduino教學-使用ESP8266 wifi模組+DHT 溫溼度感測器上傳thingspeak
# 使用 Arduino IDE 開發 ESP8266 物聯網應用 - ThingSpeak, HTTP GET / POST 資料上傳方法
# Arduino 結合ESP8266將空氣資料送到Synology NAS(取代Thingspeak)
# 家庭環境自動監測管理器-Arduino nano與esp8266實作(韌體篇)
# UDP SERVER EXAMPLE
# A UDP example of ESP8266
# at_example_0020000903
# IoT, Internet Of Thing, ESP8266 開始玩了
# BROWSER CONTROLLED RASPBERRY PI CAMERA CAR
# http://lindr.org/IoT/ (ESP8266 整合開發板 Uninus)
# Uninus 無線Wifi 手機控制 繼電器模組 $410
# [第3代] Uninus 無線Wifi 手機控制 繼電器模組 支援Web/REST/MQTT IoT 智慧開關 小K $420
# 第3代 Uninus ESP8266 NodeMCU ESP-12E/07 最小系統 測試版 $230
# AT+CIPSTART bug when using UDP and DNS lookup
# help with esp8266
# Arduino ESP8266 and NTP (Read 6723 times)
# Help to Decipher Arduino Code
# https://forum.arduino.cc/index.php?topic=285188.0
# Arduino新款EEPROM函式庫小觀察
# Arduino 學習筆記
2016-06-08 補充 :
我已經在下列文章解決此問題 :
# 使用 ITEAD 的 WeeESP8266 函式庫進行網路對時
# 以 Arduino + ESP8266 物聯網模組重作 NTP 實驗 (二)
# 鳥哥的 Linux 私房菜 : 第十五章、時間伺服器: NTP 伺服器
# NTP Request Packet
# Addition of NTP support for 0020000903/0020000904 (0.9.3 版韌體)
# Using an ESP8266 as a time source (Part 1) (0.9.3 版韌體)
# Using an ESP8266 as a time source (Part 2) (使用 NodeMCU)
# Display Internet based Time (NTP)
# UDP NTP CLIENT
# NTP DRIVER FOR ESP8266
# Richard's stuff (ESP8266 剖析)
# A SIMPLE NTP CLIENT FOR ESP8266
# REAL TIME CLOCK (DS1307/DS3231) FOR THE ESP8266
# Low-memory footprint, scheduler-friendly NTP client
# Arduino ESP8266 and NTP (Read 6501 times)
# Network Time Protocol (NTP) Client
# Arduino教學-使用ESP8266 wifi模組+DHT 溫溼度感測器上傳thingspeak
# 使用 Arduino IDE 開發 ESP8266 物聯網應用 - ThingSpeak, HTTP GET / POST 資料上傳方法
# Arduino 結合ESP8266將空氣資料送到Synology NAS(取代Thingspeak)
# 家庭環境自動監測管理器-Arduino nano與esp8266實作(韌體篇)
# UDP SERVER EXAMPLE
# A UDP example of ESP8266
# at_example_0020000903
# IoT, Internet Of Thing, ESP8266 開始玩了
# BROWSER CONTROLLED RASPBERRY PI CAMERA CAR
# http://lindr.org/IoT/ (ESP8266 整合開發板 Uninus)
# Uninus 無線Wifi 手機控制 繼電器模組 $410
# [第3代] Uninus 無線Wifi 手機控制 繼電器模組 支援Web/REST/MQTT IoT 智慧開關 小K $420
# 第3代 Uninus ESP8266 NodeMCU ESP-12E/07 最小系統 測試版 $230
# AT+CIPSTART bug when using UDP and DNS lookup
# help with esp8266
# Arduino ESP8266 and NTP (Read 6723 times)
# Help to Decipher Arduino Code
# https://forum.arduino.cc/index.php?topic=285188.0
# Arduino新款EEPROM函式庫小觀察
# Arduino 學習筆記
2016-06-08 補充 :
我已經在下列文章解決此問題 :
# 使用 ITEAD 的 WeeESP8266 函式庫進行網路對時
2016-06-12 補充 :
直接使用 AT 指令查詢 NTP 的問題已經解決了, 詳見 :
小狐狸您好
回覆刪除我才剛剛玩arduino
我有個設想要利用arduino 做一個按照順序開燈可以打開電磁鎖的機關
目前我想做4-6個開關a b c d e f
這四個開關一開始都是關掉的
當按照順序從a b c d e f 依序打開燈 電磁鎖也可以打開
想請問這種按照順序的要如何寫出來了
謝謝您的答覆
Sorry, 今天收信才看到您的詢問, 我沒做過這種實驗, 不過按照順序判斷的需要使用有限狀態機, 可參考葉難這篇 :
回覆刪除http://yehnan.blogspot.tw/2012/02/arduinosimon-says.html
AT+RST
回覆刪除OK
WIFI DISCONNECT
bBֆQR⸮⸮⸮ȤSN⸮ȤRN⸮H⸮⸮O⸮⸮b(D⸮⸮aH⸮⸮⸮Z⸮H⸮HH⸮⸮⸮⸮5
AT+GMR
AT version:1.1.0.0(May 11 2016 18:09:56)
SDK version:1.5.4(baaeaebb)
compile time:Feb 24 2017 10:13:27
OK
WIFI CONNECTED
AT+CWMODE=3
OK
AT+CIPMUX=1
OK
AT+CIPSERVER=1,80
OK
WIFI GOT IP
AT+CIFSR
+CIFSR:APIP,"192.168.4.1"
+CIFSR:APMAC,"a2:20:a6:21:b7:c8"
+CIFSR:STAIP,"192.168.1.243"
+CIFSR:STAMAC,"a0:20:a6:21:b7:c8"
OK
請問我是這樣就停住了是發生什麼事情?
可能是資料太多記憶體洩漏嘞
回覆刪除