STM32 ESP8266 WiFi模块驱动
STM32 ESP8266 WiFi模块驱动
1. 简介
ESP8266是一款高度集成的WiFi芯片,可以为其他设备提供WiFi联网功能。本笔记记录了基于STM32 HAL库的ESP8266驱动实现,包括硬件连接、初始化配置、AT指令交互等关键部分。
项目源码仓库:STM32_Sensor_Drives
2. 硬件连接
2.1 引脚定义
ESP8266与STM32的连接引脚定义如下:
/******************************** ESP8266 连接引脚定义 ***********************************/
#define macESP8266_CH_PD_PORT GPIOA
#define macESP8266_CH_PD_PIN GPIO_PIN_11
#define macESP8266_RST_PORT GPIOA
#define macESP8266_RST_PIN GPIO_PIN_12#define macESP8266_USART_BAUD_RATE 115200
#define macESP8266_USART_TX_PORT GPIOB
#define macESP8266_USART_TX_PIN GPIO_Pin_10
#define macESP8266_USART_RX_PORT GPIOB
#define macESP8266_USART_RX_PIN GPIO_Pin_11
#define macESP8266_USARTx huart3
2.2 GPIO初始化
在gpio.c
中,我们初始化了ESP8266所需的GPIO引脚:
void MX_GPIO_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};/* GPIO Ports Clock Enable */__HAL_RCC_GPIOC_CLK_ENABLE();__HAL_RCC_GPIOD_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();/*Configure GPIO pin Output Level */HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4|GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_SET);/*Configure GPIO pins : PA4 PA11 PA12 */GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_11|GPIO_PIN_12;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);// ... 其他GPIO配置 ...
}
3. 数据结构定义
3.1 ESP8266工作模式枚举
/******************************* ESP8266 数据类型定义 ***************************/
typedef enum{STA, // 客户端模式AP, // 热点模式STA_AP // 客户端+热点模式
} ENUM_Net_ModeTypeDef;typedef enum{enumTCP, // TCP协议enumUDP, // UDP协议
} ENUM_NetPro_TypeDef;typedef enum{Multiple_ID_0 = 0,Multiple_ID_1 = 1,Multiple_ID_2 = 2,Multiple_ID_3 = 3,Multiple_ID_4 = 4,Single_ID_0 = 5,
} ENUM_ID_NO_TypeDef;typedef enum{OPEN = 0, // 开放模式WEP = 1, // WEP加密WPA_PSK = 2, // WPA-PSK加密WPA2_PSK = 3, // WPA2-PSK加密WPA_WPA2_PSK = 4, // WPA/WPA2混合加密
} ENUM_AP_PsdMode_TypeDef;
3.2 数据接收缓冲区结构
/******************************* ESP8266 外部全局变量声明 ***************************/
#define RX_BUF_MAX_LEN 1024 //最大接收缓存字节数extern struct STRUCT_USARTx_Fram //串口数据帧的处理结构体
{char Data_RX_BUF [ RX_BUF_MAX_LEN ]; // 数据接收缓冲区union {__IO uint16_t InfAll;struct {__IO uint16_t FramLength :15; // 数据帧长度__IO uint16_t FramFinishFlag :1; // 数据帧接收完成标志} InfBit;}Inf;} strEsp8266_Fram_Record;
4. 函数宏定义
/*********************************************** ESP8266 函数宏定义 *******************************************/
#define macESP8266_CH_ENABLE() HAL_GPIO_WritePin(macESP8266_CH_PD_PORT, macESP8266_CH_PD_PIN, GPIO_PIN_SET)
#define macESP8266_CH_DISABLE() HAL_GPIO_WritePin(macESP8266_CH_PD_PORT, macESP8266_CH_PD_PIN, GPIO_PIN_RESET)#define macESP8266_RST_HIGH_LEVEL() HAL_GPIO_WritePin(macESP8266_RST_PORT, macESP8266_RST_PIN, GPIO_PIN_SET)
#define macESP8266_RST_LOW_LEVEL() HAL_GPIO_WritePin(macESP8266_RST_PORT, macESP8266_RST_PIN, GPIO_PIN_RESET)
5. 串口重定向
为了方便打印调试信息,重定向了printf
函数:
/* 串口重定向相关定义 */
extern UART_HandleTypeDef *current_huart;
#define printf_log(...) do { \current_huart = &huart2; \printf(__VA_ARGS__); \
} while(0)#define printf_wifi(...) do { \current_huart = &macESP8266_USARTx; \printf(__VA_ARGS__); \
} while(0)/* printf重定向实现 */
int fputc(int ch, FILE *f)
{if (f == stdout) // 仅处理标准输出{HAL_UART_Transmit(current_huart, (uint8_t *)&ch, 1, 100); // 阻塞发送if (ch == '\n') // 发送\n时自动补充\rHAL_UART_Transmit(current_huart, (uint8_t *)"\r", 1, 100);}return ch;
}
6. ESP8266初始化与配置
6.1 ESP8266启动函数
/*** @brief ESP8266 (Sta Tcp Client)透传* @param 无* @retval 无*/
void ESP8266_start(void)
{printf_log("\r\n正在配置 ESP8266 ......\r\n");HAL_UART_Receive_IT(&macESP8266_USARTx, &UART_TEMP_CHAR, 1);macESP8266_CH_ENABLE(); // 使能ESP8266ESP8266_AT_Test(); // AT测试ESP8266_Net_Mode_Choose(STA_AP); // 设置为STA+AP模式// 连接到指定的WiFi热点while (!ESP8266_JoinAP(macUser_ESP8266_ApSsid, macUser_ESP8266_ApPwd));ESP8266_Cmd("AT+CIFSR", "OK", 0, 1000); // 查询IP地址ESP8266_Cmd("AT+CIPMUX=1", "OK", 0, 1000); // 启用多连接ESP8266_Cmd("AT+CIPSERVER=1,8288", "OK", 0, 1000); // 创建TCP服务器// ESP8266_Cmd("AT+CIPSTART="TCP",192.168.1.1,8000","OK",0,1000);printf_log("\r\n配置 ESP8266 完毕\r\n");
}
6.2 ESP8266复位函数
/** 函数名:ESP8266_Rst* 描述 :重启WF-ESP8266模块* 输入 :无* 返回 : 无* 调用 :被 ESP8266_AT_Test 调用*/
void ESP8266_Rst(void)
{
#if 0ESP8266_Cmd ( "AT+RST", "OK", "ready", 2500 );#elsemacESP8266_RST_LOW_LEVEL(); // 复位引脚拉低HAL_Delay(500);macESP8266_RST_HIGH_LEVEL(); // 复位引脚拉高
#endif
}
7. AT指令交互
7.1 AT指令发送与响应检查
/** 函数名:ESP8266_Cmd* 描述 :对WF-ESP8266模块发送AT指令* 输入 :cmd,待发送的指令* reply1,reply2,期待的响应,为NULL表不需响应,两者为或逻辑关系* waittime,等待响应的时间* 返回 : 1,指令发送成功* 0,指令发送失败* 调用 :被外部调用*/
uint8_t ESP8266_Cmd(char *cmd, char *reply1, char *reply2, uint32_t waittime)
{strEsp8266_Fram_Record.Inf.InfBit.FramLength = 0; // 从新开始接收新的数据包printf_wifi("%s\r\n", cmd); // 发送AT指令if ((reply1 == NULL) && (reply2 == NULL)) // 不需要接收数据return 1;HAL_Delay(waittime); // 延时等待响应strEsp8266_Fram_Record.Data_RX_BUF[strEsp8266_Fram_Record.Inf.InfBit.FramLength] = '\0';printf_log("%s", strEsp8266_Fram_Record.Data_RX_BUF); // 打印响应数据// 检查响应是否包含期望的字符串if ((reply1 != NULL) && (reply2 != NULL)){if (strstr(strEsp8266_Fram_Record.Data_RX_BUF, reply1) != NULL ||strstr(strEsp8266_Fram_Record.Data_RX_BUF, reply2) != NULL)return 1;return 0;}else if (reply1 != NULL){if (strstr(strEsp8266_Fram_Record.Data_RX_BUF, reply1) != NULL)return 1;return 0;}else{if (strstr(strEsp8266_Fram_Record.Data_RX_BUF, reply2) != NULL)return 1;return 0;}
}
7.2 AT测试函数
/** 函数名:ESP8266_AT_Test* 描述 :对WF-ESP8266模块进行AT测试启动* 输入 :无* 返回 : 无* 调用 :被外部调用*/
void ESP8266_AT_Test(void)
{char count = 0;macESP8266_RST_HIGH_LEVEL();HAL_Delay(1000);while (count < 10){if (ESP8266_Cmd("AT", "OK", NULL, 500)) // 发送AT测试指令return;ESP8266_Rst(); // 如果失败则复位ESP8266++count;}
}
8. WiFi连接功能
8.1 设置工作模式
/** 函数名:ESP8266_Net_Mode_Choose* 描述 :选择WF-ESP8266模块的工作模式* 输入 :enumMode,工作模式* 返回 : 1,选择成功* 0,选择失败* 调用 :被外部调用*/
uint8_t ESP8266_Net_Mode_Choose(ENUM_Net_ModeTypeDef enumMode)
{switch (enumMode){case STA: // 客户端模式return ESP8266_Cmd("AT+CWMODE=1", "OK", "no change", 2500);case AP: // 热点模式return ESP8266_Cmd("AT+CWMODE=2", "OK", "no change", 2500);case STA_AP: // 客户端+热点模式return ESP8266_Cmd("AT+CWMODE=3", "OK", "no change", 2500);default:return 0;}
}
8.2 连接WiFi热点
/** 函数名:ESP8266_JoinAP* 描述 :WF-ESP8266模块连接外部WiFi* 输入 :pSSID,WiFi名称字符串* :pPassWord,WiFi密码字符串* 返回 : 1,连接成功* 0,连接失败* 调用 :被外部调用*/
uint8_t ESP8266_JoinAP(char *pSSID, char *pPassWord)
{char cCmd[120];sprintf(cCmd, "AT+CWJAP=\"%s\",\"%s\"", pSSID, pPassWord);return ESP8266_Cmd(cCmd, "OK", NULL, 5000);
}
8.3 创建WiFi热点
/** 函数名:ESP8266_BuildAP* 描述 :WF-ESP8266模块创建WiFi热点* 输入 :pSSID,WiFi名称字符串* :pPassWord,WiFi密码字符串* :enunPsdMode,WiFi加密方式代号字符串* 返回 : 1,创建成功* 0,创建失败* 调用 :被外部调用*/
uint8_t ESP8266_BuildAP(char *pSSID, char *pPassWord, ENUM_AP_PsdMode_TypeDef enunPsdMode)
{char cCmd[120];sprintf(cCmd, "AT+CWSAP=\"%s\",\"%s\",1,%d", pSSID, pPassWord, enunPsdMode);return ESP8266_Cmd(cCmd, "OK", 0, 1000);
}
9. TCP/IP通信功能
9.1 启用多连接
/** 函数名:ESP8266_Enable_MultipleId* 描述 :WF-ESP8266模块启动多连接* 输入 :enumEnUnvarnishTx,配置是否多连接* 返回 : 1,配置成功* 0,配置失败* 调用 :被外部调用*/
uint8_t ESP8266_Enable_MultipleId(FunctionalState enumEnUnvarnishTx)
{return ESP8266_Cmd("AT+CIPMUX=%d", "OK", 0, 500);
}
9.2 连接服务器
/** 函数名:ESP8266_Link_Server* 描述 :WF-ESP8266模块连接外部服务器* 输入 :enumE,网络协议* :ip,服务器IP字符串* :ComNum,服务器端口字符串* :id,模块连接服务器的ID* 返回 : 1,连接成功* 0,连接失败* 调用 :被外部调用*/
uint8_t ESP8266_Link_Server(ENUM_NetPro_TypeDef enumE, char *ip, char *ComNum, ENUM_ID_NO_TypeDef id)
{char cStr[100] = {0}, cCmd[120];switch (enumE){case enumTCP:sprintf(cStr, "\"%s\",\"%s\",%s", "TCP", ip, ComNum);break;case enumUDP:sprintf(cStr, "\"%s\",\"%s\",%s", "UDP", ip, ComNum);break;default:break;}if (id < 5)sprintf(cCmd, "AT+CIPSTART=%d,%s", id, cStr);elsesprintf(cCmd, "AT+CIPSTART=%s", cStr);return ESP8266_Cmd(cCmd, "OK", "ALREAY CONNECT", 4000);
}
9.3 创建/关闭TCP服务器
/** 函数名:ESP8266_StartOrShutServer* 描述 :WF-ESP8266模块开启或关闭服务器模式* 输入 :enumMode,开启/关闭* :pPortNum,服务器端口号字符串* :pTimeOver,服务器超时时间字符串,单位:秒* 返回 : 1,操作成功* 0,操作失败* 调用 :被外部调用*/
uint8_t ESP8266_StartOrShutServer(FunctionalState enumMode, char *pPortNum, char *pTimeOver)
{char cCmd1[120], cCmd2[120];if (enumMode) // 开启服务器{sprintf(cCmd1, "AT+CIPSERVER=%d,%s", 1, pPortNum);sprintf(cCmd2, "AT+CIPSTO=%s", pTimeOver);return (ESP8266_Cmd(cCmd1, "OK", 0, 500) &&ESP8266_Cmd(cCmd2, "OK", 0, 500));}else // 关闭服务器{sprintf(cCmd1, "AT+CIPSERVER=%d,%s", 0, pPortNum);return ESP8266_Cmd(cCmd1, "OK", 0, 500);}
}
10. 数据收发功能
10.1 发送数据
/** 函数名:ESP8266_SendString* 描述 :WF-ESP8266模块发送字符串* 输入 :enumEnUnvarnishTx,声明是否已使能了透传模式* :pStr,要发送的字符串* :ulStrLength,要发送的字符串的字节数* :ucId,哪个ID发送的字符串* 返回 : 1,发送成功* 0,发送失败* 调用 :被外部调用*/
uint8_t ESP8266_SendString(FunctionalState enumEnUnvarnishTx, char *pStr, uint32_t ulStrLength, ENUM_ID_NO_TypeDef ucId)
{char cStr[20];uint8_t bRet = 0;if (enumEnUnvarnishTx) // 透传模式{printf_wifi("%s", pStr);bRet = 1;}else // 非透传模式{if (ucId < 5)sprintf(cStr, "AT+CIPSEND=%d,%d", ucId, ulStrLength + 2);elsesprintf(cStr, "AT+CIPSEND=%d", ulStrLength + 2);ESP8266_Cmd(cStr, "> ", 0, 1000); // 等待发送指令的响应bRet = ESP8266_Cmd(pStr, "SEND OK", 0, 1000); // 发送数据}return bRet;
}
10.2 接收数据
/** 函数名:ESP8266_ReceiveString* 描述 :WF-ESP8266模块接收字符串* 输入 :enumEnUnvarnishTx,声明是否已使能了透传模式* 返回 : 接收到的字符串首地址* 调用 :被外部调用*/
char *ESP8266_ReceiveString(FunctionalState enumEnUnvarnishTx)
{char *pRecStr = 0;strEsp8266_Fram_Record.Inf.InfBit.FramLength = 0;strEsp8266_Fram_Record.Inf.InfBit.FramFinishFlag = 0;while (!strEsp8266_Fram_Record.Inf.InfBit.FramFinishFlag); // 等待接收完成strEsp8266_Fram_Record.Data_RX_BUF[strEsp8266_Fram_Record.Inf.InfBit.FramLength] = '\0';if (enumEnUnvarnishTx) // 透传模式pRecStr = strEsp8266_Fram_Record.Data_RX_BUF;else // 非透传模式{if (strstr(strEsp8266_Fram_Record.Data_RX_BUF, "+IPD"))pRecStr = strEsp8266_Fram_Record.Data_RX_BUF;}return pRecStr;
}
10.3 UART接收中断回调
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if (huart == &macESP8266_USARTx){if (strEsp8266_Fram_Record.Inf.InfBit.FramLength < (RX_BUF_MAX_LEN - 1))strEsp8266_Fram_Record.Data_RX_BUF[strEsp8266_Fram_Record.Inf.InfBit.FramLength++] = UART_TEMP_CHAR;if (HAL_UART_GetState(&macESP8266_USARTx) == HAL_UART_STATE_READY){strEsp8266_Fram_Record.Inf.InfBit.FramFinishFlag = 1;ucTcpClosedFlag = strstr(strEsp8266_Fram_Record.Data_RX_BUF, "CLOSED\r\n") ? 1 : 0;strEsp8266_Fram_Record.Data_RX_BUF[strEsp8266_Fram_Record.Inf.InfBit.FramLength] = '\0';if (strEsp8266_Fram_Record.Data_RX_BUF[strEsp8266_Fram_Record.Inf.InfBit.FramLength-1] == '}'){printf_log(strEsp8266_Fram_Record.Data_RX_BUF);strEsp8266_Fram_Record.Inf.InfBit.FramLength = 0;}}HAL_UART_Receive_IT(&macESP8266_USARTx, &UART_TEMP_CHAR, 1);}
}
11. 主程序调用
在main.c
中,我们初始化外设并调用ESP8266启动函数:
int main(void)
{/* 初始化HAL库 */HAL_Init();/* 配置系统时钟 */SystemClock_Config();/* 初始化外设 */MX_GPIO_Init();MX_USART2_UART_Init();MX_USART3_UART_Init();/* 启动ESP8266 WiFi模块 */ESP8266_start();/* 主循环 */while (1){HAL_Delay(200);}
}
12. 总结
本驱动实现了基于STM32 HAL库的ESP8266 WiFi模块驱动,主要功能包括:
- ESP8266初始化与复位
- AT指令交互
- WiFi连接与热点创建
- TCP/IP服务器创建与客户端连接
- 数据收发功能
通过这些功能,可以实现STM32与ESP8266的通信,进而实现WiFi联网功能,为物联网应用提供基础。
使用时,只需修改macUser_ESP8266_ApSsid
和macUser_ESP8266_ApPwd
为实际的WiFi名称和密码即可。