二、在freertos中对应esp01s模块的ap模式下的通信测试。

一、前期配置

参考:

https://blog.csdn.net/weixin_44386927/article/details/155891583

的配置。

二、代码部分

1、定义部分
c 复制代码
1、串口的初始化参考之前的配置。

#define AP_SSID             "ESP01S_AP"       // AP模式的WiFi名称
#define AP_PWD              "12345678"       // AP模式的WiFi密码(至少8位)
#define AP_CHANNEL          6                // AP信道(1-13)
#define AP_ENCRYPT          3                // 加密方式:3=WPA2_PSK(推荐)
#define  TCP_SERVER_PORT 8080

typedef struct {
    uint8_t conn_id;      // 数据发送方的conn_id
    uint16_t data_len;    // 原始数据长度
    char data_content[UART_RX_BUF_SIZE];// 原始数据内容
    uint8_t parse_ok;     // 解析成功标记
} IPD_Data_Proc_t;

extern uint8_t ESP01_Send_To_Client(uint8_t conn_id, const char *proc_data) ;
extern void ESP01_Parse_IPD_Data(const char *ipd_str, IPD_Data_Proc_t *ipd_proc) ;
2、函数部分
c 复制代码
/**
 * @brief 去除字符串首尾空白符(\r\n/空格/制表符)
 */
void trim_whitespace(char *str) {
    if (str == NULL || *str == '\0') return;
    // 去除开头空白符
    char *start = str;
    while (*start != '\0' && isspace((unsigned char)*start)) start++;
    // 去除结尾空白符
    char *end = str + strlen(str) - 1;
    while (end > start && isspace((unsigned char)*end)) end--;
    *(end + 1) = '\0';
    // 移动到原字符串开头
    memmove(str, start, strlen(start) + 1);
}

/**
 * @brief 解析ESP01S的+IPD数据(兼容首尾\r\n,提取conn_id和内容)
 */
void ESP01_Parse_IPD_Data(const char *ipd_str, IPD_Data_Proc_t *ipd_proc) {
    memset(ipd_proc, 0, sizeof(IPD_Data_Proc_t));
    ipd_proc->parse_ok = 0;

    // 空数据校验
    if (ipd_str == NULL || strlen(ipd_str) == 0) return;

    // 去除首尾空白符(处理\r\n+IPD...\r\n)
    char temp_buf[128] = {0};
    strncpy(temp_buf, ipd_str, sizeof(temp_buf)-1);
    trim_whitespace(temp_buf);

    // 校验是否为IPD格式
    if (strncmp(temp_buf, "+IPD,", 5) != 0) return;

    // 解析conn_id(IPD后的第一个数字)
    const char *p = temp_buf + 5;
    char id_str[4] = {0};
    int i = 0;
    while (*p != ',' && *p != '\0') id_str[i++] = *p++;
    ipd_proc->conn_id = atoi(id_str);
    p++; // 跳过逗号

    // 解析数据长度
    char len_str[8] = {0};
    i = 0;
    while (*p != ':' && *p != '\0') len_str[i++] = *p++;
    ipd_proc->data_len = atoi(len_str);
    p++; // 跳过冒号

    // 解析原始数据内容
    if (ipd_proc->data_len > 0 && ipd_proc->data_len < 64) {
        strncpy(ipd_proc->data_content, p, ipd_proc->data_len);
        ipd_proc->data_content[ipd_proc->data_len] = '\0';
        ipd_proc->parse_ok = 1;
    }
}

/**
 * @brief 向指定conn_id的客户端回传处理后的数据
 */
