基于STM32F4+ESP8266+OLED的天气查询系统

STM32F4+ESP8266+OLED的天气查询系统

在嵌入式系统领域,利用 STM32 和 ESP8266 实现天气查询并在 LCD 屏幕上显示的项目已经有很多文章和教程了。然而,我想在这篇博客中分享一个特定的项目,关于如何使用正点原子的 STM32F407 开发板和 ESP8266 模块来实现天气查询(无需手动发送 AT 指令),并将结果显示在 OLED 屏幕上。

开发板:STM32F407ZGT6(正点原子探索者)
外设:正点原子0.96’ OLED 显示器模块、ESP8266
软件:Keil MDK5、串口调试助手

项目流程图

在这里插入图片描述

一、注册“心知天气”

心知天气是一家提供高精度气象数据和行业气象解决方案的企业,通过标准的 Restful API 接口,为各行各业提供实时、准确、全面的天气数据服务。心知天气的数据覆盖全国的每个角落,实现分钟级天气数据更新频率,基于 AI 增强更准确的天气预报。
本项目是通过 ESP8266 连接网络,向“心知天气”请求天气信息。所以需要注册账号,得到自己的API 私钥。
在这里插入图片描述

二、ESP8266 连接网络

访问天气平台需要先连接网络。本项目使用 AT 指令控制 ESP8266 连接手机热点。关于 AT 指令,可以简单了解一下,本文最后给出相关文章。

  1. 在代码中定义 wifista_ssid 和 wifista_password,分别存储热点名称和密码。
  2. 发送 AT 指令检测 ESP8266 模块是否正常工作,并关闭回显功能。然后,发送AT+CWMODE=1 指令设置 ESP8266 模块为 STA 模式,即客户端模式。
  3. 发送 AT+CIPMUX=0 指令设置 ESP8266 模块为单连接模式,即只能同时连接一个服务器。最后,发送 AT+CWJAP 指令并附上热点名称和密码,让 ESP8266 模块连接到手机热点,并等待返回 WIFI GOT IP 的信息,表示连接成功并获取到 IP 地址。

具体代码如下:

u8 atk_8266_wifista_config(void)
{
	u8 *p;
	p=mymalloc(SRAMIN,50);	    
	POINT_COLOR=RED;
	
	while(atk_8266_send_cmd("AT","OK",20))
	{
		atk_8266_quit_trans();//Í˳ö͸´«
		//atk_8266_send_cmd("AT+CIPMODE=0","OK",200);  	
		delay_ms(800);
	}
	while(atk_8266_send_cmd("ATE0","OK",20));
	atk_8266_send_cmd("AT+CWMODE=1","OK",50);		
	atk_8266_send_cmd("AT+RST","OK",20);	
	delay_ms(1000);       
	delay_ms(1000);
	delay_ms(1000);
	delay_ms(1000);
	
	atk_8266_send_cmd("AT+CIPMUX=0","OK",20);  
	sprintf((char*)p,"AT+CWJAP=\"%s\",\"%s\"",wifista_ssid,wifista_password);
	printf((char*)p,"AT+CWJAP=\"%s\",\"%s\"",wifista_ssid,wifista_password);
	while(atk_8266_send_cmd(p,"WIFI GOT IP",300));					
	myfree(SRAMIN,p);	
	return 0;		
}

需要打开串口调试助手,看到交互过程。
在这里插入图片描述

三、获取当前天气信息

