QTSerialPort Read/Write doesn't properly work on Windows



  • Hello, I'm trying to write a code for my application. My goal is to write a bunch of bits to the serial port and in this time read the serial port for a reply from the connected device. While I'm writing to the serial port and reading from the serial port, also I want to measure elapsed time. I'm not really experience with QT and I'm facing with some problems.

    I started from a basic application which read serial port, but it's not working as it's supposed to work. I tested my code in Linux machine, it's work flowless, but in windows, it's not reading serial port.

    In Linux, when I run the code, it listens until i close the program. Whatever I sent from serial port, It directly print to the terminal. But in windows, it just waits and doesn't read anything. I also set my buffer to smaller but that's also didn't work. But when I uncomment that //selectedPort->waitForReadyRead(5000); part in windows, It works and read serial port for 5 sec. Then it's idle without reading anything.

    On the other hand, I want to measure the time elapsed during writing and reading. I'm trying to write 1MB of data, with 115200baudrate which is around 11,25kb/sec as my calculation. But when I try with my code it returns 1-2ms which seems wrong. I created 16kb of data with a for-loop. I suppose to take more than 1sec but my code returns 0ms. I think, I don't know how to make it properly. Also i need this kind of stuff also for reading.

    I'm looking for any kind of suggestion for my application. Sorry for my mistakes.

    myserial.cpp

    MySerial::MySerial(QString portn, uint32_t baudrate){
        selectedPort = new QSerialPort;
        this->baudrate = baudrate;
        selectedPort->setPortName(portn);
        selectedPort->setBaudRate(static_cast<qint32>(baudrate));
        selectedPort->open(QIODevice::ReadWrite);
        connect(selectedPort, SIGNAL(readyRead()),this, SLOT(MyReadyRead()));
        //selectedPort->waitForReadyRead(5000);
    
    }
    
    void MySerial::MyReadyRead()
    {
        QByteArray myData = selectedPort->readAll();
        std::cout << myData.toStdString();fflush(stdout);
    }
    
    
    void MySerial::startWrite(QByteArray myData){
        myTime.start();
        for(long i=0; i<1024;i++){ // 16byte -> 16kb
            selectedPort->write(myData);
        }
        std::cout << "Time elapsed(in ms):" <<myTime.elapsed()<<std::endl;
    }
    
    

    main.cpp

    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
        MySerial *s0 = new MySerial("COM6",115200);
        QByteArray testData = "1111111111111111";//
                              // 16 char = 16byte of data
    
        s0->startWrite(testData);
    
    
        return a.exec();
    }
    
    

  • Lifetime Qt Champion

    Hi and welcome to devnet,

    You should check whether your open call is successful. Then you should also check for errors in your code.

    Are you sure that you are opening the correct com port ?


  • Lifetime Qt Champion

    @canerbldk and to add to @sgaist, which Qt version are you using? there has been a bug recently which influenced the receiving of characters.

    Regards



  • @SGaist with //selectedPort->waitForReadyRead(5000); i'm able to read correctly and i also tested my open function seems correct.

        if(selectedPort->open(QIODevice::ReadWrite))
        std::cout << "Port " << portn.toStdString() << "is open" << std::endl;
    
    

    @aha_1980 I'm using Qt 5.13.1 (MSVC 2017, 32 bit)

    I also created 2 virtual serial comm. port to test everything. I connected one port to Docklight and the other one to my code, I received and delivered everything correctly.



  • @canerbldk

    std::cout << myData.toStdString();fflush(stdout);

    Why use fflush(stdout), are you sure std::cout is using the stdout buffer like that? Prefer

    cout.flush();
    //  or
    cout << ... << flush;
    
    

    Next:
    In startWrite() you time QSerialPort::write() operations. I am not an expert on serial I/O, but in general that returns immediately only guaranteeing to queue the output, not execute it. You have signals like QIODevice::bytesWritten or function QSerialPort::waitForBytesWritten() for that. Also you do not check the return result from QSerialPort::write(), it might be returning less bytes than you sent or even -1 for error. And you don't have any calls to QSerialPort::flush() or handler for QSerialPort::errorOccurred, just in case. @SGaist mentioned this.

    The above two might behave differently on Windows vs Linux, so worth just checking.



  • @JonB I modified my code a bit as much as what I understood. I added an error handler and I modified fflush(stdout). Nothing changed. I don't get any error. I'm still not able to read without //selectedPort->waitForReadyRead(5000);. Also, I have the same problem for bytesWritten. I have to add //selectedPort->waitForBytesWritten(3000); to trigger MyBytesWritten() function.

    On the other hand, although I run the same code twice, I'm getting different outputs, in terms of elapsed time. My guess, I'm measuring data write time to write-buffer, instead of write time to the serial port.

    I still need some help to figure out how to measure the time elapsed correctly. Thank you for your help.

    main.cpp

    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        MySerial *s0 = new MySerial("COM6",115200);
        QByteArray testData = "1111111111111111";//
                              // 16 char = 16byte of data
    
        s0->startWrite(testData);
        s0->startListen();
        s0->startWrite("C");
    
        return a.exec();
    }
    

    myserial.cpp

    #include "myserial.h"
    #include <stdio.h>
    #include <iostream>
    #include <unistd.h>
    #include <QCoreApplication>
    #include <QObject>
    
    
    MySerial::MySerial(QString portn, uint32_t baudrate)
    {
        selectedPort = new QSerialPort;
        this->baudrate = baudrate;
        selectedPort->setPortName(portn);
        selectedPort->setBaudRate(static_cast<qint32>(baudrate));
        if(selectedPort->open(QIODevice::ReadWrite))
        std::cout << "Port " << portn.toStdString() << " is open" << std::endl;
    
        connect(selectedPort, &QSerialPort::errorOccurred, this, &MySerial::handleError);
        connect(selectedPort, SIGNAL(readyRead()),this, SLOT(MyReadyRead()));
        connect(selectedPort,SIGNAL(bytesWritten(qint64)),this,SLOT(MyBytesWritten(qint64)));
    
        myTime.start();
    }
    
    void MySerial::MyReadyRead()
    {
        QByteArray myData = selectedPort->readAll();
        std::cout << myData.toStdString();
        std::cout.flush()<<std::endl;
    }
    
    void MySerial::MyBytesWritten(qint64){
        selectedPort->flush();
        std::cout << "MyBytesWritten(in ms):" <<myTime.elapsed()<<std::endl;
    }
    
    MySerial::~MySerial(){
        selectedPort->close();
        selectedPort->disconnect();
    }
    
    void MySerial::startWrite(QByteArray myData){
        std::cout << "Time elapsed(in ms):" <<myTime.elapsed()<<std::endl;
        myTime.restart();
        for(long i=0; i<1024;i++){ // byte -> kilobyte
            selectedPort->write(myData);
        }
        selectedPort->waitForBytesWritten(3000);
        myTime.restart();
    }
    
    void MySerial::startListen()
    {
         while(selectedPort->waitForReadyRead(3000)){
             std::cout << "Read Data" <<myTime.elapsed()<<std::endl;
             MyReadyRead();
         }
    }
    
    void MySerial::handleError(QSerialPort::SerialPortError serialPortError)
    {
        if (serialPortError == QSerialPort::ReadError) {
              m_standardOutput << QObject::tr("An I/O error occurred while reading "
                                              "the data from port %1, error: %2")
                                  .arg(selectedPort->portName())
                                  .arg(selectedPort->errorString())
                               << endl;
            QCoreApplication::exit(1);
        }
    }
    

    output1

    Port COM6 is open
    Time elapsed(in ms):0
    MyBytesWritten(in ms):606
    ----o Ping
    ----o Ping
    Time elapsed(in ms):3001
    MyBytesWritten(in ms):27
    

    output2

    Port COM6 is open
    Time elapsed(in ms):0
    MyBytesWritten(in ms):644
    ----o Ping
    ----o Ping
    Time elapsed(in ms):3001
    MyBytesWritten(in ms):28
    

  • Moderators

    @canerbldk said in QTSerialPort Read/Write doesn't properly work on Windows:

    I'm using Qt 5.13.1 (MSVC 2017, 32 bit)

    jepp, IIRC that's one of the versions where the signals are not emitted correctly.
    5.13.2 should be out and it should work there, if I'm not mistaken. @aha_1980 should be able to correct me if I'm wrong :)


  • Lifetime Qt Champion



  • @J-Hilk OH my god! I'm trying to fix that problem for a week. I tried lots of things to understand why it's not working correctly on windows, but I never think about QT version :( Thanks a lot for your help !!!

    But, I'm still looking for a suggestion for read-write timing measurement. As I stated before I'm thinking that I'm measuring write-to-buffer time while I need write-to-serial timing. Thanks in advance


  • Lifetime Qt Champion

    @canerbldk said in QTSerialPort Read/Write doesn't properly work on Windows:

    As I stated before I'm thinking that I'm measuring write-to-buffer time while I need write-to-serial timing.

    That is hardly possible, as there are many buffers involved: in your app, in Qt, in the OS, in the driver, ...

    What you can do is to time the round trip i.e. send a command and measure the time until the answer comes back.

    Regards


Log in to reply