ESP8266 模块简易驱动程序 -- 基于 STM32F103 及原子云固件实现云功能


一、说明

本简易驱动程序是基于正点原子的ESP8266模块,主要用于实现连接原子云的功能。MCU选用的是STM32F103ZET6

注:原子云固件添加了
AT+ATKCLDSTAAT+ATKCLDCLS 两条指令
用于连接正点原子自家的原子云平台,原厂的AT指令不受任何影响

本程序主要实现了如下功能:

  1. 设定工作模式
  2. 连接路由器
  3. 连接TCP服务器
  4. 连接原子云(需刷写了原子云固件的ESP8266模块)
  5. 发送数据(包括正常发送、透传发送、云发送)

二、文件依赖

2.1 esp8266.c 的头文件依赖如下(自行修改相关路径)

#include "../../Basic/sys/sys.h"
#include "../../Basic/usart/usart.h"
#include "../../Basic/delay/delay.h"
#include <string.h>

2.2 usart.c 的头文件依赖如下(自行修改相关路径)

#include "stdio.h"
#include "stdarg.h"
#include <string.h>
#include "../../Basic/sys/sys.h"
#include "../../Hardware/ESP8266/esp8266_timer.h"

2.3 esp8266_timer.c 的头文件依赖如下(自行修改相关路径)

#include "../../Basic/sys/sys.h"
#include "../../Basic/usart/usart.h"

三、USART相关文件

3.1 usart.h文件

#ifndef __USART_H
#define __USART_H
#include "stdio.h"
#include "stdarg.h"
#include <string.h>
#include "../../Basic/sys/sys.h"
#include "../../Hardware/ESP8266/esp8266_timer.h"

#define printfByUSARTx  USART1 //定义支持printf函数的串口

#define USART1_REC_LEN	200  	//定义最大接收字节数 200
#define	USART3_REC_LEN	600		//定义最大接收字节数 600
#define USART3_SEN_LEN	600		//定义最大发送字节数 600

#define EN_USART1	1		//1使能,0禁止  串口1接收
#define EN_USART2	0
#define EN_USART3	1

