普中玄武f103使用mqtt杂记

搞这个有点小累了,先简单把代码粘贴出来,后面再逐步把涉及的问题说明一下

wificonfig.h:

#ifndef __WIFI_CONFIG_H
#define	__WIFI_CONFIG_H

#include "system.h"


#if defined ( __CC_ARM   )
#pragma anon_unions
#endif


//* 数据类型 */
typedef enum{
	STA,
  AP,
  STA_AP  
} ENUM_Net_ModeTypeDef;


typedef enum{
	 enumTCP,
	 enumUDP,
} 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,
	WPA_PSK = 2,
	WPA2_PSK = 3,
	WPA_WPA2_PSK = 4,
} ENUM_AP_PsdMode_TypeDef;


#define RX_BUF_MAX_LEN     1024                                     //最大接收缓存字节数

// extern struct  STRUCT_USARTx_Fram                                  //串口数据帧的处理结构体
// {
// 	char  Data_RX_BUF[ RX_BUF_MAX_LEN ];
	
//   union {
//     __IO u16 InfAll;
//     struct {
// 		  __IO u16 FramLength       :15;                               // 14:0 
// 		  __IO u16 FramFinishFlag   :1;                                // 15 
// 	  } InfBit;
//   }; 
	
// } strPc_Fram_Record, strEsp8266_Fram_Record;



// 结构体定义(仅类型)
typedef struct STRUCT_USARTx_Fram {
    char  Data_RX_BUF[RX_BUF_MAX_LEN];
    union {
        __IO u16 InfAll;
        struct {
            __IO u16 FramLength     :15;
            __IO u16 FramFinishFlag :1;
        } InfBit;
    };
} USARTx_Fram_t;

// 声明外部变量(不分配内存)
extern USARTx_Fram_t strPc_Fram_Record, strEsp8266_Fram_Record;; // 仅声明



void WiFi_Config( void );
void USART3_printf( USART_TypeDef* USARTx, char *Data, ... );
#endif    /* __WIFI_CONFIG_H */

wificonfig.c

#ifndef __WIFI_CONFIG_H
#define	__WIFI_CONFIG_H

#include "system.h"


#if defined ( __CC_ARM   )
#pragma anon_unions
#endif


//* 数据类型 */
typedef enum{
	STA,
  AP,
  STA_AP  
} ENUM_Net_ModeTypeDef;


typedef enum{
	 enumTCP,
	 enumUDP,
} 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,
	WPA_PSK = 2,
	WPA2_PSK = 3,
	WPA_WPA2_PSK = 4,
} ENUM_AP_PsdMode_TypeDef;


#define RX_BUF_MAX_LEN     1024                                     //最大接收缓存字节数

// extern struct  STRUCT_USARTx_Fram                                  //串口数据帧的处理结构体
// {
// 	char  Data_RX_BUF[ RX_BUF_MAX_LEN ];
	
//   union {
//     __IO u16 InfAll;
//     struct {
// 		  __IO u16 FramLength       :15;                               // 14:0 
// 		  __IO u16 FramFinishFlag   :1;                                // 15 
// 	  } InfBit;
//   }; 
	
// } strPc_Fram_Record, strEsp8266_Fram_Record;



// 结构体定义(仅类型)
typedef struct STRUCT_USARTx_Fram {
    char  Data_RX_BUF[RX_BUF_MAX_LEN];
    union {
        __IO u16 InfAll;
        struct {
            __IO u16 FramLength     :15;
            __IO u16 FramFinishFlag :1;
        } InfBit;
    };
} USARTx_Fram_t;

// 声明外部变量(不分配内存)
extern USARTx_Fram_t strPc_Fram_Record, strEsp8266_Fram_Record;; // 仅声明



void WiFi_Config( void );
void USART3_printf( USART_TypeDef* USARTx, char *Data, ... );
#endif    /* __WIFI_CONFIG_H */

wifi_function.h:

#ifndef __WIFI_FUNCTION_H
#define	__WIFI_FUNCTION_H 


#include "system.h"
#include "wifi_config.h"
#include <stdbool.h>


#define     ESP8266_Usart( fmt, ... )           USART3_printf (USART3, fmt, ##__VA_ARGS__ )
#define     ESP8266_Usar_Json( fmt, ... )       USART3_printf (USART3, fmt, ##__VA_ARGS__ ) 

#define     PC_Usart( fmt, ... )                printf ( fmt, ##__VA_ARGS__ )

#define     ESP8266_CH_HIGH_LEVEL()             GPIO_SetBits( GPIOA, GPIO_Pin_4 )
#define     ESP8266_CH_LOW_LEVEL()              GPIO_ResetBits( GPIOA, GPIO_Pin_4 )

#define     ESP8266_RST_HIGH_LEVEL()            GPIO_SetBits( GPIOA, GPIO_Pin_15 )
#define     ESP8266_RST_LOW_LEVEL()             GPIO_ResetBits( GPIOA, GPIO_Pin_15 )



void        ESP8266_Choose                      ( FunctionalState enumChoose );
void        ESP8266_Rst                         ( void );
void        ESP8266_AT_Test                     ( void );
bool        ESP8266_Cmd                         ( char * cmd, char * reply1, char * reply2, u32 waittime );
bool        ESP8266_Net_Mode_Choose             ( ENUM_Net_ModeTypeDef enumMode );
bool        ESP8266_JoinAP                      ( char * pSSID, char * pPassWord );
bool        ESP8266_BuildAP                     ( char * pSSID, char * pPassWord, char * enunPsdMode );
bool        ESP8266_Enable_MultipleId           ( FunctionalState enumEnUnvarnishTx );
bool        ESP8266_Link_Server                 ( ENUM_NetPro_TypeDef enumE, char * ip, char * ComNum, ENUM_ID_NO_TypeDef id);
bool        ESP8266_Link_MQTT                   ( char * ip, char * ComNum, ENUM_ID_NO_TypeDef id);
bool        ESP8266_Set_MQTT_User               ( void );
bool        ESP8266_Set_MQTT_Public             (char * topicId ,char * val);
bool        ESP8266_MQTT_Public_key_Value       (char * topicId ,char * key,char * val );
bool        ESP8266_Set_MQTT_Public_JSON        (char * topicId ,char * val);
bool        ESP8266_Set_MQTT_Sub                (const char * topicId, const char * qos);



bool        ESP8266_StartOrShutServer           ( FunctionalState enumMode, char * pPortNum, char * pTimeOver );
bool        ESP8266_SendString                  ( FunctionalState enumEnUnvarnishTx, char * pStr, u32 ulStrLength, ENUM_ID_NO_TypeDef ucId );
char *      ESP8266_ReceiveString               ( FunctionalState enumEnUnvarnishTx );

void        ESP8266_STA_TCP_Client              ( void );
void        ESP8266_STA_TCP_Client_Single       ( void );
void        ESP8266_STA_TCP_Client_MQTT         ( void );

void        ESP8266_AP_TCP_Server               ( void );
void        ESP8266_StaTcpClient_ApTcpServer    ( void );



#endif    /* __WIFI_FUNCTION_H */





wifi_function.c

#include "wifi_function.h"
#include "wifi_config.h"
#include "SysTick.h"
#include <string.h> 
#include <stdio.h>  
#include <stdbool.h>


#include <stdint.h>



// 定义单连接ID,假设为0,具体根据实际情况
#define Single_ID 0 


char esp8266_response[256];     //
volatile uint8_t response_received = 0;


// 清空ESP8266数据缓冲区
void ClearEsp8266Buffer(void) {
    // 1. 清空数据缓冲区(填入0)
    memset(strEsp8266_Fram_Record.Data_RX_BUF, 0, RX_BUF_MAX_LEN);
    
    // 2. 重置长度和标志位(两种方式均可)
    strEsp8266_Fram_Record.InfAll = 0;  // 整体赋值(推荐)
    // 或单独设置:
    // strEsp8266_Fram_Record.InfBit.FramLength = 0;
    // strEsp8266_Fram_Record.InfBit.FramFinishFlag = 0;
}

/*
 * 函数名:ESP8266_Choose
 * 描述  :使能/禁用WF-ESP8266模块
 * 输入  :enumChoose = ENABLE,使能模块
 *         enumChoose = DISABLE,禁用模块
 * 返回  : 无
 * 调用  :被外部调用
 */
void ESP8266_Choose ( FunctionalState enumChoose )
{
	if ( enumChoose == ENABLE )
		ESP8266_CH_HIGH_LEVEL();
	
	else
		ESP8266_CH_LOW_LEVEL();
	
}


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

}


/*
 * 函数名:ESP8266_AT_Test
 * 描述  :对WF-ESP8266模块进行AT测试启动
 * 输入  :无
 * 返回  : 无
 * 调用  :被外部调用
 */
void ESP8266_AT_Test ( void )
{
	ESP8266_RST_HIGH_LEVEL();
	
	delay_ms ( 1000 ); 
	
	while ( ! ESP8266_Cmd ( "AT", "OK", NULL, 200 ) ) ESP8266_Rst ();  	

}


/*
 * 函数名: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;               //从新开始接收新的数据包
	ClearEsp8266Buffer(); // 清空ESP8266数据缓冲区

	ESP8266_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';

	PC_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 ) );
	
}



bool ESP8266_Cmd_Json ( char * cmd, char * reply1, char * reply2, u32 waittime )
{    
	//strEsp8266_Fram_Record .InfBit .FramLength = 0;               //从新开始接收新的数据包
	ClearEsp8266Buffer(); // 清空ESP8266数据缓冲区

	ESP8266_Usar_Json ( "%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';

	PC_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_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, 7000 );
	
}


/*
 * 函数名:ESP8266_BuildAP
 * 描述  :WF-ESP8266模块创建WiFi热点
 * 输入  :pSSID,WiFi名称字符串
 *       :pPassWord,WiFi密码字符串
 *       :enunPsdMode,WiFi加密方式代号字符串
 * 返回  : 1,创建成功
 *         0,创建失败
 * 调用  :被外部调用
 */
bool ESP8266_BuildAP ( char * pSSID, char * pPassWord, char * enunPsdMode )
{
	char cCmd [120];

	sprintf ( cCmd, "AT+CWSAP=\"%s\",\"%s\",1,%s", pSSID, pPassWord, enunPsdMode );
	
	return ESP8266_Cmd ( cCmd, "OK", 0, 1000 );
	
}


/*
 * 函数名:ESP8266_Enable_MUSART3_printfultipleId
 * 描述  :WF-ESP8266模块启动多连接
 * 输入  :enumEnUnvarnishTx,配置是否多连接
 * 返回  : 1,配置成功
 *         0,配置失败
 * 调用  :被外部调用
 */
bool ESP8266_Enable_MultipleId ( FunctionalState enumEnUnvarnishTx )
{
	char cStr [20];
	
	sprintf ( cStr, "AT+CIPMUX=%d", ( enumEnUnvarnishTx ? 1 : 0 ) );
	
	return ESP8266_Cmd ( cStr, "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", 500 );
	
}




/*
 * ESP8266_Link_MQTT
 * 描述  :WF-ESP8266模块连接外部服务器
 * 输入  :enumE,网络协议
 *       :ip,服务器IP字符串
 *       :ComNum,服务器端口字符串
 *       :id,模块连接服务器的ID
 * 返回  : 1,连接成功
 *         0,连接失败
 * 调用  :被外部调用
 */
bool ESP8266_Link_MQTT (  char * ip, char * ComNum, ENUM_ID_NO_TypeDef id)
{
	char cStr [100] = { 0 }, cCmd [120];

	sprintf ( cStr, "\"%s\",%s",  ip, ComNum );
    sprintf ( cCmd, "AT+MQTTCONN=0,%s,0", cStr );
	return ESP8266_Cmd ( cCmd, "OK", "ALREAY CONNECT", 500 );
	
}




/*
 * ESP8266_Set_MQTT_User
 * 描述  :set MQTT user

 * 返回  : 1,连接成功
 *         0,连接失败
 * 调用  :被外部调用
 */
bool ESP8266_Set_MQTT_User ()
{
	char cStr [100] = { 0 }, cCmd [120];
    // init  cCmd's value :AT+MQTTUSERCFG=0,1,"testUser4002","user4","1234",0,0,""
	sprintf ( cCmd, "AT+MQTTUSERCFG=0,1,\"testUser4002\",\"user4\",\"1234\",0,0,\"\"" );
	return ESP8266_Cmd ( cCmd, "OK", "ALREAY CONNECT", 500 );
}