uint8_t ESP01_Send_To_Client(uint8_t conn_id, const char *proc_data) {
    if (proc_data == NULL || strlen(proc_data) == 0) {
        printf("❌ 回传数据为空\r\n");
        return 0;
    }
    uint16_t proc_len = strlen(proc_data);

    // 步骤1:发送AT+CIPSEND指令(指定ID和长度)
    char send_cmd[64] = {0};
    snprintf(send_cmd, sizeof(send_cmd), "AT+CIPSEND=%d,%d\r\n", conn_id, proc_len);
    if (!ESP01_SendATCmd(send_cmd, ">", "ERROR", 1000)) {
        printf("❌ ID%d:AT+CIPSEND指令失败\r\n", conn_id);
        return 0;
    }

    // 步骤2:发送处理后的数据(纯数据,无\r\n)
    HAL_UART_Transmit(&huart2, (uint8_t*)proc_data, proc_len, 100);
    osDelay(20); // 确保数据发送完成
    return 1;

}
3、任务部分
c 复制代码
void StartTask02 (void *argument)
{
  ESP01_UART_Data_t rx_data =
    { 0 };
  char cmd_buf[USART1_DMA_BUF_SIZE] =
    { 0 };
  char at_cmd[128] =
    { 0 };
  printf ("串口1调试指令接收任务已启动,支持指令:\r\n");
  printf ("1. LED_ON - 点亮LED\r\n");
  printf ("2. LED_OFF - 关闭LED\r\n");
  printf ("3. ESP_AT+AT - 配置ESP01并开启\r\n");

while(1)
{
 	.....前面省略,参考之前的。
 			else if (strncmp (cmd_buf, "ESP_AT+", 7) == 0)
	    {
	      // 提取AT指令(去掉前�?ESP_AT+,得到真正的AT指令�?
	      strncpy (at_cmd, cmd_buf + 7, sizeof(at_cmd) - 1);
	      printf ("手动发AT指令给ESP01:%s\r\n", at_cmd);
	      if (strcmp (at_cmd, "AP") == 0)
			{
		 		 HAL_UART_Transmit (&huart2, (uint8_t*) "+++", 3, 100);
		 		 osDelay (1000);
		  if (!ESP01_SendATCmd ("AT", "OK", "ERROR", AT_CMD_TIMEOUT))
		    {
		      printf ("AT失败\r\n");
		    }
		  // 2. 切换到AP模式(CWMODE=2:仅AP;3:AP+STA)
		  if (!ESP01_SendATCmd ("AT+CWMODE=2", "OK", "ERROR",
					AT_CMD_TIMEOUT))
		    {
		      printf ("AT+CWMODE=2失败\r\n");
		    }
		  ESP01_SendATCmd ("AT+RST", "READY", "ERROR", 2000);
		  osDelay (1500); // 等待重启完成(至少1秒)
		  if (!ESP01_SendATCmd ("AT", "OK", "ERROR", AT_CMD_TIMEOUT))
		        {
		          printf ("重启后AT测试失败\r\n");
		        }
		  if (!ESP01_SendATCmd ("AT+CIPMUX=1", "OK", "ERROR", AT_CMD_TIMEOUT))
		     {
		       printf ("开启多路连接失败\r\n");
		     }
		     else
		     {
		       printf ("多路连接模式已开启\r\n");
		     }
		  //配置AP参数(SSID、密码、信道、加密方式)
		  //AT+CWSAP="ESP01S_AP","12345678",11,3
		  snprintf (at_cmd, sizeof(at_cmd),
			    "AT+CWSAP=\"%s\",\"%s\",%d,%d", AP_SSID, AP_PWD,
			    AP_CHANNEL, AP_ENCRYPT);
		  if (!ESP01_SendATCmd (at_cmd, "OK", "ERROR", AT_CMD_TIMEOUT))
		    {
		      printf ("AT+CWSAP(配置AP参数)失败\r\n");
		   }
		  //AT+CIPMUX=1  # 1=多连接模式,0=单连接(默认)
		  //AT+CIPSERVER=1,8080  # 1=开启服务器,8080=监听端口
		  //AT+CIPSERVER?        # 验证服务器状态,返回+CIPSERVER:1,8080即生效
		  snprintf (at_cmd, sizeof(at_cmd), "AT+CIPSERVER=1,%d",
		  TCP_SERVER_PORT);
		  if (!ESP01_SendATCmd (at_cmd, "OK", "ERROR", AT_CMD_TIMEOUT))
		    {
		      printf ("AT+CIPSERVER(启动TCP服务器)失败\r\n");
		    }
		  else
		    {
		      printf ("AP模式TCP服务器已启动:%d\r\n", TCP_SERVER_PORT);

		      // 可选:定时查询客户端连接状态
		      flag_ap_on=1;
		      osThreadFlagsSet (myTask03Handle, 0x01); // 通知myTask04开始检测连接状态
		    }
		}
	 }
	  memset (&rx_data, 0, sizeof(rx_data));
	}
}

void StartTask03(void *argument)
{

	ESP01_UART_Data_t rx_data = {0};
	IPD_Data_Proc_t ipd_proc;

 while(1)
  {

      if (osMessageQueueGet (myQueue02Handle, (void*) &rx_data, NULL, 5000)
	  == osOK)
	{

	  if (flag_ap_on == 1)
	    {
	      // 1. 解析IPD数据(提取conn_id和原始内容)
	      memset (&ipd_proc, 0, sizeof(ipd_proc));
	      ESP01_Parse_IPD_Data ((char*) rx_data.data, &ipd_proc);
	      if (ipd_proc.parse_ok == 0)
		{

		  memset (&rx_data, 0, sizeof(rx_data));
		  continue;
		}

		if(strcmp(ipd_proc.data_content,"+++")== 0)
		{
		    printf("look\n");
		    HAL_UART_Transmit(&huart2, (uint8_t*)ipd_proc.data_content, ipd_proc.data_len, 100);
		    osDelay(20); // 确保数据发送完成
		}
	      ESP01_Send_To_Client(ipd_proc.conn_id, ipd_proc.data_content);

	    }
	  memset (&rx_data, 0, sizeof(rx_data));
	  osDelay(100);
	}
    osDelay(100);
  }
}

注意:1、初始化配置完后,pc端即客户端,要重新连接。

效果如下:

源码:https://download.csdn.net/download/weixin_44386927/92458532?spm=1001.2014.3001.5501

相关推荐
点灯小铭2 小时前
基于单片机的多路热电偶温度监测与报警器
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
wzfj123453 小时前
FreeRTOS 学习方法
freertos
tianyue1007 小时前
STM32G431 ADC 多个channel 采集
stm32·单片机·嵌入式硬件
清风6666668 小时前
基于单片机的水泵效率温差法测量与报警系统设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
z20348315209 小时前
定时器练习报告
单片机·嵌入式硬件
zk009 小时前
内容分类目录
单片机·嵌入式硬件
安生生申10 小时前
STM32 ESP8266连接ONENET
c语言·stm32·单片机·嵌入式硬件·esp8266
广药门徒10 小时前
电子器件烧毁的底层逻辑与避坑指南
单片机·嵌入式硬件
点灯小铭14 小时前
基于单片机的社区医院小型高压蒸汽灭菌自动控制器设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
youcans_15 小时前
【动手学STM32G4】(3)STM32G431之定时器
stm32·单片机·嵌入式硬件·定时器