#if EN_USART1
extern u8  USART1_RX_BUF[USART1_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern u16 USART1_RX_STA;         		 //接收状态标记	
#endif // EN_USART1

#if EN_USART2
extern u8  USART2_RX_BUF[USART2_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern u16 USART2_RX_STA;         		 //接收状态标记	
#endif // EN_USART2

#if EN_USART3
extern u8  USART3_RX_BUF[USART3_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern u8  USART3_TX_BUF[USART3_SEN_LEN]; //发送缓冲
extern u16 USART3_RX_STA;         		 //接收状态标记	
#endif // EN_USART3

#if EN_USART1
void USART1_Init(u32 baudRate);
#endif // EN_USART1

#if EN_USART2
void USART2_Init(u32 baudRate);
#endif // EN_USART2

#if EN_USART3
/**
 * @brief	串口3初始化
*/
void USART3_Init(u32 baudRate);

/**
 * @brief	串口3 printf
*/
void USART3_printf(char* fmt, ...);

/**
 * @brief	USART3_RX_BUF串口3数据缓冲清空
*/
void USART3_RX_BUF_CLEAR(void);
#endif // EN_USART3

#endif


3.2 usart.c文件

#include "usart.h"	  
// 	 
//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "includes.h"					//ucos 使用	  
#endif

//
//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE
{
    
    
	int handle;
};

FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
void _sys_exit(int x)
{
    
    
	x = x;
}

//重定义fputc函数 
int fputc(int ch, FILE* f)
{
    
    
	while ((printfByUSARTx->SR & 0X40) == 0);//循环发送,直到发送完毕   
	printfByUSARTx->DR = (u8)ch;
	return ch;
}
#endif 

/*使用microLib的方法*/
 /* 
int fputc(int ch, FILE *f)
{
	USART_SendData(USART1, (uint8_t) ch);

	while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}	
   
    return ch;
}
int GetKey (void)  { 

    while (!(USART1->SR & USART_FLAG_RXNE));

    return ((int)(USART1->DR & 0x1FF));
}
*/

#if EN_USART1   //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误   	
u8 USART1_RX_BUF[USART1_REC_LEN];     //接收缓冲,最大USART1_REC_LEN个字节.
//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
u16 USART1_RX_STA = 0;       //接收状态标记	  

/**
 * @brief 串口1初始化
*/
void USART1_Init(u32 baudRate) {
    
    
	//GPIO端口设置
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟

	USART_DeInit(USART1); //复位USART1

	//USART1_TX   GPIOA.9
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9

	//USART1_RX	  GPIOA.10初始化
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

	//Usart1 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器

	//USART 初始化设置
	USART_InitStructure.USART_BaudRate = baudRate;//串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式

	USART_Init(USART1, &USART_InitStructure); //初始化串口1
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
	USART_Cmd(USART1, ENABLE);                    //使能串口1 
}

/**
 * @brief	串口1中断服务程序
*/
void USART1_IRQHandler(void) {
    
    
	u8 Res;

#if SYSTEM_SUPPORT_OS 		//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
	OSIntEnter();
#endif

	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) {
    
     //接收中断(接收到的数据必须是0x0d 0x0a结尾)
		Res = USART_ReceiveData(USART1);	//读取接收到的数据
		//注意在主函数处理完串口数据后,要将USART1_RX_STA清0
		if ((USART1_RX_STA & 0x8000) == 0) {
    
    //最高位为0,还未接受完成
			if (USART1_RX_STA & 0x4000) {
    
    //接收到了0x0d
				if (Res != 0x0a) USART1_RX_STA = 0;//接收错误,重新开始
				else USART1_RX_STA |= 0x8000;	//如果最后一位是0a,则标记为接受完成
			}
			else {
    
     //还没收到0d
				if (Res == 0x0d) USART1_RX_STA |= 0x4000;
				else {
    
    
					USART1_RX_BUF[USART1_RX_STA & 0X3FFF] = Res;
					USART1_RX_STA++;
					if (USART1_RX_STA > (USART1_REC_LEN - 1)) USART1_RX_STA = 0;//接收数据错误,重新开始接收	  
				}
			}
		}
	}

#if SYSTEM_SUPPORT_OS 	//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
	OSIntExit();
#endif
}
#endif	

#if EN_USART3
u8  USART3_RX_BUF[USART3_REC_LEN];  //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
u8  USART3_TX_BUF[USART3_SEN_LEN];  //发送缓冲
u16 USART3_RX_STA = 0;				//接收状态标记	

/**
 * @brief	串口3初始化
*/
void USART3_Init(u32 baudRate) {
    
    
	//GPIO端口设置
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);	//使能USART3,GPIOB时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

	USART_DeInit(USART3); //复位USART3

	//USART3_TX   GPIOB.10
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PB10
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	//USART3_RX	  GPIOB.11
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;//PB11
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	//Usart3 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	

	//USART 初始化设置
	USART_InitStructure.USART_BaudRate = baudRate;//串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式

	USART_Init(USART3, &USART_InitStructure); //初始化串口
	USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启串口接受中断
	USART_Cmd(USART3, ENABLE);                    //使能串口

	TIM7_Int_Init(1000 - 1, 7200 - 1);	//TIM7初始化,每10ms触发中断
	TIM_Cmd(TIM7, DISABLE);	//关闭定时器7
}

/**
 * @brief	串口3中断服务程序
*/
void USART3_IRQHandler(void) {
    
    
	u8 res;
	if (USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) {
    
     //串口3产生中断
		res = USART_ReceiveData(USART3); //暂存数据
		if ((USART3_RX_STA & 0x8000) == 0) {
    
     //最高位接收完成标志还为0时
			if (USART3_RX_STA < USART3_REC_LEN) {
    
     //如果USART3接收缓存仍有余量
				TIM_SetCounter(TIM7, 0); //清空TIM7计数值
				if (USART3_RX_STA == 0)	 TIM_Cmd(TIM7, ENABLE);	//使能TIM7
				USART3_RX_BUF[USART3_RX_STA++] = res; //存储数据
			}
			else {
    
     //否则当达到最大接受缓冲时强制结束存储
				USART3_RX_STA |= 0x8000; //强制标记接收完成,最高位赋值为1
			}
		}
	}
}

/**
 * @brief	串口3 printf
*/
void USART3_printf(char* fmt, ...) {
    
    
	u16 i, j;
	va_list ap;
	va_start(ap, fmt);
	vsprintf((char*)USART3_TX_BUF, fmt, ap);
	va_end(ap);
	i = strlen((const char*)USART3_TX_BUF);//此次发送数据的长度
	for (j = 0; j < i; j++) {
    
     //循环发送
		while (USART_GetFlagStatus(USART3, USART_FLAG_TC) != SET);  //等待上次传输完成 
		USART_SendData(USART3, (uint8_t)USART3_TX_BUF[j]); 	 //发送数据到串口3 
	}
}

/**
 * @brief	USART3_RX_BUF串口3数据缓冲清空
*/
void USART3_RX_BUF_CLEAR(void) {
    
    
	memset(USART3_RX_BUF, 0, USART3_REC_LEN);
	USART3_RX_STA = 0;
}
#endif


四、ESP8266相关文件

  1. esp8266_timer.c (timer文件使用了TIM7,用于usart3传输期间定时)
  2. esp8266_timer.h
  3. esp8266.c
  4. esp8266.h

4.1 esp8266_timer相关文件

4.1.1 esp8266_timer.h文件

#ifndef __ESP8266_TIMER_H
#define __ESP8266_TIMER_H
#include "../../Basic/sys/sys.h"
#include "../../Basic/usart/usart.h"

/**
 * @brief	定时器7初始化
 * @param	arr,计数器值
 * @param	psc,预分频值
 */
void TIM7_Int_Init(u16 arr, u16 psc);

#endif // !__ESP8266_TIMER_H


4.1.2 esp8266_timer.c文件

#include "esp8266_timer.h"

extern u16 USART3_RX_STA;

/**
 * @brief	定时器7中断服务程序
 */
void TIM7_IRQHandler(void) {
    
    
	if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET) {
    
     //TIM7触发中断
		USART3_RX_STA |= 0x8000;	//超时,强制标记串口3数据接收完成
		TIM_ClearITPendingBit(TIM7, TIM_IT_Update);  //清除TIM7更新中断标志    
		TIM_Cmd(TIM7, DISABLE);  //关闭TIM7
	}
}

