RT-thread(10) cubeMX+RT-Thread nano +Keil环境下,应用AT指令操作WIFI8266实现通信

WIFI模块程序设计步骤

分为三步:

1、组网

方式1: WIFI模块作为热点,手机或PC接入此热点; 方式2:手机开热点,WIFI模块与PC机接入;方式3:WIFI路由器当“热点”用,WIFI模块、手机及PC机接入。

2、建链接  分为TCP/IP 和UDP等方式,设1个服务器,其他客户端,必须在同一个网内(使用同一个热点)

3、传通信帧,实现应用功能。

 

ESP8266笔记 ---- 常用AT指令

本段引用来源:ESP8266笔记 ---- 常用AT指令

一般来说,ESP8266 模块默认的波特率是115200,指令以\r\n结尾,所以本文的所有指令都要使用\r\n,后面就不重复了。
还有,发送AT命令后,返回OK即认为设置成功。

一、测试指令

AT(这里应该输入 AT\r\n,为了不繁琐,后面就省略了\r\n,应用时一定要自己加)
返回 OK,说明模块正常。

二、开启/关闭回显(关系到模块是否回发发送的内容)

ATE1 – 打开回显
ATE0 – 关闭回显

三、设置 WIFI 模式

3.1、AP 模式(可以理解为热点,别的设备可以连接)

首先,设置为AP 模式: AT+CWMODE=2
配置热点信息: AT+CWSAP="Eureka1024","123456789",1,3 ---- 热点名为 Eureka1024,密码为 123456789,使用通道 1,加密方式为 WPA2_PSK。

这时候别的设备就可以连接该模块了。

3.2、Station 模式(最常用的模式,用来连接热点,从而接入外部网络)

设置为Station 模式:AT+CWMODE=1 。
连接到指定路由器:
AT+CWJAP_DEF="PiWiFi","12345678" 。//wifi名wifi密码

如果遇到同名的热点,可以通过增加 MAC地址 (下面命令的最后一串字符) 的方式,比如

AT+CWJAP_DEF="PiWiFi","12345678","db:d7:18:d5:a7:54"

在这里插入图片描述

获取 IP 地址:AT+CIPSTA?
在这里插入图片描述

设置模块开机自动连接热点:AT+CWAUTOCONN=1,模块上电后就会自动连接之前连接过的路由器(存储过名字和密码)。

扫描附近的 AP 热点信息:AT+CWLAP,包括的信息有(热点名,热点 MAC 地址,加密方式等),由于串口助手显示中文乱码,所以显示很奇怪,不过还好,都不用打码了,哈哈。
在这里插入图片描述

断开连接:AT+CWQAP
在这里插入图片描述

四、 简单的网络通信

4.1、简单的实现连接

在PC机上,打开网络调试助手,选择 TCP Server,会自动生成本地主机地址和本地主机端口号,点击打开,如下图所示:
在这里插入图片描述

