【毕设调试一】WiFi模块esp8266的调试

2023-05-16

硬件说明:

提示 :主控芯片STM32F103C8T6,与WiFi通信串口为串口二,打印串口为串口三(不使用串口一是当时设计时,方便pcb走线)

说明 :因为我是使用PCB画板的方式来走线的,并不是按模块来拼接的,走线的时候需要注意WiFi模块和主控芯片串口的连接(RX-TX),如下图所示,这个是可以直接安信可的手册上查到,不需要更改。(提醒一下,很多新手可能会找不到对应的原理图,或者是移植他人的,那么我们需要注意的是,我们自己的WiFi模块实际端口,与他人原理图端口还有封装端口是否对的上如下图本人的设计解释

①要首先确定WiFi模块,我是在立创商城购买的:ESP-12F(ESP8266MOD),安信可官网给出如图外观

②再确定原理图,电路外围可以参考安信可的手册,原理图是自己重新画的。(注意看我原理图的端口顺序,是从RST绕逆时针方向走到TX0,从1-22,电源是3.3V,C6和C8两个去耦电容与VCC端口走线尽量短

③确定封装,可以从立创商城直接导出,但并不代表可以直接用(本人走过的坑)。第一张图是立创商城给的封装图,第二张是我自己修改过后的封装图,两图是端口号是不一样  的,需要与原理图的端口号对上,那么焊接的时候才不会出现端口错误。第三张图是pcb的效果图(需要注意天线部分不可面对元器件,并且该部分不铺地,直接露空,信号线尽量在同一层,不要相等长度,过孔虽不会影响信号传送,但尽量少打,至于是否需要隔地,因为这个传输虽说不算太过高速,也只是用来完成毕设,暂时不考虑辐射问题)

                                   


串口配置

说明:因为这是我第一次尝试调试WIFI模块,也参考了大量其他博主的文章,才终于勉勉强强的调试出来,接下来说的是,我在调试过程中因为不了解WiFi模块而产生的各种问题。

我的第一个坑:相信大家看到的,很多博主都是把WiFi模块的指令烧进去,然后去测试该模块是否是正常运行(因为他们的模块是可以单独出来,使用串口助手来通信)。而当我解决的板子的问题时候才发现,我的并不可以这样直接烧写,来检测模块是否正常工作,那么我就只能通过单片机来与WiFi模块通信检测是否正常工作。而我比较好运,我买的模块都是正常的,这也是我为什么在硬件准备的时候,准备多了一个串口三来帮助我打印信息,这样子方便了我的调试(个人建议,大家自己画的板子一定要留一个串口来打印,好方便自己调试的各种问题)。

调试过程:

        AT指令,我们调试的时候离不开的就是它,我建议大家在开始之前先了解熟悉一下这个WiFi模块的AT指令,这样子好方便我们调试(可以在安信可官网查到所有指令,也都有解释,当然也可以看一些大佬的博客,也是有很多大佬写的很清晰的)。

配置串口三,用于和串口打印

#if 1
//#pragma import(__use_no_semihosting)       
#pragma (__use_no_semihosting)   
//标准库需要支持的函数
struct __FILE 
{
  int handle;
};

FILE __stdout;       
//定义_sys_exit()以避免工作在半主机状态
void _sys_exit(int x) 
{ 
  x = x; 
} 
//重定义fputc函数
//这个需要根据MCU和我们希望printf从哪个串口输出来确认 __WAIT_TODO__
int fputc(int ch, FILE *f)
{
	//注意:USART_FLAG_TXE是检查发送缓冲区是否为空,这个要在发送前检查,检查这个提议提高发送效率,但是在休眠的时候可能导致最后一个字符丢失
	//USART_FLAG_TC是检查发送完成标志,这个在发送后检查,这个不会出现睡眠丢失字符问题,但是效率低(发送过程中发送缓冲区已经为空了,可以接收下一个数据了,但是因为要等待发送完成,所以效率低)
	//不要两个一起用,一起用效率最低
	
	//循环等待直到发送缓冲区为空(TX Empty)此时可以发送数据到缓冲区
  while (USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET)
  {}
  USART_SendData(USART3, (uint8_t) ch);

  /* 循环等待直到发送结束*/
  while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET){}
  return ch;
}
#endif 

void uart3_Init(u32 bound)//串口3  引脚为PB10  PB11
{

    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断向量分组:第2组 抢先优先级:0 1 2 3 子优先级:0 1 2 3		
	
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);   

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PB10
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //?′ó?í?íìê?3?
    GPIO_Init(GPIOB, &GPIO_InitStructure);


    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;//PB11
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO

    //Usart3 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级0
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;      //响应优先级0
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;         //USART_IRQn通道使能
    NVIC_Init(&NVIC_InitStructure); //初始化NVIC

    //USART3 配置
    USART_InitStructure.USART_BaudRate = bound;//波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//数据长度
    USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位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_ITConfig(USART3, USART_IT_RXNE, ENABLE);//配置了接收中断中断
	USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);//配置了总线空闲中断

    USART_Cmd(USART3, ENABLE);      //串口外设使能 (打印的测试串口)   
}

