STM32玩转物联网07-WIFI实验

news2024/11/24 18:36:08

前言
上一节我们学习了串口的简单使用,本节我们增加难度,做一个demo通过AT指令控制ESP8266,使用DMA方式接收ESP8266发来的数据,后续我们便开始通过ESP8266连接物联网云平台,敬请关注。
一、准备
1. ESP8266硬件准备
准备ESP8266模块,本实验使用便宜好用的ESP-01s模块,某宝只需要5RMB左右,很适合新手测试使用,ESP-01S默认支持AT指令,所谓AT指令是说WIFI厂商把复杂的TCP/IP协议坐在了ESP8266芯片内部了,单片机只需要通过串口发送AT+CMD形式的指令即可达到调用TCP/IP协议栈的功能,极大的降低了学习和开发成本。
在这里插入图片描述

2. ESP8266固件准备
更新AT MQTT固件,进入安信可官网,下载MQTT透传固件(固件号:1471),参考之前文章即可轻松完成烧录。
在这里插入图片描述

3. 硬件连接
注意串口2连接ESP01S模块,串口1连接串口工具
在这里插入图片描述

二、实例
1. 建立工程
将上节串口实验代码复制并修改名字为:7.WiFi,通过CubeMx关闭Usart2,打开Usart3,并设置DMA方法方式接收数据。
在这里插入图片描述
取消勾选USART3 global interrupt中断回调,打开DMA1 channel3 global interrupt。
在这里插入图片描述
Keil打开工程,查看stm32f1xx_it.c中相关配置如下:

/**
  * @brief This function handles DMA1 channel3 global interrupt.
  */
void DMA1_Channel3_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Channel3_IRQn 0 */

  /* USER CODE END DMA1_Channel3_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart3_rx);
  /* USER CODE BEGIN DMA1_Channel3_IRQn 1 */

  /* USER CODE END DMA1_Channel3_IRQn 1 */
}

按照下图顺序,创建Application/User/Driver文件夹。
在这里插入图片描述

同时在本地根目录中创建User文件夹,文件夹中创建esp8266.c和esp8266.h两个文件。
在这里插入图片描述

在Keil中Application/User/Driver处右击选择Add Existing Files to Group ‘Application/user/Driver’…选择上述创建的esp8266.c文件,将文件加入到工程。
在这里插入图片描述

将/User文件路径按照下图方式添加到工程,以便能访问到esp8266.h文件,
在这里插入图片描述

编辑esp8266.h内容如下,增加#ifndef #define #endif格式文件,防止因为重复包含头文件导致错误;重点关注Uart_Frame_Record_t结构体,该结构体内部访问共用体,希望结构体直接访问共用体内元素,需要增加#pragma anon_unions。

#ifndef __ESP8266_H
#define __ESP8266_H 

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


#pragma anon_unions


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

#define RX_BUF_MAX_LEN 1024       //最大字节数

//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;

//网络状态
typedef enum{
    WIFI_DISCONNECT= 0,
    WIFI_CONNECTED = 1,
    WIFI_GOT_IP = 2,
    WIFI_CLOUD_CONNECTED = 3,
    WIFI_CLOUD_FAILED = 4,
} ENUM_WIFI_STATUS_TypeDef;

typedef struct _Uart_Frame_Record_t   //数据帧结构体
{
    char Data_RX_BUF[RX_BUF_MAX_LEN];
    union 
    {
        __IO uint16_t InfAll;
    __IO uint16_t FramLength;
    }; 
    
}Uart_Frame_Record_t;

extern Uart_Frame_Record_t Uart_Frame_Record;

extern volatile int wifi_connect_status;


//初始化和TCP功能函数
void ESP8266_Init(uint32_t bound);
void ESP8266_ATE0(void);
bool ESP8266_Send_AT_Cmd(char *cmd,char *ack1,char *ack2,uint32_t time);
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_UnvarnishSend ( void );
void ESP8266_ExitUnvarnishSend ( void );
uint8_t ESP8266_Get_LinkStatus ( void );

