Embedded HMI Development

Running Qt on embedded Linux (Raspberry Pi, i.MX6), eglfs/linuxfb backends, and touch input handling.

3 min read
23312 chars

Qt on Embedded Linux

Qt runs on embedded Linux without a desktop environment. It drives the framebuffer or GPU directly via platform plugins:

PluginDescription
eglfsOpenGL ES directly to framebuffer — best for GPU targets (i.MX6, Pi 4)
linuxfbSoftware rendering to /dev/fb0 — for targets without GPU
xcbX11 — when running a full desktop
waylandWayland compositor support

Target Hardware

Common embedded Linux targets for Qt HMI:

BoardSoCGPUPlugin
Raspberry Pi 4BCM2711 (ARM A72)VideoCore VIeglfs
Raspberry Pi Zero 2WBCM2710 (ARM A53)VideoCore IVlinuxfb or eglfs
NXP i.MX6ARM Cortex-A9Vivante GC2000eglfs
BeagleBone BlackAM335x (ARM A8)No GPUlinuxfb
STM32MP1ARM Cortex-A7No 3D GPUlinuxfb

Cross-Compiling Qt for Raspberry Pi

Step 1 — Install Sysroot

# On your host PC, create a Raspberry Pi sysroot
mkdir -p ~/rpi-sysroot
rsync -avz --rsync-path="sudo rsync" \
    pi@raspberrypi.local:/usr/include  ~/rpi-sysroot/usr/
rsync -avz --rsync-path="sudo rsync" \
    pi@raspberrypi.local:/usr/lib      ~/rpi-sysroot/usr/
rsync -avz --rsync-path="sudo rsync" \
    pi@raspberrypi.local:/lib          ~/rpi-sysroot/

Step 2 — Toolchain File (cmake/rpi-toolchain.cmake)

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

set(SYSROOT $ENV{HOME}/rpi-sysroot)
set(CMAKE_SYSROOT ${SYSROOT})

set(CMAKE_C_COMPILER   arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)

set(CMAKE_FIND_ROOT_PATH ${SYSROOT})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

Step 3 — Build

cmake -B build-rpi \
    -DCMAKE_TOOLCHAIN_FILE=cmake/rpi-toolchain.cmake \
    -DCMAKE_PREFIX_PATH=~/Qt/6.x/gcc_arm \
    -DCMAKE_BUILD_TYPE=Release

cmake --build build-rpi --parallel $(nproc)

Step 4 — Deploy and Run

# Copy binary to Pi
scp build-rpi/MyHMI pi@raspberrypi.local:~/

# Run on Pi (no desktop needed)
ssh pi@raspberrypi.local \
    "QT_QPA_PLATFORM=eglfs ./MyHMI"

# For software rendering (no GPU)
ssh pi@raspberrypi.local \
    "QT_QPA_PLATFORM=linuxfb:fb=/dev/fb0 ./MyHMI"

Touch Input

Qt auto-detects touchscreens on Linux via evdev. Set the device if needed:

# Run with explicit touch device
QT_QPA_PLATFORM=eglfs \
QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS=/dev/input/event0 \
./MyHMI

In code, handle touch events:

class TouchWidget : public QWidget {
protected:
    void touchEvent(QTouchEvent *event) override {
        for (const auto &point : event->points()) {
            if (point.state() == QEventPoint::Pressed) {
                qDebug() << "Touch at:" << point.position();
            }
        }
        event->accept();
    }

    bool event(QEvent *e) override {
        if (e->type() == QEvent::TouchBegin ||
            e->type() == QEvent::TouchUpdate ||
            e->type() == QEvent::TouchEnd)
        {
            touchEvent(static_cast<QTouchEvent*>(e));
            return true;
        }
        return QWidget::event(e);
    }
};

Enable multi-touch:

setAttribute(Qt::WA_AcceptTouchEvents);

Screen Orientation & Resolution

// Force landscape orientation in QML
Window {
    contentOrientation: Qt.LandscapeOrientation
}

// Set screen via environment
// QT_QPA_PLATFORM=eglfs QT_QPA_EGLFS_ROTATION=90 ./MyHMI
// Query screen info at runtime
QScreen *screen = QGuiApplication::primaryScreen();
qDebug() << "Resolution:" << screen->size();
qDebug() << "DPI:"        << screen->physicalDotsPerInch();
qDebug() << "Pixel ratio:" << screen->devicePixelRatio();

Optimizing Qt for Embedded

Reduce Binary Size

# Strip debug symbols in release
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -s")

# Enable LTO
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)

Reduce Memory Usage

// Disable unused Qt features in qtconfig.pri or configure Qt with:
// -no-feature-accessibility
// -no-feature-printer
// -no-feature-sql

// Use QOpenGLWidget instead of QWidget where GPU acceleration helps
// Prefer QML for animated UIs (GPU-composited)

Startup Time

# Pre-load Qt libraries via ldconfig
ldconfig

# Use Qt Quick Compiler to pre-compile QML
# In CMakeLists.txt:
qt_add_qml_module(MyApp
    QML_FILES Main.qml
    RESOURCE_PREFIX /
    OUTPUT_TARGETS MyApp_qmlcache  # pre-compiled QML
)

Systemd Service — Auto-Start on Boot

# /etc/systemd/system/hmi.service
[Unit]
Description=EmbeddexAI HMI Application
After=multi-user.target

[Service]
Type=simple
User=pi
Environment="QT_QPA_PLATFORM=eglfs"
Environment="QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS=/dev/input/event0"
ExecStart=/home/pi/MyHMI
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo systemctl enable hmi.service
sudo systemctl start hmi.service

Summary

TopicKey Point
eglfsBest plugin for GPU targets (Pi 4, i.MX6)
linuxfbSoftware rendering, no GPU required
Cross-compileUse sysroot + CMake toolchain file
Touch inputQt auto-detects evdev; set device via env var
Startup serviceUse systemd to auto-launch HMI on boot
OptimizationStrip symbols, LTO, pre-compile QML

Next tutorial → Cross-Platform Deployment