void USART3_IRQHandler( void )
{   
    u8 ucCh;

    if(USART_GetITStatus( USART3, USART_IT_RXNE ) != RESET ) //如果接收
    {
        ucCh  = USART_ReceiveData( USART3 );                                    
    }

    if( USART_GetITStatus( USART3, USART_IT_IDLE ) == SET )  //如果总线空闲
    {
        ucCh = USART_ReceiveData( USART3 );                                                             
    }   

}

配置串口二,用于和WiFi模块通信

void uart2_Init(u32 bound)//串口2  引脚为PA2  PA3
{
	USART_InitTypeDef USART_InitStructure;
	GPIO_InitTypeDef  GPIO_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断向量分组:第2组 抢先优先级:0 1 2 3 子优先级:0 1 2 3		
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	    //使能指定端口时钟
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);	//初始化GPIO
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOA, &GPIO_InitStructure);	//初始化GPIO
	
	//Usart2 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;      
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;         
    NVIC_Init(&NVIC_InitStructure); 

	
	//USART2配置
	USART_InitStructure.USART_BaudRate = bound;	                //设置串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;	//字长为8
	USART_InitStructure.USART_StopBits = USART_StopBits_1;	    //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_ITConfig(USART2, USART_IT_RXNE, ENABLE);//配置了接收中断中断
	USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);//配置了总线空闲中断
	
	USART_Init(USART2, &USART_InitStructure);     //配置USART参数
	USART_Cmd(USART2, ENABLE);                    //使能USART
}


void USART2_IRQHandler(void)
{   
    u8 ucCh;

    if(USART_GetITStatus( USART2, USART_IT_RXNE ) != RESET )
    {
        ucCh  = USART_ReceiveData( USART2 );	  
	    USART_SendData(USART3,ucCh); /需配置了串口三方可打印

        /**下面这一部分用于接收WiFi模块传回的信息存储**/
        if(ESP8266_Fram_Record_Struct .InfBit .FramLength < ( RX_BUF_MAX_LEN - 1 ) ) 
        {           
            ESP8266_Fram_Record_Struct .Data_RX_BUF[ ESP8266_Fram_Record_Struct .InfBit .FramLength ++ ]  = ucCh;   
        }                			
    }

    if( USART_GetITStatus( USART2, USART_IT_IDLE ) == SET ) //如果总线空闲
    {
	    u8 temp = 0;

		/**接收WiFi传输完成的标志位**/
        ESP8266_Fram_Record_Struct .InfBit .FramFinishFlag = 1;	

        ucCh = USART_ReceiveData( USART2 );                  //由软件序列清除中断标志位(先读USART_SR,然后读USART_DR),作用等同下面清除IDLE中断标志位的方式,只要读一次字节就能清除			  
		//USART_SendData(USART3,ucCh);                         //输出最后一个字节,测试时用于检测,可删除			
	    //USART_ClearFlag(USART2,USART_FLAG_IDLE);                //清除串口空闲中断标志位(两者选一)
	    //temp = USART2->SR;
        //temp = USART2->DR;
			
        /**作为AT指令设置过程中的一个检测手段**/
        TcpClosedFlag = strstr ( ESP8266_Fram_Record_Struct .Data_RX_BUF, "CLOSED\r\n" ) ? 1 : 0;
    } 
}

串口配置说明一下:无论是串口二还是串口三的配置,在网上都是已成熟的模板,我的只是其中一小个例子,大家也可以参考其他博主的配置,我需要解释一下就是空闲中断和接收中断。