/**
 * @brief	定时器7初始化
 * @param	arr,计数器值
 * @param	psc,预分频值
 */
void TIM7_Int_Init(u16 arr, u16 psc) {
    
    
	NVIC_InitTypeDef NVIC_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);//TIM7时钟使能    

	//定时器TIM7初始化
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	
	TIM_TimeBaseStructure.TIM_Prescaler = psc; //设置用来作为TIMx时钟频率除数的预分频值
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位

	TIM_ITConfig(TIM7, TIM_IT_Update, ENABLE); //使能指定的TIM7中断,允许更新中断

	TIM_Cmd(TIM7, ENABLE);//开启定时器7

	NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级0
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;		//子优先级2
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
}


4.2 esp8266相关文件

注:本人在sys.h中定义了operateSta枚举类型,用于标记操作是否成功。可自行添加在esp8266.h文件中

typedef enum {
    
    
	FAIL = 0,
	SUCCEED
} operateSta; //操作状态

4.2.1 esp8266.h文件

#ifndef __ESP8266_H
#define __ESP8266_H
// <头文件> //
#include "../../Basic/sys/sys.h"
#include "../../Basic/usart/usart.h"
#include "../../Basic/delay/delay.h"
#include <string.h>
// <头文件> //

#define	FIRMWARE_YUANZI_SUPPORT	1	//是否为刷了原子云固件的ESP8266

/
//							-- 枚举类型定义 --				   		   //
/

//ESP8266 工作模式
typedef enum {
    
    
	STA_MODE = 1, /* STA模式 */
	AP_MODE,	  /* AP模式 */
	AP_STA_MODE	  /* AP+STA模式 */
}ESP8266_WORK_MODE;

//ESP8266 加密方式
typedef enum {
    
    
	OPEN = 0,
	WPA_PSK = 2,
	WPA2_PSK,
	WPA_WPA2_PSK
}ESP8266_ENCRYPTION_TYPE;

//AP模式下最大可连接客户端数量
typedef enum {
    
    
	MAX_CLIENT_NUMBER_1 = 1, /* 设置最大可连接的客户端数量为1 */
	MAX_CLIENT_NUMBER_2,	 /* 设置最大可连接的客户端数量为2 */
	MAX_CLIENT_NUMBER_3,	 /* 设置最大可连接的客户端数量为3 */
	MAX_CLIENT_NUMBER_4		 /* 设置最大可连接的客户端数量为4 */
}MAX_CLIENT_NUMBER;

/
//							-- 相关结构体定义 --				   		   //
/

//STA相关参数
typedef struct {
    
    
	const u8* STA_SSID;		//要连接的 WIFI SSID
	const u8* STA_PWD;		//要连接的 WIFI 密码
	u8 WIFI_CONNECTED;		//WIFI连接标志位
}STA_Parameter_Type;
extern STA_Parameter_Type STA_Parameters;

//AP相关参数
typedef struct {
    
    
	const u8* AP_SSID;		//软路由 AP SSID
	const u8* AP_PWD; 		//软路由 AP 密码(至少8位)
	u8 AP_CHANNEL;			//通道号
	ESP8266_ENCRYPTION_TYPE AP_ENCRYPTION; //加密方式
	MAX_CLIENT_NUMBER MAX_CLIENT; //最大客户端数量
	u8 BROADCAST;			//是否广播AP的SSID
}AP_Parameter_Type;
extern AP_Parameter_Type AP_Parameters;

//需连接的TCP服务器相关参数
typedef struct {
    
    
	const u8* serverIP;		//TCP服务器IP
	const u8* serverPort;	//TCP服务器连接端口
	u8 TCP_SERVER_CONNECTED;//TCP服务器连接标志位
	u8 TRANSPARENT_MODE;	//透传模式标志位
}TCP_Server_Parameter_Type;
extern TCP_Server_Parameter_Type TCP_Server_Parameters;

#if FIRMWARE_YUANZI_SUPPORT	//如果为刷了原子云固件的ESP8266
//原子云设备ID及密码
typedef struct {
    
    
	const u8* cloudID;		//原子云设备ID
	const u8* cloadPwd;		//原子云设备密码
	u8 CLOUD_CONNECTED;		//原子云连接标志位
}Cloud_Parameter_Type;
extern Cloud_Parameter_Type Cloud_Parameters;
#endif // FIRMWARE_YUANZI_SUPPORT