uint8_t AT_Printf(char* fmt,...);
uint8_t ESP8266_SendCMD(uint8_t *cmd, uint8_t *ack, uint16_t waittime);
uint8_t ESP8266_SendData(uint8_t *tbuf, uint16_t len);
uint8_t ESP8266_APInit(char *name, char *password);
uint8_t ESP8266_STAInit(void);
uint8_t ESP8266_STAConnect(char *name, char *password);

#endif

esp8266.c中内容如下所示:

#include "esp8266.h"
#include "usart.h"

volatile int wifi_connect_status = 0;  

Uart_Frame_Record_t Uart_Frame_Record= { 0 };  //定义了一个数据帧结构体

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

    else
        return ( ( bool ) strstr ( Uart_Frame_Record.Data_RX_BUF, ack2 ) );
}

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

//发送恢复出厂默认设置指令将模块恢复成出厂设置
void ESP8266_AT_Restore(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 检测连接状态
//返回0:获取状态失败
//返回2:获得ip
//返回3:建立连接 
//返回4:失去连接 
uint8_t ESP8266_Get_LinkStatus ( void )
{
    if (ESP8266_Send_AT_Cmd( "AT+CIPSTATUS", "OK", 0, 500 ) )
    {
        if ( strstr ( Uart_Frame_Record.Data_RX_BUF, "STATUS:2\r\n" ) )
            return 2;

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

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

    }

    return 0;
}

//检测应答命令
static uint8_t* ESP8266_CheckCMD(uint8_t *str)
{
//    char *strx = 0;
//    if(Uart_Frame_Record.FramLength&0x8000)
//    { 
//        Uart_Frame_Record.Data_RX_BUF[Uart_Frame_Record.FramLength&0x7FFF] = 0;//添加结束符
//        strx = strstr((const char*)Uart_Frame_Record.Data_RX_BUF,(const char*)str);
//    }
//    return (uint8_t*)strx;
    return (uint8_t *)strstr((const char*)Uart_Frame_Record.Data_RX_BUF,(const char*)str);
}

// 串口2 PA2 TX  PA3 RX
void Dev_UART3SendStr(uint8_t* tbuf, uint16_t tlen, uint8_t tByte){  
    uint16_t i = 0,j = 0;

    if(tlen > 0)
    j =  tlen;
    else
        j = strlen((const char*)tbuf);

    for( i = 0; i < j; i++)
    {
        if((tByte>0)&&(i==2))
        {
        HAL_UART_Transmit(&huart3, &tByte, 1, 10);
        }
        
        HAL_UART_Transmit(&huart3, &tbuf[i], 1, 10);
    }
}

//cmd:发送的命令字符串
//ack:期待的应答结果,如果为空,则表示不需要等待应答
//返回值:0,发送成功  1,发送失败       
uint8_t ESP8266_SendCMD(uint8_t *cmd, uint8_t *ack,uint16_t waittime) 
{
    Uart_Frame_Record.FramLength = 0;
    memset((void *)Uart_Frame_Record.Data_RX_BUF, 0, sizeof(Uart_Frame_Record.Data_RX_BUF));

    Dev_UART3SendStr(cmd, 0, 0);
    if(ack&&waittime)
    {
        while(--waittime)
        {
            delay_ms(10);
            if(Uart_Frame_Record.FramLength&0x8000)
            {
                if(ESP8266_CheckCMD(ack))
                {
                     printf("AT: %s, ack: %s\r\n", cmd, Uart_Frame_Record.Data_RX_BUF);
                    Uart_Frame_Record.FramLength = 0;                    
                    return 0;  
                }
                else 
                {
                    Uart_Frame_Record.FramLength = 0;                    
                    return 1;
                }
            }
        }
        if(waittime==0) 
        {
        return 1;
        }
    }
    return 1;
}

uint8_t AT_Printf(char* fmt,...)
{
    uint8_t  tbuf[300] = {0};
    uint16_t j = 0;
    
    va_list ap;
    va_start(ap, fmt);
    vsprintf((char*)tbuf, fmt, ap);
    va_end(ap);

    j = strlen((const char*)tbuf);
    HAL_UART_Transmit(&huart3, tbuf, j, 10);

    return 0; 
}

uint8_t ESP8266_SendData(uint8_t *tbuf, uint16_t len)
{
    HAL_UART_Transmit(&huart3, tbuf, len, 10);
    
    return 0; 
}

// STA模式下获取本地IP
void ESP8266_GetLocalIP(uint8_t* ipbuf)
{
    uint8_t *p,*p1;
    
    if(ESP8266_SendCMD( (uint8_t *)"AT+CIFSR\r\n", (uint8_t *)"OK", 50))
    {
        ipbuf[0] = 0;
        return;
    }
    
    p  = ESP8266_CheckCMD((uint8_t *)"\"");
    p1 = (uint8_t *)strstr((const char*)(p+1),"\"");
    *p1=0;
    sprintf((char*)ipbuf,"%s",p+1);
}

//退出透传模式 0,退出成功; 1,退出失败
uint8_t ESP8266_QuitTrans(void)
{    
    ESP8266_SendData((uint8_t *)"+++", 3);
    delay_ms(500);                    //等待500ms
    
    return ESP8266_SendCMD((uint8_t *)"AT\r\n",(uint8_t *)"OK",20);//退出透传判断
}

//获取连接状态  0,未连接;1,连接成功.
uint8_t ESP8266_ConstaCheck(void)
{
    while(ESP8266_SendCMD((uint8_t *)"AT+CIPSTATUS\r\n",(uint8_t *)"OK",50));    
    return 0;
}

uint8_t ESP8266_APInit(char *name, char *password)
{
    uint8_t Sbuf[60] ={0};

    while(ESP8266_SendCMD((uint8_t *)"AT\r\n",(uint8_t *)"OK",200))
    {
        //退出透传
        ESP8266_QuitTrans();
        
        //关闭透传模式            
        ESP8266_SendCMD((uint8_t *)"AT+CIPMODE=0\r\n",(uint8_t *)"OK",200);
        delay_ms(1000);
    }
    
    // 关闭回显
    while(ESP8266_SendCMD((uint8_t *)"ATE0\r\n",(uint8_t *)"OK",200));       
    
    // 设置波特率
    while(ESP8266_SendCMD((uint8_t *)"AT+UART=115200,8,1,0,0\r\n",(uint8_t *)"OK",200));    
    delay_ms(10);

    // 设置WIFI AP模式
    while(ESP8266_SendCMD((uint8_t *)"AT+CWMODE=2\r\n",(uint8_t *)"OK",200));
    while(ESP8266_SendCMD((uint8_t *)"AT+RST\r\n",(uint8_t *)"OK",200));        
    
    // 延时4秒等待重启成功    
    delay_ms(1000); 
    delay_ms(1000);
    delay_ms(1000); 
    delay_ms(1000);
    
    memset(Sbuf, 0 , sizeof(Sbuf));
    sprintf((char*)Sbuf, "AT+CWSAP=\"%s\",\"%s\",1,4\r\n", name, password);
    while(ESP8266_SendCMD(Sbuf, (uint8_t *)"OK", 1000));
  
    #ifdef UDP_Mode
    while(ESP8266_SendCMD((uint8_t *)"AT+CIPMUX=0\r\n",(uint8_t *)"OK",200));       
    while(ESP8266_SendCMD((uint8_t *)"AT+CIPSTART=\"UDP\",\"255.255.255.255\",60156,42254,0\r\n",(uint8_t *)"OK",500));
    while(ESP8266_SendCMD((uint8_t *)"AT+CIPMODE=1\r\n",(uint8_t *)"OK",300));
    while(ESP8266_SendCMD((uint8_t *)"AT+CIPSEND\r\n",(uint8_t *)"OK",200));
    #endif    

    Uart_Frame_Record.FramLength = 0;
    memset((void *)Uart_Frame_Record.Data_RX_BUF, 0, sizeof(Uart_Frame_Record.Data_RX_BUF)); 
    return 0;
}

uint8_t ESP8266_STAConnect(char *name, char *password)
{
    uint8_t Sbuf[60] ={0};
    
    memset(Sbuf, 0 , sizeof(Sbuf));
    sprintf((char*)Sbuf,"AT+CWJAP=\"%s\",\"%s\"\r\n", name, password);   
    if(ESP8266_SendCMD( Sbuf, (uint8_t *)"WIFI GOT IP", 3000)){
        return 1;
    }
    
    return 0;
}

uint8_t ESP8266_STAInit(void)
{   
    // 延时2秒等待串口初始化完成 
    while(ESP8266_SendCMD((uint8_t *)"AT\r\n",(uint8_t *)"OK",200))
    {
        //退出透传
        ESP8266_QuitTrans();
        
        //关闭透传模式        
        ESP8266_SendCMD((uint8_t *)"AT+CIPMODE=0\r\n",(uint8_t *)"OK",200);
        delay_ms(800);
    }
    // 关闭回显
    while(ESP8266_SendCMD((uint8_t *)"ATE0\r\n",(uint8_t *)"OK",200));       
    
//    // 设置波特率
//    while(ESP8266_SendCMD((uint8_t *)"AT+UART=115200,8,1,0,0\r\n",(uint8_t *)"OK",200));

    delay_ms(10);
    
    // 设置WIFI STA模式
    while(ESP8266_SendCMD((uint8_t *)"AT+CWMODE=1\r\n",(uint8_t *)"OK",200));
//    while(ESP8266_SendCMD((uint8_t *)"AT+RST\r\n",(uint8_t *)"OK",200));     

    delay_ms(1000);
//    delay_ms(1000);
//    delay_ms(1000);
    
    return 1;  
}

2. 核心函数
main函数内容如下,调用__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE)启动串口空闲中断,使用HAL_UART_Receive_DMA函数将Uart_Frame_Record.Data_RX_BUF设置为DMA接收存储区。

