搞这个有点小累了,先简单把代码粘贴出来,后面再逐步把涉及的问题说明一下
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
- OK:表示订阅指令已成功执行
- +MQTTSUBRECV:这是模块主动推送的 MQTT 消息通知,格式为:
0:客户端 ID+MQTTSUBRECV:<ClientID>,"<Topic>",<PayloadLength>,<Payload>
- "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 码(0x0d
和0x0a
)。 - 发送同步:每次发送字符后等待
USART_FLAG_TXE
标志,确保数据发送完成,避免缓冲区溢出。
6. 代码总结与应用场景
核心功能:
- WiFi 模块硬件控制:通过 GPIO 引脚控制模块的片选和复位,确保模块进入正常工作状态。
- 串口 3 通信:作为与 WiFi 模块交互的通道,通过中断接收模块返回的 AT 指令响应数据。
- 自定义输出函数:方便向 WiFi 模块发送格式化的 AT 指令(如
AT+MQTTCONN=0,"192.168.1.1",1883,0\r\n
),避免依赖标准库,适合资源受限的嵌入式场景。
典型流程:
- 调用
WiFi_Config
初始化硬件和串口 3。 - 通过
USART3_printf
发送 AT 指令(如AT+CWMODE=1
设置 STA 模式)。 - 在中断处理函数中接收模块响应数据,主程序通过
strEsp8266_Fram_Record
结构体获取完整数据帧。 - 解析响应数据,判断 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)
这两个函数分别实现了printf
和scanf
函数的重定向:
fpuc
将字符输出到 USART1,实现了printf
功能fgetc
从 USART1 读取字符,实现了scanf
功能
2. 串口初始化函数
void USART1_Init(u32 bound)
该函数完成 USART1 的初始化配置:
- 使能 GPIOA 和 USART1 的时钟
- 配置 PA9 为复用推挽输出(TX)
- 配置 PA10 为浮空输入(RX)
- 设置波特率、数据位、停止位等参数
- 使能串口但默认不开启中断
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 的接收中断:
- 检查是否是接收中断触发
- 读取接收到的数据
- 根据当前接收状态处理数据:
- 如果接收到回车符(0x0D),设置相应标志
- 如果接收到换行符(0x0A)且之前已收到回车符,表示接收完成
- 将有效数据存入接收缓冲区
代码使用说明
-
初始化串口:
USART1_Init(115200); // 初始化USART1,波特率115200
-
发送数据:
printf("Hello, world!\r\n"); // 使用printf发送数据
-
接收数据:
- 开启中断:取消
USART1_Init
函数中中断使能的注释 - 等待接收完成:检查
USART1_RX_STA
的 bit15 是否为 1 - 处理接收到的数据:从
USART1_RX_BUF
读取数据
- 开启中断:取消
-
注意事项:
- 使用
scanf
时需要关闭 USART1 中断,避免冲突 - 接收缓冲区大小由
USART1_REC_LEN
定义,需确保足够大 - 接收协议使用回车换行(CRLF)作为结束标志
- 使用
代码优化建议
- 增加超时机制:当前代码没有超时处理,如果没有接收到换行符,可能导致一直等待
- 添加错误处理:可以增加缓冲区溢出、校验错误等处理
- 扩展功能:可以添加多字节命令解析、命令执行等功能
- 提高安全性:限制接收缓冲区大小,防止缓冲区溢出攻击
ESP8266_Cmd
函数深度解析
函数功能总览
该函数是 STM32 与 ESP8266 模块通信的核心接口,负责完成以下任务:
- 向 ESP8266 发送 AT 指令,并自动添加指令结束符
\r\n
- 等待模块响应数据,并根据预设条件验证响应是否符合预期
- 支持多条件响应匹配(逻辑或关系)和超时机制
- 返回指令执行结果,用于判断通信是否成功
核心数据结构与全局变量
// 假设在头文件中定义的全局接收结构体
typedef struct {
uint16_t FramLength; // 接收数据长度计数器
char Data_RX_BUF[256]; // 接收数据缓冲区(最大256字节)
// 其他状态标志位(如接收完成标志)
} ESP8266_Fram_Record_TypeDef;
ESP8266_Fram_Record_TypeDef strEsp8266_Fram_Record; // 全局实例
- 作用:通过全局结构体实现跨函数的数据共享,存储接收的原始响应数据及长度
函数参数详解
参数 | 类型 | 必要性 | 功能描述 |
---|---|---|---|
cmd | char* | 必须 | 待发送的 AT 指令字符串(如"AT+CWMODE=1" ),无需手动添加\r\n |
reply1 | char* | 可选 | 期望的响应字符串 1(如"OK" ),若为NULL 则忽略该条件 |
reply2 | char* | 可选 | 期望的响应字符串 2(如"ERROR" ),若为NULL 则忽略该条件 |
waittime | u32 | 必须 | 等待响应的超时时间(单位:毫秒),建议根据指令特性设置(如连接指令需更长时间) |
执行流程分步解析
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"
- AT 指令规范要求以
- 通信接口:
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
返回后,主程序可通过检查FramFinishFlag
和Data_RX_BUF
内容来处理响应。- 当前代码仅在 RXNE 中断中检查缓冲区边界,若数据量过大,可能在 IDLE 触发前已溢出。
- 优化建议:在 IDLE 中断中增加缓冲区溢出检查,并设置错误标志。
- 当前代码未处理 IDLE 中断触发后的缓冲区使用,需在主程序中主动检查
FramFinishFlag
。 - 优化建议:可添加回调函数,在 IDLE 中断中直接处理完整数据帧。
- 若 ESP8266 无响应,
ESP8266_Cmd
的delay_ms
可能导致长时间阻塞。 - 优化建议:结合定时器实现超时检测,避免无限等待。
- 缓冲区管理安全(预留结束符空间)。
- 与
ESP8266_Cmd
函数配合,实现了 AT 指令发送 - 响应的完整流程。