stm32 + ESP8266 wifi获取网络时间和天气 stm32天气预报

大家遇到什么什么题,可以私聊我。

近期由于某些原因,玩了一下stm32+WiFi获取网络天气和网络时间。之前一直觉得这个东西会有点难度,其实,做完了才发现,其实那只是想象中的难而已。现在,将这几天的成果分享一下,做个学习记录,也分享给有需要的伙伴。

有关原文链接如下:
原文地址

在文末有改良好的代码,问题肯定是有的,但是比之前,好很多,有需要的朋友可以自己下载。

上面链接给的代码其实是有问题的,经过测试,发现三点不足:
1.RTC时钟,仅仅只是RTC时间;
2.程序运行一段时间后(三个小时内),100%会死机,因此基本可以判断出,程序是有问题的具体原因不详,貌似是堆栈溢出导致的;
3.天气更新情况,在没有死机的情况下,看似是在更新,其实从串口调试助手可以看到,人机交互界面并没有更新。

经过改善,满足了以下要求:
1.开机获取网络时间,之后将会由这个时间借助RTC继续运行;
2.在测试的三个小时内没有再出现死机的情况;
3.在测试的三个小时内,天气每隔一段时间都会自动更新,并体现在LCD屏幕上。

一、测试要求(只说我用到的)

1.硬件:

①stm32f103ZET6(我用的战舰板,请用RAM容量尽量大的芯片)
		②TFTLCD屏(我的是4.3寸)
		③ESP8266 WIFI串口模块(串口3)
		④Jlink(程序烧录)
		⑤串口线
		⑥路由器

2.软件

①keil5
②串口调试助手

二、相关设置

这里就不做过多的介绍,只做简单的说明,有疑问请参考上面给出的链接。
①WiFi串口模块固件为AT固件
②jlink需要安装对应的驱动,用于程序的调试和烧录
③串口也需要对应的驱动,用于输出调试信息

三、程序流程

其实简单点说,就几个步骤而已,我把他转换成几条AT指令,只需要按照下面步骤控制WiFi模块,就可以获取到网络天气和时间信息,如下:
1.首先来看看获取当前天气情况

①AT    ack:OK                          //判断WiFi模块是否存在以及是否支持AT指令
②ATE0    ack: OK		                //取消回显,可要可不要,为了方便接下来演示,这句我就不写进去了
③AT+CWMODE=1  ack:OK      //设置WiFi模块为STA模式
④AT+RST  ack:OK			               //复位WiFi模块
⑥AT+CIPMUX=0  ack:OK			//单连接模式
⑦AT+CWJAP="HUAWEI nova 5","b12345678"   ack:WIFI GOT IP //连接热点,对应的是热点名和热点密码,注意,务必等待该条指令返回WIFI GOT IP后再发送下面的指令
⑧AT+CIPSTART="TCP","api.seniverse.com",80     ack:OK  //建立TCP连接
⑨AT+CIPMODE=1  ack:OK   //透传模式
⑩AT+CIPSEND  ack:OK     //AT+CIPMODE=1并且作为客户端模式下,进入透传模式(需要支持硬件流控,否则大量数据情况下会丢数据)模块收到指令后先换行返回”>”,然后会发送串口接收到的数据。

发送完上面的指令,并正确返回后,发送下面一串数据(不需要换行)

GET https://api.seniverse.com/v3/weather/now.json?key=SWaCmu3LmzT_kS21g&location=changsha&language=zh-Hans&unit=c
//发送获取天气信息的网址,记得前面的GET,不可少

最后,记得发送一条退出透传的指令,否则后面AT指令将无法发送:

+++(不需要换行,也不要有空格)
AT+CIPCLOSE  ack:OK

为了方便大家查看,我保存了串口调试助手发送指令以及模块返回情况,如下图:
stm32 + ESP8266 wifi获取网络时间和天气 stm32天气预报_第1张图片
上图中,最后一行,就是发送指令联网后返回来的一串字符串,有些是乱码,乱码部分其实就是中文,只不多串口没有对应的协议,没办法解析而已。这一串就是当前天气情况,也是我们需要解析的部分,中文部分,我们是需要把它转换成utf8格式的(具体请看代码),整个解析过程代码(其实很简单的)如下:

//解析当前天气
void parse_now_weather(void)
{
	u16 i = 0,j = 0,line = 0;
	int len = 0;
	char gbkstr[50] = {0};
	Now_Weather *weather_Structure = mymalloc(SRAMIN,sizeof(Now_Weather));
	printf("jieshou->1dayjson = %s\r\n",USART3_RX_BUF);
	for(i = 0; i < strlen((char *)USART3_RX_BUF); i++)
	{
		if(USART3_RX_BUF[i] == 'n' && USART3_RX_BUF[i+1] == 'a' && USART3_RX_BUF[i+2] == 'm' && USART3_RX_BUF[i+3] == 'e')
		{
			while(USART3_RX_BUF[i+7] != '"')
				weather_Structure->m_CityName[j++] = USART3_RX_BUF[7+(i++)];
			weather_Structure->m_CityName[j] = '\0';
			break;
		}
	}
	SwitchToGbk((const u8*)weather_Structure->m_CityName,strlen(weather_Structure->m_CityName),(u8 *)gbkstr,&len);  //获取城市名称转换为gbk文件
	if(strstr(gbkstr,"衡阳") != NULL)
		LCD_ShowString(0,20,sizeof("hengyang")*8,16,16,"hengyang");
	else if(strstr(gbkstr,"长沙") != NULL)  
		LCD_ShowString(0,140,sizeof("changsha")*8,16,16,"changsha");
	
	j = 0;
	for(i = 0; i < strlen((char *)USART3_RX_BUF); i++)
	{
		if(USART3_RX_BUF[i] == 't' && USART3_RX_BUF[i+1] == 'e' && USART3_RX_BUF[i+2] == 'x' && USART3_RX_BUF[i+3] == 't')
		{
			while(USART3_RX_BUF[i+7] != '"')
				weather_Structure->m_Text[j++] = USART3_RX_BUF[7+(i++)];
			weather_Structure->m_Text[j] = '\0';
			break;
		}
	}
	memset(gbkstr,0,sizeof(gbkstr));
	if(flag == 1) line = 160;//长沙
	else if(flag == 2) line = 40;//衡阳
	SwitchToGbk((const u8*)weather_Structure->m_Text,strlen(weather_Structure->m_Text),(u8 *)gbkstr,&len);
	if(strstr(gbkstr,"阴") != NULL)
		LCD_ShowString(88,line,sizeof("overcast      ")*8,16,16,"overcast      ");
	else if(strstr(gbkstr,"多云") != NULL)
		LCD_ShowString(88,line,sizeof("overcast      ")*8,16,16,"cloudy        ");
	else if(strstr(gbkstr,"晴") != NULL)
		LCD_ShowString(88,line,sizeof("overcast      ")*8,16,16,"fine day      ");
	else if(strstr(gbkstr,"小雨") != NULL)
		LCD_ShowString(88,line,sizeof("overcast      ")*8,16,16,"light rain    ");
	else if(strstr(gbkstr,"中雨") != NULL)
		LCD_ShowString(88,line,sizeof("overcast      ")*8,16,16,"moderate rain ");
	else if(strstr(gbkstr,"大雨") != NULL)
		LCD_ShowString(88,line,sizeof("overcast      ")*8,16,16,"heavy rain    ");
	else if(strstr(gbkstr,"雷阵雨") != NULL)
		LCD_ShowString(88,line,sizeof("overcast      ")*8,16,16,"thunder shower");
	else if(strstr(gbkstr,"阵雨") != NULL)
		LCD_ShowString(88,line,sizeof("overcast      ")*8,16,16,"shower        ");

	
	j = 0;
	for(i = 0; i < strlen((char *)USART3_RX_BUF); i++)
	{
		if(USART3_RX_BUF[i] == 't' && USART3_RX_BUF[i+1] == 'e' && USART3_RX_BUF[i+2] == 'm' && USART3_RX_BUF[i+3] == 'p')
		{
			while(USART3_RX_BUF[i+14] != '"')
				weather_Structure->m_Temp[j++] = USART3_RX_BUF[14+(i++)];
			weather_Structure->m_Temp[j] = '\0';
			break;
		}
	}
	
	if(flag == 1) line = 220;//长沙
	else if(flag == 2) line = 100;//衡阳
	LCD_ShowString(100,line,25,20,16,(u8*)weather_Structure->m_Temp);	
	printf("wendu = %s\r\n",weather_Structure->m_Temp);
	
	j = 0;
	for(i = 0; i < strlen((char *)USART3_RX_BUF); i++)
	{
		if(USART3_RX_BUF[i] == 'l' && USART3_RX_BUF[i+1] == 'a' && USART3_RX_BUF[i+2] == 's' && USART3_RX_BUF[i+3] == 't')
		{
			while(USART3_RX_BUF[i+14] != '"')
				weather_Structure->m_LastUpdataTime[j++] = USART3_RX_BUF[14+(i++)];
			weather_Structure->m_LastUpdataTime[j] = '\0';
			break;
		}
	}
	if(flag == 1) line = 140;
	else if(flag == 2)line = 20;
	LCD_ShowString(210,line,200,20,16,(u8*)weather_Structure->m_LastUpdataTime);
	printf("1day_updata_time = %s\r\n",(u8*)weather_Structure->m_LastUpdataTime);
	
	myfree(SRAMIN,weather_Structure);
}

