STM32F103C8T6连接ESP-01S实现WiFi通信:TCP客户端开发实战(二)

文章目录

前言

  上一篇介绍了ESP-01S简单测试和实现通讯常用AT指令,这一篇实现F103与ESP-01S的WiFi通讯。如果不清楚ESP-01S常用AT指令可以参考这篇文章:STM32F103C8T6连接ESP-01S实现WiFi通信:TCP客户端开发实战(一)

一、硬件准备

硬件 作用
STM32F103C8T6 主控制器
ESP-01S WiFi模块
USB-TTL 用于调试ESP-01S
手机开启热点 提供无线网络

ESP-01S本质上就是一个ESP8266模块。它本身已经集成了WiFi功能,并且可以运行AT固件。因此STM32不需要了解复杂的WiFi底层协议,只需要通过串口给ESP-01S发送命令即可。

二、STM32与ESP-01S连接

STM32F103C8T6 ESP-01S
PA2 RX
PA3 TX
PA9 USB转TTL -> RX
PA10 USB转TTL -> TX

注意:我们在拿到优信的ESP01S之后可以通过他的规格书查到其工作电压为3.3V,不要直接给ESP-01S使用5V供电。另外,ESP8266在WiFi发送数据的时候电流会明显增加,所以ESP-01S的3.3V电源一定要稳定。如果使用STM32开发板上的3.3V给ESP-01S供电,建议确认一下3.3V稳压器的带载能力。

三、ESP-01S 工作模式

ESP8266 可以通过 AT 指令配置成不同工作模式。

本项目使用:

  AT+CWMODE=1,即 STA 模式。

  ESP-01S 连接现有 WiFi:AT+CWJAP="WiFi名称","WiFi密码"

  然后获取 IP:AT+CIFSR

  开启多连接:AT+CIPMUX=1

  开启 TCP Server:AT+CIPSERVER=1,8288

  这里使用:8288,作为 TCP Server 端口。

  手机 TCP Client 连接:STM32所在局域网IP:8288

即可建立通信。

四、STM32 工程软件结构

工程主要分为:main.c,esp8266.c,esp8266.h,usart.c,usart.h

其中:

  main.c 负责:系统初始化→ESP8266初始化→连接WiFi→开启TCP Server→手机数据处理→电脑数据处理

  esp8266.c 负责:ESP8266 AT指令;WiFi连接;TCP Server;TCP发送;TCP接收

  USART1 负责:STM32 <-> 电脑

  USART2 负责:STM32 <-> ESP-01S

五、ESP8266 初始化与测试

c 复制代码
首先初始化 USART2:

void ESP8266_Init(void)
{
    ESP8266_USART_Config();
}

ESP8266 串口参数:

USART_InitStructure.USART_BaudRate = macESP8266_USART_BAUD_RATE;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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(macESP8266_USARTx, &USART_InitStructure);

同时开启:

USART_IT_RXNE
USART_IT_IDLE

也就是:接收中断 + 空闲中断

初始化之后先发送:AT

ESP8266 正常情况下返回:OK

void ESP8266_AT_Test(void)
{
    char count = 0;

    macESP8266_RST_HIGH_LEVEL();
    delay_ms(1000);

    while(count < 10)
    {
        if(ESP8266_Cmd("AT", "OK", NULL, 500))
            return;

        ESP8266_Rst();
        count++;
    }
}

六、连接 WiFi和开启TCP Server

c 复制代码
设置 STA:

ESP8266_Net_Mode_Choose(STA);

然后连接 WiFi:

while(!ESP8266_JoinAP(macUser_ESP8266_ApSsid,
                      macUser_ESP8266_ApPwd));

对应 AT 指令:

AT+CWMODE=1
AT+CWJAP="SSID","PASSWORD"

连接成功后:OK

首先设置多连接:

ESP8266_Cmd("AT+CIPMUX=1", "OK", 0, 1000);

然后开启 TCP Server:

ESP8266_Cmd("AT+CIPSERVER=1,8288", "OK", 0, 1000);

完整初始化函数:

void ESP8266_StaTcpClient(void)
{
    printf("\r\n正在配置 ESP8266 ......\r\n");

    macESP8266_CH_ENABLE();

    ESP8266_AT_Test();

    ESP8266_Net_Mode_Choose(STA);

    while(!ESP8266_JoinAP(
        macUser_ESP8266_ApSsid,
        macUser_ESP8266_ApPwd));

    ESP8266_Cmd(
        "AT+CIFSR",
        "OK",
        0,
        1000
    );

    ESP8266_Cmd(
        "AT+CIPMUX=1",
        "OK",
        0,
        1000
    );

    ESP8266_Cmd(
        "AT+CIPSERVER=1,8288",
        "OK",
        0,
        1000
    );

    printf("\r\n配置 ESP8266 完毕\r\n");
}

七、USART1和USART2数据传输

c 复制代码
USART1 用于连接电脑。

电脑发送:abcdefg
USART1 接收中断:

volatile uint8_t PC_RX_BUF[256];
volatile uint16_t PC_RX_LEN = 0;
volatile uint8_t PC_RX_FINISH = 0;

void DEBUG_USART_IRQHandler(void)
{
    uint8_t ucTemp;

    if(USART_GetITStatus(
        USART1,
        USART_IT_RXNE) != RESET)
    {
        ucTemp = USART_ReceiveData(USART1);

        if(PC_RX_LEN < sizeof(PC_RX_BUF) - 1)
        {
            PC_RX_BUF[PC_RX_LEN++] = ucTemp;
        }
    }

    if(USART_GetITStatus(
        USART1,
        USART_IT_IDLE) != RESET)
    {
        USART_GetFlagStatus(
            USART1,
            USART_FLAG_IDLE
        );

        USART_ReceiveData(USART1);

        PC_RX_BUF[PC_RX_LEN] = '\0';

        PC_RX_FINISH = 1;
    }
}

USART2接收 ESP8266 数据
ESP8266 的串口中断采用:
void macESP8266_USART_INT_FUN(void)
{
    uint8_t ucCh;

    if(USART_GetITStatus(
        macESP8266_USARTx,
        USART_IT_RXNE) != RESET)
    {
        ucCh = USART_ReceiveData(macESP8266_USARTx);

        if(strEsp8266_Fram_Record.InfBit.FramLength <
           (RX_BUF_MAX_LEN - 1))
        {
            strEsp8266_Fram_Record.Data_RX_BUF[
                strEsp8266_Fram_Record.InfBit.FramLength++
            ] = ucCh;
        }
    }

    if(USART_GetITStatus(
        macESP8266_USARTx,
        USART_IT_IDLE) == SET)
    {
        strEsp8266_Fram_Record.InfBit.FramFinishFlag = 1;

        ucCh = USART_ReceiveData(macESP8266_USARTx);

        ucTcpClosedFlag = strstr(
            (char *)strEsp8266_Fram_Record.Data_RX_BUF,
            "CLOSED\r\n"
        ) ? 1 : 0;
    }
}

八、main.c 处理手机数据

c 复制代码
主循环检测:FramFinishFlag,表示一帧数据接收完成。

char *pData;

while(1)
{
    if(strEsp8266_Fram_Record.InfBit.FramFinishFlag)
    {
        strEsp8266_Fram_Record.InfBit.FramFinishFlag = 0;

        strEsp8266_Fram_Record.Data_RX_BUF[
            strEsp8266_Fram_Record.InfBit.FramLength
        ] = '\0';

        if(strstr(
            (char *)strEsp8266_Fram_Record.Data_RX_BUF,
            "+IPD") != NULL)
        {
            pData = strchr(
                (char *)strEsp8266_Fram_Record.Data_RX_BUF,
                ':'
            );

            if(pData != NULL)
            {
                pData++;

                printf(
                    "手机 -> 电脑:%s",
                    pData
                );
            }
        }

        strEsp8266_Fram_Record.InfBit.FramLength = 0;
    }
}

九、手机 TCP 客户端连接与电脑串口传输数据

