Clicca qui per iscriverti.
Because of an issue with the hosting service, my website (together with other websites on the same provider) has been unavailable for three days. Now the problem is fixed and I’m back online.
Ever thought that you can make a torch using a glue stick?
Ever thought that you can hack an inductor into a transformer?
Here is the final result of a rather old project I’ve done in 2005, but never published until now.
Read the full article on my website.
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:
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:
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?
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:
Over time, I find myself more and more interested in CMake. That’s because I write many little programs, and those that use makefiles routinely break as I switch OS.
The last one was a program that depended on libpng. When I tried to compile it on Mac OS X, it failed to find the library. That’s because on Linux libpng.so is simply in /usr/lib and png.h is in /usr/include, both of which are in the compiler’s search path, so that all you need to do is add “-lpng” when linking.
But on a Mac libpng.dylib (yes, shared libraries on a Mac have .dylib extension) is in /usr/X11/lib and png.h is in /usr/X11/include that are not in the compiler’s search path, so that when compiling you need to add “-I/usr/X11/include” and when linking “-L/usr/X11/lib -lpng”.
The solution was not to keep two separate makefiles, but rather to throw away the makefile and replace it with a CMakeLists.txt
Which brings us to the question: how to link with libraries using CMake?
There are two ways, the first is the find_package command, the other is the find_library command.
Let’s start with find_package. CMake “knows” about many widely used libraries. For them, there is a script to find them in all supported platforms. So, to use a library all you need to do is find it with the find_package command. Here is a simple example of a program that uses threads and so depends on “-lpthread”, main.cpp:
#include <iostream> using namespace std; void *thread(void *argv) { cout<<"Into a spawned thread"<<endl; } int main() { pthread_t t; pthread_create(&t,NULL,thread,NULL); pthread_join(t,NULL); cout<<"Back in main thread"<<endl; }and here is the CMakeLists.txt file:
cmake_minimum_required(VERSION 2.6) project(TEST) ## Target set(TEST_SRCS main.cpp) add_executable(test ${TEST_SRCS}) ## Link libraries find_package(Threads REQUIRED) target_link_libraries(test ${CMAKE_THREAD_LIBS_INIT})As can be seen, the first parameter passed to find_package is the name of the package, the second is “REQUIRED” and means that if the library could not be found, CMake should stop and print an error message.
Once the library is found, you have to say which executable needs it (because a single CMakeLists.txt can be used to produce many executable by just using more add_executable commands). This is achieved with the target_link_libraries command that appends a library to the list of libraries an executable needs. The first parameter is the executable name, the second is the library. Note that find_library generates a variable that contains the name of the library, in this case the name is CMAKE_THREAD_LIBS_INIT. This strange name is an exception, usually all find_package scripts create a variable with the name <libraryname>_LIBRARY.
Now a more complex example: the libpng issue I talked about earlier. It is more complex because you don’t just need to add a library when linking, you also need to tell the compiler where is the png.h file when compiling. Luckily, CMake has a package for libpng that does all that, and here is the CMakeLists.txt example:
cmake_minimum_required(VERSION 2.6) project(TEST) ## Targets set(TEST_SRCS test.cpp) add_executable(test ${TEST_SRCS}) ## Link libraries find_package(PNG REQUIRED) include_directories(${PNG_INCLUDE_DIR}) target_link_libraries(test ${PNG_LIBRARY})The find_package command finds the PNG library, target_link_libraries adds the library to the list of libraries the executable needs, and include_directories adds the directory where the .h file is when compiling.
But this isn’t the end. Other than libraries there are collections of libraries. And CMake supports them too. One example are the boost libraries. There is no single libboost.so to link to; instead every sub-library has its .so file. So there should be a way to link only with the desired sub-libraries. This is an example CMakeLists.txt that does that:
cmake_minimum_required(VERSION 2.6) project(TEST) ## Target set(TEST_SRCS main.cpp) add_executable(test ${TEST_SRCS}) ## Link libraries set(BOOST_LIBS thread date_time system) find_package(Boost COMPONENTS ${BOOST_LIBS} REQUIRED) target_link_libraries(test ${Boost_LIBRARIES}) find_package(Threads REQUIRED) target_link_libraries(test ${CMAKE_THREAD_LIBS_INIT})In this case we initialize a variable with the sub-libraries we want (boost.thread, boost.date_time and boost.system). Then we call find_package with the library name (Boost), the word COMPONENTS followed by the list of sub-libraries and as usual the REQUIRED word. Since boost.thread depends on the system’s thread library, we also use another find_package command to link with threads.
This ends the examples of find_package, but there is one last issue: what if we need a library for which there isn’t a package script? The solution is to use the find_library command. It will search in the system paths for the needed library. Here is an example that uses the command to find the Poco libraries:
cmake_minimum_required(VERSION 2.6) project(TEST) ## Target set(TEST_SRCS main.cpp) add_executable(test ${TEST_SRCS}) ## Link libraries find_library(POCO_FOUNDATION PocoFoundation) find_library(POCO_NET PocoNet) target_link_libraries(test ${POCO_FOUNDATION} ${POCO_NET}) find_package(Threads REQUIRED) target_link_libraries(test ${CMAKE_THREAD_LIBS_INIT})The find_library command takes two parameters, the first is the variable where the found library will be stored, and the second is the library name (the name is camelcase in this example because Poco libraries are camelcase, the library name is really libPocoFoundation.so).
References: CMake wiki