2.未来三天天气情况:
和上面类似的,我就不演示了,直接上AT指令:

①AT    ack:OK                          //判断WiFi模块是否存在以及是否支持AT指令
②ATE0    ack: OK		                //取消回显,可要可不要,为了方便接下来演示,这句我就不写进去了
③AT+CWMODE=1  ack:OK      //设置WiFi模块为STA模式
④AT+RST  ack:OK			               //复位WiFi模块
⑥AT+CIPMUX=0  ack:OK			//单连接模式
⑦AT+CWJAP="HUAWEI nova 5","b12345678"   ack:WIFI GOT IP //连接热点,对应的是热点名和热点密码,注意,务必等待该条指令返回WIFI GOT IP后再发送下面的指令
⑧AT+CIPSTART="TCP","api.seniverse.com",80     ack:OK  //建立TCP连接
⑨AT+CIPMODE=1  ack:OK   //透传模式
⑩AT+CIPSEND  ack:OK     //AT+CIPMODE=1并且作为客户端模式下,进入透传模式(需要支持硬件流控,否则大量数据情况下会丢数据)模块收到指令后先换行返回”>”,然后会发送串口接收到的数据。

完事之后,发送下面一串字符串,用于获取未来三天天气情况:

GET https://api.seniverse.com/v3/weather/daily.json?key=SWaCmu3LmzT_kS21g&location=changsha&language=zh-Hans&unit=c&start=0&days=5

最后,关闭透传,发送和上面一样的指令。

数据解析代码如下:

