You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用UART中断时,如何检测ESP-01(ESP8266 AT指令)响应结束?

检测ESP8266 AT固件响应结束的可靠方法(基于STM32 UART中断)

我用ESP-01(刷写ESP8266 AT固件)与STM32 Bluepill连接,通过HAL_UART_Receive_IT中断接收ESP的数据,但不清楚如何判断ESP的完整响应何时到达。

简化代码

接收回调函数:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART1)
    {
        rx_buffer[rx_index++] = rx_byte;

        if (rx_index >= sizeof(rx_buffer))
            rx_index = 0;     // 防止溢出

        if (condition)        // <-- 此处应填入什么判断条件?
            flag = 1;

        HAL_UART_Receive_IT(&huart1, &rx_byte, 1);
    }
}

发送函数:

void sendATcommand(char* cmd){
  memset(command, 0, sizeof(command));
  strcpy(command, cmd);
  HAL_UART_Transmit(&huart1, (uint8_t*)command, strlen(command), HAL_MAX_DELAY);
}

ESP-01的响应类型

ESP的回复没有统一终止符,常见形式包括:

  • OK\r\n
  • ERROR\r\n
  • SEND OK\r\n
  • +IPD,<len>:<data>
  • 上述内容混合出现(部分内容可能在大响应的中间)

可靠的检测方案

要避免阻塞函数,需在中断或上层状态机里完成解析,推荐结合超时检测+特定模式匹配+IPD长度解析的组合方案:

1. 超时检测(核心兜底逻辑)

ESP的响应各部分间隔通常不超过100ms(可根据实际调试调整),一旦超过该时间未收到新字节,即可判定当前响应段结束。

  • 实现:每次中断接收到字节时,重置一个定时器(比如用SysTick或通用定时器);定时器溢出时,设置响应结束的flag=1。
  • 注意:若连续接收大量数据(如IPD长数据),定时器会被不断重置,不会误触发结束。

2. 特定终止符匹配

对OK\r\n、ERROR\r\n这类明确的响应结尾,直接检测缓冲区末尾的字符组合:

  • 每次接收到新字节后,检查最后2个字符是否为\r\n,再往前匹配对应前缀:
    • 若最后4个字符是OK\r\n,或最后7个字符是ERROR\r\n/SEND OK\r\n,直接标记响应结束。
  • 简化判断代码示例:
// 考虑环形缓冲区的索引循环
if (rx_index >= 2) {
    uint16_t idx1 = (rx_index - 2 + sizeof(rx_buffer)) % sizeof(rx_buffer);
    uint16_t idx2 = (rx_index - 1 + sizeof(rx_buffer)) % sizeof(rx_buffer);
    if (rx_buffer[idx1] == '\r' && rx_buffer[idx2] == '\n') {
        uint16_t idx_ok = (rx_index -4 + sizeof(rx_buffer)) % sizeof(rx_buffer);
        uint16_t idx_err = (rx_index -7 + sizeof(rx_buffer)) % sizeof(rx_buffer);
        uint16_t idx_send_ok = (rx_index -7 + sizeof(rx_buffer)) % sizeof(rx_buffer);
        if ((rx_index >=4 && memcmp(&rx_buffer[idx_ok], "OK\r\n",4)==0) ||
            (rx_index >=7 && memcmp(&rx_buffer[idx_err], "ERROR\r\n",7)==0) ||
            (rx_index >=7 && memcmp(&rx_buffer[idx_send_ok], "SEND OK\r\n",7)==0)) {
            flag = 1;
        }
    }
}

3. 解析+IPD帧的长度字段

当检测到+IPD,开头的帧时,提取<len>数值,累计接收字节数,达到指定长度时判定该帧结束:

  • 用状态机跟踪流程:
    • 状态1:等待+IPD,出现
    • 状态2:解析逗号后的数字,得到数据长度data_len
    • 状态3:接收数据,直到收到的字节数等于data_len(需加上帧头和分隔符长度),标记该IPD帧结束
  • 实现时需在中断里逐个字符解析数字,计算出data_len。

4. 整合状态机

把上述逻辑整合到状态机中,在中断回调或后台任务(如FreeRTOS任务)里更新状态:

  • 初始状态:发送AT命令后,进入「等待响应」状态
  • 接收状态:同时进行超时检测、终止符匹配、IPD帧跟踪
  • 结束状态:触发flag=1,通知上层处理缓冲区中的完整响应

修改后的回调函数示例(结合超时和终止符检测)

// 全局变量:记录最后一次接收字节的时间
uint32_t last_rx_time = 0;
#define RESP_TIMEOUT 100 // 100ms超时阈值

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART1)
    {
        rx_buffer[rx_index++] = rx_byte;

        if (rx_index >= sizeof(rx_buffer))
            rx_index = 0;     // 防止溢出

        // 重置超时计时
        last_rx_time = HAL_GetTick();

        // 检测特定终止符
        if (rx_index >= 2) {
            uint16_t idx1 = (rx_index - 2 + sizeof(rx_buffer)) % sizeof(rx_buffer);
            uint16_t idx2 = (rx_index - 1 + sizeof(rx_buffer)) % sizeof(rx_buffer);
            if (rx_buffer[idx1] == '\r' && rx_buffer[idx2] == '\n') {
                uint16_t idx_ok = (rx_index -4 + sizeof(rx_buffer)) % sizeof(rx_buffer);
                uint16_t idx_err = (rx_index -7 + sizeof(rx_buffer)) % sizeof(rx_buffer);
                uint16_t idx_send_ok = (rx_index -7 + sizeof(rx_buffer)) % sizeof(rx_buffer);
                if ((rx_index >=4 && memcmp(&rx_buffer[idx_ok], "OK\r\n",4)==0) ||
                    (rx_index >=7 && memcmp(&rx_buffer[idx_err], "ERROR\r\n",7)==0) ||
                    (rx_index >=7 && memcmp(&rx_buffer[idx_send_ok], "SEND OK\r\n",7)==0)) {
                    flag = 1;
                }
            }
        }

        HAL_UART_Receive_IT(&huart1, &rx_byte, 1);
    }
}

// 主循环中的超时检测逻辑
void main(void)
{
    // ... 初始化代码 ...
    while(1)
    {
        // 检测超时
        if (rx_index > 0 && (HAL_GetTick() - last_rx_time) > RESP_TIMEOUT) {
            flag = 1;
            last_rx_time = HAL_GetTick(); // 重置计时,避免重复触发
        }

        if (flag) {
            // 处理完整响应
            process_response(rx_buffer, rx_index);
            // 重置缓冲区和标记
            rx_index = 0;
            flag = 0;
        }
    }
}

内容的提问来源于stack exchange,提问作者Mohamed Ali Mohamed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 00:34:57