①接收中断(作为信息开始的标志):因为串口的传输是按位传输的,所以我们的开始就是只要接收到就进入中断,然后每一次都存储在数组里(涉及到后面提取上位机通讯的信息)。

②空闲中断(作为信息结束的标志):因为要涉及到处理上位机的信息,那么什么时候处理,这时候就是空闲的时候出去标志位,然后返回到对应的处理函数去处理,可以保证信息接收的完整性和及时性。

注意:USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//配置了接收中断中断
           USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);//配置了总线空闲中断

           这两个家伙不可以这样子写:

           USART_ITConfig(USART2, USART_IT_RXNE|USART_ IT_IDLE, ENABLE),否则会出现导致空闲中断没有打开,当然大家可以尝试一下。

当配置完了,就可以进行AT指令的测试了,我们今天只需要测试到WiFi模块是否可以正常执行!


AT测试

我们可以了解到WiFi模块有三个模式:STA、AP、STA+AP,以STA来一步步演示测试。

首先解释下STA模式,这是个需要联网的,我们以电脑作为主机服务器,单片机作为客机服务器。

我先上代码,需要四个代码,这是基本配置,代码并非我所原创,已经是很久以前的了,不知道当时是哪位博主大大分享的,大家也可以参考其他大佬的代码。

esp8266.h

#ifndef __ESP8266_H
#define __ESP8266_H 			   
#include "stm32f10x.h"

#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#if defined ( __CC_ARM   )
#pragma anon_unions
#endif

//ESP8266模式选择
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;

#define ESP8266_RST_Pin          GPIO_Pin_4    //复位管脚
#define ESP8266_RST_Pin_Port     GPIOA    //复位 
#define ESP8266_RST_Pin_Periph_Clock  RCC_APB2Periph_GPIOA       //复位时钟

#define ESP8266_CH_PD_Pin     GPIO_Pin_5   //使能管脚
#define ESP8266_CH_PD_Pin_Port     GPIOA   //使能端口
#define ESP8266_CH_PD_Pin_Periph_Clock  RCC_APB2Periph_GPIOA                     //使能时钟


#define ESP8266_RST_Pin_SetH     GPIO_SetBits(ESP8266_RST_Pin_Port,ESP8266_RST_Pin)
#define ESP8266_RST_Pin_SetL     GPIO_ResetBits(ESP8266_RST_Pin_Port,ESP8266_RST_Pin)


#define ESP8266_CH_PD_Pin_SetH     GPIO_SetBits(ESP8266_CH_PD_Pin_Port,ESP8266_CH_PD_Pin)
#define ESP8266_CH_PD_Pin_SetL     GPIO_ResetBits(ESP8266_CH_PD_Pin_Port,ESP8266_CH_PD_Pin)


#define ESP8266_USART(fmt, ...)  USART_printf (USART2, fmt, ##__VA_ARGS__)    
#define PC_USART(fmt, ...)       printf(fmt, ##__VA_ARGS__)       //这是串口打印函数,串口1,执行printf后会自动执行fput函数,重定向了printf。



#define RX_BUF_MAX_LEN 1024       //最大字节数
extern struct STRUCT_USART_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;
    }; 
	
}ESP8266_Fram_Record_Struct;


//初始化和TCP功能函数
void ESP8266_Init(u32 bound);
void ESP8266_AT_Test(void);
bool ESP8266_Send_AT_Cmd(char *cmd,char *ack1,char *ack2,u32 time);
void ESP8266_Rst(void);
bool ESP8266_Net_Mode_Choose(ENUM_Net_ModeTypeDef enumMode);
bool ESP8266_JoinAP( char * pSSID, char * pPassWord );
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_SendString(FunctionalState enumEnUnvarnishTx, char * pStr, u32 ulStrLength, ENUM_ID_NO_TypeDef ucId );
bool ESP8266_UnvarnishSend ( void );
void ESP8266_ExitUnvarnishSend ( void );
u8 ESP8266_Get_LinkStatus ( void );
void USART_printf( USART_TypeDef * USARTx, char * Data, ... );

#endif

esp8266.c

#include "esp8266.h"
#include "usart.h"
#include "delay.h"
#include <stdarg.h>

