为何Qt中调用readAll()才会持续触发串口的readyRead信号?
Qt串口读取Arduino数据的readyRead信号触发异常问题
问题现象
用Qt的QSerialPort读取Arduino发送的double类型数据时,出现以下异常:
- 槽函数
newData中执行readAll()时,readyRead信号反复触发,槽函数持续执行(终端不断打印bonjour je recoit une donnée)。 - 注释掉
readAll()后,readyRead信号仅触发一次,槽函数只执行一次。
两种场景的终端输出
场景1:执行readAll()时的输出
qt.core.qmetaobject.connectslotsbyname: QMetaObject::connectSlotsByName: No matching signal for on_on_clicked() qt.core.qmetaobject.connectslotsbyname: QMetaObject::connectSlotsByName: No matching signal for on_off_clicked() conecion ok bonjour je recoit une donnée bonjour je recoit une donnée bonjour je recoit une donnée ...(持续重复打印)
场景2:注释readAll()后的输出
13:07:59: Démarrage de C:\Users\user\Documents\GUI2\debug\monappli.exe... qt.core.qmetaobject.connectslotsbyname: QMetaObject::connectSlotsByName: No matching signal for on_on_clicked() qt.core.qmetaobject.connectslotsbyname: QMetaObject::connectSlotsByName: No matching signal for on_off_clicked() conecion ok bonjour je recoit une donnée
相关代码
seriallink.h
#ifndef SERIALLINK_H #define SERIALLINK_H #pragma once #include <QObject> #include <QSerialPort> class seriallink : public QObject { Q_OBJECT public: explicit seriallink(QObject *parent = nullptr); ~seriallink(); //destructor void openConnection(); void closeConnection(); void write (const char* messageToWrite); bool isOpen(); bool isWritable(); void data_process(QByteArray read); private slots: void newData(); signals: void gotNewData(QByteArray data); void availableData(QByteArray data); private: QByteArray _data; QSerialPort _serial; }; #endif // SERIALLINK_H
seriallink.cpp
#include "seriallink.h" #include <QDebug> #include <iostream> seriallink::seriallink(QObject *parent) : QObject{parent} { _serial.setPortName("COM3"); _serial.setBaudRate(QSerialPort::Baud9600); _serial.setDataBits(QSerialPort::Data8); _serial.setParity(QSerialPort::NoParity); _serial.setStopBits(QSerialPort::OneStop); _serial.setFlowControl(QSerialPort::NoFlowControl); _serial.setReadBufferSize(QSerialPort::Data8); connect(& _serial, &QSerialPort::readyRead, this, &seriallink::newData); //connect(this, &seriallink::gotNewData, this, &seriallink::data_process); } seriallink::~seriallink(){ closeConnection(); } void seriallink::openConnection(){ if(!_serial.open(QIODevice::ReadWrite)) qDebug()<<"impossible connection"; else qDebug() <<"conecion ok"; } void seriallink::closeConnection(){ _serial.close(); } void seriallink::write(const char* messageToWrite){ _serial.write(messageToWrite); } void seriallink::newData(){ qDebug()<<"bonjour je reçoit une donnée"; _serial.readAll(); } bool seriallink::isOpen(){ return _serial.isOpen(); } bool seriallink::isWritable() { return _serial.isWritable(); }
mainwindow.cpp(核心初始化部分)
#include "mainwindow.h" #include "ui_mainwindow.h" #include <iostream> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); ihmGraphique = new IhmGraphique(); ui->scrollArea->setWidget(ihmGraphique->get_graphique()); arduino= new seriallink; arduino->openConnection(); file=new datafile(nullptr, "DATA.txt"); } // 其余UI操作代码省略
Arduino发送的数据格式
2.10,8.28,6.34,7.49,5.99,1.09,9.14,8.31,9.91,9.81,5.27,2
参考代码(YouTube开发者实现,无readAll()仍持续触发)
#include "dialog.h" #include "ui_dialog.h" #include <QSerialPort> #include <QSerialPortInfo> #include <string> #include <QDebug> #include <QMessageBox> #include <iostream> Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog) { ui->setupUi(this); ui->temp_lcdNumber->display("-------"); arduino = new QSerialPort(this); serialBuffer = ""; parsed_data = ""; temperature_value = 0.0; bool arduino_is_available = true; if(arduino_is_available){ qDebug() << "Found the arduino port...\n"; arduino->setPortName("COM3"); arduino->open(QSerialPort::ReadOnly); arduino->setBaudRate(QSerialPort::Baud9600); arduino->setDataBits(QSerialPort::Data8); arduino->setFlowControl(QSerialPort::NoFlowControl); arduino->setParity(QSerialPort::NoParity); arduino->setStopBits(QSerialPort::OneStop); QObject::connect(arduino, SIGNAL(readyRead()), this, SLOT(readSerial())); }else{ qDebug() << "Couldn't find the correct port for the arduino.\n"; QMessageBox::information(this, "Serial Port Error", "Couldn't open serial port to arduino."); } } Dialog::~Dialog() { if(arduino->isOpen()){ arduino->close(); } delete ui; } void Dialog::readSerial() { QStringList buffer_split = serialBuffer.split(","); qDebug() << serialBuffer << "\n"; if(false){ if(buffer_split.length() < 3){ serialData = arduino->readAll(); serialBuffer = serialBuffer + QString::fromStdString(serialData.toStdString()); serialData.clear(); }else{ serialBuffer = ""; qDebug() << buffer_split << "\n"; parsed_data = buffer_split[1]; temperature_value = (9/5.0) * (parsed_data.toDouble()) + 32; qDebug() << "Temperature: " << temperature_value << "\n"; parsed_data = QString::number(temperature_value, 'g', 4); Dialog::updateTemperature(parsed_data); } } } void Dialog::updateTemperature(QString sensor_reading) { ui->temp_lcdNumber->display(sensor_reading); }
问题原因分析
- QSerialPort的readyRead信号机制:
readyRead仅在串口接收缓冲区从空变为非空时触发。如果不读取缓冲区中的数据,缓冲区会一直处于非空状态,后续新数据到来时,缓冲区不会触发"从空到非空"的变化,因此readyRead不会再次触发。 - 代码差异说明:参考代码中
if(false)分支被跳过,但它的串口是只读模式,且如果去掉该判断,实际也会读取数据;你的代码用了读写模式,但核心问题还是未读取数据导致缓冲区一直非空,信号无法再次触发。另外你设置的_serial.setReadBufferSize(QSerialPort::Data8);是错误的——QSerialPort::Data8是数据位枚举值(值为8),会把缓冲区大小设为8字节,进一步加剧了数据堆积问题。
解决方案
- 必须读取缓冲区数据:无论是否立即处理,都要读取串口缓冲区的数据,否则
readyRead信号不会再次触发。可以将读取的数据存入自定义缓冲区,后续处理完整数据包:
修改newData槽函数并添加数据处理逻辑:void seriallink::newData(){ qDebug()<<"bonjour je reçoit une donnée"; // 读取数据并存入自定义缓冲区 _data += _serial.readAll(); // 处理完整数据包(假设Arduino每行发送一组数据) processReceivedData(); } // 新增完整数据包处理函数 void seriallink::processReceivedData(){ int newlineIndex = _data.indexOf('\n'); while(newlineIndex != -1){ QByteArray line = _data.left(newlineIndex); _data = _data.mid(newlineIndex + 1); // 发射信号通知上层处理完整数据 emit gotNewData(line); newlineIndex = _data.indexOf('\n'); } } - 修正缓冲区大小设置:移除错误的缓冲区大小设置,或设置合理值:
// 移除错误设置,或改为合理大小 // _serial.setReadBufferSize(QSerialPort::Data8); _serial.setReadBufferSize(1024); - 处理数据包完整性:
readyRead不保证一次性接收完整数据包,必须通过自定义缓冲区缓存数据,直到接收到完整的分隔标记(如换行符)再解析,避免数据截断。
内容的提问来源于stack exchange,提问作者Tony Abadia
相关产品推荐
相关产品推荐