//							-- 硬件相关定义 --				   		  //
//				USART3_TX引脚为PB10,USART3_RX引脚为PB11				  //


#define ESP8266_RST_PIN			GPIO_Pin_14				//ESP8266模块RST引脚
#define ESP8266_RST_PORT		GPIOE					//ESP8266模块RST端口
#define ESP8266_RST_OUT			PEout(14)				//ESP8266模块RST输出
#define	ESP8266_RST_PORT_CLOCK	RCC_APB2Periph_GPIOE	//ESP8266模块RST引脚端口时钟


//							-- 相关操作函数 --				   		  //


/**
 * @brief	检查收到的应答
 * @param	res	预期应答字符串
 * @retval	res首次出现的位置指针,如果为0则说明响应无该字符串
*/
u8* ESP8266_Check_Response(u8* res);

/**
 * @brief	发送命令给ESP8266
 * @param	cmd	命令
 * @param	ack 期待的应答结果,如果为空则不等待应答
 * @param	waitTime	等待时间(单位ms)
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_SendCmd(u8* cmd, u8* ack, u16 waitTime);

/**
 * @brief	ESP8266发送数据内容。
 *			如果连接了原子云,消息收发会被云占用,无法和TCP服务器收发
 * @param	data	数据
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_SendData(u8* data);

/**
 * @brief	ESP8266进入透传模式
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Enter_TransparentTransmit(void);

/**
 * @brief	ESP8266通过透传发送数据内容。
 *			如果连接了原子云,消息收发会被云占用,无法和TCP服务器收发
 * @note	进入透传模式后,直接发送数据即可,无需先发送AT+CIPSEND=%d说明要发送的长度
 * @param	data	数据
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_TransparentSendData(u8* data);

/**
 * @brief	ESP8266退出透传模式
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Exit_TransparentTransmit(void);

#if FIRMWARE_YUANZI_SUPPORT	//如果为刷了原子云固件的ESP8266
/**
 * @brief	ESP8266连接原子云
 * @note	只有刷了原子云固件的才可以使用
 * @param	parameters, 原子云相关参数结构体指针
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Connect_YuanZiCloud(Cloud_Parameter_Type* parameters);

/**
 * @brief	ESP8266发送数据内容给原子云。
 *			如果连接了原子云,消息收发会被云占用,无法和TCP服务器收发
 * @note	只有刷了原子云固件的才可以使用
 * @param	data	数据
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta  ESP8266_CloudSendData(u8* data);

/**
 * @brief	ESP8266断开与原子云的连接
 * @note	只有刷了原子云固件的才可以使用
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Disconnect_YuanZiCloud(void);
#endif // FIRMWARE_YUANZI_SUPPORT

/**
 * @brief	AT指令测试
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_ATCmd_Test(void);

/**
 * @brief	查询AT固件版本信息
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Check_FirmWare_Info(void);

/**
 * @brief	设置ESP8266工作模式
 * @param	mode 工作模式.
			@arg	取值为: STA_MODE, AP_MODE, AP_STA_MODE
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_WorkMode_Conf(ESP8266_WORK_MODE mode);

/**
 * @brief	设置ESP8266软路由相关参数
 * @param	parameters, AP模式相关参数结构体指针
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_SoftAP_Conf(AP_Parameter_Type* parameters);

/**
 * @brief	ESP8266设置AP参数后,查询与ESP8266建立连接的客户端信息
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Check_Connected_ClientInfo(void);

/**
 * @brief	ESP8266连接WIFI
 * @param	parameters, STA模式下的WIFI相关参数结构体指针
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Connect_WIFI(STA_Parameter_Type* parameters);

/**
 * @brief	ESP8266连接TCP服务器
 * @param	parameters, TCP服务器相关参数结构体指针
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Connect_TCP_Server(TCP_Server_Parameter_Type* parameters);

/**
 * @brief	断开与TCP服务器的连接
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Disconnect_TCP_Server(void);

/**
 * @brief	获取ip地址
 * @param	ipbuf	ip地址字符串缓冲区
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_GetIP(u8* ipbuf);


//							-- 初始化相关函数 --				   		  //


/**
 * @brief	ESP8266恢复出厂设置
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Restore_Default(void);

/**
 * @brief	ESP8266软件复位
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_SW_Reset(void);

/**
 * @brief	ESP8266初始化
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Init(void);

#endif // !__ESP8266_H


4.2.2 esp8266.c文件

#include "esp8266.h"

// <全局变量声明> //
STA_Parameter_Type STA_Parameters;				//STA模式参数
AP_Parameter_Type AP_Parameters;				//AP模式参数
TCP_Server_Parameter_Type TCP_Server_Parameters;//TCP Server参数
#if FIRMWARE_YUANZI_SUPPORT	//如果为刷了原子云固件的ESP8266
Cloud_Parameter_Type Cloud_Parameters;			//原子云设备参数
#endif // FIRMWARE_YUANZI_SUPPORT
// <全局变量声明> //

/
//						-- 结构体参数初始化 --				   		   //
/

//STA参数初始化
void STA_Info_Init(STA_Parameter_Type* parameters) {
    
    
	parameters->STA_SSID = "***"; //自行修改WIFI名称
	parameters->STA_PWD = "********"; //自行修改WIFI密码
	parameters->WIFI_CONNECTED = 0;
}

//AP参数初始化
void AP_Info_Init(AP_Parameter_Type* parameters) {
    
    
	parameters->AP_SSID = "ESP_8266"; //自行修改软路由名称
	parameters->AP_PWD = "123456789"; //自行修改软路由密码
	parameters->AP_CHANNEL = 5;
	parameters->AP_ENCRYPTION = WPA_WPA2_PSK;
	parameters->MAX_CLIENT = MAX_CLIENT_NUMBER_1;
	parameters->BROADCAST = 0;
}

//TCP_Server参数初始化
void TCP_Server_Info_Init(TCP_Server_Parameter_Type* parameters) {
    
    
	parameters->serverIP = "192.168.0.10"; //自行修改TCP服务器ip
	parameters->serverPort = "9090";	   //自行修改TCP服务器端口
	parameters->TCP_SERVER_CONNECTED = 0;
	parameters->TRANSPARENT_MODE = 0;
}

#if FIRMWARE_YUANZI_SUPPORT	//如果为刷了原子云固件的ESP8266
//Cloud参数初始化
void Cloud_Info_Init(Cloud_Parameter_Type* parameters) {
    
    
	parameters->cloudID = "60002472859501******"; //自行修改原子云设备编号
	parameters->cloadPwd = "12345678";			  //自行修改原子云设备密码
	parameters->CLOUD_CONNECTED = 0;
}
#endif // FIRMWARE_YUANZI_SUPPORT


/
//							-- 相关操作函数 --				   		   //
/

/**
 * @brief	检查收到的应答
 * @param	res	预期应答字符串
 * @retval	res首次出现的位置指针,如果为0则说明响应无该字符串
*/
u8* ESP8266_Check_Response(u8* res) {
    
    
	char* index = 0;
	//接收到数据
	if (USART3_RX_STA & 0x8000) {
    
    
		USART3_RX_BUF[USART3_RX_STA & 0x3FFF] = '\0'; //添加结束符
		index = strstr((const char*)USART3_RX_BUF, (const char*)res);
	}
	return (u8*)index;
}

