# 利用 NTP 伺服器來同步 Arduino 系統時鐘 (一)
下面是針對其中的測試 4 進行修改, 主要修改處是 sync_clock() 函數中 getUnixTime() 的傳回值需要加上 8 小時 (28800 秒) 才是台灣時間的秒數, 因為 getUnixTime() 傳回的是格林威治 (GMT) 自 1970/1/1 以來之秒數, 台灣是 GMT+8.
注意, 編譯下面程式前必須下載 Time 與 TimeAlarm 這兩個函式庫 :
# https://github.com/PaulStoffregen/Time
# https://github.com/PaulStoffregen/TimeAlarms
解壓縮後將 Time 與 TimeAlarm 兩個子目錄複製到 Arduino IDE 安裝目錄下的 libraries 下即可. 注意, libraries\Time\Time 這樣會抓不到函式庫, 正確為 libraries\Time. 其函式列表如下 :
函式 | 說明 |
hour() | 傳回現在的時 (24 小時制) |
hourFormat12() | 傳回現在的時 (12小時制) |
minute() | 傳回現在的分 |
second() | 傳回現在的秒 |
year() | 傳回現在的年 |
month() | 傳回現在的月 |
day() | 傳回現在的日 |
weekday() | 傳回現在的星期 (星期日為 1) |
其傳回值均為整數.
測試 4-1 :
#include <SoftwareSerial.h>
#define DEBUG true
#include <Time.h>
#include <TimeAlarms.h>
SoftwareSerial esp8266(7,8); //(RX,TX)
const String ssid="H30-L02-webbot";
const String pwd="1234567890";
void setup() {
Serial.begin(9600);
esp8266.begin(9600);
sendData(F("AT+RST\r\n"),2000,DEBUG); // reset ESP8266
while (!connectWifi(ssid, pwd)) {
Serial.println(F("Connecting WiFi ... failed"));
delay(2000);
}
sendData(F("AT+GMR\r\n"),1000,DEBUG);
delay(3000); //wait for wifi connection to get local ip
sendData(F("AT+CIFSR\r\n"),1000,DEBUG); //get ip address
Serial.println(F("Sending request to NTP server ..."));
sync_clock();
Alarm.timerRepeat(60, sync_clock);
}
void loop() {
Serial.println(getDateTime());
Alarm.delay(1000);
}
void sync_clock() {
setTime(getUnixTime() + 28800L);
}
String getDateTime() { //傳回日期時間
String dt=(String)year() + "-";
byte M=month();
if (M < 10) {dt.concat('0');}
dt.concat(M);
dt.concat('-');
byte d=day();
if (d < 10) {dt.concat('0');}
dt.concat(d);
dt.concat(' ');
byte h=hour();
if (h < 10) {dt.concat('0');}
dt.concat(h);
dt.concat(':');
byte m=minute();
if (m < 10) {dt.concat('0');}
dt.concat(m);
dt.concat(':');
byte s=second();
if (s < 10) {dt.concat('0');}
dt.concat(s);
return dt; //傳回格式如 2016-07-16 16:09:23 的日期時間字串
}
unsigned long getUnixTime() {
//NTP Server candidates : 91.226.136.136 82.209.243.241 192.5.41.40
sendData(F("AT+CIPSTART=\"UDP\",\"91.226.136.136\",123\r\n"),5000,DEBUG);
byte packetBuffer[48]; //packet buffer : send/recv data to/from NTP
memset(packetBuffer,0,48); //clear packet buffer
//configure packet buffer for requesting NTP server
packetBuffer[0]=0xE3; //LI, Version, Mode
packetBuffer[1]=0x00; //Stratum, or type of clock
packetBuffer[2]=0x06; //Polling Interval
packetBuffer[3]=0xEC; //Peer Clock Precision
packetBuffer[12]=0x31; //reference ID (4 bytes)
packetBuffer[13]=0x4E;
packetBuffer[14]=0x31;
packetBuffer[15]=0x34;
//send 48-bytes request to NTP Server
sendData(F("AT+CIPSEND=48\r\n"),1000,DEBUG); //send data length
for (byte i=0; i < 48; i++) { //sending byte by byte
esp8266.write(packetBuffer[i]);
delay(1); //wait for writing
}
//processing NTP response
memset(packetBuffer,0,48); //clear buffer to store NTP response
Serial.println();
if (esp8266.available() > 0) { //if receive NTP response
if (esp8266.find("+IPD,48:")) { //search for IPD marker
Serial.println(F("'+IPD,48:' found, NTP server answered :"));
Serial.println();
for (byte i=0; i<48; i++) { //read following 48 bytes from serial buffer
byte ch=esp8266.read(); //read one byte each time
packetBuffer[i]=ch; //store byte packet buffer
//show receving bytes in hex
if (ch < 0x10) {Serial.print(F("0"));} //prefix with "0" if byte value 0~9
Serial.print(ch, HEX);
Serial.print(F(" ")); //space between each byte
if ((((i+1) % 10) == 0)) {Serial.println();} //newline if exceeds 10 bytes
delay(1); //wait for next incoming byte
if ((i < 48) && (esp8266.available() == 0)) { //wait if receiving lags
//time packets not complete but no response : wait 1.5 seconds
byte wcount=0; //waiting counter
while (esp8266.available() == 0) { //loop until timeout (1.5 seconds)
Serial.print(F("!")); //show ! means waiting for response packet
delay(100);
wcount += 1; //increment waiting counter
if (wcount >= 15) {break;} //waiting timeout : quit loop
} //end of while
} //end of if
} //end of for
} //end of if
} //end of if
sendData(F("AT+CIPCLOSE\r\n"),1000,DEBUG); //close session
Serial.println();
Serial.println();
//Show time stamp (locates at index 32~35 of the response packets)
Serial.print(F("NTP time stamp packets (byte 32~35)="));
Serial.print(packetBuffer[32],HEX);
Serial.print(F(" "));
Serial.print(packetBuffer[33],HEX);
Serial.print(F(" "));
Serial.print(packetBuffer[34],HEX);
Serial.print(F(" "));
Serial.print(packetBuffer[35],HEX);
Serial.println();
//combine 4 bytes time packets into words
unsigned long highWord=word(packetBuffer[32],packetBuffer[33]);
unsigned long lowWord=word(packetBuffer[34],packetBuffer[35]);
//shift high word 16 bits left, OR with low word to form a double word
//the result is a NTP time stamp (seconds since Jan 1 1900):
unsigned long secsSince1900=highWord << 16 | lowWord;
Serial.print(F("NTP time stamp (seconds since 1900-01-01)="));
Serial.println(secsSince1900);
//convert NTP time stamp to Unix time stamp :
//Unix time starts on Jan 1 1970=2208988800 seconds since 1900-01-01
//subtract seventy years to get Unix time stamp
unsigned long unix_time=secsSince1900 - 2208988800UL;
Serial.print(F("Unix time stamp (seconds since 1970-01-01)="));
Serial.println(unix_time); //print Unix time
return unix_time; //return seconds since 1970-01-01
}
boolean connectWifi(String ssid, String pwd) {
String res=sendData("AT+CWJAP=\"" + ssid + F("\",\"") + pwd + F("\"\r\n"),8000,DEBUG);
res.replace("\r\n",""); //remove all line terminator
if (res.indexOf("OK") != -1) {return true;}
else {return false;}
}
String sendData(String command, const int timeout, boolean debug) {
String response="";
esp8266.print(command); // send the read character to the esp8266
long int time=millis();
while ((time+timeout) > millis()) {
while(esp8266.available()) {
// The esp has data so display its output to the serial window
char c=esp8266.read(); // read the next character.
response += c;
}
}
if (debug) {Serial.print(response);}
return response;
}
此程式編譯後全域變數僅耗掉約 1/4 :
草稿碼使用了 12,752 bytes (41%) 的程式存儲空間。最大值為 30,720 bytes。
全域變數使用了 500 bytes (24%) 的動態記憶體,剩餘 1,548 bytes 供局部變數。最大值為 2,048 bytes 。
在 "利用 NTP 伺服器來同步 Arduino 系統時鐘 (一)" 的測試 4 則耗去 41%, 當然這也要歸功於使用了 F() 來節省 SRAM 的耗用情形, 代價是程式記憶體消耗量從 35% 提升到 41%.
序列埠監控視窗擷取訊息如下 :
AT+GMR
0018000902
OK
AT+CIFSR
192.168.43.151
OK
Sending request to NTP server ...
AT+CIPSTART="UDP","91.226.136.136",123
OK
AT+CIPSEND=48
>
'+IPD,48:' found, NTP server answered :
24 02 06 ED 00 00 00 21 00 00
07 F2 6B DC 0B 87 DB 4E C0 61
93 8E 58 2E 00 00 00 00 00 00
00 00 DB 4E C5 E7 29 F8 29 79
DB 4E C5 E7 2A 7E 27 66
NTP time stamp packets (byte 32~35)=DB 4E C5 E7
NTP time stamp (seconds since 1900-01-01)=3679372775
Unix time stamp (seconds since 1970-01-01)=1470383975
2016-08-05 15:59:35
2016-08-05 15:59:36
2016-08-05 15:59:37
2016-08-05 15:59:38
2016-08-05 15:59:39
2016-08-05 15:59:40
2016-08-05 15:59:41
2016-08-05 15:59:42
2016-08-05 15:59:43
2016-08-05 15:59:44
2016-08-05 15:59:45
2016-08-05 15:59:46
2016-08-05 15:59:47
2016-08-05 15:59:48
2016-08-05 15:59:49
2016-08-05 15:59:50
2016-08-05 15:59:51
2016-08-05 15:59:52
2016-08-05 15:59:53
2016-08-05 15:59:54
2016-08-05 15:59:55
2016-08-05 15:59:56
2016-08-05 15:59:57
2016-08-05 15:59:58
2016-08-05 15:59:59
2016-08-05 16:00:00
2016-08-05 16:00:01
2016-08-05 16:00:02
2016-08-05 16:00:03
2016-08-05 16:00:04
2016-08-05 16:00:05
2016-08-05 16:00:06
2016-08-05 16:00:07
2016-08-05 16:00:08
2016-08-05 16:00:09
2016-08-05 16:00:10
2016-08-05 16:00:11
2016-08-05 16:00:12
2016-08-05 16:00:13
2016-08-05 16:00:14
2016-08-05 16:00:15
2016-08-05 16:00:16
2016-08-05 16:00:17
2016-08-05 16:00:18
2016-08-05 16:00:19
2016-08-05 16:00:20
2016-08-05 16:00:21
2016-08-05 16:00:22
2016-08-05 16:00:23
2016-08-05 16:00:24
2016-08-05 16:00:25
2016-08-05 16:00:26
2016-08-05 16:00:27
2016-08-05 16:00:28
2016-08-05 16:00:29
2016-08-05 16:00:30
2016-08-05 16:00:31
2016-08-05 16:00:32
2016-08-05 16:00:33
2016-08-05 16:00:34
OK
AT+CIPSTART="UDP","91.226.136.136",123
ALREAY CONNECT
AT+CIPSEND=48
>
'+IPD,48:' found, NTP server answered :
24 02 06 ED 00 00 00 21 00 00
08 34 6B DC 0B 87 DB 4E C0 61
93 8E 58 2E 00 00 00 00 00 00
00 00 DB 4E C6 2A 10 E3 30 0B
DB 4E C6 2A 10 ED 53 26
NTP time stamp packets (byte 32~35)=DB 4E C6 2A
NTP time stamp (seconds since 1900-01-01)=3679372842
Unix time stamp (seconds since 1970-01-01)=1470384042
2016-08-05 16:00:42
2016-08-05 16:00:43
2016-08-05 16:00:44
2016-08-05 16:00:45
2016-08-05 16:00:46
2016-08-05 16:00:47
2016-08-05 16:00:48
2016-08-05 16:00:49
.......
接著來幫下面這篇文章的程式改版 :
# 利用 NTP 伺服器來同步 Arduino 系統時鐘 (二)
首先是將原來的測試 6 改為 6-1 如下, 這篇是用 1602 LCD 液晶顯示器顯示日期時間與星期 :
測試 6-1 :
#include <SoftwareSerial.h>
#define DEBUG true
#include <Time.h>
#include <TimeAlarms.h>
#include <LiquidCrystal.h>
#define RS 2
#define E 3
#define D4 10
#define D5 11
#define D6 12
#define D7 13
SoftwareSerial esp8266(7,8); //(RX,TX)
const String ssid="H30-L02-webbot";
const String pwd="1234567890";
LiquidCrystal lcd(RS,E,D4,D5,D6,D7); //create LCD object
void setup() {
Serial.begin(9600);
esp8266.begin(9600);
sendData(F("AT+RST\r\n"),2000,DEBUG); // reset ESP8266
while (!connectWifi(ssid, pwd)) {
Serial.println(F("Connecting WiFi ... failed"));
delay(2000);
}
sendData(F("AT+GMR\r\n"),1000,DEBUG);
delay(3000); //wait for wifi connection to get local ip
sendData(F("AT+CIFSR\r\n"),1000,DEBUG); //get ip address
Serial.println(F("Sending request to NTP server ..."));
sync_clock();
Alarm.timerRepeat(60, sync_clock);
lcd.begin(16,2); //define 2*16 LCD
lcd.clear(); //clear screen
}
void loop() {
String d=getDate();
String t=getTime();
String w=getWeek();
Serial.println(d + " " + t + " " + w);
lcd.setCursor(0,0); //move to (x,y)
lcd.print(d); //print date
lcd.setCursor(11,0); //move to (x,y)
lcd.print(w); //print date
lcd.setCursor(0,1); //move to (x,y)
lcd.print(t); //print date
Alarm.delay(1000);
}
void sync_clock() {
setTime(getUnixTime() + 28800L);
}
String getDate() {
String d=(String)year() + "-";
byte M=month();
if (M < 10) {d.concat('0');}
d.concat(M);
d.concat('-');
byte D=day();
if (D < 10) {d.concat('0');}
d.concat(D);
return d;
}
String getTime() {
String t="";
byte h=hour();
if (h < 10) {t.concat('0');}
t.concat(h);
t.concat(':');
byte m=minute();
if (m < 10) {t.concat('0');}
t.concat(m);
t.concat(':');
byte s=second();
if (s < 10) {t.concat('0');}
t.concat(s);
return t;
}
String getWeek() {
String w[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
return w[weekday()-1];
}
unsigned long getUnixTime() {
//NTP Server candidates : 91.226.136.136 82.209.243.241 192.5.41.40
sendData(F("AT+CIPSTART=\"UDP\",\"91.226.136.136\",123\r\n"),5000,DEBUG);
byte packetBuffer[48]; //packet buffer : send/recv data to/from NTP
memset(packetBuffer,0,48); //clear packet buffer
//configure packet buffer for requesting NTP server
packetBuffer[0]=0xE3; //LI, Version, Mode
packetBuffer[1]=0x00; //Stratum, or type of clock
packetBuffer[2]=0x06; //Polling Interval
packetBuffer[3]=0xEC; //Peer Clock Precision
packetBuffer[12]=0x31; //reference ID (4 bytes)
packetBuffer[13]=0x4E;
packetBuffer[14]=0x31;
packetBuffer[15]=0x34;
//send 48-bytes request to NTP Server
sendData(F("AT+CIPSEND=48\r\n"),1000,DEBUG); //send data length
for (byte i=0; i < 48; i++) { //sending byte by byte
esp8266.write(packetBuffer[i]);
delay(1); //wait for writing
}
//processing NTP response
memset(packetBuffer,0,48); //clear buffer to store NTP response
Serial.println();
if (esp8266.available() > 0) { //if receive NTP response
if (esp8266.find("+IPD,48:")) { //search for IPD marker
Serial.println(F("'+IPD,48:' found, NTP server answered :"));
Serial.println();
for (byte i=0; i<48; i++) { //read following 48 bytes from serial buffer
byte ch=esp8266.read(); //read one byte each time
packetBuffer[i]=ch; //store byte packet buffer
//show receving bytes in hex
if (ch < 0x10) {Serial.print(F("0"));} //prefix with "0" if byte value 0~9
Serial.print(ch, HEX);
Serial.print(F(" ")); //space between each byte
if ((((i+1) % 10) == 0)) {Serial.println();} //newline if exceeds 10 bytes
delay(1); //wait for next incoming byte
if ((i < 48) && (esp8266.available() == 0)) { //wait if receiving lags
//time packets not complete but no response : wait 1.5 seconds
byte wcount=0; //waiting counter
while (esp8266.available() == 0) { //loop until timeout (1.5 seconds)
Serial.print(F("!")); //show ! means waiting for response packet
delay(100);
wcount += 1; //increment waiting counter
if (wcount >= 15) {break;} //waiting timeout : quit loop
} //end of while
} //end of if
} //end of for
} //end of if
} //end of if
sendData(F("AT+CIPCLOSE\r\n"),1000,DEBUG); //close session
Serial.println();
Serial.println();
//Show time stamp (locates at index 32~35 of the response packets)
Serial.print(F("NTP time stamp packets (byte 32~35)="));
Serial.print(packetBuffer[32],HEX);
Serial.print(F(" "));
Serial.print(packetBuffer[33],HEX);
Serial.print(F(" "));
Serial.print(packetBuffer[34],HEX);
Serial.print(F(" "));
Serial.print(packetBuffer[35],HEX);
Serial.println();
//combine 4 bytes time packets into words
unsigned long highWord=word(packetBuffer[32],packetBuffer[33]);
unsigned long lowWord=word(packetBuffer[34],packetBuffer[35]);
//shift high word 16 bits left, OR with low word to form a double word
//the result is a NTP time stamp (seconds since Jan 1 1900):
unsigned long secsSince1900=highWord << 16 | lowWord;
Serial.print(F("NTP time stamp (seconds since 1900-01-01)="));
Serial.println(secsSince1900);
//convert NTP time stamp to Unix time stamp :
//Unix time starts on Jan 1 1970=2208988800 seconds since 1900-01-01
//subtract seventy years to get Unix time stamp
unsigned long unix_time=secsSince1900 - 2208988800UL;
Serial.print(F("Unix time stamp (seconds since 1970-01-01)="));
Serial.println(unix_time); //print Unix time
return unix_time; //return seconds since 1970-01-01
}
boolean connectWifi(String ssid, String pwd) {
String res=sendData("AT+CWJAP=\"" + ssid + F("\",\"") + pwd + F("\"\r\n"),8000,DEBUG);
res.replace("\r\n",""); //remove all line terminator
if (res.indexOf("OK") != -1) {return true;}
else {return false;}
}
String sendData(String command, const int timeout, boolean debug) {
String response="";
esp8266.print(command); // send the read character to the esp8266
long int time=millis();
while ((time+timeout) > millis()) {
while(esp8266.available()) {
// The esp has data so display its output to the serial window
char c=esp8266.read(); // read the next character.
response += c;
}
}
if (debug) {Serial.print(response);}
return response;
}
此程式編譯後記憶體耗用情形 :
草稿碼使用了 14,032 bytes (45%) 的程式存儲空間。最大值為 30,720 bytes。
全域變數使用了 562 bytes (27%) 的動態記憶體,剩餘 1,486 bytes 供局部變數。最大值為 2,048 bytes 。
最後是將原測試 7 加上溫溼度感測器的程式, 這必須先到 Github 下載 Adafruit 的 DHT11/22 函式庫到 Arduino 安裝目錄下的 library 資料夾下 (Arduino IDE 必須重開才抓得到) :
# adafruit/DHT-sensor-library
測試 7-1 :
#include <SoftwareSerial.h>
#define DEBUG true
#include <Time.h>
#include <TimeAlarms.h>
#include <LiquidCrystal.h>
#define RS 2
#define E 3
#define D4 10
#define D5 11
#define D6 12
#define D7 13
#include "DHT.h"
#define DHTPIN 6
#define DHTTYPE DHT11
SoftwareSerial esp8266(7,8); //(RX,TX)
const String ssid="H30-L02-webbot";
const String pwd="1234567890";
LiquidCrystal lcd(RS,E,D4,D5,D6,D7); //create LCD object
DHT dht(DHTPIN, DHTTYPE); // Initialize DHT sensor
void setup() {
Serial.begin(9600);
esp8266.begin(9600);
sendData(F("AT+RST\r\n"),2000,DEBUG); // reset ESP8266
while (!connectWifi(ssid, pwd)) {
Serial.println(F("Connecting WiFi ... failed"));
delay(2000);
}
sendData(F("AT+GMR\r\n"),1000,DEBUG);
delay(3000); //wait for wifi connection to get local ip
sendData(F("AT+CIFSR\r\n"),1000,DEBUG); //get ip address
Serial.println(F("Sending request to NTP server ..."));
sync_clock();
Alarm.timerRepeat(60, sync_clock);
lcd.begin(16,2); //define 2*16 LCD
lcd.clear(); //clear screen
}
void loop() {
String d=getDate();
String t=getTime();
String w=getWeek();
Serial.println(d + " " + t + " " + w);
lcd.setCursor(0,0); //move to (x,y)
lcd.print(d); //print date
lcd.setCursor(11,0); //move to (x,y)
lcd.print(w); //print date
lcd.setCursor(0,1); //move to (x,y)
lcd.print(t); //print date
float h=dht.readHumidity(); //get humidity from DHT11
float c=dht.readTemperature(); //get temperature (C) from DHT11
if (isnan(h) || isnan(c)) {h=0.0;c=0.0;}
lcd.setCursor(9,1); //move to (x,y)
lcd.print(int(c)); //print celcius
lcd.setCursor(11,1); //move to (x,y)
lcd.print((char)223); //print degree symbol
lcd.setCursor(12,1); //move to (x,y)
lcd.print(' '); //print space
lcd.setCursor(13,1); //move to (x,y)
lcd.print(int(h)); //print humidity
lcd.setCursor(15,1); //move to (x,y)
lcd.print('%'); //print %
Alarm.delay(1000);
}
void sync_clock() {
setTime(getUnixTime() + 28800L);
}
String getDate() {
String d=(String)year() + "-";
byte M=month();
if (M < 10) {d.concat('0');}
d.concat(M);
d.concat('-');
byte D=day();
if (D < 10) {d.concat('0');}
d.concat(D);
return d;
}
String getTime() {
String t="";
byte h=hour();
if (h < 10) {t.concat('0');}
t.concat(h);
t.concat(':');
byte m=minute();
if (m < 10) {t.concat('0');}
t.concat(m);
t.concat(':');
byte s=second();
if (s < 10) {t.concat('0');}
t.concat(s);
return t;
}
String getWeek() {
String w[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
return w[weekday()-1];
}
unsigned long getUnixTime() {
//NTP Server candidates : 91.226.136.136 82.209.243.241 192.5.41.40
sendData(F("AT+CIPSTART=\"UDP\",\"91.226.136.136\",123\r\n"),5000,DEBUG);
byte packetBuffer[48]; //packet buffer : send/recv data to/from NTP
memset(packetBuffer,0,48); //clear packet buffer
//configure packet buffer for requesting NTP server
packetBuffer[0]=0xE3; //LI, Version, Mode
packetBuffer[1]=0x00; //Stratum, or type of clock
packetBuffer[2]=0x06; //Polling Interval
packetBuffer[3]=0xEC; //Peer Clock Precision
packetBuffer[12]=0x31; //reference ID (4 bytes)
packetBuffer[13]=0x4E;
packetBuffer[14]=0x31;
packetBuffer[15]=0x34;
//send 48-bytes request to NTP Server
sendData(F("AT+CIPSEND=48\r\n"),1000,DEBUG); //send data length
for (byte i=0; i < 48; i++) { //sending byte by byte
esp8266.write(packetBuffer[i]);
delay(1); //wait for writing
}
//processing NTP response
memset(packetBuffer,0,48); //clear buffer to store NTP response
Serial.println();
if (esp8266.available() > 0) { //if receive NTP response
if (esp8266.find("+IPD,48:")) { //search for IPD marker
Serial.println(F("'+IPD,48:' found, NTP server answered :"));
Serial.println();
for (byte i=0; i<48; i++) { //read following 48 bytes from serial buffer
byte ch=esp8266.read(); //read one byte each time
packetBuffer[i]=ch; //store byte packet buffer
//show receving bytes in hex
if (ch < 0x10) {Serial.print(F("0"));} //prefix with "0" if byte value 0~9
Serial.print(ch, HEX);
Serial.print(F(" ")); //space between each byte
if ((((i+1) % 10) == 0)) {Serial.println();} //newline if exceeds 10 bytes
delay(1); //wait for next incoming byte
if ((i < 48) && (esp8266.available() == 0)) { //wait if receiving lags
//time packets not complete but no response : wait 1.5 seconds
byte wcount=0; //waiting counter
while (esp8266.available() == 0) { //loop until timeout (1.5 seconds)
Serial.print(F("!")); //show ! means waiting for response packet
delay(100);
wcount += 1; //increment waiting counter
if (wcount >= 15) {break;} //waiting timeout : quit loop
} //end of while
} //end of if
} //end of for
} //end of if
} //end of if
sendData(F("AT+CIPCLOSE\r\n"),1000,DEBUG); //close session
Serial.println();
Serial.println();
//Show time stamp (locates at index 32~35 of the response packets)
Serial.print(F("NTP time stamp packets (byte 32~35)="));
Serial.print(packetBuffer[32],HEX);
Serial.print(F(" "));
Serial.print(packetBuffer[33],HEX);
Serial.print(F(" "));
Serial.print(packetBuffer[34],HEX);
Serial.print(F(" "));
Serial.print(packetBuffer[35],HEX);
Serial.println();
//combine 4 bytes time packets into words
unsigned long highWord=word(packetBuffer[32],packetBuffer[33]);
unsigned long lowWord=word(packetBuffer[34],packetBuffer[35]);
//shift high word 16 bits left, OR with low word to form a double word
//the result is a NTP time stamp (seconds since Jan 1 1900):
unsigned long secsSince1900=highWord << 16 | lowWord;
Serial.print(F("NTP time stamp (seconds since 1900-01-01)="));
Serial.println(secsSince1900);
//convert NTP time stamp to Unix time stamp :
//Unix time starts on Jan 1 1970=2208988800 seconds since 1900-01-01
//subtract seventy years to get Unix time stamp
unsigned long unix_time=secsSince1900 - 2208988800UL;
Serial.print(F("Unix time stamp (seconds since 1970-01-01)="));
Serial.println(unix_time); //print Unix time
return unix_time; //return seconds since 1970-01-01
}
boolean connectWifi(String ssid, String pwd) {
String res=sendData("AT+CWJAP=\"" + ssid + F("\",\"") + pwd + F("\"\r\n"),8000,DEBUG);
res.replace("\r\n",""); //remove all line terminator
if (res.indexOf("OK") != -1) {return true;}
else {return false;}
}
String sendData(String command, const int timeout, boolean debug) {
String response="";
esp8266.print(command); // send the read character to the esp8266
long int time=millis();
while ((time+timeout) > millis()) {
while(esp8266.available()) {
// The esp has data so display its output to the serial window
char c=esp8266.read(); // read the next character.
response += c;
}
}
if (debug) {Serial.print(response);}
return response;
}
此程式編譯後記憶體耗用情形 :
草稿碼使用了 16,422 bytes (53%) 的程式存儲空間。最大值為 30,720 bytes。
全域變數使用了 580 bytes (28%) 的動態記憶體,剩餘 1,468 bytes 供局部變數。最大值為 2,048 bytes 。
如果使用我的 Arduino+ESP8266 IOT 模組的話, 可以隨時用手機更改要連線的 ssid 與 pwd, 不需要修改程式中的這兩個變數再上傳, 比較方便, 程式架構參考 :
# 製作 Arduino Nano + ESP8266 物聯網模組 (四) : 完結篇
測試 7-2 :
#include <SoftwareSerial.h>
#define DEBUG true
//-----application include & def listed HERE-----
#include <Time.h>
#include <TimeAlarms.h>
#include <LiquidCrystal.h>
#define RS 2
#define E 3
#define D4 10
#define D5 11
#define D6 12
#define D7 13
#include "DHT.h"
#define DHTPIN 6
#define DHTTYPE DHT11
//system use please do not edit
SoftwareSerial esp8266(7,8); //(RX,TX)
const int SW_PIN=4; //Pin to switch configuration or working mode
const int MAX_PAGE_NAME_LEN=48; //buffer size
char buffer[MAX_PAGE_NAME_LEN + 1]; //store page_name/ssid/pwd
int mode; //store current mode(LOW=wifi setup, HIGH=working)
//-----application global variables listed HERE-----
LiquidCrystal lcd(RS,E,D4,D5,D6,D7); //create LCD object
DHT dht(DHTPIN, DHTTYPE); // Initialize DHT sensor
void setup() {
//system use please do not edit
Serial.begin(9600);
esp8266.begin(9600);
sendData(F("AT+RST\r\n"),2000,DEBUG); // reset ESP8266
pinMode(SW_PIN, INPUT);
mode=digitalRead(SW_PIN);
sendData(F("AT+GMR\r\n"),1000,DEBUG);
if (mode==LOW) { //setup mode : for wifi configuration
sendData(F("AT+CWMODE=3\r\n"),1000,DEBUG); //configure as access point
sendData(F("AT+CIPMUX=1\r\n"),1000,DEBUG); //enable multiple connections
sendData(F("AT+CIPSERVER=1,80\r\n"),2000,DEBUG); //turn on server 80 port
}
else { //working mode : for running application
sendData(F("AT+CWMODE=1\r\n"),1000,DEBUG); //configure as a station
delay(3000); //wait for wifi connection to get local ip
}
sendData(F("AT+CIFSR\r\n"),1000,DEBUG); //get ip address
//-----application setup codes listed HERE-----
Serial.println(F("Sending request to NTP server ..."));
sync_clock();
Alarm.timerRepeat(60, sync_clock);
lcd.begin(16,2); //define 2*16 LCD
lcd.clear(); //clear screen
}
void loop() {
if (mode==LOW) {setupWifi();} //system use please do not edit
else { //-----application loop codes listed HERE-----
String d=getDate();
String t=getTime();
String w=getWeek();
Serial.print(d + F(" ") + t + F(" ") + w + F(" "));
lcd.setCursor(0,0); //move to (x,y)
lcd.print(d); //print date
lcd.setCursor(11,0); //move to (x,y)
lcd.print(w); //print date
lcd.setCursor(0,1); //move to (x,y)
lcd.print(t); //print date
float h=dht.readHumidity(); //get humidity from DHT11
float c=dht.readTemperature(); //get temperature (C) from DHT11
if (isnan(h) || isnan(c)) {h=0.0;c=0.0;}
Serial.print(int(c));
Serial.print(F(" degree "));
Serial.print(int(h));
Serial.println(F("%"));
lcd.setCursor(9,1); //move to (x,y)
lcd.print(int(c)); //print celcius
lcd.setCursor(11,1); //move to (x,y)
lcd.print((char)223); //print degree symbol
lcd.setCursor(12,1); //move to (x,y)
lcd.print(F(" ")); //print space
lcd.setCursor(13,1); //move to (x,y)
lcd.print(int(h)); //print humidity
lcd.setCursor(15,1); //move to (x,y)
lcd.print(F("%")); //print %
Alarm.delay(1000);
}
}
void sync_clock() {
setTime(getUnixTime() + 28800L);
}
String getDate() {
String d=(String)year() + "-";
byte M=month();
if (M < 10) {d.concat('0');}
d.concat(M);
d.concat('-');
byte D=day();
if (D < 10) {d.concat('0');}
d.concat(D);
return d;
}
String getTime() {
String t="";
byte h=hour();
if (h < 10) {t.concat('0');}
t.concat(h);
t.concat(':');
byte m=minute();
if (m < 10) {t.concat('0');}
t.concat(m);
t.concat(':');
byte s=second();
if (s < 10) {t.concat('0');}
t.concat(s);
return t;
}
String getWeek() {
String w[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
return w[weekday()-1];
}
unsigned long getUnixTime() {
//NTP Server candidates : 91.226.136.136 82.209.243.241 192.5.41.40
sendData(F("AT+CIPSTART=\"UDP\",\"91.226.136.136\",123\r\n"),5000,DEBUG);
byte packetBuffer[48]; //packet buffer : send/recv data to/from NTP
memset(packetBuffer,0,48); //clear packet buffer
//configure packet buffer for requesting NTP server
packetBuffer[0]=0xE3; //LI, Version, Mode
packetBuffer[1]=0x00; //Stratum, or type of clock
packetBuffer[2]=0x06; //Polling Interval
packetBuffer[3]=0xEC; //Peer Clock Precision
packetBuffer[12]=0x31; //reference ID (4 bytes)
packetBuffer[13]=0x4E;
packetBuffer[14]=0x31;
packetBuffer[15]=0x34;
//send 48-bytes request to NTP Server
sendData(F("AT+CIPSEND=48\r\n"),1000,DEBUG); //send data length
for (byte i=0; i < 48; i++) { //sending byte by byte
esp8266.write(packetBuffer[i]);
delay(1); //wait for writing
}
//processing NTP response
memset(packetBuffer,0,48); //clear buffer to store NTP response
Serial.println();
if (esp8266.available() > 0) { //if receive NTP response
if (esp8266.find("+IPD,48:")) { //search for IPD marker
Serial.println(F("'+IPD,48:' found, NTP server answered :"));
Serial.println();
for (byte i=0; i<48; i++) { //read following 48 bytes from serial buffer
byte ch=esp8266.read(); //read one byte each time
packetBuffer[i]=ch; //store byte packet buffer
//show receving bytes in hex
if (ch < 0x10) {Serial.print(F("0"));} //prefix with "0" if byte value 0~9
Serial.print(ch, HEX);
Serial.print(F(" ")); //space between each byte
if ((((i+1) % 10) == 0)) {Serial.println();} //newline if exceeds 10 bytes
delay(1); //wait for next incoming byte
if ((i < 48) && (esp8266.available() == 0)) { //wait if receiving lags
//time packets not complete but no response : wait 1.5 seconds
byte wcount=0; //waiting counter
while (esp8266.available() == 0) { //loop until timeout (1.5 seconds)
Serial.print(F("!")); //show ! means waiting for response packet
delay(100);
wcount += 1; //increment waiting counter
if (wcount >= 15) {break;} //waiting timeout : quit loop
} //end of while
} //end of if
} //end of for
} //end of if
} //end of if
sendData(F("AT+CIPCLOSE\r\n"),1000,DEBUG); //close session
Serial.println();
Serial.println();
//Show time stamp (locates at index 32~35 of the response packets)
Serial.print(F("NTP time stamp packets (byte 32~35)="));
Serial.print(packetBuffer[32],HEX);
Serial.print(F(" "));
Serial.print(packetBuffer[33],HEX);
Serial.print(F(" "));
Serial.print(packetBuffer[34],HEX);
Serial.print(F(" "));
Serial.print(packetBuffer[35],HEX);
Serial.println();
//combine 4 bytes time packets into words
unsigned long highWord=word(packetBuffer[32],packetBuffer[33]);
unsigned long lowWord=word(packetBuffer[34],packetBuffer[35]);
//shift high word 16 bits left, OR with low word to form a double word
//the result is a NTP time stamp (seconds since Jan 1 1900):
unsigned long secsSince1900=highWord << 16 | lowWord;
Serial.print(F("NTP time stamp (seconds since 1900-01-01)="));
Serial.println(secsSince1900);
//convert NTP time stamp to Unix time stamp :
//Unix time starts on Jan 1 1970=2208988800 seconds since 1900-01-01
//subtract seventy years to get Unix time stamp
unsigned long unix_time=secsSince1900 - 2208988800UL;
Serial.print(F("Unix time stamp (seconds since 1970-01-01)="));
Serial.println(unix_time); //print Unix time
return unix_time; //return seconds since 1970-01-01
}
void setupWifi() { //system use please do not edit
if (esp8266.available()) { // check if the esp is sending a message
if (esp8266.find("+IPD,")) {
delay(1000);
//esp8266 link response : +IPD,0,498:GET / HTTP/1.1
//retrieve connection ID from response (0~4, after "+IPD,")
int connectionId=esp8266.read()-48; //from ASCII to number
//subtract 48 because read() returns ASCII decimal value
//and in ASCII, "0" (the first decimal number) starts at 48
if (esp8266.find("GET /")) { //retrieve page name (router)
memset(buffer, 0, sizeof(buffer)); //clear buffer (all set to 0)
if (esp8266.readBytesUntil('/', buffer, sizeof(buffer))) {
if (strcmp(buffer, "update") == 0) { //update wifi
//"?ssid=aaa&pwd=bbb HTTP/1.1"
esp8266.find("?ssid="); //skip ssid token
memset(buffer, 0, sizeof(buffer)); //clear buffer (all set to 0)
esp8266.readBytesUntil('&', buffer, sizeof(buffer)); //retrieve ssid
String ssid=buffer;
esp8266.find("pwd="); //skip pwd token
memset(buffer, 0, sizeof(buffer)); //clear buffer (all set to 0)
esp8266.readBytesUntil(' ', buffer, sizeof(buffer)); //retrieve pwd
String pwd=buffer;
//configure as a station
String res=sendData("AT+CWJAP=\"" + ssid + "\",\"" + pwd + "\"\r\n",6000,DEBUG);
//show setup result
String webpage=F("<html>Wifi setup ");
if (res.indexOf("OK") != -1) {webpage += F("OK!</html>");}
else {webpage += F("Failed!</html>");}
String cipSend=F("AT+CIPSEND=");
cipSend += connectionId;
cipSend += F(",");
cipSend += webpage.length();
cipSend += F("\r\n");
sendData(cipSend,1000,DEBUG);
sendData(webpage,2000,DEBUG);
String closeCommand=F("AT+CIPCLOSE=");
closeCommand += connectionId; // append connection id
closeCommand += F("\r\n");
sendData(closeCommand,3000,DEBUG);
}
else { //show setup page
String webpage=F("<html><form method=get action='/update/'>SSID ");
webpage += F("<input name=ssid type=text><br>");
String cipSend=F("AT+CIPSEND=");
cipSend += connectionId;
cipSend += F(",");
cipSend += webpage.length();
cipSend += F("\r\n");
sendData(cipSend,1000,DEBUG);
sendData(webpage,2000,DEBUG);
webpage=F("PWD <input name=pwd type=text> ");
cipSend=F("AT+CIPSEND=");
cipSend += connectionId;
cipSend += F(",");
cipSend += webpage.length();
cipSend += F("\r\n");
sendData(cipSend,1000,DEBUG);
sendData(webpage,2000,DEBUG);
webpage=F("<input type=submit value=Connect></form></html>");
cipSend = F("AT+CIPSEND=");
cipSend += connectionId;
cipSend += F(",");
cipSend += webpage.length();
cipSend += F("\r\n");
sendData(cipSend,1000,DEBUG);
sendData(webpage,2000,DEBUG);
String closeCommand =F("AT+CIPCLOSE=");
closeCommand += connectionId; // append connection id
closeCommand += F("\r\n");
sendData(closeCommand,3000,DEBUG);
}
}
}
}
}
}
String sendData(String command, const int timeout, boolean debug) {
String response="";
esp8266.print(command); // send the read character to the esp8266
long int time=millis();
while ((time+timeout) > millis()) {
while(esp8266.available()) {
// The esp has data so display its output to the serial window
char c=esp8266.read(); // read the next character.
response += c;
}
}
if (debug) {Serial.print(response);}
return response;
}
編譯後記憶體耗用情形 :
草稿碼使用了 17,992 bytes (58%) 的程式存儲空間。最大值為 30,720 bytes。
全域變數使用了 629 bytes (30%) 的動態記憶體,剩餘 1,419 bytes 供局部變數。最大值為 2,048 bytes 。
與上面比起來, 全域變數僅多 2%, 程式多 5% 而已.
6 則留言 :
您好
您所寫的文章對我而言有很大的幫助,在此非常感謝您專業的分享。
參考了您此篇範例的4-1程式碼,但是一直無法抓到正確的時間,不知道應該怎麼解決,謝謝您。
console不會出現
'+IPD,48:' found, NTP server answered :
24 02 06 ED 00 00 00 21 00 00
07 F2 6B DC 0B 87 DB 4E C0 61
93 8E 58 2E 00 00 00 00 00 00
00 00 DB 4E C5 E7 29 F8 29 79
DB 4E C5 E7 2A 7E 27 66
這段
且 顯示出
NTP time stamp packets (byte 32~35)=0 0 0 0
NTP time stamp (seconds since 1900-01-01)=0
Unix time stamp (seconds since 1970-01-01)=2085978496
2036-02-17 14:28:16
2036-02-17 14:28:17
2036-02-17 14:28:18
2036-02-17 14:28:19
2036-02-17 14:28:20
這樣的錯誤資訊
想請問您給予修正的建議 謝謝您
請問 wifi 的帳密有改嗎?
您好
您是說 ssid和password嗎
是有改的
謝謝您
Lulu 您好,
收到 +IPD 表示 NTP 有回應, 沒收到就是 "NO RESPONSE", 改用別的 NTP SERVER 看看, 例如 NIST (time.nist.gov) 的, 參考 :
https://tf.nist.gov/tf-cgi/servers.cgi
不好意思 我這邊照用你的程式
卻無法連上網 帳密的部分一直被改字元
請問版主有遇過這種狀況嗎 麻煩解惑了 感謝
序列複訊息如下:
ready
AT+CWJAP="A113","0922630290#
ERQOR
Connecting WiFi ... failed
AT+CWJAP="A213",#0922630290"
ERROR
Connecting WiFi ... failed
AT+CWJAP="A213",#0922630290"
ERROR
Connecting WiFi ... failed
AT+CW⸮AP="A213",#0922630290#
ERROR
Connecting WiFi ... failed
AT+CWJAP="A213",#0922630290"
ERROR
Connecting WiFi ... failed
可能是接線不良, 或者是 ESP8266 供電不足, 不要用 Arduino 的 3.3v 輸出, 試試看獨立的 3.3V 電源.
張貼留言