NodeMCU(ESP8266)获取NTP时间

NodeMCU(ESP8266)获取NTP时间

很久没有搞ESP8266了,可能是这两年工作太忙了,又或者是对生活失去了斗志,所以最近又重新把两年前的东西重新收拾收拾。

NTP协议

我之前有写一篇NTP 入门介绍,大家如果有对NTP不了解的,可以先查阅这篇《NTP 入门介绍》

为啥要同步时间

之前看到有一个用户通过esp8266做了一个时钟出来( ESP8266物联网创意点阵时钟,女朋友看了都想要!
),自己也想搞一个类似的,然后就发现他有一个功能就是网络自动校准时间,才了解到有NTP这个协议的存在,所以就找到一些代码研究了一番,所以就有了今天这篇博文。

实现思路

esp8266感觉是一个很简单的东西,网上有很多的代码示例,这里我更推荐使用官方的示例库。

如何从官方的示例库中找到我们要用的示例代码:

  • net-client-demo
2.png
  • Ticker 图片

    ticker的使用请参考从零开始的ESP8266探索(11)-定时任务调度器Ticker使用演示

1.png

代码

根据上述的示例和其他用户分享的库文件使用方法,我们稍微整理一下,把代码改成我们想要的样子。

  1. 先连接wifi
  2. 判断时间是否是正确的,如果不正确就去同步时间
  3. 如果时间正确就每秒中增加我们的时钟信息,顺便打印出来

上代码

#include 
#include 
#include 

#ifndef STASSID
#define STASSID "你的WiFi名称"
#define STAPSK  "你的WiFi密码"
#endif

const char * ssid = STASSID;
const char * pass = STAPSK;
// 定义时分秒
unsigned int h = 99, m = 99, s = 99;
// 监听本地UDP数据包端口
unsigned int localPort = 2390;
// NTP服务器IP地址
IPAddress timeServerIP;
// NTP服务器网址
const char* ntpServerName = "time.windows.com";
// NTP数据包数据长度
const int NTP_PACKET_SIZE = 48;

byte packetBuffer[ NTP_PACKET_SIZE];

WiFiUDP udp;
// 创建一个需要定时调度的对象
Ticker ticker;

void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.print("连接wifi中 ");
  Serial.println(ssid);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, pass);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");

  Serial.println("WiFi已连接");
  Serial.println("设备IP地址: ");
  Serial.println(WiFi.localIP());

  Serial.println("开启UDP通信");
  udp.begin(localPort);
  Serial.print("本地端口为: ");
  Serial.println(udp.localPort());
}

void loop() {
  //get a random server from the pool
  WiFi.hostByName(ntpServerName, timeServerIP);
  if (h == 99 || m == 99 || s == 99) {
    sendNTPpacket(timeServerIP); // send an NTP packet to a time server
    // 等一秒后获取结果
    delay(1000);
    setTimes();
    // 设置定时累加时间操作
    ticker.attach(1, addtime);
  }
}


/**
   累加时间
*/
void addtime() {
  if (s == 59) {
    s = 0;
    if (m == 59) {
      m = 0;
      if (h == 23) {
        h = 0;
      } else {
        h++;
      }
    } else {
      m++;
    }
  } else {
    s++;
  }
  Serial.print("当前时间为: ");
  Serial.print(h);
  Serial.print(":");
  Serial.print(m);
  Serial.print(":");
  Serial.println(s);
}

/**
   获取十分秒信息
*/
void setTimes() {
  //解析Udp数据包
  int cb = udp.parsePacket();
  if (!cb) {
    //解析包为空
    Serial.println("没有接收到任何的数据包!");
  } else {
    //解析包不为空
    Serial.print("接收到的数据包的长度为: ");
    Serial.println(cb);
    // 解析UDP数据包中的数据
    udp.read(packetBuffer, NTP_PACKET_SIZE);
    // 说明 todo这里获取到的时间其实不是真实的时间,实际上还包含了网络延时的,但是为了方便,这里我们忽略这个因素的存在
    // 取出t2时间的高位和低位数据拼凑成以秒为单位的时间戳
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
    // 拼凑成以秒为单位的时间戳(时间戳的记录以秒的形式从 1900-01-01 00:00:00 算起)
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    Serial.print("1900年格式标准的时间戳为:");
    Serial.println(secsSince1900);
    // 前面的32bit是时间戳的秒数(是用1900-01-01 00:00:00开始的秒数,但是我们的是1970年,所以需要减掉2208988800秒)
    const unsigned long seventyYears = 2208988800UL;
    unsigned long epoch = secsSince1900 - seventyYears;
    Serial.print("1970年格式标准的时间戳为:");
    Serial.println(epoch);

    // 这里加8 是因为时区的问题,如果不加8,得到的结果就会是其他时区的时间
    h =  (epoch  % 86400L) / 3600 + 8;
    m = (epoch % 3600) / 60;
    s = epoch % 60;
  }
}

/**
   发送ntp协议数据包
*/
void sendNTPpacket(IPAddress& address) {
  Serial.println("发送ntp数据包...");
  // 将字节数组的数据全部设置为0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // 请求部分其实是有很多数据的,具体的请看参考请求报文说明,这里我们就只设置一个请求头部分即可
  packetBuffer[0] = 0b11100011;
  // 配置远端ip地址和端口
  udp.beginPacket(address, 123);
  // 把数据写入发送缓冲区
  udp.write(packetBuffer, NTP_PACKET_SIZE);
  // 发送数据
  udp.endPacket();
}

效果图

3.png

参考文章

ESP8266物联网创意点阵时钟,女朋友看了都想要!
从零开始的ESP8266探索(11)-定时任务调度器Ticker使用演示
ESP8266 – WiFiUDP库

你可能感兴趣的:(NodeMCU(ESP8266)获取NTP时间)