找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 769|回复: 8
收起左侧

stm32+ESP8266 wifi获取网络时间和天气 附源程序

  [复制链接]
廖恒 发表于 2020-11-24 15:52 | 显示全部楼层 |阅读模式
近期由于某些原因,玩了一下stm32+WiFi获取网络天气和网络时间。之前一直觉得这个东西会有点难度,其实,做完了才发现,其实那只是想象中的难而已。现在,将这几天的成果分享一下,做个学习记录,也分享给有需要的伙伴。
有关原文链接如下:https://blog.csdn.net/weixin_43952192/article/details/106877400
在文末有改良好的代码,问题肯定是有的,但是比之前,好很多,有需要的朋友可以自己下载。
上面链接给的代码其实是有问题的,经过测试,发现三点不足:1.RTC时钟,仅仅只是RTC时间;2.程序运行一段时间后(三个小时内),100%会死机,因此基本可以判断出,程序是有问题的具体原因不详,貌似是堆栈溢出导致的;3.天气更新情况,在没有死机的情况下,看似是在更新,其实从串口调试助手可以看到,人机交互界面并没有更新。
经过改善,满足了以下要求:1.开机获取网络时间,之后将会由这个时间借助RTC继续运行;2.在测试的三个小时内没有再出现死机的情况;3.在测试的三个小时内,天气每隔一段时间都会自动更新,并体现在LCD屏幕上。
一、测试要求(只说我用到的)
1.硬件:①stm32f103ZET6(我用的战舰板,请用容量尽量大的模块)②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并且作为客户端模式下,进入透传模式(需要支持硬件流控,否则大量数据情况下会丢数据)模块收到指令后先换行返回”>”,然后会发送串口接收到的数据。
发送完上面的指令,并正确返回后,发送下面一串数据(不需要换行)
  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
为了方便大家查看,我保存了串口调试助手发送指令以及模块返回情况,如下图:
1.png
上图中,最后一行,就是发送指令联网后返回来的一串字符串,有些是乱码,乱码部分其实就是中文,只不多串口没有对应的协议,没办法解析而已。这一串就是当前天气情况,也是我们需要解析的部分,中文部分,我们是需要把它转换成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并且作为客户端模式下,进入透传模式(需要支持硬件流控,否则大量数据情况下会丢数据)模块收到指令后先换行返回”>”,然后会发送串口接收到的数据。
完事之后,发送下面一串字符串,用于获取未来三天天气情况:
  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;
}
最后附上代码,请自行下载,谢谢。
51hei.png
全部资料51hei下载地址:
程序源码-天气+时间GUI.7z (6.76 MB, 下载次数: 56)

评分

参与人数 1黑币 +100 收起 理由
admin + 100 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

nsj21n 发表于 2020-11-25 08:34 | 显示全部楼层
非常好的资料,可以借此学习ESP8266了,楼主能否秀几张美图以飨读者,谢谢
回复

使用道具 举报

lightday 发表于 2020-11-25 09:38 | 显示全部楼层
非常好,有空可以参考玩下。
回复

使用道具 举报

lyMarvin 发表于 2020-11-25 12:51 | 显示全部楼层
感谢分享,有得玩儿的啦
回复

使用道具 举报

 楼主| 廖恒 发表于 2020-11-25 13:46 | 显示全部楼层
nsj21n 发表于 2020-11-25 08:34
非常好的资料,可以借此学习ESP8266了,楼主能否秀几张美图以飨读者,谢谢

好的,回复在评论中了
回复

使用道具 举报

 楼主| 廖恒 发表于 2020-11-25 13:49 | 显示全部楼层
实物图:
weather_GUI.jpg

评分

参与人数 1黑币 +30 收起 理由
admin + 30 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

 楼主| 廖恒 发表于 2020-11-25 14:18 | 显示全部楼层
nsj21n 发表于 2020-11-25 08:34
非常好的资料,可以借此学习ESP8266了,楼主能否秀几张美图以飨读者,谢谢

实物发在下面哈
回复

使用道具 举报

刘全鑫 发表于 2020-11-25 19:06 | 显示全部楼层
好东西学习一下!楼主奈斯
回复

使用道具 举报

smartphone 发表于 2021-1-14 11:25 | 显示全部楼层
很好的资料,可惜不懂
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表