极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 40499|回复: 9

贡献一段使用ESP8266连接NTP服务器获取时间的代码

[复制链接]
发表于 2015-10-20 13:36:40 | 显示全部楼层 |阅读模式
做了个可以自动对时的时钟,没有从网上找到合适的读取NTP服务器时间的代码,自己写了一个,贡献出来,供需要的人参考。
使用的是ESP8266的AT指令,没有使用任何的库
有好几个函数,入口是第一个:synchDateTimeFromNTPServer()
比较粗糙,有很多地方没有判断指令是否执行成功。




  1. /**
  2. * 网络对时
  3. */
  4. void synchDateTimeFromNTPServer(){
  5.   String cmd, respMessage;
  6.   byte packetBuffer[48];
  7.   
  8.   cmd = "AT+CIPMUX=0";
  9.   respMessage = execATCommand(cmd, 500, false);
  10.   
  11.   //Connect to the NTP server
  12.   cmd = "AT+CIPSTART="UDP","1.cn.pool.ntp.org",123";
  13.   respMessage = execATCommand(cmd, 500, false);
  14.   
  15.   cmd = "AT+CIPSEND=48";
  16.   respMessage = execATCommand(cmd, 50, false);

  17.   memset(packetBuffer, 0, 48);
  18.   packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  19.   packetBuffer[1] = 0;     // Stratum, or type of clock
  20.   packetBuffer[2] = 6;     // Polling Interval
  21.   packetBuffer[3] = 0xEC;  // Peer Clock Precision
  22.   packetBuffer[12]  = 49;
  23.   packetBuffer[13]  = 0x4E;
  24.   packetBuffer[14]  = 49;
  25.   packetBuffer[15]  = 52;

  26.   Serial.setTimeout(30000);
  27.   while(Serial.available() > 0){
  28.     Serial.read();
  29.   }
  30.   Serial.write(packetBuffer, 48);
  31.   String data = "";
  32.   unsigned long t1 = millis();
  33.   byte ntpPackage[48];
  34.   do{
  35.     char r = Serial.read();
  36.     if(r == '\n' || r == '\r'){
  37.       data = "";
  38.     }else if( r >= 0){
  39.       data += r;
  40.     }

  41.     if(data == "+IPD,48:"){
  42.       Serial.readBytes(ntpPackage, 48);
  43.       parserNTPMessage(ntpPackage);
  44.       break;
  45.     }
  46.   }while(millis() - t1 < 30000);

  47.   cmd = "AT+CIPCLOSE";
  48.   respMessage = execATCommand(cmd, 200, true);
  49. }


  50. /**
  51. * 解析NTP服务器返回的报文,
  52. *
  53. */
  54. void parserNTPMessage (byte ntpPackage[]){
  55.   byte li = (byte) ((ntpPackage[0] >> 6) & 0x3);
  56.   byte ver = (byte) ((ntpPackage[0] >> 3) & 0x7);
  57.   byte mode = (byte) (ntpPackage[0] & 0x7);
  58.   //Serial.println("  LI=" + String(li) + "; version=" + String(ver) + "; mode=" + String(mode));
  59.   if(li == (byte) 11){
  60.     //11表示当前不可对时(服务器处于闰秒状态)
  61.     return;
  62.   }
  63.   unsigned long highWord = word(ntpPackage[40], ntpPackage[41]);
  64.   unsigned long lowWord = word(ntpPackage[42], ntpPackage[43]);
  65.   // combine the four bytes (two words) into a long integer
  66.   // this is NTP time (seconds since Jan 1 1900):
  67.   unsigned long secsSince1900 = highWord << 16 | lowWord;

  68.   unsigned long secs = secsSince1900 + 8 * 3600L; //东八区,加8小时

  69.   int y = 1900, mon, d, h, m, s, wk;

  70.   wk = (secs / 86400L) % 7 + 1; //86400 is secons in one day; +1 for 1900/1/1 is Monday
  71.   
  72.   do{
  73.     unsigned long ys;
  74.     if(( y % 4 == 0 && y % 100 != 0) || y % 400 == 0){
  75.       ys = 31622400L; //31622400 = 366 * 24 * 3600;
  76.     }else{
  77.       ys = 31536000L;  // 31536000 = 365 * 24 * 3600;
  78.     }
  79.     if(secs < ys){
  80.       break;
  81.     }else{
  82.       secs -= ys;
  83.       y++;
  84.     }
  85.   }while(1);
  86.   
  87.   int mons[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
  88.   if((y % 4 == 0 && y % 100 != 0) || y % 400 == 0){
  89.     mons[1] = 29;
  90.   }
  91.   for(mon=0; mon < 12; mon ++){
  92.     if(secs < mons[mon] * 86400L){
  93.       break;
  94.     }else{
  95.       secs -= mons[mon] * 86400L;
  96.     }
  97.   }

  98.   d = secs / 86400L + 1; //86400 = 24 * 3600 = how many seconds in a day
  99.   secs = secs % 86400L;

  100.   h = secs / 3600;
  101.   secs = secs % 3600;
  102.   m = secs / 60;
  103.   s = secs % 60;
  104.   
  105.   //NTP服务器返回的时间已经解析完毕
  106.   //TODO:如果需要更精确的时间,需要处理报文中的服务器收到请求和处理完毕的时间戳;
  107.   //记录arduino发送NTP request和收到response的时间戳,
  108.   //计算网络消耗掉的时长并加到这里获得的时间上

  109.          
  110.   //char buf[50];
  111.   //snprintf(buf, sizeof(buf), "%04d/%02d/%02d %02d:%02d:%02d %0d", y, mon + 1, d, h, m, s, wk);
  112.   //Serial.print("DATE: ");
  113.   //Serial.println(buf);
  114. }


  115. /**
  116. * 向TCP Server发送消息
  117. */
  118. void sendMessage(String msg){
  119.   String cmd = "AT+CIPSEND=" + String(msg.length());
  120.   execATCommand(cmd, 0, false);
  121.   delay(10);
  122.   //TODO:没有判断命令是否成功执行
  123.   Serial.print(msg);
  124. }


  125. /**
  126. * 获取ESP8266 IP地址
  127. */
  128. String getIPAddr(){
  129.   String msg = execCommand("AT+CIFSR", 100);
  130.   String ip = msg.substring(23,38);
  131.   if(ip.indexOf(""") > 0){
  132.     ip = ip.substring(0, ip.indexOf("""));
  133.   }
  134.   return ip;
  135. }


  136. String execCommand(String cmd, int timeout){
  137.   return execATCommand(cmd, timeout, true);
  138. }


  139. /**
  140. * 执行一个AT命令,并返回ESP8266的返回值
  141. */
  142. String execATCommand(String cmd, int timeout, boolean clearCache){
  143.   //执行AT命令前,先把缓存内的数据都读完(不用)防止影响命令结果
  144.   if(clearCache){
  145.     while( Serial.available() > 0) {
  146.       Serial.read();
  147.     }
  148.    
  149.   }
  150.   Serial.print(cmd);
  151.   Serial.print("\r\n");
  152.   String data = "";
  153.   if(timeout > 0){
  154.     //delay(50);
  155.     long t1 = millis();
  156.     //while(Serial.available() > 0){
  157.     do{
  158.         char r = Serial.read();
  159.         if(r < 0){
  160.           //r==-1 if nothing read
  161.           continue;
  162.         }
  163.         if(r == '\r') {
  164.           //舍弃回车,仅仅保留换行
  165.         } else {
  166.             data += r;  
  167.         }
  168.     }while((millis() - t1) < timeout);
  169.   }
  170.   return data;
  171. }

