赞
踩
大家遇到什么什么题,可以私聊我。
近期由于某些原因,玩了一下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
为了方便大家查看,我保存了串口调试助手发送指令以及模块返回情况,如下图:
上图中,最后一行,就是发送指令联网后返回来的一串字符串,有些是乱码,乱码部分其实就是中文,只不多串口没有对应的协议,没办法解析而已。这一串就是当前天气情况,也是我们需要解析的部分,中文部分,我们是需要把它转换成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; }
最后附上代码,请自行下载,谢谢:
代码资料
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。