/* USER CODE BEGIN PV */

uint8_t  Wssid[20]     = "ioter";
uint8_t  Wpassword[20] = "1234567980";
/* USER CODE END PV */

int main(void)
{
    /* USER CODE BEGIN 1 /int ret = 0;/ USER CODE END 1 */
    /* MCU Configuration--------------------------------------------------------*/
    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();
    /* USER CODE BEGIN Init */
    /* USER CODE END Init */
    /* Configure the system clock */
    SystemClock_Config();
    /* USER CODE BEGIN SysInit */
    /* USER CODE END SysInit */
    /* Initialize all configured peripherals /MX_GPIO_Init();MX_DMA_Init();MX_USART1_UART_Init();MX_USART3_UART_Init();/ USER CODE BEGIN 2 */
    printf("start application\r\n");
    // 使能空闲中断
    __HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
    HAL_UART_Receive_DMA(&huart3, (uint8_t *)Uart_Frame_Record.Data_RX_BUF, RX_BUF_MAX_LEN);  // 启动DMA接收
    // 连接路由器
    ESP8266_STAInit();
    delay_ms(1000);
    if (wifi_connect_status != WIFI_GOT_IP)
    {
        while(ESP8266_STAConnect((char *)Wssid, (char *)Wpassword));
    }
    /* USER CODE END 2 */
    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
        /* USER CODE END WHILE */
        
        /* USER CODE BEGIN 3 */
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
        printf("reset gpio!\r\n");
        HAL_Delay(1000); 
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
        printf("set gpio!\r\n");
        HAL_Delay(1000);        
    }
    return ret;
  /* USER CODE END 3 */
}

