[文章]【HarmonyOS HiSpark Wi-Fi IoT 套件试用连载】六、同步网络时间

阅读量0
0
0

今天的帖子我写的是关于网络时间同步,主要功能为按下按键,进入网络NTP时间获取功能,最后完成网络时间的同步。

一、NTP时间获取

首先说说怎么获取网络时钟。现在很多NTP时间服务器,从服务器中就能获取网络时间。我所使用的时间服务器为time.windows.com,IP地址为52.231.114.183,端口号为123,采用UDP协议。需要先采用UDP方式,连接到该服务器。之后再发送48个字节的十六进制数(不用回车加换行):0b 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 d6 6d d9 00 00 00 00 00然后就可以看到时间服务器返回来的数据,下图是我使用调试组手获得的数据,正常情况下一共会收到48个字节数据,第41-44字节的数据是我们所需要的时间数据。如下获得的NTP数据为0xE398556A。

  • 注意:该NTP数据是从1900年开始算的,而时间戳是从1970年开始算的

ntp.JPG

二、软件设计
首先需要移植许思维老师例程的部分代码。其中包括:net_common.h、net_demo.h、net_params.h、wifi_connecter.c、wifi_connecter.h。
其中net_params.h需要注意
PARAM_HOTSPOT_SSID改为你的路由器或者wifi热点名字,PARAM_HOTSPOT_PSK更改为路由器获取热点密码。
  1. #ifndef PARAM_HOTSPOT_SSID
  2. #define PARAM_HOTSPOT_SSID "YYYYY"   // your AP SSID
  3. #endif

  4. #ifndef PARAM_HOTSPOT_PSK
  5. #define PARAM_HOTSPOT_PSK  "123456"  // your AP PSK
  6. #endif
复制代码
1. getNTP.c