/**
 * @brief	发送命令给ESP8266
 * @param	cmd	命令
 * @param	ack 期待的应答结果,如果为空则不等待应答
 * @param	waitTime	等待时间(单位ms)
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_SendCmd(u8* cmd, u8* ack, u16 waitTime) {
    
    
	USART3_RX_STA = 0;			  //初始化串口3接收标记
	USART3_printf("%s\r\n", cmd); //发送命令
	if (ack && waitTime) {
    
    
		while (waitTime--) {
    
    
			if (USART3_RX_STA & 0x8000) {
    
    
				if (ESP8266_Check_Response(ack) != 0) return SUCCEED;
				USART3_RX_STA = 0;
			}
			delay_ms(1);
		}
	}
	if (ack == NULL) return SUCCEED;
	return FAIL;
}

/**
 * @brief	ESP8266发送数据内容。
 *			如果连接了原子云,消息收发会被云占用,无法和TCP服务器收发
 * @param	data	数据
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_SendData(u8* data) {
    
    
	u8 state;
	u8 cmd[12];
	u16 len = strlen((const char*)data) + 2; //要发送的数据长度+2个结束字符0d0a
	//如果TCP服务器未连接成功则直接返回FAIL
	if (!(&TCP_Server_Parameters)->TCP_SERVER_CONNECTED) return FAIL;
#if FIRMWARE_YUANZI_SUPPORT	//如果为刷了原子云固件的ESP8266
	if ((&Cloud_Parameters)->CLOUD_CONNECTED) return FAIL;
#endif // FIRMWARE_YUANZI_SUPPORT
	sprintf((char*)cmd, "AT+CIPSEND=%d", len);
	state = ESP8266_SendCmd(cmd, ">", 500);
	if (state == FAIL)	return FAIL;
	state = ESP8266_SendCmd(data, "SEND OK", 1000);
	if (state == SUCCEED)	return SUCCEED;
	return FAIL;
}

/**
 * @brief	ESP8266进入透传模式
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Enter_TransparentTransmit(void) {
    
    
	u8 state;
	state = ESP8266_SendCmd("AT+CIPMODE=1", "OK", 500);
	if (state == FAIL) return FAIL;
	state = ESP8266_SendCmd("AT+CIPSEND", ">", 500);
	if (state == SUCCEED) return SUCCEED;
	return FAIL;
}

/**
 * @brief	ESP8266通过透传发送数据内容。
 *			如果连接了原子云,消息收发会被云占用,无法和TCP服务器收发
 * @note	进入透传模式后,直接发送数据即可,无需先发送AT+CIPSEND=%d说明要发送的长度
 * @param	data	数据
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_TransparentSendData(u8* data) {
    
    
	u8 state;
	//如果TCP服务器未连接成功则直接返回FAIL
	if (!(&TCP_Server_Parameters)->TCP_SERVER_CONNECTED) return FAIL;
#if FIRMWARE_YUANZI_SUPPORT	//如果为刷了原子云固件的ESP8266
	if ((&Cloud_Parameters)->CLOUD_CONNECTED) return FAIL;
#endif // FIRMWARE_YUANZI_SUPPORT
	if (!(&TCP_Server_Parameters)->TRANSPARENT_MODE) {
    
    
		state = ESP8266_Enter_TransparentTransmit();
		if (state != SUCCEED) return FAIL;
		(&TCP_Server_Parameters)->TRANSPARENT_MODE = 1;
	}
	state = ESP8266_SendCmd(data, NULL, 500);
	if (state == SUCCEED) return SUCCEED;
	return FAIL;
}

/**
 * @brief	ESP8266退出透传模式
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Exit_TransparentTransmit(void) {
    
    
	u8 state;
	if (!(&TCP_Server_Parameters)->TRANSPARENT_MODE) return SUCCEED;
	USART3_printf("+++");
	delay_ms(1500);
	state = ESP8266_SendCmd("AT+CIPMODE=0", "OK", 500);
	if (state == SUCCEED) {
    
    
		(&TCP_Server_Parameters)->TRANSPARENT_MODE = 0;
		return SUCCEED;
	}
	return FAIL;
}

#if FIRMWARE_YUANZI_SUPPORT	//如果为刷了原子云固件的ESP8266
/**
 * @brief	ESP8266连接原子云
 * @note	只有刷了原子云固件的才可以使用
 * @param	parameters, 原子云相关参数结构体指针
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Connect_YuanZiCloud(Cloud_Parameter_Type* parameters) {
    
    
	u8 state;
	u8 cmd[50];

	sprintf((char*)cmd, "AT+ATKCLDSTA=\"%s\",\"%s\"",
		parameters->cloudID,
		parameters->cloadPwd);
	state = ESP8266_SendCmd(cmd, "CLOUD CONNECTED", 10000);
	if (state == SUCCEED) {
    
    
		parameters->CLOUD_CONNECTED = 1;
		return SUCCEED;
	}
	return FAIL;
}

/**
 * @brief	ESP8266发送数据内容给原子云。
 *			如果连接了原子云,消息收发会被云占用,无法和TCP服务器收发
 * @note	只有刷了原子云固件的才可以使用
 * @param	data	数据
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta  ESP8266_CloudSendData(u8* data) {
    
    
	u8 state;

	if (!(&Cloud_Parameters)->CLOUD_CONNECTED) return FAIL;
	state = ESP8266_SendCmd(data, NULL, 500);
	if (state == SUCCEED) return SUCCEED;
	return FAIL;
}

/**
 * @brief	ESP8266断开与原子云的连接
 * @note	只有刷了原子云固件的才可以使用
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Disconnect_YuanZiCloud(void) {
    
    
	u8 state;
	state = ESP8266_SendCmd("AT+ATKCLDCLS", "CLOUD DISCONNECT", 500);
	if (state == SUCCEED) {
    
    
		(&Cloud_Parameters)->CLOUD_CONNECTED = 0;
		return SUCCEED;
	}
	return FAIL;
}
#endif // FIRMWARE_YUANZI_SUPPORT

/**
 * @brief	AT指令测试
 * @note	单独发送"AT"给ESP8266,会返回"OK"
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_ATCmd_Test(void) {
    
    
	u8 i;
	u8 state;
	for (i = 0; i < 10; i++) {
    
    
		state = ESP8266_SendCmd("AT", "OK", 500);
		if (state == SUCCEED) return SUCCEED;
	}
	return FAIL;
}

/**
 * @brief	查询AT固件版本信息
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Check_FirmWare_Info(void) {
    
    
	u8 state;
	state = ESP8266_SendCmd("AT+GMR", "OK", 500);
	if (state == SUCCEED) return SUCCEED;
	return FAIL;
}

/**
 * @brief	设置ESP8266工作模式
 * @param	mode 工作模式
			@arg	STA_MODE, AP_MODE, AP_STA_MODE
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_WorkMode_Conf(ESP8266_WORK_MODE mode) {
    
    
	u8 state;
	switch (mode) {
    
    
		case STA_MODE: {
    
    
			state = ESP8266_SendCmd("AT+CWMODE_DEF=1", "OK", 500); break;
		}
		case AP_MODE: {
    
    
			state = ESP8266_SendCmd("AT+CWMODE_DEF=2", "OK", 500); break;
		}
		case AP_STA_MODE: {
    
    
			state = ESP8266_SendCmd("AT+CWMODE_DEF=3", "OK", 500); break;
		}
		default: return FAIL;
	}
	if (state == SUCCEED) return SUCCEED;
	return FAIL;
}

/**
 * @brief	设置ESP8266软路由相关参数
 * @param	parameters, AP模式相关参数结构体指针
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_SoftAP_Conf(AP_Parameter_Type* parameters) {
    
    
	u8 state;
	u8 cmd[50];

	sprintf((char*)cmd, "AT+CWSAP_DEF=\"%s\",\"%s\",%d,%d,%d,%d", 
		parameters->AP_SSID, 
		parameters->AP_PWD,
		parameters->AP_CHANNEL,
		parameters->AP_ENCRYPTION,
		parameters->MAX_CLIENT,
		parameters->BROADCAST);
	state = ESP8266_SendCmd(cmd, "OK", 5000);
	if (state == SUCCEED) return SUCCEED;
	return FAIL;
}

/**
 * @brief	ESP8266设置AP参数后,查询与ESP8266建立连接的客户端信息
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Check_Connected_ClientInfo(void) {
    
    
	u8 state;
	state = ESP8266_SendCmd("AT+CWLIF", "192.", 5000);
	if (state == SUCCEED) return SUCCEED;
	return FAIL;
}

/**
 * @brief	ESP8266连接WIFI
 * @param	parameters, STA模式下的WIFI相关参数结构体指针
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Connect_WIFI(STA_Parameter_Type* parameters) {
    
    
	u8 state;
	u8 cmd[50];

	sprintf((char*)cmd, "AT+CWJAP_DEF=\"%s\",\"%s\"", 
		parameters->STA_SSID, 
		parameters->STA_PWD);
	state = ESP8266_SendCmd(cmd, "WIFI GOT IP", 10000);
	if (state == SUCCEED) {
    
    
		parameters->WIFI_CONNECTED = 1;
		return SUCCEED;
	}
	return FAIL;
}

/**
 * @brief	ESP8266连接TCP服务器
 * @param	parameters, TCP服务器相关参数结构体指针
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Connect_TCP_Server(TCP_Server_Parameter_Type* parameters) {
    
    
	u8 state;
	u8 cmd[50];

	state = ESP8266_SendCmd("AT+CIPMUX=0", "OK", 500);
	if (state != SUCCEED) return FAIL;
	sprintf((char*)cmd, "AT+CIPSTART=\"TCP\",\"%s\",%s", 
		parameters->serverIP,
		parameters->serverPort);
	state = ESP8266_SendCmd(cmd, "CONNECT", 5000);
	if (state == SUCCEED) {
    
    
		parameters->TCP_SERVER_CONNECTED = 1;
		return SUCCEED;
	}
	return FAIL;
}

/**
 * @brief	断开与TCP服务器的连接
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Disconnect_TCP_Server(void) {
    
    
	u8 state;
	state = ESP8266_SendCmd("AT+CIPCLOSE", "CLOSED", 500);
	if (state == SUCCEED) {
    
    
		(&TCP_Server_Parameters)->TCP_SERVER_CONNECTED = 0;
		return SUCCEED;
	}
	return FAIL;
}

/**
 * @brief	获取ip地址
 * @param	ipbuf	ip地址字符串缓冲区
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_GetIP(u8* ipbuf) {
    
    
	u8* startIndex;
	u8* endIndex;
	u8 state;

	state = ESP8266_SendCmd("AT+CIFSR", "OK", 500);
	//如果获取IP地址失败
	if (state != SUCCEED) return FAIL;
	//如果获取IP成功
	startIndex = ESP8266_Check_Response("\""); //找到开始的("符号)指针位置
	endIndex = (u8*)strstr((const char*)(startIndex + 1), "\""); //找到结束的("符号)指针位置
	*endIndex = '\0'; //把最后一个"变为结束符
	sprintf((char*)ipbuf, "%s", startIndex + 1); //把""内的ip地址字符串赋给ipbuf
	return SUCCEED;
}


//							-- 初始化相关函数 --				   		  //


/**
 * @brief	ESP8266硬件初始化
*/
void ESP8266_HW_Init(void) {
    
    
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(ESP8266_RST_PORT_CLOCK, ENABLE);

	GPIO_InitStructure.GPIO_Pin = ESP8266_RST_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(ESP8266_RST_PORT, &GPIO_InitStructure);
}