struct STRUCT_USART_Fram ESP8266_Fram_Record_Struct = { 0 };  //定义了一个数据帧结构体
void ESP8266_Init(u32 bound)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(ESP8266_RST_Pin_Periph_Clock|ESP8266_CH_PD_Pin_Periph_Clock, ENABLE);

    GPIO_InitStructure.GPIO_Pin = ESP8266_RST_Pin;             
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     //复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    
    GPIO_Init(ESP8266_RST_Pin_Port, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = ESP8266_CH_PD_Pin;               
    GPIO_Init(ESP8266_CH_PD_Pin_Port, &GPIO_InitStructure);

    uart2_Init(bound); 
	  ESP8266_Rst();
}
//对ESP8266模块发送AT指令
// cmd 待发送的指令
// ack1,ack2;期待的响应,为NULL表不需响应,两者为或逻辑关系
// time 等待响应时间
//返回1发送成功, 0失败
bool ESP8266_Send_AT_Cmd(char *cmd,char *ack1,char *ack2,u32 time)
{ 
    ESP8266_Fram_Record_Struct .InfBit .FramLength = 0; //重新接收新的数据包
    ESP8266_USART("%s\r\n", cmd);
    if(ack1==0&&ack2==0)     //不需要接收数据
    {
    return true;
    }
    delay_ms(time);   //延时
		delay_ms(1000);
    ESP8266_Fram_Record_Struct.Data_RX_BUF[ESP8266_Fram_Record_Struct.InfBit.FramLength ] = '\0';
		
    printf("%s",ESP8266_Fram_Record_Struct .Data_RX_BUF);
    if(ack1!=0&&ack2!=0)
    {
        return ( ( bool ) strstr ( ESP8266_Fram_Record_Struct .Data_RX_BUF, ack1 ) || 
                         ( bool ) strstr ( ESP8266_Fram_Record_Struct .Data_RX_BUF, ack2 ) );
    }
    else if( ack1 != 0 )  //strstr(s1,s2);检测s2是否为s1的一部分,是返回该位置,否则返回false,它强制转换为bool类型了
        return ( ( bool ) strstr ( ESP8266_Fram_Record_Struct .Data_RX_BUF, ack1 ) );

    else
        return ( ( bool ) strstr ( ESP8266_Fram_Record_Struct .Data_RX_BUF, ack2 ) );

}


//复位重启
void ESP8266_Rst(void)
{
    ESP8266_RST_Pin_SetL;
    delay_ms(500); 
    ESP8266_RST_Pin_SetH;
}


//发送恢复出厂默认设置指令将模块恢复成出厂设置
void ESP8266_AT_Test(void)
{
    char count=0;
    delay_ms(1000); 
    while(count < 10)
    {
        if(ESP8266_Send_AT_Cmd("AT+RESTORE","OK",NULL,500)) 
        {
            printf("OK\r\n");
            return;
        }
        ++ count;
    }
}


//选择ESP8266的工作模式
// enumMode 模式类型
//成功返回true,失败返回false
bool ESP8266_Net_Mode_Choose(ENUM_Net_ModeTypeDef enumMode)
{
    switch ( enumMode )
    {
        case STA:
            return ESP8266_Send_AT_Cmd ( "AT+CWMODE=1", "OK", "no change", 2500 ); 

        case AP:
            return ESP8266_Send_AT_Cmd ( "AT+CWMODE=2", "OK", "no change", 2500 ); 

        case STA_AP:
            return ESP8266_Send_AT_Cmd ( "AT+CWMODE=3", "OK", "no change", 2500 ); 

        default:
          return false;
    }       
}


//ESP8266连接外部的WIFI
//pSSID WiFi帐号
//pPassWord WiFi密码
//设置成功返回true 反之false
bool ESP8266_JoinAP( char * pSSID, char * pPassWord)
{
    char cCmd [120];
	
    sprintf ( cCmd, "AT+CWJAP=\"%s\",\"%s\"", pSSID, pPassWord );
    return ESP8266_Send_AT_Cmd( cCmd, "OK", NULL, 5000 );
}

//ESP8266 多人连接使能
//enumEnUnvarnishTx  是否多连接,bool类型
//设置成功返回true,反之false
bool ESP8266_Enable_MultipleId (FunctionalState enumEnUnvarnishTx )
{
    char cStr [20];

    sprintf ( cStr, "AT+CIPMUX=%d", ( enumEnUnvarnishTx ? 1 : 0 ) );

    return ESP8266_Send_AT_Cmd ( cStr, "OK", 0, 500 );

}


