大家遇到什么什么题,可以私聊我。
近期由于某些原因,玩了一下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;
}
最后附上代码,请自行下载,谢谢:
代码资料