Episode 15: Burkhard on Qt Embedded Systems
Welcome to Episode 15 of my newsletter on Qt Embedded Systems!
If you are planning to give a presentation at a Qt event this year, you’ll find two calls for presentations in the News section. Note that the deadline for events organised by The Qt Company (including a new Qt designer event and Qt World Summit) is 28 February 2021! So you better hurry up ⏰
KDAB organises the Qt Embedded Days on 13-14 April 2021 - the first event ever exclusively on Qt embedded systems 🎉 The call for presentations ends on 21 March 2021. So, there is a bit more time.
This month, I have been busy working on a display unit controlling a robot. The display unit is mounted in portrait mode. You’ll find a link to my blog post how to rotate a Wayland compositor by 90 degrees.
The display unit has two hardware buttons connected via GPIOs. You find two links in the Reading section how to detect with libgpiod whether the user pressed the buttons.
Enjoy reading and stay safe - Burkhard 💜
My Blog Posts
Rotating your monitor physically by 90 degrees isn’t enough to show your 800x1280px application fullscreen in portrait mode. You must tell the window manager to rotate the screen contents as well and to remove the system toolbar shown at the top. I show in this post how to do this for the standard Wayland compositor Weston on a Toradex Verdin i.MX8M Mini board.
This is the call for presentations for all Qt events organised by The Qt Company in 2021. If you want to give a talk at the Qt World Summit in Q4/2021, at a new designer event or at Qt webinars, you better hurry up. The deadline for your proposals is 28 February 2021. You will have to provide the complete presentation slides by 24 April 2021. It’s unclear which events are virtual.
If my memory serves me right, this is the first ever Qt event dedicated exclusively to Qt embedded systems 😃 Thanks to KDAB for organising it 🙏 The deadline for your proposals is 21 March 2021. The event is virtual. KDAB looks for “technically deep talks regarding all topics around Embedded Development”. So you will be spared the marketing wish-wash that has become a bit too common at official Qt events.
My Planned Submissions
I plan to submit the first two talks to Qt Embedded Days and all three talks to the “official” Qt events:
How to Set Up QtCreator for Cross-Compilation with CMake in 5 Minutes. This is a considerably improved version of this post, which I presented at Qt Day Italy, 2020.
How to Get the Architecture of Qt Embedded Systems Right. I have posed over 50 questions about the architecture in this post. I’ll answer these questions for the architecture of driver terminals of agricultural and construction machines.
Migrating a Harvester HMI from Qt 5.12 to Qt 6.0. This talk is based on my eponymous post. I hope that, by then, Qt 6 will support all Qt modules used by the harvester HMI - including QtMultimedia and QtCanBus.
The Art of Readable Code by Dustin Boswell and Trevor Foucher (O’Reilly, 2011)
When I looked up the publishing date for this book, I was surprised to find out that the book is 10 years old. The content feels as if written today. The authors’ advice is as relevant as ever. I am sure that it will stay relevant at least for the next 10 years.
The book is organised in 15 chapters and 4 parts. Each chapter comes with many illuminative code examples. I read each chapter in 30-45 minutes. I read the chapters out of order without losing track. For my review, I picked one chapter from Part 1: Surface-Level Improvements, one from Part 2: Simplifying Loops and Logic and one from Part 3: Reorganizing Your Code.
Chapter 10 is my favourite as it is the essence of making your code more readable. The key idea is to write code on a higher abstraction level by delegating lower-level code into library functions and classes with telling names.
Chapter 5: Knowing What to Comment
The key idea for what to comment and what not is this (emphasis mine): “The purpose of commenting is to help the reader know as much as the writer did.”
What NOT to Comment. You need not comment the constructor with // Constructor or the getter getBalance() of an Account class with // Returns the balance of the account. These comments don’t provide any additional value over the code.
If bad names are compensated with detailed comments, it may be a good idea to turn the comments into good function names. The golden rule is that good code is always more readable than bad code plus comments.
Include “Director Commentary”. You could tell the reader that you tried other solutions and why you didn’t choose them. This prevents readers from wasting their time on trying out these other solutions.
If, for example, you define a singleton for QML that you also want to use in C++, it looks like a memory leak. You better write a comment that it isn’t and that the QML engine will take ownership. What looks like a bug need not be one. And you better say so in a comment.
Comment the Flaws in Your Code. Mark flaws in your code with tags like TODO, FIXME, HACK or XXX (danger). These markers help you and your co-developers to keep track of all the pending micro-tasks.
When I try to make a failing micro-test pass again, I often see scenarios for the next micro-tests. I quickly write a TODO comment, carry on with my current micro-test and come back to the TODO later. Before I push my changes, I check whether I have fixed all the TODOs.
Anticipating Likely Questions. You can ask yourself two questions about your code: “What is surprising about this code? How might it be misused [or misunderstood]?” Then write down the answers as comments.
Recently, I had to check when a hardware button is pressed and released. The library function blocked until an event occurred or until a timeout (1s) happened. The blocking function call is repeated forever. If this function was called in the main GUI thread, the GUI would regularly freeze for one second. I wrote a comment why the loop with the blocking function must run in its own thread - anticipating questions about the use of an extra thread.
“Big Picture” Comments describe “how classes interact, how data flows through the whole system, and where the entry points are.” You could use message sequence charts to visualise the interaction between classes in several layers. I often scribble down call sequences in my paper notebooks. Just adding photos of these sequences to the documentation will help you and others.
You may know CRC cards for designing object-oriented software. CRC stands for Class-Responsibilities-Collaborators. You can use CRC as the blueprint for your comments. Big-picture comments list the Collaborators and why the Class interacts with these Collaborators.
Chapter 7: Making Control Flow Easy to Read
The key idea of this chapter is to write “all your conditionals, loops, and other changes to control flows […] in a way that doesn’t make the reader stop and reread your code.” This is good advice in general: Write all your code so that readers understand it on their first read.
The Order of if/else Blocks. The authors give three rules of thumb for the order of if/else cases.
“[Deal] with the positive case instead of the negative.”
“[Deal] with the simpler case first to get it out of the way.”
“[Deal] with the more interesting or conspicuous case first.”
Which rule applies is mostly clear, but is sometimes a judgement call.
Avoid do/while Loops. I don’t even know when I last used a do/while loop. But I never thought about the reason. The authors did. You must skip to the end of the loop to see when it is repeated. All other control-flow statements put the “logical conditions […] above the code they guard”.
Returning Early from a Function reduces the nesting of code significantly and makes the code easier to read. In contrast, enforcing the one-return-per-function policy increases nesting and unreadability. The authors are very clear about this policy: “This is nonsense.”
The authors dedicated a complete section to Minimize Nesting. Returning early from functions is a powerful method to achieve this goal. In loops, you can use continue as an equivalent to returning early.
The Infamous goto. Goto is not always evil. In C code, it can be put to good use. Instead of writing the cleanup code after every failed if-condition again and again, you can write a goto exit. You write the cleanup code once at the exit label. In C++, destructors take over the cleanup. Hence, there is rarely a need for goto.
The ?: Conditional Expression (a.k.a. “Ternary Operator”). The authors’ advice is to use the ternary operator only for very simple cases. You shouldn’t nest ternary operators. You definitely shouldn’t use them to “squeeze everything on one line”.
This leads the authors to some timeless and general advice (emphasis mine): “Instead of minimizing the number of lines, a better metric is to minimize the time needed for someone to understand it.”
Chapter 10: Extracting Unrelated Subproblems
“The advice for this chapter is to aggressively identify and extract unrelated subproblems.” The authors give a three-step procedure.
For a function ask: “What is the high-level goal of this [function]?”
For each line of the function ask: “Is it working directly to that goal? Or is it solving an unrelated subproblem needed to meet it?
Extract the unrelated lines into separate functions.
This is an excellent description of the Single Responsibility Principle (SRP). And it is more: It gives you a practical procedure how to ensure that a function has only one responsibility. The unrelated lines of code are additional responsibilities of the function.
The example function findClosestLocation() goes through a list of locations and finds the location that is closest to a given location on a sphere. For each location in the list, the function calculates the spherical distance to the given location and updates the closest location if necessary. The goal of the function is to determine the closest location. The unrelated subproblem is to calculate the spherical distance.
Once the calculation of the spherical distance is extracted, the function is much shorter and more readable. The reader doesn’t need to understand the geometry for calculating the spherical distance to understand the function findClosestLocation(). All linesof this function are now on the same abstraction level. The lines for calculating the spherical distance were on a lower abstraction level and are now hidden in the function sphericalDistance().
Pure Utility Code. The function sphericalDistance() is a good example of pure utility code or general purpose code. Such functions are used in many other applications. So, you should put them in a library for easy reuse.
It’s fairly easy to spot pure utility code: “[…] if you find yourself thinking, “I wish our library had an XYZ function,” go ahead and write it! […] Over time, you’ll build up a nice collection of utility code that can be used across projects.”
I think that the Qt developers are fairies who have granted our wishes long before we knew about them. The Qt libraries are full of pure utility functions and classes. By the way, QGeoCoordinate::distanceTo(const QGeoCoordinate&) is the Qt function for calculating the spherical distance.
Create a Lot of General-Purpose Code. The authors urge you to create your own libraries with general-purpose code (often called utils) or to use third-party libraries. You can reap the following benefits.
“General-purpose code is great because it’s completely decoupled from the rest of your project. Code like this is easier to develop, easier to test, and easier to understand. If only all of your code could be like this!”
Linux kernel GPIO user space interface by Sergio Prado (EmbeddedBits)
In several projects, I have used the sysfs interface to read from or to write to GPIOs. The value of GPIO can be retrieved by cat /sys/class/gpio/gpio72/value. A Qt application could use a QFileSystemWatcher on this file to get notified about rising or falling edges (see my post Monitoring Sys Files with QFileSystemWatcher). Every time an edge occurs, the application must open the value file and read the value. This is inefficient. Most importantly, the sysfs approach has been deprecated since Linux kernel version 4.7.
The display computer in my current project has two hardware buttons connected to GPIOs. As the display computer will be in use for the next 10-15 years, I thought it a bad idea to use a feature that has been deprecated since 2016. A quick search revealed that libgpiod is the new way to access GPIOs. A slightly longer search lead me to Sergio’s post - very helpful as all of his posts.
Sergio lists the pros and cons of the two approaches. The new approach ensures that only one process can access a GPIO at a time. It allows to access GPIOs by the chipset name and by the line name. For example, GPIO_1 on my Verdin i.MX8M Mini board has the chipset name /dev/gpiochip2 and the line name SODIMM_206: no magic GPIO numbers any more. Libgpiod provides operations on arrays of GPIOs. Notifications on events like edges works robustly.
After this comparison, Sergio gives C example code how to read from a GPIO, e.g. a button or the ignition. You find a detailed documentation of the gpiod functions in the header file gpiod.h. As a Qt/C++ developer, I found the C++ bindings even more useful. The documentation is in the header file gpiod.hpp. The C++ code is even more compact than the C code.
The C++ sample code for logging the rising and falling edges on a GPIO was especially useful for my application. The hardware buttons send a Qt signal pressed() on the rising edge (from not-pressed to pressed), which triggers an action on a robot.
Sergio closes his article with a short description of the utilities gpiodetect (list all GPIO chipsets), gpioinfo (lists all lines of a chipset), gpioset (set the value of a line), gpioget (read the value of a line) and gpiomon (monitor edges on a line). You find simplified C++ implementations of the utilities here.
GPIO Programming: Exploring the libgpiod Library by Jeff Tranter (ICS)
Jeff’s article is similar to Sergio’s above. He gives descriptions of the gpiod utilities. His C example program blinks three LEDs (outputs) and stops when the user presses a button (input). It shows how to use GPIOs as outputs and inputs. Jeff uses a Raspberry Pi for his experiments.
This post is Part 9 of Jeff’s series about GPIOs. I provide the links to all parts of the series, as they are a bit hard to find on the ICS web site. The articles are worth reading.
Part 8: Examining the Cross-Platform libsoc Library. The library provides interfaces to common interfaces like GPIO, SPI, I2C and PWM.
Copyright (C) *|CURRENT_YEAR|* *|LIST:COMPANY|*. All rights reserved.
*|IF:REWARDS|* *|HTML:REWARDS|* *|END:IF|*