连接网络之后,向“心知天气”请求信息,返回 JSON 数据。具体步骤如下:

  1. 用 sprintf 函数拼接一个 AT+CIPSTART 指令,让 ESP8266 模块建立一个 TCP 连接到心知天气的服务器。然后调用 atk_8266_send_cmd 函数发送该指令,并等待返回 OK 的信息,表示连接成功。如果返回失败,则释放内存空间并返回1。
  2. 发送 AT+CIPMODE=1 指令,该指令的作用是设置 ESP8266 模块为透传模式,即直接将串口收到的数据发送到服务器。然后,清零 USART3_RX_STA 变量,该变量用来记录串口 3 接收到的数据状态。再发送 AT+CIPSEND 指令,该指令的作用是开始透传数据。然后打印一条提示信息 “start trans…”,表示开始传输数据。
  3. 用 u3_printf 函数发送一个 GET 请求,该请求的作用是向心知天气的 API 接口请求当前重庆市的天气信息,并指定语言为英文和单位为摄氏度。请求的格式为:GET https://api.seniverse.com/v3/weather/now.json?key=你的密钥&location=chongqing&language=en&unit=c\n\n,等待服务器返回数据。
  4. 判断 USART3_RX_STA 变量是否有 0X8000 位为1,表示接收到了数据。如果是,则在串口 3 接收缓冲区的末尾加上一个结束符 0,表示字符串结束。

具体代码如下:

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);    
	res = atk_8266_send_cmd(p,"OK",500);
	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");

	u3_printf("GET https://api.seniverse.com/v3/weather/now.json?key=你的密钥&location=chongqing&language=en&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();     

	atk_8266_quit_trans();
	atk_8266_send_cmd("AT+CIPCLOSE","OK",50);        
	myfree(SRAMIN,p);
	return 0;
}

四、解析天气数据并显示

得到的 JSON 数据需要解析,本文只给出解析的步骤,其中有关 JSON 数据的内容可以参考附录中的文章。

  1. 定义一些变量和指针,用来存储和处理 JSON 数据。例如,pr 指针用来存储 JSON 字符串,strlocation 指针用来存储城市名称,strweather 指针用来存储天气状况,strtemp 指针用来存储温度,strtime 指针用来存储更新时间等。还有一些 cJSON 结构体的指针,用来表示 JSON 对象和数组。
  2. cJSON_Parse 函数将串口 3 接收到的数据转换为 cJSON 对象,并赋值给 root 指针。如果转换成功,则继续执行。
  3. 获取 results 对应的值。用 cJSON_GetObjectItem 函数从 root 对象中获取 results 键对应的值,并赋值给 pSub 指针。如果获取成功,则继续执行。获取数组中的第一个元素。接着用cJSON_GetArrayItem 函数从 pSub 数组中获取第0个元素,并赋值给 arrayItem 指针。然后用cJSON_Print 函数将 arrayItem 对象转换为字符串,并赋值给 pr 指针。再用 cJSON_Parse 函数将pr 字符串转换为 cJSON 对象,并赋值给 pItem 指针。如果转换成功,则继续执行。
  4. 获取 location 对应的值。接着用 cJSON_GetObjectItem 函数从 pItem 对象中获取 location 键对应的值,并赋值给 pSubItem 指针。如果获取成功,则继续执行。获取城市名称。获取天气状况和温度。获取更新时间。
  5. 显示城市名称、天气状况、温度和更新时间等信息。

具体代码如下:

