收藏官网首页
查看: 3675|回复: 0

[经验分享] esp8266 请求ntp时间戳实例

106

主题

107

帖子

7264

积分

论坛元老

Rank: 8Rank: 8

积分
7264
跳转到指定楼层
楼主
 楼主| 发表于 2023-10-7 23:16:52 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
校园创客福利
    NTP时间戳(Network Time Protocol Timestamp)是一种用于在计算机和网络设备之间同步时间的标准方式。它表示从协调世界时(UTC)的标准参考点(通常是1970年1月1日)到特定事件或数据包的经过的时间。NTP时间戳对于许多网络应用非常重要,因为它可以确保设备在分布式系统中具有一致的时间。在机智云和许多其他云服务中,都需要依赖时间戳进行验证或者记录数据的时间,此外在使用SSL(Secure Sockets Layer)或TLS(Transport Layer Security)等安全协议进行通信时,时间戳可以用于验证证书的有效性。SSL证书通常包含证书的有效期,而NTP时间戳用于确保证书在其有效期内。

要想在8266的non-os sdk上实现获取ntp时间戳,我们有两种方式,一种是使用espconn的udp协议请求ntp服务器对接收的数据进行解析,另一种是使用sdk提供的接口进行ntp服务器请求,第一种虽然复杂,但是再某些情况下可以保证数据解析的正确,第二种则较为简单,设置好ntp服务器后,在wifi连接的情况下会自动请求ntp时间戳。

首先是第一种,使用udp进行请求ntp服务器,使用这种方式,我们要了解ntp协议的报文格式,请求头,如果抛开精度要求,我们主要关注vn版本号和mode模式,mode模式主要为3和4 3是我们作为客户端发起请求,4则是服务端返回的数据。

首先我们定义需要请求的ntp服务器,端口号,包大小和超时时间,通常端口号是123。
  1. #define NTP_SERVER "pool.ntp.org"
  2. #define NTP_PORT 123
  3. #define NTP_PACKET_SIZE 48
  4. #define NTP_TIMEOUT_MS 5000
复制代码
接下来,我们实现udp发送请求到ntp服务器:

  1. static void ntp_request() {
  2.     static unsigned int ntp_packet[NTP_PACKET_SIZE / 4];
  3.     os_memset(ntp_packet, 0, sizeof(ntp_packet));

  4.     ntp_packet[0] = 0x08000000;

  5.     udp_conn.type = ESPCONN_UDP;
  6.     udp_conn.state = ESPCONN_NONE;
  7.     udp_conn.proto.udp = (esp_udp *)os_zalloc(sizeof(esp_udp));
  8.     udp_conn.proto.udp->local_port = espconn_port();
  9.     udp_conn.proto.udp->remote_port = NTP_PORT;

  10.     ip_addr_t ntp_server_ip;
  11.     espconn_gethostbyname(&udp_conn, NTP_SERVER, &ntp_server_ip, ntp_recv_callback);
  12.     udp_conn.proto.udp->remote_ip[0] = ip4_addr1(&ntp_server_ip);
  13.     udp_conn.proto.udp->remote_ip[1] = ip4_addr2(&ntp_server_ip);
  14.     udp_conn.proto.udp->remote_ip[2] = ip4_addr3(&ntp_server_ip);
  15.     udp_conn.proto.udp->remote_ip[3] = ip4_addr4(&ntp_server_ip);

  16.     espconn_create(&udp_conn);

  17.     espconn_sent(&udp_conn, (uint8 *)ntp_packet, sizeof(ntp_packet));
  18. }
复制代码


接下来,实现udp回调得到的数据解析:

  1. static void ntp_recv_callback(void *arg, char *pdata, unsigned short len) {
  2.     if (len < NTP_PACKET_SIZE) {
  3.         os_printf("Invalid NTP response\n");
  4.     } else {
  5.         uint64_t ntp_timestamp = ((uint64_t)pdata[43] << 56) | ((uint64_t)pdata[42] << 48) | ((uint64_t)pdata[41] << 40) |
  6.                                  ((uint64_t)pdata[40] << 32) | ((uint64_t)pdata[39] << 24) | ((uint64_t)pdata[38] << 16) |
  7.                                  ((uint64_t)pdata[37] << 8) | (uint64_t)pdata[36];

  8.         uint64_t unix_timestamp = ntp_timestamp - 2208988800UL;

  9.         os_printf("NTP Timestamp: %llu\n", unix_timestamp);
  10.     }

  11.     espconn_delete(&udp_conn);
  12.     os_free(udp_conn.proto.udp);
  13. }
复制代码
在这里,因为ntp请求得到的时间是以1900年为基准,而unix时间则是以1970年为基准,所以我们要减去2208988800UL 来转换得到unix时间戳。

第二种,使用自带的ntp函数自动请求服务器;



从手册可以看到,自带的函数已经足够我们简单实现ntp时间戳获取了,并且自带了适用我们时区的时间戳转换。

因此,我们首先设置一下ntp服务器,最多可以设置三个:

  1. void ICACHE_FLASH_ATTR
  2. SNTP_Init(void)
  3. {
  4.         sntp_setservername(0,"0.cn.pool.ntp.org");
  5.         sntp_setservername(1,"1.cn.pool.ntp.org");
  6.         sntp_setservername(2,"2.cn.pool.ntp.org");
  7.         sntp_init();
  8. }
复制代码


然后在user_init里调用初始化ntp服务器函数,最后在自行创建的任务里调用sntp_get_real_time(),参数传入sntp_get_current_timestamp当前时间戳即可。

我们到串口查看一下效果:



可以看见,成功获取到了时间,并且这个ntp是可以由esp8266自动维持,也就是我们有任何接口需要显示时间或者发送时间戳都是非常方便的。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

加入Q群 返回顶部

版权与免责声明 © 2006-2024 Gizwits IoT Technology Co., Ltd. ( 粤ICP备11090211号 )

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