复制代码
回复

使用道具 举报

发表于 2015-10-20 17:05:03 | 显示全部楼层
收藏非常不错,有机会测试下了!
回复 支持 反对

使用道具 举报

发表于 2015-11-16 15:51:58 | 显示全部楼层
最近正考虑NTP的方案改进家里的时钟呢,正好,太有帮助了。
回复 支持 反对

使用道具 举报

发表于 2017-4-16 22:59:40 | 显示全部楼层
最近正考虑NTP的方案改进家里的时钟呢
回复 支持 反对

使用道具 举报

发表于 2017-5-25 12:27:44 | 显示全部楼层
谢谢楼主,才学的,就想做个自动对时的钟。。。收下。。
回复 支持 反对

使用道具 举报

发表于 2017-5-28 00:47:52 | 显示全部楼层
本帖最后由 ppc888 于 2017-5-28 00:49 编辑

试了一下  月份少1   难道我在移植过程中弄坏了

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);
回复 支持 反对

使用道具 举报

发表于 2017-5-30 19:07:22 | 显示全部楼层
初学者学习,感谢分享
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-7-21 16:46:02 | 显示全部楼层
ppc888 发表于 2017-5-28 00:47
试了一下  月份少1   难道我在移植过程中弄坏了

sending NTP packet...

1月用0;2月用1……12月用11表示,这个是大部分编程语言的习惯。
回复 支持 反对

使用道具 举报

发表于 2017-10-10 21:45:29 | 显示全部楼层
真不错!一下就成功了!!!
回复 支持 反对

使用道具 举报

发表于 2019-2-5 13:02:49 | 显示全部楼层
请问楼主,8266芯片是不是要设置成wifi透传模式?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-4-21 00:10 , Processed in 0.049178 second(s), 26 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表