如何解析AT命令返回的C字符串以判断Quectel模块FOTA固件升级完成
解决思路与实现方案
我之前处理过不少Quectel模块的FOTA升级场景,这个需求其实很常见,核心就是处理AT命令的流式响应输出——模块会持续推送状态信息,直到升级完成(或失败)。咱们一步步拆解怎么实现你的需求:
核心逻辑步骤
- 先建立并配置好与模块的通信连接(串口/虚拟串口,确保波特率、数据位、奇偶校验等参数和模块完全匹配)
- 发送
AT+QFOTADL="https://www.quectel.com:100/update.zip"命令后,立刻进入循环读取流程 - 每次读取一段数据,将其追加到缓冲区(避免目标结束标识被拆分到两次读取中)
- 持续检查缓冲区中是否出现目标字符串
+QIND: "FOTA","END",0:- 匹配到则退出循环,判定升级成功完成
- 未匹配到就继续读取,同时要设置超时机制,防止升级异常时程序无限挂起
具体实现示例
1. Python脚本(适合PC端调试/自动化场景)
用pyserial库操作串口是快速验证的首选方案:
import serial import time def wait_for_fota_complete(serial_port, timeout=3600): # 初始化缓冲区和超时计时 recv_buffer = "" start_time = time.time() while time.time() - start_time < timeout: # 读取串口内所有可用数据 if serial_port.in_waiting > 0: raw_data = serial_port.read(serial_port.in_waiting).decode('utf-8') recv_buffer += raw_data print(f"收到状态: {raw_data.strip()}") # 检查成功结束标识 if '+QIND: "FOTA","END",0' in recv_buffer: print("✅ FOTA固件升级完成!") return True # 可选:处理升级失败的情况 elif '+QIND: "FOTA","END",1' in recv_buffer: print("❌ FOTA固件升级失败!") return False time.sleep(0.1) # 短暂延时,避免CPU占用过高 print("⏰ 升级超时,请检查模块状态!") return False # 配置串口参数(根据你的模块实际情况修改) ser = serial.Serial( port='COM3', # Windows端口,Linux为/dev/ttyUSB0这类路径 baudrate=115200, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_1, bytesize=serial.EIGHTBITS, timeout=1 ) # 发送FOTA升级命令 ser.write(b'AT+QFOTADL="https://www.quectel.com:100/update.zip"\r\n') time.sleep(0.5) # 等待升级完成 wait_for_fota_complete(ser) ser.close()
2. 嵌入式C语言(适合MCU对接模块的场景)
如果是在嵌入式设备上实现,核心是通过UART读取数据并维护缓冲区,示例如下:
#include <string.h> #include "uart_driver.h" // 替换为你的MCU UART驱动头文件 #define FOTA_SUCCESS_FLAG "+QIND: \"FOTA\",\"END\",0" #define FOTA_FAILED_FLAG "+QIND: \"FOTA\",\"END\",1" #define BUFFER_MAX_SIZE 512 uint8_t uart_recv_buf[BUFFER_MAX_SIZE]; uint16_t buf_len = 0; // 获取系统tick的函数,需根据你的MCU实现替换 uint32_t get_system_tick(void); // 毫秒延时函数,需根据你的MCU实现替换 void delay_ms(uint16_t ms); int check_fota_result(void) { uint32_t start_tick = get_system_tick(); // 设置1小时超时(可根据固件大小调整) while (get_system_tick() - start_tick < 3600 * 1000) { uint8_t recv_byte; // 循环读取UART接收到的每一个字节 while (uart_read_byte(&recv_byte)) { if (buf_len < BUFFER_MAX_SIZE - 1) { uart_recv_buf[buf_len++] = recv_byte; uart_recv_buf[buf_len] = '\0'; // 确保字符串以'\0'结尾 // 检查成功标识 if (strstr((char*)uart_recv_buf, FOTA_SUCCESS_FLAG) != NULL) { return 1; // 返回1表示升级成功 } // 检查失败标识 if (strstr((char*)uart_recv_buf, FOTA_FAILED_FLAG) != NULL) { return -1; // 返回-1表示升级失败 } } else { // 缓冲区满时重置,避免溢出 buf_len = 0; memset(uart_recv_buf, 0, BUFFER_MAX_SIZE); } } delay_ms(10); } return 0; // 返回0表示超时 } // 使用示例 int main(void) { uart_init(115200); // 初始化UART波特率 // 发送FOTA升级命令 uart_send_string("AT+QFOTADL=\"https://www.quectel.com:100/update.zip\"\r\n"); int fota_result = check_fota_result(); if (fota_result == 1) { // 升级成功后的处理逻辑 } else if (fota_result == -1) { // 升级失败后的处理逻辑 } else { // 超时后的处理逻辑 } while(1); }
关键注意事项
- 缓冲区累积:一定要用缓冲区存储读取到的所有数据,因为目标结束标识可能被拆分成多次UART发送,单次读取可能只拿到一半内容。
- 超时机制:必须设置合理的超时时间(FOTA升级耗时取决于固件大小,从几分钟到几十分钟都有可能),避免程序无限挂起。
- 错误处理:别忘了处理
+QIND: "FOTA","END",1的情况,这代表升级失败,需要做对应的错误排查或重试逻辑。
内容的提问来源于stack exchange,提问作者John