接着在PC机上使用串口助手对 ESP8266 模块进行操作(电路已连接,PC机插上USB转串口模块,USB转串口模块接WIFI模块)。(前提是模块已经连接 wifi 成功,注意与PC机使用相同的”WIFI热点“,或则PC机接入工作在AP模式的WIFI模块”热点“
首先,建立 TCP 连接,(可能会失败,可以按这个来设置ESP8266 TCP ERROR CLOSED的常见原因及解决办法,连手机的热点就可以,连电脑生成的热点就失败,不知道为啥,最好多试几次)
串口助手输入如下指令:
AT+CIPSTART="TCP","192.168.0.107",8080
连接成功:
在这里插入图片描述
之后,使用键入指令: AT+CIPSEND=10,其中10指的是这次发送数据的长度。
串口助手界面显示 >,然后发送数据即可:
在这里插入图片描述
这是网络调试助手会接收到数据:
在这里插入图片描述
此时再通过 TCP Server 发送“hello,boy”,
在这里插入图片描述
串口接收端会打印 Client 收到的数据,结果如下图:
在这里插入图片描述

4.2、透传模式

前面在使用 TCP 进行数据发送时,在每次发送数据之前都要指定发送数据的长度,这样很不方便进行数据的处理。

使用透传模式可以解决这个问题。

使用 AT+CIPMODE=1 指令开启透传模式,开启透传模式后只需要在第一次发送数据时使用 AT+CIPSEND 指令来告诉 ESP8266 开始透传发送,随后我们直接发送想要的数据即可,如下操作所示:
在这里插入图片描述
此时你会看到接收到的数据:
在这里插入图片描述
若要结束透传模式,要有两个步骤:
1、发送 +++ 这三个字符,值得注意的是该命令没有换行符,不然后失败。
2、接着发送 AT+CIPMODE=0 命令即可退出透传模式,即回到正常的模式。

4.3、使用 SmartConfig 为设备配网

未成功,后补充

4.4、获取网络时间

先使用 AT+CIPSNTPCFG=1,8 来设置时区,这里设置的是 东八区
然后使用 AT+CIPSNTPTIME? 来获取网络时间,如下所示:
在这里插入图片描述

 WIFI模块的模式及设置AT指令如下

TCP通信测试:
(
1) STA模式: 1.ESP8266 Client: -> AT+CWMODE=1 //设为STA模式 -> AT+CWJAP_DEF="FAE","123456789." //连接无线网络Wi-Fi -> AT+CIPSTART="TCP","192.168.0.116",8888 //连接服务端 -> AT+CIPSEND=5 //启动发送 2.ESP8266 Server: -> AT+CWMODE=1 //设置为 STA 模式 -> AT+CWJAP_DEF="FAE","123456789." //连接无线网络 wifi -> AT+CIPMUX=1 //开启多连接 -> AT+CIPSERVER=1,5000 //开启服务端 -> AT+CIFSR //查看 mac 地址和 IP -> AT+CIPSEND=0,5 //启动发送 (2)AP模式 ESP8266 Client: -> AT+CWMODE=2 //设置为AP模式 -> AT+CWSAP="ESP8266","12345678",11,3 //开启wifi热点 -> AT+CIPSTART="TCP","192.168.4.2",8888 //连接服务端 -> AT+CIPSEND=5 //启动发送 ESP8266 Server: -> AT+CWMODE=2 //设为AP模式 -> AT+CWSAP="ESP8266","12345678",11,3 //开启wifi热点 -> AT+CIPMUX=1 //开启多连接 -> AT+CIPSERVER=1,5000 //开启服务端 -> AT+CIFSR -> AT+CIPSEND=0,5

 

根据电路连接,cubeMX配置STM32G070RB,配置RT-thread nano

按照WIFI模块作为热点,作为TCP Server 的代码设计

AT指令操作WIFI模块的硬件驱动函数  wifi8266_uart_bsp.h

/**
 ****************************************************************************************************
 * @file        wifi8266_uart_bsp.h
 * @author      
 * @version     V1.0
 * @date        2023-02-22
 * @brief       
 * @license    
 ****************************************************************************************************
 * @attention
 *        AT指令操作WIFI模块的硬件驱动函数
 *
 ****************************************************************************************************
 */

#ifndef __WIFI8266_UART_BSP_H
#define __WIFI8266_UART_BSP_H

//#include "./SYSTEM/sys/sys.h"
#include "stm32g0xx_hal.h"
/**
 *是否使用RT—thread操作系统
 * WIFI_USING_RT_THREAD_OS用于定义系统文件夹是否支持OS
 * 0,不支持OS
 * 1,支持OS
 */
#define  WIFI_USING_RT_THREAD_OS 1
/* 如果使用os,则包括下面的头文件即可. */
#if WIFI_USING_RT_THREAD_OS
#include <rtthread.h> /* os 使用 */
#endif

/* 引脚定义 */
#define WIFI8266_UART_TX_GPIO_PORT           GPIOD
#define WIFI8266_UART_TX_GPIO_PIN            GPIO_PIN_5
#define WIFI8266_UART_TX_GPIO_CLK_ENABLE()   do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0) /* PC口时钟使能 */

#define WIFI8266_UART_RX_GPIO_PORT           GPIOD
#define WIFI8266_UART_RX_GPIO_PIN            GPIO_PIN_6
#define WIFI8266_UART_RX_GPIO_CLK_ENABLE()   do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0) /* PD口时钟使能 */

#define WIFI8266_UART_INTERFACE              USART2
#define WIFI8266_UART_IRQn                   USART2_IRQn
#define WIFI8266_UART_IRQHandler             USART2_IRQHandler
#define WIFI8266_UART_CLK_ENABLE()           do{ __HAL_RCC_UART5_CLK_ENABLE(); }while(0) /* UART5时钟使能 */

/* UART收发缓冲大小 */
#define WIFI8266_UART_RX_BUF_SIZE            256
#define WIFI8266_UART_TX_BUF_SIZE            256

#ifdef WIFI_USING_RT_THREAD_OS
/*防止多个线程访问WIFI,调用wifi接口函数时使用*/
void wifi8266_lock(void); //获取互斥量
void wifi8266_unlock(void); //释放互斥量
/*用于接收帧处理函数与接收中断通信*/
//uint8_t wifi8266_wait_event(rt_int32_t timeout);//已接收帧,但串口没有接收到新的字节。等待这种情况
uint8_t wifi8266_take_sem(rt_int32_t timeout);//已接收帧,但串口没有接收到新的字节。等待这种情况
#endif
/* 操作函数 */
void wifi_delay_ms(uint16_t n);                  /*等待WIFI8266应答的延时函数*/
void wifi8266_uart_printf(char *fmt, ...);       /* WIFI8266 UART printf */
void wifi8266_uart_rx_restart(void);             /* WIFI8266 UART重新开始接收数据 */
uint8_t *wifi8266_uart_rx_get_frame(void);       /* 获取WIFI8266 UART接收到的一帧数据 */
uint16_t wifi8266_uart_rx_get_frame_len(void);   /* 获取WIFI8266 UART接收到的一帧数据的长度 */
void wifi8266_uart_init(uint32_t baudrate);      /* WIFI8266 UART初始化 */

#endif
View Code

 AT指令操作WIFI模块的硬件驱动函数  wifi8266_uart_bsp.c 

/**
 ****************************************************************************************************
 * @file        wifi8266_uart_bsp.h
 * @author      
 * @version     V1.0
 * @date        2023-02-22
 * @brief       
 * @license    
 ****************************************************************************************************
 * @attention
 *        AT指令操作WIFI模块的硬件驱动函数
 *
 ****************************************************************************************************
 */
#include <wifi8266_uart_bsp.h>

#include "usart.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>


static UART_HandleTypeDef g_uart_handle;                    /* WIFI8266 UART */

static struct
{
    uint8_t buf[WIFI8266_UART_RX_BUF_SIZE];              /* 帧接收缓冲 */
    struct
    {
        uint16_t len    : 15;                               /* 帧接收长度,sta[14:0] */
        uint16_t finsh  : 1;                                /* 帧接收完成标志,sta[15] */
    } sta;                                                  /* 帧状态信息 */
} g_uart_rx_frame = {0};                                    /* WIFI8266 UART接收帧缓冲信息结构体 */


static uint8_t g_uart_tx_buf[WIFI8266_UART_TX_BUF_SIZE]; /* WIFI8266 UART发送缓冲 */

#if WIFI_USING_RT_THREAD_OS
///* 定义事件控制块(句柄) 及代码*/
//static rt_event_t     WIFI8266_IT_event = RT_NULL;// 中断向通信线程发送的事件句柄
//#define WIFI8266_RX_IT_EVENT   (0x0001 << 0) //设置事件掩码的位0  WIFI8266发来的数据RX中断触发 接收结束事件
/* 定义信号量*/
rt_sem_t WIFI8266_RX_IT_sem = RT_NULL;
/* 定义互斥量控制块 */
static rt_mutex_t WIFI8266_usart_mux = RT_NULL;   // 防止多个线程同时使用WIFI及相应的串口外设


#endif

/**
 * @brief       等待WIFI8266应答的延时函数
 * @param       无
 * @retval      无
 */
//static 
void delay_one_ms(void)/*等待WIFI8266应答的延时函数*/
{
    uint16_t i;
    /* 
         下面的时间是通过逻辑分析仪测试得到的。
    工作条件:CPU主频72MHz ,MDK编译环境,1级优化
  
        循环次数为10时,SCL频率 = 205KHz 
        循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us 
         循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us 
    */
    for (i = 0; i < 1366; i++);
}
//__weak 
void wifi_delay_ms(uint16_t n)
{
      uint16_t i;
        for (i = 0; i < n; i++)
           delay_one_ms();
}
/**
 * @brief       WIFI8266 UART printf
 * @param       fmt: 待打印的数据
 * @retval      无
 */
void wifi8266_uart_printf(char *fmt, ...)
{
    va_list ap;//定义一具VA_LIST型的变量,包括 #include <stdarg.h>
    uint16_t len;
    
    va_start(ap, fmt);//用VA_START宏初始化变量刚定义的VA_LIST变量
    vsprintf((char *)g_uart_tx_buf, fmt, ap);//具VA_LIST型的变量
    va_end(ap);//最后用VA_END宏结束可变参数的获取
    
    len = strlen((const char *)g_uart_tx_buf);
    HAL_UART_Transmit(&g_uart_handle, g_uart_tx_buf, len, HAL_MAX_DELAY);
}

/**
 * @brief       WIFI8266 UART重新开始接收数据
 * @param       无
 * @retval      无
 */
void wifi8266_uart_rx_restart(void)
{
    g_uart_rx_frame.sta.len     = 0;
    g_uart_rx_frame.sta.finsh   = 0;
}

/**
 * @brief       获取WIFI8266 UART接收到的一帧数据
 * @param       无
 * @retval      NULL: 未接收到一帧数据
 *              其他: 接收到的一帧数据
 */
uint8_t *wifi8266_uart_rx_get_frame(void)
{
    if (g_uart_rx_frame.sta.finsh == 1)
    {
        g_uart_rx_frame.buf[g_uart_rx_frame.sta.len] = '\0';//末尾加字符串结束符
        return g_uart_rx_frame.buf;
    }
    else
    {
        return NULL;
    }
}

/**
 * @brief       获取WIFI8266 UART接收到的一帧数据的长度
 * @param       无
 * @retval      0   : 未接收到一帧数据
 *              其他: 接收到的一帧数据的长度
 */
uint16_t wifi8266_uart_rx_get_frame_len(void)
{
    if (g_uart_rx_frame.sta.finsh == 1)
    {
        return g_uart_rx_frame.sta.len;
    }
    else
    {
        return 0;
    }
}

/**
 * @brief       WIFI8266 UART初始化
 * @param       baudrate: UART通讯波特率
 * @retval      无
 */
void wifi8266_uart_init(uint32_t baudrate)
{
    g_uart_handle.Instance          = WIFI8266_UART_INTERFACE;   /* WIFI8266 UART */
    g_uart_handle.Init.BaudRate     = baudrate;                     /* 波特率 */
    g_uart_handle.Init.WordLength   = UART_WORDLENGTH_8B;           /* 数据位 */
    g_uart_handle.Init.StopBits     = UART_STOPBITS_1;              /* 停止位 */
    g_uart_handle.Init.Parity       = UART_PARITY_NONE;             /* 校验位 */
    g_uart_handle.Init.Mode         = UART_MODE_TX_RX;              /* 收发模式 */
    g_uart_handle.Init.HwFlowCtl    = UART_HWCONTROL_NONE;          /* 无硬件流控 */
    g_uart_handle.Init.OverSampling = UART_OVERSAMPLING_16;         /* 过采样 */
    HAL_UART_Init(&g_uart_handle);                                  /* 使能WIFI8266 UART
                                                                     * HAL_UART_Init()会调用函数HAL_UART_MspInit()
                                                                     * 该函数定义在文件usart.c中
                                                                     */
     __HAL_UART_ENABLE_IT(&g_uart_handle, UART_IT_IDLE);// 开空闲中断
     __HAL_UART_ENABLE_IT(&g_uart_handle, UART_IT_RXNE);// 开接收非空中断    
    
    #if WIFI_USING_RT_THREAD_OS
    /* 创建一个事件*/
//    WIFI8266_IT_event = rt_event_create("wifi_IT_event",   /* 事件标志组名字*/
//                                        RT_IPC_FLAG_PRIO); /* 事件模式FIFO(0x00)*/
//    if (WIFI8266_IT_event == RT_NULL)
//            rt_kprintf("error@ %s %s:%d,The mux for WIFI can‘t be created!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码    
  /* 创建一个互斥信号*/  
        WIFI8266_usart_mux = rt_mutex_create("test_mux",RT_IPC_FLAG_PRIO);
    if (WIFI8266_usart_mux == RT_NULL)
            rt_kprintf("error@ %s %s:%d,The mux for WIFI can‘t be created!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
    /* 创建一个信号*/
        WIFI8266_RX_IT_sem = rt_sem_create("binary_sem",
                                                                                0,
                                                                                RT_IPC_FLAG_FIFO);
    if (WIFI8266_RX_IT_sem == RT_NULL)
            rt_kprintf("error@ %s %s:%d,The semphore for WIFI can‘t be created!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
        
    #endif
}
/**
 * @brief       WIFI8266_UART互斥锁
 * @param       baudrate: UART通讯波特率
 * @retval      无
 */
#if WIFI_USING_RT_THREAD_OS
void wifi8266_lock(void)
{
   rt_mutex_take( WIFI8266_usart_mux,RT_WAITING_FOREVER); //获取互斥量
}
void wifi8266_unlock(void)
{
   rt_mutex_release( WIFI8266_usart_mux ); //释放互斥量 
}    
#endif
/**
 * @brief       线程调用的应用函数与串口通信中断同步事件
 * @param       timeout: 延时多少ticks
 * @retval      无
 */
#if WIFI_USING_RT_THREAD_OS
//uint8_t wifi8266_wait_event(rt_int32_t timeout)
//{
//  rt_err_t uwRet = RT_EOK;
//  rt_uint32_t EventID;
///**等待触发事件,启动通信一次**/  
////while(1)
////{  
//    uwRet=rt_event_recv(WIFI8266_IT_event, /* 事件对象句柄 */
//                        WIFI8266_RX_IT_EVENT,/* 接收线程感兴趣的事件 */ 
//                        RT_EVENT_FLAG_OR|RT_EVENT_FLAG_CLEAR,/* 接收选项 */
//                        timeout,/* 指定超时事件, 100 ticks */
//                        &EventID); /* 指向接收到的事件 */ 
//    if(-RT_ETIMEOUT == uwRet){
//            rt_kprintf("error@ %s %s:%d, WIFI8266_RX_IT_EVENT:time out!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
//      return 1;//超时
//    }         
//    if(-RT_ERROR  == uwRet){
//            rt_kprintf("error@ %s %s:%d,WIFI8266_RX_IT_EVENT:ERROR!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
//      return 2;//错误
//    }              
//    if (EventID != ( WIFI8266_RX_IT_EVENT ) )/* 如果事件接收完成并且正确 */
//            rt_kprintf("error@ %s %s:%d,WIFI8266_RX_IT_EVENT: ID incorrect!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
//     return 0;        
////    }    
//}
//void wifi8266_send_event(void)
//{
//    /* 发送一个事件 UART_TX_IT_EVENT */        
//    rt_event_send(WIFI8266_IT_event,WIFI8266_RX_IT_EVENT);
//}
#endif
/**
 * @brief       线程调用的应用函数与串口通信中断同步信号
 * @param       timeout: 延时多少ticks
 * @retval      无
 */
#if WIFI_USING_RT_THREAD_OS
uint8_t wifi8266_take_sem(rt_int32_t timeout)
{
  rt_err_t uwRet = RT_EOK;
    //WIFI8266_RX_IT_sem->value =0;//信号值清零
    uwRet = rt_sem_take(WIFI8266_RX_IT_sem, timeout);//等待信号量
    if(-RT_ETIMEOUT == uwRet){
            rt_kprintf("error@ %s %s:%d, WIFI8266_RX_IT_sem:time out!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
      return 1;//超时
    }         
    if(-RT_ERROR  == uwRet){
            rt_kprintf("error@ %s %s:%d,WIFI8266_RX_IT_sem:ERROR!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
      return 2;//错误
    }              
    /* 如果信号接收完成并且正确 */
      return 0;
}
uint8_t wifi8266_release_sem(void)
{
    rt_sem_release(WIFI8266_RX_IT_sem);//放出信号量
    return 0;
}
#endif
/**
 * @brief       WIFI8266 UART中断回调函数
 * @param       无
 * @retval      无
 */
void WIFI8266_UART_IRQHandler(void)
{
    uint8_t tmp;
    
    if (__HAL_UART_GET_FLAG(&g_uart_handle, UART_FLAG_ORE) != RESET)        /* UART接收过载错误中断 */
    {
        __HAL_UART_CLEAR_OREFLAG(&g_uart_handle);                           /* 清除接收过载错误中断标志 */
        (void)g_uart_handle.Instance->ISR;//SR;                                   /* 先读SR寄存器,再读DR寄存器 */
        (void)g_uart_handle.Instance->RDR;//DR;
    }
    
    if (__HAL_UART_GET_FLAG(&g_uart_handle, UART_FLAG_RXNE) != RESET)       /* UART接收中断 */
    {
        HAL_UART_Receive(&g_uart_handle, &tmp, 1, HAL_MAX_DELAY);           /* UART接收数据 */
        
        if (g_uart_rx_frame.sta.len < (WIFI8266_UART_RX_BUF_SIZE - 1))   /* 判断UART接收缓冲是否溢出
                                                                             * 留出一位给结束符'\0'
                                                                             */
        {
            g_uart_rx_frame.buf[g_uart_rx_frame.sta.len] = tmp;             /* 将接收到的数据写入缓冲 */
            g_uart_rx_frame.sta.len++;                                      /* 更新接收到的数据长度 */
        }
        else                                                                /* UART接收缓冲溢出 */
        {
            g_uart_rx_frame.sta.len = 0;                                    /* 覆盖之前收到的数据 */
            g_uart_rx_frame.buf[g_uart_rx_frame.sta.len] = tmp;             /* 将接收到的数据写入缓冲 */
            g_uart_rx_frame.sta.len++;                                      /* 更新接收到的数据长度 */
        }
    }
    
    if (__HAL_UART_GET_FLAG(&g_uart_handle, UART_FLAG_IDLE) != RESET)       /* UART总线空闲中断 */
    {
        g_uart_rx_frame.sta.finsh = 1;                                      /* 标记帧接收完成 */
        
        __HAL_UART_CLEAR_IDLEFLAG(&g_uart_handle);                          /* 清除UART总线空闲中断 */
            
            #if WIFI_USING_RT_THREAD_OS
            //wifi8266_send_event();                                                /* 发送事件,通知帧接收完成 */
            wifi8266_release_sem();                                                     /* 发送信号,通知帧接收完成 */
            #endif
    }
}
View Code

AT指令操作WIFI模块的应用函数  wifi_8266_app.h

/**
 ****************************************************************************************************
 * @file        wifi_8266_app.h
 * @author      
 * @version     V1.0
 * @date        2023-02-22
 * @brief       
 * @license    
 ****************************************************************************************************
 * @attention
 *        AT指令操作WIFI模块的应用函数
 *
 ****************************************************************************************************
 */

#ifndef __WIFI_8266_APP_H
#define __WIFI_8266_APP_H

#include "stm32g0xx_hal.h"
#include <wifi8266_uart_bsp.h>
/*网络设置*/
#define WIFI_SSID          "REGAIN_001" /*WIFI热点名称*/
#define WIFI_PWD           "1234567899" /*WIFI热点密码*/
#define WIFI_AP_TCP_SERVER_IP      "192.168.8.1" /*设置WIFI服务器的IP*/
#define WIFI_AP_TCP_SERVER_PORT    "8181"        /*设置WIFI服务器端口*/

/* 引脚定义 */
#define WIFI8266_RST_GPIO_PORT           GPIOC
#define WIFI8266_RST_GPIO_PIN            GPIO_PIN_4
#define WIFI8266_RST_GPIO_CLK_ENABLE()   do{ __HAL_RCC_GPIOC_CLK_ENABLE(); }while(0)   /* PC口时钟使能 */

/* IO操作 */
#define WIFI8266_RST(x)                  do{ x ?                                                                                     \
                                                HAL_GPIO_WritePin(WIFI8266_RST_GPIO_PORT, WIFI8266_RST_GPIO_PIN, GPIO_PIN_SET) :  \
                                                HAL_GPIO_WritePin(WIFI8266_RST_GPIO_PORT, WIFI8266_RST_GPIO_PIN, GPIO_PIN_RESET); \
                                            }while(0)

/* 错误代码 */
#define WIFI8266_EOK         0   /* 没有错误 */
#define WIFI8266_ERROR       1   /* 通用错误 */
#define WIFI8266_ETIMEOUT    2   /* 超时错误 */
#define WIFI8266_EINVAL      3   /* 参数错误 */
                                                                                        
/* TCP客户端与服务器间通信使用,本机WIFI模块作为服务器 */                                                                                        
typedef  struct
{
  char  data[WIFI8266_UART_RX_BUF_SIZE];/* 帧接收的数据 */
    uint16_t linkNo;                         /* 收到网络连接的ID号*/
    uint16_t len;                            /* 数据长度 */
} TCP_FRAME_TYPE;
extern volatile TCP_FRAME_TYPE g_uart_tcp_frame;
                                                                                        

/* 操作函数 */
uint8_t wifi8266_init(uint32_t baudrate);                                /* WIFI8266初始化 */
void wifi8266_hw_reset(void);                                            /* WIFI8266硬件复位 */
uint8_t wifi8266_send_at_cmd(char *cmd, char *ack, uint32_t timeout);    /* WIFI8266发送AT指令 */                                                                                    
uint8_t wifi8266_restore(void);                                          /* WIFI8266恢复出厂设置 */
uint8_t wifi8266_at_test(void);                                          /* WIFI8266 AT指令测试 */
uint8_t wifi8266_sw_reset(void);                                         /* WIFI8266软件复位 */
uint8_t wifi8266_ate_config(uint8_t cfg);                                /* WIFI8266设置回显模式 */                                                                                    
uint8_t wifi8266_set_mode(uint8_t mode);                                 /* 设置WIFI8266工作模式 1 STA,2 AP,3AP+STA*/    
/*WIFI的IP地址*/                                                                                        
uint8_t wifi8266_cipap_set(char *ip);                                    /*AT+CIPAP   WIFI模块AP模式的IP设置*/
uint8_t wifi8266_get_ip(char *buf);                                      /* WIFI8266获取热点分配的IP地址 */
/*WIFI组网*/
uint8_t wifi8266_join_ap(char *ssid, char *pwd);                         /* WIFI8266连接外部WIFI热点 */
uint8_t wifi8266_set_ap(char *ssid, char *pwd,uint8_t channel,           /*设置WIFI8266模块成WIFI热点,及其账户密码等信息*/
                          uint8_t encryptionMethod, uint8_t  maxConnection,
                        uint8_t broadCast);        
/*WIFI模块作为TCP Server通信*/
uint8_t wifi8266_cipmux_set(uint8_t setMux);                             /*AT+CIPMUX  WIFI模块AP模式多连接*/
uint8_t wifi8266_servicer_set(uint8_t Enable,char *port);               /*AT+CIPMUX=1 ,AT+CIPSERVER指令,将WIFI模块设置为TCP服务器*/

uint8_t wifi8266_connect_tcp_server(char *server_ip, char *server_port); /* WIFI8266连接外部TCP服务器 */
uint8_t wifi8266_tcp_server_receive(uint32_t timeout);                   /* WIFI8266连接外部TCP服务器 +IPD*/
uint8_t wifi8266_servicer_get_data(void);                                /*解析通信帧,得到客户端发来的信息*/
uint8_t wifi8266_tcp_server_sendcmd( uint16_t linkNo,uint16_t len);      /*向客户端发送数据帧的AT指令及客户端链接号,数据字节数*/
uint8_t wifi8266_servicer_sendData(void);                                /*向客户端发送数据帧*/

/*WIFI模块透传*/
uint8_t wifi8266_enter_unvarnished(void);                                /* WIFI8266进入透传 */
void wifi8266_exit_unvarnished(void);                                    /* WIFI8266退出透传 */

uint8_t wifi8266_connect_atkcld(char *id, char *pwd);                    /* WIFI8266连接原子云服务器 */
uint8_t wifi8266_disconnect_atkcld(void);                                /* WIFI8266断开原子云服务器连接 */

#endif
View Code

AT指令操作WIFI模块的应用函数  wifi_8266_app.c

/**
 ****************************************************************************************************
 * @file        wifi_8266_app.c
 * @author      
 * @version     V1.0
 * @date        2023-02-22
 * @brief       
 * @license    
 ****************************************************************************************************
 * @attention
 *        AT指令操作WIFI模块的应用函数
 *
 ****************************************************************************************************
 */
#include <wifi_8266_app.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h> //字符串和数值间转换

/*TCP帧信息*/
volatile TCP_FRAME_TYPE g_uart_tcp_frame={0};
/**
 * @brief       WIFI8266硬件初始化
 * @param       无
 * @retval      无
 * @attention   WIFI模块复位引脚,设置GPIO引脚控制复位
 */
static void wifi8266_hw_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    
    WIFI8266_RST_GPIO_CLK_ENABLE();
    
    gpio_init_struct.Pin = WIFI8266_RST_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
    gpio_init_struct.Pull = GPIO_NOPULL;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(WIFI8266_RST_GPIO_PORT, &gpio_init_struct);
}

/**
 * @brief       WIFI8266硬件复位
 * @param       无
 * @retval      无
 */
/*来自WIFI模块的应答*/
void wifi8266_hw_reset(void)
{
    WIFI8266_RST(0);
    wifi_delay_ms(100);
    WIFI8266_RST(1);
    wifi_delay_ms(500);
}

/**
 * @brief       WIFI8266发送AT指令
 * @param       cmd    : 待发送的AT指令
 *              ack    : 等待的响应
 *              timeout: 等待超时时间
 * @retval      WIFI8266_EOK     : 函数执行成功
 *              WIFI8266_ETIMEOUT: 等待期望应答超时,函数执行失败
 */
uint8_t wifi8266_send_at_cmd(char *cmd, char *ack, uint32_t timeout)
{
    uint8_t *ret = NULL;
      uint8_t ubRet;
    
    wifi8266_uart_rx_restart();
      wifi8266_uart_printf("%s\r\n", cmd); //发送AT指令
    
    if ((ack == NULL) || (timeout == 0))
    { 

        return WIFI8266_EOK;
    }
    else
    {
             #ifndef WIFI_USING_RT_THREAD_OS
        while (timeout > 0)//延时循环查询WIFI回传通信帧,判断是否接收
        {
            ret = wifi8266_uart_rx_get_frame();
            if (ret != NULL)
            {
                if (strstr((const char *)ret, ack) != NULL)//在ret字符串中查找 ack字符串
                                {                                
                    return WIFI8266_EOK;
                }
                else
                {
                    wifi8266_uart_rx_restart();//清零RX缓冲
                }
            }
            timeout--;
            wifi_delay_ms(1);
        }    
            #endif
            #ifdef WIFI_USING_RT_THREAD_OS        
            receive:
            //ubRet = wifi8266_wait_event(timeout);//已接收帧,但串口没有接收到新的字节。等待这种情况
            ubRet = wifi8266_take_sem(timeout);//已接收帧,但串口没有接收到新的字节。等待这种情况
            if( ubRet ==0){//从通信帧中查找ack
                ret = wifi8266_uart_rx_get_frame();
                if (ret != NULL)
                {
                        if (strstr((const char *)ret, ack) != NULL)//在ret字符串中查找 ack字符串
                        {                                
                                return WIFI8266_EOK;//从通信帧中找到ack
                        }
                        else
                        {            
                              wifi8266_uart_rx_restart();//清零RX缓冲
                                goto receive;//从通信帧中没有找到ack,再次接收新的帧
                        }
                }    
        else{// ret没有字符串
                    wifi8266_uart_rx_restart();//清零RX缓冲
                    return  WIFI8266_ERROR;
                }                    
            }
            if(ubRet ==1){
                /*超时,没有接收数据*/
                return WIFI8266_ETIMEOUT;        
            }
            if(ubRet ==2){
              /*错误,没有接收数据*/
              return WIFI8266_ERROR;            
            }
            #endif
            return WIFI8266_ERROR;
    }
}
/**
 * @brief       WIFI8266初始化
 * @param       baudrate: WIFI8266 UART通讯波特率
 * @retval      WIFI8266_EOK  : WIFI8266初始化成功,函数执行成功
 *              WIFI8266_ERROR: WIFI8266初始化失败,函数执行失败
 */
uint8_t wifi8266_init(uint32_t baudrate)
{
    wifi8266_hw_init();                          /* WIFI8266硬件初始化 */
    wifi8266_hw_reset();                         /* WIFI8266硬件复位 */
    wifi8266_uart_init(baudrate);                /* WIFI8266 UART初始化 */
    if (wifi8266_at_test() != WIFI8266_EOK)   /* WIFI8266 AT指令测试 */
    {
        return WIFI8266_ERROR;
    }
    
    return WIFI8266_EOK;
}

/**
 * @brief       WIFI8266恢复出厂设置
 * @param       无
 * @retval      WIFI8266_EOK  : 恢复出场设置成功
 *              WIFI8266_ERROR: 恢复出场设置失败
 */
/*来自WIFI模块的应答*/
//AT+RESTORE

//OK

// ...

//ready
uint8_t wifi8266_restore(void)
{
    uint8_t ret;
    
    ret = wifi8266_send_at_cmd("AT+RESTORE", "ready", 30000);
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}

/**
 * @brief       WIFI8266 AT指令测试
 * @param       无
 * @retval      WIFI8266_EOK  : AT指令测试成功
 *              WIFI8266_ERROR: AT指令测试失败
 */
/*来自WIFI模块的应答*/
//AT

//OK
uint8_t wifi8266_at_test(void)
{
    uint8_t ret;
    uint8_t i;
    
    for (i=0; i<10; i++)
    {
        ret = wifi8266_send_at_cmd("AT", "OK", 500);
        if (ret == WIFI8266_EOK)
        {
            return WIFI8266_EOK;
        }
    }
    
    return WIFI8266_ERROR;
}

/**
 * @brief       设置WIFI8266工作模式
 * @param       mode: 1,Station模式
 *                    2,AP模式
 *                    3,AP+Station模式
 * @retval      WIFI8266_EOK   : 工作模式设置成功
 *              WIFI8266_ERROR : 工作模式设置失败
 *              WIFI8266_EINVAL: mode参数错误,工作模式设置失败
 */
/*来自WIFI模块的应答*/
//AT+CWMODE=1

//OK
uint8_t wifi8266_set_mode(uint8_t mode)
{
    uint8_t ret;
    
    switch (mode)
    {
        case 1:
        {
            ret = wifi8266_send_at_cmd("AT+CWMODE_DEF=1", "OK", 500);    /* Station模式 */
            break;
        }
        case 2:
        {
            ret = wifi8266_send_at_cmd("AT+CWMODE_DEF=2", "OK", 500);    /* AP模式 */
            break;
        }
        case 3:
        {
            ret = wifi8266_send_at_cmd("AT+CWMODE_DEF=3", "OK", 500);    /* AP+Station模式 */
            break;
        }
        default:
        {
            return WIFI8266_EINVAL;
        }
    }
    
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}

/**
 * @brief       WIFI8266软件复位
 * @param       无
 * @retval      WIFI8266_EOK  : 软件复位成功
 *              WIFI8266_ERROR: 软件复位失败
 *//*来自WIFI模块的应答*/
//AT+RST

//OK

// ets Jan  8 2013,rst cause:2, boot mode:(3,7)
// ...
//ready
uint8_t wifi8266_sw_reset(void)
{
    uint8_t ret;
    
    ret = wifi8266_send_at_cmd("AT+RST", "ready", 10000);
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}

/**
 * @brief       WIFI8266设置回显模式
 * @param       cfg: 0,关闭回显
 *                   1,打开回显
 * @retval      WIFI8266_EOK  : 设置回显模式成功
 *              WIFI8266_ERROR: 设置回显模式失败
 * @specifi     回显,在WIFI模块返回应答的时候,首先返回发给它的指令。关闭时,直接返回应答。
 *//*来自WIFI模块的应答*/
//ATE1

//OK
uint8_t wifi8266_ate_config(uint8_t cfg)
{
    uint8_t ret;
    
    switch (cfg)
    {
        case 0:
        {
            ret = wifi8266_send_at_cmd("ATE0", "OK", 500);   /* 关闭回显 */
            break;
        }
        case 1:
        {
            ret = wifi8266_send_at_cmd("ATE1", "OK", 500);   /* 打开回显 */
            break;
        }
        default:
        {
            return WIFI8266_EINVAL;
        }
    }
    
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}

/**
 * @brief       WIFI8266连接WIFI
 * @param       ssid: WIFI名称
 *              pwd : WIFI密码
 * @retval      WIFI8266_EOK  : WIFI连接成功
 *              WIFI8266_ERROR: WIFI连接失败
 */
/*来自WIFI模块的应答*/
uint8_t wifi8266_join_ap(char *ssid, char *pwd)
{
    uint8_t ret;
    char cmd[64];
    
    sprintf(cmd, "AT+CWJAP_DEF=\"%s\",\"%s\"", ssid, pwd);//组合AT指令字符串
    ret = wifi8266_send_at_cmd(cmd, "WIFI GOT IP", 10000);
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}
/**
 * @brief       WIFI8266设置WIFI热点的账户密码等信息
 * @param       ssid:             WIFI名称
 *              pwd :             WIFI密码
 *              channel:          无线信道
 *              encryptionMethod:加密方法
 *              maxConnection:   最多连接数,不超过4
 *              broadCast:       广播名称,0或1
 * @retval      WIFI8266_EOK  : WIFI连接成功
 *              WIFI8266_ERROR: WIFI连接失败
 *              wifi8266_set_ap("REGAIN_001","1234567899",10,4,1,0);
 */
/*来自WIFI模块的应答*/
//AT+CWSAP="REGAIN_001","1234567899",10,4,1,0

//OK
uint8_t wifi8266_set_ap(char *ssid, char *pwd,uint8_t channel, 
                          uint8_t encryptionMethod,uint8_t  maxConnection,
                        uint8_t broadCast)//WIFI8266设置WIFI热点的账户密码等信息
{
    uint8_t ret;
    char cmd[128];
    
    sprintf(cmd, "AT+CWSAP_DEF=\"%s\",\"%s\",%d,%d,%d,%d", 
              ssid, pwd,channel,encryptionMethod,maxConnection,broadCast);//组合AT指令字符串
    ret = wifi8266_send_at_cmd(cmd, "OK", 8000);
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}
/**
 * @brief       WIFI8266获取IP地址
 * @param       buf: IP地址,需要16字节内存空间
 * @retval      WIFI8266_EOK  : 获取IP地址成功
 *              WIFI8266_ERROR: 获取IP地址失败
 */
/*来自WIFI模块的应答*/
//AT+CIFSR
//+CIFSR:APIP,"192.168.4.1"
//+CIFSR:APMAC,"32:83:98:82:4f:34"

//OK
uint8_t wifi8266_get_ip(char *buf)
{
    uint8_t ret;
    char *p_start;
    char *p_end;
    
    ret = wifi8266_send_at_cmd("AT+CIFSR", "OK", 500);
    if (ret != WIFI8266_EOK)
    {
        return WIFI8266_ERROR;
    }
    
    p_start = strstr((const char *)wifi8266_uart_rx_get_frame(), "\"");
    p_end = strstr(p_start + 1, "\"");
    *p_end = '\0';
    sprintf(buf, "%s", p_start + 1);
    
    return WIFI8266_EOK;
}

/**
 * @brief       WIFI8266作为客户端,连接TCP服务器
 * @param       server_ip  : TCP服务器IP地址
 *              server_port: TCP服务器端口号
 * @retval      WIFI8266_EOK  : 连接TCP服务器成功
 *              WIFI8266_ERROR: 连接TCP服务器失败
 */
uint8_t wifi8266_connect_tcp_server(char *server_ip, char *server_port)
{
    uint8_t ret;
    char cmd[64];
    
    sprintf(cmd, "AT+CIPSTART=\"TCP\",\"%s\",%s", server_ip, server_port);
    ret = wifi8266_send_at_cmd(cmd, "CONNECT", 5000);
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}

/**
 * @brief       作为客户端,WIFI8266进入透传
 * @param       无
 * @retval      WIFI8266_EOK  : 进入透传成功
 *              WIFI8266_ERROR: 进入透传失败
 *@attention    CIPMUX and CIPSERVER must be 0
 */
uint8_t wifi8266_enter_unvarnished(void)
{
    uint8_t ret;
    
    ret  = wifi8266_send_at_cmd("AT+CIPMODE=1", "OK", 500);
    ret += wifi8266_send_at_cmd("AT+CIPSEND", ">", 500);
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}

/**
 * @brief       WIFI8266退出透传
 * @param       无
 * @retval      无
 */
void wifi8266_exit_unvarnished(void)
{
    wifi8266_uart_printf("+++");
}
/**
 * @brief       AT+CIPMUX  多连接
 * @param       setMux 0,1
 * @retval      WIFI8266_EOK  : 成功
 *              WIFI8266_ERROR: 失败
 */
/*来自WIFI模块的应答*/
//AT+CIPMUX=1

//OK
uint8_t wifi8266_cipmux_set(uint8_t setMux)/*AT+CIPMUX  多连接*/
{
    uint8_t ret;
    char cmd[64];    
    sprintf(cmd, "AT+CIPMUX=%d", setMux);
    
    ret = wifi8266_send_at_cmd(cmd, "OK", 500);
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}
/**
 * @brief       AT+CIPAP  AP模式的IP设置
 * @param       setMux 0,1
 * @retval      WIFI8266_EOK  : 成功
 *              WIFI8266_ERROR: 失败
 */
/*来自WIFI模块的应答*/
//AT+CIPAP="192.168.5.1"

//OK
uint8_t wifi8266_cipap_set(char *ip)/*AT+CIPAP  AP模式的IP设置*/
{
    uint8_t ret;
    char cmd[64];    
    sprintf(cmd, "AT+CIPAP=\"%s\"", ip);
    
    ret = wifi8266_send_at_cmd(cmd, "OK", 500);
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}
/**
 * @brief       AT+CIPMUX=1 ,AT+CIPSERVER指令,将 WIFI模块设置为TCP服务器
 * @param       Enable 0,1 ;port 端口号字符串
 * @retval      WIFI8266_EOK  : 成功
 *              WIFI8266_ERROR: 失败
 */
/*来自WIFI模块的应答*/
//AT+CIPSERVER=1,8080

//OK
uint8_t wifi8266_servicer_set(uint8_t Enable,char *port)/*AT+CIPMUX=1 ,AT+CIPSERVER指令,将WIFI模块设置为TCP服务器*/
{
    uint8_t ret;
    char cmd[64];    
      ret  = wifi8266_send_at_cmd("AT+CIPMUX=1","OK",500);//多接入模式
    sprintf(cmd, "AT+CIPSERVER=%d,%s", Enable,port);//服务器开启及端口设置    
    ret += wifi8266_send_at_cmd(cmd, "OK", 500);
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}
/**
 * @brief       AT+CIPSERVER=0  关闭TCP服务器,注意需要重启WIFI模块
 * @param       无
 * @retval      WIFI8266_EOK  : 成功
 *              WIFI8266_ERROR: 失败
 */
/*来自WIFI模块的应答*/
//AT+CIPSERVER=0

//OK
uint8_t wifi8266_servicer_reset(void)/*AT+CIPSERVER=0  关闭 WIFI模块TCP服务器,注意需要重启WIFI模块*/
{
    uint8_t ret;
    ret = wifi8266_send_at_cmd("AT+CIPSERVER=0", "OK", 500);
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}
/**
 * @brief       WIFI8266作为TCP服务器,启动向客户端发送帧
 *               AT指令 AT+CIPSEND
 * @param       linkNo :收到客户端网络连接的ID号
 *              len;   :数据长度 
 * @retval      WIFI8266_EOK     : 函数执行成功
 *              WIFI8266_ETIMEOUT: 等待期望应答超时,函数执行失败
 * @global      g_uart_tcp_frame  
 *              数据帧的 客户端链接号linkNo,长度len,及数据帧data
 */
uint8_t wifi8266_tcp_server_sendcmd( uint16_t linkNo,uint16_t len)
{
    //uint8_t *ret = NULL;
      uint8_t ubRet;
    char cmd[128];
    
    sprintf(cmd, "AT+CIPSEND=%d,%d", linkNo, len);//组合AT指令字符串
    ubRet = wifi8266_send_at_cmd(cmd, ">", 8000);    
      return ubRet;
}
/**
* @brief       向远端发送数据
 * @param       无
 * @retval      WIFI8266_EOK  : 成功
 *              WIFI8266_ERROR: 失败
 * @global      g_uart_tcp_frame  
 *              数据帧的 客户端链接号linkNo,长度len,及数据帧data
 */
//>              // 提示符>出现后,串口发送字节

//busy s...

//Recv 4 bytes

//SEND OK
uint8_t wifi8266_servicer_sendData(void)/*向客户端发送数据帧*/
{
    uint8_t ret;
    ret = wifi8266_send_at_cmd(( char*)g_uart_tcp_frame.data, "SEND OK", 500);//发送数据data帧
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }        
}

/**
 * @brief       WIFI8266作为TCP服务器,接收+IPD
 * @param       timeout: 等待超时时间
 * @retval      WIFI8266_EOK     : 函数执行成功
 *              WIFI8266_ETIMEOUT: 等待期望应答超时,函数执行失败
 */
/*来自WIFI模块的通信帧*/
//+IPD,0,3:ACK  //接客户端0发来的3BYTES帧,是 ACK
uint8_t wifi8266_tcp_server_receive(uint32_t timeout) /* WIFI8266连接外部TCP服务器 +IPD*/
{
        uint8_t *ret = NULL;
        uint8_t ubRet;

        wifi8266_uart_rx_restart();
        #ifdef WIFI_USING_RT_THREAD_OS        
        receive:
        //ubRet = wifi8266_wait_event(timeout);//已接收帧,但串口没有接收到新的字节。等待这种情况
        ubRet = wifi8266_take_sem(timeout);//已接收帧,但串口没有接收到新的字节。等待这种情况

        if( ubRet ==0){//从通信帧中查找ack
            ret = wifi8266_uart_rx_get_frame();
            if (ret != NULL)
            {
                    if (strstr((const char *)ret, "+IPD") != NULL)//在ret字符串中查找+IPD字符串
                    {                                
                            return WIFI8266_EOK;//从通信帧中找到+IPD
                    }
                    else
                    {            
                            wifi8266_uart_rx_restart();//清零RX缓冲
                            goto receive;//从通信帧中没有找到ack,再次接收新的帧
                    }
            }    
            else{// ret没有字符串
                wifi8266_uart_rx_restart();//清零RX缓冲
                return  WIFI8266_ERROR;
            }                    
        }
        if(ubRet ==1){
            /*超时,没有接收数据*/
            return WIFI8266_ETIMEOUT;        
        }
        if(ubRet ==2){
            /*错误,没有接收数据*/
            return WIFI8266_ERROR;            
        }
        #endif
        return WIFI8266_ERROR;    

}
/**
 * @brief       取出接收远端发来的数据
 * @param       无
 * @retval      WIFI8266_EOK  : 成功
 *              WIFI8266_ERROR: 失败
 */
/*来自WIFI模块的通信帧*/
//+IPD,0,3:ACK  //接客户端0发来的3BYTES帧,是 ACK
uint8_t wifi8266_servicer_get_data(void)/*解析通信帧,得到客户端发来的信息*/
{
    uint8_t ret;
    char *p_start;
    char *p_end;
      char buf[3];
      ret = WIFI8266_EOK;
      /*取帧内data部分*/
      p_start = strstr((const char *)wifi8266_uart_rx_get_frame(), ":");
        sprintf((char*)g_uart_tcp_frame.data, "%s", p_start + 1);//数据
      /*取帧内len部分*/      
      p_start = strstr((const char *)wifi8266_uart_rx_get_frame(), ",");    
    p_start = strstr(p_start + 1, ",");    
      p_end   = strstr(p_start + 1, ":");
      *p_end = '\0';//字符串结束符号
      if(p_end-p_start>sizeof(buf)+1) return WIFI8266_ERROR;
        sprintf(buf, "%s", p_start + 1);    
      g_uart_tcp_frame.len = atoi(buf);//字符串转整数
      /*取帧内link No部分*/    
      p_start = strstr((const char *)wifi8266_uart_rx_get_frame(), ",");        
      p_end   = strstr(p_start + 1, ",");
      *p_end = '\0';//字符串结束符号    
      if(p_end-p_start>sizeof(buf)+1) return WIFI8266_ERROR;
        sprintf(buf, "%s", p_start + 1);
    g_uart_tcp_frame.linkNo= atoi(buf);//字符串转整数
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}
View Code

AT指令操作WIFI模块的线程  wifi_8266_rtth.h

#ifndef WIFI_8266_RTTH_H
#define WIFI_8266_RTTH_H
#include "stm32g0xx_hal.h"

uint8_t wifi_tcpserver_rtthread_Init(void);
    
#endif

AT指令操作WIFI模块的线程  wifi_8266_rtth.c

#include "wifi_8266_rtth.h"
#include <rtthread.h>
#include <wifi_8266_app.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h> //字符串和数值间转换

/* RT-Thread 定义线程控制块指针 */
static rt_thread_t tcp_server_thread = RT_NULL;
/* RT-Thread 线程入口函数声明 */
static void tcp_server_thread_entry(void* parameter);
uint8_t wifi_tcp_server_check(void);/*检查WIFI模块配置是否正确*/
/**
 * @brief       WIFI8266硬件初始化
 * @param       无
 * @retval      无
 * @attention   WIFI模块复位引脚,设置GPIO引脚控制复位
 */
uint8_t wifi_tcpserver_rtthread_Init(void)
{
        /*创建线程控制块,并赋给一个该类型指针变量f*/
        tcp_server_thread= /* 线程控制块指针 */
        rt_thread_create( "tcp_server", /* 线程名字 */
                                            tcp_server_thread_entry, /* 线程入口函数 */
                                            RT_NULL, /* 线程入口函数参数 */
                                            512, /* 线程栈大小 */
                                            3, /* 线程的优先级 */
                                            20); /* 线程时间片 */
        /* 启动线程,开启调度 */
        if (tcp_server_thread != RT_NULL)
                 rt_thread_startup(tcp_server_thread);
        else
                 return 1;               
        return 0;
}
/**
 * @brief       WIFI8266硬件初始化
 * @param       无
 * @retval      无
 * @attention   WIFI模块复位引脚,设置GPIO引脚控制复位
 */
static void tcp_server_thread_entry(void* parameter)
{
    wifi8266_uart_init(115200);

        if(WIFI8266_EOK !=         wifi8266_at_test()){
            /* WIFI8266 AT指令测试 */        
            rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码          
            rt_thread_delay(100);
        }
        /*组网参数设置*/
        if(WIFI8266_EOK !=wifi_tcp_server_check())/*检查WIFI模块配置是否正确*/
        {
            if(WIFI8266_EOK != wifi8266_cipap_set(WIFI_AP_TCP_SERVER_IP)){ 
             /*WIFI模块的IP设置*/    
                rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
                rt_thread_delay(100);
            }        
            if(WIFI8266_EOK != wifi8266_set_mode(2)){
                /* 设置WIFI8266工作模式 sta+ap */        
                rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
                rt_thread_delay(100);
            }    
            
            if(WIFI8266_EOK != wifi8266_set_ap(WIFI_SSID,WIFI_PWD,10,4,1,0)){ 
                /* WIFI8266软件复位,新设置的工作模式生效*/            
                rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码          
                rt_thread_delay(100);
            }    
            if(WIFI8266_EOK != wifi8266_sw_reset()){ 
                /* WIFI8266软件复位,新设置的工作模式生效*/            
                rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
                rt_thread_delay(100);
            }                        
      }           
        /*网络链接参数设置*/                        
        if(WIFI8266_EOK != wifi8266_servicer_set(1,WIFI_AP_TCP_SERVER_PORT)){ 
      /*AT+CIPMUX=1 ,AT+CIPSERVER指令,将WIFI模块设置为TCP服务器*/
          rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
          rt_thread_delay(100);
        }    
        /*服务器侦听数据包,侦听客户端发来通信帧,解析通信帧,AT指令启动发送,发送数据帧*/        
    while(1)
        {            
            if(WIFI8266_EOK !=wifi8266_tcp_server_receive(100)){     
            /* WIFI8266作为服务器,接收+IPD开始的通信帧*/
                continue;
            }
      /*解析通信帧,得到客户端发来的信息*/   
            if(WIFI8266_EOK !=wifi8266_servicer_get_data()){
                rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);
            }; 
      if( 0==        g_uart_tcp_frame.len) continue;    
      /*AT指令通知WIFI模块作为TCP server发送链接号,数据字节数*/   
            
      sprintf((char*)g_uart_tcp_frame.data, "success transfer!linkNo:%d", g_uart_tcp_frame.linkNo);//组合AT指令字符串
            g_uart_tcp_frame.len = strlen((char*)g_uart_tcp_frame.data);
            
            if(WIFI8266_EOK != wifi8266_tcp_server_sendcmd( g_uart_tcp_frame.linkNo,g_uart_tcp_frame.len)){
                rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);
            }; 
            /*向客户端发送数据帧*/   
            if(WIFI8266_EOK != wifi8266_servicer_sendData()){
                rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);
            };         
          rt_thread_delay(50);    
        }             
}
/**
 * @brief       检查WIFI模块配置是否正确
 * @param       无
 * @retval      无
 * @attention   WIFI模块复位引脚,设置GPIO引脚控制复位
 */
