How to make QSerialPort from Qt5.13.1 work?

2020-03-20 09:32发布

问题:

Problem

QSerialPort from version 5.13.1 of the Qt library does not physically output data under Windows 7 and 10.

Example

In order to demonstrate the described problem I have prepared the following setup:

  1. Hardware

I have tested the connection between a PC with a physical serial port (COM1) and a real serial device, but for demonstration purposes I have created a simple loopback by connecting together pins 2 and 3 of the DSub connector of the PC, i.e. Tx and Rx.

  1. Software

The problem occurs in my own GUI applications, as well as in the official examples shipped with Qt. However, for the sake of the demonstration I wrote a very basic console app:

SerialBug.pro

QT -= gui
QT += serialport

CONFIG += console

SOURCES += \
        main.cpp

main.cpp

#include <QCoreApplication>
#include <QSerialPort>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QSerialPort port("COM1");

    port.setBaudRate(QSerialPort::Baud4800);
    port.setDataBits(QSerialPort::Data8);
    port.setStopBits(QSerialPort::OneStop);
    port.setParity(QSerialPort::NoParity);
    port.setFlowControl(QSerialPort::NoFlowControl);

    QObject::connect(&port, &QSerialPort::readyRead,
                     [&port](){
        qDebug() << port.readAll();
    });

    QObject::connect(&port, &QSerialPort::bytesWritten,
                     [](qint64 bytes){
        qDebug() << bytes;
    });

    QObject::connect(&port, &QSerialPort::errorOccurred,
                     [](QSerialPort::SerialPortError error){
        qDebug() << error;
    });

    if (port.open(QSerialPort::ReadWrite)) {
        qDebug() << port.write("Test");
    }

    return a.exec();
}

Result

Compiling and running the example code with MSVC2017 and Qt 5.13.0 in release mode, the following output is produced:

QSerialPort::NoError
4
4
"Test"

The exact same code compiled in release mode with MSVC2017, but this time with Qt 5.13.1, produces the following output:

QSerialPort::NoError
4

port.write returns 4, meaning 4 bytes are send to the serial port, but that is not actually done. bytesWritten is not emitted and the data is not read back.

Note: A serial monitor program is showing the written data, but the data does not reach the pins.

Is it possible to fix the code in any way in order to make it work with Qt5.13.1?

回答1:

Cause

Searching Qt bug tracker there seem to be multiple bugs about QSerialPort not working on Qt 5.13.1 on Windows. All of them are duplicated with QTBUG-78086 which also contains a link to Gerrit review of the fix.

From the bug description:

The signal readyRead is never emitted, even if data is sent to the serial port from a connected device. The member bytesAvailable returns 0 even if data has been sent to the serial port from a connected device.

Basically, they have tried to emit _q_notify in qwinoverlappedionotifier.cpp only if there's no notification pending. Unfortunatelly

That commit completely breaks the I/O on Windows.

Solution

For now you have the options to downgrade to 5.13.0, wait for Qt 5.13.2 or

Fix the Qt 5.13.1 qserialport yourself:

  • open QTDIR\5.13.1\Src\qtserialport\qtserialport.pro with QtCreator
  • (optional) you might need to select a kit, e.g. Projects -> Manage kits -> Desktop Qt 5.13.1 MSVC2017 64bit
  • in the project tree open src/serialport/serialport-lib/sources/qwinoverlappedionotifier.cpp
  • delete QAtomicInt pendingNotifications;
  • change

    if (!waiting && pendingNotifications-- == 0)
        emit q->_q_notify();
    

    to

    if (!waiting)
        emit q->_q_notify();
    
  • change

    int n = pendingNotifications.fetchAndStoreAcquire(0);
    while (--n >= 0) {
        if (WaitForSingleObject(hSemaphore, 0) == WAIT_OBJECT_0)
            dispatchNextIoResult();
    }
    

    to

    if (WaitForSingleObject(hSemaphore, 0) == WAIT_OBJECT_0)
        dispatchNextIoResult();
    
  • In QtCreator go to build -> clean all, then run qmake, then rebuild all

  • locate the build folder, then copy and replace Qt5SerialPort.dll and Qt5SerialPortd.dll from build\bin to QTDIR\5.13.1\msvc2017_64\bin

Your code should work now.



标签: c++ windows qt qt5