//ESP8266 连接服务器
//enumE  网络类型
//ip ,服务器IP
//ComNum  服务器端口
//id,连接号,确保通信不受外界干扰
//设置成功返回true,反之fasle
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_Send_AT_Cmd ( cCmd, "OK", "ALREAY CONNECT", 4000 );

}


//透传使能
//设置成功返回true, 反之false
bool ESP8266_UnvarnishSend ( void )
{
    if (!ESP8266_Send_AT_Cmd ( "AT+CIPMODE=1", "OK", 0, 500 ))
        return false;

    return 
        ESP8266_Send_AT_Cmd( "AT+CIPSEND", "OK", ">", 500 );

}


//ESP8266发送字符串
//enumEnUnvarnishTx是否使能透传模式
//pStr字符串
//ulStrLength字符串长度
//ucId 连接号
//设置成功返回true, 反之false
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 );

        bRet = true;

    }

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

        else
            sprintf ( cStr, "AT+CIPSEND=%d", ulStrLength + 2 );

        ESP8266_Send_AT_Cmd ( cStr, "> ", 0, 1000 );

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

    return bRet;

}


//ESP8266退出透传模式
void ESP8266_ExitUnvarnishSend ( void )
{
    delay_ms(1000);
    ESP8266_USART( "+++" );
    delay_ms( 500 );    
}


//ESP8266 检测连接状态
//返回0:获取状态失败
//返回2:获得ip
//返回3:建立连接 
//返回4:失去连接 
u8 ESP8266_Get_LinkStatus ( void )
{
    if (ESP8266_Send_AT_Cmd( "AT+CIPSTATUS", "OK", 0, 500 ) )
    {
        if ( strstr ( ESP8266_Fram_Record_Struct .Data_RX_BUF, "STATUS:2\r\n" ) )
            return 2;

        else if ( strstr ( ESP8266_Fram_Record_Struct .Data_RX_BUF, "STATUS:3\r\n" ) )
            return 3;

        else if ( strstr ( ESP8266_Fram_Record_Struct .Data_RX_BUF, "STATUS:4\r\n" ) )
            return 4;       

    }

    return 0;
}

static char *itoa( int value, char *string, int radix )
{
    int     i, d;
    int     flag = 0;
    char    *ptr = string;

    /* This implementation only works for decimal numbers. */
    if (radix != 10)
    {
        *ptr = 0;
        return string;
    }

    if (!value)
    {
        *ptr++ = 0x30;
        *ptr = 0;
        return string;
    }

    /* if this is a negative value insert the minus sign. */
    if (value < 0)
    {
        *ptr++ = '-';

        /* Make the value positive. */
        value *= -1;

    }

    for (i = 10000; i > 0; i /= 10)
    {
        d = value / i;

        if (d || flag)
        {
            *ptr++ = (char)(d + 0x30);
            value -= (d * i);
            flag = 1;
        }
    }

    /* Null terminate the string. */
    *ptr = 0;

    return string;

} /* NCL_Itoa */


void USART_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 == 0x5c )  //'\'
        {                                     
            switch ( *++Data )
            {
                case 'r':                                     //回车符
                USART_SendData(USARTx, 0x0d);
                Data ++;
                break;

                case 'n':                                     //换行符
                USART_SendData(USARTx, 0x0a);   
                Data ++;
                break;

                default:
                Data ++;
                break;
            }            
        }

        else if ( * Data == '%')
        {                                     
            switch ( *++Data )
            {               
                case 's':                                         //字符串
                s = va_arg(ap, const char *);
                for ( ; *s; s++) 
                {
                    USART_SendData(USARTx,*s);
                    while( USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET );
                }
                Data++;
                break;

                case 'd':           
                    //十进制
                d = va_arg(ap, int);
                itoa(d, buf, 10);
                for (s = buf; *s; s++) 
                {
                    USART_SendData(USARTx,*s);
                    while( USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET );
                }
                     Data++;
                     break;
                default:
                     Data++;
                     break;
            }        
        }
        else USART_SendData(USARTx, *Data++);
        while ( USART_GetFlagStatus ( USARTx, USART_FLAG_TXE ) == RESET );

    }
}

tcp.h