uint8_t wifi_tcp_server_check(void)/*检查WIFI模块配置是否正确*/
{
    uint8_t ret; 
          
      ret  = wifi8266_send_at_cmd("AT+CWMODE?","2",500);                 //AP模式?
      ret += wifi8266_send_at_cmd("AT+CWSAP?",WIFI_SSID,500);            //热点名称?
    ret += wifi8266_send_at_cmd("AT+CIFSR",WIFI_AP_TCP_SERVER_IP,500); //IP地址?
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}
View Code

主函数,作为线程,演示功能(PC机WIFI链接模块AP,在PC机上设置TCP Client并发数据给在模块上的TCP server)

/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
#include <rtthread.h>
#include <wifi_8266_app.h>
#include "wifi_8266_rtth.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h> //字符串和数值间转换
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */ 
uint8_t  count;
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* 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_USART1_UART_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
   wifi_tcpserver_rtthread_Init();    // 启动 wifi线程
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
    count =0;
    //wifi8266_at_test();
  while (1)
  {
    count++;
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
View Code

特殊:需要修改 线程 堆栈 要足够大,例如512bytes。WIFI线程需要较大的RAM作为线程堆栈。否者出现奇怪的内核错误。

(USB转串口模块RX 接WIFI模块发送TX端,PC机开始TCP Client端)

注意事项:

1、注意RT-thread 线程堆栈THREAD_STACK_SIZE大小,因为通信时使用较多数组作为缓存器,使用较多RAM。分配过小,导致RT-thread内核程序错误。

2、注意AT指令使用顺序,重启动使得设置生效。

3、考虑AT指令,STM32下达命令帧到接收应答帧有时间间隔,这里使用USART接收RX中断服务过程中发送rtth内核信号量方式解决。

 

网络资源

AT 命令集 乐鑫公司

ESP系列模组AT指令如何设置透传范例_doiting

posted @ 2023-02-22 14:13  辛河  阅读(183)  评论(0)    收藏  举报