贡献一段使用ESP8266连接NTP服务器获取时间的代码
做了个可以自动对时的时钟,没有从网上找到合适的读取NTP服务器时间的代码,自己写了一个,贡献出来,供需要的人参考。使用的是ESP8266的AT指令,没有使用任何的库
有好几个函数,入口是第一个:synchDateTimeFromNTPServer()
比较粗糙,有很多地方没有判断指令是否执行成功。
/**
* 网络对时
*/
void synchDateTimeFromNTPServer(){
String cmd, respMessage;
byte packetBuffer;
cmd = "AT+CIPMUX=0";
respMessage = execATCommand(cmd, 500, false);
//Connect to the NTP server
cmd = "AT+CIPSTART=\"UDP\",\"1.cn.pool.ntp.org\",123";
respMessage = execATCommand(cmd, 500, false);
cmd = "AT+CIPSEND=48";
respMessage = execATCommand(cmd, 50, false);
memset(packetBuffer, 0, 48);
packetBuffer = 0b11100011; // LI, Version, Mode
packetBuffer = 0; // Stratum, or type of clock
packetBuffer = 6; // Polling Interval
packetBuffer = 0xEC;// Peer Clock Precision
packetBuffer= 49;
packetBuffer= 0x4E;
packetBuffer= 49;
packetBuffer= 52;
Serial.setTimeout(30000);
while(Serial.available() > 0){
Serial.read();
}
Serial.write(packetBuffer, 48);
String data = "";
unsigned long t1 = millis();
byte ntpPackage;
do{
char r = Serial.read();
if(r == '\n' || r == '\r'){
data = "";
}else if( r >= 0){
data += r;
}
if(data == "+IPD,48:"){
Serial.readBytes(ntpPackage, 48);
parserNTPMessage(ntpPackage);
break;
}
}while(millis() - t1 < 30000);
cmd = "AT+CIPCLOSE";
respMessage = execATCommand(cmd, 200, true);
}
/**
* 解析NTP服务器返回的报文,
*
*/
void parserNTPMessage (byte ntpPackage[]){
byte li = (byte) ((ntpPackage >> 6) & 0x3);
byte ver = (byte) ((ntpPackage >> 3) & 0x7);
byte mode = (byte) (ntpPackage & 0x7);
//Serial.println("LI=" + String(li) + "; version=" + String(ver) + "; mode=" + String(mode));
if(li == (byte) 11){
//11表示当前不可对时(服务器处于闰秒状态)
return;
}
unsigned long highWord = word(ntpPackage, ntpPackage);
unsigned long lowWord = word(ntpPackage, ntpPackage);
// 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;
unsigned long secs = secsSince1900 + 8 * 3600L; //东八区,加8小时
int y = 1900, mon, d, h, m, s, wk;
wk = (secs / 86400L) % 7 + 1; //86400 is secons in one day; +1 for 1900/1/1 is Monday
do{
unsigned long ys;
if(( y % 4 == 0 && y % 100 != 0) || y % 400 == 0){
ys = 31622400L; //31622400 = 366 * 24 * 3600;
}else{
ys = 31536000L;// 31536000 = 365 * 24 * 3600;
}
if(secs < ys){
break;
}else{
secs -= ys;
y++;
}
}while(1);
int mons = {31,28,31,30,31,30,31,31,30,31,30,31};
if((y % 4 == 0 && y % 100 != 0) || y % 400 == 0){
mons = 29;
}
for(mon=0; mon < 12; mon ++){
if(secs < mons * 86400L){
break;
}else{
secs -= mons * 86400L;
}
}
d = secs / 86400L + 1; //86400 = 24 * 3600 = how many seconds in a day
secs = secs % 86400L;
h = secs / 3600;
secs = secs % 3600;
m = secs / 60;
s = secs % 60;
//NTP服务器返回的时间已经解析完毕
//TODO:如果需要更精确的时间,需要处理报文中的服务器收到请求和处理完毕的时间戳;
//记录arduino发送NTP request和收到response的时间戳,
//计算网络消耗掉的时长并加到这里获得的时间上
//char buf;
//snprintf(buf, sizeof(buf), "%04d/%02d/%02d %02d:%02d:%02d %0d", y, mon + 1, d, h, m, s, wk);
//Serial.print("DATE: ");
//Serial.println(buf);
}
/**
* 向TCP Server发送消息
*/
void sendMessage(String msg){
String cmd = "AT+CIPSEND=" + String(msg.length());
execATCommand(cmd, 0, false);
delay(10);
//TODO:没有判断命令是否成功执行
Serial.print(msg);
}
/**
* 获取ESP8266 IP地址
*/
String getIPAddr(){
String msg = execCommand("AT+CIFSR", 100);
String ip = msg.substring(23,38);
if(ip.indexOf("\"") > 0){
ip = ip.substring(0, ip.indexOf("\""));
}
return ip;
}
String execCommand(String cmd, int timeout){
return execATCommand(cmd, timeout, true);
}
/**
* 执行一个AT命令,并返回ESP8266的返回值
*/
String execATCommand(String cmd, int timeout, boolean clearCache){
//执行AT命令前,先把缓存内的数据都读完(不用)防止影响命令结果
if(clearCache){
while( Serial.available() > 0) {
Serial.read();
}
}
Serial.print(cmd);
Serial.print("\r\n");
String data = "";
if(timeout > 0){
//delay(50);
long t1 = millis();
//while(Serial.available() > 0){
do{
char r = Serial.read();
if(r < 0){
//r==-1 if nothing read
continue;
}
if(r == '\r') {
//舍弃回车,仅仅保留换行
} else {
data += r;
}
}while((millis() - t1) < timeout);
}
return data;
}
收藏非常不错,有机会测试下了! 最近正考虑NTP的方案改进家里的时钟呢,正好,太有帮助了。 最近正考虑NTP的方案改进家里的时钟呢 谢谢楼主,才学的,就想做个自动对时的钟。。。收下。。 本帖最后由 ppc888 于 2017-5-28 00:49 编辑
试了一下月份少1 难道我在移植过程中弄坏了 :Q
sending NTP packet...
packet received, length=48
Seconds since Jan 1 1900 = 3704892427
Unix time = 1495903627
2017-5-28 0:47:7 --- wk7
显示正确的因为在显示时补加了1
Serial.print(mon+1); 初学者学习,感谢分享 ppc888 发表于 2017-5-28 00:47
试了一下月份少1 难道我在移植过程中弄坏了
sending NTP packet...
1月用0;2月用1……12月用11表示,这个是大部分编程语言的习惯。 真不错!一下就成功了!!!:) 请问楼主,8266芯片是不是要设置成wifi透传模式?
页:
[1]