手机安装一个 TCP 网络调试助手,设置:协议:TCP Client,IP:ESP-01S获取到的IP,端口:8288

例如 ESP-01S 获取的 IP 为:192.168.1.100

手机连接:192.168.1.100:8288

手机发送:123456,ESP8266 在普通 TCP 模式下会将接收数据通过串口发送给 STM32,电脑串口就会收到:123456

电脑串口发送:abcde,手机客户端就会收到:abcde

十、代码示例

esp8266.c

c 复制代码
#include "esp8266.h"	

/*****************辰哥单片机设计******************
											STM32
 * 项目			:	ESP8266模块通信实验                     
 * 版本			: V1.0
 * 日期			: 2024.9.30
 * MCU			:	STM32F103C8T6
 * 接口			:	串口2									
 * IP账号		:	辰哥单片机设计(同BILIBILI|抖音|快手|小红书|CSDN|公众号|视频号等)
 * 作者			:	辰哥 
 * 工作室		: 异方辰电子工作室
 * 讲解视频	:	https://www.bilibili.com/video/BV1oSxteYEZy/?share_source=copy_web
 * 官方网站	:	www.yfcdz.cn

**********************BEGIN***********************/

#include "esp8266.h"
#include "common.h"
#include <stdio.h>  
#include <string.h>  
#include <stdbool.h>
#include "delay.h"

volatile uint8_t ucTcpClosedFlag = 0;
volatile uint8_t ESP8266_Silent = 0;

char cStr [ 1500 ] = { 0 };
volatile uint8_t PC_RX_BUF[256];
volatile uint16_t PC_RX_LEN = 0;
volatile uint8_t PC_RX_FINISH = 0;
static void                   ESP8266_GPIO_Config                 ( void );
static void                   ESP8266_USART_Config                ( void );
static void                   ESP8266_USART_NVIC_Configuration    ( void );
struct  STRUCT_USARTx_Fram strEsp8266_Fram_Record = { 0 };


/**
  * @brief  ESP8266 (Sta Tcp Client)透传
  * @param  无
  * @retval 无
  */
void ESP8266_StaTcpClient ( void )
{
//	uint8_t ucStatus;
//	char cStr [ 100 ] = { 0 };
  printf ( "\r\n正在配置 ESP8266 ......\r\n" );

	macESP8266_CH_ENABLE();
	
	ESP8266_AT_Test ();
	
	ESP8266_Net_Mode_Choose ( STA );

  while ( ! ESP8266_JoinAP ( macUser_ESP8266_ApSsid, macUser_ESP8266_ApPwd ) );	
	ESP8266_Cmd ( "AT+CIFSR", "OK", 0, 1000 );
	ESP8266_Cmd ( "AT+CIPMUX=1", "OK", 0, 1000 );
	
	
	ESP8266_Cmd ( "AT+CIPSERVER=1,8288", "OK", 0, 1000 );
	//ESP8266_Cmd("AT+CIPSTART="TCP",192.168.1.1,8000","OK",0,1000);
	
	printf( "\r\n配置 ESP8266 完毕\r\n" ); 
	
	
		
}


/**
  * @brief  ESP8266初始化函数
  * @param  无
  * @retval 无
  */
void ESP8266_Init ( void )
{
//	ESP8266_GPIO_Config (); 
	
	ESP8266_USART_Config (); 
	
	
//	macESP8266_RST_HIGH_LEVEL();

//	macESP8266_CH_DISABLE();
	
	
}


/**
  * @brief  初始化ESP8266用到的GPIO引脚
  * @param  无
  * @retval 无
  */
static void ESP8266_GPIO_Config ( void )
{
	/*定义一个GPIO_InitTypeDef类型的结构体*/
	GPIO_InitTypeDef GPIO_InitStructure;


	/* 配置 CH_PD 引脚*/
	macESP8266_CH_PD_APBxClock_FUN ( macESP8266_CH_PD_CLK, ENABLE ); 
											   
	GPIO_InitStructure.GPIO_Pin = macESP8266_CH_PD_PIN;	

	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   
   
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 

	GPIO_Init ( macESP8266_CH_PD_PORT, & GPIO_InitStructure );	 

	
	/* 配置 RST 引脚*/
	macESP8266_RST_APBxClock_FUN ( macESP8266_RST_CLK, ENABLE ); 
											   
	GPIO_InitStructure.GPIO_Pin = macESP8266_RST_PIN;	

	GPIO_Init ( macESP8266_RST_PORT, & GPIO_InitStructure );	 
}


