Qt and serial ports
Update Jun 22, 2011 : Fixed a bug for compiling under visual studio 2010.
This is intended to be a small add-on to the article I’ve posted on my website which explains how to use boost’s asio library to interface with serial ports from C++ code. This blog post focuses on a topic not covered in the article: using serial ports in Qt GUI applications.
Since serial ports are used primarily to interface a computer with a microcontroller, we’ll focus on this case.
The additional problem that occurs when the application has a GUI is the result of two restrictions that GUI applications have:
- The thread that runs the GUI event loop should not block for long periods of time (say, more than a second). If this happens, the GUI “freezes” and this annoys users who think the application has crashed and usually force-quit it.
- Code that updates the GUI should not be called from threads different from the main thread.
Now, if the microcontroller never sends data to the serial port unless the computer sends a command to it, and replies are always fast there is no problem.
Let’s show a basic protocol of this kind:
- The PC can send the command “A” to the microcontroller.
- If the microcontroller receives “A”, it reads an ADC channel and sends the value back to the PC
As said, in this case there is no problem. A possible GUI would have a button to read the value, and a label to show the result.
Such an application can easily use the TimeoutSerial class, with code to write the letter “A” in the callback of the button, immediately followed by a read from the serial port to get the result and show it on the GUI. The timeout, set to an appropriate value (such as 500ms) would ensure that the GUI won’t freeze even if something goes wrong, and the programmer can catch the TimeoutException to show an error message to the user.
But what to do if the command takes much time to complete, say 10 seconds? In this case this simple approach won’t work, because the GUI will freeze for ten seconds with the main thread waiting for the response from the serial port.
And what to do with a protocol like this?
- The microcontroller sends the value read from the ADC 20 times a second.
In this case the PC application should always listen on the serial port for incoming data.
Well, who has read my article will probably think of using the AsyncSerial class, which allows to register a callback that will be called every time some data arrives from the serial port.
But also this approach won’t work, because of the constraint that the GUI code can be called only from the main thread, but AsyncSerial calls the callback from a separate thread.
To show how to solve this problem I’ve written a class, called QAsyncSerial which is a wrapper class for AsyncSerial that uses Qt’s system of signals and slots. It basically emits a signal every time a line is received from the serial port, and because signals and slots are thread safe, the code works as expected.
To show everything in practice, this is a simple GUI that uses QAsyncSerial running on Linux:
This simple GUI allows to select a serial port and open it (the baud rate is fixed to 115200, 8N1 format). Once it is opened, there is a line where to write text to send to the serial port while received text is showed below.
The code of the QAsyncSerial class and of the GUI example is posted at the end of the original article (here). It is meant to be compiled with QtCreator. If you try to compile it don’t forget to edit the line “-L/usr/local/lib” in the SerialGUI.pro file to point to where you have the boost libraries installed.
Last note, since the Qt libraries are cross platform, here’s the same application running on a Mac: