2

I'm working on a windows application that receives data from a sensor at 600Hz. In two out of five cases, my IO thread reads the 4 bytes of data from the sensor successfully and passes it on to the GUI thread.

The problem is three out of five times, QSerialPort has inexplicable timeouts where QSerialPort's waitForReadyRead() returns false and serial.errorString() has a timeout error. In which case it will never read data. If I read from the serial port despite the timeout error I will read 2000+ bytes of data in the next waitForReadyRead which will be delivered in chunks which renders the realtime data reception aspect of my application obsolete.

I've tried using the readyRead() signal of the serial port but it exhibits the same behaviour ie. if the timeout error appears, no readyRead() signal is ever fired.

UPDATE: I am able to reproduce the issue with Qt's terminal example ([QT_INSTALL_EXAMPLES]/serialport/terminal) which uses a non-blocking read. The frequency of the bug is considerably less but it's definitely still there.

UPDATE: Using Serial Port Monitor, I can see that when it gets stuck, the Qt Terminal Example gets stuck on IOCTL_SERIAL_WAIT_ON_MASK, my example gets stuck on IRP_MJ_WRITE DOWN just after the IOCT_SERIAL_WAIT_ON_MASK. This never happens with other terminal softwares leading me to think the problem is definitely with Qt.

Pastebin of Serial Port Monitor Output

void IOThread::run(){

QSerialPort serial;
serial.setPortName(portname)
serial.setBaudRage(QSerialPort::Baud115200);
serial.setStopBits(QSerialPort::OneStop)
serial.setParity(QSerialPort::NoParity);
serial.setDataBits(QSerialPort::Data8);
serial.setFlowControl(QSerialPort::NoFlowControl);

if(!serial.open(QIODevice::ReadWrite)
{
    qDebug() << "Error Opening Port";
    return;
}
else
{
    qDebug() << "Error Message: " << serial.errorString() // prints "Unknown Error"
}

while(true)
{
    if(serial.waitForReadyRead(1000))
    {
        qDebug() << "Normal read";
        reception_buffer = serial.readAll();
    }
    else
    {
        qDebug() << "Timeout";
        /* serial.readAll() here will read nothing but force next read to read huge chunk of data */ 
        continue;
    }
}

// Process data...
}
Galaxy
  • 210
  • 4
  • 15
  • Is there any reason you are not using signal `QSerialPort::readyread()` instead of waiting fixed time to read the port? – Mohammad Kanan Mar 19 '18 at 23:34
  • I've tried using readyRead() the behaviour is the same. Sometime signal fires all the time, sometimes not at all. – Galaxy Mar 19 '18 at 23:38
  • When you say _Not at all_, does that happen while there are data on the serial buffer! – Mohammad Kanan Mar 20 '18 at 06:12
  • Yes, there is data in the buffer. If I put a serial.readAll() in the else of the timeout, I force the serialport to read but the data comes in chunks as described in the question. – Galaxy Mar 20 '18 at 08:41
  • 1
    Which Qt version is yours? look carefully at this bug report [QTBUG-33987](https://bugreports.qt.io/browse/QTBUG-33987) – Mohammad Kanan Mar 20 '18 at 08:58
  • I'm using Qt 5.10.1 (MinGW530_32). I've also tried using MSVC 2015 as my Qt compiler and the same problem appears. – Galaxy Mar 20 '18 at 09:18
  • Is this a real 16550 UART or a USB-attached serial bridge? – Ben Voigt Apr 09 '18 at 14:12

3 Answers3

0

Try if this makes any difference:

while (true) {
    QByteArray reception_buffer;
    if (serial.waitForReadyRead(1000)) {
        reception_buffer = serial.readAll();
        while (serial.waitForReadyRead(10)) {
            reception_buffer += serial.readAll();
        }
        qDebug() << "reception_buffer ready";
    }
    else {
        qDebug() << "Timeout";
    }
}

If you want to prevent from timeouting from waitForReadyRead you can set:

if(serial.waitForReadyRead(-1))

bool QSerialPort::waitForReadyRead(int msecs = 30000) will timeout after msecs milliseconds; the default timeout is 30000 milliseconds. If msecs is -1, the function will not time out.

talamaki
  • 5,324
  • 1
  • 27
  • 40
  • No difference, still the same problem of either normal operation 'reception_buffer ready' or no data 'Timeout'. If I use a timeout value of -1, my thread blocks forever. Closing the port and opening with another serial software reads data normally. – Galaxy Mar 20 '18 at 11:23
0

gets stuck on IOCTL_SERIAL_WAIT_ON_MASK

Most likelly a problem is in your HW or driver. QSP use asynchronous notification, based on WaitCommEvent. If WaitCommEvent get stuck - then a problem is in your device or driver (most likelly).

Denis Shienkov
  • 517
  • 3
  • 12
  • But this only happens when using QSerialPort. Other terminal softwares like RealTerm never have this issue (or at least not that I have been able to see) – Galaxy Mar 21 '18 at 11:05
  • Please try this code: https://pastebin.com/i16MtNvz, and paste a results. – Denis Shienkov Mar 21 '18 at 12:40
  • Have tried the code, two cases possible, normal output and hanging program. Please find attached: [link](https://pastebin.com/GK0HNPED) – Galaxy Mar 21 '18 at 13:48
0

Thanks to the guys at QSerialPort, this bug in Qt 5.10.1 is solved by applying this patch: https://codereview.qt-project.org/#/c/225277/

"QSP may ignore all the read events when the data comes to the device within opening. In this case, even re-opening of a device does not help. Reason is that the QWinOverlappedIoNotifier is enabled after than the startAsyncCommunication() called, that probably, leads to ignoring for all EV_RXCHAR events. A workaround is to enable the notifier before than any of I/O operation called." - Denis Shienkov, QSerialPort Maintainer

Galaxy
  • 210
  • 4
  • 15