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

为何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

相关代码

#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
#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);
}

问题原因分析

  1. QSerialPort的readyRead信号机制:readyRead仅在串口接收缓冲区从空变为非空时触发。如果不读取缓冲区中的数据,缓冲区会一直处于非空状态,后续新数据到来时,缓冲区不会触发"从空到非空"的变化,因此readyRead不会再次触发。
  2. 代码差异说明:参考代码中if(false)分支被跳过,但它的串口是只读模式,且如果去掉该判断,实际也会读取数据;你的代码用了读写模式,但核心问题还是未读取数据导致缓冲区一直非空,信号无法再次触发。另外你设置的_serial.setReadBufferSize(QSerialPort::Data8);是错误的——QSerialPort::Data8是数据位枚举值(值为8),会把缓冲区大小设为8字节,进一步加剧了数据堆积问题。

解决方案

  1. 必须读取缓冲区数据:无论是否立即处理,都要读取串口缓冲区的数据,否则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');
        }
    }
    
  2. 修正缓冲区大小设置:移除错误的缓冲区大小设置,或设置合理值:
    // 移除错误设置,或改为合理大小
    // _serial.setReadBufferSize(QSerialPort::Data8);
    _serial.setReadBufferSize(1024);
    
  3. 处理数据包完整性:readyRead不保证一次性接收完整数据包,必须通过自定义缓冲区缓存数据,直到接收到完整的分隔标记(如换行符)再解析,避免数据截断。

内容的提问来源于stack exchange,提问作者Tony Abadia

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 02:36:59