关于esp8266介绍网上挺多资料的,我就不再介绍了,有不懂的地方欢迎讨论交流。主要讲解一下如何配置esp8266获取心知天气数据,并通过穿cJSON库解析其中的关键数据。


背景知识

粗略了解就行,看不懂代码再回去看对应内容。

1.GPIO输入输出(IO口配置)

2.串口通信(通过串口发送AT指令给esp8266)

3.定时器中断(通过定时器中断,将每次接收数据区分开)

4.cJSON(C语言无法直接处理get请求获取的json数据包,使用cJSON库获取关键数据)

关键函数说明

cJSON*cJSON_Parse(const char *value);//解析原始JSON数据包,并按照cJSON结构体的结构序列化整个数据包。
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)://获得字典元素object中键名为string的元素,key-value对
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);//获得数组元素array第index的元素
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);//释放cJSON_Parse函数内部申请的堆内存。

5.心知天气使用说明

心知天气是免费的,通过get命令获取天气数据时需要自己的密钥,下面展示如何获得密钥

注册以后选择免费试用

esp8266 设置时间_#include

esp8266 设置时间_#include_02

esp8266 设置时间_数据_03

查看api使用说明

esp8266 设置时间_串口_04

esp8266 设置时间_esp8266 设置时间_05

esp8266 设置时间_#include_06

配置流程

1.配置led和按键:led显示状态,key选择执行命令

2.配置串口1:输出信息到上位机,方便调试

3.配置定时器3:方便区分串口每次接收到的数据包

4.配置串口2:与esp8266进行数据传输

使用流程

硬件连接;

ESP8266-RX->PA2
ESP8266-TX->PA3
ESP8266-3V3->3V3
ESP8266-GND->GND

英文翻译问的gpt方便记忆。AT指令后面必须加空行。使用串口2向esp8266发送AT指令进行配置。

esp8266 设置时间_串口_07

配置esp8266指令流程

成功返回值

作用

1.AT

OK

返回OK表示识别到esp8266模块

2.AT+CWMODE=1

OK

设置wifi模块工作为sta模式(connection wifi mode),会记录不用每次配置

3.AT+RST

重启模块并生效

4.AT+CWJAP="WIFI名",“wifi密码”

WIFI CONNECTED

WIFI GOT IP

连接wifi(connection wifi join access point,连接wifi接入点)。会记录不用每次配置。重复配置会先显示WIFI DISCONNECTED,在进行连接。连接完成需要4-6秒(可以自行测试再选择延迟时间),中可以多等待一会。

5.AT+CIPMUX=0

OK

禁用多连接(Connection IP Multiple Connection)

6.AT+CIPSTART="TCP","目标ip“,目标端口

CONNECT


OK

连接服务器指定端口,每次要重新配置(Connection IP Start)

        7.AT+CIPMODE=1

OK

开启透传模式(connect ip mode),AT指令失效。每次传输不用设置数据长度,每次要重新配置

8.AT+CIPSEND

>

开始传输数据(connection ip send)

9.GET https://api.seniverse.com/v3/weather/now.json?key=Ses06roxQh0JI5CRq&location=wuxi&language=zh-Hans&unit=c

天气数据

GET请求获取天气数据

10.+++(不要加换行)


退出透传模式,AT指令生效。透传模式中AT指令会作为数据直接发送到所连接服务器

关键代码

实验现象,一次打印初始化信息,并获得一次天气数据

esp8266 设置时间_数据_08

按下key0键,再次获取天气数据

esp8266 设置时间_网络_09

wifista.c

#include "wifista.h"
#include "usart.h"
#include "usart2.h"
#include "string.h"
#include "delay.h"
//#include <stdlib.h>

//WIFI STA模式,设置要去连接的路由器无线参数,请根据你自己的路由器设置,自行修改.
const u8* WIFI_NAME="HL";			//路由器SSID号
const u8* wifista_encryption="WPA_WAP2_PSK";	//wpa/wpa2 aes加密方式
const u8* WIFI_PSW="qq123456"; 	//连接密码

//将AT指令接收到的应答数据通过串口1返回给电脑
//mode:0 不清零串口2状态标志位
//			1 清零串口2状态标志位
void atk_8266_at_response(mode)
{
	if(USART2_RX_STA&1<<15)//串口2接收完成
	{
		USART2_RX_BUF[USART2_RX_STA]=0;//添加结束符
		printf("%s",USART2_RX_BUF);//通过串口1输出串口2接收到的数据
		if(mode) USART2_RX_STA=0;
	}
}
 
//检查接收到的应答
//str:期待应答值
//返回值:0 未获得期待应答
//			其他 期待应答结果的位置
u8* atk_8266_check_cmd(u8 *str)
{
	u8 *strx=0;//创建指向内存地址为0的位置,避免未定义行为
	if(USART2_RX_STA&0x8fff)//接受到一次数据
	{
		USART2_RX_BUF[USART2_RX_STA]=0;//添加结束符
		strx=strstr((const u8*)USART2_RX_BUF,(const u8*)str);//通过strstr函数查找str在USART2_RX_BUF中出现的位置
	}
	
	return strx;
}

