本博文实现了HTTP 每隔20秒请求一次心知天气的天气数据,并且在OLED上显示天气信息,HTTP天气信息使用cJSON进行解析。
本博文Demo所有代码下载地址:https://download.csdn.net/download/zq666888/13125206
效果图如下:
我这里使用的天气API接口是心知天气的,如果学习用的话可以申请免费版的做测试用,详情见:https://www.seniverse.com/login?callback=%2Fproducts%3Fiid%3Dnew
要想实现获取天气信息我们需要经过以下步骤:
1、设置ESP8266工作模式,连接wifi。
/******************************************************************************
* FunctionName : user_init
* Description : entry of user application, init user function here
* Parameters : none
* Returns : none
*******************************************************************************/
void ICACHE_FLASH_ATTR
user_init(void)
{
uart_init(115200,115200); // 初始化串口波特率
os_delay_us(10000); // 等待串口稳定
os_printf("SDK version:%s\n", system_get_sdk_version());
OLED_Init();// OLED初始化
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U,FUNC_GPIO4);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U,FUNC_GPIO5);
GPIO_OUTPUT_SET(GPIO_ID_PIN(4),1); //绿灯默认灭
GPIO_OUTPUT_SET(GPIO_ID_PIN(5),0); //红灯默认灭
OLED_ShowString(0,0,"ESP8266 HTTP"); // ESP8266
wifi_set_opmode(0x01);//设置wifi为Station模式
os_memcpy(MyStation.ssid,"Leagend_K2P",strlen("Leagend_K2P"));//要连接的wifi名字
os_memcpy(MyStation.password,"www.leagend.com",strlen("www.leagend.com"));//要连接的wifi密码
wifi_station_set_config(&MyStation);//初始化Station参数
wifi_station_connect();//连接wifi
os_timer_disarm(&CheckWifiState);//取消定时器
os_timer_setfn(&CheckWifiState,(os_timer_func_t *)CheckWifiFunction,NULL);
os_timer_arm(&CheckWifiState,500,1);
}
2、解析URL,进行DNS解析。
http_parse_request_url(char *URL,char *host,char *filename,unsigned short *port)
{
char *PA;
char *PB;
memset(host,0,sizeof(host));
memset(filename,0,sizeof(filename));
*port=0;
if(!(*URL)) return;
PA=URL;
if(!strncmp(PA,"http://",strlen("http://")))
PA=URL+strlen("http://");
if(!strncmp(PA,"https://",strlen("https://")))
PA=URL+strlen("https://");
PB=strchr(PA,'/');
if(PB){
memcpy(host,PA,strlen(PA)-strlen(PB));
if(PB+1){
memcpy(filename,PB+1,strlen(PB-1));
filename[strlen(PB)-1]=0;
}
host[strlen(PA)-strlen(PB)]=0;
}else{
memcpy(host,PA,strlen(PA));
host[strlen(PA)]=0;
}
PA=strchr(host,':');
if(PA)
*port=atoi(PA+1);
else
*port=80;
}
http_parse_request_url(ServiceURL,Host,Filename,&Port);//解析URL
os_printf("Host:%s\r\nFilename:%s\r\nPort:%d\r\n",Host,Filename,Port);
os_sprintf(buffer,GET,Filename,Host);
espconn_gethostbyname(&MyEspconn,Host,&addr,DNSFoundCb);//DNS功能
3、根据DNS解析到的IP和端口,在DNS解析回调函数中进行Station相关初始化并进行TCP连接
void ICACHE_FLASH_ATTR
MyStationInit(struct ip_addr *RemoteIP,struct ip_addr *LocalIP,int RemotePort)
{
MyEspconn.type = ESPCONN_TCP;//连接类型
MyEspconn.proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp));//开辟TCP空间
os_memcpy(MyEspconn.proto.tcp->remote_ip,RemoteIP,4);
os_memcpy(MyEspconn.proto.tcp->local_ip,LocalIP,4);
MyEspconn.proto.tcp->remote_port = RemotePort;
MyEspconn.proto.tcp->local_port = espconn_port();//获取 ESP8266可用的端口
//和连接失败的回调函数
espconn_regist_connectcb(&MyEspconn,TcpConnectCb);//注册连接成功的回调函数
espconn_regist_reconcb(&MyEspconn,TcpReconCb);//注册 TCP 连接发生异常断开时的回调函数
espconn_connect(&MyEspconn);
}
void DNSFoundCb(const char *name, ip_addr_t *ipaddr, void *callback_arg)
{
struct ip_info MyIPInfo;
os_printf("DNS Found\r\n");
wifi_get_ip_info(STATION_IF,&MyIPInfo);//查询 Wi-Fi Station 接口或者 SoftAP 接口的IP地址
MyStationInit(ipaddr,&MyIPInfo.ip,Port);
}
4、在TCP连接成功回调函数中发送GET请求天气数据
void TcpConnectCb(void *arg)
{
os_printf("Tcp Connect OK!\r\n");
espconn_regist_recvcb(&MyEspconn,TCPRecvCb);
espconn_regist_sentcb(&MyEspconn,TCPSendCb);
espconn_regist_disconcb(&MyEspconn,TCPDisconCb);
os_printf("buffer:%s\r\n",buffer);
espconn_send(&MyEspconn,buffer,strlen(buffer));
}
5、从TCP服务器返回的数据中进行JSON解析,获取城市、温度、湿度等天气信息
void TCPRecvCb(void *arg, char *pdata, unsigned short len)
{
os_printf("Receive Data:%s\r\n",pdata);
cJSON *Result = 0;
cJSON *now = 0;
cJSON *Temper = 0;
cJSON *Humidi = 0;
cJSON *Name = 0;
cJSON *Location = 0;
cJSON *Array = 0;
char *Body = strstr(pdata,"{\"");
if(Body)
{
OLED_Clear();
os_printf("Find JSON Body!\r\n");
cJSON *DataRoot = cJSON_Parse((const char *)Body);
if(DataRoot)
{
os_printf("JSON Parse OK!\r\n");
Result = cJSON_GetObjectItem(DataRoot,"results");
if(Result)
{
os_printf("Parse results OK!\r\n");
Array = cJSON_GetArrayItem(Result,0);//找到数组的第0个元素
if(Array)
{
Location = cJSON_GetObjectItem(Array,"location");
if(Location)
{
Name = cJSON_GetObjectItem(Location,"name");
if(Name)
{
char *str = Name->valuestring;
os_printf("name:%s\r\n",str);
OLED_ShowString(0,0,str);
}
}
now = cJSON_GetObjectItem(Array,"now");
if(now)
{
Temper = cJSON_GetObjectItem(now,"temperature");
if(Temper)
{
char *Tem = Temper->valuestring;
os_printf("temperature:%s\r\n",Tem);
OLED_ShowString(0,2,"Tem:");
OLED_ShowString(33,2,Tem);
}
Humidi = cJSON_GetObjectItem(now,"humidity");
if(Humidi)
{
char *Hum = Humidi->valuestring;
os_printf("humidity:%s\r\n",Hum);
OLED_ShowString(0,4,"Hum:");
OLED_ShowString(33,4,Hum);
}
}
}
}
else
{
os_printf("Parse results error!\r\n");
}
cJSON_Delete(DataRoot);
}
else
{
os_printf("JSON Parse Error!\r\n");
}
}
else
{
os_printf("Not Find JSON Body!\r\n");
}
os_timer_disarm(&BreakTcp);
os_timer_setfn(&BreakTcp,(os_timer_func_t *)BreakTcpFun,NULL);
os_timer_arm(&BreakTcp,5000,0);
}
6、设置一个定时器,处理完数据断开TCP连接,20秒后重新请求数据并解析。
void TCPDisconCb(void *arg)
{
os_printf("Tcp Break!\r\n");
os_timer_disarm(&ConnectTcp);
os_timer_setfn(&ConnectTcp,(os_timer_func_t *)ConnectTcpFun,NULL);
os_timer_arm(&ConnectTcp,20000,0);
}
关于cJSON的具体用法请参考:https://zhuanlan.zhihu.com/p/53730181