#ifndef __TCP_H
#define __TCP_H 	

#include "stm32f10x.h"


/*
*以下参数需要用户自行修改才能测试用过
*/

#define User_ESP8266_SSID     "219"          //wifi名
#define User_ESP8266_PWD      "219219219"      //wifi密码

#define User_ESP8266_TCPServer_IP     "10.60.106.165"     //服务器IP
#define User_ESP8266_TCPServer_PORT   "8888"      //服务器端口号


extern volatile uint8_t TcpClosedFlag;  //连接状态标志
extern u8 connect_flag;
extern u8 res;
extern char str[100];
void ESP8266_STA_TCPClient_Test(void);


#endif

tcp.c

#include "tcp.h"
#include "usart.h"
#include "esp8266.h"
#include "delay.h"
#include "stdio.h"
#include "string.h"
#include "stm32f10x.h"

volatile u8 TcpClosedFlag = 0;
u8 connect_flag = 0;
u8 rv_buff = 0;
u8 res = 0;
char str[100]= {0};

void ESP8266_STA_TCPClient_Test(void)
{   
    ESP8266_AT_Test();
    printf("正在设置 ESP8266\r\n");
    ESP8266_Net_Mode_Choose(STA);
    while(!ESP8266_JoinAP(User_ESP8266_SSID, User_ESP8266_PWD));
    ESP8266_Enable_MultipleId ( DISABLE );
    while(!ESP8266_Link_Server(enumTCP, User_ESP8266_TCPServer_IP,     User_ESP8266_TCPServer_PORT, Single_ID_0));
    while(!ESP8266_UnvarnishSend());
    printf("\r\nTCP 设置完成");	
	  
	ESP8266_Fram_Record_Struct .InfBit .FramLength = 0;
    ESP8266_Fram_Record_Struct .InfBit .FramFinishFlag = 0;	
	
    sprintf (str,"HALLE WORLD" );//格式化发送字符串到TCP服务器
    ESP8266_SendString ( ENABLE, str, 0, Single_ID_0 ); 
    // USART_Cmd(USART3, DISABLE);//关闭打印测试端口,避免占用资源
	connect_flag = 2;	 	
	
    GPIO_SetBits(GPIOB,GPIO_Pin_0);
	delay_ms(1000);	
	GPIO_ResetBits(GPIOB,GPIO_Pin_0);
}

说明一下:延迟函数我没有上传,因为大家的配置都不一样!!

主要是以这一份代码,来梳理一下STA模式,以及后续MQTT需要连接服务器做准备。

①先看esp8266.h里面的两个宏定义:

#define ESP8266_USART(fmt, ...)    USART_printf (USART2, fmt, ##__VA_ARGS__)    
#define PC_USART(fmt, ...)               printf(fmt, ##__VA_ARGS__)     

第一个是串口二输出函数重定义,使得我们只需要通过ESP8266_USART(fmt, ...)即可通过串口二发送数据给WiFi模块,这个函数每一个人的写法都不一样(在esp8266.c中)。

第二个是串口三的重定向,简单来说我们只要执行printf就会自动执行fput函数,从而可以通过串口助手查看输出的信息(具体的重定向,大家可以看看其他博主的解释,因为keil版本的更替,在这个重定向上面,由于内核不同,每一次移植都会出现不同的bug)

②ESP8266_Send_AT_Cmd(...);该函数是执行单片机发送信息到WiFi模块功能,每个指令的发送都经过,该函数的每一个参数都有注释在代码里。

**①②两者缺一不可,写的方式可以有多种,但必须要有!!!

接下来那么我们开始尝试配置。

1.  ESP8266_AT_Test();这个函数是WiFi模块恢复出厂设置的函数,我们也可以用其检测esp8266是否正常工作。发送的指令是“AT+RESTORE”,WiFi模块的回复是“OK”。下面是串口打印出来数据,可以看到模块的信息,那证明可以没有问题。

 

2. ESP8266_Net_Mode_Choose(STA);该函数用于选择模式,这里我们选择STA模式。

    AT+CWMODE=1,WiFi模块的回复是“OK”。(1:STA,2:AP,3:STA+AP)