//解析3天天气
void parse_3days_weather(void)
{
	u16 i = 0,j = 0,line = 0;
	int len = 0;
	char gbkstr[50] = {0};
	Now_Weather *weather_Structure = mymalloc(SRAMIN,sizeof(Now_Weather));
	printf("jieshou->1dayjson = %s\r\n",USART3_RX_BUF);
	for(i = 0; i < strlen((char *)USART3_RX_BUF); i++)
	{
		if(USART3_RX_BUF[i] == 'w' && USART3_RX_BUF[i+1] == 'i' && USART3_RX_BUF[i+2] == 'n' && USART3_RX_BUF[i+3] == 'd')
		{
			while(USART3_RX_BUF[i+17] != '"')
				weather_Structure->m_WindDir[j++] = USART3_RX_BUF[17+(i++)];
			weather_Structure->m_WindDir[j] = '\0';
			break;
		}
	}
	SwitchToGbk((const u8*)weather_Structure->m_WindDir,strlen(weather_Structure->m_WindDir),(u8 *)gbkstr,&len);  //获取城市名称转换为gbk文件
	if(flag == 1) line = 200;//长沙
	else if(flag == 2) line = 80;//衡阳
	LCD_Fill(220,45,320,61,BLACK);
	if(strstr(gbkstr,"北") != NULL)           
	{
		if(strstr(gbkstr,"西") != NULL)       LCD_ShowString(0,line,sizeof("southerly        ")*8,16,16,"Northwest wind   ");
		else if(strstr(gbkstr,"东") != NULL)  LCD_ShowString(0,line,sizeof("southerly        ")*8,16,16,"Northeastern wind");									  
		else LCD_ShowString(0,line,sizeof("southerly        ")*8,16,16,"northerly        ");
		   
	}
	else if(strstr(gbkstr,"南") != NULL)  
	{
		if(strstr(gbkstr,"西") != NULL)      LCD_ShowString(0,line,sizeof("southerly        ")*8,16,16,"Southwest wind   ");
		else if(strstr(gbkstr,"东") != NULL) LCD_ShowString(0,line,sizeof("southerly        ")*8,16,16,"Southeast wind   ");
		else LCD_ShowString(0,line,sizeof("southerly        ")*8,16,16,"southerly        ");
	}

	j = 0;
	for(i = 0; i < strlen((char *)USART3_RX_BUF); i++)
	{
		if(USART3_RX_BUF[i] == 's' && USART3_RX_BUF[i+1] == 'p' && USART3_RX_BUF[i+2] == 'e' && USART3_RX_BUF[i+3] == 'e')
		{
			while(USART3_RX_BUF[i+8] != '"')
				weather_Structure->m_WindSpeed[j++] = USART3_RX_BUF[8+(i++)];
			weather_Structure->m_WindSpeed[j] = '\0';
			break;
		}
	}
	LCD_ShowString(200,line,sizeof(weather_Structure->m_WindSpeed)*8,16,16,(u8 *)weather_Structure->m_WindSpeed);   //显示风速km/h
	
	j = 0;
	for(i = 0; i < strlen((char *)USART3_RX_BUF); i++)
	{
		if(USART3_RX_BUF[i] == 's' && USART3_RX_BUF[i+1] == 'c' && USART3_RX_BUF[i+2] == 'a' && USART3_RX_BUF[i+3] == 'l')
		{
			while(USART3_RX_BUF[i+8] != '"')
				weather_Structure->m_WindGrade[j++] = USART3_RX_BUF[8+(i++)];
			weather_Structure->m_WindGrade[j] = '\0';
			break;
		}
	}
	LCD_ShowString(160,line,sizeof(weather_Structure->m_WindGrade)*8,16,16,(u8 *)weather_Structure->m_WindGrade);
	
	
	j = 0;
	for(i = 0; i < strlen((char *)USART3_RX_BUF); i++)
	{
		if(USART3_RX_BUF[i] == 'h' && USART3_RX_BUF[i+1] == 'u' && USART3_RX_BUF[i+2] == 'm' && USART3_RX_BUF[i+3] == 'i')
		{
			while(USART3_RX_BUF[i+11] != '"')
				weather_Structure->m_Humi[j++] = USART3_RX_BUF[11+(i++)];
			weather_Structure->m_Humi[j] = '\0';
			break;
		}
	}
	if(flag == 1) line = 220;//长沙
	else if(flag == 2) line = 100;//衡阳
	LCD_ShowString(160,line,sizeof(weather_Structure->m_Humi)*8,16,16,(u8 *)weather_Structure->m_Humi);
	
	
	j = 0;
	for(i = 0; i < strlen((char *)USART3_RX_BUF); i++)
	{
		if(USART3_RX_BUF[i] == 'l' && USART3_RX_BUF[i+1] == 'a' && USART3_RX_BUF[i+2] == 's' && USART3_RX_BUF[i+3] == 't')
		{
			while(USART3_RX_BUF[i+14] != '"')
				weather_Structure->m_LastUpdataTime[j++] = USART3_RX_BUF[14+(i++)];
			weather_Structure->m_LastUpdataTime[j] = '\0';
			break;
		}
	}
	
	LCD_ShowString(0,300,200,20,12,(u8*)weather_Structure->m_LastUpdataTime);
	printf("1day_updata_time = %s\r\n",(u8*)weather_Structure->m_LastUpdataTime);
	
	myfree(SRAMIN,weather_Structure);
}

3.获取网络时间
套路类似的:

①AT    ack:OK                          //判断WiFi模块是否存在以及是否支持AT指令
②ATE0    ack: OK		                //取消回显,可要可不要,为了方便接下来演示,这句我就不写进去了
③AT+CWMODE=1  ack:OK      //设置WiFi模块为STA模式
④AT+RST  ack:OK			               //复位WiFi模块
⑥AT+CIPMUX=0  ack:OK			//单连接模式
⑦AT+CWJAP="HUAWEI nova 5","b12345678"   ack:WIFI GOT IP //连接热点,对应的是热点名和热点密码,注意,务必等待该条指令返回WIFI GOT IP后再发送下面的指令
⑧AT+CIPSTART="TCP","cgi.im.qq.com",80     ack:OK  //建立TCP连接
⑨AT+CIPMODE=1  ack:OK   //透传模式
⑩AT+CIPSEND  ack:OK     //AT+CIPMODE=1并且作为客户端模式下,进入透传模式(需要支持硬件流控,否则大量数据情况下会丢数据)模块收到指令后先换行返回”>”,然后会发送串口接收到的数据。

发送完上面的指令后,发送下面一串字符

GET http://cgi.im.qq.com

最后一样,需要关闭透传模式。

数据解析格式如下:

