Episode 26: Burkhard on Qt Embedded Systems
Welcome to Episode 26 of My Newsletter on Qt Embedded Systems
Last week, I gave my second interview to investors on Qt in Automotive. The biggest fear of the investors in both interviews was that the value gap between Qt LGPLv3 and Qt Commercial is too small. The investors think that Qt Commercial doesn't add enough extra value to Qt LGPLv3 to entice potential customers to pay for Qt. Unfortunately, I don't see how the Simplified Qt Commercial Licensing addresses this problem. See my thoughts below.
Enjoy reading and take care - Burkhard 💜
In November and December, I attended the Positioning Bootcamp from Win Without Pitching (see also the books Win Without Pitching Manifesto and Pricing Creativity by Blair Enns and his colleagues). The Bootcamp helped me to define my niche and differentiate me from my competition. I now have a much clearer picture what kind of projects I want to work on in the future. A clear position is the precondition for moving from hourly billing to value pricing.
I am the specialist in smart user interface for industrial machinery. I am on a mission to free smart machines from dumb user interfaces, one machine at a time.
In my mission statement, I give five guidelines how manufacturers will get a much higher return on investment when equipping their machines with smart user interfaces.
Guideline 1: Focus on what customers will buy.
Guideline 2: Give machines all information needed.
Guideline 3: Build deep learning into user interfaces.
Guideline 4: Let your best people develop your user interface.
Guideline 5: Focus on value, not on costs.
My Thoughts on the Simplified Qt Commercial Licensing
Santtu Ahonen, the Senior Product Manager responsible for Portfolio Management at The Qt Company, announced a new and simplified Qt commercial licensing. Did The Qt Company simplify the Qt commercial licensing? Yes, in some way. Will they find more paying customers? No. Will they make a higher profit? Unsure.
Until now, there were many separate licenses for Qt embedded systems: Qt for Device Creation (~$4K per developer and royalties per device), Qt for MCU (> $50K per product), Qt Safe Renderer (> $50K per product), Qt Automative Suite (> $100K), Qt for Automation (~$10K), Qt Marketplace license (including M2M protocols) and probably some more (see more in my post Using Qt 5.15 and Qt 6 under LGPLv3).
The new commercial licensing folds these half a dozen licenses into two - some kind of simplification.
Qt for Device Creation Enterprise. This license includes everything from the Professional license and adds the Safe Renderer and the application manager. The Qt applications may run on any hardware including automotive-grade hardware.
Qt for Device Creation Professional. This is the old Qt for Device Creation license enriched by Qt for MCU, the M2M protocols (e.g., MQTT, CoAP, OPC UA) and technical support. Qt applications under this license must not run on automotive-grade hardware that is hardened for temperatures from -40°C to +125°C (the exact range is not clear yet). Industrial-grade hardware (0°C to +70°C) and consumer hardware (0°C to +40°C) is OK.
I have no insight into the pricing of the new licenses. I expect them to be considerably higher than before, because the new license layers include some expensive features that were separately licensed before. There are still per-developer fees and per-devices fees (royalties).
The Qt Marketplace license wasn't mentioned at all. I suspect that it will be removed, as the M2M protocols are now part of the Professional license. It wouldn't make sense to offer Qt MQTT or Qt CoAP at $130 per year and to let companies combine them with Qt LGPLv3.
Even if your subscription has expired, you can now distribute your applications forever with the last Qt version you paid for. This is important for medical devices. They may be shut down by regulatory authorities, when the lifetime of a component like Qt ends.
The old Qt for Device Creation license will be converted into the Professional license. The new license adds some features: MCU support and M2M protocols. It also removes an important feature: running on any system-on-chip (SoC) including automotive-grade SoCs. Manufacturers of agricultural machines, construction machines, trucks and cars would not qualify for the Professional license. They would have to buy the even more expensive Enterprise license.
These raises two questions:
How many companies will move from Qt LGPLv3 to Qt Professional? Fewer than before.
How many companies will move from Qt Professional to Qt Enterprise because they use automotive-grade hardware? Very few. Some may even return to Qt LGPLv3.
Many companies are already skeptical about the additional value of the old Qt for Device Creation license compared to Qt LGPLv3. They will hardly ever run their HMI on an MCU. The iMX6ULL SoCs are cheaper than the MCUs powerful enough for Qt. There are plenty of FOSS and commercial alternatives available for the M2M protocols. These companies had few reasons to buy the cheaper old license. They have even fewer reasons to buy the more expensive Professional license.
Manufacturers of agricultural and construction machines using the old license would have to upgrade from the Professional license to the even more expensive Enterprise license - just because they happen to use automotive-grade SoCs. The application manager is a hardly convincing sales argument. The workarounds are plenty and simple. Few would see the need for the Safe Renderer. I'd suppose that many of these companies would try hard to figure out how to move to Qt LGPLv3 and pay nothing.
Qt Enterprise targets medical device and car OEMs. Car OEMs are extremely cost-conscious. Saving small amounts for a million cars adds up quickly. Hence, it isn't surprising when car OEMs require that their suppliers use FOSS components wherever possible. One OEM told their prospective supplier that they must use Qt LGPLv3 instead of Qt Commercial to be considered.
Qt Professional targets makers of industrial-grade machines, consumer products and home appliances. One of the world's top-5 home appliance makers decided for Qt LGPLv3. A direct competitor decided for Qt Commercial. The higher prices for Qt Professional may make them reconsider.
From many conversations, I know that almost all companies hate paying prices depending on the number of developers and devices and having to pay a big sum 5 years in advance to get a discount. However, most of these companies would happily pay a fixed price to The Qt Company per year or per product - for the features they need.
My conclusion. The Qt Company should stop gaming the license terms every year. They should start adding commercial-only features that add considerable value compared to Qt LGPLv3. And, they should create license bundles at fixed prices that match the needs of their customers. Then, they might actually increase their profits and not just their revenues.
Trunk-Based Development (TBD) is one of the fundamental practices of Continuous Delivery. Paul's website is the source about TBD. It also available as a book on Leanpub.
In Trunk-Based Development for Smaller Teams, each developer or each pair of developers (for pair-programming) works directly on the main branch of a git repository. Each developer commits their small code changes to their local copy. After building their software and running enough unit tests to be highly confident that they won't break anything for other developers, they push their changes to main.
Pushing directly to main only works for small teams (4-6 people), where each team member can be trusted to write good unit tests and to run these tests frequently. The developers must use Test-Driven Development (TDD) and best work in pairs. The team should also be able to release their software at any time from main.
Bigger teams or teams sharing code with other teams move to Scaled Trunk-Based Development (scaled TBD, for short). Each developer creates a short-lived branch when they start implementing a feature. Short-lived means 1-2 days with a preference to shorter life times. One developer or one pair works on each feature branch. Having multiple people working on the same feature branch or deriving branches from feature branches is a no-go.
When the developer merges the feature branch back into main, the CI/CD pipeline runs a test suite to decide quickly (less than 5 minutes) and with high confidence that the code changes won't break main. On success, the pipeline merges the code changes into main. On failure, the pipeline rejects the merge. The developer must fix the problem.
TBD ensures that developers integrate small changes frequently, quickly and safely into main. The integration costs for small changes become negligible. In contrast, a branching policy like Git-Flow makes developers go through integration hell regularly and inflates the integration costs.
Some teams think that they do TBD, because they push directly to main. This is nonsense! TBD isn't possible without TDD and without the other preconditions (short-lived features branches, one developer per branch, etc.).
Reading tip: Martin Fowler post Patterns for Managing Source Code Branches explains almost everything about branching policies.
Accessing Private Git Repositories from Docker Containers
A couple of weeks ago, I added a customer-specific Yocto layer to the BSP provided by Topcon for its Opus A8s terminal. This layer also builds my customer's Qt applications, which are stored in a private repository on GitHub. I use kas for building Linux images. Kas uses Docker containers. You'll find numerous tricks on the web (e.g., here, here and here) explaining how to pass the SSH keys from the host to the container or how to use deployment keys. None of these tricks worked for my customers and me.
When I searched this time, I stumbled over the post How to Mirror (Copy) an Entire Existing Git Repository Into a New One. The command git clone --mirror <origin-url> creates a full copy of the repository <original-url> including all refs (remote-tracking branches, local branches, tags, notes, etc.). The command git remote update updates all the refs from the remote repository.
The build script for the Linux image and SDK runs on the host computer (not in the container) and performs multiple steps. One step is to create a mirror of the private repository in the directory /host/path/to/private-repo.git on the host computer.
When the build script runs kas some steps later, the recipes for the Qt applications access the mirror repository. The kas container maps the host directory /host/path/to/private-repo.git to the container path /work/to/private-repo.git. The kas configuration files tells Yocto to search the directory /work/to for repositories by adding to local.conf the line:
SOURCE_MIRROR_URL ?= "file:///work/to/"
The SOURCE_URI in the applications' recipes can still point to the URL of the remote repository. When Yocto can't access the remote repository, it will try the other source mirror URLs including the local one above.
Mutually Exclusive CMake Options
When you build an application for production use, the application shall communicate with the real machine (e.g., over CAN). For development purposes, an adapter simulating the communication is good enough. You need mutually exclusive CMake options. The CMake module CMakeDependentOptions provides them. Here are the relevant lines from the top-level CMakeLists.txt file of the terminal application.
option(MACHINE_ADAPTER_PRODUCT "Product" ON)
MACHINE_ADAPTER_SIMULATOR "Simulator" OFF
"NOT MACHINE_ADAPTER_PRODUCT" ON)
By default, option MACHINE_ADAPTER_PRODUCT is on and option MACHINE_ADAPTER_SIMULATOR is off. In Build Settings > CMake, QtCreator shows
If you set this option to OFF, the "NOT MACHINE_ADAPTER_PRODUCT" ON forces the value of MACHINE_ADAPTER_SIMULATOR to ON, because MACHINE_ADAPTER_OFF is OFF. After pressing the button Apply Configuration, QtCreator will show
I'd like to illustrate the two-way binding problem with a real-life problem. The driver of a forage harvester can change the cutting length of the maize with an always-present button on the home screen.
The button has a value property showing the cutting length. When the button is instantiated, value is bound to the cuttingLength property of type qreal provided by a C++ controller:
value: _controller.cuttingLength # (1)
The driver can change value by tapping the Plus or Minus button or by turning the rotary knob. In all cases, the button calls an event handler and assigns a new value to value, e.g.:
value = value + 1 # (2)
This assignment creates a new binding for value and breaks all previous bindings including binding (1). The GUI sends the changed value to an ECU via CAN. The ECU changes the cutting length of the knives.
The GUI provides a second way to change the cutting length. The driver can go to a menu and open a keypad dialog as developed in this post. When the driver accepts the dialog, the new value is sent to the ECU. The ECU also notifies the button from the home screen about the changed value. As binding (2) broke binding (1), the modified value is never shown on the button.
In his unbreakable binding approach, André suggests to instantiate a C++ class RealValue in the button component. The instance has the ID internal. This class provides a readable and writable property value of type qreal. Binding (1) stays unchanged. Binding (2) becomes unbreakable by calling the WRITE function of the value property.
The WRITE function does not break the binding.
The unbreakable binding approach is André's preferred solution. An alternative, which doesn't need a C++ class, is the proposed value approach. He also describes three ways how not to do it. I agree with his assessment of Don't explicitly re-create the binding and Don't use aliased-in value.
I am not convinced by his argument against the model approach. The model approach suggests moves the instantiation of RealValue from the QML button component to the C++ controller. The controller would provide the cuttingLength property (the "model" in André's parlance) as follows:
Q_PROPERTY(RealValue *cuttingLength READ cuttingLength CONSTANT)
André writes that the model approach is wasteful and bloated. This is probably true for a boolean value as in his example, but not for the RealValue class required for ECU parameters like cutting length. RealValue must provide properties for value, minimum, maximum, step size, precision and unit.
Qt 5.3 saw the introduction of the QtQuick compiler qmlcompiler. The QML compiler converted all QML code into C++. The C++ compiler translated all files into machine code. The QML engine didn't need JIT compilation any more. The QML compiler reduced the start-up times of applications dramatically. I saw a 30% speedup for a terminal application.
This weakness of the QML compiler lead to the introduction of qmlcachegen in Qt 5.9. qmlcachegen translates QML code into byte code. The QML engine compiles the byte code into machine code and caches the byte code between invocations of the applications.
Neither the qmlcompiler nor qmlcachegen were ideal. The crucial question was: How could the type information known at runtime be made available at compile time? The new QML modules introduced by Qt 6.2 came to the rescue (see Ulf's post QML Modules in Qt 6.2). They define QML types at compile time through the CMake API.