/**
  * @brief  初始化ESP8266用到的 USART
  * @param  无
  * @retval 无
  */
static void ESP8266_USART_Config ( void )
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	
	
	/* config USART clock */
	macESP8266_USART_APBxClock_FUN ( macESP8266_USART_CLK, ENABLE );
	macESP8266_USART_GPIO_APBxClock_FUN ( macESP8266_USART_GPIO_CLK, ENABLE );
	
	/* USART GPIO config */
	/* Configure USART Tx as alternate function push-pull */
	GPIO_InitStructure.GPIO_Pin =  macESP8266_USART_TX_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(macESP8266_USART_TX_PORT, &GPIO_InitStructure);  
  
	/* Configure USART Rx as input floating */
	GPIO_InitStructure.GPIO_Pin = macESP8266_USART_RX_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(macESP8266_USART_RX_PORT, &GPIO_InitStructure);
	
	/* USART1 mode config */
	USART_InitStructure.USART_BaudRate = macESP8266_USART_BAUD_RATE;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	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(macESP8266_USARTx, &USART_InitStructure);
	
	
	/* 中断配置 */
	USART_ITConfig ( macESP8266_USARTx, USART_IT_RXNE, ENABLE ); //使能串口接收中断 
	USART_ITConfig ( macESP8266_USARTx, USART_IT_IDLE, ENABLE ); //使能串口总线空闲中断 	

	ESP8266_USART_NVIC_Configuration ();
	
	
	USART_Cmd(macESP8266_USARTx, ENABLE);
	
	
}


/**
  * @brief  配置 ESP8266 USART 的 NVIC 中断
  * @param  无
  * @retval 无
  */
static void ESP8266_USART_NVIC_Configuration ( void )
{
	NVIC_InitTypeDef NVIC_InitStructure; 
	
	
	/* Configure the NVIC Preemption Priority Bits */  
	NVIC_PriorityGroupConfig ( macNVIC_PriorityGroup_x );

	/* Enable the USART2 Interrupt */
	NVIC_InitStructure.NVIC_IRQChannel = macESP8266_USART_IRQ;	 
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);

}


/*
 * 函数名:ESP8266_Rst
 * 描述  :重启WF-ESP8266模块
 * 输入  :无
 * 返回  : 无
 * 调用  :被 ESP8266_AT_Test 调用
 */
void ESP8266_Rst ( void )
{
	#if 0
	 ESP8266_Cmd ( "AT+RST", "OK", "ready", 2500 );   	
	
	#else
	 macESP8266_RST_LOW_LEVEL();
	 delay_ms ( 500 ); 
	 macESP8266_RST_HIGH_LEVEL();
	#endif

}


/*
 * 函数名:ESP8266_Cmd
 * 描述  :对WF-ESP8266模块发送AT指令
 * 输入  :cmd,待发送的指令
 *         reply1,reply2,期待的响应,为NULL表不需响应,两者为或逻辑关系
 *         waittime,等待响应的时间
 * 返回  : 1,指令发送成功
 *         0,指令发送失败
 * 调用  :被外部调用
 */