/**
 * @brief	ESP8266硬件复位
*/
void ESP8266_HW_Reset(void) {
    
    
	ESP8266_RST_OUT = 0;
	delay_ms(100);
	ESP8266_RST_OUT = 1;
	delay_ms(500);
}

/**
 * @brief	ESP8266软件复位
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_SW_Reset(void) {
    
    
	u8 state;
	state = ESP8266_SendCmd("AT+RST", "OK", 500);
	if (state == SUCCEED) return SUCCEED;
	return FAIL;
}

/**
 * @brief	ESP8266恢复出厂设置
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Restore_Default(void) {
    
    
	u8 state;
	state = ESP8266_SendCmd("AT+RESTORE", "ready", 3000);
	if (state == SUCCEED) return SUCCEED;
	return FAIL;
}

/**
 * @brief	ESP8266初始化
 * @retval	成功SUCCEED,失败FAIL
*/
operateSta ESP8266_Init(void) {
    
    
	u8 state;

	USART3_Init(115200); //初始化串口3
	ESP8266_HW_Init();   //硬件初始化
	ESP8266_HW_Reset();  //硬件复位
	STA_Info_Init(&STA_Parameters);	//初始化STA模式相关参数
	AP_Info_Init(&AP_Parameters);	//初始化AP模式相关参数
	TCP_Server_Info_Init(&TCP_Server_Parameters); //初始化TCP Server相关参数
#if FIRMWARE_YUANZI_SUPPORT	//如果为刷了原子云固件的ESP8266
	Cloud_Info_Init(&Cloud_Parameters);	//初始化云平台相关参数
#endif // FIRMWARE_YUANZI_SUPPORT
	state = ESP8266_ATCmd_Test();	//测试AT指令
	if (state == SUCCEED) return SUCCEED;
	return FAIL;
}