u8 parse_now_weather(void)
{
	printf("begin parse_now_weather\n");
	char *pr,*strlocation, *strweather, *strtemp, *strtime;
	u8 size = 0;
	int len;
	u8 res;
	u8 temperature;
	pr = mymalloc(SRAMIN,1000);
	strlocation = mymalloc(SRAMIN,50);
	strweather = mymalloc(SRAMIN,50);
	strtemp = mymalloc(SRAMIN,50);
	strtime = mymalloc(SRAMIN,50);
	memset(pr,0,1000);
	memset(strlocation,0,50);
	memset(strweather,0,50);
	memset(strtemp,0,50);
	memset(strtime,0,50);
	
	cJSON* root;
	cJSON *pSub;
	cJSON *arrayItem;
	cJSON *pItem;
	cJSON *pSubItem;
	cJSON *pChildItem;
	root = mymalloc(SRAMIN,sizeof(cJSON));
	pSub = mymalloc(SRAMIN,sizeof(cJSON));
	pItem = mymalloc(SRAMIN,sizeof(cJSON));
	pSubItem = mymalloc(SRAMIN,sizeof(cJSON));
	pChildItem = mymalloc(SRAMIN,sizeof(cJSON));
	arrayItem = mymalloc(SRAMIN,sizeof(cJSON));
	
	// 获取 json 对象
	root = cJSON_Parse((const char*)USART3_RX_BUF);
	if (root != NULL)
	{
		printf("begin GetObjectItem results\n");
		// 获取 results 对应的值
		pSub = cJSON_GetObjectItem(root, "results");
		if (pSub != NULL)
		{
			printf("begin GetArrayItem 0\n");
            // 根据下标获取 json 对象数组中的对象
			arrayItem = cJSON_GetArrayItem(pSub, 0);
			pr = cJSON_Print(arrayItem);
			pItem = cJSON_Parse(pr);
			
			if (pItem != NULL)
			{
				printf("begin GetObjectItem location\n");
				/*-------------------------------城市名称---------------------------*/
				//根据location 键获取对应的值(cjson对象)
				pSubItem =cJSON_GetObjectItem(pItem, "location");
				if (pSubItem != NULL)
				{
					printf("begin GetObjectItem location name\n");
					pChildItem = cJSON_GetObjectItem(pSubItem, "name");
					if (pChildItem != NULL)
					{
						printf("begin cout location name\n");
						sprintf(strlocation,"%s", pChildItem->valuestring);
						printf("%s\n", pChildItem->valuestring);
							//OLED_Refresh_Gram();
							//OLED_ShowString(0,0, strlocation, 16);
					}
				}
				/*-------------------------------天气信息---------------------------*/
				pSubItem = cJSON_GetObjectItem(pItem,"now");
				if(pSubItem != NULL)
				{
					pChildItem = cJSON_GetObjectItem(pSubItem,"text");  
					if(pChildItem != NULL)
					{
						printf("begin cout weather\n");
						sprintf(strweather,"%s", pChildItem->valuestring);
						printf("%s\n", pChildItem->valuestring);
					}
					pChildItem = cJSON_GetObjectItem(pSubItem,"temperature");    
					if(pChildItem != NULL)
					{
						printf("begin cout temperature\n");
						sprintf(strtemp,"%s", pChildItem->valuestring);
						printf("%s\n", pChildItem->valuestring);
					}
			  }
				/*-------------------------------更新时间--------------------------*/
				pSubItem = cJSON_GetObjectItem(pItem,"last_update");  
				if(pSubItem != NULL)		
				{
						printf("begin cout time\n");
						sprintf(strtime,"%s", pSubItem->valuestring);
						printf("%s\n", pSubItem->valuestring);
				}
		  }
	}

	while(1)
	{
		OLED_Refresh_Gram();
	  OLED_ShowString(0,0, strlocation, 12);
		 OLED_ShowString(0,16, strweather, 12);
		 OLED_ShowString(60,16, strtemp, 12);
		OLED_ShowString(75,16, "*C", 12);
		 OLED_ShowString(0,32, strtime, 12);
	}
	
	
	cJSON_Delete(root);
	myfree(SRAMIN,root);
	myfree(SRAMIN,pSub);
	myfree(SRAMIN,pItem);
	myfree(SRAMIN,pSubItem);
	myfree(SRAMIN,pChildItem);
	myfree(SRAMIN,arrayItem);
	myfree(SRAMIN,pr);
	myfree(SRAMIN,strlocation);
	myfree(SRAMIN,strweather);
	myfree(SRAMIN,strtemp);
	myfree(SRAMIN,strtime);
	
	return 0;
}
}

串口调试助手的交互页面
在这里插入图片描述
OLED 屏幕显示效果图

附录

AT 指令:玩转ESP8266-01——AT指令集
JSON 数据:cJSON使用详细教程 | 一个轻量级C语言JSON解析器
本项目源码

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值