//向esp8266发送命令
//cmd:发送到命令
//ack:期望应答
//waittime:最大等待应答时间(单位10ms)	
//返回值:0 发送成功(得到了期待应答信号)
//			 1 发送失败
u8 atk_8266_send_cmd(u8 *cmd,u8 *ack,u16 waittime)
{
	u8 res=0;
	USART2_RX_STA=0;//将串口2状态标志位清零,使串口2可以接收数据
	u2_printf("%s\r\n",cmd);//发送命令,自动添加了换行符,所以传入的AT指令不用加换行符
	
	if(ack&&waittime)//需要应答
	{
		while(--waittime)//等待倒计数
		{
			delay_ms(10);//每10ms检查一次是否接收到期望应答信号
			if(USART2_RX_STA&0x8000)//串口2接收到数据
			{
//				printf("%s返回结果为:%s",cmd,USART2_RX_BUF);//调试信息查看
				if(atk_8266_check_cmd(ack))//检查接收数据是否为期望应答
				{
//					printf("%s ask:%s\r\n",cmd,(u8*)ack);//调试信息查看
				break;
				}
				USART2_RX_STA=0;//未接收到期望数据则将接收标志位清零,继续等待接收
			}
		}
		if(waittime==0) res=1;//达到最大等待时间还是没有接收到目标信号,表示发送失败
	}
	
	return res;
}
 

//退出通透模式
//返回值;0 退出成功
//   	   1 退出失败
u8 atk_8266_quit_trans(void)
{
	u2_printf("+++");//发送+++退出透传模式,一定不能将换行符"\r\n"
	delay_ms(100);
	return atk_8266_send_cmd("AT","OK",20);//退出透传判断.
}

//esp8266连接初始化,配置为sta模式,并连接wifi
u8 atk_8266_wifista_config(void)
{
//	u8 *p=malloc(100*sizeof(char));
	
	while(atk_8266_send_cmd("AT","OK",20))//1.检查wifi模块是否在线
	{
		printf("connect fail\r\n");
		printf("try to connect\r\n");
	}
	atk_8266_send_cmd("AT+CWMODE=1","OK",20);//2.配置es8266工作为sta模式
	u2_printf("AT+RST/r/n");//3.重启模块
	delay_ms(4000);//延时4s等待模块重启
//	sprintf((char*)p,"AT+CWJAP=\"%s\",\"%s\"",WIFI_NAME,WIFI_PSW);
	while(atk_8266_send_cmd("AT+CWJAP=\"HL\",\"qq123456\"","WIFI GOT IP",600))//4.连接wifi,连接时间较长,设置最大等待时间为600*10=6000ms
	{
		printf("正在连接wifi\r\n");
	}
	printf("wifi连接成功\r\n");
	u2_printf("AT+CIPMUX=0");//5.禁用多连接
//	free(p);
	return 0;
}

wifista.h

#ifndef __WIFISTA_H
#define __WIFISTA_H
#include "sys.h"
#include "usart2.h"

u8 atk_8266_wifista_config(void);//esp8266初始化为sta模式
u8 atk_8266_quit_trans(void);//退出透传
u8 atk_8266_send_cmd(u8 *cmd,u8 *ack,u16 waittime);//发送指令
	

#endif

weather.c

#include "weather.h"
#include "wifista.h"
#include "usart.h"
#include "usart2.h"
#include "delay.h"
#include "cJSON.h"

#define WEATHER_SERVERIP "www.seniverse.com" //心知天气域名
#define WEATHER_PORTNUM "80"//心知天气端口号

//通过cJSON库提取获得json文件中的温度信息
//返回值:0 提取成功
//        1 提取失败
//一定要修改startup_stm32f10x_hd.s文件中的堆栈空间(扩大为0x800即可),否则无法提取:Heap_Size       EQU     0x00000800
u8 parse_now_weather()
{
	cJSON *root;
	cJSON *results_array;
	cJSON *first_result;
	cJSON *now_object;
	cJSON *temperature_value;
	
	root = cJSON_Parse((const char*)USART2_RX_BUF);//提取最外层字典对象
	if (root == NULL) {
			printf("Error parsing JSON\n");
			return 1;
	}

	// 获取 "results" 数组
	results_array = cJSON_GetObjectItemCaseSensitive(root, "results");
	if (!cJSON_IsArray(results_array)) {
			printf("Error: 'results' is not an array\n");
			cJSON_Delete(root);
			return 1;
	}

	// 获取数组中的第一个元素
	first_result = cJSON_GetArrayItem(results_array, 0);

	// 获取 "now" 对象
	now_object = cJSON_GetObjectItemCaseSensitive(first_result, "now");

	// 获取温度值
	temperature_value = cJSON_GetObjectItemCaseSensitive(now_object, "temperature");
	if (cJSON_IsString(temperature_value)) {
			printf("Temperature: %s\n", temperature_value->valuestring);//打印温度信息
	} else {
			printf("Error: Unable to retrieve temperature\n");
	}

	// 释放 cJSON 结构
	cJSON_Delete(root);

	return 0;
}