bool ESP8266_Cmd ( char * cmd, char * reply1, char * reply2, u32 waittime )
{    
	strEsp8266_Fram_Record .InfBit .FramLength = 0;               //从新开始接收新的数据包

	macESP8266_Usart ( "%s\r\n", cmd );

	if ( ( reply1 == 0 ) && ( reply2 == 0 ) )                      //不需要接收数据
		return true;
	
	delay_ms( waittime );                 //延时
	
	strEsp8266_Fram_Record.Data_RX_BUF[strEsp8266_Fram_Record.InfBit.FramLength] = '\0';

	if (!ESP8266_Silent)
	{
		macPC_Usart("%s", strEsp8266_Fram_Record.Data_RX_BUF);
	}
	  
	if ( ( reply1 != 0 ) && ( reply2 != 0 ) )
		return ( ( bool ) strstr ( strEsp8266_Fram_Record .Data_RX_BUF, reply1 ) || 
						 ( bool ) strstr ( strEsp8266_Fram_Record .Data_RX_BUF, reply2 ) ); 
 	
	else if ( reply1 != 0 )
		return ( ( bool ) strstr ( strEsp8266_Fram_Record .Data_RX_BUF, reply1 ) );
	
	else
		return ( ( bool ) strstr ( strEsp8266_Fram_Record .Data_RX_BUF, reply2 ) );
	
}

/*
 * 函数名:ESP8266_AT_Test
 * 描述  :对WF-ESP8266模块进行AT测试启动
 * 输入  :无
 * 返回  : 无
 * 调用  :被外部调用
 */

void ESP8266_AT_Test ( void )
{
	char count=0;
	
	macESP8266_RST_HIGH_LEVEL();	
	delay_ms( 1000 );
	while ( count < 10 )
	{
		if( ESP8266_Cmd ( "AT", "OK", NULL, 500 ) ) return;
		ESP8266_Rst();
		++ count;
	}
}


/*
 * 函数名:ESP8266_Net_Mode_Choose
 * 描述  :选择WF-ESP8266模块的工作模式
 * 输入  :enumMode,工作模式
 * 返回  : 1,选择成功
 *         0,选择失败
 * 调用  :被外部调用
 */
bool 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 false;
  }
	
}


/*
 * 函数名:ESP8266_JoinAP
 * 描述  :WF-ESP8266模块连接外部WiFi
 * 输入  :pSSID,WiFi名称字符串
 *       :pPassWord,WiFi密码字符串
 * 返回  : 1,连接成功
 *         0,连接失败
 * 调用  :被外部调用
 */
bool ESP8266_JoinAP ( char * pSSID, char * pPassWord )
{
	char cCmd [120];

	sprintf ( cCmd, "AT+CWJAP=\"%s\",\"%s\"", pSSID, pPassWord );
	
	return ESP8266_Cmd ( cCmd, "OK", NULL, 5000 );
	
}


/*
 * 函数名:ESP8266_BuildAP
 * 描述  :WF-ESP8266模块创建WiFi热点
 * 输入  :pSSID,WiFi名称字符串
 *       :pPassWord,WiFi密码字符串
 *       :enunPsdMode,WiFi加密方式代号字符串
 * 返回  : 1,创建成功
 *         0,创建失败
 * 调用  :被外部调用
 */
bool 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 );
	
}


/*
 * 函数名:ESP8266_Enable_MultipleId
 * 描述  :WF-ESP8266模块启动多连接
 * 输入  :enumEnUnvarnishTx,配置是否多连接
 * 返回  : 1,配置成功
 *         0,配置失败
 * 调用  :被外部调用
 */
bool ESP8266_Enable_MultipleId ( FunctionalState enumEnUnvarnishTx )
{
	
	return ESP8266_Cmd ( "AT+CIPMUX=%d", "OK", 0, 500 );
	
}


/*
 * 函数名:ESP8266_Link_Server
 * 描述  :WF-ESP8266模块连接外部服务器
 * 输入  :enumE,网络协议
 *       :ip,服务器IP字符串
 *       :ComNum,服务器端口字符串
 *       :id,模块连接服务器的ID
 * 返回  : 1,连接成功
 *         0,连接失败
 * 调用  :被外部调用
 */
bool 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);

  else
	  sprintf ( cCmd, "AT+CIPSTART=%s", cStr );

	return ESP8266_Cmd ( cCmd, "OK", "ALREAY CONNECT", 4000 );
	
}