该函数主要完成网络连接,连接时间服务器及时间获取,并根据获取到的NTP时间计算出对应的时间戳。获取到的NTP时间数值是从1900年开始计算,时间戳数值从1970年开始计算。因此需要减去1970-1900年间的数值,才能得到我们所需的时间戳。

  1. #include <errno.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <unistd.h>

  5. #include "net_demo.h"
  6. #include "net_common.h"
  7. #include "net_params.h"
  8. #include "wifi_connecter.h"
  9. #include "ohos_init.h"
  10. #include "cmsis_os2.h"

  11. #define     NTPIPADDR       "52.231.114.183"
  12. #define     NTPPORT         123

  13. #define NTP_TIMESTAMP_DELTA 2208988800ull

  14. extern  uint32_t timedata;
  15. extern bool GetNTPFlag;

  16. void getNtpTime(void)
  17. {

  18.     uint32_t NTP_Time;
  19.     unsigned char buf[48];//存储NTP服务器返回的数据
  20.     unsigned char NTP_Data[]=
  21.      { 0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  22.        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  23.        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  24.        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  25.        0x0d6,0x06d,0x0d9,0x00,0x00,0x00,0x00,0x00};
  26.     ; //48字节的报文


  27.     WifiDeviceConfig config = {0};

  28.     // 准备AP的配置参数
  29.     strcpy(config.ssid, PARAM_HOTSPOT_SSID);
  30.     strcpy(config.preSharedKey, PARAM_HOTSPOT_PSK);
  31.     config.securityType = PARAM_HOTSPOT_TYPE;
  32.     osDelay(10);
  33.     int netId = ConnectToHotspot(&config);

  34.     ssize_t retval = 0;
  35.     int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // UDP socket

  36.     struct sockaddr_in toAddr = {0};
  37.     toAddr.sin_family = AF_INET;
  38.     toAddr.sin_port = htons(NTPPORT); // 端口号,从主机字节序转为网络字节序
  39.     if (inet_pton(AF_INET, NTPIPADDR, &toAddr.sin_addr) <= 0) { // 将主机IP地址从“点分十进制”字符串 转化为 标准格式(32位整数)
  40.         printf("inet_pton failed!rn");
  41.         goto do_cleanup;
  42.     }

  43.     // UDP socket 是 “无连接的” ,因此每次发送都必须先指定目标主机和端口,主机可以是多播地址
  44.     retval = sendto(sockfd, NTP_Data, 48, 0, (struct sockaddr *)&toAddr, sizeof(toAddr));
  45.     if (retval < 0) {
  46.         printf("sendto failed!rn");
  47.         goto do_cleanup;
  48.     }

  49.     printf("send NTP message %ld done!rn",retval);
  50.     for(uint8_t i = 0;i<48;i++)
  51.     {
  52.         printf("%02xt",(unsigned char)NTP_Data[i]);
  53.         if( (i+1) % 8 == 0 )
  54.         printf("rn");
  55.     }
  56.     struct sockaddr_in fromAddr = {0};
  57.     socklen_t fromLen = sizeof(fromAddr);

  58.     // UDP socket 是 “无连接的” ,因此每次接收时前并不知道消息来自何处,通过 fromAddr 参数可以得到发送方的信息(主机、端口号)
  59.     retval = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&fromAddr, &fromLen);
  60.     if (retval <= 0) {
  61.         printf("recvfrom failed or abort, %ld, %d!rn", retval, errno);
  62.         goto do_cleanup;
  63.     }

  64.     NTP_Time = buf[40]<<24 | buf[40+1]<<16|buf[40+2]<<8 |buf[40+3];
  65.     timedata = NTP_Time - NTP_TIMESTAMP_DELTA;

  66.     printf("timedata is %ldn",timedata);
  67.     //response[retval] = '\0';
  68.     printf("recv UDP message {%s} %ld done!rn", buf, retval);
  69.     printf("peer info: ipaddr = %s, port = %drn", inet_ntoa(fromAddr.sin_addr), ntohs(fromAddr.sin_port));
  70.     GetNTPFlag = false;

  71.     for(uint8_t i = 0;i<48;i++)
  72.     {
  73.         printf("%02xt",(unsigned char)buf[i]);
  74.         if( (i+1) % 8 == 0 )
  75.         printf("rn");
  76.     }
  77. do_cleanup:

  78.     GetNTPFlag = false;
  79.     printf("do_cleanup...rn");
  80.     close(sockfd);

  81.     printf("disconnect to AP ...rn");
  82.     DisconnectWithHotspot(netId);
  83.     printf("disconnect to AP done!rn");
  84. }
复制代码
2. oled_demo.c

在oled_demo.c文件中增加获取时间功能代码。按下按键进入时间同步功能。

  1.         AdcRead(ANALOG_KEY_CHAN_NAME, &data, WIFI_IOT_ADC_EQU_MODEL_4, WIFI_IOT_ADC_CUR_BAIS_DEFAULT, 0);
  2.         float voltage = ConvertToVoltage(data);

  3.         if(voltage>0.45 && voltage<0.65)
  4.         {
  5.             OledShowString(16,7,"Sync time...",1);
  6.             getNtpTime();
  7.             OledFillScreen(0);
  8.         }
复制代码
3. BUILD.gn
修改BUILD.gn,增加wifi_connecter.c和getNTP.c
  1.     sources = [
  2.         "oled_demo.c",
  3.         "oled_ssd1306.c",
  4.         "timeconv.c",
  5.         "envrionment_demo.c",
  6.         "aht20.c",
  7.         "wifi_connecter.c",
  8.         "getNTP.c"
  9.     ]
复制代码
三、结果演示
按下OLED显示板左边按键,进入时间同步功能。会显示“Sync time...”提示。
时间获取.jpg
时间同步完成后,可以看到OLED显示为最新同步的时间。
时间获取完成.jpg


四、总结
NTP时间获取很简单,只需要设备连接到网络,并且连接到时间服务器,再发送48个字节的固定数据到服务器,服务器会自动返回带有NTP时间数值的报文。下一篇我打算写一篇关于获取天气预报的贴子,涉及到如何从网络获取天气预报数据,如何使用CJson解析Json格式的天气预报内容。
还有2个多小时就2021年了,祝各位工程师2021越来越好。


回帖

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
链接复制成功,分享给好友