什么是SNTP(Simple Network Time Protocol)?简单时钟控制协议,主要用来同步因特网中的计算机时钟。
SNTP,使用的默认端口号是 UDP123。
ESP8266中,SNTP相关的API:
1 设置SNTP服务器(IP地址、域名两种方式);
2 查询SNTP时间戳;
在 API 参考手册中,还给出了示例:
1 设置SNTP服务器,使能SNTP;
2 设置软件定时器,在定时器回调函数中,获取SNTP信息;
3 在定时器回调函数中,查询SNTP时间戳,并转换成实时时间;
国内可用的NTP服务器;
接下来看程序:
1 user_init函数,初始化STA模式和软件定时器;
2 STA模式初始化函数,设置STA模式和WiFi参数;
3 软件定时器回调函数,获取IP地址,初始化SNTP,代码如下:
// IP定时的回调函数
void ICACHE_FLASH_ATTR OS_Timer_IP_cb(void)
{u8 C_LED_Flash = 0; // LED闪烁计次struct ip_info ST_ESP8266_IP; // ESP8266的IP信息u8 ESP8266_IP[4]; // ESP8266的IP地址// 成功接入WIFI【STA模式下,如果开启DHCP(默认),则ESO8266的IP地址由WIFI路由器自动分配】//-----------------------------------------------------------------------------------if( wifi_station_get_connect_status() == STATION_GOT_IP ) // 判断是否获取IP{wifi_get_ip_info(STATION_IF,&ST_ESP8266_IP); // 获取STA的IP信息ESP8266_IP[0] = ST_ESP8266_IP.ip.addr; // IP地址高八位 == addr低八位ESP8266_IP[1] = ST_ESP8266_IP.ip.addr>>8; // IP地址次高八位 == addr次低八位ESP8266_IP[2] = ST_ESP8266_IP.ip.addr>>16; // IP地址次低八位 == addr次高八位ESP8266_IP[3] = ST_ESP8266_IP.ip.addr>>24; // IP地址低八位 == addr高八位// 接入WIFI成功后,LED快闪3次//----------------------------------------------------for(; C_LED_Flash<=5; C_LED_Flash++){GPIO_OUTPUT_SET(GPIO_ID_PIN(4),(C_LED_Flash%2));delay_ms(100);}os_timer_disarm(&OS_Timer_IP); // 关闭定时器ESP8266_SNTP_Init_JX(); // 初始化SNTP}
}
4 SNTP初始化函数,设置SNTP服务器并初始化,然后调用软件定时器;
// 初始化SNTP
//=============================================================================
void ICACHE_FLASH_ATTR ESP8266_SNTP_Init_JX(void)
{ip_addr_t * addr = (ip_addr_t *)os_zalloc(sizeof(ip_addr_t));sntp_setservername(0, "us.pool.ntp.org"); // 服务器_0【域名】sntp_setservername(1, "ntp.sjtu.edu.cn"); // 服务器_1【域名】ipaddr_aton("210.72.145.44", addr); // 点分十进制 => 32位二进制sntp_setserver(2, addr); // 服务器_2【IP地址】os_free(addr); // 释放addrsntp_init(); // SNTP初始化APIOS_Timer_SNTP_Init_JX(1000,1); // 1秒重复定时(SNTP)
}
//=============================================================================// SNTP定时初始化
//=============================================================================
void ICACHE_FLASH_ATTR OS_Timer_SNTP_Init_JX(u32 time_ms, u8 time_repetitive)
{os_timer_disarm(&OS_Timer_SNTP);os_timer_setfn(&OS_Timer_SNTP,(os_timer_func_t *)OS_Timer_SNTP_cb,NULL);os_timer_arm(&OS_Timer_SNTP, time_ms, time_repetitive);
}
//=============================================================================
5 在SNTP定时器回调函数中,获取时间戳和实时时间并显示;
// SNTP定时回调函数
//=======================================================================================
void ICACHE_FLASH_ATTR OS_Timer_SNTP_cb(void * arg)
{// 字符串整理 相关变量//------------------------------------------------------u8 C_Str = 0; // 字符串字节计数char A_Str_Data[20] = {0}; // 【"日期"】字符串数组char *T_A_Str_Data = A_Str_Data; // 缓存数组指针char A_Str_Clock[10] = {0}; // 【"时间"】字符串数组char * Str_Head_Week; // 【"星期"】字符串首地址char * Str_Head_Month; // 【"月份"】字符串首地址char * Str_Head_Day; // 【"日数"】字符串首地址char * Str_Head_Clock; // 【"时钟"】字符串首地址char * Str_Head_Year; // 【"年份"】字符串首地址//------------------------------------------------------uint32 TimeStamp; // 时间戳char * Str_RealTime; // 实际时间的字符串// 查询当前距离基准时间(1970.01.01 00:00:00 GMT+8)的时间戳(单位:秒)//-----------------------------------------------------------------TimeStamp = sntp_get_current_timestamp();if(TimeStamp) // 判断是否获取到偏移时间{//os_timer_disarm(&OS_Timer_SNTP); // 关闭SNTP定时器// 查询实际时间(GMT+8):东八区(北京时间)//--------------------------------------------Str_RealTime = sntp_get_real_time(TimeStamp);// 【实际时间】字符串 == "周 月 日 时:分:秒 年"//------------------------------------------------------------------------os_printf("\r\n----------------------------------------------------\r\n");os_printf("SNTP_TimeStamp = %d\r\n",TimeStamp); // 时间戳os_printf("\r\nSNTP_InternetTime = %s",Str_RealTime); // 实际时间os_printf("--------------------------------------------------------\r\n");// 时间字符串整理,OLED显示【"日期"】、【"时间"】字符串//…………………………………………………………………………………………………………………// 【"年份" + ' '】填入日期数组//------------------------------------------------------------------------------Str_Head_Year = Str_RealTime; // 设置起始地址while( *Str_Head_Year ) // 找到【"实际时间"】字符串的结束字符'\0'Str_Head_Year ++ ;// 【注:API返回的实际时间字符串,最后还有一个换行符,所以这里 -5】//-----------------------------------------------------------------Str_Head_Year -= 5 ; // 获取【"年份"】字符串的首地址T_A_Str_Data[4] = ' ' ;os_memcpy(T_A_Str_Data, Str_Head_Year, 4); // 【"年份" + ' '】填入日期数组T_A_Str_Data += 5; // 指向【"年份" + ' '】字符串的后面的地址//------------------------------------------------------------------------------// 获取【日期】字符串的首地址//------------------------------------------------------------------------------Str_Head_Week = Str_RealTime; // "星期" 字符串的首地址Str_Head_Month = os_strstr(Str_Head_Week, " ") + 1; // "月份" 字符串的首地址Str_Head_Day = os_strstr(Str_Head_Month, " ") + 1; // "日数" 字符串的首地址Str_Head_Clock = os_strstr(Str_Head_Day, " ") + 1; // "时钟" 字符串的首地址// 【"月份" + ' '】填入日期数组//------------------------------------------------------------------------------C_Str = Str_Head_Day - Str_Head_Month; // 【"月份" + ' '】的字节数os_memcpy(T_A_Str_Data, Str_Head_Month, C_Str); // 【"月份" + ' '】填入日期数组T_A_Str_Data += C_Str; // 指向【"月份" + ' '】字符串的后面的地址// 【"日数" + ' '】填入日期数组//------------------------------------------------------------------------------C_Str = Str_Head_Clock - Str_Head_Day; // 【"日数" + ' '】的字节数os_memcpy(T_A_Str_Data, Str_Head_Day, C_Str); // 【"日数" + ' '】填入日期数组T_A_Str_Data += C_Str; // 指向【"日数" + ' '】字符串的后面的地址// 【"星期" + ' '】填入日期数组//------------------------------------------------------------------------------C_Str = Str_Head_Month - Str_Head_Week - 1; // 【"星期"】的字节数os_memcpy(T_A_Str_Data, Str_Head_Week, C_Str); // 【"星期"】填入日期数组T_A_Str_Data += C_Str; // 指向【"星期"】字符串的后面的地址// OLED显示【"日期"】、【"时钟"】字符串//------------------------------------------------------------------------------*T_A_Str_Data = '\0'; // 【"日期"】字符串后面添加'\0'OLED_ShowString(0,0,A_Str_Data); // OLED显示日期os_memcpy(A_Str_Clock, Str_Head_Clock, 8); // 【"时钟"】字符串填入时钟数组A_Str_Clock[8] = '\0';OLED_ShowString(64,2,A_Str_Clock); // OLED显示时间//…………………………………………………………………………………………………………………}
}
下载程序并执行,结果如下所示:
可以看到,串口调试助手成功的打印了SNTP时间戳和实时时间。