USART3_IRQHandler函数如下, 当串口接收完一帧数据后,会触发空闲中断,中断回调函数检测到空闲中断被触发,停止DMA接收数据,置位Uart_Frame_Record.FramLength,然后对接收的数据处理,通过wifi_event_cb函数将WIFI连接状态传送出去。

void USART3_IRQHandler(void)
{
    if(__HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE) != RESET)  // 空闲中断标记被置位
    {
    __HAL_UART_CLEAR_IDLEFLAG(&huart3);  // 清除中断标记
    HAL_UART_DMAStop(&huart3);           // 停止DMA接收
    Uart_Frame_Record.FramLength = RX_BUF_MAX_LEN - __HAL_DMA_GET_COUNTER(huart3.hdmarx);  // 总数据量减去未接收到的数据量为已经接收到的数据量
    Uart_Frame_Record.Data_RX_BUF[Uart_Frame_Record.FramLength] = 0;  // 添加结束符     
    Uart_Frame_Record.FramLength |= 1 << 15;
    if (strstr(Uart_Frame_Record.Data_RX_BUF, "WIFI CONNECTED"))
    {
        wifi_event_cb(WIFI_CONNECTED);
    }else if(strstr(Uart_Frame_Record.Data_RX_BUF, "WIFI DISCONNECT"))
    {
        wifi_event_cb(WIFI_DISCONNECT);
    }else if(strstr(Uart_Frame_Record.Data_RX_BUF, "WIFI GOT IP"))
    {
        wifi_event_cb(WIFI_GOT_IP);
    }
    HAL_UART_Receive_DMA(&huart3, (uint8_t *)Uart_Frame_Record.Data_RX_BUF, RX_BUF_MAX_LEN);  // 重新启动DMA接收
    }
}

