STM32与ESP8266AT指令超时重传方案

在 STM32 与 ESP8266 的串口通信中,网络波动或模块忙乱常导致 AT 指令无响应。为了保证系统的稳定性,必须设计一套完善的超时检测与重传机制。以下是详细的解决方案,包含状态机设计、代码实现及调试建议。


一、 问题分析与设计思路

AT 指令通信通常采用"请求-响应"模式。若发送指令后未在规定时间内收到预期的回复(如 OKERROR),则视为通信超时。

核心设计逻辑

  1. 发送指令:通过 UART 发送 AT 指令。
  2. 开启计时:启动定时器或利用系统滴答定时器(SysTick)进行倒计时。
  3. 接收解析:在超时时间内持续检查接收缓冲区,判断是否包含期望的回复字符串。
  4. 超时判定
    • 成功:收到预期回复,清除超时标志,执行下一步逻辑。
    • 失败 :倒计时结束仍未收到,触发重传机制
  5. 重传限制:设置最大重试次数(如 3 次),超过次数后报错并复位模块,防止死循环。

二、 软件状态机与代码实现

以下代码基于 STM32 标准库,展示如何封装一个带有超时和重传功能的 AT 指令发送函数。

  1. 定义全局变量与宏
c 复制代码
#include "stm32f10x.h"
#include <string.h>
#include <stdio.h>

#define ESP8266_USART   USART2  // 假设使用串口2
#define MAX_RETRY_COUNT 3       // 最大重试次数
#define TIMEOUT_MS      2000    // 超时时间 2000ms

// 简单的延时函数(需根据实际环境实现)
void Delay_ms(uint32_t ms) {
    uint32_t i, j;
    for (i = 0; i < ms; i++)
        for (j = 0; j < 7990; j++); // 粗略延时,建议使用SysTick
}

// 串口发送字符串
void USART_SendString(USART_TypeDef* USARTx, char *str) {
    while(*str) {
        while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
        USART_SendData(USARTx, *str++);
    }
}

// 接收缓冲区及相关标志
volatile uint8_t rx_buffer[512];
volatile uint16_t rx_index = 0;
volatile uint8_t rx_done = 0; // 接收完成标志
  1. 封装核心 AT 指令发送函数(带重传)

这是解决超时重传问题的核心函数。它利用指针操作在接收缓冲区中查找期望的回复字符串。

c 复制代码
/**
 * @brief  发送AT指令并等待回复(支持超时与重传)
 * @param  cmd: 发送的AT指令字符串
 * @param  resp: 期望的回复字符串(如 "OK", "SEND OK")
 * @param  timeout: 超时时间(毫秒)
 * @retval 1: 成功收到回复; 0: 失败(超时且重试次数耗尽)
 */