/*
 * 函数名:ESP8266_StartOrShutServer
 * 描述  :WF-ESP8266模块开启或关闭服务器模式
 * 输入  :enumMode,开启/关闭
 *       :pPortNum,服务器端口号字符串
 *       :pTimeOver,服务器超时时间字符串,单位:秒
 * 返回  : 1,操作成功
 *         0,操作失败
 * 调用  :被外部调用
 */
bool 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 );
	}
	
}


/*
 * 函数名:ESP8266_Get_LinkStatus
 * 描述  :获取 WF-ESP8266 的连接状态,较适合单端口时使用
 * 输入  :无
 * 返回  : 2,获得ip
 *         3,建立连接
 *         3,失去连接
 *         0,获取状态失败
 * 调用  :被外部调用
 */
uint8_t ESP8266_Get_LinkStatus ( void )
{
	if ( ESP8266_Cmd ( "AT+CIPSTATUS", "OK", 0, 500 ) )
	{
		if ( strstr ( strEsp8266_Fram_Record .Data_RX_BUF, "STATUS:2\r\n" ) )
			return 2;
		
		else if ( strstr ( strEsp8266_Fram_Record .Data_RX_BUF, "STATUS:3\r\n" ) )
			return 3;
		
		else if ( strstr ( strEsp8266_Fram_Record .Data_RX_BUF, "STATUS:4\r\n" ) )
			return 4;		

	}
	
	return 0;
	
}


/*
 * 函数名:ESP8266_Get_IdLinkStatus
 * 描述  :获取 WF-ESP8266 的端口(Id)连接状态,较适合多端口时使用
 * 输入  :无
 * 返回  : 端口(Id)的连接状态,低5位为有效位,分别对应Id5~0,某位若置1表该Id建立了连接,若被清0表该Id未建立连接
 * 调用  :被外部调用
 */
uint8_t ESP8266_Get_IdLinkStatus ( void )
{
	uint8_t ucIdLinkStatus = 0x00;
	
	
	if ( ESP8266_Cmd ( "AT+CIPSTATUS", "OK", 0, 500 ) )
	{
		if ( strstr ( strEsp8266_Fram_Record .Data_RX_BUF, "+CIPSTATUS:0," ) )
			ucIdLinkStatus |= 0x01;
		else 
			ucIdLinkStatus &= ~ 0x01;
		
		if ( strstr ( strEsp8266_Fram_Record .Data_RX_BUF, "+CIPSTATUS:1," ) )
			ucIdLinkStatus |= 0x02;
		else 
			ucIdLinkStatus &= ~ 0x02;
		
		if ( strstr ( strEsp8266_Fram_Record .Data_RX_BUF, "+CIPSTATUS:2," ) )
			ucIdLinkStatus |= 0x04;
		else 
			ucIdLinkStatus &= ~ 0x04;
		
		if ( strstr ( strEsp8266_Fram_Record .Data_RX_BUF, "+CIPSTATUS:3," ) )
			ucIdLinkStatus |= 0x08;
		else 
			ucIdLinkStatus &= ~ 0x08;
		
		if ( strstr ( strEsp8266_Fram_Record .Data_RX_BUF, "+CIPSTATUS:4," ) )
			ucIdLinkStatus |= 0x10;
		else 
			ucIdLinkStatus &= ~ 0x10;	

	}
	
	return ucIdLinkStatus;
	
}


/*
 * 函数名:ESP8266_Inquire_ApIp
 * 描述  :获取 F-ESP8266 的 AP IP
 * 输入  :pApIp,存放 AP IP 的数组的首地址
 *         ucArrayLength,存放 AP IP 的数组的长度
 * 返回  : 0,获取失败
 *         1,获取成功
 * 调用  :被外部调用
 */
uint8_t ESP8266_Inquire_ApIp ( char * pApIp, uint8_t ucArrayLength )
{
	char uc;
	
	char * pCh;
	
	
  ESP8266_Cmd ( "AT+CIFSR", "OK", 0, 500 );
	
	pCh = strstr ( strEsp8266_Fram_Record .Data_RX_BUF, "APIP,\"" );
	
	if ( pCh )
		pCh += 6;
	
	else
		return 0;
	
	for ( uc = 0; uc < ucArrayLength; uc ++ )
	{
		pApIp [ uc ] = * ( pCh + uc);
		
		if ( pApIp [ uc ] == '\"' )
		{
			pApIp [ uc ] = '\0';
			break;
		}
		
	}
	
	return 1;
	
}


