1.STM32驱动ESP8266模块
笔者所使用的ESP8266模块为正点原子开发的模块,该模块将通信接口变成了串口。接下来关于ESP8266模块的介绍均以此模块为基础。
1.1 CubeMX配置STM32F103C8T6芯片引脚
1.1.1 选择芯片型号
我们需要给芯片配置引脚,使它能够完成我们所需要的功能。
1.双击打开CubeMX软件,弹出界面如下图所示:
2.点击上图中的“ACCESS TO MCU SELECTOR”按钮,进入如下界面:
3.在“Part Number”中输入“STM32F103C8”字符,然后双击“Reference”中的“STM32F103C8Tx”,进入如下界面:
这样,我们就选择好了芯片类型。在上图所示的界面中,我们就可以配置芯片引脚,以满足我们的开发需要。
1.1.2 配置串口引脚和时钟频率
1.点击“System Core”,再点击“RCC”,将时钟源设置为外部时钟源:
2.点击“SYS”,将“Debug”设置为“Serial Wire”,“Timebase Source”设置为“SysTick”:
3.点击“Connectivity”,然后点击“USART3”,将通信方式设置为“Asynchronous”(异步通信):
4.在“UART3”界面,点击“DMA Settings”,将串口3的通信引脚模式设置为DMA(直接寄存器访问)模式。这是因为在笔者的工程中,WiFi需要不断地接收陀螺仪发送过来的数据并不断地将其发送出去,所以采用DMA模式,减轻CPU的负担:
读者在新建工程时,需要点击“Add”添加DMA通道。添加好后,将“Mode”设置为“Normal”,“Data Width”设置为“Byte”,如上图所示。
6.点击“Clock Configuration”,将“HCLK(MHz)”设置为72:
1.1.3 生成keil工程文件
1.所有的部分都配置好后,点击“Project Manager”,在“Toolchain/IDE”中选择编辑器为“MDK-ARM”,然后在“Min Version”中选择“V5”(此处笔者电脑安装的是V5版本,读者可以根据自己安装的Keil版本选择对应版本):
2.在“Project Manager”界面,点击“Code Generator”,在“Generated files”中将第一项也勾选上:
3.点击界面右上角的“GENERATE CODE”:
出现上述界面,就说明代码已经生成好了。
4.选择上图中的“Open Project”,代码界面如下图所示:
至此,我们就配置好了芯片的引脚并生成了代码。
1.2 接线
正点原子ESP8266模块保留了六个针脚,分别是VCC、GND、RXD、TXD、RST和IO0。平常使用过程中,我们仅仅需要使用前四个引脚即可。在上面的配置过程中,我们选择使用串口3与ESP8266模块进行通信,因此需要看芯片的引脚定义,看哪两个引脚是串口3的引脚:
可以看到,PB10为TX(发送),PB11为RX(接收)。则PB10接模块的RX脚,PB11接模块的TX脚。接线方式如下:
芯片板可以通过micro-USB线供电,也可以通过外接电源稳压模块供电。注意,在使用稳压模块时,最好使用5V电压给芯片供电,否则烧录器无法识别到芯片板的存在。ESP8266也可以使用稳压模块向其供电,5V和3.3V均可。
1.3 驱动逻辑和代码
要实现一个完整的工程,我们需要设计好模块的使用逻辑并使用代码将其实现。
1.3.1 AT指令与模块配置过程介绍
首先我们要配置模块的各项参数。参考正点原子的模块使用资料,结合本身项目需求,笔者选择将模块设置为AP模式,使用AP模式下的透传功能实现大量数据的透明传输。配置过程如下:
我们需要按上述流程完成模块模式的配置。那么,我们需要完成如下几件事请:
- 向模块发送第一个AT指令;
- 等待模块的返回内容并判断是否配置成功;
- 如果成功,发送下一个AT指令;如果不成功,则再发送一次当前AT指令;
- 直至最后开始发送数据的AT指令发送并配置成功
发送指令比较简单,使用HAL库的串口发送函数即可:
HAL_UART_Transmit(&huart3, (uint8_t *)AT_RESTORE, sizeof(AT_RESTORE), 0xFFFF)
其中,AT_RESTORE表示AT指令中的“重启”指令。该指令发送完成后,向串口发送检查指令“AT\r\n”。经笔者测试,模块会对检查指令返回字符串“AT\r\n\r\nOK\r\n”(在CubeMX工程中,“char”类型被定义为“uint8_t”类型)。同样,其他指令返回内容的数据类型也为字符串。所以,我们需要读取到串口3的返回值并识别是否是重启成功的返回值。声明uint8_t类型:
uint8_t AT[] = "AT\r\n";//检查指令
uint8_t ATRE[] = "AT\r\n\r\nOK\r\n";//检查指令的正确返回值
其中“AT”和“ATRE”均为字符串类型,AT是MCU向串口3(也就是模块)发送的检查指令,而ATRE为该指令的正确返回值。如果返回值不是ATRE的内容,则需要重新发送AT指令。
检查返回值的代码如下:
int checkResponse(uint8_t* response){
for(int i = 0;i < ATResRecCount;i ++){
if(ATCmdRxBuffer[i] != response[i]){
return 0;
}
}
return 1;
}
在上面的函数中,输入参数是正确的返回值。ATCmdRxBuffer中是串口接收到的实际的返回值。在这个函数中,按照顺序依次将实际的返回值和正确的返回值比较,如果实际返回值与正确返回值不一致,则函数返回整型0,否则返回1。我们可以根据返回值来判断模块是否返回了正确的返回值,进而决定是否进行下一步。
1.3.2 DMA空闲中断接收不定长数据与模块驱动代码
根据正点原子的模块资料:《ATK-ESP8266 WIFI用户手册_V1.5》(下载链接:WIFI模块ATK-ESP8266(ESP 01) — 正点原子资料下载中心 1.0.0 文档 (openedv.com)),不同的AT指令的返回值长度是不同的。也就是说,在串口向模块发送指令后,串口需要接收不定长的数据。在这里,我们可以使用DMA空闲中断来接收不定长数据,并判断返回值是否是配置成功的返回值。
1.我们已经在CubeMX中配置了串口3的DMA传输,这里不再赘述。生成代码后,界面如下:
点击左上角红色方框框柱的图标,对软件生成的工程代码进行编译,检查是否存在问题。编译完成后,控制台显示如下信息:
出现字样“0 Error(s),0 Warning(s)”,这说明生成的代码中不存在问题。然后,点击“魔术棒”:
出现如下界面:
将“Target”
2.在工程文件的文件树中找到“stm32f1xx_it.c”文件,找到如下代码块并在代码块中的* USER CODE BEGIN USART3_IRQn 1 /和/ USER CODE END USART3_IRQn 1 */中间添加代码,最终代码结果如下所示:
/**
* @brief This function handles USART3 global interrupt.
*/
void USART3_IRQHandler(void)
{
/* USER CODE BEGIN USART3_IRQn 0 */
/* USER CODE END USART3_IRQn 0 */
HAL_UART_IRQHandler(&huart3);
/* USER CODE BEGIN USART3_IRQn 1 */
if(__HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE) != RESET){
__HAL_UART_CLEAR_IDLEFLAG(&huart3);
HAL_UART_IdleCpltCallback(&huart3);
}
/* USER CODE END USART3_IRQn 1 */
}
3.由于在项目中涉及到接收机和遥控器之间的信息交换,有关WiFi驱动的代码较为复杂,笔者将驱动WiFi的相关代码统一放在一个头文件当中。在这里,笔者先介绍一下如何在Cube工程中添加.h文件。
打开项目文件夹,如下图所示:
打开上图中红框框出的“Core”文件夹:
打开“Inc”文件夹:
可以看到,这个位置就是项目中的头文件所在的位置了。在这个文件夹下,新建一个文本文档,命名为“esp8266”,并将文件后缀修改为“.h”。创建好后,打开keil工程,找到如下代码块并添加代码:
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "esp8266.h"
#include "stdio.h"
/* USER CODE END Includes */
然后编译。编译完成后,展开“main.c”文件的文件树,可以看到“esp8266.h”文件已经添加至文件树,我们就可以在esp8266.h文件中添加我们的业务代码了:
4.添加好头文件后,我们就可以开始着手编写ESP8266模块的驱动代码了。首先,头文件的首尾部分需要添加如下信息:
#ifndef __ESP8266_H__
#define __ESP8266_H__
/**
业务代码
*/
#endif
注意,在“#endif”这一行,要按回车键再添加一行空白行,否则编译时会报错。
5.在“stm32f1xx_it.c”文件中,找到如下代码:
/* External variables --------------------------------------------------------*/
extern DMA_HandleTypeDef hdma_usart1_rx;
extern DMA_HandleTypeDef hdma_usart1_tx;
extern DMA_HandleTypeDef hdma_usart2_rx;
extern DMA_HandleTypeDef hdma_usart2_tx;
extern DMA_HandleTypeDef hdma_usart3_rx;
extern DMA_HandleTypeDef hdma_usart3_tx;
extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart2;
extern UART_HandleTypeDef huart3;
将第十行复制并粘贴到“esp8266.h”文件中。
6.完成上一步骤后,在“esp8266.h”文件中添加如下代码。每行代码的意义以及作用笔者将在代码注释中加以说明。
/**
* @brief 项目中需要的AT指令
* @note
* @param
* @retval
*/
extern DMA_HandleTypeDef hdma_usart3_rx;
#define ATRELENGTH 256 //接收缓冲区的大小。这个大小需要大于一组数据的长度
uint8_t ATCmdRxBuffer[ATRELENGTH]; //接收缓冲区
uint8_t ATResRecCount = 0; //每次接收到的字节数
uint8_t ATCmdRxFlag = 0; //接收状态标志
7.在“main.c”文件中找到如下对应的位置并添加代码:
/* USER CODE BEGIN 2 */
__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);//使能串口3 IDLE中断
HAL_Delay(100);
/* USER CODE END 2 */
/**
......
*/
/* USER CODE BEGIN 4 */
/* 中断接收回调函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
if(huart -> Instance == USART3){
//重新启动DMA接收
HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
}
}
/* IDLE空闲中断回调函数 */
void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart){
ATCmdRxFlag = 1;//设置接收完成标志
}
/* USER CODE END 4 *
8.在“mian.h”文件中找到如下位置并添加代码:
/* USER CODE BEGIN EFP */
void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart);
/* USER CODE END EFP */
9.以上步骤都完成后,在“esp8266.h”文件中添加代码。其中,LED灯的引脚配置为将PA4脚设置为GPIO_Output模式,已经在CubeMX中配置。完整的代码如下:
#ifndef __ESP8266_H__
#define __ESP8266_H__
/**
* @brief esp8266模块需要的各类定义和函数文件
* @note
* @param
* @retval
*
*
*
*/
#include "stm32f1xx_hal.h"
#include "usart.h"
#include "string.h"
#include "main.h"
/**
* @brief 项目中需要的AT指令
* @note
* @param
* @retval
*/
extern DMA_HandleTypeDef hdma_usart3_rx;
#define ATRELENGTH 256
uint8_t ATCmdRxBuffer[ATRELENGTH];
uint8_t ATResRecCount = 0;
uint8_t ATCmdRxFlag = 0;
uint8_t AT_RESTORE[] = "AT+RESTORE\r\n"; //恢复出厂设置
uint8_t AT[] = "AT\r\n";//检查指令
uint8_t ATRE[] = "AT\r\n\r\nOK\r\n";//检查指令的正确返回值
uint8_t AT_MODEAP[] = "AT+CWMODE=2\r\n";//设置为AP模式
uint8_t ATMODEAPRE[] = "AT+CWMODE=2\r\n\r\nOK\r\n";//设置为AP模式指令的正确返回值
uint8_t AT_RST[] = "AT+RST\r\n";//重启生效
uint8_t ATRSTRE[] = "AT+RST\r\n\r\nOK\r\n";//重启生效指令的正确返回值
//设置WiFi模块的名称和密码,读者记得设置自己的名称和密码
uint8_t AT_SAP[] = "AT+CWSAP=\"xxxxxx\",\"xxxxxx\",1,4\r\n";//设置WIFI名称和密码,通道号1,加密方式WPA_WPA2_PSK
uint8_t ATSAPRE[] = "AT+CWSAP=\"xxxxxx\",\"xxxxxx\",1,4\r\n\r\nOK\r\n";//设置名称密码的正确返回值
uint8_t AT_MUX0[] = "AT+CIPMUX=0\r\n";//开启单链接
uint8_t ATMUXRE[] = "AT+CIPMUX=0\r\n\r\nOK\r\n";//开启单链接命令的正确返回值
uint8_t AT_CWLIF[] = "AT+CWLIF\r\n";//查看已接入设备的IP
uint8_t ATCWLIFRENONE[] = "AT+CWLIF\r\n\r\nOK\r\n";//没有设备接入时查看命令的返回值
uint8_t AT_START[] = "AT+CIPSTART=\"TCP\",\"192.168.4.2\",8080\r\n";//建立TCP连接
uint8_t ATSTARTRESUCCESS[] = "AT+CIPSTART=\"TCP\",\"192.168.4.2\",8080\r\nCONNECT\r\n\r\nOK\r\n";//期待TCP连接成功回应(第一次)
uint8_t ATSTARTREALREADY[] = "AT+CIPSTART=\"TCP\",\"192.168.4.2\",8080\r\nALREADY CONNECTED\r\n\r\nERROR\r\n";//已经建立TCP连接的返回值(成功建立TCP连接以后的值)
uint8_t ATSTARTREFAILED[] = "AT+CIPSTART=\"TCP\",\"192.168.4.2\",8080\r\n\r\nERROR\r\nCLOSED\r\n";//TCP连接建立失败的返回值
uint8_t AT_CIPMODE1[] = "AT+CIPMODE=1\r\n";//开启透传模式命令
uint8_t ATCIPMODE1RE[] = "AT+CIPMODE=1\r\n\r\nOK\r\n";//开启透传模式的正确返回值
uint8_t AT_SEND[] = "AT+CIPSEND\r\n";//开始传输命令
uint8_t ATSENDRE[] = "AT+CIPSEND\r\n\r\nOK\r\n\r\n>";//开始传输命令的正确返回值
uint8_t AT_QUIT[] = "+++"; //退出透传
uint8_t AT_SAVETRANSLINK[] = "AT+SAVETRANSLINK=1,\"192.168.4.2\",8080,\"TCP\"";//不保存透传连接到flash
uint8_t ATSAVETRANSLINKRE[] = "AT+SAVETRANSLINK=1,\"192.168.4.2\",8080,\"TCP\"\r\n\r\nOK\r\n";//不保存连接到flash的回应
/**
* @brief 发送AT指令的函数宏定义
* @note
* @param
* @retval
*/
#define ATRESTORE HAL_UART_Transmit(&huart3, (uint8_t *)AT_RESTORE, sizeof(AT_RESTORE), 0xFFFF) //发送恢复出厂设置命令
#define ATCHECK HAL_UART_Transmit(&huart3, (uint8_t *)AT, sizeof(AT), 0xFFFF) //发送AT检查指令
#define ATMODEAP HAL_UART_Transmit(&huart3, (uint8_t *)AT_MODEAP, sizeof(AT_MODEAP), 0xFFFF) //发送设置为AP模式指令
#define ATRST HAL_UART_Transmit(&huart3, (uint8_t *)AT_RST, sizeof(AT_RST), 0xFFFF) //发送RESET指令
#define ATSAP HAL_UART_Transmit(&huart3, (uint8_t *)AT_SAP, sizeof(AT_SAP), 0xFFFF) //发送设置WIFI名称密码命令
#define ATCWLIF HAL_UART_Transmit(&huart3, (uint8_t *)AT_CWLIF, sizeof(AT_CWLIF), 0xFFFF);//发送查询接入设备命令
#define ATMUX0 HAL_UART_Transmit(&huart3, (uint8_t *)AT_MUX0, sizeof(AT_MUX0), 0xFFFF) //发送开启单链接命令
#define ATSTART HAL_UART_Transmit(&huart3, (uint8_t *)AT_START, sizeof(AT_START), 0xFFFF) //发送建立TCP连接命令
#define ATCIPMODE1 HAL_UART_Transmit(&huart3, (uint8_t *)AT_CIPMODE1, sizeof(AT_CIPMODE1), 0xFFFF) //发送开启透传模式命令
#define ATSEND HAL_UART_Transmit(&huart3, (uint8_t *)AT_SEND, sizeof(AT_SEND), 0xFFFF) //发送开始传输命令
#define ATQUIT HAL_UART_Transmit(&huart3, (uint8_t *)AT_QUIT, 3, 0xFFFF) //发送退出透传命令
#define ATSAVETRANSLINK HAL_UART_Transmit(&huart3, (uint8_t *)AT_SAVETRANSLINK,sizeof(AT_SAVETRANSLINK), 0xFFFF);//发送保存TCP到Flash
/**
* @brief 各类操作函数
* @note
* @param
* @retval
*/
/* 检查返回值和期待值是否一致 */
int checkResponse(uint8_t* response){
for(int i = 0;i < ATResRecCount;i ++){
if(ATCmdRxBuffer[i] != response[i]){
return 0;
}
}
return 1;
}
/* 设置小灯泡亮 */
void LED(int Delay)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
HAL_Delay(Delay);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
HAL_Delay(Delay);
}
/* 初始化AP模式下TCP客户端 */
void Init_AP_TCPClient(){
ATQUIT;//发送退出透传命令
HAL_Delay(1000);
HAL_UART_Receive_DMA(&huart3, (uint8_t *)ATCmdRxBuffer,ATRELENGTH);//启动DMA接收
int compareResult = 0;//字符串比较结果
int STARTcR = 0;//建立TCP连接时的第二个比较结果
int STARTcE = 0;//建立TCP连接失败的比较结果
//发送AT检查指令直至响应成功
do{
ATCHECK;
HAL_Delay(500);
if(ATCmdRxFlag == 1){
ATCmdRxFlag = 0;
HAL_UART_DMAStop(&huart3);
ATResRecCount = ATRELENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
compareResult = checkResponse(ATRE);
ATResRecCount = 0;
HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
}
}while(compareResult != 1);
LED(500);
compareResult = 0;
//发送设置AP模式指令直至响应成功
do{
ATMODEAP;
HAL_Delay(500);
if(ATCmdRxFlag == 1){
ATCmdRxFlag = 0;
HAL_UART_DMAStop(&huart3);
ATResRecCount = ATRELENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
compareResult = checkResponse(ATMODEAPRE);
ATResRecCount = 0;
HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
}
}while(compareResult != 1);
LED(500);
compareResult = 0;
//发送RESET指令
ATRST;
HAL_Delay(500);
if(ATCmdRxFlag == 1){
ATCmdRxFlag = 0;
HAL_UART_DMAStop(&huart3);
ATResRecCount = ATRELENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
ATResRecCount = 0;
HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
}
LED(500);
//发送设置SSID PASSWORD命令
do{
ATSAP;
HAL_Delay(500);
if(ATCmdRxFlag == 1){
ATCmdRxFlag = 0;
HAL_UART_DMAStop(&huart3);
ATResRecCount = ATRELENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
compareResult = checkResponse(ATSAPRE);
ATResRecCount = 0;
HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
}
}while(compareResult != 1);
LED(500);
compareResult = 0;
//发送查询接入设备命令直至成功
do{
ATCWLIF;
HAL_Delay(500);
if(ATCmdRxFlag == 1){
ATCmdRxFlag = 0;
HAL_UART_DMAStop(&huart3);
ATResRecCount = ATRELENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
compareResult = checkResponse(ATCWLIFRENONE);
ATResRecCount = 0;
HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
}
}while(compareResult == 1);
LED(500);
compareResult = 0;
//发送开启单链接命令直至成功
do{
ATMUX0;
HAL_Delay(500);
if(ATCmdRxFlag == 1){
ATCmdRxFlag = 0;
HAL_UART_DMAStop(&huart3);
ATResRecCount = ATRELENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
compareResult = checkResponse(ATMUXRE);
ATResRecCount = 0;
HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
}
}while(compareResult != 1);
LED(500);
compareResult = 0;
//发送建立TCP连接命令直至成功
do{
ATSTART;
HAL_Delay(7000);//此处延时需要长一些,该命令反应较慢
if(ATCmdRxFlag == 1){
ATCmdRxFlag = 0;
HAL_UART_DMAStop(&huart3);
ATResRecCount = ATRELENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
compareResult = checkResponse(ATSTARTRESUCCESS);
STARTcR = checkResponse(ATSTARTREALREADY);
STARTcE = checkResponse(ATSTARTREFAILED);
if(STARTcE == 1){
do{
ATMUX0;
HAL_Delay(500);
if(ATCmdRxFlag == 1){
ATCmdRxFlag = 0;
HAL_UART_DMAStop(&huart3);
ATResRecCount = ATRELENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
compareResult = checkResponse(ATMUXRE);
ATResRecCount = 0;
HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
}
}while(compareResult != 1);
LED(200);
compareResult = 0;
}
ATResRecCount = 0;
HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
}
}while(compareResult != 1 && STARTcR != 1);
LED(500);
compareResult = 0;
//发送开启透传命令直至成功
do{
ATCIPMODE1;
HAL_Delay(500);
if(ATCmdRxFlag == 1){
ATCmdRxFlag = 0;
HAL_UART_DMAStop(&huart3);
ATResRecCount = ATRELENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
compareResult = checkResponse(ATCIPMODE1RE);
ATResRecCount = 0;
HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
}
}while(compareResult != 1);
LED(500);
compareResult = 0;
//发送开始传输命令直至成功
do{
ATSEND;
HAL_Delay(500);
if(ATCmdRxFlag == 1){
ATCmdRxFlag = 0;
HAL_UART_DMAStop(&huart3);
ATResRecCount = ATRELENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
compareResult = checkResponse(ATSENDRE);
ATResRecCount = 0;
HAL_UART_Receive_DMA(&huart3,(uint8_t*)ATCmdRxBuffer,ATRELENGTH);
}
}while(compareResult != 1);
HAL_UART_DMAStop(&huart3);
LED(500);
compareResult = 0;
HAL_UART_DMAStop(&huart2);
HAL_Delay(100);
__HAL_UNLOCK(&huart2);
HAL_Delay(100);
}
#endif
注意,在建立TCP链接时,电脑需要接入到模块热点后才能成功建立TCP连接。上述代码完成后,就可以实现WiFi模块和电脑之间数据的透明传输了。CPU可以通过串口发送给模块,模块通过WiFi发送给电脑;而电脑也可以通过WiFi发送信息给模块,模块再通过串口将信息发送给CPU。这样就完成了电脑和CPU之间的无线通信。