3. 开始进入正戏: while(!ESP8266_JoinAP(User_ESP8266_SSID, User_ESP8266_PWD));

   解释:  User_ESP8266_SSID wifi或热点的名字

             User_ESP8266_PWD wifi或热点的密码

   这一步是加入网络:AT+CWJAP=\"%s\",\"%s\",两个%s就是名字和密码。

   失败就不断的识别,一直不成功就一直识别:

             成功,返回OK:

4. ESP8266_Enable_MultipleId ( DISABLE );这一步是设置多人连接,简单直白就是,只能我自  己连。发送指令 AT+CIPMUX= (1或者0),WiFi模块的回复是“OK”。

5. 这一步就是重中之重了,连接服务器。

while(!ESP8266_Link_Server(enumTCP, User_ESP8266_TCPServer_IP, User_ESP8266_TCPServer_PORT, Single_ID_0));

解释:     enumTCP     网络传输层协议

             User_ESP8266_TCPServer_IP   IP地址

             User_ESP8266_TCPServer_PORT    端口号

             Single_ID_0   连接号

我们使用电脑的网络调试助手来模拟这个服务器

协议同样是TCP Server ,本地主机地址是大家自己电脑联网后的IPV4地址,端口号自行设置。

这三个分别就对应到代码里面的TCP,User_ESP8266_TCPServer_IP,User_ESP8266_TCPServer_PORT

只有IP是需要更改的,因为每一个用户都不同。

发送指令是AT+CIPSTART=%s,因为在代码里面已经将这三个都整合一起了

如图示:

当然同时要打开网络调试助手,否则无法连接上就会一直打印:

当然你打开了,同样也会连不上!!!(咆哮!!!!草!!!!)

它需要关闭你电脑的防火墙!!!!!!!!!!!!!!!!!!

我也解释不了为啥,当时调了N久,也是查看了很多博主,看到的一条留言说如果关掉了就可以连接上了!!

成功打印:

同时助手显示:

6. while(!ESP8266_UnvarnishSend());打开透传模式,简答来说就是网络调试助手发送什么就所 接收什么打印什么(固定的模式)。

成功:

随后就是TCP配置完成,从单片机发送一个"HELLO WORLD"到助手:

 同时也可以从网络调试助手发送信息到单片机并打印:

STA的设置基本到这里就已经完成了,如果需要到物联网连接云服务器,我们还需要很多的工作,而我最开始并没想使用物联网控制的,因为我并不需要提取信息,我只需要到上位机的控制信息,所以我最开始完成调试的版本是使用AP模式(热点模式)来控制的,因此接下来下一篇我会讲到我调试AP模式,以及使用可视化网站来制作一个简单的APP来控制单片机。

我只是个小白,此文只是想分享下自己在毕设调试过程中遇到的坑,仅供参考,如有错误,请各位大佬不吝赐教!!!

 

 

 

 

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【毕设调试一】WiFi模块esp8266的调试 的相关文章

  • 深度学习的GPU:深度学习中使用GPU的经验和建议

    向AI转型的程序员都关注了这个号 大数据挖掘DT数据分析 公众号 xff1a datadw 深度学习是一个计算需求强烈的领域 xff0c 您的GPU的选择将从根本上决定您的深度学习体验 在没有GPU的情况下 xff0c 这可能看起来像是等待