/*
 * 函数名:ESP8266_UnvarnishSend
 * 描述  :配置WF-ESP8266模块进入透传发送
 * 输入  :无
 * 返回  : 1,配置成功
 *         0,配置失败
 * 调用  :被外部调用
 */
bool ESP8266_UnvarnishSend ( void )
{
	 ESP8266_Cmd ( "AT+CIPMODE=1", "OK", 0, 500 );
}

/*
 * 函数名:ESP8266_ExitUnvarnishSend
 * 描述  :配置WF-ESP8266模块退出透传模式
 * 输入  :无
 * 返回  : 无
 * 调用  :被外部调用
 */
void ESP8266_ExitUnvarnishSend ( void )
{
	delay_ms( 1000 );
	
	macESP8266_Usart ( "+++" );
	
	delay_ms( 500 ); 
	
}


/*
 * 函数名:ESP8266_SendString
 * 描述  :WF-ESP8266模块发送字符串
 * 输入  :enumEnUnvarnishTx,声明是否已使能了透传模式
 *       :pStr,要发送的字符串
 *       :ulStrLength,要发送的字符串的字节数
 *       :ucId,哪个ID发送的字符串
 * 返回  : 1,发送成功
 *         0,发送失败
 * 调用  :被外部调用
 */
bool ESP8266_SendString(
    FunctionalState enumEnUnvarnishTx,
    char *pStr,
    u32 ulStrLength,
    ENUM_ID_NO_TypeDef ucId
)
{
    char cStr[32];

    if(pStr == NULL || ulStrLength == 0)
    {
        return false;
    }

    /* 透传模式 */
    if(enumEnUnvarnishTx)
    {
        macESP8266_Usart("%s", pStr);
        return true;
    }

    if(ucId < 5)
    {
        sprintf(cStr,"AT+CIPSEND=%d,%lu",ucId,(unsigned long)ulStrLength);
    }
    else
    {
        return false;
    }

    ESP8266_Silent = 1;

    if(!ESP8266_Cmd(cStr, ">", NULL, 1000))
    {
        ESP8266_Silent = 0;
        return false;
    }

    ESP8266_Silent = 0;

    macESP8266_Usart("%s", pStr);

    delay_ms(500);

    return true;
}

/*
 * 函数名:ESP8266_ReceiveString
 * 描述  :WF-ESP8266模块接收字符串
 * 输入  :enumEnUnvarnishTx,声明是否已使能了透传模式
 * 返回  : 接收到的字符串首地址
 * 调用  :被外部调用
 */
