Episode 11: Burkhard on Qt Embedded Systems
Welcome to Episode 11 of my newsletter on Qt Embedded Systems!
Germany is going into its second Corona lockdown on Monday. I am fortunate that not much will change for me. Even before Corona (BC), I have been working 75% from home. This year it has been 100% - without any discussions about working onsite. Business has been good and the first projects for 2021 are on the horizon.
Most importantly, I don't have to go through the Corona schemozzle alone. My wife of 25 years, Semiha, is at my side. She is just finishing her third Turkish cookbook - with me doing all the testing. We go for regular walks along the Inn river and for hikes in the nearby Alpes. Enjoying things together is what keeps us sane.
I wish you strength to get through this hard time. I hope that reading this newsletter can distract you a little bit.
Enjoy reading and stay safe - Burkhard 💜
P.S.: Here is a photo from one the walks along the Inn river.
My Blog Posts
Book Review: “A Philosophy of Software Design” by John Ousterhout.
Loyal readers of my newsletter will recognise the review from Episode 8 of my newsletter. So far, the post is the same as the newsletter entry. I'll add more chapters to the post in the future.
The main topic of John's book are deep modules. They “maximize the amount of complexity that is concealed”. And: “It is more important for a module to have a simple interface than a simple implementation.” Deep modules help you to avoid the code smells listed in Martin Fowler's book Refactoring.
Events
QtDay Italy 2020 was cancelled in spring because of COVID-19. It will take place from 16th to 19th November. You can still book tickets to enjoy some good talks. I'll miss chatting in people in person while having Tuscan food and looking over Florence 😢
I am giving two talks:
Cross-Compiling Qt Compilations with CMake (18th November, 16:50). This is a new talk specifically created for this autumn event. I'll show you how to set up QtCreator for cross-compilation with one call to a shell script.
A Deep Dive into Qt CAN Bus (19th November, 11:00 - 12:00). With the HMI of a sugar beet harvester as the running example, I'll explain how to architect the CAN communication layer, how to tame 1100 CAN message hitting the GUI per second and how to avoid buffer overflows when sending a bunch of message. This is a revised version of my talk from Qt World Summit 2019.
I hope to meet you in Florence - virtually 🙏
My Thoughts on Qt Marketplace
Recently, I was looking for a charts component to draw waveforms on a medical device. The customer wanted to use Qt under LGPLv3 for the single application. My first thought was to use Qt Charts, which makes it easy to build dynamic spline charts. But Qt Charts was licensed under GPLv3 and Commercial. An Internet search didn't turn up any must-have QML chart components.
At this point, I had the idea to check out the fairly new Qt Marketplace. My search for "charts" turned up two useful results: Qt Charts and KQuickCharts. Qt Charts naturally peaked my interest. It was sold for a yearly fee of 199 USD. The terms and conditions of the Qt Marketplace allow you to combine Qt Charts with an application using Qt under LGPLv3. There are no per-device fees (a.k.a. royalties). A Qt sales person confirmed my understanding of the terms and conditions.
I could present three options to my customers:
Option 1. Buy Qt Charts for a yearly fee of 199 USD. The spline chart example is pretty much what the customer needs.
Option 2. Evaluate KQuickCharts, which comes under LGPLv2.1, and probably adapt it to the customer's needs.
Option 3. Implement a QML chart component themselves.
My customer chose Option 1 without much ado. Paying 199 USD per year for a high-quality component, which was fit for the purpose, was a no-brainer.
The Qt Marketplace offers several other commercial Qt components under the same conditions: Qt MQTT (299 USD p.a.), Qt KNX (49 USD p.a.), Qt PDF (49 USD p.a.) and Qt Charts (199 USD p.a.). The bridges from Photoshop and Sketch to Design Studio are available in one extension for 300 USD p.a.
I'd reckon that many more companies will follow the lead of my customer and choose Option 1. These extensions provide good bang for your buck. Options 2 and 3 are typically more expensive.
Froglogic offers its Squish GUI Automation Tool for 2880 USD p.a. and Squish Coco for 3600 USD p.a. on the Qt Marketplace. KDAB offers GammaRay for 1000 USD p.a. and the Kuesa 3D Runtime for 2000 USD p.a. The GammaRay subscription contains the binaries and technical support from KDAB. GammaRay is still available under GPLv3. Then, you must build GammaRay from sources and you don't have any support. I describe how to build GammaRay and how to use it for detecting overdraw in QML HMIs in this post.
The extensions mentioned so far are all the commercial extensions in the Qt Marketplace. The Qt Marketplace contains 100+ Qt libraries, many of them from KDE.
If you publish an extension on the Qt Marketplace, you will receive 80% of the fee and The Qt Company will take 20%. The more developers offer their Qt extensions on the Qt Marketplace, the more attractive the Qt Marketplace becomes. The more attractive the Qt Marketplace becomes, the more developers will offer their extensions on it. This can turn into a vicious or a virtuous cycle. It's early days for the Qt Marketplace. Let's give it a chance.
Reading
QList Changes in Qt 6 by Andrei Golubev (The Qt Company),
The C++ Core Guidelines are very clear: SL.con.2: Prefer using STL vector by default unless you have a reason to use a different container. In their book C++ Coding Standards, Herb Sutter and Andrei Alexandrescu state in Guideline 76: "Use vector by default. Otherwise, choose an appropriate container". The same guideline holds for Qt: Use QVector by default. Otherwise, choose an appropriate container. Especially, don't use QList by default (see the section QVector in Marc Mutz' excellent post Understanding the Qt Containers for the reasons).
There is a serious problem with using QVector as the default for Qt applications. QList is used everywhere in the Qt APIs. So, it's often more convenient to use QList in your application code as well. Qt 6 finds a cheeky solution for this problem. QList in Qt 6 is implemented the same way as QVector. QVector becomes an alias to QList. Hence, the APIs can remain unchanged and QList provides all the benefits of QVector.
Andrei explains some more improvements of QList in Qt 6: fast prepend, shrinking on removal of elements and size greater than 2 GB.
Quickly Testing Qt Desktop Applications with Approval Tests (video) by Clare Macrae (Independent Consultant).
Clare's talk is one of those rare talks, where I can immediately start using the presented ideas and tips in my own work. And I did: My first GUI components are secured with approval tests. More will follow. The project team lost the excuse to forgo tests for GUI components 😉
Approval testing provides a methodology how to write tests for Qt GUI applications that lack tests (a.k.a. legacy applications). Writing unit tests for legacy applications is extremely onerous, because the class under test (CuT) depends on dozens of other classes. It's not unusual that the CuT depends on almost all classes of the application.
Approval testing extracts everything outside main() into a library dubbed UI.lib. You can instantiate any GUI or non-GUI component from UI.lib and start writing tests. You could use the findChild and findChildren functions from QObject to find buttons, labels and other widgets by their object name. This would be a brittle and fragile approach.
Clare introduces a much better approach: test fixtures. Test fixtures act as mediators and adapters between between test classes and classes under test. Your test calls functions from the fixture to click a button of the CuT or to send a signal to a slot of the CuT. It also calls fixture functions to check whether a label shows the right text or whether some buttons are correctly enabled. If the fixture class is a a friend of the CuT, it can access the private member variables of the CuT. Fixtures make tests shorter and easier to understand.
Whenever you fix a bug or add a new feature, you first write approval tests and then refactor the code until the change becomes easy. You always have tests to guide your codings steps. After some time you have sufficient test coverage to extract parts from UI.lib into new libraries. The first new library will probably be Model.lib, which contains all the non-GUI parts of your application. Based on your ever growing suite of tests, you break out smaller and smaller components for testing until you reach the level of unit tests.
The split between UI.lib and Model.lib is natural for QML applications. UI.lib contains all the QML components and is best tested with QtQuick Test. Model.lib comprises all the Qt/C++ components and is test with QTest, Catch2 or Google Test.
Approval testing helps you overcome the paralysis when you start on a legacy code base. Clare's customers typically need half a day to write their first approval tests. They need a week or two to get rolling with approval tests. They start seeing productivity gains and quality improvements after 1-2 months. So, what are you waiting for? Hire Clare to coach your team or take one of her courses.
So you want to build an embedded Linux system? by Jay Carlson.
Jay has brought up Linux on 10 (!) low-cost boards with microprocessors including the TI AM335X, NXP i.MX 6ULL and three Allwinner SoCs. And, Jay assembled the boards himself! Simply amazing! The bring-up is done, when Linux boots on the board and he can log in via SSH or serial console.
Jay makes a very interesting statement about the prices of microcontrollers (MCUs) and microprocessors (MPUs) in the Introduction: "Cortex-M7 parts that can barely hit 500 MHz routinely go for double or quadruple the price of faster Cortex-A7s."
I think that this statement raises some questions about the commercial viability for Qt on MCUs. Qt runs best on the powerful Cortex-M7s and Cortex-M4s with graphics acceleration. They cost at least as much as Cortex-A7s. The "Qt for MCU" license adds costs in the middle 5-digit EUR range. Development on MPUs with Linux is a lot easier and less expensive than on MCUs. Why should customers pay good money for using Qt on MCUs? I'd be very interested in your answers.
Jay discusses a couple of reasons why you should use Linux on an MPU: dynamic memory allocation, interoperability (USB, I2S, cameras, etc.), security, filesystems, databases, libraries, multiple processes, hardware abstraction layers, developer availability and cost of development. He also gives some reasons why you shouldn't use Linux: power consumption, boot time and responsiveness for real-time tasks.
The author groups the MPUs into three classes with their typical applications.
Slower ARM9 cores are for simple headless gadgets written in C/C++. You can run simple Qt GUIs without animations and blending on these MPUs. The dumb phones of the early noughties are good examples. The NXP i.MX27 would be a classic ARM9 core.
Mid-range 500-1000 MHz Cortex-A-series systems can start to support interpreted / JIT-compiled languages better (with at least 128 MB RAM). These MPUs are good enough for a single QML application with animations, blending and some other graphical effects. Examples are driver terminals in harvesters or the touch HMIs of home appliances. Typical MPUs are NXP i.MX5x, i.MX7 and i.MX6.
Multi-core 1+ GHz Cortex-A parts with 256 MB of RAM or more will begin to support desktop/server-like deployments (with GPUs).You are now looking at multi-application systems like in-vehicle infotainments systems. Typical MPUs are NXP i.MX8, NVIDIA Jetson and AMD Ryzen.
You might ask: Why not use a Raspberry Pi for your embedded systems? Jay's answer: "This is a processor that was custom-made, from the ground up, to go into smart TVs and set-top boxes — it’s not a general-purpose embedded Linux application processor, so it isn’t generally suited for embedded Linux work."
The Contenders contain 3x ARM9, 1x Cortex-A5, 4x Cortex-A7, 1x Cortex-A9 and 1x Cortex-A32 MPU. The MPUs from Microchip, TI, NXP and ST cover a wide variety of embedded systems. The Nuvoton MPU is best suited for IoT applications. The Allwinner and Rockchip MPUs are bested used in consumer devices like IP cameras or tablets. Allwinner's support for mainstream Yocto and buildroot builds is wanting.
Jay focuses on truly low-cost SoCs. The NXP i.MX 6ULL, for example, costs 2.68 USD as a single piece. The Nest Thermostat runs on this SoC. The SoC should be good enough for single QML applications with light use of animations and other graphical effects. It could be a good replacement for the NXP i.MX35, which is used in the operator terminals of cranes and of feed mixers for cattle.
Now enjoy the detailed evaluations of the ten contenders. You can get a quick overview in the Discussion.
What's Wrong with MQTT? by John S. Rinaldi (CEO, Real Time Automation).
John acknowledges the benefits of MQTT: lightweight, publisher-subscriber messaging, scalable, multicast, firewall traversal, no coupling between publisher and subscriber. This leads many people to believe that MQTT is the "magic bullet for moving data around the factory floor". Well, it's not. No IoT protocol is.
All IoT protocols have their strengths and weaknesses. You must know both, when you choose an IoT protocol for your device fleet. As the strengths of MQTT are well-known, John focuses on its weaknesses. I add my own thoughts to balance John's strong statements a bit.
MQTT Offers No Interoperability
MQTT transfers byte arrays. If sender and receiver interpret the byte arrays differently, they don't understand each other. If sender and receiver are from different vendors, they will rarely interoperate with each other. This leads to vendor lock-in.
This reminds me of the situation on agricultural machines when CAN bus was new. The tractor of vendor A couldn't talk with the seeder of vendor B, because each vendor had its proprietary interpretation of CAN messages. Farmers better bought all machines from the same vendor - for higher prices than necessary.
Then, the agricultural industry decided to standardise the structure and interpretation of CAN messages with J1939. J1939 messages have a standardised ID, which defines how to interpret the data bytes (see Introduction to the SAE J1939 Standard). MQTT could use a similar lightweight approach to improve its interoperability. GATT profiles used by Bluetooth Low Energy (BLE) could be another approach.
MQTT Provides No Data Discovery
A subscriber has no means to discover which topic names a publisher provides (e.g. "/weather/de/84453/temperature" would be the temperature in the ZIP code area I live). All subscribers must be provided with the available topic names by a mechanism outside MQTT.
Well-specified interpretations of MQTT messages similar to J1939 messages or GATT profiles would provide such an out-of-band mechanism for data discovery. All communication nodes (brokers, publishers, subscribers) must know the message specification - through configuration files, for example.
As long as MQTT messages are not standardised, I'd advise you to specify all admissible messages in configurations files. You can generate code from these configuration files and integrate the generated code in the applications running on the publishers, brokers and subscribers. This approach guarantees that all communication nodes understand each other.
Subscribers are Unaware of Status Changes in the Producers
If a publisher resends the same message, all subscribers will see the message again. This wastes bandwidth.
I'm not quite sure what the author means. The obvious solution would be that publishers don't resend messages or that publishers send messages only if their values changed. With the quality-of-service level "at least once", duplicate messages can occur. This could be fixed by using the "at most once" or "exactly once" level.
MQTT Doesn’t Work For Battery Backed Devices
In this absoluteness, the statement is wrong. MQTT can be used on battery backed devices. However, it wouldn't be my first choice for an ultra-low-power device like a sensor that must run for 12 years on a coin cell. Smart gas meters are examples of such sensors.
Power consumption depends on how often a device wakes up to send a message (wake-up frequency), how long the device is awake for sending a message (awake interval) and how much power the device draws during the average awake interval. Given these three parameters and the battery capacity, you can roughly calculate how long the battery will last.
Most of the time MQTT is used over TCP - and Qt MQTT is no exception. Before a publisher can send a message, it must establish a TCP connection. Establishing a TCP connection takes a lot longer than sending a message. So, the awake interval is longer for MQTT over TCP than for MQTT over UDP. If the wake-up frequency is the same for both variants, MQTT over TCP will consume more power than MQTT over UDP. The battery will be empty quicker.
So, you may end up with 5 years of battery life instead of 7 years. Alternatively, you could reduce the wake-up frequency and achieve 7 years as well. So, you can definitely use vanilla MQTT for battery-backed devices, but it might not be the best choice for ultra-low-power devices. For that, you could use CoAP (see Qt CoAP) or MQTT-SN, where SN stands for sensor networks. MQTT-SN uses UDP or Bluetooth as the transport layer. Or, you write your own protocol, which was the solution for the smart gas meters mentioned above.
MQTT Requires TLS for Security
MQTT sends messages unencrypted. As MQTT works over TCP, it uses TLS for encryption. Then, MQTT is no longer a lightweight protocol. It's not suited for heavily resource-constrained devices like sensors.
Again, the statement seems to be a bit too strong. Sensors could use MQTT-SN. Especially, the user-space security layer of Bluetooth should be a lot smaller than TLS. I am not sure about UDP, which uses datagram TLS (DTLS) for security. Like MQTT-SN over UDP, CoAP uses DTLS.
A Field Guide to CoAP — Part 1 by Jonathan Beri.
Jonathan gives an informative introduction into CoAP, the Constrained Application Protocol, defined in RFC7228. CoAP is geared towards heavily resource-constrained devices like sensors. The typical device has 100s of KBs of RAM and flash, runs at frequencies of 10s of MHz and runs from battery for years. Data rates are measured in Kbps (kilo bits per second). Lightweight security is a must. CoAP competes with MQTT-SN and may be the better choice for ultra-low-power devices.
CoAP is a "lighter version of HTTP designed for IoT". It uses the same request-response model as HTTP, provides URIs like coaps://[URI]:5684 (secure) and coap://[URI]:5683 (insecure) and understands the operations GET, PUT, POST and DELETE. As devices may go offline for a planned and unplanned reasons, CoAP provides means for caching and de-duplication. It supports multicast messaging.
Based on DTLS, CoAP provides for security levels: no security, pre-shared keys, raw public keys and certificates. The more secure these methods are the less lightweight they are. Jonathan will cover security in detail in Part 4 of his series.
Jonathan lists a few CoAP implementations including libcoap (C/C++), aiocoap (Python) and node-coap (Node.js). I'd like to add Qt CoAP. I am looking to the next three parts of the series: Beyond the Basics, Advanced Uses Cases and CoAP Security in Detail.