随机推荐

  • Git仓库集成到VScode

    前提是一种安装了Git xff0c 这里就不再介绍安装过程 xff0c 进入Git官网进行下载安装即可 这里用Gitee作为远程仓库演示 xff0c 首先在gitee上新建仓库 新建完毕 xff0c 生成了HTTPS地址 xff0c 复制该
  • Docker镜像打标签(Tag)和保存镜像的操作

    Docker镜像打Tag标签和保存镜像的操作 示例 xff1a docker tag 192 168 180 204 19080 ai box redis soc 1 0 eve redis soc 1 0 保存镜像 示例 xff1a do
  • 我的2013,梦在路上

    我的2013 xff0c 在路上 今年最后一次给姐姐打电话 xff0c 她在那里像我炫耀自己和爸爸妈妈一起跨年 xff0c 说1314的意义 xff0c 而我还在北京苦逼着 回想2013年对于我来说 xff0c 或许是不错的一年 这一年我进
  • MFC 的CList,CPtrList,CObList,CStringList 的用法之CList

    CList 类 C 43 43 中实现通用数据结构 在程序设计当中经常会出现使用同种数据结构的不同实例的情况 例如 在一个 程序中可以使用多个队列 树 图等结构来组织数据 同种结构的不同实例 也 许只在数据元素的类型或数量上略有差异 如果对
  • 事务是什么?

    事务 xff1a 简单来说 xff0c 事务就是几个操作要作为一个处理单元来完成 xff0c 要么全部完成 xff0c 要么全部不完成 事务可以是一条SQL语句 xff0c 也可以是多条SQL语句或者整个程序 事务日志 xff1a 重做日志
  • 各种加解密算法比较

    一 加密 算法介绍 对称加密算法 对称加密算法用来对敏感数据等信息进行加密 xff0c 常用的算法包括 xff1a DES xff08 Data Encryption Standard xff09 xff1a 数据加密标准 xff0c 速度
  • 系统提示缺少libltdl.so.3

    今天安装heartbeat pils 2 1 4 11 el5 i386 rpm时 xff0c 显示 因为重新安装的linux xff0c 所以以前的一些操作都丢失了 xff0c 安装了一大堆的开发工具 34 Development lib
  • 5 essential skills every Web Developer should have?

    The idea here is that most of us should already know most of what is on this list But there just might be one or two ite
  • 安装的虚拟机没有了VMnet1

    虚拟的东西终归时有其缺陷的 xff0c 大家安装好虚拟机之后 xff0c 网络适配器中是有VMnat1和VMnat8俩块网卡的 xff0c VMnat1负责主机域虚拟机的host only通信 xff0c 而VMnat8则负责和虚拟机的na
  • 未来已来

    虚拟现实 xff0c 又称VR xff08 virtualreality xff09 xff0c 是一种综合利用计算机图形系统和现实中各种接口设备 xff0c 在计算机上生成可交互的沉浸式环境的技术 xff0c 可以将虚拟世界和现实世界实现
  • mount:No medium found

    使用vmware时 xff0c 科技将iso作为系统的镜像 但是 xff0c 在配置yum源的时候 xff0c 可能会遇到这样的问题 究其原因 xff0c 是由于镜像文件未启动 解决方法 xff1a 右击 xff0c 点击连接 xff0c
  • 什么叫跨平台语言

    什么叫跨平台语言呢 xff1f 今天就个人理解简单谈一下 xff0c 还望指正 简单的说 xff0c 就像插座和插头 xff0c 这世界上有没有完全通用的插座呢 xff1f 没有 但是比如某家公司 xff0c 制作了插座和插头 xff0c
  • rpm包管理功能全解

    通常在linux系统中 xff0c 服务是要通过程序来提供的 xff0c 通过调用各种接口编译好之后的源码包文件 xff0c 需要使用rpm xff08 redhat package manager xff09 命令来安装并提供相应的服务
  • 加密

    lt div id 61 34 article content 34 class 61 34 article content clearfix csdn tracking statistics 34 data pid 61 34 blog
  • 国内代码托管平台Gitee(码云)的入门使用

    网址在这 gt gt gt 码云官网地址 中文代码托管平台 xff0c 英文不好的话 xff0c 使用github一定的障碍 xff0c 所有gitee是很好的选择 文章目录 一 新建仓库二 AndroidStudio使用码云 xff08
  • Docker

    1 环境准备 官方网址 xff1a https docs docker com engine install centos CentOS 7 虚拟机 环境查看 root 64 localhost cat etc centos release
  • Idea kafka 远程 debug

    1 kafka kafka run class sh 修改 xff0c 总共两处需要修改 xff1a mhbtest 64 localhost kafka 2 11 1 0 1 vim bin kafka run class sh if l
  • 高质量嵌入式Linux C编程 .pdf

    http 链接 xff1a https pan baidu com s 10MjISMt0nNeVWo3L 8VCaQ 提取码 xff1a mxhh
  • 来到CSDN

    刚到CSDN 的时候写过一篇关于来到CSDN 的博客 感觉表达不够清楚在此修改一下 写博客是进入提高班后老师要求的 xff0c 至于老师为什么让我们写博客大家可以看这里 博客是需要用心经营的 开始的时候一直在网易上写 xff0c 后来发现网
  • 【毕设调试一】WiFi模块esp8266的调试

    硬件说明 xff1a span style color fe2c24 strong 提示 xff1a strong span span style color 0d0016 主控芯片STM32F103C8T6 xff0c 与WiFi通信串口