/*
 * ESP8266_Set_MQTT_public
 * 描述  :set MQTT public

 * 返回  : 1,连接成功
 *         0,连接失败
 * 调用  :被外部调用
 */
bool ESP8266_Set_MQTT_Public (char * topicId ,char * val)
{
	char cStr [100] = { 0 }, cCmd [120];
     //construct string: AT+MQTTPUB=0,"topic911","66693",0,0
	//sprintf ( cCmd, "AT+MQTTPUB=0,\"%s\",\"%s\",0,0", topicId, val );
	sprintf ( cCmd, "AT+MQTTPUB=0,\"topic911\",\"{\\\"aa\\\":\\\"01\\\"}\",1,0");

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

/*
 * ESP8266_MQTT_Public_key_Value
 * 描述  :set MQTT public key and value
 * 输入  :topicId,主题ID字符串
 * 	  :key,键字符串
 * 	  :val,值字符串
 * 返回  : 1,连接成功
 *         0,连接失败
 * 调用  :被外部调用
 */


bool ESP8266_MQTT_Public_key_Value (char * topicId ,char * key,char * val )
{
	char cStr [100] = { 0 }, cCmd [120];
     //construct string: AT+MQTTPUB=0,"topic911","66693",0,0
	//sprintf ( cCmd, "AT+MQTTPUB=0,\"%s\",\"%s\",0,0", topicId, val );
	sprintf ( cCmd, "AT+MQTTPUB=0,\"%s\",\"{\\\"%s\\\":\\\"%s\\\"}\",1,0",topicId, key, val );
	return ESP8266_Cmd ( cCmd, "OK", "ALREAY CONNECT", 500 );
}


bool ESP8266_Set_MQTT_Public_JSON (char * topicId ,char * val)
{
	char cStr [100] = { 0 }, cCmd [120];
     //construct string: AT+MQTTPUB=0,"topic911","66693",0,0
	sprintf ( cCmd, "AT+MQTTPUB=0,\"topic911\",\"{\"aa\":\"01\"}\",1,0 " );
	// set cCmd's value is :
	return ESP8266_Cmd ( cCmd, "OK", "ALREAY CONNECT", 500 );
}


//su  esp8266 sub mqtt topic function ,parameter is topicId & qos  ,include  parameter declaration 
bool ESP8266_Set_MQTT_Sub (const  char * topicId,const  char * qos)
{
	char cStr [100] = { 0 }, cCmd [120];
	//construct string: AT+MQTTSUB=0,"topic911",1,0
	sprintf ( cCmd, "AT+MQTTSUB=0,\"%s\",%s", topicId, qos );
	PC_Usart("send sub cmd: %s\r\n", cCmd); // 添加日志输出
	return ESP8266_Cmd (cCmd, "OK", "ALREAY CONNECT", 500);
}







/*
 * 函数名: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_UnvarnishSend
 * 描述  :配置WF-ESP8266模块进入透传发送
 * 输入  :无
 * 返回  : 1,配置成功
 *         0,配置失败
 * 调用  :被外部调用
 */
bool ESP8266_UnvarnishSend ( void )
{
	return (
	  ESP8266_Cmd ( "AT+CIPMODE=1", "OK", 0, 500 ) &&
	  ESP8266_Cmd ( "AT+CIPSEND", "\r\n", ">", 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 [20];
	bool bRet = false;
		
	if ( enumEnUnvarnishTx )
		ESP8266_Usart ( "%s", pStr );

	
	else
	{
		// if ( ucId < 5 )
		// 	sprintf ( cStr, "AT+CIPSEND=%d,%d", ucId, ulStrLength + 2 );

		// else
		// 	sprintf ( cStr, "AT+CIPSEND=%d", ulStrLength + 2 );
		
		sprintf ( cStr, "AT+CIPSEND=%d", ulStrLength + 2 );
		
		ESP8266_Cmd ( cStr, "> ", 0, 1000 );

		bRet = ESP8266_Cmd ( pStr, "SEND OK", 0, 1000 );
  }
	
	return bRet;

}


/*
 * 函数名: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 )
	{
		if ( strstr ( strEsp8266_Fram_Record .Data_RX_BUF, ">" ) )
			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;
	
}


/*
 * 函数名:ESP8266_STA_TCP_Client
 * 描述  :PZ-ESP8266模块进行STA TCP Clien测试
 * 输入  :无
 * 返回  : 无
 * 调用  :被外部调用
 */
void ESP8266_STA_TCP_Client ( void )
{
	char cStrInput [100] = { 0 }, * pStrDelimiter [2], * pBuf, * pStr;
	u8 uc = 0;
  u32 ul = 0;

	ESP8266_Choose ( ENABLE );	

  ESP8266_AT_Test ();
	
	ESP8266_Net_Mode_Choose ( STA );
  
	ESP8266_Cmd ( "AT+CWLAP", "OK", 0, 5000 );
		
  do
	{
		//PC_Usart ( "\r\n请输入要连接的WiFi名称和密钥,输入格式为:名称字符+英文逗号+密钥字符+空格,点击发送\r\n" );
         // write english 		PC_Usart ( "\r\n请输入要连接的WiFi名称和密钥,输入格式为:名称字符+英文逗号+密钥字符+空格,点击发送\r\n" );
		PC_Usart ( "\r\nPlease input the WiFi name and password to connect, format: SSID+comma+password+space, then click send\r\n" );
		scanf ( "%s", cStrInput );

		// write english PC_Usart ( "\r\n稍等片刻 ……\r\n" );
		PC_Usart ( "\r\nPlease wait a moment...\r\n" );

		pBuf = cStrInput;
		uc = 0;
		while ( ( pStr = strtok ( pBuf, "," ) ) != NULL )
		{
			pStrDelimiter [ uc ++ ] = pStr;
			pBuf = NULL;
		} 
		
  } while ( ! ESP8266_JoinAP ( pStrDelimiter [0], pStrDelimiter [1] ) );
	
	ESP8266_Enable_MultipleId ( ENABLE );
	
	do 
	{
	// write english	PC_Usart ( "\r\n请在电脑上将网络调试助手以TCP Server连接网络,并输入电脑的IP和端口号,输入格式为:电脑IP+英文逗号+端口号+空格,点击发送\r\n" );
		PC_Usart ( "\r\nPlease connect the network debugging assistant to the TCP Server on your computer, and input the computer's IP and port number, format: IP+comma+port+space, then click send\r\n" );

		scanf ( "%s", cStrInput );

	// write english		PC_Usart ( "\r\n稍等片刻 ……\r\n" );
		

		pBuf = cStrInput;
		uc = 0;
		while ( ( pStr = strtok ( pBuf, "," ) ) != NULL )
		{
			pStrDelimiter [ uc ++ ] = pStr;
			pBuf = NULL;
		} 
		
  } while ( ! ( ESP8266_Link_Server ( enumTCP, pStrDelimiter [0], pStrDelimiter [1], Multiple_ID_0 ) &&
	              ESP8266_Link_Server ( enumTCP, pStrDelimiter [0], pStrDelimiter [1], Multiple_ID_1 ) &&
	              ESP8266_Link_Server ( enumTCP, pStrDelimiter [0], pStrDelimiter [1], Multiple_ID_2 ) &&
	              ESP8266_Link_Server ( enumTCP, pStrDelimiter [0], pStrDelimiter [1], Multiple_ID_3 ) &&
	              ESP8266_Link_Server ( enumTCP, pStrDelimiter [0], pStrDelimiter [1], Multiple_ID_4 ) ) );

  for ( uc = 0; uc < 5; uc ++ )
	{
		// write english	 PC_Usart ( "\r\n请输入端口ID%d要发送的字符串,输入格式为:字符串(不含空格)+空格,点击发送\r\n", uc );
		PC_Usart ( "\r\nPlease input the string to send for port ID%d, format: string (no spaces) + space, then click send\r\n", uc );

		scanf ( "%s", cStrInput );

		ul = strlen ( cStrInput );
		
		ESP8266_SendString ( DISABLE, cStrInput, ul, ( ENUM_ID_NO_TypeDef ) uc );
		
	}
	
	
	// write english PC_Usart ( "\r\n请在网络调试助手发送字符串\r\n" );
	PC_Usart ( "\r\nPlease send strings in the network debugging assistant\r\n" );
	while (1)
	{
	  pStr = ESP8266_ReceiveString ( DISABLE );
		PC_Usart ( "%s", pStr );
	}

}





/*
 * 函数名:ESP8266_STA_TCP_Client_Single
 * 描述  :ESP8266模块进行STA TCP Client测试(单连接模式)
 * 输入  :无
 * 返回  : 无
 * 调用  :被外部调用
 */

// 假设需要的头文件已经包含,这里示例包含常用的一些


void ESP8266_STA_TCP_Client_Single ( void )
{
    // 固定配置
    const char* ssid = "x";                
    const char* password = "12345678";        
    const char* server_ip = "192.168.254.195"; 
    const char* server_port = "8080";        
    
    // **函数体开头:声明所有变量**
    u8 wifi_retry = 0;
    bool wifi_connected = false;
    u8 server_retry = 0;
    bool server_connected = false;
    const char* test_data;
    u32 data_len;

    // 启用模块
    PC_Usart("\r\nInitializing ESP8266...\r\n");
    ESP8266_Choose(ENABLE);
    ESP8266_AT_Test();
    
    // 设置STA模式
    PC_Usart("\r\nConnecting to WiFi: %s...\r\n", ssid);
    ESP8266_Net_Mode_Choose(STA);
    
    // WiFi连接循环(变量已在开头声明,无需重复)
    while (wifi_retry < 3 && !wifi_connected) {
        wifi_connected = ESP8266_JoinAP((char*)ssid, (char*)password);
        if (!wifi_connected) {
            PC_Usart("\r\nWiFi connection failed, retrying %d/3...\r\n", wifi_retry+1);
            wifi_retry++;
            delay_ms(1000);
        }
    }
    
    // 单连接模式
    //ESP8266_Enable_MultipleId(DISABLE); // 关闭多连接
    
    // 连接服务器
    PC_Usart("\r\nConnecting to server: %s:%s...\r\n", server_ip, server_port);
    while (server_retry < 3 && !server_connected) {
        // 强制转换枚举类型为uint8_t(若函数参数为uint8_t)
        server_connected = ESP8266_Link_Server((uint8_t)enumTCP, (char*)server_ip, (char*)server_port, 9); // 假设Single_ID=0
        if (!server_connected) {
            PC_Usart("\r\nServer connection failed, retrying %d/3...\r\n", server_retry+1);
            server_retry++;
            delay_ms(1000);
        }
    }
    
    // 发送数据(变量已在开头声明)
    test_data = "Hello from ESP8266!";
    data_len = strlen(test_data);
    PC_Usart("\r\nSending test data: %s\r\n", test_data);
    if (ESP8266_SendString(DISABLE, (char*)test_data, data_len, 0)) { // 假设Single_ID=0
        PC_Usart("\r\nData sent successfully!\r\n");
    } else {
        PC_Usart("\r\nFailed to send data!\r\n");
    }
    
    // 接收循环
    while (1) { /* ... */ }
}





/*
 * 函数名:ESP8266_STA_TCP_Client_MQTT
 * 描述  :ESP8266模块进行STA TCP Client测试(单连接模式)
 * 输入  :无
 * 返回  : 无
 * 调用  :被外部调用
 */

// 假设需要的头文件已经包含,这里示例包含常用的一些


void ESP8266_STA_TCP_Client_MQTT ( void )
{
    // 固定配置
    const char* ssid = "x";                
    const char* password = "12345678";        
    const char* server_ip = "192.168.254.195"; 
    const char* server_port = "1883";        
    
    // **函数体开头:声明所有变量**
    u8 wifi_retry = 0;
    bool wifi_connected = false;
    u8 server_retry = 0;
    bool server_connected = false;
    u8 setUser_retry = 0;   // 添加设置用户的重试次数
    bool setUser_mqtt = false;  // 添加设置用户的连接状态
	u8 setPublic_retry = 0;   // 添加设置发布的重试次数
	bool setPublic_mqtt = false;  // 添加设置发布的连接状态
	// sub mqtt topic retry
	u8 sub_retry = 0;   // 添加订阅的重试次数
	bool sub_mqtt = false;  // 添加订阅的连接状态
	//sub topic id
	const char* topic_id = "topic911"; // 假设订阅的主题ID为topic911 
	// sub qos
	char qos[] = "0"; // 创建可修改的字符数组
	// 发送数据(变量已在开头声明)



    const char* test_data;
    u32 data_len;

    // 启用模块
    PC_Usart("\r\nInitializing ESP8266...\r\n");
    ESP8266_Choose(ENABLE);
    ESP8266_AT_Test();
    
    // 设置STA模式
    PC_Usart("\r\nConnecting to WiFi: %s...\r\n", ssid);
    ESP8266_Net_Mode_Choose(STA);
    
    // WiFi连接循环(变量已在开头声明,无需重复)
    while (wifi_retry < 3 && !wifi_connected) {
        wifi_connected = ESP8266_JoinAP((char*)ssid, (char*)password);
        if (!wifi_connected) {
            PC_Usart("\r\nWiFi connection failed, retrying %d/3...\r\n", wifi_retry+1);
            wifi_retry++;
            delay_ms(1000);
        }
    }


// 后面的内容 不应该放到初始化当中......

    
    // 单连接模式
    //ESP8266_Enable_MultipleId(DISABLE); // 关闭多连接

    //setUser_mqtt  in while loop  3 times
	PC_Usart("\r\nSetting MQTT user...\r\n");
	while (setUser_retry < 3 && !setUser_mqtt) {
		setUser_mqtt = ESP8266_Set_MQTT_User();
		if (!setUser_mqtt) {
			PC_Usart("\r\nSetting MQTT user failed, retrying %d/3...\r\n", setUser_retry+1);
			setUser_retry++;
			delay_ms(1000);
		}
	}

	// link mqtt server
	PC_Usart("\r\nConnecting to MQTT server...\r\n");
	while (server_retry < 3 && !server_connected) {
		// 强制转换枚举类型为uint8_t(若函数参数为uint8_t)
		server_connected = ESP8266_Link_MQTT((char*)server_ip, (char*)server_port, 9); // 假设Single_ID=0
		if (!server_connected) {
			PC_Usart("\r\nMQTT server connection failed, retrying %d/3...\r\n", server_retry+1);
			server_retry++;
			delay_ms(1000);
		}
	}


// setPublic_mqtt  in while loop  1 time
	// PC_Usart("\r\n MQTT public topic...\r\n");
	// while (setPublic_retry < 3 && !setPublic_mqtt) {
	// 	char topicId[] = "topic911";
	// 	char val[] = "{status: 1}"; // 复杂JSON示例
	// 	setPublic_mqtt = ESP8266_Set_MQTT_Public(topicId, val);
	// 	if (!setPublic_mqtt) {
	// 		PC_Usart("\r\nSetting MQTT public topic failed, retrying %d/3...\r\n", setPublic_retry+1);
	// 		setPublic_retry++;
	// 		delay_ms(1000);
	// 	}
	// }


	// sub mqtt topic
	// PC_Usart("\r\nSubscribing to MQTT topic...\r\n");
	// while (sub_retry < 3 && !sub_mqtt) {
	// 	sub_mqtt = ESP8266_Set_MQTT_Sub(topic_id,qos); // 假设qos为1
	// 	if (!sub_mqtt) {
	// 		PC_Usart("\r\nSubscribing to MQTT topic failed, retrying %d/3...\r\n", sub_retry+1);
	// 		sub_retry++;
	// 		delay_ms(1000);



	// 	}
	// }



    
    // 发送数据(变量已在开头声明)
    // test_data = "Hello from ESP8266!";
    // data_len = strlen(test_data);
    // PC_Usart("\r\nSending test data: %s\r\n", test_data);
    // if (ESP8266_SendString(DISABLE, (char*)test_data, data_len, 0)) { // 假设Single_ID=0
    //     PC_Usart("\r\nData sent successfully!\r\n");
    // } else {
    //     PC_Usart("\r\nFailed to send data!\r\n");
    // }
    
    // 接收循环
	// while (1) {

	// }
}


// public mqtt topic message  function,parameter is topicId & val
bool ESP8266_Public_MQTT_Message (const char * topicId, const char * val)
{
	char cStr [100] = { 0 }, cCmd [120];
	// construct string: AT+MQTTPUB=0,"topic911","66693",0,0
	sprintf ( cCmd, "AT+MQTTPUB=0,\"%s\",\"%s\",0,0", topicId, val );
	return ESP8266_Cmd ( cCmd, "OK", "ALREAY CONNECT", 500 );
}	






/*
 * 函数名:ESP8266_AP_TCP_Server
 * 描述  :PZ-ESP8266模块进行AP TCP Server测试
 * 输入  :无
 * 返回  : 无
 * 调用  :被外部调用
 */
void ESP8266_AP_TCP_Server ( void )
{
	char cStrInput [100] = { 0 }, * pStrDelimiter [3], * pBuf, * pStr;
	u8 uc = 0;
  u32 ul = 0;

  ESP8266_Choose ( ENABLE );

	ESP8266_AT_Test ();
	
	ESP8266_Net_Mode_Choose ( AP );


	PC_Usart ( "\r\n请输入要创建的WiFi的名称、加密方式和密钥,加密方式的编号为:\
              \r\n0 = OPEN\
              \r\n1 = WEP\
              \r\n2 = WPA_PSK\
	            \r\n3 = WPA2_PSK\
              \r\n4 = WPA_WPA2_PSK\
							\r\n输入格式为:名称字符+英文逗号+加密方式编号+英文逗号+密钥字符+空格,点击发送\r\n" );

	scanf ( "%s", cStrInput );

	PC_Usart ( "\r\n稍等片刻 ……\r\n" );

	pBuf = cStrInput;
	uc = 0;
	while ( ( pStr = strtok ( pBuf, "," ) ) != NULL )
	{
		pStrDelimiter [ uc ++ ] = pStr;
		pBuf = NULL;
	} 
	
	ESP8266_BuildAP ( pStrDelimiter [0], pStrDelimiter [2], pStrDelimiter [1] );
	ESP8266_Cmd ( "AT+RST", "OK", "ready", 2500 ); //*
		

	ESP8266_Enable_MultipleId ( ENABLE );
		
	
	PC_Usart ( "\r\n请输入服务器要开启的端口号和超时时间(0~28800,单位:秒),输入格式为:端口号字符+英文逗号+超时时间字符+空格,点击发送\r\n" );

	scanf ( "%s", cStrInput );

	PC_Usart ( "\r\n稍等片刻 ……\r\n" );

	pBuf = cStrInput;
	uc = 0;
	while ( ( pStr = strtok ( pBuf, "," ) ) != NULL )
	{
		pStrDelimiter [ uc ++ ] = pStr;
		pBuf = NULL;
	} 

	ESP8266_StartOrShutServer ( ENABLE, pStrDelimiter [0], pStrDelimiter [1] );
	
	
	do
	{
		PC_Usart ( "\r\n正查询本模块IP……\r\n" );
	  ESP8266_Cmd ( "AT+CIFSR", "OK", "Link", 500 );
		
		PC_Usart ( "\r\n请用手机连接刚才创建的WiFi,这里只连接一个手机,作为ID0,然后用手机网络调试助手以TCP Client连接刚才开启的服务器(AP IP)……\r\n" );
		delay_ms ( 20000 ) ;
	}	while ( ! ESP8266_Cmd ( "AT+CIPSTATUS", "+CIPSTATUS:0", 0, 500 ) );
	

	PC_Usart ( "\r\n请输入要向端口手机(ID0)发送的字符串,输入格式为:字符串(不含空格)+空格,点击发送\r\n" );

	scanf ( "%s", cStrInput );

	ul = strlen ( cStrInput );
	
	ESP8266_SendString ( DISABLE, cStrInput, ul, Multiple_ID_0 );

	
	PC_Usart ( "\r\n请在手机网络调试助手发送字符串\r\n" );
	while (1)
	{
	  pStr = ESP8266_ReceiveString ( DISABLE );
		PC_Usart ( "%s", pStr );
	}

}


/*
 * 函数名:ESP8266_StaTcpClient_ApTcpServer
 * 描述  :PZ-ESP8266模块进行STA(TCP Client)+AP(TCP Server)测试
 * 输入  :无
 * 返回  : 无
 * 调用  :被外部调用
 */
void ESP8266_StaTcpClient_ApTcpServer ( void )
{
	char cStrInput [100] = { 0 }, * pStrDelimiter [3], * pBuf, * pStr;
	u8 uc = 0;
  u32 ul = 0;

  ESP8266_Choose ( ENABLE );

	ESP8266_AT_Test ();
	
	ESP8266_Net_Mode_Choose ( STA_AP );


	PC_Usart ( "\r\n请输入要创建的WiFi的名称、加密方式和密钥,加密方式的编号为:\
						\r\n0 = OPEN\
						\r\n1  =WEP\
						\r\n2 = WPA_PSK\
						\r\n3 = WPA2_PSK\
						\r\n4 = WPA_WPA2_PSK\
						\r\n输入格式为:名称字符+英文逗号+加密方式编号+英文逗号+密钥字符+空格,点击发送\r\n" );

	scanf ( "%s", cStrInput );

	PC_Usart ( "\r\n稍等片刻 ……\r\n" );

	pBuf = cStrInput;
	uc = 0;
	while ( ( pStr = strtok ( pBuf, "," ) ) != NULL )
	{
		pStrDelimiter [ uc ++ ] = pStr;
		pBuf = NULL;
	} 
	
	ESP8266_BuildAP ( pStrDelimiter [0], pStrDelimiter [2], pStrDelimiter [1] );
	ESP8266_Cmd ( "AT+RST", "OK", "ready", 2500 ); //*
	

	ESP8266_Cmd ( "AT+CWLAP", "OK", 0, 5000 );
		
  do
	{
		PC_Usart ( "\r\n请输入要连接的WiFi名称和密钥,输入格式为:名称字符+英文逗号+密钥字符+空格,点击发送\r\n" );

		scanf ( "%s", cStrInput );

		PC_Usart ( "\r\n稍等片刻 ……\r\n" );

		pBuf = cStrInput;
		uc = 0;
		while ( ( pStr = strtok ( pBuf, "," ) ) != NULL )
		{
			pStrDelimiter [ uc ++ ] = pStr;
			pBuf = NULL;
		} 
		
  } while ( ! ESP8266_JoinAP ( pStrDelimiter [0], pStrDelimiter [1] ) );

	
	ESP8266_Enable_MultipleId ( ENABLE );
		
	
	PC_Usart ( "\r\n请输入服务器要开启的端口号和超时时间(0~28800,单位:秒),输入格式为:端口号字符+英文逗号+超时时间字符+空格,点击发送\r\n" );

	scanf ( "%s", cStrInput );

	PC_Usart ( "\r\n稍等片刻 ……\r\n" );

	pBuf = cStrInput;
	uc = 0;
	while ( ( pStr = strtok ( pBuf, "," ) ) != NULL )
	{
		pStrDelimiter [ uc ++ ] = pStr;
		pBuf = NULL;
	} 

	ESP8266_StartOrShutServer ( ENABLE, pStrDelimiter [0], pStrDelimiter [1] );
	
	
	do 
	{
		PC_Usart ( "\r\n请在电脑上将网络调试助手以TCP Server连接网络,并输入电脑的IP和端口号,输入格式为:电脑IP+英文逗号+端口号+空格,点击发送\r\n" );

		scanf ( "%s", cStrInput );

		PC_Usart ( "\r\n稍等片刻 ……\r\n" );

		pBuf = cStrInput;
		uc = 0;
		while ( ( pStr = strtok ( pBuf, "," ) ) != NULL )
		{
			pStrDelimiter [ uc ++ ] = pStr;
			pBuf = NULL;
		} 
		
  } while ( ! ( ESP8266_Link_Server ( enumTCP, pStrDelimiter [0], pStrDelimiter [1], Multiple_ID_0 ) &&
	              ESP8266_Link_Server ( enumTCP, pStrDelimiter [0], pStrDelimiter [1], Multiple_ID_1 ) &&
	              ESP8266_Link_Server ( enumTCP, pStrDelimiter [0], pStrDelimiter [1], Multiple_ID_2 ) ) );
		
	
	do
	{
		PC_Usart ( "\r\n正查询本模块IP,前一个为AP IP,后一个为STA IP……\r\n" );
	  ESP8266_Cmd ( "AT+CIFSR", "OK", "Link", 500 );
		
		PC_Usart ( "\r\n请用手机连接刚才创建的WiFi,这里只连接一个手机,作为ID3,然后用手机网络调试助手以TCP Client连接刚才开启的服务器(AP IP)……\r\n" );
		delay_ms ( 20000 ) ;
	}	while ( ! ESP8266_Cmd ( "AT+CIPSTATUS", "+CIPSTATUS:3", 0, 500 ) );
	

	for ( uc = 0; uc < 3; uc ++ )
	{
		PC_Usart ( "\r\n请输入端口ID%d要发送的字符串,输入格式为:字符串(不含空格)+空格,点击发送\r\n", uc );

		scanf ( "%s", cStrInput );

		ul = strlen ( cStrInput );
		
		ESP8266_SendString ( DISABLE, cStrInput, ul, ( ENUM_ID_NO_TypeDef ) uc );
		
	}
	
	
	PC_Usart ( "\r\n请输入要向端口手机(ID3)发送的字符串,输入格式为:字符串(不含空格)+空格,点击发送\r\n" );

	scanf ( "%s", cStrInput );

	ul = strlen ( cStrInput );
	
	ESP8266_SendString ( DISABLE, cStrInput, ul, Multiple_ID_3 );

	
	PC_Usart ( "\r\n请在电脑网络调试助手或手机网络调试助手发送字符串……\r\n" );
	while (1)
	{
	  pStr = ESP8266_ReceiveString ( DISABLE );
		PC_Usart ( "%s", pStr );
	}
	
}






wifi_function.c

#include "wifi_function.h"
#include "wifi_config.h"
#include "SysTick.h"
#include <string.h> 
#include <stdio.h>  
#include <stdbool.h>


#include <stdint.h>



// 定义单连接ID,假设为0,具体根据实际情况
#define Single_ID 0 


char esp8266_response[256];     //
volatile uint8_t response_received = 0;


// 清空ESP8266数据缓冲区
void ClearEsp8266Buffer(void) {
    // 1. 清空数据缓冲区(填入0)
    memset(strEsp8266_Fram_Record.Data_RX_BUF, 0, RX_BUF_MAX_LEN);
    
    // 2. 重置长度和标志位(两种方式均可)
    strEsp8266_Fram_Record.InfAll = 0;  // 整体赋值(推荐)
    // 或单独设置:
    // strEsp8266_Fram_Record.InfBit.FramLength = 0;
    // strEsp8266_Fram_Record.InfBit.FramFinishFlag = 0;
}

/*
 * 函数名:ESP8266_Choose
 * 描述  :使能/禁用WF-ESP8266模块
 * 输入  :enumChoose = ENABLE,使能模块
 *         enumChoose = DISABLE,禁用模块
 * 返回  : 无
 * 调用  :被外部调用
 */
void ESP8266_Choose ( FunctionalState enumChoose )
{
	if ( enumChoose == ENABLE )
		ESP8266_CH_HIGH_LEVEL();
	
	else
		ESP8266_CH_LOW_LEVEL();
	
}


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

}


/*
 * 函数名:ESP8266_AT_Test
 * 描述  :对WF-ESP8266模块进行AT测试启动
 * 输入  :无
 * 返回  : 无
 * 调用  :被外部调用
 */
void ESP8266_AT_Test ( void )
{
	ESP8266_RST_HIGH_LEVEL();
	
	delay_ms ( 1000 ); 
	
	while ( ! ESP8266_Cmd ( "AT", "OK", NULL, 200 ) ) ESP8266_Rst ();  	

}


/*
 * 函数名: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;               //从新开始接收新的数据包
	ClearEsp8266Buffer(); // 清空ESP8266数据缓冲区

	ESP8266_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';

	PC_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 ) );
	
}



bool ESP8266_Cmd_Json ( char * cmd, char * reply1, char * reply2, u32 waittime )
{    
	//strEsp8266_Fram_Record .InfBit .FramLength = 0;               //从新开始接收新的数据包
	ClearEsp8266Buffer(); // 清空ESP8266数据缓冲区

	ESP8266_Usar_Json ( "%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';

	PC_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_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, 7000 );
	
}


/*
 * 函数名:ESP8266_BuildAP
 * 描述  :WF-ESP8266模块创建WiFi热点
 * 输入  :pSSID,WiFi名称字符串
 *       :pPassWord,WiFi密码字符串
 *       :enunPsdMode,WiFi加密方式代号字符串
 * 返回  : 1,创建成功
 *         0,创建失败
 * 调用  :被外部调用
 */
bool ESP8266_BuildAP ( char * pSSID, char * pPassWord, char * enunPsdMode )
{
	char cCmd [120];

	sprintf ( cCmd, "AT+CWSAP=\"%s\",\"%s\",1,%s", pSSID, pPassWord, enunPsdMode );
	
	return ESP8266_Cmd ( cCmd, "OK", 0, 1000 );
	
}


/*
 * 函数名:ESP8266_Enable_MUSART3_printfultipleId
 * 描述  :WF-ESP8266模块启动多连接
 * 输入  :enumEnUnvarnishTx,配置是否多连接
 * 返回  : 1,配置成功
 *         0,配置失败
 * 调用  :被外部调用
 */
bool ESP8266_Enable_MultipleId ( FunctionalState enumEnUnvarnishTx )
{
	char cStr [20];
	
	sprintf ( cStr, "AT+CIPMUX=%d", ( enumEnUnvarnishTx ? 1 : 0 ) );
	
	return ESP8266_Cmd ( cStr, "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", 500 );
	
}




/*
 * ESP8266_Link_MQTT
 * 描述  :WF-ESP8266模块连接外部服务器
 * 输入  :enumE,网络协议
 *       :ip,服务器IP字符串
 *       :ComNum,服务器端口字符串
 *       :id,模块连接服务器的ID
 * 返回  : 1,连接成功
 *         0,连接失败
 * 调用  :被外部调用
 */
bool ESP8266_Link_MQTT (  char * ip, char * ComNum, ENUM_ID_NO_TypeDef id)
{
	char cStr [100] = { 0 }, cCmd [120];

	sprintf ( cStr, "\"%s\",%s",  ip, ComNum );
    sprintf ( cCmd, "AT+MQTTCONN=0,%s,0", cStr );
	return ESP8266_Cmd ( cCmd, "OK", "ALREAY CONNECT", 500 );
	
}




/*
 * ESP8266_Set_MQTT_User
 * 描述  :set MQTT user

 * 返回  : 1,连接成功
 *         0,连接失败
 * 调用  :被外部调用
 */
bool ESP8266_Set_MQTT_User ()
{
	char cStr [100] = { 0 }, cCmd [120];
    // init  cCmd's value :AT+MQTTUSERCFG=0,1,"testUser4002","user4","1234",0,0,""
	sprintf ( cCmd, "AT+MQTTUSERCFG=0,1,\"testUser4002\",\"user4\",\"1234\",0,0,\"\"" );
	return ESP8266_Cmd ( cCmd, "OK", "ALREAY CONNECT", 500 );
}


/*
 * ESP8266_Set_MQTT_public
 * 描述  :set MQTT public

 * 返回  : 1,连接成功
 *         0,连接失败
 * 调用  :被外部调用
 */
bool ESP8266_Set_MQTT_Public (char * topicId ,char * val)
{
	char cStr [100] = { 0 }, cCmd [120];
     //construct string: AT+MQTTPUB=0,"topic911","66693",0,0
	//sprintf ( cCmd, "AT+MQTTPUB=0,\"%s\",\"%s\",0,0", topicId, val );
	sprintf ( cCmd, "AT+MQTTPUB=0,\"topic911\",\"{\\\"aa\\\":\\\"01\\\"}\",1,0");

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

/*
 * ESP8266_MQTT_Public_key_Value
 * 描述  :set MQTT public key and value
 * 输入  :topicId,主题ID字符串
 * 	  :key,键字符串
 * 	  :val,值字符串
 * 返回  : 1,连接成功
 *         0,连接失败
 * 调用  :被外部调用
 */


bool ESP8266_MQTT_Public_key_Value (char * topicId ,char * key,char * val )
{
	char cStr [100] = { 0 }, cCmd [120];
     //construct string: AT+MQTTPUB=0,"topic911","66693",0,0
	//sprintf ( cCmd, "AT+MQTTPUB=0,\"%s\",\"%s\",0,0", topicId, val );
	sprintf ( cCmd, "AT+MQTTPUB=0,\"%s\",\"{\\\"%s\\\":\\\"%s\\\"}\",1,0",topicId, key, val );
	return ESP8266_Cmd ( cCmd, "OK", "ALREAY CONNECT", 500 );
}


bool ESP8266_Set_MQTT_Public_JSON (char * topicId ,char * val)
{
	char cStr [100] = { 0 }, cCmd [120];
     //construct string: AT+MQTTPUB=0,"topic911","66693",0,0
	sprintf ( cCmd, "AT+MQTTPUB=0,\"topic911\",\"{\"aa\":\"01\"}\",1,0 " );
	// set cCmd's value is :
	return ESP8266_Cmd ( cCmd, "OK", "ALREAY CONNECT", 500 );
}


//su  esp8266 sub mqtt topic function ,parameter is topicId & qos  ,include  parameter declaration 
bool ESP8266_Set_MQTT_Sub (const  char * topicId,const  char * qos)
{
	char cStr [100] = { 0 }, cCmd [120];
	//construct string: AT+MQTTSUB=0,"topic911",1,0
	sprintf ( cCmd, "AT+MQTTSUB=0,\"%s\",%s", topicId, qos );
	PC_Usart("send sub cmd: %s\r\n", cCmd); // 添加日志输出
	return ESP8266_Cmd (cCmd, "OK", "ALREAY CONNECT", 500);
}







/*
 * 函数名: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_UnvarnishSend
 * 描述  :配置WF-ESP8266模块进入透传发送
 * 输入  :无
 * 返回  : 1,配置成功
 *         0,配置失败
 * 调用  :被外部调用
 */
bool ESP8266_UnvarnishSend ( void )
{
	return (
	  ESP8266_Cmd ( "AT+CIPMODE=1", "OK", 0, 500 ) &&
	  ESP8266_Cmd ( "AT+CIPSEND", "\r\n", ">", 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 [20];
	bool bRet = false;
		
	if ( enumEnUnvarnishTx )
		ESP8266_Usart ( "%s", pStr );

	
	else
	{
		// if ( ucId < 5 )
		// 	sprintf ( cStr, "AT+CIPSEND=%d,%d", ucId, ulStrLength + 2 );

		// else
		// 	sprintf ( cStr, "AT+CIPSEND=%d", ulStrLength + 2 );
		
		sprintf ( cStr, "AT+CIPSEND=%d", ulStrLength + 2 );
		
		ESP8266_Cmd ( cStr, "> ", 0, 1000 );

		bRet = ESP8266_Cmd ( pStr, "SEND OK", 0, 1000 );
  }
	
	return bRet;

}


/*
 * 函数名: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 )
	{
		if ( strstr ( strEsp8266_Fram_Record .Data_RX_BUF, ">" ) )
			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;
	
}


/*
 * 函数名:ESP8266_STA_TCP_Client
 * 描述  :PZ-ESP8266模块进行STA TCP Clien测试
 * 输入  :无
 * 返回  : 无
 * 调用  :被外部调用
 */
void ESP8266_STA_TCP_Client ( void )
{
	char cStrInput [100] = { 0 }, * pStrDelimiter [2], * pBuf, * pStr;
	u8 uc = 0;
  u32 ul = 0;

	ESP8266_Choose ( ENABLE );	

  ESP8266_AT_Test ();
	
	ESP8266_Net_Mode_Choose ( STA );
  
	ESP8266_Cmd ( "AT+CWLAP", "OK", 0, 5000 );
		
  do
	{
		//PC_Usart ( "\r\n请输入要连接的WiFi名称和密钥,输入格式为:名称字符+英文逗号+密钥字符+空格,点击发送\r\n" );
         // write english 		PC_Usart ( "\r\n请输入要连接的WiFi名称和密钥,输入格式为:名称字符+英文逗号+密钥字符+空格,点击发送\r\n" );
		PC_Usart ( "\r\nPlease input the WiFi name and password to connect, format: SSID+comma+password+space, then click send\r\n" );
		scanf ( "%s", cStrInput );

		// write english PC_Usart ( "\r\n稍等片刻 ……\r\n" );
		PC_Usart ( "\r\nPlease wait a moment...\r\n" );

		pBuf = cStrInput;
		uc = 0;
		while ( ( pStr = strtok ( pBuf, "," ) ) != NULL )
		{
			pStrDelimiter [ uc ++ ] = pStr;
			pBuf = NULL;
		} 
		
  } while ( ! ESP8266_JoinAP ( pStrDelimiter [0], pStrDelimiter [1] ) );
	
	ESP8266_Enable_MultipleId ( ENABLE );
	
	do 
	{
	// write english	PC_Usart ( "\r\n请在电脑上将网络调试助手以TCP Server连接网络,并输入电脑的IP和端口号,输入格式为:电脑IP+英文逗号+端口号+空格,点击发送\r\n" );
		PC_Usart ( "\r\nPlease connect the network debugging assistant to the TCP Server on your computer, and input the computer's IP and port number, format: IP+comma+port+space, then click send\r\n" );

		scanf ( "%s", cStrInput );

	// write english		PC_Usart ( "\r\n稍等片刻 ……\r\n" );
		

		pBuf = cStrInput;
		uc = 0;
		while ( ( pStr = strtok ( pBuf, "," ) ) != NULL )
		{
			pStrDelimiter [ uc ++ ] = pStr;
			pBuf = NULL;
		} 
		
  } while ( ! ( ESP8266_Link_Server ( enumTCP, pStrDelimiter [0], pStrDelimiter [1], Multiple_ID_0 ) &&
	              ESP8266_Link_Server ( enumTCP, pStrDelimiter [0], pStrDelimiter [1], Multiple_ID_1 ) &&
	              ESP8266_Link_Server ( enumTCP, pStrDelimiter [0], pStrDelimiter [1], Multiple_ID_2 ) &&
	              ESP8266_Link_Server ( enumTCP, pStrDelimiter [0], pStrDelimiter [1], Multiple_ID_3 ) &&
	              ESP8266_Link_Server ( enumTCP, pStrDelimiter [0], pStrDelimiter [1], Multiple_ID_4 ) ) );

  for ( uc = 0; uc < 5; uc ++ )
	{
		// write english	 PC_Usart ( "\r\n请输入端口ID%d要发送的字符串,输入格式为:字符串(不含空格)+空格,点击发送\r\n", uc );
		PC_Usart ( "\r\nPlease input the string to send for port ID%d, format: string (no spaces) + space, then click send\r\n", uc );

		scanf ( "%s", cStrInput );

		ul = strlen ( cStrInput );
		
		ESP8266_SendString ( DISABLE, cStrInput, ul, ( ENUM_ID_NO_TypeDef ) uc );
		
	}
	
	
	// write english PC_Usart ( "\r\n请在网络调试助手发送字符串\r\n" );
	PC_Usart ( "\r\nPlease send strings in the network debugging assistant\r\n" );
	while (1)
	{
	  pStr = ESP8266_ReceiveString ( DISABLE );
		PC_Usart ( "%s", pStr );
	}

}





/*
 * 函数名:ESP8266_STA_TCP_Client_Single
 * 描述  :ESP8266模块进行STA TCP Client测试(单连接模式)
 * 输入  :无
 * 返回  : 无
 * 调用  :被外部调用
 */

// 假设需要的头文件已经包含,这里示例包含常用的一些


void ESP8266_STA_TCP_Client_Single ( void )
{
    // 固定配置
    const char* ssid = "x";                
    const char* password = "12345678";        
    const char* server_ip = "192.168.254.195"; 
    const char* server_port = "8080";        
    
    // **函数体开头:声明所有变量**
    u8 wifi_retry = 0;
    bool wifi_connected = false;
    u8 server_retry = 0;
    bool server_connected = false;
    const char* test_data;
    u32 data_len;

    // 启用模块
    PC_Usart("\r\nInitializing ESP8266...\r\n");
    ESP8266_Choose(ENABLE);
    ESP8266_AT_Test();
    
    // 设置STA模式
    PC_Usart("\r\nConnecting to WiFi: %s...\r\n", ssid);
    ESP8266_Net_Mode_Choose(STA);
    
    // WiFi连接循环(变量已在开头声明,无需重复)
    while (wifi_retry < 3 && !wifi_connected) {
        wifi_connected = ESP8266_JoinAP((char*)ssid, (char*)password);
        if (!wifi_connected) {
            PC_Usart("\r\nWiFi connection failed, retrying %d/3...\r\n", wifi_retry+1);
            wifi_retry++;
            delay_ms(1000);
        }
    }
    
    // 单连接模式
    //ESP8266_Enable_MultipleId(DISABLE); // 关闭多连接
    
    // 连接服务器
    PC_Usart("\r\nConnecting to server: %s:%s...\r\n", server_ip, server_port);
    while (server_retry < 3 && !server_connected) {
        // 强制转换枚举类型为uint8_t(若函数参数为uint8_t)
        server_connected = ESP8266_Link_Server((uint8_t)enumTCP, (char*)server_ip, (char*)server_port, 9); // 假设Single_ID=0
        if (!server_connected) {
            PC_Usart("\r\nServer connection failed, retrying %d/3...\r\n", server_retry+1);
            server_retry++;
            delay_ms(1000);
        }
    }
    
    // 发送数据(变量已在开头声明)
    test_data = "Hello from ESP8266!";
    data_len = strlen(test_data);
    PC_Usart("\r\nSending test data: %s\r\n", test_data);
    if (ESP8266_SendString(DISABLE, (char*)test_data, data_len, 0)) { // 假设Single_ID=0
        PC_Usart("\r\nData sent successfully!\r\n");
    } else {
        PC_Usart("\r\nFailed to send data!\r\n");
    }
    
    // 接收循环
    while (1) { /* ... */ }
}





/*
 * 函数名:ESP8266_STA_TCP_Client_MQTT
 * 描述  :ESP8266模块进行STA TCP Client测试(单连接模式)
 * 输入  :无
 * 返回  : 无
 * 调用  :被外部调用
 */

// 假设需要的头文件已经包含,这里示例包含常用的一些


void ESP8266_STA_TCP_Client_MQTT ( void )
{
    // 固定配置
    const char* ssid = "x";                
    const char* password = "12345678";        
    const char* server_ip = "192.168.254.195"; 
    const char* server_port = "1883";        
    
    // **函数体开头:声明所有变量**
    u8 wifi_retry = 0;
    bool wifi_connected = false;
    u8 server_retry = 0;
    bool server_connected = false;
    u8 setUser_retry = 0;   // 添加设置用户的重试次数
    bool setUser_mqtt = false;  // 添加设置用户的连接状态
	u8 setPublic_retry = 0;   // 添加设置发布的重试次数
	bool setPublic_mqtt = false;  // 添加设置发布的连接状态
	// sub mqtt topic retry
	u8 sub_retry = 0;   // 添加订阅的重试次数
	bool sub_mqtt = false;  // 添加订阅的连接状态
	//sub topic id
	const char* topic_id = "topic911"; // 假设订阅的主题ID为topic911 
	// sub qos
	char qos[] = "0"; // 创建可修改的字符数组
	// 发送数据(变量已在开头声明)



    const char* test_data;
    u32 data_len;

    // 启用模块
    PC_Usart("\r\nInitializing ESP8266...\r\n");
    ESP8266_Choose(ENABLE);
    ESP8266_AT_Test();
    
    // 设置STA模式
    PC_Usart("\r\nConnecting to WiFi: %s...\r\n", ssid);
    ESP8266_Net_Mode_Choose(STA);
    
    // WiFi连接循环(变量已在开头声明,无需重复)
    while (wifi_retry < 3 && !wifi_connected) {
        wifi_connected = ESP8266_JoinAP((char*)ssid, (char*)password);
        if (!wifi_connected) {
            PC_Usart("\r\nWiFi connection failed, retrying %d/3...\r\n", wifi_retry+1);
            wifi_retry++;
            delay_ms(1000);
        }
    }


// 后面的内容 不应该放到初始化当中......

    
    // 单连接模式
    //ESP8266_Enable_MultipleId(DISABLE); // 关闭多连接

    //setUser_mqtt  in while loop  3 times
	PC_Usart("\r\nSetting MQTT user...\r\n");
	while (setUser_retry < 3 && !setUser_mqtt) {
		setUser_mqtt = ESP8266_Set_MQTT_User();
		if (!setUser_mqtt) {
			PC_Usart("\r\nSetting MQTT user failed, retrying %d/3...\r\n", setUser_retry+1);
			setUser_retry++;
			delay_ms(1000);
		}
	}

	// link mqtt server
	PC_Usart("\r\nConnecting to MQTT server...\r\n");
	while (server_retry < 3 && !server_connected) {
		// 强制转换枚举类型为uint8_t(若函数参数为uint8_t)
		server_connected = ESP8266_Link_MQTT((char*)server_ip, (char*)server_port, 9); // 假设Single_ID=0
		if (!server_connected) {
			PC_Usart("\r\nMQTT server connection failed, retrying %d/3...\r\n", server_retry+1);
			server_retry++;
			delay_ms(1000);
		}
	}


// setPublic_mqtt  in while loop  1 time
	// PC_Usart("\r\n MQTT public topic...\r\n");
	// while (setPublic_retry < 3 && !setPublic_mqtt) {
	// 	char topicId[] = "topic911";
	// 	char val[] = "{status: 1}"; // 复杂JSON示例
	// 	setPublic_mqtt = ESP8266_Set_MQTT_Public(topicId, val);
	// 	if (!setPublic_mqtt) {
	// 		PC_Usart("\r\nSetting MQTT public topic failed, retrying %d/3...\r\n", setPublic_retry+1);
	// 		setPublic_retry++;
	// 		delay_ms(1000);
	// 	}
	// }


	// sub mqtt topic
	// PC_Usart("\r\nSubscribing to MQTT topic...\r\n");
	// while (sub_retry < 3 && !sub_mqtt) {
	// 	sub_mqtt = ESP8266_Set_MQTT_Sub(topic_id,qos); // 假设qos为1
	// 	if (!sub_mqtt) {
	// 		PC_Usart("\r\nSubscribing to MQTT topic failed, retrying %d/3...\r\n", sub_retry+1);
	// 		sub_retry++;
	// 		delay_ms(1000);



	// 	}
	// }



    
    // 发送数据(变量已在开头声明)
    // test_data = "Hello from ESP8266!";
    // data_len = strlen(test_data);
    // PC_Usart("\r\nSending test data: %s\r\n", test_data);
    // if (ESP8266_SendString(DISABLE, (char*)test_data, data_len, 0)) { // 假设Single_ID=0
    //     PC_Usart("\r\nData sent successfully!\r\n");
    // } else {
    //     PC_Usart("\r\nFailed to send data!\r\n");
    // }
    
    // 接收循环
	// while (1) {

	// }
}


// public mqtt topic message  function,parameter is topicId & val
bool ESP8266_Public_MQTT_Message (const char * topicId, const char * val)
{
	char cStr [100] = { 0 }, cCmd [120];
	// construct string: AT+MQTTPUB=0,"topic911","66693",0,0
	sprintf ( cCmd, "AT+MQTTPUB=0,\"%s\",\"%s\",0,0", topicId, val );
	return ESP8266_Cmd ( cCmd, "OK", "ALREAY CONNECT", 500 );
}	






/*
 * 函数名:ESP8266_AP_TCP_Server
 * 描述  :PZ-ESP8266模块进行AP TCP Server测试
 * 输入  :无
 * 返回  : 无
 * 调用  :被外部调用
 */
void ESP8266_AP_TCP_Server ( void )
{
	char cStrInput [100] = { 0 }, * pStrDelimiter [3], * pBuf, * pStr;
	u8 uc = 0;
  u32 ul = 0;

  ESP8266_Choose ( ENABLE );

	ESP8266_AT_Test ();
	
	ESP8266_Net_Mode_Choose ( AP );


	PC_Usart ( "\r\n请输入要创建的WiFi的名称、加密方式和密钥,加密方式的编号为:\
              \r\n0 = OPEN\
              \r\n1 = WEP\
              \r\n2 = WPA_PSK\
	            \r\n3 = WPA2_PSK\
              \r\n4 = WPA_WPA2_PSK\
							\r\n输入格式为:名称字符+英文逗号+加密方式编号+英文逗号+密钥字符+空格,点击发送\r\n" );

	scanf ( "%s", cStrInput );

	PC_Usart ( "\r\n稍等片刻 ……\r\n" );

	pBuf = cStrInput;
	uc = 0;
	while ( ( pStr = strtok ( pBuf, "," ) ) != NULL )
	{
		pStrDelimiter [ uc ++ ] = pStr;
		pBuf = NULL;
	} 
	
	ESP8266_BuildAP ( pStrDelimiter [0], pStrDelimiter [2], pStrDelimiter [1] );
	ESP8266_Cmd ( "AT+RST", "OK", "ready", 2500 ); //*
		

	ESP8266_Enable_MultipleId ( ENABLE );
		
	
	PC_Usart ( "\r\n请输入服务器要开启的端口号和超时时间(0~28800,单位:秒),输入格式为:端口号字符+英文逗号+超时时间字符+空格,点击发送\r\n" );

	scanf ( "%s", cStrInput );

	PC_Usart ( "\r\n稍等片刻 ……\r\n" );

	pBuf = cStrInput;
	uc = 0;
	while ( ( pStr = strtok ( pBuf, "," ) ) != NULL )
	{
		pStrDelimiter [ uc ++ ] = pStr;
		pBuf = NULL;
	} 

	ESP8266_StartOrShutServer ( ENABLE, pStrDelimiter [0], pStrDelimiter [1] );
	
	
	do
	{
		PC_Usart ( "\r\n正查询本模块IP……\r\n" );
	  ESP8266_Cmd ( "AT+CIFSR", "OK", "Link", 500 );
		
		PC_Usart ( "\r\n请用手机连接刚才创建的WiFi,这里只连接一个手机,作为ID0,然后用手机网络调试助手以TCP Client连接刚才开启的服务器(AP IP)……\r\n" );
		delay_ms ( 20000 ) ;
	}	while ( ! ESP8266_Cmd ( "AT+CIPSTATUS", "+CIPSTATUS:0", 0, 500 ) );
	

	PC_Usart ( "\r\n请输入要向端口手机(ID0)发送的字符串,输入格式为:字符串(不含空格)+空格,点击发送\r\n" );

	scanf ( "%s", cStrInput );

	ul = strlen ( cStrInput );
	
	ESP8266_SendString ( DISABLE, cStrInput, ul, Multiple_ID_0 );

	
	PC_Usart ( "\r\n请在手机网络调试助手发送字符串\r\n" );
	while (1)
	{
	  pStr = ESP8266_ReceiveString ( DISABLE );
		PC_Usart ( "%s", pStr );
	}

}


/*
 * 函数名:ESP8266_StaTcpClient_ApTcpServer
 * 描述  :PZ-ESP8266模块进行STA(TCP Client)+AP(TCP Server)测试
 * 输入  :无
 * 返回  : 无
 * 调用  :被外部调用
 */
void ESP8266_StaTcpClient_ApTcpServer ( void )
{
	char cStrInput [100] = { 0 }, * pStrDelimiter [3], * pBuf, * pStr;
	u8 uc = 0;
  u32 ul = 0;

  ESP8266_Choose ( ENABLE );

	ESP8266_AT_Test ();
	
	ESP8266_Net_Mode_Choose ( STA_AP );


	PC_Usart ( "\r\n请输入要创建的WiFi的名称、加密方式和密钥,加密方式的编号为:\
						\r\n0 = OPEN\
						\r\n1  =WEP\
						\r\n2 = WPA_PSK\
						\r\n3 = WPA2_PSK\
						\r\n4 = WPA_WPA2_PSK\
						\r\n输入格式为:名称字符+英文逗号+加密方式编号+英文逗号+密钥字符+空格,点击发送\r\n" );

	scanf ( "%s", cStrInput );

	PC_Usart ( "\r\n稍等片刻 ……\r\n" );

	pBuf = cStrInput;
	uc = 0;
	while ( ( pStr = strtok ( pBuf, "," ) ) != NULL )
	{
		pStrDelimiter [ uc ++ ] = pStr;
		pBuf = NULL;
	} 
	
	ESP8266_BuildAP ( pStrDelimiter [0], pStrDelimiter [2], pStrDelimiter [1] );
	ESP8266_Cmd ( "AT+RST", "OK", "ready", 2500 ); //*
	

	ESP8266_Cmd ( "AT+CWLAP", "OK", 0, 5000 );
		
  do
	{
		PC_Usart ( "\r\n请输入要连接的WiFi名称和密钥,输入格式为:名称字符+英文逗号+密钥字符+空格,点击发送\r\n" );

		scanf ( "%s", cStrInput );

		PC_Usart ( "\r\n稍等片刻 ……\r\n" );

		pBuf = cStrInput;
		uc = 0;
		while ( ( pStr = strtok ( pBuf, "," ) ) != NULL )
		{
			pStrDelimiter [ uc ++ ] = pStr;
			pBuf = NULL;
		} 
		
  } while ( ! ESP8266_JoinAP ( pStrDelimiter [0], pStrDelimiter [1] ) );

	
	ESP8266_Enable_MultipleId ( ENABLE );
		
	
	PC_Usart ( "\r\n请输入服务器要开启的端口号和超时时间(0~28800,单位:秒),输入格式为:端口号字符+英文逗号+超时时间字符+空格,点击发送\r\n" );

	scanf ( "%s", cStrInput );

	PC_Usart ( "\r\n稍等片刻 ……\r\n" );

	pBuf = cStrInput;
	uc = 0;
	while ( ( pStr = strtok ( pBuf, "," ) ) != NULL )
	{
		pStrDelimiter [ uc ++ ] = pStr;
		pBuf = NULL;
	} 

	ESP8266_StartOrShutServer ( ENABLE, pStrDelimiter [0], pStrDelimiter [1] );
	
	
	do 
	{
		PC_Usart ( "\r\n请在电脑上将网络调试助手以TCP Server连接网络,并输入电脑的IP和端口号,输入格式为:电脑IP+英文逗号+端口号+空格,点击发送\r\n" );

		scanf ( "%s", cStrInput );

		PC_Usart ( "\r\n稍等片刻 ……\r\n" );

		pBuf = cStrInput;
		uc = 0;
		while ( ( pStr = strtok ( pBuf, "," ) ) != NULL )
		{
			pStrDelimiter [ uc ++ ] = pStr;
			pBuf = NULL;
		} 
		
  } while ( ! ( ESP8266_Link_Server ( enumTCP, pStrDelimiter [0], pStrDelimiter [1], Multiple_ID_0 ) &&
	              ESP8266_Link_Server ( enumTCP, pStrDelimiter [0], pStrDelimiter [1], Multiple_ID_1 ) &&
	              ESP8266_Link_Server ( enumTCP, pStrDelimiter [0], pStrDelimiter [1], Multiple_ID_2 ) ) );
		
	
	do
	{
		PC_Usart ( "\r\n正查询本模块IP,前一个为AP IP,后一个为STA IP……\r\n" );
	  ESP8266_Cmd ( "AT+CIFSR", "OK", "Link", 500 );
		
		PC_Usart ( "\r\n请用手机连接刚才创建的WiFi,这里只连接一个手机,作为ID3,然后用手机网络调试助手以TCP Client连接刚才开启的服务器(AP IP)……\r\n" );
		delay_ms ( 20000 ) ;
	}	while ( ! ESP8266_Cmd ( "AT+CIPSTATUS", "+CIPSTATUS:3", 0, 500 ) );
	

	for ( uc = 0; uc < 3; uc ++ )
	{
		PC_Usart ( "\r\n请输入端口ID%d要发送的字符串,输入格式为:字符串(不含空格)+空格,点击发送\r\n", uc );

		scanf ( "%s", cStrInput );

		ul = strlen ( cStrInput );
		
		ESP8266_SendString ( DISABLE, cStrInput, ul, ( ENUM_ID_NO_TypeDef ) uc );
		
	}
	
	
	PC_Usart ( "\r\n请输入要向端口手机(ID3)发送的字符串,输入格式为:字符串(不含空格)+空格,点击发送\r\n" );

	scanf ( "%s", cStrInput );

	ul = strlen ( cStrInput );
	
	ESP8266_SendString ( DISABLE, cStrInput, ul, Multiple_ID_3 );

	
	PC_Usart ( "\r\n请在电脑网络调试助手或手机网络调试助手发送字符串……\r\n" );
	while (1)
	{
	  pStr = ESP8266_ReceiveString ( DISABLE );
		PC_Usart ( "%s", pStr );
	}
	
}






main.c

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "wifi_config.h"
#include "wifi_function.h"
#include <string.h>

// 声明外部变量
extern struct STRUCT_USARTx_Fram strEsp8266_Fram_Record;

// 函数声明
void ProcessWiFiData(uint8_t* data, uint16_t length);
void HandleMQTTMessage(const char* topic, const char* message,const char* message_le);

// 全局变量
char topic_id[20] = "topic911";
char sub_topic_id[20] = "topic108";
uint8_t wifi_rx_buffer[RX_BUF_MAX_LEN];  // 用于暂存接收数据的缓冲区

int main()
{
    // 初始化部分保持不变...
    SysTick_Init(72);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    USART1_Init(9600);
    LED_Init();
    WiFi_Config();
    
    printf("Hello, world!\r\n");
    printf("\r\n====================================\r\n");
    printf("STM32 + ESP8266 TCP Client Demo\r\n");
    printf("====================================\r\n");

    // 初始化WiFi和MQTT
    ESP8266_STA_TCP_Client_MQTT();
    if (ESP8266_Set_MQTT_Sub(sub_topic_id, "0")) {
        printf("\r\nSubscribed to MQTT topic successfully!\r\n");
    } else {
        printf("\r\nFailed to subscribe to MQTT topic!\r\n");
    }

    
    if (ESP8266_MQTT_Public_key_Value(topic_id, "aa", "01")) {
        printf("\r\nPublished MQTT message successfully!\r\n");
    } else {
        printf("\r\nFailed to publish MQTT message!\r\n");
    }

    while(1) 
    {
        // 处理WiFi数据
        if(strEsp8266_Fram_Record.InfBit.FramFinishFlag)
        {
			uint16_t data_len; // **提前在代码块开头声明变量**
            __disable_irq();  // 进入临界区
            data_len = strEsp8266_Fram_Record.InfBit.FramLength;
            
            // 复制数据到本地缓冲区
            memcpy(wifi_rx_buffer, strEsp8266_Fram_Record.Data_RX_BUF, data_len);
            wifi_rx_buffer[data_len] = '\0';  // 添加字符串结束符
            
            // 清除标志和计数器
            strEsp8266_Fram_Record.InfBit.FramFinishFlag = 0;
            strEsp8266_Fram_Record.InfBit.FramLength = 0;
            __enable_irq();   // 退出临界区
            
            // 处理接收到的数据
            ProcessWiFiData(wifi_rx_buffer, data_len);
        }

        // 其他业务逻辑...
        delay_ms(100);
    }
}

// 处理WiFi数据的函数
void ProcessWiFiData(uint8_t* data, uint16_t length)
{
    // 示例:直接打印原始数据
    printf("[RAW DATA] %s\r\n", data);

    // 检查是否是MQTT消息
    if(strstr((char*)data, "+MQTTSUBRECV:") != NULL)
    {
        // 解析MQTT消息
        char* topic_start = strchr((char*)data, ',') + 1;
        char* msg_length_start = strchr(topic_start, ',') + 1;
        char* msg_start = strchr(msg_length_start, ',') + 1;

        // 提取主题和消息
        char topic[32] = {0};
        char msg_length[32] = {0};
        char message[128] = {0};
        
        sscanf(topic_start, "%[^,]", topic);
        sscanf(msg_length_start, "%[^,]", msg_length);
        sscanf(msg_start, "%[^,]", message);
        
        HandleMQTTMessage(topic, message,msg_length);
    }
    // 可以添加其他协议处理...
}

// 处理MQTT消息
void HandleMQTTMessage(const char* topic, const char* message,const char* message_length)
{
    printf("Received MQTT message:\r\n");
    printf("Topic: %s\r\n", topic); //topic_id
    printf("Message: %s\r\n\r\n", message);  // json 字符串

    //todo: 根据实际需求处理消息



}

发布订阅的指令

AT+MQTTSUB=0,"topic123",0

MQTT发布订阅指令:

这是一个 MQTT 订阅指令,含义是:

  • 通过 ID 为 0 的 MQTT 客户端订阅主题 "topic123"
  • QoS 级别设置为 0(最多一次)
响应解析
OK
+MQTTSUBRECV:0,"topic123",3,123
+MQTTSUBRECV:0,"topic123",9,123456xxx
  1. OK:表示订阅指令已成功执行
  2. +MQTTSUBRECV:这是模块主动推送的 MQTT 消息通知,格式为:
    +MQTTSUBRECV:<ClientID>,"<Topic>",<PayloadLength>,<Payload>
    
    0:客户端 ID
    • "topic123":消息来自的主题
    • 3/9:消息载荷的长度(字节)
    • 123/123456xxx:实际的消息内容
消息含义
  • 第一条消息:在主题 "topic123" 上收到长度为 3 的消息 "123"
  • 第二条消息:在同一主题上收到长度为 9 的消息 "123456xxx"

基于 STM32 微控制器的 WiFi 模块(如 ESP8266)初始化及串口通信处理程序

,主要实现 WiFi 模块硬件控制、串口 3(USART3)初始化、数据接收中断处理及自定义格式化输出功能。以下是详细解释:

1. 头文件与全局变量

#include "wifi_config.h"
#include <stdio.h>
#include <stdarg.h>

struct STRUCT_USARTx_Fram strEsp8266_Fram_Record = { 0 };

  • 头文件
    • wifi_config.h:包含 WiFi 模块配置相关的宏定义、结构体声明等。
    • stdio.h:提供标准输入输出函数原型(尽管代码中自定义了printf)。
    • stdarg.h:用于处理可变参数(在USART3_printf中使用)。
  • 全局结构体变量
    • strEsp8266_Fram_Record:存储 WiFi 模块通过串口 3 接收的数据帧,包括缓冲区、帧长度、接收完成标志等信息。

2. WiFi_Config 函数:初始化 WiFi 模块硬件与串口 3

void WiFi_Config( void )
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    // 使能时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);    // USART3时钟(APB1总线)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);     // GPIOB时钟(USART3的TX/RX引脚在PB10/PB11)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);     // GPIOA时钟(用于WiFi模块片选和复位)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);      // 复用功能时钟
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);   // 禁用JTAG,释放PA15等引脚

    // 配置WiFi模块的片选(CH)和复位(RST)引脚(PA4和PA15)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;          // 推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_ResetBits(GPIOA, GPIO_Pin_4);                        // 拉低片选引脚(模块使能)
    GPIO_SetBits(GPIOA, GPIO_Pin_15);                         // 拉高复位引脚(模块正常工作)

    // 配置USART3的TX(PB10)和RX(PB11)引脚
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;                // TX引脚:复用推挽输出
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;                // RX引脚:浮空输入
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    // 初始化USART3
    USART_InitStructure.USART_BaudRate = 9600;                // 波特率与WiFi模块匹配(通常为9600或115200)
    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(USART3, &USART_InitStructure);
    USART_Cmd(USART3, ENABLE);                                // 使能USART3

    // 开启接收中断和空闲中断(用于检测数据帧结束)
    USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
    USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);

    // 配置USART3中断优先级(最高优先级)
    NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}
关键功能
  • 硬件控制引脚
    • PA4(片选 CH):拉低以选中 WiFi 模块,使其响应串口指令。
    • PA15(复位 RST):拉高表示模块正常工作,拉低可复位模块。
  • USART3 配置
    • 波特率需与 WiFi 模块的 AT 指令波特率一致(默认 9600 或 115200)。
    • 启用接收中断(USART_IT_RXNE)和空闲中断(USART_IT_IDLE),前者用于逐字节接收,后者用于检测一帧数据接收完成(当串口空闲时触发)。
  • 中断优先级:设置为最高优先级(0, 0),确保及时处理 WiFi 模块返回的数据。

3. USART3_IRQHandler 函数:串口 3 中断处理

void USART3_IRQHandler( void )
{
    char ch;

    // 处理接收中断(接收到单个字节)
    if (USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
    {
        ch = USART_ReceiveData(USART3);
        // 将字节存入接收缓冲区,未超过最大长度时
        if (strEsp8266_Fram_Record.InfBit.FramLength < (RX_BUF_MAX_LEN - 1))
        {
            strEsp8266_Fram_Record.Data_RX_BUF[strEsp8266_Fram_Record.InfBit.FramLength++] = ch;
        }
    }

    // 处理空闲中断(一帧数据接收完成)
    if (USART_GetITStatus(USART3, USART_IT_IDLE) == SET)
    {
        strEsp8266_Fram_Record.InfBit.FramFinishFlag = 1; // 设置接收完成标志
        ch = USART_ReceiveData(USART3);                  // 读取空闲标志对应的垃圾字节(必须操作)
    }
}
关键逻辑
  • 逐字节接收:通过USART_IT_RXNE中断逐字节存入缓冲区Data_RX_BUF,直到缓冲区快满(预留 1 字节给结束符)。
  • 帧结束检测:当串口空闲(USART_IT_IDLE)时,说明一帧数据接收完成,设置FramFinishFlag标志,用于主程序处理完整数据帧。
  • 清除空闲中断:必须先读取USART_SR寄存器,再读取USART_DR寄存器,否则中断标志不会清除。

4. itoa 函数:整数转字符串(自定义实现)

static char *itoa( int value, char *string, int radix )
{
    // 仅支持十进制转换
    if (radix != 10) return string;

    char *ptr = string;
    int flag = 0, i, d;

    // 处理0值
    if (!value) { *ptr++ = '0'; *ptr = '\0'; return string; }

    // 处理负数
    if (value < 0) { *ptr++ = '-'; value = -value; }

    // 转换逻辑:从高位到低位分解数字
    for (i = 10000; i > 0; i /= 10)
    {
        d = value / i;
        if (d || flag) { // 跳过前导零,除非已开始记录数字
            *ptr++ = d + '0';
            value -= d * i;
            flag = 1;
        }
    }
    *ptr = '\0'; // 字符串结束符
    return string;
}
用途
  • 将整数转换为字符串,用于USART3_printf中处理%d格式符,避免依赖标准库的sprintf,节省代码空间。

5. USART3_printf 函数:自定义格式化输出

void USART3_printf( USART_TypeDef* USARTx, char *Data, ... )
{
    const char *s;
    int d;
    char buf[16];
    va_list ap;
    va_start(ap, Data); // 初始化可变参数指针

    while (*Data != '\0')
    {
        if (*Data == '\\') { // 处理转义字符
            Data++;
            switch (*Data) {
                case 'r': USART_SendData(USARTx, 0x0d); break; // 回车
                case 'n': USART_SendData(USARTx, 0x0a); break; // 换行
                default: break;
            }
            Data++;
        }
        else if (*Data == '%') { // 处理格式符
            Data++;
            switch (*Data) {
                case 's': // 字符串
                    s = va_arg(ap, const char*);
                    while (*s) {
                        USART_SendData(USARTx, *s++);
                        while (!USART_GetFlagStatus(USARTx, USART_FLAG_TXE)); // 等待发送完成
                    }
                    Data++;
                    break;
                case 'd': // 十进制整数
                    d = va_arg(ap, int);
                    itoa(d, buf, 10); // 调用自定义itoa转换
                    s = buf;
                    while (*s) {
                        USART_SendData(USARTx, *s++);
                        while (!USART_GetFlagStatus(USARTx, USART_FLAG_TXE));
                    }
                    Data++;
                    break;
                default: Data++; break;
            }
        }
        else { // 普通字符直接发送
            USART_SendData(USARTx, *Data++);
            while (!USART_GetFlagStatus(USARTx, USART_FLAG_TXE));
        }
    }
    va_end(ap); // 释放可变参数指针
}
关键功能
  • 可变参数支持:通过va_list解析可变参数,支持%s(字符串)和%d(十进制整数)格式。
  • 转义字符处理:手动解析\r(回车)和\n(换行),转换为对应的 ASCII 码(0x0d0x0a)。
  • 发送同步:每次发送字符后等待USART_FLAG_TXE标志,确保数据发送完成,避免缓冲区溢出。

6. 代码总结与应用场景

核心功能
  1. WiFi 模块硬件控制:通过 GPIO 引脚控制模块的片选和复位,确保模块进入正常工作状态。
  2. 串口 3 通信:作为与 WiFi 模块交互的通道,通过中断接收模块返回的 AT 指令响应数据。
  3. 自定义输出函数:方便向 WiFi 模块发送格式化的 AT 指令(如AT+MQTTCONN=0,"192.168.1.1",1883,0\r\n),避免依赖标准库,适合资源受限的嵌入式场景。
典型流程
  1. 调用WiFi_Config初始化硬件和串口 3。
  2. 通过USART3_printf发送 AT 指令(如AT+CWMODE=1设置 STA 模式)。
  3. 在中断处理函数中接收模块响应数据,主程序通过strEsp8266_Fram_Record结构体获取完整数据帧。
  4. 解析响应数据,判断 WiFi 模块是否成功执行指令(如连接到路由器、建立 MQTT 连接等)。
注意事项
  • 波特率匹配:确保 USART3 的波特率与 WiFi 模块的 AT 指令波特率一致(通常需通过模块出厂设置或 AT 指令AT+UART配置)。
  • 缓冲区大小RX_BUF_MAX_LEN需根据实际数据帧长度设置,避免溢出。
  • 中断优先级:USART3 中断优先级设为最高,确保及时处理模块响应,避免数据丢失。

串口通信代码解析再说明

针对 USART1 的配置和使用。它包含了基本的串口初始化、数据收发、中断处理以及标准输入输出函数的重定向。

核心功能模块

1. 标准输入输出重定向

int fputc(int ch,FILE *p)
int fgetc(FILE *f)

这两个函数分别实现了printfscanf函数的重定向:

  • fpuc将字符输出到 USART1,实现了printf功能
  • fgetc从 USART1 读取字符,实现了scanf功能
2. 串口初始化函数
void USART1_Init(u32 bound)

该函数完成 USART1 的初始化配置:

  1. 使能 GPIOA 和 USART1 的时钟
  2. 配置 PA9 为复用推挽输出(TX)
  3. 配置 PA10 为浮空输入(RX)
  4. 设置波特率、数据位、停止位等参数
  5. 使能串口但默认不开启中断
3. 接收缓冲区和状态标记

u8 USART1_RX_BUF[USART1_REC_LEN];
u16 USART1_RX_STA;

  • USART1_RX_BUF:接收缓冲区,存储接收到的数据
  • USART1_RX_STA:接收状态标记,使用 16 位中的不同位表示不同状态:
    • bit15:接收完成标志
    • bit14:接收到 0x0D(回车符)
    • bit13~0:接收到的有效字节数目
4. 中断处理函数

void USART1_IRQHandler(void)

该函数处理 USART1 的接收中断:

  1. 检查是否是接收中断触发
  2. 读取接收到的数据
  3. 根据当前接收状态处理数据:
    • 如果接收到回车符(0x0D),设置相应标志
    • 如果接收到换行符(0x0A)且之前已收到回车符,表示接收完成
    • 将有效数据存入接收缓冲区

代码使用说明

  1. 初始化串口

    USART1_Init(115200); // 初始化USART1,波特率115200
    
  2. 发送数据

    printf("Hello, world!\r\n"); // 使用printf发送数据
    
  3. 接收数据

    • 开启中断:取消USART1_Init函数中中断使能的注释
    • 等待接收完成:检查USART1_RX_STA的 bit15 是否为 1
    • 处理接收到的数据:从USART1_RX_BUF读取数据
  4. 注意事项

    • 使用scanf时需要关闭 USART1 中断,避免冲突
    • 接收缓冲区大小由USART1_REC_LEN定义,需确保足够大
    • 接收协议使用回车换行(CRLF)作为结束标志

代码优化建议

  1. 增加超时机制:当前代码没有超时处理,如果没有接收到换行符,可能导致一直等待
  2. 添加错误处理:可以增加缓冲区溢出、校验错误等处理
  3. 扩展功能:可以添加多字节命令解析、命令执行等功能
  4. 提高安全性:限制接收缓冲区大小,防止缓冲区溢出攻击

 

ESP8266_Cmd 函数深度解析

函数功能总览

该函数是 STM32 与 ESP8266 模块通信的核心接口,负责完成以下任务:

  1. 向 ESP8266 发送 AT 指令,并自动添加指令结束符\r\n
  2. 等待模块响应数据,并根据预设条件验证响应是否符合预期
  3. 支持多条件响应匹配(逻辑或关系)和超时机制
  4. 返回指令执行结果,用于判断通信是否成功

核心数据结构与全局变量

// 假设在头文件中定义的全局接收结构体
typedef struct {
    uint16_t FramLength;   // 接收数据长度计数器
    char Data_RX_BUF[256]; // 接收数据缓冲区(最大256字节)
    // 其他状态标志位(如接收完成标志)
} ESP8266_Fram_Record_TypeDef;

ESP8266_Fram_Record_TypeDef strEsp8266_Fram_Record; // 全局实例

  • 作用:通过全局结构体实现跨函数的数据共享,存储接收的原始响应数据及长度

函数参数详解

参数类型必要性功能描述
cmdchar*必须待发送的 AT 指令字符串(如"AT+CWMODE=1"),无需手动添加\r\n
reply1char*可选期望的响应字符串 1(如"OK"),若为NULL则忽略该条件
reply2char*可选期望的响应字符串 2(如"ERROR"),若为NULL则忽略该条件
waittimeu32必须等待响应的超时时间(单位:毫秒),建议根据指令特性设置(如连接指令需更长时间)

执行流程分步解析

1. 接收缓冲区初始化

strEsp8266_Fram_Record.InfBit.FramLength = 0; // 重置接收长度为0

  • 操作目的:清空上一次通信残留数据,为新指令响应做准备
  • 潜在风险:若接收缓冲区未正确清空,可能导致新旧数据混合,影响响应解析
2. AT 指令发送

ESP8266_Usart("%s\r\n", cmd); // 发送指令并自动添加\r\n

  • 指令格式处理
    • AT 指令规范要求以\r\n作为结束符,函数自动补全
    • 示例:若cmd"AT",实际发送数据为"AT\r\n"
  • 通信接口
    • ESP8266_Usart通常对应 STM32 的 USART 外设驱动,负责底层串口发送
3. 无响应需求处理

if ((reply1 == NULL) && (reply2 == NULL)) 
    return true; // 无需验证响应,直接返回成功

  • 适用场景
    • 部分指令无需模块响应(如复位指令AT+RST
    • 仅需发送指令,不关心执行结果的场景
4. 响应超时等待

delay_ms(waittime); // 阻塞等待指定时间

  • 时间控制逻辑
    • 通过waittime参数灵活控制超时时间
    • 短指令(如AT)可设为 200ms,复杂指令(如AT+CWJAP)需设为 5000ms 以上
  • 潜在问题
    • 阻塞式延时会暂停 STM32 主程序,建议在非实时系统中使用
5. 响应数据处理

// 添加字符串终止符,转换为C语言字符串
strEsp8266_Fram_Record.Data_RX_BUF[strEsp8266_Fram_Record.InfBit.FramLength] = '\0'; 
PC_Usart("%s", strEsp8266_Fram_Record.Data_RX_BUF); // 打印响应数据到调试串口

  • 字符串格式化
    • 使用\0将缓冲区数据转为标准 C 字符串,便于strstr等函数操作
  • 调试辅助
    • 通过PC_Usart输出原始响应数据,方便开发者定位问题(如响应包含ERROR
6. 响应匹配与结果返回

// 逻辑或匹配:只要满足任意一个预期响应即返回成功
if ((reply1 != NULL) && (reply2 != NULL)) {
    return (bool)strstr(buffer, reply1) || (bool)strstr(buffer, reply2);
} else if (reply1 != NULL) {
    return (bool)strstr(buffer, reply1); // 单条件匹配
} else {
    return (bool)strstr(buffer, reply2); // 单条件匹配
}

  • 匹配算法
    • 使用strstr函数进行子字符串匹配(非精确匹配,如"OKAY"会匹配"OK"
    • 可通过strncmp优化为精确匹配(需指定长度)
  • 返回值逻辑
    • true:响应包含任一预期字符串(或无需响应)
    • false:超时未收到响应,或响应不包含预期字符串

典型应用场景示例

场景 1:基础指令测试(AT 指令验证)

bool result = ESP8266_Cmd("AT", "OK", NULL, 200); // 验证模块是否存活
if (result) {
    PC_Usart("Module is ready!\r\n");
} else {
    PC_Usart("Module no response!\r\n");
}

  • 预期响应:模块返回OK,函数返回true
场景 2:WiFi 连接指令(带双条件匹配)

// 连接WiFi,预期响应为"OK"或"ERROR"
bool connect_result = ESP8266_Cmd("AT+CWJAP=\"MyWiFi\",\"Password123\"", "OK", "ERROR", 7000);
if (connect_result) {
    PC_Usart("WiFi connection completed\r\n");
} else {
    PC_Usart("WiFi connection failed\r\n");
}
场景 3:无需响应的指令(模块复位)

ESP8266_Cmd("AT+RST", NULL, NULL, 2500); // 发送复位指令,不等待响应
PC_Usart("Module resetting...\r\n");

函数缺陷与优化方向

1. 阻塞式延时的优化
  • 问题delay_ms会阻塞主程序,影响多任务处理
  • 优化方案

    // 改用非阻塞式超时检测(伪代码)
    uint32_t start_time = GetTickCount();
    while ((GetTickCount() - start_time < waittime) && !ReceiveCompleteFlag) {
        // 执行其他任务
    }
    
2. 缓冲区溢出处理
  • 问题:固定 256 字节缓冲区可能无法容纳长响应(如AT+CWLAP的 AP 列表)
  • 优化方案

    #define RX_BUFF_SIZE 1024 // 增大缓冲区
    char Data_RX_BUF[RX_BUFF_SIZE];
    
3. 响应匹配精度提升
  • 问题strstr可能误匹配子字符串
  • 优化方案

    // 精确匹配响应前缀
    bool MatchPrefix(char* buffer, char* target) {
        return strncmp(buffer, target, strlen(target)) == 0;
    }
    
4. 错误信息增强

多路连接(AT+CIPMUX=1 )

  • 当前缺陷:仅返回布尔值,无法区分超时 / 响应错误
  • 优化方案

    // 定义枚举类型返回值
    typedef enum {
        CMD_OK,
        CMD_TIMEOUT,
        CMD_RESP_ERROR
    } CmdResult;
    
    CmdResult ESP8266_Cmd(...) {
        // 添加超时判断逻辑
        if (waittime && !ReceiveCompleteFlag) return CMD_TIMEOUT;
        // 添加响应判断逻辑
        if (/* 匹配失败 */) return CMD_RESP_ERROR;
        return CMD_OK;
    }
    

    ESP8266 发送消息的 AT 指令完整格式,根据连接模式不同而有所区别:

    单路连接(AT+CIPMUX=0 )

  • 指令格式AT+CIPSEND=<length>
  • 参数说明: <length> 代表要发送数据的长度 ,是一个数字参数,最大长度为 2048 。比如要发送 “Hello World” 这 11 个字符,指令就是 AT+CIPSEND=11 。
  • 发送流程:发送该指令后,模块会先返回 > ,此时再发送实际数据 。当数据发送成功,模块返回 SEND OK 。
  • 指令格式AT+CIPSEND=<id>,<length>
  • 参数说明: <id> 是连接的编号,取值范围为 0 - 4 ,用于指定通过哪个连接发送数据;<length> 同样是要发送数据的长度,最大为 2048 。例如,要通过编号为 2 的连接发送 “Goodbye” 这 7 个字符,指令就是 AT+CIPSEND=2,7 。
  • 发送流程:发送此指令后,模块返回 > ,接着发送实际数据,发送成功模块会返回 SEND OK 。

采用了双中断机制(RXNE 和 IDLE)来高效接收完整的数据帧

1. 整体功能概述

这个中断服务函数通过两种中断事件来接收和处理串口数据:

RXNE(接收缓冲区非空):每当接收到一个字节时触发,将数据存入缓冲区。 IDLE(总线空闲):当检测到串口总线空闲时触发,表示一帧数据接收完毕。

2. RXNE 中断处理(字节接收)

if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
{
    ch  = USART_ReceiveData(USART3);
    
    if( strEsp8266_Fram_Record.InfBit.FramLength < (RX_BUF_MAX_LEN - 1) )
    {
        strEsp8266_Fram_Record.Data_RX_BUF[strEsp8266_Fram_Record.InfBit.FramLength++] = ch;
    }
}

关键点

RXNE 触发条件:当串口接收缓冲区(USART_DR 寄存器)有新数据时触发。

3. IDLE 中断处理(帧结束)

if (USART_GetITStatus(USART3, USART_IT_IDLE) == SET)
{
    strEsp8266_Fram_Record.InfBit.FramFinishFlag = 1;
    ch = USART_ReceiveData(USART3);
}

关键点

IDLE 触发条件:当串口接收线上检测到 **1 个字节的高电平(空闲状态)** 时触发。这意味着:

4. 双中断机制的优势

对比传统方法

传统方式需在 RXNE 中断中手动判断结束符(如检测\r\n),实现复杂且容易出错。

5. 与 ESP8266_Cmd 函数的协同工作

ESP8266_Cmd函数中:

发送 AT 指令前,通过strEsp8266_Fram_Record.InfBit.FramLength = 0清空缓冲区。

6. 潜在问题与优化建议

6.1 缓冲区溢出风险

6.3 缺少超时机制

7. 总结

这段代码通过 RXNE 和 IDLE 双中断机制,高效且可靠地实现了 ESP8266 响应数据的接收。其核心优势在于:

自动识别完整数据帧,无需依赖特定结束符。

  • 数据存储:将接收到的字节存入Data_RX_BUF缓冲区,并递增FramLength
  • 缓冲区保护:通过检查FramLength < (RX_BUF_MAX_LEN - 1)防止缓冲区溢出,预留一个字节用于存储字符串结束符\0
    • 连续接收到数据时不会触发 IDLE。
    • 当数据发送完毕,总线进入空闲状态时才会触发。
  • 帧完成标志:设置FramFinishFlag为 1,表示一帧数据接收完成。
  • 清除中断标志
    • IDLE 标志需要通过特定序列清除:先读 USART_SR 寄存器,再读 USART_DR 寄存器
    • 代码中的ch = USART_ReceiveData(USART3)就是为了完成这个序列。
  • RXNE 逐字节接收:确保每个字节都被及时捕获并存入缓冲区。
  • IDLE 判断帧结束:无需依赖特定结束符(如\r\n),只要数据传输暂停就认为帧结束,适应性更强。
  • IDLE 中断可自动识别完整帧,无论数据内容如何(即使包含\r\n)。
  • 发送指令后,通过delay_ms(waittime)等待响应。
  • 在等待期间,IDLE 中断会在数据接收完毕时自动设置FramFinishFlag
  • ESP8266_Cmd返回后,主程序可通过检查FramFinishFlagData_RX_BUF内容来处理响应。
  • 当前代码仅在 RXNE 中断中检查缓冲区边界,若数据量过大,可能在 IDLE 触发前已溢出。
  • 优化建议:在 IDLE 中断中增加缓冲区溢出检查,并设置错误标志。
  • 当前代码未处理 IDLE 中断触发后的缓冲区使用,需在主程序中主动检查FramFinishFlag
  • 优化建议:可添加回调函数,在 IDLE 中断中直接处理完整数据帧。
  • 若 ESP8266 无响应,ESP8266_Cmddelay_ms可能导致长时间阻塞。
  • 优化建议:结合定时器实现超时检测,避免无限等待。
  • 缓冲区管理安全(预留结束符空间)。
  • ESP8266_Cmd函数配合,实现了 AT 指令发送 - 响应的完整流程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值