接着我们看下ESP8266_SendCMD函数,该函数实现发送自定义AT指令,并根据结果判断AT指令是否执行成功,该函数为本节核心内容,函数首先清空缓存,然后调用串口发送函数将AT指令发送出去,然后进入while(–waittime)等待超时,如果正常收到数据Uart_Frame_Record.FramLength最高位会被置位,判断是否接收到ack数据。

//cmd:发送的命令字符串
//ack:期待的应答结果,如果为空,则表示不需要等待应答
//返回值:0,发送成功  1,发送失败       
uint8_t ESP8266_SendCMD(uint8_t *cmd, uint8_t *ack,uint16_t waittime) 
{
    Uart_Frame_Record.FramLength = 0;
    memset((void *)Uart_Frame_Record.Data_RX_BUF, 0, sizeof(Uart_Frame_Record.Data_RX_BUF));

    Dev_UART3SendStr(cmd, 0, 0);
    if(ack&&waittime)
    {
        while(--waittime)
        {
            delay_ms(10);
            if(Uart_Frame_Record.FramLength&0x8000)
            {
                if(ESP8266_CheckCMD(ack))
                {
                     printf("AT: %s, ack: %s\r\n", cmd, Uart_Frame_Record.Data_RX_BUF);
                    Uart_Frame_Record.FramLength = 0;                    
                    return 0;  
                }
                else 
                {
                    Uart_Frame_Record.FramLength = 0;                    
                    return 1;
                }
            }
        }
        if(waittime==0) 
        {
        return 1;
        }
    }
    return 1;
}

3. 程序执行
编译后下载程序,打开路由器或者设置WIFI热点,ioter:1234567980, 注意不要选择5G,ESP8266仅2.4G,串口1打印数据如下所示,看到WIFI GOT IP说明设备联网成功,并且得到了DHCP分配的IP地址。
在这里插入图片描述

三、小结
如您在使用过程中有任何问题,请加QQ群进一步交流。

QQ交流群:573122190 (备注:物联网项目交流)

小叶老师出品:种一棵树最好的时间是十年前,其次是现在!

完整代码获取:
https://download.csdn.net/download/weixin_45006076/89472819

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1852839.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[图解]建模相关的基础知识-16

1 00:00:00,350 --> 00:00:04,130 刚才那个&#xff0c;就相当于&#xff0c;12这个我们可以认为是什么 2 00:00:05,020 --> 00:00:11,360 我们用类图来表达就是&#xff0c;员工、电话 3 00:00:13,320 --> 00:00:15,080 多个 4 00:00:15,090 --> 00:00:16,440 …

