基于PlatformIO Arduino框架的Qt5读取ESP32串口接收字节数不稳定问题解决
串口分片接收的原因说明
- 每次
readyRead触发仅收到1-2字节属于完全正常的系统行为,并非代码或硬件故障:- Qt的
QSerialPort抛出readyRead信号的触发逻辑是:只要操作系统内核的串口接收缓冲区出现新数据,就立即向应用层通知,不会等待、攒够一整段业务数据再触发信号。 - 9600波特率下单字节传输耗时约1ms,串口驱动的触发阈值极低,往往每接收1-2个字节就会向应用层抛一次信号,最终导致ESP32连续发送的"Hello"字符串被拆成多次回调送达。
- Qt的
- 现有代码存在一个容易忽略的逻辑bug:每次点击连接按钮,都会重复将
readyRead信号绑定到对应槽函数,多次操作后同一信号会触发多次槽执行,引发串口数据重复读取、打印混乱的问题。
可落地的优化方案
1. 基础逻辑修复
- 信号绑定前先清除旧连接,避免重复触发:
void Dialog::on_connect_clicked() { if(esp_available){ // 原有串口配置代码保持不变 esp->setPortName(espPortName); esp->open(QSerialPort::ReadWrite); esp->setBaudRate(9600); esp->setDataBits(QSerialPort::Data8); esp->setParity(QSerialPort::NoParity); esp->setStopBits(QSerialPort::OneStop); esp->setFlowControl(QSerialPort::NoFlowControl); // 替换原有的connect逻辑 disconnect(esp, &QSerialPort::readyRead, this, &Dialog::on_startStream_clicked); connect(esp, &QSerialPort::readyRead, this, &Dialog::on_startStream_clicked, Qt::UniqueConnection); } }
- 在Dialog类的头文件中维护一个累计接收的成员变量,避免零散读取导致的数据拆分:
// 在Dialog类的private成员区添加 QByteArray rxBuffer;
- 修改接收槽函数,把每次触发收到的零散字节先追加到累计缓冲区,不直接处理打印:
void Dialog::on_startStream_clicked() { if(!esp->isOpen()) return; rxBuffer.append(esp->readAll()); // 后续在此处添加对应需求的处理逻辑 }
2. 匹配两种需求的实现
场景1:逐字符连续无拆分展示
只要将累计缓冲区中的数据统一打印,再清空缓冲区即可,输出效果为连续的完整字符串,不会出现单字符拆分的零散输出:
// 追加完rxBuffer之后 qDebug() << rxBuffer; rxBuffer.clear();
场景2:每次读取获取完整业务数据包
串口本质是无边界的字节流,不存在天然的"完整包"判断标准,必须在Qt和ESP32两端预先约定统一的包边界规则,才能准确拆分出完整数据包。
- 固定长度包方案:如果ESP32发送的内容长度固定(比如"Hello"固定为5字节),可按固定长度截取出完整包:
// 循环判断缓冲区是否够一个完整包长度 while(rxBuffer.size() >= 5) { QByteArray completePacket = rxBuffer.left(5); rxBuffer.remove(0, 5); // 从缓冲区移除已经处理完的完整包 qDebug() << "收到完整数据包:" << completePacket; }
- 结束符标识方案:如果发送的内容长度不固定,可在ESP32端发送完有效内容后追加固定结束符(比如换行符
\n),Qt端检测到结束符即代表收到完整包:
对应ESP32端代码修改:
if (val == 1) { Serial.write("Hello"); Serial.write('\n'); // 追加固定结束符 }
Qt端的完整包判断逻辑:
// 循环检测缓冲区中是否存在结束符 while(rxBuffer.contains('\n')) { int endPos = rxBuffer.indexOf('\n'); QByteArray completePacket = rxBuffer.left(endPos); rxBuffer.remove(0, endPos + 1); // 连同结束符一起从缓冲区移除 qDebug() << "收到完整数据包:" << completePacket; }
内容的提问来源于stack exchange,提问作者Carlos Muñiz
相关产品推荐
相关产品推荐

