基于STM32和ESP8266的天气预报系统
本章将介绍一个关于STM32+ESP8266的练手小项目,项目来源地址在下面:
http://www.openedv.com/forum.php?mod=viewthread&tid=229818&highlight=%CC%EC%C6%F8
项目的源码地址请见文章最后。
一、实验环境要求
1.1硬件要求
● STM32单片机
● ESP8266WIFI模块
● LCD屏幕
● SD卡,安装文件系统
1.2软件环境要求
● Keil5
● 程序烧录软件
● 固件烧录软件
● 串口调试助手
二、系统示意图
系统整体示意图:
为增加学习兴趣,附上实验结果图:
三、程序流程图
程序流程图主要是主程序的流程图,如下:
在流程图中,单片机的初始化主要是一些硬件的初始化,主要有中断、串口、按键、LCD等等一些初始化,具体内容参考程序,这里即将不会介绍了。
四、ESP8266配置
4.1 AT固件烧录
由于本项目STM32单片机与WIFI模块之间是通过串口3发送AT指令来实现配置,因此需要烧录AT固件,固件相关资源见文章最后。
硬件连接示意图:
烧录步骤如下:
先打开烧录软件,软件在文的末尾提供:
然后对烧录软件进行配置,这一步很关键,按照下图进行配置:
固件是bin文件:
4.2 ESP8266配置函数atk_8266_wifista_config()
函数的具体内容参看下图:
主要通过发送AT指令来配置ESP8266,主要做了以下工作:
● 检查ESP8266硬件连接是否正常;
atk_8266_send_cmd("AT","OK",20)
● 配置wifi为STA模式;
atk_8266_send_cmd("AT+CWMODE=1","OK",50)
● 连接路由器。
atk_8266_send_cmd("AT+CIPMUX=0","OK",20); //0:单连接;1:多连接
sprintf((char*)p,"AT+CWJAP=\"%s\",\"%s\"",wifista_ssid,wifista_password);//传输路由器参数
while(atk_8266_send_cmd(p,"WIFI GOT IP",300)); //连接路由器
五、获取天气
5.1获取天气函数get_current_weather()
函数在工程的weather.c文件中。
//获取实时天气
//成功返回0,失败返回1
u8 get_current_weather(void)
{
u8 *p;
u8 res;
p=mymalloc(SRAMIN,40);
sprintf((char*)p,"AT+CIPSTART=\"TCP\",\"%s\",%s",WEATHER_SERVERIP,WEATHER_PORTNUM); //配置服务器IP和PORT,建立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); //´开启透传模式
USART3_RX_STA=0;
atk_8266_send_cmd("AT+CIPSEND","OK",100); //开始透传
printf("start trans...\r\n");
//发送get请求:网站密钥+目的城市
u3_printf("GET https://api.seniverse.com/v3/weather/now.json?key=“修改处:你的自密钥”&location=“修改处:目的城市拼音”&language=zh-Hans&unit=c\n\n");
delay_ms(20);
USART3_RX_STA=0;
delay_ms(1000);
if(USART3_RX_STA&0X8000) //接收天气数据
{
USART3_RX_BUF[USART3_RX_STA&0X7FFF]=0;//添加结束符
}
parse_now_weather(); //CJSON解析
atk_8266_quit_trans();//退出透传
atk_8266_send_cmd("AT+CIPCLOSE","OK",50); //关闭TCP连接
myfree(SRAMIN,p);
return 0;
}
分析如下:
● 建立TCP连接,连接天气服务器;
sprintf((char*)p,"AT+CIPSTART=\"TCP\",\"%s\",%s",WEATHER_SERVERIP,WEATHER_PORTNUM); //配置服务器IP和PORT,建立TCP连接
res = atk_8266_send_cmd(p,"OK",200);//发送TCP连接命令
● 开启透传模式;
atk_8266_send_cmd("AT+CIPMODE=1","OK",100); //´开启透传模式
atk_8266_send_cmd("AT+CIPSEND","OK",100); //开始透传
● 发送get请求;
u3_printf("GET https://api.seniverse.com/v3/weather/now.json?key=“修改处:你的自密钥”&location=“修改处:目的城市拼音”&language=zh-Hans&unit=c\n\n");
● parse_now_weather()解析天气 ;
parse_now_weather(); //CJSON解析
下一节将介绍这个函数。
● 关闭透传;
atk_8266_quit_trans();//退出透传
● 断开TCP连接。
atk_8266_send_cmd("AT+CIPCLOSE","OK",50); //关闭TCP连接
说明:我用的是知心天气服务平台,密钥可以自己去知心网站申请。
5.2 CJSON解析
服务器发送给我们的数据是以CJSON格式,保存在USART3_RX_BUF缓存中,我们需要对其进行解析。
CJSON具体学习内容参考:
https://blog.csdn.net/Mculover666/article/details/103796256
我们只用到两个相关的API:
CJSON主要用到的相关的API:
extern cJSON *cJSON_Parse(const char *value);//从给定的json字符串中得到cjson对象
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);//根据下标获取cjosn对象数组中的对象
extern char *cJSON_Print(cJSON *item);//从cjson对象中获取有格式的json对象
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);//根据键获取对应的值(cjson对象)
extern void cJSON_Delete(cJSON *c);//删除cjson对象,释放链表占用的内存空间
从知心天气服务器获取的数据格式如下:
{
"results": [
{
"location": {
"id": "C23NB62W20TF",
"name": "西雅图",
"country": "US",
"path": "西雅图,华盛顿州,美国",
"timezone": "America/Los_Angeles",
"timezone_offset": "-07:00"
},
"now": {
"text": "多云", //天气现象文字
"code": "4", //天气现象代码
"temperature": "14", //温度,单位为c摄氏度或f华氏度
"feels_like": "14", //体感温度,单位为c摄氏度或f华氏度
"pressure": "1018", //气压,单位为mb百帕或in英寸
"humidity": "76", //相对湿度,0~100,单位为百分比
"visibility": "16.09", //能见度,单位为km公里或mi英里
"wind_direction": "西北", //风向文字
"wind_direction_degree": "340", //风向角度,范围0~360,0为正北,90为正东,180为正南,270为正西
"wind_speed": "8.05", //风速,单位为km/h公里每小时或mph英里每小时
"wind_scale": "2", //风力等级,请参考:http://baike.baidu.com/view/465076.htm
"clouds": "90", //云量,单位%,范围0~100,天空被云覆盖的百分比 #目前不支持中国城市#
"dew_point": "-12" //露点温度,请参考:http://baike.baidu.com/view/118348.htm #目前不支持中国城市#
},
"last_update": "2015-09-25T22:45:00-07:00" //数据更新时间(该城市的本地时间)
}
]
}
程序中与天气解析相关的程序:
u8 parse_now_weather(void)
{
//....
root = cJSON_Parse((const char*)USART3_RX_BUF); //获取json对象
if(root != NULL)
{
pSub = cJSON_GetObjectItem(root,"results"); //获取 results 对应的值
if(pSub != NULL)
{
arrayItem = cJSON_GetArrayItem(pSub,0); //根据下标获取josn对象数组中的对象
pr = cJSON_Print(arrayItem);
pItem = cJSON_Parse(pr); //获取json对象
if(pItem != NULL)
{
/*-------------------------------获取城市名称---------------------------*/
pSubItem = cJSON_GetObjectItem(pItem,"location"); // 根据 location 键获取对应的值(cjson对象)
if(pSubItem != NULL)
{
pChildItem = cJSON_GetObjectItem(pSubItem,"name");// 根据 name 键获取对应的值(cjson对象)
if(pChildItem != NULL)
{
utf8str = pChildItem->valuestring;
SwitchToGbk((const u8*)utf8str,strlen(utf8str),(u8 *)gbkstr,&len); //获取城市名称,转化为gbk格式
Show_Str(0,0,lcddev.width,lcddev.height,(u8 *)gbkstr,16,0); //显示城市名称
}
}
/*-------------------------------获取天气信息---------------------------*/
pSubItem = cJSON_GetObjectItem(pItem,"now");
if(pSubItem != NULL)
{
pChildItem = cJSON_GetObjectItem(pSubItem,"text"); //天气现象文字,比如多云
if(pChildItem != NULL)
{
utf8str = pChildItem->valuestring;
SwitchToGbk((const u8*)utf8str,strlen(utf8str),(u8 *)gbkstr,&len);
Show_Str(220,25,lcddev.width,lcddev.height,(u8 *)gbkstr,16,0); //显示天气现象文字
}
pChildItem = cJSON_GetObjectItem(pSubItem,"code"); //获取天气代码
if(pChildItem != NULL)
{
gbkstr = pChildItem->valuestring;
show_weather_icon((u8 *)gbkstr,0); //根据天气代码,显示天气图片
}
pChildItem = cJSON_GetObjectItem(pSubItem,"temperature"); //获取温度
if(pChildItem != NULL)
{
gbkstr = pChildItem->valuestring;
temperature = str2int((u8 *)gbkstr);
gui_show_num(140,22,2,RED,54,temperature,0x80);
printf("wendu = %d\r\n",temperature);
}
}
pSubItem = cJSON_GetObjectItem(pItem,"last_update"); //获取最新更新时间
if(pSubItem != NULL)
{
gbkstr =pSubItem->valuestring;
LCD_ShowString(0,92,200,20,12,(u8*)gbkstr);
printf("1day_updata_time = %s\r\n",(u8*)gbkstr);
}
}
cJSON_Delete(pItem);
}
}
//...
return 0;
}
通过以上解析函数就能够得到,从服务器发送过来的天气信息,获取三天信息跟以上步骤雷同,就不再赘述了。
六、资源获取
第一次写项目博客,经验不足,描述不全,希望以后提高。
最后附上工程源码,提供给大家学习:
https://github.com/caosongwang/ESP8266