stm32使用time模块输出pwm波,stm32-matlab开发电机控制

simulink: stm32cubemx : 注意在stm32配置了两路的一个互补输出&#xff0c;但实际上在matlab里只需要给定占空比就行了&#xff0c;他会自动输出互补&#xff0c;驱动电机&#xff0c;这是因为有点的电机输出需要6路&#xff0c;有的只需要1路&#xff0c;我们看下图就知道了…

Vue.JS中如何监听生命周期事件?

目录 一、Vue.JS框架介绍二、Vue.JS的监听事件三、Vue.JS的生命周期事件四、Vue.JS中如何监听生命周期事件 一、Vue.JS框架介绍 Vue.js是一个用于构建用户界面的渐进式JavaScript框架。它设计得非常灵活&#xff0c;可以轻松地被集成到现有的项目中&#xff0c;也可以作为一个…

能正常执行但是 cion 标红/没有字段提示

ctrl q 退出 clion 找到工程根目录&#xff0c;删除隐藏文件 .idea 再重新打开 clion 标红消失&#xff0c;同时再次输入函数/类属性&#xff0c;出现字段提示 clion 的智能提示方案存储在 .idea 文件中&#xff0c;如果工程能够正常编译执行&#xff0c;那么说明是智能提示…

ARM32开发--WDGT看门狗

知不足而奋进 望远山而前行 目录 文章目录 前言 目标 内容 什么是看门狗 ARM中的看门狗 独立看门狗定时器 窗口看门狗定时器 独立看门狗FWDGT 初始化配置 喂狗 完整代码 窗口看门狗WWDGT 初始化配置 喂狗 完整代码 注意 总结 前言 嵌入式系统在如今的科技发…

程序猿大战Python——面向对象——私有权限

私有属性 目标&#xff1a;掌握私有属性的使用。 为了更好的限制属性的访问和包含隐私&#xff0c;可以给属性设置私有权限。 当把属性设置为私有属性后&#xff0c;则该属性只能被本类直接访问。 定义私有属性语法&#xff1a; self.__属性名 设置和获取私有属性值语法&am…

云计算期末综合测试题

云计算综合测试题 单选题填空题判断题简答题 单选题 这里选择题&#xff0c;直接以填空题展示&#xff0c;并给出解析 Bigtable是&#xff08;Google&#xff09;开发的分布式存储系统 解析&#xff1a;分布式结构化数据表Bigtable是Google基于GFS和Chubby开发的分布式存储系统…

Redis 学习笔记(2)

目录 1 Redis的持久化1.1 RDB持久化方案1.2 AOF持久化方案 2 Redis架构2.1 主从复制架构2.2 哨兵集群设计2.3 哨兵集群设计 3 Redis事务机制4 Redis过期策略与内存淘汰机制4.1 过期策略4.2 内存淘汰机制 5 Redis高频面试题4.1 缓存穿透4.2 缓存击穿4.3 缓存雪崩 1 Redis的持久化…

2. 数据结构分析即索引库的crud

