Embedded Linux HMI Development: Custom Interface Design with Yocto and Qt

Amazeng Technical Team
10 min read
Embedded LinuxHMIYocto ProjectQtQMLIndustrial UITouchscreen

Introduction

The dominance of Windows CE and proprietary solutions in industrial HMI systems has been rapidly shifting towards Embedded Linux in recent years. Our HMI Industrial Display Solutions are at the center of this paradigm shift: customized Linux distribution with Yocto Project and modern interface development with Qt/QML.

In this article, we examine how to build an embedded Linux HMI from scratch, Qt integration on the Yocto infrastructure we explained in our blog, and real-world applications.

Why Embedded Linux + Qt?

Limitations of Traditional HMI Solutions

1. Windows CE / Windows Embedded

  • License costs ($100+ per device)
  • Security updates ended (Windows CE EOL in 2018)
  • High hardware requirements (minimum 512MB RAM)

2. Proprietary Solutions (Siemens WinCC, Rockwell FactoryTalk)

  • Vendor lock-in
  • Limited customization options
  • High software costs

3. Arduino/MCU-based Simple Displays

  • Low graphics performance
  • Limited networking (weak TCP/IP stack)
  • Insufficient for complex applications

Advantages of Embedded Linux + Qt

Open Source & Free: No license costs
Full Control: Customize every layer of OS
Powerful Graphics: Hardware-accelerated UI with Qt Quick (QML)
Networking: Native TCP/IP, MQTT, Modbus support
OTA Updates: Secure software updates with A/B partition
Long Lifespan: Linux kernel and Qt, 10+ years support guarantee

Yocto Project: Custom Linux Image

What is Yocto?

Yocto Project is a build system for creating customized Linux distributions for embedded devices. Difference from standard Ubuntu/Debian:

  • Minimal footprint: Only needed packages included (~50MB rootfs)
  • Cross-compilation: Compile for ARM processors on development PC (x86)
  • Reproducible builds: Every build produces same result

Yocto Layer Structure for HMI

meta-hmi-amazeng/
├── recipes-core/
│   └── images/
│       └── hmi-image.bb          # Main image recipe
├── recipes-graphics/
│   └── qt5/
│       └── qtbase_%.bbappend     # Qt customizations
├── recipes-connectivity/
│   └── modbus/
│       └── libmodbus_git.bb      # Modbus library
├── recipes-kernel/
│   └── linux/
│       └── linux-%.bbappend      # Touchscreen drivers
└── recipes-app/
    └── hmi-app/
        └── hmi-app_git.bb        # HMI application

Example HMI Image Recipe

# hmi-image.bb
DESCRIPTION = "Amazeng HMI Linux Image"
LICENSE = "MIT"

IMAGE_FEATURES += " \
    splash \
    ssh-server-dropbear \
    hwcodecs \
"

IMAGE_INSTALL = " \
    packagegroup-core-boot \
    qtbase \
    qtdeclarative \
    qtquickcontrols2 \
    qtgraphicaleffects \
    qtsvg \
    qtwebsockets \
    libmodbus \
    hmi-app \
    ca-certificates \
    tzdata \
"

inherit core-image

Modern HMI Interface with Qt/QML

Qt Quick (QML) vs Qt Widgets

FeatureQt Widgets (C++)Qt Quick (QML)
PerformanceCPU-basedGPU-accelerated
Development SpeedSlow (compile)Fast (interpreted)
AnimationComplexVery easy
TouchscreenMouse eventsNative touch
Designer SupportQt DesignerQt Design Studio

Qt Quick (QML) should definitely be preferred for HMI applications.

Simple HMI Application: Loadcell Reader

HMI reading weight from GDT Digital Transmitter via Modbus TCP:

main.qml

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15

ApplicationWindow {
    visible: true
    width: 1024
    height: 600
    title: "GDT Loadcell Monitor"

    // Background gradient
    Rectangle {
        anchors.fill: parent
        gradient: Gradient {
            GradientStop { position: 0.0; color: "#2C3E50" }
            GradientStop { position: 1.0; color: "#34495E" }
        }
    }

    ColumnLayout {
        anchors.centerIn: parent
        spacing: 30

        // Title
        Text {
            text: "Tank 1 - Net Weight"
            font.pixelSize: 36
            font.bold: true
            color: "#ECF0F1"
            Layout.alignment: Qt.AlignHCenter
        }

        // Weight display
        Rectangle {
            width: 600
            height: 200
            color: "#1ABC9C"
            radius: 20

            Text {
                anchors.centerIn: parent
                text: modbusBackend.weight.toFixed(2) + " kg"
                font.pixelSize: 72
                font.bold: true
                color: "white"
            }
        }

        // Control buttons
        RowLayout {
            Layout.alignment: Qt.AlignHCenter
            spacing: 20

            Button {
                text: "TARE"
                font.pixelSize: 24
                implicitWidth: 150
                implicitHeight: 80
                onClicked: modbusBackend.tare()
            }

            Button {
                text: "ZERO"
                font.pixelSize: 24
                implicitWidth: 150
                implicitHeight: 80
                onClicked: modbusBackend.zero()
            }
        }
    }
}

Real World Application: Multi-Tank Monitoring

Scenario

3 milk tanks, each with GDT Digital Transmitter + 4 loadcells. Monitoring all tanks with a central 10.1" HMI.

Architecture

┌──────────────┐    Modbus TCP     ┌──────────────┐
│  Tank 1 GDT  │◄─────────────────►│              │
│ 192.168.1.101│                   │              │
└──────────────┘                   │   HMI Panel  │
                                   │  (10.1 inch) │
┌──────────────┐    Modbus TCP     │   Qt/QML     │
│  Tank 2 GDT  │◄─────────────────►│              │
│ 192.168.1.102│                   │              │
└──────────────┘                   └──────────────┘

Cloud Integration: HMI + AWS IoT

HMI panel can also work as an IoT gateway. In our Cloud & IoT Data Collection solution, HMI:

  1. Collects data from ZMA and GDT devices via Modbus RTU/TCP
  2. Sends to AWS IoT Core via MQTT
  3. Displays both on local interface and Grafana dashboard

Qt MQTT Client Example:

#include <QtMqtt/QMqttClient>

QMqttClient *mqttClient = new QMqttClient(this);
mqttClient->setHostname("xxxxx.iot.eu-west-1.amazonaws.com");
mqttClient->setPort(8883);
mqttClient->connectToHost();

// Publish data read from GDT
QByteArray payload = QString("{\"tank_id\":1,\"weight\":%1}").arg(weight).toUtf8();
mqttClient->publish("amazeng/tank/data", payload);

Conclusion

The Embedded Linux + Qt combination is the most flexible and powerful solution for modern industrial HMIs. Our HMI Industrial Display Solutions are built on this technology stack and:

✅ 70% more economical than proprietary solutions
✅ Full customization freedom
✅ 10+ years long-term support guarantee
✅ Ready for Cloud and IoT integration