uint8_t ESP8266_SendCmd(char *cmd, char *resp, uint16_t timeout) {
    uint8_t retry_count = 0;
    uint16_t tick_start = 0; // 模拟时间戳计数
    uint16_t current_tick = 0;
    
    while (retry_count < MAX_RETRY_COUNT) {
        // 1. 清空接收缓冲区
        rx_index = 0;
        memset((void*)rx_buffer, 0, sizeof(rx_buffer));
        rx_done = 0;
        
        // 2. 发送指令
        USART_SendString(ESP8266_USART, cmd);
        
        // 3. 等待响应(模拟超时检测)
        // 注意:实际项目中建议使用 HAL_GetTick() 或 SysTick 计时
        for (current_tick = 0; current_tick < timeout; current_tick++) {
            Delay_ms(1); // 1ms 延时
            
            // 检查是否收到数据(这里假设串口中断负责填充 rx_buffer)
            // 如果使用查询方式,这里需要调用 USART_ReceiveData
            
            // 4. 判断缓冲区是否包含期望的回复
            if (strstr((const char*)rx_buffer, resp) != NULL) {
                return 1; // 成功
            }
            
            // 可选:如果收到 "ERROR" 或 "FAIL" 可立即重试,不必等超时
            if (strstr((const char*)rx_buffer, "ERROR") != NULL) {
                break; 
            }
        }
        
        // 超时或收到错误,进行重试
        retry_count++;
        printf("Retry %d/%d...\r
", retry_count, MAX_RETRY_COUNT);
        Delay_ms(500); // 重试前稍作等待
    }
    
    return 0; // 失败
}

三、 应用实例:连接 WiFi

利用上述封装好的函数,可以非常稳健地执行连接 WiFi 的操作。即使网络环境差导致第一次 AT 指令丢失,程序也会自动重试。

c 复制代码
void ESP8266_Connect_Wifi(void) {
    // 1. 关闭回显,简化接收解析
    if (ESP8266_SendCmd("ATE0\r
", "OK", 500) == 0) {
        printf("ATE0 Failed!\r
");
        return;
    }
    
    // 2. 设置 Wi-Fi 模式为 Station
    // ESP8266_SendCmd 内部会自动处理超时和重试 
    if (ESP8266_SendCmd("AT+CWMODE=1\r
", "OK", 1000) == 0) {
        printf("Set Mode Failed!\r
");
        return;
    }
    
    // 3. 连接目标路由器 (SSID: TPLINK, PWD: 12345678)
    // 连接过程较慢,建议超时时间设长一些,如 10秒-20秒
    // 指令: AT+CWJAP="SSID","PASSWORD"
    if (ESP8266_SendCmd("AT+CWJAP=\"TPLINK\",\"12345678\"\r
", "WIFI GOT IP", 20000) == 0) {
        printf("Connect WiFi Failed!\r
");
        // 这里可以添加复位 ESP8266 的逻辑 
    } else {
        printf("WiFi Connected Successfully!\r
");
    }
}

四、 进阶优化建议

在实际工程中,除了简单的超时重传,还需要注意以下几点以提高系统的鲁棒性:

优化点 说明 实施建议
接收中断处理 不要在主循环中死等查询接收,应使用串口中断(USART_IT_RXNE)将数据存入 rx_buffer 在中断服务函数中置位 rx_done 标志或直接更新缓冲区索引。
DMA 传输 ESP8266 透传大数据时,CPU 频繁处理中断会影响效率。 使用串口 DMA 接收,利用空闲中断(IDLE)判断一帧数据接收完毕。
模块复位 若连续多次 AT 指令无响应,说明模块可能死机。 在重试次数耗尽后,拉低 ESP8266 的 RST 引脚 100ms 进行硬件复位。
心跳检测 长时间运行可能发生静默断连。 定时发送 AT 指令进行心跳检测,无回复则触发重连流程 。

通过这种"封装+重试+复位"的层级防御策略,可以有效解决 STM32 与 ESP8266 通信中的不稳定问题,确保智能农业或其他 IoT 系统长期在线。​​​​​

相关推荐
LCG元2 小时前
STM32实战:基于STM32F103的智能共享充电宝管理系统
stm32·单片机·嵌入式硬件
点灯师3 小时前
基于单片机的智能家居智能雨水自动关窗控制系统设计
单片机·嵌入式硬件·毕业设计·智能家居·课程设计·期末大作业
Smart-佀3 小时前
涨薪秘技:智能家居中的BLE协议与实现
网络·arm开发·嵌入式硬件·microsoft
LCG元3 小时前
STM32嵌入式开发:基于LD3320的智能语音识别系统
stm32·语音识别·xcode
freeinlife'4 小时前
onenet云平台下发数据到单片机并且OLED屏显示
单片机·嵌入式硬件
硅农深芯5 小时前
为什么有的芯片电源pin叫VCC,有的叫VDD?
单片机·嵌入式硬件·vcc·vdd·vee·vss
d111111111d6 小时前
STM32-UART封装问题解析
笔记·stm32·单片机·嵌入式硬件·学习·算法
国产化创客7 小时前
龙芯 2K0300-- 实现工业网关监控仪表盘项目
嵌入式硬件·物联网·数据可视化
项目題供诗7 小时前
STM32-OLED显示屏(六)
stm32·单片机·嵌入式硬件