本简易驱动程序是基于正点原子的ESP8266模块,主要用于实现连接原子云的功能。MCU选用的是STM32F103ZET6
注:原子云固件添加了
AT+ATKCLDSTA 和 AT+ATKCLDCLS 两条指令
用于连接正点原子自家的原子云平台,原厂的AT指令不受任何影响
本程序主要实现了如下功能:
2.1 esp8266.c 的头文件依赖如下(自行修改相关路径)
#include "../../Basic/sys/sys.h"
#include "../../Basic/usart/usart.h"
#include "../../Basic/delay/delay.h"
#include
2.2 usart.c 的头文件依赖如下(自行修改相关路径)
#include "stdio.h"
#include "stdarg.h"
#include
#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"
#ifndef __USART_H
#define __USART_H
#include "stdio.h"
#include "stdarg.h"
#include
#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
#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
#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
#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寄存器
}
注:本人在sys.h中定义了operateSta枚举类型,用于标记操作是否成功。可自行添加在esp8266.h文件中
typedef enum {
FAIL = 0,
SUCCEED
} operateSta; //操作状态
#ifndef __ESP8266_H
#define __ESP8266_H
// <头文件> //
#include "../../Basic/sys/sys.h"
#include "../../Basic/usart/usart.h"
#include "../../Basic/delay/delay.h"
#include
// <头文件> //
#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
#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;
}
// <头文件> //
#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;
}
}
}