After showing how to build a minimal Linux system for the STM32MP157 platform, how to connect and use an I2C based pressure/temperature/humidity sensor and how to integrate Qt5 in our system, in this blog post, we are going to see how to set up a development environment to write our own Qt5 application, with QtCreator.
List of articles in this series:
- Building a Linux system for the STM32MP1: basic system
- Building a Linux system for the STM32MP1: connecting an I2C sensor
- Building a Linux system for the STM32MP1: enabling Qt5 for graphical applications
- Building a Linux system for the STM32MP1: setting up a Qt5 application development environment
- Building a Linux system for the STM32MP1: developing a Qt5 graphical application
- Building a Linux system for the STM32MP1: implementing factory flashing
- Building a Linux system for the STM32MP1: remote firmware updates
A minimal Qt5 application
We’ll call our application qt-sensor-demo
, so create a directory with this name, outside of Buildroot. It’s important to not mix up your application code with your build system: you could very well decide to use another build system one day, while keeping your application code. To keep things simple, create this qt-sensor-demo
side-by-side with Buildroot, as this will be important for a future step in this blog post.
In this directory, create a main.cpp
file with the following code:
#include <QApplication> #include <QPushButton> int main(int argc, char* argv[]) { QApplication app(argc, argv); QPushButton hello("Hello world!"); hello.resize(100,30); hello.show(); return app.exec(); }
It should be fairly straight-forward to understand that this program creates a QApplication object, a push button with the Hello world! label, sets the button size to 100 by 30 pixels, shows the button, and enters the application event loop. It is obviously a very basic application, because it doesn’t do anything useful, but that’s good enough as a starting point.
Now, we need to build this application. Building Qt applications by hand is definitely not reasonable, as Qt may need to run several tools on the source code before it gets built, and requires a number of compiler and linker flags. So, we won’t write a Makefile by hand, but instead use a build tool that generates the Makefile for us. We have a number of options here:
- qmake, the build tool provided by Qt itself
- cmake, with its Qt5 integration
- autotools, with the ax_have_qt macro from autoconf-archive
- meson with its Qt5 module
In this blog post, we’ll simply stick to qmake, which is good enough for a number of Qt-based applications. qmake takes as input one or several .pro
files describing the project, and uses that to generate Makefiles (on Linux systems).
In our case, the qt-sensor-demo.pro
file will be as simple as:
QT += widgets SOURCES = main.cpp
Building our application
We have two ways to build our application:
- Manually outside of Buildroot. In this case, we’ll use the Buildroot-provided compiler and tools, but we will trigger the build of our application separately from the Buildroot build.
- Using Buildroot. In this case, our application would have a corresponding Buildroot package, that would automate building the application as part of the complete system build process.
Ultimately, we definitely want to have a Buildroot package for our application, to make sure the entire build is fully automated. However, during the active development of the application, it may be useful to build it manually outside of Buildroot, so we are going to see both solutions, which are not mutually exclusive: you can have a Buildroot package for your application, and still build it manually when you’re doing active development/debugging.
Building manually outside of Buildroot
To build manually, we simply need to first invoke Buildroot’s provided qmake
:
/path/to/buildroot/output/host/bin/qmake
This will generate a Makefile
, that we can use to build our application:
make
At this point, you should have:
$ ls main.cpp main.o Makefile qt-sensor-demo qt-sensor-demo.pro
The qt-sensor-demo
executable is compiled for ARM, and linked against the various libraries built by Buildroot.
Now, we need this executable on our STM32MP15 target. For now, we’ll simply add it to the SD card image:
cp qt-sensor-demo /path/to/buildroot/output/target/usr/bin/ cd /path/to/buildroot/ make
This will copy the executable to the output/target
folder, which contains the root filesystem produced by Buildroot. Then invoking Buildroot’s make
will ensure that the root filesystem and SD card images get re-generated. Of course, beware that if you run a Buildroot make clean
, all the contents of output/
, including output/target/
get removed. So this technique is only suitable for temporary changes. This is fine since anyway as discussed above, ultimately we’ll have a proper Buildroot package to build our qt-sensor-demo application.
Reflash your SD card with the new image, and on the target, run the demo:
# qt-sensor-demo -platform linuxfb
Setting SSH for communication with the board
Regenerating the SD card image and reflashing the entire SD card every time we want to change our application is not going to be very efficient during the application development/debugging. So instead, we’ll set up networking communication with the board, and use SSH to transfer files. This will also be useful for Qt Creator, as it uses SFTP to deploy files to the target.
Let’s start by enabling a small SSH client/server, called Dropbear. Go in Buildroot menuconfig, and enable the BR2_PACKAGE_DROPBEAR
option (in Target packages, Networking applications, dropbear). While Dropbear provides SSH access, it does not support SFTP which will be needed by Qt Creator, so we’ll also enable an SFTP server, gesftpserver. So, we’ll enable BR2_PACKAGE_GESFTPSERVER
as well (in Target packages, Networking applications, gesftpserver).
Then, in order to log in through SSH as root, we must have a non-empty root password, so set BR2_TARGET_GENERIC_ROOT_PASSWD
(in System configuration, Root password) to a value you like.
You can now exit menuconfig, as we have enabled all features we needed. Before restarting the build, we need to do one last thing: set up a network configuration file so that our STM32MP15 system configures an IP address. To do this, we’ll create a /etc/network/interfaces
file, and add it to the root filesystem using the root filesystem overlay mechanism, which was presented in the first post of this series. So, in your Buildroot sources, just create a file board/stmicroelectronics/stm32mp157-dk/overlay/etc/network/interfaces
, with the following contents:
auto lo iface lo inet loopback auto eth0 iface eth0 inet static address 192.168.42.2 netmask 255.255.255.0
This will ensure the eth0
interface of our target gets configured with the 192.168.42.2
IP address. Of course, feel free to use a different IP address.
Then, run make
in Buildroot, reflash your SD card, and boot your system. At boot time, you should see:
Starting dropbear sshd: OK
You can also run ip addr show dev eth0
to check the IP address of the eth0
interface:
2: eth0:mtu 1500 qdisc mq qlen 1000 link/ether 00:80:e1:42:4d:e3 brd ff:ff:ff:ff:ff:ff inet 192.168.42.2/24 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::280:e1ff:fe42:4de3/64 scope link valid_lft forever preferred_lft forever
So the IPv4 address is properly set to 192.168.42.2
, as expected.
Now, on your workstation, we need to configure the 192.168.42.1
static IP address so that you can connect to your board. It is very likely that the Linux system on your workstation is using NetworkManager. Let’s add a connection:
$ nmcli con add con-name buildroot-target type ethernet ifname enp57s0u1u3 ip4 192.168.42.1/24 Connection 'buildroot-target' (234e0d9a-5c4f-4eac-9277-c3587bbd370d) successfully added.
Make sure to replace enp57s0u1u
by the name of your PC wired interface, to which the board is connected. We of course assume you have an Ethernet cable directly connecting your PC to the board.
Finally, enable the connection:
$ nmcli con up id buildroot-target Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/10)
We can now ping our target:
$ ping 192.168.42.2 PING 192.168.42.2 (192.168.42.2) 56(84) bytes of data. 64 bytes from 192.168.42.2: icmp_seq=1 ttl=64 time=1.33 ms
Log-in over SSH:
$ ssh root@192.168.42.2 root@192.168.42.2's password: # uname -a Linux buildroot 4.19.26 #1 SMP PREEMPT Wed Aug 28 15:54:58 CEST 2019 armv7l GNU/Linux # cat /etc/issue Welcome to Buildroot #
And verify that SFTP is working:
$ sftp root@192.168.42.2 root@192.168.42.2's password: Connected to root@192.168.42.2. sftp> ls / /bin /boot /dev /etc /lib /lib32 /linuxrc /lost+found /media /mnt /opt /proc /root /run /sbin /sys /tmp /usr /var sftp>
So, now we can make a change to our Qt5 application, for example changing the label of the button, recompile by running make
in the application directory, and directly copy the application using scp
, and run it over ssh
:
$ make [...] $ scp qt-sensor-demo root@192.168.42.2:/usr/bin/ root@192.168.42.2's password: qt-sensor-demo 100% 12KB 634.7KB/s 00:00 $ ssh root@192.168.42.2 root@192.168.42.2's password: # qt-sensor-demo -platform linuxfb
Much nicer, we don’t have to reflash our SD card every time we want to test a change in our application!
Note that we could create a public/private key pair, with the public key on our target, and this way not have to enter our password every time we want to transfer a file or log-in to the target. Since this blog post is already very long, we’ll live that as an exercise for the reader, there are plenty of resources on the Web about this topic.
Setting up Qt Creator
Some people (such as your author) are happy with using a powerful text editor (such as Vim or Emacs) and a terminal to do their application development. But others are sometimes more comfortable with an integrated development environment (IDE). So in this section, we’ll see how to set up Qt Creator to write, build, deploy and debug a Qt5 application.
Installing Qt Creator
First of all, you’ll have to install Qt Creator, which you can do using the package management system of your distribution. On Fedora systems, this would be:
$ sudo dnf install qt-creator
On Debian/Ubuntu systems:
$ sudo apt install qtcreator
The following instructions have been written and tested against Qt Creator version 4.9.2.
Creating a kit
After starting Qt Creator, the first thing to do is to create a kit, which describes the cross-compiler and Qt installation provided by Buildroot. Go to Tools -> Options
, and the first item should be Kits:
Click on Add
, and fill in the different fields as follows:
- Name:
Buildroot ARM
- Device type:
Generic Linux Device
- Sysroot:
/path/to/buildroot/output/host/arm-buildroot-linux-gnueabihf/sysroot/
. Of course, replace/path/to/buildroot/
with the appropriate path on your system. - For the compiler, click on Manage, then in the Compiler panel:
- Add one GCC C compiler, with the name
Buildroot GCC
and pointing to/path/to/buildroot/output/host/bin/arm-linux-gnueabihf-gcc
- Add one GCC C++ compiler, with the name
Buildroot G++
and pointing to/path/to/buildroot/output/host/bin/arm-linux-gnueabihf-g++
- Add one GCC C compiler, with the name
- Back in the Kits panel, select Buildroot GCC and Buildroot G++ as the C and C++ compilers, respectively.
- For the debugger, click on Manage, then in the debugger panel add one debugger named
Buildroot GDB
, and pointing to/path/to/buildroot/output/host/bin/arm-linux-gnueabihf-gdb
. Back in the Kits panel, select Buildroot GDB as our debugger. - For the Qt version, click on Manage, then on Add, and point to the
qmake
binary in/path/to/buildroot/output/host/bin/
. It will auto-detect that Buildroot has built Qt 5.11.3. You may want to adjust the version name fromQt %{Qt:Version} (host)
toQt %{Qt:Version} (Buildroot)
, as this Qt version is clearly not built for our host PC. Then back in the Kits panel, select this new Qt version. - For the Qt mkspec, enter
devices/linux-buildroot-g++
, which is the name of the mkspec configuration Buildroot generates.
You’ll find below screenshots of the various panels, with the details related to the Buildroot cross-compiler, cross-debugger and Qt installation:
We’re now done configuring a Kit!
Creating a device
In order to allow Qt Creator to deploy our application to the device, run it and debug it, we need to create a Device. Go again in Tools -> Options
, and this time go to the Devices panel.
In the first window, select Generic Linux Device.
Then, for the device name, use STM32MP15 Discovery board
for example, for the IP address, 192.168.42.2
and for the user, root
, which should give:
In the next step about Key deployment, simply skip to the next section, as we have not created a private/public key pair, as explained previously in this blog post. You can then finalize the device creation. Qt Creator will now test that it can communicate as expected with our device:
As you can see, it doesn’t find rsync
on the target, because we have not installed it. It will use sftp
instead, which is fine.
Back in the Device panel, you should see our device definition as follows:
You can click on Open Remote Shell to directly open a shell over SSH to your target, or Show Running processes.
Our device is now set up correctly, time to create our first application!
Importing our project
We now want to import our qt-sensor-demo project in Qt Creator. To do so, go in File -> Open File or Project
, then browse to the directory containing our qt-sensor-demo application, and select both the main.cpp
and qt-sensor-demo.pro
files, and click Open. Qt Creator should now switch to a Configure project window, where it asks you to select the Kit to use for this project. Obviously, select the Buildroot ARM kit we have just created, and validate by clicking Configure Project:
You should now see our project imported, with both of its files, and main.cpp
is opened by default:
If we now use Build -> Build All
, and then go in the Compile Output
panel, we see:
13:11:58: Running steps for project qt-sensor-demo... 13:11:59: Starting: "/home/thomas/projets/outputs/st/host/bin/qmake" /home/thomas/qt-sensor-demo/qt-sensor-demo.pro -spec devices/linux-buildroot-g++ CONFIG+=debug CONFIG+=qml_debug Info: creating stash file /home/thomas/build-qt-sensor-demo-Buildroot_ARM-Debug/.qmake.stash 13:11:59: The process "/home/thomas/projets/outputs/st/host/bin/qmake" exited normally. 13:11:59: Starting: "/usr/bin/make" -f /home/thomas/build-qt-sensor-demo-Buildroot_ARM-Debug/Makefile qmake_all make: Nothing to be done for 'qmake_all'. 13:11:59: The process "/usr/bin/make" exited normally. 13:11:59: Starting: "/usr/bin/make" -j4 /home/thomas/projets/outputs/st/host/bin/arm-linux-gnueabihf-g++ -c -pipe -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Os -Og --sysroot=/home/thomas/projets/outputs/st/host/arm-buildroot-linux-gnueabihf/sysroot -g -Wall -W -D_REENTRANT -fPIC -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I../qt-sensor-demo -I. -I../projets/outputs/st/host/arm-buildroot-linux-gnueabihf/sysroot/usr/include/qt5 -I../projets/outputs/st/host/arm-buildroot-linux-gnueabihf/sysroot/usr/include/qt5/QtWidgets -I../projets/outputs/st/host/arm-buildroot-linux-gnueabihf/sysroot/usr/include/qt5/QtGui -I../projets/outputs/st/host/arm-buildroot-linux-gnueabihf/sysroot/usr/include/qt5/QtCore -I. -I../projets/outputs/st/host/mkspecs/devices/linux-buildroot-g++ -o main.o ../qt-sensor-demo/main.cpp /home/thomas/projets/outputs/st/host/bin/arm-linux-gnueabihf-g++ --sysroot=/home/thomas/projets/outputs/st/host/arm-buildroot-linux-gnueabihf/sysroot -o qt-sensor-demo main.o -lQt5Widgets -lQt5Gui -lQt5Core -lrt -ldl -latomic -lpthread 13:12:00: The process "/usr/bin/make" exited normally. 13:12:00: Elapsed time: 00:02.
So we see that it is invoking qmake
from Buildroot, and then running make
, which builds our application, with the appropriate cross-compiler provided by Buildroot!
The application has been built in /home/thomas/build-qt-sensor-demo-Buildroot_ARM-Debug
, which contains:
-rw-rw-r-- 1 thomas thomas 620760 30 août 13:12 main.o -rw-rw-r-- 1 thomas thomas 31522 30 août 13:11 Makefile -rwxrwxr-x 1 thomas thomas 516504 30 août 13:12 qt-sensor-demo
Running the application on the target
In order for Qt to deploy our application on the target, we need to adjust our .pro
file so that it has directives to install the application. We’ll simply make our .pro
file look like this:
QT += widgets SOURCES = main.cpp INSTALLS += target target.path = /usr/bin
We invite you to read the relevant part of the Qt documentation to get details about the INSTALLS
directive and the special target
keyword.
Before we can really deploy on your target, we need to adjust the Run configuration, so click on the Project icon in the left bar, which should bring you to:
We’re seeing the Build settings, so click on Run to see the Run settings. Everything should already be auto-detected: we want to deploy qt-sensor-demo
to /usr/bin
on the target, the target is STM32MP15 Discovery board. The only thing we need to change is to set Command line arguments to -platform linuxfb
. Your settings should then look like this:
Now, you can finally do Build -> Run
. Qt Creator will prompt you for the root password of your target, and automatically deploy and run the application!
Just to test it, make a change to the QPushButton
label, and do Build -> Run
again. You’ll see the new version of your application running!
Debugging your application
The last part in setting up our development environment is to be able to debug our application from Qt Creator. This involves remote debugging, where the debugger runs on your workstation, while the program being debugged runs on a separate target. As part of the Kit definition done previously, we have already told Qt Creator where the cross debugger provided by Buildroot is.
Now, we need to have gdbserver on the target, which is the program with which the cross-debugger will communicate to control the execution of our application on the target. To achieve this, go to the Buildroot menuconfig, and enable the option BR2_TOOLCHAIN_EXTERNAL_GDB_SERVER_COPY
, in Toolchain -> Copy gdb server to the Target
. With this done, we now need to have Buildroot take this change into account. Unfortunately simply running make will not take this change into account (see here for more details). We could do a full clean rebuild of Buildroot (make clean all
), but that would take quite some time, so we’ll ask Buildroot to only reinstall the toolchain
package and regenerate the root filesystem image:
make toolchain-external-arm-arm-reinstall all
Reflash your SD card, and reboot the system. You should now have gdbserver available on the target:
# ls -l /usr/bin/gdbserver -rwxr-xr-x 1 root root 355924 Aug 29 2019 /usr/bin/gdbserver
We’ll now change a bit our program with some additional dummy code to play around with the debugger:
#include <QApplication> #include <QPushButton> int main(int argc, char* argv[]) { int a = 42; QApplication app(argc, argv); QPushButton hello("Hello world!"); a++; qDebug("Test 1"); a++; qDebug("Test 2"); hello.resize(100,30); hello.show(); return app.exec(); }
Place a breakpoint on the line QApplication app(argc, argv)
by clicking to the left of this line, it should show a red dot, like this:
Then you can start debugging by clicking on the following button in the left bar:
It will switch to the debug view, with the program stopped at our breakpoint:
At the bottom of the screen, click on Application Output
so that we can see the stdout of the application running on the target. Now hit F10
to step through our code line by line. You should then see the value of the variable a
updated in the top right panel, and the Test 1
and then Test 2
messages printed in the application output:
So, as expected, we are able to debug our application! This concludes the setup of Qt Creator, which allows us to very easily make a change to our application, build it, deploy it on the target and debug it.
Building using a Buildroot package
Before we conclude this article, we want to see how to integrate the build of our application with Buildroot. Indeed, building the application manually or through Qt Creator is perfectly fine during the active development of the application. But in the end, we want Buildroot to be able to build our complete system, including all the applications and libraries we have developed, in a fully automated and reproducible fashion.
To achieve this, in this section, we’ll create a Buildroot package for our qt-sensor-demo application. A package in Buildroot speak is a small set of metadata that tells Buildroot how to retrieve and build a particular piece of software.
To learn how to create a Buildroot package, we suggest you to read the relevant section of the Buildroot manual, or to read the slides of our Buildroot training course. The following steps will however guide you in the process of creating our qt-sensor-demo package.
First, in the Buildroot source tree, create a package/qt-sensor-demo/
directory. Then, create a file named package/qt-sensor-demo/Config.in
, which describes one configuration option to be able to enable/disable this package from Buildroot’s menuconfig:
config BR2_PACKAGE_QT_SENSOR_DEMO bool "qt-sensor-demo" depends on BR2_PACKAGE_QT5 select BR2_PACKAGE_QT5BASE_WIDGETS help This is the qt-sensor-demo application.
Note that the bool
, depends on
, select
and help
keywords need to be prefixed with a tab (not spaces), and that the BR2_PACKAGE_QT_SENSOR_DEMO
string should be exactly as-is, as it needs to match the name of the directory qt-sensor-demo
.
This Config.in
file basically creates a boolean option which will appear as qt-sensor-demo
in menuconfig. The depends on BR2_PACKAGE_QT5
definition ensures that our option will only be selectable if Qt5 is available, while select BR2_PACKAGE_QT5BASE_WIDGETS
makes sure Qt5 will be built with QtWidgets support, as we use them.
Now, edit the existing package/Config.in
file, and at a relevant place (perhaps Graphic libraries and applications, submenu Graphic applications), you need to add:
source "package/qt-sensor-demo/Config.in"
So that Buildroot’s menuconfig properly includes and reads our new package Config.in
file. Now, if you run make menuconfig
in Buildroot, you should be able to see our new option and enable it. Of course for now, it doesn’t do anything useful.
The next step is to create a qt-sensor-demo.mk
file in package/qt-sensor-demo/
to teach Buildroot how to build our package. This .mk
file is a Makefile, which uses a number of Buildroot-specific variables and macros, to a point where it doesn’t really look like a typical Makefile. In our case, qt-sensor-demo.mk
will look like this:
################################################################################ # # qt-sensor-demo # ################################################################################ QT_SENSOR_DEMO_SITE = $(TOPDIR)/../qt-sensor-demo QT_SENSOR_DEMO_SITE_METHOD = local QT_SENSOR_DEMO_DEPENDENCIES = qt5base define QT_SENSOR_DEMO_CONFIGURE_CMDS (cd $(@D); $(QT5_QMAKE)) endef define QT_SENSOR_DEMO_BUILD_CMDS $(TARGET_MAKE_ENV) $(MAKE) -C $(@D) endef define QT_SENSOR_DEMO_INSTALL_TARGET_CMDS $(INSTALL) -D -m 0755 $(@D)/qt-sensor-demo $(TARGET_DIR)/usr/bin/qt-sensor-demo endef $(eval $(generic-package))
The first two variables, QT_SENSOR_DEMO_SITE
and QT_SENSOR_DEMO_SITE_METHOD
tell Buildroot how to retrieve the source code for this application. Most Buildroot packages retrieve tarballs of source from HTTP servers, or clone source code from Git repositories. But in the case of our package, we are simply taking the source from the qt-sensor-demo
directory, located just one level up from the main Buildroot source directory.
The QT_SENSOR_DEMO_DEPENDENCIES
variable tells Buildroot that the qt5base
package needs to be built before qt-sensor-demo
gets built.
The QT_SENSOR_DEMO_CONFIGURE_CMDS
variable describes the commands to run to configure our package. Here, we simply call Qt’s qmake utility, using the Buildroot-provided variable QT5_QMAKE
.
The QT_SENSOR_DEMO_BUILD_CMDS
variable describes the commands to run to build our package. In our case, we invoke make in the application directory, $(@D)
, passing appropriate variables in the environment ($(TARGET_MAKE_ENV)
).
Then, the QT_SENSOR_DEMO_INSTALL_TARGET_CMDS
variable describes the commands to run to install our package. We simply copy the qt-sensor-demo
executable from the build directory ($(@D)
) to usr/bin
in the target directory.
Finally, the generic-package
macro invocation is what triggers the Buildroot machinery to create a package. Read the Buildroot manual and/or our Buildroot training slides for more details.
With this in place, if you have already enabled qt-sensor-demo
in menuconfig, when you run make in Buildroot, you should see:
>>> qt-sensor-demo Syncing from source dir /home/thomas/qt-sensor-demo rsync -au --chmod=u=rwX,go=rX --exclude .svn --exclude .git --exclude .hg --exclude .bzr --exclude CVS /home/thomas/qt-sensor-demo/ /home/thomas/buildroot/output/build/qt-sensor-demo >>> qt-sensor-demo Configuring (cd /home/thomas/buildroot/output/build/qt-sensor-demo; /home/thomas/buildroot/output/host/bin/qmake -spec devices/linux-buildroot-g++) >>> qt-sensor-demo Building PATH="..." /usr/bin/make -j5 -C /home/thomas/buildroot/output/build/qt-sensor-demo /home/thomas/buildroot/output/host/bin/arm-linux-gnueabihf-g++ --sysroot=/home/thomas/buildroot/output/host/arm-buildroot-linux-gnueabihf/sysroot -Wl,-O1 -o qt-sensor-demo main.o -L/home/thomas/buildroot/output/host/arm-buildroot-linux-gnueabihf/sysroot/usr/lib -lQt5Widgets -lQt5Gui -lQt5Core -lrt -ldl -latomic -lpthread >>> qt-sensor-demo Installing to target /usr/bin/install -D -m 0755 /home/thomas/buildroot/output/build/qt-sensor-demo/qt-sensor-demo /home/thomas/buildroot/output/target/usr/bin/qt-sensor-demo
You’re seeing:
- Buildroot copying the source code from its original location to the Buildroot build directory
- Buildroot configuring the build of our package by invoking qmake
- Buildroot building our application
- Buildroot installing our application
The application being installed in Buildroot’s target directory, it is automatically part of the root filesystem image, and consequently the SD card image sdcard.img
. You can flash it again, and see that you have the same application.
Now, if you want to change the source code of your application, you can simply change it in its original location, the qt-sensor-demo
directory, and issue the following command in Buildroot:
$ make qt-sensor-demo-rebuild all
Buildroot will synchronize again the source code from its original directory to Buildroot’s build directory, and rebuild the application. It will only transfer the files that have changed, and only rebuild the files that have changed. The all
target ensures that the root filesystem and SD card images get regenerated with the new version of the code.
Conclusion
In this article, we’ve seen many things:
- How to create and manually build our first Qt5 application
- How to deploy our application to the target by adding it to the SD card
- How to set up network communication, and SSH, to deploy our application more efficiently during development
- How to set up Qt Creator as a development environment to write, build, deploy and debug our application
- How to create a Buildroot package to automate the build of our application
You can find the Buildroot changes corresponding to this blog post in the 2019.02/stm32mp157-dk-blog-4 branch of our repository. The qt-sensor-demo
application code can be found in the blog-4 branch of this application Git repository.
In our next blog post, we’ll extend our qt-sensor-demo
application to make it really useful!
This is a truly excellent article.
You completely address all my concerns about using the open source version of QtCreator to build and debug Qt apps. And now we see how it all ties in with Buildroot.
Thank you! Awesome!
You’ve convinced me to order an MP157C discovery board and get to work.
I did notice one small potential error. Are there missing includes in main.cpp?
#include xxxxxx
#include xxxxxx
Or are the #includes unnecessary?
Hello ClarkS. Thanks for your comment, glad to hear that the blog post was useful. Regarding the #include, the less than and greater than sign were interpreted as an HTML tag, which obviously didn’t work. I fixed that up, and now the blog post should hopefully show correct and working code. Thanks!
As usual, very good article. Also thanks for all the training materials!
That is a great tutorial. Thank you very much. I like this chip. I come from an RTOS background so this seems like an ideal transitional path
The only issue I see with it is that there is no flash on chip so you need to load the M4 on powerup. Maybe ST should have put some flash in for the M4.
I am looking at using the M4 to support an RTOS application and have some sort of IPC to the main A7 Linux Application.
Any tips on loading the M4 and using OpenAmp?
Hello Eoin. Thanks for your comment. There is no need for a flash for the M4. Indeed, since there is an application processor running Linux next to it, either U-Boot (the bootloader) or Linux can load the appropriate code from flash/MMC/network and ask the M4 to run it.
There are obviously several IPC mechanisms between the A7 and the M4: shared memory and mailbox. I encourage you to browse https://wiki.st.com/stm32mpu/wiki/Category:Coprocessor_management_STM32Cube which contains a number of pages related to this. It explains how Linux can control the M4 (load code and run it on the M4) and how Linux can communicate with the M4.
This is something we hope to cover in a future article, but we are not sure we will get there. Stay tuned!
thanks again for this great blog !
I have a question:
Whenever I deploy and run my qt program on stm32mp157c-dk2, I see the qt application interface on the MIPI-DSI touch display and it is great but after I finish the running process of the program, the qt app interface is still there on the touch display. Why ?
And I think since the first version of program was not terminated, I couldnt start to remotely debug my modified program. How can I really terminate the app on target and clear teh display ?
here is the output:
14:34:00: Checking available ports…
14:34:00: Found 101 free ports.
14:34:00: Starting gdbserver…
14:34:00: Unable to create a debugging engine.
14:34:00: User requested stop. Shutting down…
Listening on port 10000
14:34:05: Timeout waiting for remote process to finish.
Hello Carl. Thanks for your comment. The contents of the screen remains visible simply because there is nothing that cleans up the framebuffer when the application exits, so the last frame displayed by the application remains visible.
Regarding your debugging problem, why wasn’t your program terminated ? If you’re using Qt Creator, you can stop the program, and start a new debugging session. I’m not sure how you got into the situation you’re describing.
hi,
At first, thank you so much.
MIPI-DSI touch display is functioning which is a part of discovery kit stm32mp157c-dk2.
I have also a linux compatible HDMI touch display that I connect to offical weston image and running well. But When I connect it to this buildroot image, it doesn’t function.
What should I do ?
thank you !
Hello Ibrahim. Thanks for your comment. Regarding your HDMI touch display, I believe the HDMI output should work as-is: you can try with the modetest application described in the previous blog post, it should show the HDMI output connected. Use that first to make sure that your HDMI display is working as expected. Then for Qt, you’ll probably have to pass something like QT_QPA_PLATFORM=linuxfb:fb=/dev/fb1 in the environment to make it work. I don’t have the STM32MP15 board with me, so I can’t formally verify.
Regarding the touchscreen, if I understand correctly in most HDMI touch displays, there is a separate USB cable that is used for the touchscreen. Then you will need the appropriate Linux kernel drivers: you may need to adjust your Linux kernel configuration. A first tip is to run “lsusb” with and without the display connected, and see which USB device appears. Based on that you can figure out which driver should be used.
Your blog posts have been very helpful with getting buildroot working and enabling periferals. I am now stuck on writing an application for the a7 to interact with the m4 using openamp. I have gone through all the documentation but it seem the interrupts are not being handled correctly and reliability is bad.
I hope you might cover the interprocessor communication in a later post.
Hello Cristian. We are not sure yet whether we will have the chance to cover interprocessor communication in this series. We will try to do so, but we can’t guarantee it.
Do not hesitate to contact us if you need commercial support for the STM32MP1 platform, Bootlin offers engineering services for custom development and support.
Hello Mr. Petazzoni
1. As you know we copy our qt binary to /path/to/buildroot/output/target/usr/bin/ before building the system. I want two additional files be in the same path (.elf and .sh). What should I do ? just copy the files to the path before building ? Or which config files need to be edited and how ?
2. I want my qt application run automatically after booting the system (power on). What should I do ?
a possible answer will be really helpfull for my thesis. thanks in advance !
Hello Carl,
(1) Everything that is in output/target/ will be part of the final root filesystem image. However, as explained in the blog post, it is important to understand that output/target/ is a temporary location: if you do a “make clean” in Buildroot, the contents of output/target/ will disappear, and the system will be entirely rebuilt from scratch at the next Buildroot “make”. So, copying files manually to output/target/ is only good during experimentation/development: for the final integration, you really want to have either a package or a root filesystem overlay install whatever files you need.
(2) The topic of creating an init script to start the application at boot time will be covered in our next blog post.
Hi! In the section “Debugging your application” these to lines are not displayed completely due to the HTML error:
#include
#include
They should be written in the HTML code as:
#include <:QApplication>
#include <QPushButton>
Hello Alexis, thanks for your comment! I have fixed that in the blog post now.
Hi Thomas,
I was following your tutorial for buildroot, and I have problem setting up the kit. qmake binary is missing from /path/to/buildroot/output/host/bin/
Any idea why is that and how can i fix it?
Thank you for the tutorial
Hello Ema. If the qmake binary is not present in output/host/bin, then it means that Qt5 was not built. Either your build failed, or Qt5 was not enabled in your configuration. If you want more support on this issue, could you check (1) that BR2_PACKAGE_QT5=y in your .config and (2) that you’ve done a full rebuild using “make clean all” ?
Hi Thomas,
Love your tutorials!
I, like Ema am not getting the qmake binary built.
Checking .conf I get:
~/st-buildroot/buildroot$ grep BR2_PACKAGE_QT5 .config
BR2_PACKAGE_QT5_JSCORE_AVAILABLE=y
# BR2_PACKAGE_QT5 is not set
# BR2_PACKAGE_QT5QUICK1 is not set
Is there a glage I would set in menuconfig to get this setup properly to build qmake?
Thanks
Well, as your configuration snippet shows, Qt5 is disabled:
# BR2_PACKAGE_QT5 is not set
, so obviously, you won’t get qmake, nor Qt5. Please enable Qt5, which was all explained in the previous article in this series, at https://bootlin.com/blog/building-a-linux-system-for-the-stm32mp1-enabling-qt5-for-graphical-applications/Hi Thomas,
Please forgive my missing this part of your tutorials. It worked perfectly! It is truly astonishing how well you mix theory and practice as an effortless flow! It’s not just that you take the time to write these, but that someone of your expertise takes the time to follow up on all the questions that come through. Even if it is as simple as my missing the whole tutorial…..
If you allow me to add, like so many have commented, doing the inter-processor communication to the M4 would really be the jewel on the crown on this outstanding accomplishment. As an upside, I am sure wetting peoples appetite with this type of getting started guide for the M4, would open so many opportunities for professional services to complete the communication model for these designs.
Thank you again for this gift of an invitation, to all, to join the world of Linux!
Gearge
Hello,
Thanks for the tutorial. I’m using a different target, but I followed your instructions for setting up a QT development environment. When I run my application, I get the following message:
/lib64/libstdc++.so.6: no version information available (required by /usr/lib64/libGLESv2.so.2)
It seems this might be related to having a build environment that isn’t using the correct toolchain (https://stackoverflow.com/questions/58496314/lib-libstdc-so-6-no-version-information-available) but I’m not sure where I went wrong, given that I’m compiling using QT Creator using the kit that is set up with my buildroot output.
Any ideas?
Thanks!
It’s difficult to help you without more details, especially about the toolchain you’re using. However, we have seen some Linux toolchains provided by ARM where the libstdc++ was built without version information… while the OpenGL blobs provided by vendors do expect libstdc++ to have the version information. So here, your only luck is to switch to another toolchain, which has a libstdc++ properly built with version information.
Hi Thomas,
and many thanks for your tutorial series. I had trouble with using mkspec devices/linux-buildroot-g++. The effect is, that the QtCreator (4.8.2.) Debug config did not include the “-g” flag for generating debug info, i.e. no source level debugging. I changed to devices/linux-generic-g++ and that worked.
When I checked package/qt5/qt5base/qmake.conf.in I found lines:
# Remove all optimisation flags …
…
QMAKE_CXX_FLAGS_DEBUG =
Is that the reason for the missing -g and what’s the rationale behind that?
kind regards
Frank
berlin area
Hello Frank. Thanks for your comment. The idea is that Buildroot builds everything with -g when BR2_ENABLE_DEBUG=y in the Buildroot configuration. So we “expose” that also to things built with qmake, and that’s why our qmake file contains:
QMAKE_CFLAGS += $${BR_COMPILER_CFLAGS}
QMAKE_CXXFLAGS += $${BR_COMPILER_CXXFLAGS}
The BR_COMPILER_CFLAGS variable will contain -g when BR2_ENABLE_DEBUG=y in Buildroot.
Hallo Thomas,
i am just lerning buildroot with your wonderful blogs. But now i am stuck. I don’t get the GESFTPSERVER working. Message is always like “-sh: sftp: not found”.
The relevant part of the .config file:
#
# ejabberd needs erlang, toolchain w/ C++
#
# BR2_PACKAGE_ETHTOOL is not set
# BR2_PACKAGE_FAIFA is not set
# BR2_PACKAGE_FASTD is not set
# BR2_PACKAGE_FCGIWRAP is not set
# BR2_PACKAGE_FLANNEL is not set
# BR2_PACKAGE_FPING is not set
# BR2_PACKAGE_FREESWITCH is not set
# BR2_PACKAGE_GERBERA is not set
BR2_PACKAGE_GESFTPSERVER=y
# BR2_PACKAGE_GLORYTUN is not set
OS is Ubuntu 18.041 LTS /64-Bit PC
Linux 5.4.0-72-generic #80~18.04.1-Ubuntu
I think i made a mistake, but can’t find it.
Marcus
Where do you see this message “-sh: sftp: not found” ? On the board or on your workstation ? gesftpserver only provides a SFTP server, not a SFTP client, so on the board, it is normal to not have the “sftp” command. If this error is on your workstation: install the sftp client, which is needed by Qt Creator to upload your application to the board.
Hallo Thomas,
both the terminal (connection by ETH) and the ScriptCommander (connecting St-Link) showed the message on the PC. As I assumed I made the mistake. I tried to connect to the SFTP when I was already connected to the STM32 via ssh.
I needed some time to figure that out. I work with Linux for 8 weeks, the learning curve is steep….
Thank you
Marc
Hi, thanks for great tutorials, do you know what packages do I need to install to run quick application? because currently I get this error
“qt.qpa.plugin: Could not find the Qt platform plugin “eglfs” in “” “
The error you’re getting is not related to Quick applications, any graphical Qt application would exhibit this error. It seems like you are requesting to run the application with the eglfs Qt plugin, but this plugin was not compiled into your Qt configuration. Indeed, this blog post only shows how to use the linuxfb Qt plugin, without OpenGL acceleration. You’ll need to adjust the Buildroot configuration to enable an OpenGL provider package + enable eglfs in Qt. Contact us at Bootlin if you need support to achieve this.
Hi, excellent tutorial!
However I’m having trouble with QT’s camera. Camera is based on adv7280 chip on our custom board. It works on ST’s yocto based system, but in buildroot there are some problems with DMA settings, I think. In yocto based STlinux there are some patches that are missing in buildroot?
Glad you found the tutorial useful. Buildroot doesn’t have kernel patches: it builds whichever Linux kernel version and configuration you tell Buildroot to build. So if you have a working setup, make sure to re-use the same Linux kernel version and configuration in Buildroot.
Of course, Bootlin can provide commercial support if you need help with this project. We have expertise with both Buildroot and camera support in Linux.
Thanks! I’m using the same kernel and uboot versions and I ran those patches to kernel and uboot. It compiles in buildroot without warnings, but in boot phase something goes wrong and it stops in the “starting Kernel” point. I’ll check the configurations again, maybe I’ve missed something.
Hi! I managed to implement those kernel patches, and Buildroot boots now. I tried to use the GPU driver (etnaviv) with Qt, platform eglfs, but I guess there is something wrong, as the qt-examples run extremely slow. I’m using the 5.10.61 kernel version. Something has changed and there is no fb0 device anymore, so linuxfb platform doesn’t work. Any idea how to enable it ?
Could you try using the buildroot-external-st project that we just announced yesterday: https://github.com/bootlin/buildroot-external-st ?
Thanks a lot!
This did the trick! I got my camera working in Qt5 environment, and it’s now easy to make changes to the device tree. There was some problem with i2c5 clock but I found the setting in the device tree. I’m using eglfs platform and it works now.
Best Regards,
Raimo
Hello ,
Thanks for those excellent tutorial.
I am using buildroot-external-st.
I only have one things that doesn’t works, it’s the breakpoint :
The application don’t stop on any breakpoint, don’t understand why ?
Everything else works fine, any idde ?
Hello Gabriel. Glad to hear that buildroot-external-st was useful. For your breakpoint issue, could you provide more details? Did you build your application with debugging symbols? Did you build the Buildroot system with debugging symbols (BR2_ENABLE_DEBUG=y in the Buildroot configuration) ? What are the exact issues that you observe ?
I am a bit confuse, it’s working now. As many variable are “optimized out” i dont think that debug symbol are enable in my application.
I don’t know where to config “debugging symbols” with qmake
Hi Thomas,
Thank you so much for this tremendous resource. Having these tools to work with is quite a windfall. I am working with the STM32MP157F-DK2 board and the 2022.02 configuration stm32mp157f-dk2-demo-defconfig file. My platform is Ubuntu 22.04 with Qt Creator and the goal it to run embedded projects on the target over the ethernet link on eth0. For this I have used menuconfig to include the gesftpserver module (no other changes) so that I can deploy Qt creator builds directly to the target board by SFTP. I have been able to boot the project on the board and interact by ST-link using picocom, and I’ve been able to ssh into the dropbear server as well. The problem that I’m running into is that the SFTP link is not functional and will not log in either on the command line as instructed above in this tutorial or through Qt Creator. The message that I get from the target is that there is no SFTP server. Might there be any advise or debugging clues regarding this problem?
On a secondary note, I tried a work-around by removing dropbear and gesftpserver from the menuconfig and activating the openssh as an alternative because as I understand it, openssh has an sftp server. Here I run into problems in that I cannot log into the target server and an $ ssh -v root @192.168.42.2 shows that the connection process hangs at “debug 1: expecting SSH2-MSG_KEX_ECDH_REPLY”, followed by a connection reset. A web search turns up suggestions of changing the mtu value, but at least a change from 1500 to 1200 or 1000 has not been helpful. Another recent link throws suspicion on glibc but I just don’t the experience with openssh or embedded linux to properly evaluate the thread.
Thanks in advance for any insight on this, I am so looking forward to using this system!