char * ESP8266_ReceiveString ( FunctionalState enumEnUnvarnishTx )
{
	char * pRecStr = 0;
	
	
	strEsp8266_Fram_Record .InfBit .FramLength = 0;
	strEsp8266_Fram_Record .InfBit .FramFinishFlag = 0;
	
	while ( ! strEsp8266_Fram_Record .InfBit .FramFinishFlag );
	strEsp8266_Fram_Record .Data_RX_BUF [ strEsp8266_Fram_Record .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;
	
}

main.c

c 复制代码
#include "stm32f10x.h"
#include "led.h"
#include "usart.h"
#include "delay.h"
#include "esp8266.h"
#include <stdio.h>
#include <string.h>

/*****************辰哥单片机设计******************
											STM32
 * 项目			:	ESP8266模块通信实验                     
 * 版本			: V1.0
 * 日期			: 2024.9.30
 * MCU			:	STM32F103C8T6
 * 接口			:	参看usart2.h			
 * IP账号		:	辰哥单片机设计(同BILIBILI|抖音|快手|小红书|CSDN|公众号|视频号等)
 * 作者			:	辰哥 
 * 工作室		: 异方辰电子工作室
 * 讲解视频	:	https://www.bilibili.com/video/BV1oSxteYEZy/?share_source=copy_web
 * 官方网站	:	www.yfcdz.cn

**********************BEGIN***********************/

uint8_t flag;
char *pData;

int main(void)
{ 
	
  SystemInit();//配置系统时钟为72M	
	delay_init(72);
	LED_Init();
	LED_On();
	USART_Config();
	
	ESP8266_Init ();   //初始化WiFi模块使用的接口和外设(使用串口2)
	ESP8266_StaTcpClient ();//WiFi模块设置
	
	printf("Start \n");
	delay_ms(1000);
	
	
  while (1)
    {
        /* 手机 -> 电脑 */
        if (strEsp8266_Fram_Record.InfBit.FramFinishFlag)
        {
            strEsp8266_Fram_Record.InfBit.FramFinishFlag = 0;

            strEsp8266_Fram_Record.Data_RX_BUF[strEsp8266_Fram_Record.InfBit.FramLength] = '\0';

            if (strstr((char *)strEsp8266_Fram_Record.Data_RX_BUF,"+IPD") != NULL)
            {
                pData = strchr((char *)strEsp8266_Fram_Record.Data_RX_BUF,':');

                if (pData != NULL)
                {
                    pData++;
                    printf("手机 -> 电脑:%s\r\n", pData);
                }
            }

            strEsp8266_Fram_Record.InfBit.FramLength = 0;
        }


        /* 电脑 -> 手机 */
        if (PC_RX_FINISH)
        {
            PC_RX_FINISH = 0;

            if (PC_RX_LEN < sizeof(PC_RX_BUF))
            {
                PC_RX_BUF[PC_RX_LEN] = '\0';
            }

            printf("电脑 -> 手机:%s", PC_RX_BUF);

            ESP8266_SendString(DISABLE,(char *)PC_RX_BUF,PC_RX_LEN,Multiple_ID_0
            );

            PC_RX_LEN = 0;

            memset((void *)PC_RX_BUF,0,sizeof(PC_RX_BUF));
        }
    }
}

十一、效果展示

十二、常见问题

  1. TCP连接不上?

    答:检查IP地址和TCP端口是否正确;设备与ESP是否处在同一个局域网内;ESP连接2.4GHz WiFi,不要连接5GHz WiFi。

  2. STM32 能收到手机数据,但电脑显示乱码?

    答:检查:USART1和USART2波特率,以及:8位数据,1位停止位,无校验,同时检查 STM32 与 ESP-01S 是否共地。

  3. 手机收到的数据长度不正确?

    答:检查:AT+CIPSEND=0,数据长度,这里的数据长度必须和实际发送字节数一致。

相关推荐
雪兽软件2 小时前
2026物联网:边缘智能爆发,市场动能足,监管平衡术
物联网·边缘计算
Amir_zy2 小时前
TCP/IP 与 WebSocket 测试实战:从底层原理到场景落地
websocket·网络协议·tcp/ip
紫水木鱼2 小时前
物联网通讯协议_MQTT_持续更新
物联网·中间件
智闲电子设计2 小时前
STM32 外扩 QSPI Flash 实战:字库和开机图存不下,就把 W25Q64 映射成内存直接读
网络·stm32·嵌入式硬件
嵌入式阿蔡2 小时前
逻辑分析仪实战:从波形一眼看出 UART/SPI/I2C 通信错在哪
网络·stm32·单片机·嵌入式硬件·嵌入式实时数据库
地衣君2 小时前
从 profile 到 kanban:Hermes 多 Agent 协作解析及示例
网络·数据库·tcp/ip
LCG元2 小时前
STM32H743串口DMA空闲中断不定长接收与D-Cache一致性踩坑实录
stm32·单片机·嵌入式硬件
LCG元3 小时前
STM32内部温度传感器供电漂移8℃的根因定位与VREFINT比例校准实测
stm32·单片机·嵌入式硬件
g3voip13 小时前
防爆电话如何接入SIP调度系统?工业通信系统架构解析
服务器·tcp/ip·系统架构·信息与通信·ip