//获取一次实时天气
u8 get_current_weather(void)
{
	u8 res=1;
	USART2_RX_STA=0;//开启串口2数据接收
	while(atk_8266_send_cmd("AT+CIPSTART=\"TCP\",\"api.seniverse.com\",80\r\n","CONNECT",200))//6.服务器连接会断开,所以最好每次查询天气都重新连接服务器
	{
		printf("服务器连接失败\r\n");
	}
	printf("服务器连接成功\r\n");
	while(atk_8266_send_cmd("AT+CIPMODE=1\r\n","OK",200));//7.传输模式为透传
	printf("透传模式开启\r\n");
	while(atk_8266_send_cmd("AT+CIPSEND\r\n",">",20));//8.开始透传
	printf("准备发送get请求\r\n");
	delay_ms(100);
	while(atk_8266_send_cmd("GET https://api.seniverse.com/v3/weather/now.json?key=Ses06roxQh0JI5CRq&location=wuxi&language=zh-Hans&unit=c","results",200))//9.GET请求获取天气数据
	{
		printf("数据获取失败\r\n");
	}
	printf("天气数据为:%s\r\n",USART2_RX_BUF);
	parse_now_weather();//使用CJSON解析数据
	atk_8266_quit_trans();//退出透传等待下一次天气查询命令
	
	return res;
}

weather.h

#ifndef __WEATHER_H
#define __WEATHER_H
#include "sys.h"

u8 get_current_weather(void);//获得当前天气

#endif

main.c

#include "stm32f10x.h"
#include "led.h"  
#include "stdarg.h"	 
#include "timer.h"
#include "delay.h"
#include "key.h"
#include "usart2.h"
#include "usart.h"
#include "wifista.h"
#include "weather.h"
#include "cJSON.h"


 int main(void)
 {	
	 int k=0;
	 usart2_init(115200);//串口2初始化,与esp8266进行数据传输
	 Led_Init();//led初始话
	 key_init();//按键初始化
	 delay_init();//延时函数初始化
	 uart_init(115200);//串口1初始化,打印配置信息
	 atk_8266_wifista_config();//esp8266初始化
	 get_current_weather();//获取一次天气信息,并提取温度值
	 
	 while(1)
	 {
		 if(USART2_RX_STA&0x8000)//通过串口1输出串口2接受到esp8266返回数据
		 {
			 USART2_RX_BUF[USART2_RX_STA&0x7fff]=0;
			 printf("res---:%s,USART2_RX_STA=%x\r\n",USART2_RX_BUF,USART2_RX_STA);
			 USART2_RX_STA=0;
		 }
		 if(USART_RX_STA&0x8000)//将串口1接收数据发送给串口2
		 {
			 USART_RX_BUF[USART_RX_STA&0x3fff]=0;
			 u2_printf("%s\r\n",USART_RX_BUF);
			 printf("usart1:%s,len=%x\r\n",USART_RX_BUF,USART_RX_STA);
			 USART_RX_STA=0;
		 }
		 k=key_scan(0);//获得按键值
		 if(k==KEY0_PRES)//按键0,获得一次天气数据
		 {
			 get_current_weather();
			 USART2_RX_BUF[USART2_RX_STA&0x7fff]=0;
			 printf("GET:%s,USART2_RX_STA=%x\r\n",USART2_RX_BUF,USART2_RX_STA);
		 }
		 else if(k==KEY1_PRES)//按键1,退出透传模式
		 {
			 atk_8266_quit_trans();
		 }
			 
	 }
 }

问题总结

1.无法通过cJSON解析数据,运行文件不报错,但是一直无法解析到温度数据。

cJSON解析数据占用较大栈空间,一定要修改startup_stm32f10x_hd.s文件中的堆栈空间(扩大为0x800即可),否则无法提取。

esp8266 设置时间_数据_10

2.发送AT指令接收不到返回。

配置接入透传模式以后没有退出,复位单片机esp8266保持上电,依旧没有退出通透模式,无法识别AT指令。

3.连接wifi时,一直无法接收到“WIFI GOT IP”指令,无法退出循环。

wifi连接时间较长,需要4-6s可以多等待一会

esp8266 设置时间_串口_11

4.代码无法退出透传模式

进入透传以后输入“+++”,模块退出透传。注意两点1.“+++”软件发送时后面不要加其他东西包括“\r\n”换行符,透过上位机串口发送时,不要勾选发送新行。2.发送"+++"以后需要延迟一段时间,测试100ms可以。

esp8266 设置时间_#include_12

esp8266 设置时间_#include_13

5.报错。

esp8266 设置时间_数据_14

在usart.c文件中添加如下代码块

esp8266 设置时间_#include_15

_ttywrch(int ch)
{
    ch = ch;
}