Cannot read QSerialPort without event loop



  • Hi all,

    i am writing a library which uses QSerialPort and has no event loop. Sending data works fine, but i cannot receive any. QSerialPort does not report any errors.
    According to the documentation all QIODevices should work without an event loop, so i am missing something. But what am i missing?
    Using QT 5.9.3 on Win7 x64 with MinGW. My code:

    ...
    	if(!port.open(QIODevice::ReadOnly))
    	{
    		switch (port.error())
    		{
    			case QSerialPort::DeviceNotFoundError:
    			{
    				qCritical("Port with name \"%s\" not found", port.portName().toLocal8Bit().constData());
    				return PORT_NOT_EXIST;
    			}
    
    			case QSerialPort::PermissionError:
    			{
    				qCritical("No permission to open serial port");
    				return PORT_NO_ACCESS;
    			}
    
    			case QSerialPort::OpenError:
    			{
    				qCritical("Port is already in use");
    				return PORT_BUSY;
    			}
    
    			default:
    			{
    				qCritical("Unknown error while opening the port");
    				return PORT_OPEN_UNKNOWN;
    			}
    		}
    	}
    
    	std::thread thread(readPort, &port);
    	thread.join();
    
    	port.close();
    }
    
    void readPort(QSerialPort* port)
    {
    	for(qint64 startTime = QDateTime::currentSecsSinceEpoch()+10;QDateTime::currentSecsSinceEpoch() < startTime;std::this_thread::sleep_for(std::chrono::milliseconds(100)))
    	{
    		QByteArray data = port->read(100);
    		if(!data.isEmpty())
    		{
    			qInfo("Read data %s", data.constData());
    		}
    	}
    }
    


  • The documentation for QSerialPort says:

    QSerialPort provides a set of functions that suspend the calling thread until certain signals are emitted. These functions can be used to implement blocking serial ports:

    waitForReadyRead() blocks calls until new data is available for reading.
    waitForBytesWritten() blocks calls until one payload of data has been written to the serial port.

    I think you should try waitForReadyRead() instead of implementing your own pooling.


  • Qt Champions 2017

    @Leonardo

    I think you should try waitForReadyRead() instead of implementing your own pooling.

    he can try, but I think the problem stays the same: you need an event loop.

    that said, I have written a DLL (closed source, sorry) for serial communication. if that was called from a Qt program, all was fine.

    for non Qt programs, I changed it in a way to let QtSerialPort run in a QThread (a bit difficult), und created an event loop in the DLL if QCoreApplication::instance was NULL.

    maybe @kuzulis has some more comments?



  • The same documentation page for QSerialPort states that:

    A blocking serial port does not require an event loop and typically leads to simpler code.

    So I assumed that waitForReadyRead() might somehow handle this internally. If there's a QCoreApplication instance, maybe he could also call processEvents() inside his "for" to emulate a event loop.


  • Qt Champions 2017

    Oh ?!
    So even
    bool QIODevice::waitForReadyRead(int msecs)
    says
    "This function can operate without an event loop."

    Its not in a way we except ?


  • Qt Champions 2017

    @mrjj @Leonardo Oops, you may be right. Thinking again, my DLL may have used the signal driven approach - I cannot check this now.

    @Hetsh: waitForReadyRead may be worth a try.



  • Thank you all for your input. Using waitForReadyRead did unfortunately not change anything. I think instantiating QCoreApplication as long as it does not exist will be the best short term solution for this.
    Should this be reported as bug?


  • Qt Champions 2017

    @Hetsh said in Cannot read QSerialPort without event loop:

    Should this be reported as bug?

    Yes, please do (and add a link here). That way we dig deeper into the issue.

    Thanks.



  • There are no any bug. You need in QCoreApplication instance anyway. This even not an QtSerialPort issue.


  • Qt Champions 2017

    @kuzulis @mrjj @Leonardo @Hetsh

    I prepared a documentation update to clarify the status quo: https://codereview.qt-project.org/216669


  • Qt Champions 2017

    Hi @Hetsh,

    As @kuzulis correctly said (in the doc review), there are two examples that work without event loop:

    http://doc.qt.io/qt-5/qtserialport-creadersync-example.html
    http://doc.qt.io/qt-5/qtserialport-cwritersync-example.html

    So you will need a QCoreApplication, but not an event loop.



  • @aha_1980 The example works, but it is using the waitFor methods in the "main" thread, which i cannot do.
    @kuzulis I instantiated QCoreApplication both in the main thread and in the polling thread without any success.

    I did a quick test on linux and it works without a QCoreApplication instance or event loop.

    Back on windows I implemented the workaround from @aha_1980 by instantiating QCoreApplication in another thread and starting an event loop there. An important step is to move the port to the QCoreApplications thread. It won't work otherwise: https://pastebin.com/3sDcq3Hv

    I filed a bug report.

    Thank you all for your help :-)



  • Did creadersync example works for you?



  • @kuzulis yes it did



  • I have answered in your BR, and closed it as invalid, because you use QSP wrong.

    PS: It works even w/o of QCoreApplication instrance and w/o of event loop. But needs to use the QCoreApplication instance to avoid some future/others problems (it suggested everywhere in Qt, AFAIK).



  • @kuzulis Thank you. It works perfectly now :-)

    TLDR: My mistake was to not instantiate and open the QSerialPort in the thread i was reading from.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.