1. 数据库脚本 DROP TABLE IF EXISTS tb_hotel; CREATE TABLE tb_hotel (id bigint(0) NOT NULL,name varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT COMMENT 酒店名称,address varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_090…

FlowUs2024重磅革新预告:RAG技术赋能『问问AI』,笔记变现新纪元等你开启!

&#x1f389; 在FlowUs的广阔天地间&#xff0c;知识的边界被无限拓展&#xff0c;引领着一场场创新与收获的庆典&#xff01;&#x1f680; 随着一年间不断的精进与革新&#xff0c;FlowUs与众多用户并肩前行&#xff0c;在追求极致体验的道路上迈出坚实步伐。步入2024年&am…

Javase.图书管理系统基本框架

图书管理系统基本框架 1.核心类介绍2. book包详解2.1 Book 类2.1.2 代码展示2.1.2 代码解析 2.2 BookList 类2.2.2 代码展示2.2.2 代码解析 2.3Book类和BookList类的联系 3. 用户角色与管理3.1 User 类3.1.1 代码展示3.1.2 代码解析 3.2 adminUser 类3.2.1 代码展示3.2.2代码解…

学生选课管理系统(JAVA课设)PS:有前端界面

1.课设要求描述 实现系统的所有功能&#xff0c;包括但不限于&#xff1a; 学生信息管理&#xff08;增加、删除、修改、查询&#xff09;课程信息管理选课操作成绩管理 2.制作思路及基础讲解 此项目主要是用于完成大二下半学期的JAVA大作业&#xff0c;也可当作课设&…

Ubuntu:解决github出现 Permission denied (publickey)的问题

因为使用的Ubuntu 长久没有使用&#xff0c;使用下载的时候突然报错&#xff0c;使用ssh key这种方式进行clone &#xff0c;pull github上面的项目&#xff0c;使用 git clone或者git pull origin master出现permission denied (publickey)&#xff0c;原因是因为ssh key过期失…

leetcode 二分查找·系统掌握 x的平方根

题目&#xff1a; 题解 这题可以使用~01~泛型查找在0~x/2的范围内查找答案。 int mySqrt(int x) {long l0,rx,mid;while(l<r){mid(lr1)>>1;if(mid*mid>x)rmid-1;else lmid;}//因为一定有答案所以不用判定是否查找失败return l;}

计算机系统基础实训六-ShellLab实验

实验目的与要求 1、让学生更加理解进程控制的概念和具体操作方法&#xff1b; 2、让学生更加理解信号的概念和具体使用方法&#xff1b; 3、让学生更加理解Unix shell程序的原理和实现方法&#xff1b; 实验原理与内容 shell是一种交互式的命令行解释器&#xff0c;能代表…

从关键新闻和最新技术看AI行业发展(2024.6.3-6.16第二十五期) |【WeThinkIn老实人报】

写在前面 【WeThinkIn老实人报】旨在整理&挖掘AI行业的关键新闻和最新技术&#xff0c;同时Rocky会对这些关键信息进行解读&#xff0c;力求让读者们能从容跟随AI科技潮流。也欢迎大家提出宝贵的优化建议&#xff0c;一起交流学习&#x1f4aa; 欢迎大家关注Rocky的公众号&…

Node.js 系列之 Express 框架入门实战教程

目录 1 Node.js是什么2 Express初体验3 Express 路由3.1 什么是路由3.2 路由的使用3.3 获取路由参数 4 常见响应设置4.1 express 响应方法4.2 原生响应方法 5 express 中间件5.1 中间件作用5.2 中间件类型5.3 全局中间件5.4 路由中间件 6 获取请求体数据7 路由模块化 1 Node.js…

51单片机学习记录(二)————外部中断

文章目录 前言一、中断1.中断的定义 二、51中断1.中断源2.中断的配置3.中断允许控制寄存器4.中断触发方式51中断有两种触发方式&#xff1a; 三、外部中断1.外部中断引脚2.外部中断配置&#xff08;以外部中断0为例&#xff09; 总结 前言 一个学习嵌入式的小白~ 有错误评论区…

【ARM】PK51如何将BL51链接器切换成LX51链接器

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 解决客户在使用PK51进行项目研发的时候&#xff0c;想要使用LX51链接器进行使用。 2、 问题场景 客户在使用51芯片进行开发的时候&#xff0c;发现工程中使用的是BL51链接器&#xff0c;而不是LX51链接器&#xff…

【非常实验】如何在移动设备上运行 Docker?

本章就从在 DevOps 中最基本但也是最强大的工具 Docker 开始。最近,我在尝试更多Termux的可能性,于是就想着试试Docker适不适合arm架构。 我用的是天玑9000芯片,而不是高通,所以显示不出来 Qualcomm。所以我决定从在手机上运行 docker 开始,但这可能吗?让我们一起来看看吧…