五、使用例(通过串口调试助手观察结果)

5.1 main.c 文件

// <头文件> //
#include "../CMSIS/stm32f10x.h"
#include "../Basic/sys/sys.h"
#include "../Basic/delay/delay.h"
#include "../Basic/usart/usart.h"
#include "../Hardware/ESP8266/esp8266.h"
// <头文件> //

// <变量> //
// <变量> //

int main(void) {
    
    
	u8 t = 0;
	u8 state;
	u8 time = 5;
	
	delay_init();
	delay_ms(200);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	USART1_Init(115200);
	//ESP8266初始化检查
	while (ESP8266_Init() != SUCCEED) {
    
    
		printf("ESP8266 Error\r\n");
		delay_ms(100);
	}
	printf("ESP8266 Initialization Succeed\r\n");
	//检查固件信息
	state = ESP8266_Check_FirmWare_Info();
	if (state != SUCCEED) printf("Firware Info Get Failed\r\n");
	else if (USART3_RX_STA & 0x8000){
    
    
		USART3_RX_BUF[USART3_RX_STA & 0x3fff] = '\0';
		printf("Firmware: %s\r\n", USART3_RX_BUF);
		USART3_RX_BUF_CLEAR();
	}
	//配置工作模式
	state = ESP8266_WorkMode_Conf(AP_STA_MODE);
	if (state != SUCCEED)	printf("WorkMode Setting Error\r\n");
	else  printf("AP STA Mode\r\n");
	//配置软路由
	state = ESP8266_SoftAP_Conf(&AP_Parameters);
	if (state != SUCCEED)  printf("SoftAP Configuration Fail\r\n");
	else  printf("Soft AP Established\r\n");
	//连接WIFI路由器
	state = ESP8266_Connect_WIFI(&STA_Parameters);
	while (time--) {
    
    
		if (time == 4) printf("Waiting for connecting with router...\r\n");
		delay_ms(1000);	//等待WIFI重连成功
	}
	if (state != SUCCEED) printf("Router Connecting Error\r\n");
	else printf("Router Connected\r\n");
	//连接TCP服务器
	state = ESP8266_Connect_TCP_Server(&TCP_Server_Parameters);
	if (state != SUCCEED)  printf("TCP Server Connecting Error\r\n");
	else  printf("TCP Server Connected\r\n");
	//连接原子云
	state = ESP8266_Connect_YuanZiCloud(&Cloud_Parameters);
	if (state != SUCCEED) printf("Cloud Connecting Error\r\n");
	else printf("Cloud Connected\r\n");
	//清空USART3数据
	USART3_RX_BUF_CLEAR();

	while (1) {
    
    
		//如果已连接TCP服务器或云平台,且串口3收到数据,就通过串口发送到电脑
		if (((&TCP_Server_Parameters)->TCP_SERVER_CONNECTED || 
			(&Cloud_Parameters)->CLOUD_CONNECTED) && 
			(USART3_RX_STA & 0x8000)) {
    
    
			
			USART3_RX_BUF[USART3_RX_STA & 0x3FFF] = '\0'; //添加结束符
			printf("Rec: %s\r\n", USART3_RX_BUF);
			USART3_RX_BUF_CLEAR();
		}
		t++;
		delay_ms(10);
		if (t == 200) {
    
    
			ESP8266_TransparentSendData("STM32F103ZE");
			ESP8266_CloudSendData("原子云");
			t = 0;
		}
	}
}


5.2 非原子云固件的ESP8266模块运行结果

在这里插入图片描述


5.3 正点原子ESP8266模块运行结果

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/ArthurCai/article/details/128890203