//获取北京时间
u8 get_beijing_time(void)
{
	u8 *p;
	u8 res;
	
	u8 *resp;
	u8 *p_end;
//	u8 ipbuf[16]; 	//IP缓存
	p=mymalloc(SRAMIN,40);							//申请40字节内存
	resp=mymalloc(SRAMIN,10);
	p_end=mymalloc(SRAMIN,40);
	
	sprintf((char*)p,"AT+CIPSTART=\"TCP\",\"%s\",%s",TIME_SERVERIP,TIME_PORTNUM);    //配置目标TCP服务器
	res = atk_8266_send_cmd(p,"OK",200);//连接到目标TCP服务器
	if(res==1)
	{
		myfree(SRAMIN,p);
		return 1;
	}
	delay_ms(300);
	atk_8266_send_cmd("AT+CIPMODE=1","OK",100);      //传输模式为:透传	

	printf("设备 %s\r\n",p);
	
	CLR_BUF();
	atk_8266_send_cmd("AT+CIPSEND","OK",100);         //开始透传
	printf("start trans...\r\n");

	u3_printf("GET http://cgi.im.qq.com\n\n");
	delay_ms(20);
	CLR_BUF();	
	delay_ms(1000);

	if(USART3_RX_STA != 0)
    {
		strncpy((char *)resp,"Date",5);
        USART3_RX_BUF[USART3_RX_STA] = 0;
		//printf("get_tim_srt:%s\r\n",USART3_RX_BUF);
        if(strstr((char*)USART3_RX_BUF,(char*)resp)) 
        {       
			strncpy((char *)resp,"GMT",4);
            p_end = (u8*)strstr((char*)USART3_RX_BUF,(char*)resp);
			p = p_end - 9; 
			//printf("get_net_str %s\r\n",p);
			nwt.hour = ((*p - 0x30)*10 + (*(p+1) - 0x30) + 8) % 24;  //GMT0-->GMT8

			nwt.min = ((*(p+3) - 0x30)*10 + (*(p+4) - 0x30)) % 60;

			nwt.sec = ((*(p+6) - 0x30)*10 + (*(p+7) - 0x30)) % 60;

			nwt.year = ((*(p-5) - 0x30)*1000 + (*(p-4) - 0x30)*100+ (*(p-3) - 0x30)*10+ (*(p-2) - 0x30)); 

			nwt.date = ((*(p-12) - 0x30)*10 + (*(p-11) - 0x30)); 

			if        ((u8*)strstr((char*)USART3_RX_BUF,(char*) "Jan")) nwt.month=1; 
			else if   ((u8*)strstr((char*)USART3_RX_BUF,(char*) "Feb")) nwt.month=2; 
			else if   ((u8*)strstr((char*)USART3_RX_BUF,(char*) "Mar")) nwt.month=3; 
			else if   ((u8*)strstr((char*)USART3_RX_BUF,(char*) "Apr")) nwt.month=4; 
			else if   ((u8*)strstr((char*)USART3_RX_BUF,(char*) "May")) nwt.month=5; 
			else if   ((u8*)strstr((char*)USART3_RX_BUF,(char*) "Jun")) nwt.month=6; 
			else if   ((u8*)strstr((char*)USART3_RX_BUF,(char*) "Jul")) nwt.month=7; 
			else if   ((u8*)strstr((char*)USART3_RX_BUF,(char*) "Aug")) nwt.month=8; 
			else if   ((u8*)strstr((char*)USART3_RX_BUF,(char*) "Sep")) nwt.month=9; 
			else if   ((u8*)strstr((char*)USART3_RX_BUF,(char*) "Oct")) nwt.month=10; 
			else if   ((u8*)strstr((char*)USART3_RX_BUF,(char*) "Nov")) nwt.month=11; 
			else if   ((u8*)strstr((char*)USART3_RX_BUF,(char*) "Dec")) nwt.month=12;
 
			CLR_BUF();
												 
			printf("uddate:nettime!!!");
			RTC_Set(nwt.year,nwt.month ,nwt.date ,nwt.hour ,nwt.min,nwt.sec);											
														
	    }
		CLR_BUF();														
    }               
	atk_8266_quit_trans();//退出透传
	atk_8266_send_cmd("AT+CIPCLOSE","OK",50);         //关闭连接
	myfree(SRAMIN,p);
	myfree(SRAMIN,resp);
	myfree(SRAMIN,p_end);
	return 0;
}

最后附上代码,请自行下载,谢谢:
代码资料

你可能感兴趣的:(stm32,网络,wifi,stm32)