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名稱和密碼即可。