使用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\nERROR\r\nSEND 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,直接标记响应结束。
- 若最后4个字符是
- 简化判断代码示例:
// 考虑环形缓冲区的索引循环 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帧结束
- 状态1:等待
- 实现时需在中断里逐个字符解析数字,计算出
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
相关产品推荐
相关产品推荐

