VibeKoding / Level 3 Β· MasterLevel 3 Β· Master / Antarmuka Mesin Industri (HMI) dengan Qt C++/PythonAntarmuka Mesin Industri (HMI) dengan Qt C++/Python
VK

Antarmuka Mesin Industri (HMI) dengan Qt C++/PythonAntarmuka Mesin Industri (HMI) dengan Qt C++/Python

πŸ“š Level 3 Β· MasterLevel 3 Β· Master 🌏 Dual Bahasa (ID / EN) ⚑ VibeKoding Native

Modul master Level 3 VibeKoding: Antarmuka Mesin Industri (HMI) dengan Qt C++/Python.Modul master Level 3 VibeKoding: Antarmuka Mesin Industri (HMI) dengan Qt C++/Python.

In this tutorial, we will complete a full closed loop: build an industrial-grade pump monitoring HMI (Human-Machine Interface) system from scratch with Qt. It can read sensor data in real time, draw pressure trend charts, trigger automatic over-threshold alarms, and record fault logs. The whole process uses free simulation software on a PC instead of real industrial hardware.In this tutorial, we will complete a full closed loop: build an industrial-grade pump monitoring HMI (Human-Machine Interface) system from scratch with Qt. It can read sensor data in real time, draw pressure trend charts, trigger automatic over-threshold alarms, and record fault logs. The whole process uses free simulation software on a PC instead of real industrial hardware.

For this tutorial, you should at least have:For this tutorial, you should at least have:

> Zero hardware, zero cost: use free PC simulation software (Modbus Slave) as the lower-level device; no need to buy hardware. Use official Qt QModbusTcpClient + Qt Charts modules directly, no manual protocol parsing needed. After running, you will see real-time pressure trends, over-threshold alarm popups, and fault logs, matching real factory workflow.> Zero hardware, zero cost: use free PC simulation software (Modbus Slave) as the lower-level device; no need to buy hardware. Use official Qt QModbusTcpClient + Qt Charts modules directly, no manual protocol parsing needed. After running, you will see real-time pressure trends, over-threshold alarm popups, and fault logs, matching real factory workflow.

1.1 What Are Upper Computer and Lower Computer?1.1 What Are Upper Computer and Lower Computer?

In industrial automation, there are two concepts you must understand: upper computer and lower computer.In industrial automation, there are two concepts you must understand: upper computer and lower computer.

Lower Computer: the "hands and feet" on-siteLower Computer: the "hands and feet" on-site

The lower computer is the controller that directly interacts with physical devices. In factories, it is usually a PLC (Programmable Logic Controller) or sensor, responsible for:The lower computer is the controller that directly interacts with physical devices. In factories, it is usually a PLC (Programmable Logic Controller) or sensor, responsible for:

You can think of the lower computer as a "worker" on the factory floor. It does not need complex thinking, but must execute tasks reliably.You can think of the lower computer as a "worker" on the factory floor. It does not need complex thinking, but must execute tasks reliably.

Upper Computer: the "eyes and brain" in the control roomUpper Computer: the "eyes and brain" in the control room

The upper computer is monitoring software running on PC or industrial computer, which is the HMI (Human-Machine Interface) we will build today. It is responsible for:The upper computer is monitoring software running on PC or industrial computer, which is the HMI (Human-Machine Interface) we will build today. It is responsible for:

You can think of the upper computer as the factory's "monitoring center." Operators can understand plant status from the screen.You can think of the upper computer as the factory's "monitoring center." Operators can understand plant status from the screen.

How do they communicate?How do they communicate?

Upper and lower computers exchange data through industrial communication protocols. The most common one is Modbus, a "veteran" protocol born in 1979. It is still widely used because it is simple, reliable, and supported by almost all industrial devices.Upper and lower computers exchange data through industrial communication protocols. The most common one is Modbus, a "veteran" protocol born in 1979. It is still widely used because it is simple, reliable, and supported by almost all industrial devices.

text
Control room Factory site β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” Modbus protocol β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Upper β”‚ ◄──────────────────► β”‚ Lower β”‚ β”‚ computer β”‚ "Tell me pressure" β”‚ computer β”‚ β”‚ (Qt HMI) β”‚ "Pressure is 1.20MPa"β”‚ (PLC/Sensor) β”‚ Display β”‚ β”‚ Read dataβ”‚ β”‚ Log data β”‚ β”‚ Control β”‚ β”‚ Alarms β”‚ β”‚ Protect β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ–ΌοΈ placeholder: Diagram of upper vs lower computer relationship: PC screen (upper computer) on the left, PLC and pump (lower computer) on the right, connected via Modbusplaceholder: Diagram of upper vs lower computer relationship: PC screen (upper computer) on the left, PLC and pump (lower computer) on the right, connected via Modbus

1.2 What Is Modbus Protocol?1.2 What Is Modbus Protocol?

Modbus is the "common language" of industrial communication. It defines how upper and lower computers "talk."Modbus is the "common language" of industrial communication. It defines how upper and lower computers "talk."

Only two core concepts:Only two core concepts:

Two common Modbus variants:Two common Modbus variants:

VariantTransportTypical Scenario
Modbus RTUSerial (RS-485/RS-232)Short distance, direct device connection
Modbus TCPEthernet (TCP/IP)Long distance, network communication

This tutorial uses Modbus TCP. Since it is network-based, upper-computer app and lower-computer simulator can run on the same machine with no physical wiring.This tutorial uses Modbus TCP. Since it is network-based, upper-computer app and lower-computer simulator can run on the same machine with no physical wiring.

1.3 Why Choose Qt?1.3 Why Choose Qt?

Qt is a top framework choice for industrial software. Many monitoring interfaces in factories, hospitals, and transportation systems are built with Qt. The reasons are simple:Qt is a top framework choice for industrial software. Many monitoring interfaces in factories, hospitals, and transportation systems are built with Qt. The reasons are simple:

AdvantageExplanation
Cross-platformOne codebase compiles to Windows, Linux, and embedded devices
Built-in industrial protocol supportQt Serial Bus supports Modbus natively, no third-party library required
Powerful chartingQt Charts provides professional real-time charts
High performanceC++ foundation suitable for real-time data refresh
Mature and stable30-year history, proven in industrial domain

1.4 What Are We Building?1.4 What Are We Building?

We will build a Pump Monitoring HMI System simulating real factory pump pressure monitoring:We will build a Pump Monitoring HMI System simulating real factory pump pressure monitoring:

FunctionDescription
Real-time data readingRead pressure from lower computer every second
Pressure trend chartLine chart for last 60 seconds of pressure
Over-threshold alarmPopup warning and red UI when pressure exceeds threshold
Fault logRecord all alarm events in database for history queries
Manual controlOne-click start/stop pump (write lower-computer register)

πŸ–ΌοΈ placeholder: Pump monitoring HMI preview showing real-time pressure number, trend chart, alarm indicator, start/stop button, and log listplaceholder: Pump monitoring HMI preview showing real-time pressure number, trend chart, alarm indicator, start/stop button, and log list

1.5 Tutorial Roadmap1.5 Tutorial Roadmap

We will complete the flow in these steps:We will complete the flow in these steps:

  1. Prepare environment and simulated lower computer (2 minutes): install Qt 6.5 and Modbus Slave simulatorPrepare environment and simulated lower computer (2 minutes): install Qt 6.5 and Modbus Slave simulator
  2. Create Qt project and connect Modbus (3 minutes): establish communication between upper app and simulatorCreate Qt project and connect Modbus (3 minutes): establish communication between upper app and simulator
  3. Implement real-time read and display (3 minutes): timed pressure reads and UI updatesImplement real-time read and display (3 minutes): timed pressure reads and UI updates
  4. Draw real-time pressure trend chart (3 minutes): dynamic line chart with Qt ChartsDraw real-time pressure trend chart (3 minutes): dynamic line chart with Qt Charts
  5. Implement alarm and fault logs (3 minutes): over-threshold alarm + SQLite loggingImplement alarm and fault logs (3 minutes): over-threshold alarm + SQLite logging
  6. Package and deploy (optional): package app into standalone executablePackage and deploy (optional): package app into standalone executable
  7. 2.1 Install Qt 6.52.1 Install Qt 6.5

    Qt provides a free open-source version, enough for this tutorial.Qt provides a free open-source version, enough for this tutorial.

    1. Visit [Qt official site](https://www.qt.io/download-qt-installer) and download Qt Online InstallerVisit [Qt official site](https://www.qt.io/download-qt-installer) and download Qt Online Installer
    2. Run installer, log in or register Qt account (free)Run installer, log in or register Qt account (free)
    3. In component selection, check:In component selection, check:
    4. Qt 6.5.x (or newer)Qt 6.5.x (or newer)
    5. Qt Serial Bus under Additional Libraries (Modbus support)Qt Serial Bus under Additional Libraries (Modbus support)
    6. Qt Charts under Additional Libraries (chart rendering)Qt Charts under Additional Libraries (chart rendering)
    7. Qt Creator (IDE, usually selected by default)Qt Creator (IDE, usually selected by default)
    8. Click install and waitClick install and wait
    9. > Tip: if Qt is already installed but missing Serial Bus or Charts, rerun Qt Maintenance Tool and add components.> Tip: if Qt is already installed but missing Serial Bus or Charts, rerun Qt Maintenance Tool and add components.

      πŸ–ΌοΈ placeholder: Qt installer component selection screenshot highlighting Qt Serial Bus and Qt Chartsplaceholder: Qt installer component selection screenshot highlighting Qt Serial Bus and Qt Charts

      2.2 Install Modbus Slave: Your "Virtual Pump"2.2 Install Modbus Slave: Your "Virtual Pump"

      Modbus Slave is a free Modbus slave simulator. It can simulate an industrial device (PLC/sensor) on your computer so your upper app has something to communicate with.Modbus Slave is a free Modbus slave simulator. It can simulate an industrial device (PLC/sensor) on your computer so your upper app has something to communicate with.

      1. Visit [modbustools.com](https://www.modbustools.com/modbus_slave.html) and download Modbus SlaveVisit [modbustools.com](https://www.modbustools.com/modbus_slave.html) and download Modbus Slave
      2. Install and open itInstall and open it
      3. Configure connection:Configure connection:
      4. Menu Connection -> ConnectMenu Connection -> Connect
      5. Choose Modbus TCP/IPChoose Modbus TCP/IP
      6. IP address: 127.0.0.1 (localhost)IP address: 127.0.0.1 (localhost)
      7. Port: 502 (default Modbus TCP port)Port: 502 (default Modbus TCP port)
      8. Click OK to listenClick OK to listen
        1. Set simulated data:Set simulated data:
        2. You will see a register table, each row is a register address (0, 1, 2, ...)You will see a register table, each row is a register address (0, 1, 2, ...)
        3. Double-click value at address 0, change to 120 (means pressure 1.20 MPa, divided by 100 in app)Double-click value at address 0, change to 120 (means pressure 1.20 MPa, divided by 100 in app)
        4. Double-click value at address 1, change to 350 (means temperature 35.0Β°C)Double-click value at address 1, change to 350 (means temperature 35.0Β°C)
        5. Double-click value at address 2, change to 1 (pump state: 1=running, 0=stopped)Double-click value at address 2, change to 1 (pump state: 1=running, 0=stopped)
        6. Now Modbus Slave is your "24/7 virtual pump." Keep the window open; it will continuously respond to read/write requests.Now Modbus Slave is your "24/7 virtual pump." Keep the window open; it will continuously respond to read/write requests.

          πŸ–ΌοΈ placeholder: Modbus Slave screenshot showing TCP config and simulated register valuesplaceholder: Modbus Slave screenshot showing TCP config and simulated register values

          > Dynamic simulation tip: Modbus Slave supports auto increment/random changes. Right-click register value and choose "Auto increment" or "Random" to simulate realistic sensor fluctuations.> Dynamic simulation tip: Modbus Slave supports auto increment/random changes. Right-click register value and choose "Auto increment" or "Random" to simulate realistic sensor fluctuations.

          3.1 Create New Qt Project3.1 Create New Qt Project

          Open Qt Creator and create a new project:Open Qt Creator and create a new project:

          1. Click File -> New ProjectClick File -> New Project
          2. Choose Application (Qt) -> Qt Widgets ApplicationChoose Application (Qt) -> Qt Widgets Application
          3. Project name: PumpHMIProject name: PumpHMI
          4. Select installed Qt 6.5 kitSelect installed Qt 6.5 kit
          5. Finish creationFinish creation
          6. Open PumpHMI.pro (or CMakeLists.txt if using CMake), and add key modules:Open PumpHMI.pro (or CMakeLists.txt if using CMake), and add key modules:

            pro
            QT += core gui widgets serialbus charts sql
            
            ModulePurpose
            serialbusProvides QModbusTcpClient for Modbus TCP communication
            chartsProvides QChart, QLineSeries for real-time trend chart
            sqlProvides QSqlDatabase for SQLite fault logs

            If using CMake, equivalent config:If using CMake, equivalent config:

            cmake
            find_package(Qt6 REQUIRED COMPONENTS Widgets SerialBus Charts Sql) target_link_libraries(PumpHMI PRIVATE Qt6::Widgets Qt6::SerialBus Qt6::Charts Qt6::Sql)
            

            3.2 Declare Core Members3.2 Declare Core Members

            Ask AI to generate header file:Ask AI to generate header file:

            text
            Please help me write mainwindow.h with core members for pump monitoring HMI: 1. QModbusTcpClient for Modbus TCP communication 2. QTimer for timed data reading 3. QChart + QLineSeries for real-time trend chart 4. QSqlDatabase for fault log storage 5. UI elements: pressure label, status indicator, start/stop button, log table
            

            Core header:Core header:

            cpp
            // mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QModbusTcpClient> #include <QModbusDataUnit> #include <QTimer> #include <QtCharts> #include <QSqlDatabase> #include <QLabel> #include <QPushButton> #include <QTableWidget> class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: void connectModbus(); // connect lower computer void readPressure(); // timed pressure read void onReadReady(); // read callback void triggerAlarm(float v); // trigger alarm void togglePump(); // start/stop pump private: // Modbus communication QModbusTcpClient *m_modbusClient = nullptr; QTimer *m_pollTimer = nullptr; // Real-time chart QChart *m_chart = nullptr; QLineSeries *m_series = nullptr; QDateTimeAxis *m_axisX = nullptr; QValueAxis *m_axisY = nullptr; // Database QSqlDatabase m_db; // UI elements QLabel *m_pressureLabel = nullptr; // pressure display QLabel *m_statusLight = nullptr; // status indicator QPushButton *m_pumpButton = nullptr; // start/stop button QTableWidget *m_logTable = nullptr; // log table // Alarm threshold float m_alarmThreshold = 1.50f; // alarm above 1.50 MPa bool m_pumpRunning = false; void setupUI(); void setupDatabase(); void logAlarm(float pressure, const QString &message); }; #endif // MAINWINDOW_H
            

            πŸ–ΌοΈ placeholder: Screenshot of mainwindow.h in Qt Creatorplaceholder: Screenshot of mainwindow.h in Qt Creator

            3.3 Build Modbus TCP Connection3.3 Build Modbus TCP Connection

            Implement connection logic in mainwindow.cpp:Implement connection logic in mainwindow.cpp:

            cpp
            // mainwindow.cpp - connection section void MainWindow::connectModbus() { m_modbusClient = new QModbusTcpClient(this); // Connect to Modbus Slave simulator m_modbusClient->setConnectionParameter( QModbusDevice::NetworkPortParameter, 502); m_modbusClient->setConnectionParameter( QModbusDevice::NetworkAddressParameter, "127.0.0.1"); m_modbusClient->setTimeout(1000); // 1s timeout m_modbusClient->setNumberOfRetries(3); // retry 3 times if (!m_modbusClient->connectDevice()) { statusBar()->showMessage("Failed to connect lower computer!", 3000); return; } statusBar()->showMessage("Connected to lower computer (127.0.0.1:502)", 3000); // Start timer, read once per second m_pollTimer = new QTimer(this); connect(m_pollTimer, &QTimer::timeout, this, &MainWindow::readPressure); m_pollTimer->start(1000); // 1000ms = 1s }
            

            Code notes:Code notes:

            CodeMeaning
            QModbusTcpClientBuilt-in Qt Modbus TCP client, communicates with lower computer
            NetworkPortParameter, 502Connect to port 502 (same as Modbus Slave config)
            NetworkAddressParameter, "127.0.0.1"Connect localhost (simulator runs locally)
            m_pollTimer->start(1000)Call readPressure() every second

            3.4 Read Pressure Data3.4 Read Pressure Data

            cpp
            // mainwindow.cpp - reading section void MainWindow::readPressure() { if (!m_modbusClient || m_modbusClient->state() != QModbusDevice::ConnectedState) return; // Build read request: start at address 0, read 3 holding registers QModbusDataUnit readUnit( QModbusDataUnit::HoldingRegisters, // register type 0, // start address 3 // quantity ); // Send async read request if (auto *reply = m_modbusClient->sendReadRequest(readUnit, 1)) { if (!reply->isFinished()) { connect(reply, &QModbusReply::finished, this, &MainWindow::onReadReady); } else { delete reply; // broadcast request, delete directly } } } void MainWindow::onReadReady() { auto *reply = qobject_cast<QModbusReply *>(sender()); if (!reply) return; if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); // Parse values (divide register value for real units) float pressure = unit.value(0) / 100.0f; // addr 0: pressure (MPa) float temperature = unit.value(1) / 10.0f; // addr 1: temperature (Β°C) int pumpStatus = unit.value(2); // addr 2: pump state // Update UI m_pressureLabel->setText( QString("%1 MPa").arg(pressure, 0, 'f', 2)); // Check alarm if (pressure > m_alarmThreshold) { triggerAlarm(pressure); } // Update trend chart (implemented next chapter) // updateChart(pressure); } else { statusBar()->showMessage( QString("Read failed: %1").arg(reply->errorString()), 2000); } reply->deleteLater(); }
            

            Modbus reading flow:Modbus reading flow:

            text
            readPressure() triggered by timer -> Build QModbusDataUnit ("read addresses 0-2") -> sendReadRequest() async send (UI not blocked) -> lower computer returns data -> onReadReady() triggered -> parse register values and update UI
            

            πŸ–ΌοΈ placeholder: Running app screenshot showing real-time pressure updates and status bar "connected to lower computer"placeholder: Running app screenshot showing real-time pressure updates and status bar "connected to lower computer"

            4.1 Initialize Chart4.1 Initialize Chart

            Qt Charts provides professional chart components. Ask AI to initialize in constructor:Qt Charts provides professional chart components. Ask AI to initialize in constructor:

            text
            Please help me initialize Qt Charts real-time line chart in MainWindow constructor: 1. Create QChart and QLineSeries 2. X axis uses QDateTimeAxis, showing latest 60 seconds 3. Y axis uses QValueAxis, range 0-3.0 MPa 4. Line color blue, width 2px 5. Place chart into QChartView and add to layout
            

            Core code:Core code:

            cpp
            // mainwindow.cpp - chart initialization void MainWindow::setupChart() { m_series = new QLineSeries(); m_series->setName("Pressure (MPa)"); m_series->setPen(QPen(QColor("#2196F3"), 2)); m_chart = new QChart(); m_chart->addSeries(m_series); m_chart->setTitle("Real-time Pressure Trend"); m_chart->setAnimationOptions(QChart::NoAnimation); // no animation for real-time data // X axis: time m_axisX = new QDateTimeAxis(); m_axisX->setFormat("HH:mm:ss"); m_axisX->setTitleText("Time"); m_chart->addAxis(m_axisX, Qt::AlignBottom); m_series->attachAxis(m_axisX); // Y axis: pressure m_axisY = new QValueAxis(); m_axisY->setRange(0, 3.0); m_axisY->setTitleText("Pressure (MPa)"); m_axisY->setLabelFormat("%.1f"); m_chart->addAxis(m_axisY, Qt::AlignLeft); m_series->attachAxis(m_axisY); // Create chart view QChartView *chartView = new QChartView(m_chart); chartView->setRenderHint(QPainter::Antialiasing); // Add to layout (assuming existing centralLayout) centralLayout->addWidget(chartView); }
            

            4.2 Update Chart in Real Time4.2 Update Chart in Real Time

            Whenever a new pressure value is read, append one point and keep only latest 60 seconds:Whenever a new pressure value is read, append one point and keep only latest 60 seconds:

            cpp
            // mainwindow.cpp - chart updates void MainWindow::updateChart(float pressure) { QDateTime now = QDateTime::currentDateTime(); // Append new point m_series->append(now.toMSecsSinceEpoch(), pressure); // Keep only latest 60s data QDateTime cutoff = now.addSecs(-60); while (m_series->count() > 0 && m_series->at(0).x() < cutoff.toMSecsSinceEpoch()) { m_series->remove(0); } // Update X axis range: always show latest 60s m_axisX->setRange(cutoff, now); }
            

            Then call it in onReadReady():Then call it in onReadReady():

            cpp
            // Add after pressure parsing in onReadReady(): updateChart(pressure);
            

            Now run the program. You will see a blue line updating in real time, one point per second, always showing latest 60 seconds. If you modify register values in Modbus Slave manually, the line reflects changes immediately.Now run the program. You will see a blue line updating in real time, one point per second, always showing latest 60 seconds. If you modify register values in Modbus Slave manually, the line reflects changes immediately.

            πŸ–ΌοΈ placeholder: Real-time pressure trend screenshot showing scrolling blue line, time X-axis, pressure Y-axisplaceholder: Real-time pressure trend screenshot showing scrolling blue line, time X-axis, pressure Y-axis

            > Performance tip: QChart::NoAnimation is important. Real-time data refresh every second; animations can cause UI lag. This is a common industrial HMI practice.> Performance tip: QChart::NoAnimation is important. Real-time data refresh every second; animations can cause UI lag. This is a common industrial HMI practice.

            5.1 Over-threshold Alarm5.1 Over-threshold Alarm

            When pressure exceeds threshold, we need: red UI warning + popup alert + log record.When pressure exceeds threshold, we need: red UI warning + popup alert + log record.

            cpp
            // mainwindow.cpp - alarm logic void MainWindow::triggerAlarm(float pressure) { // Turn UI red m_pressureLabel->setStyleSheet( "color: white; background-color: #F44336;" "font-size: 32px; padding: 10px; border-radius: 8px;"); // Status indicator red m_statusLight->setStyleSheet( "background-color: #F44336; border-radius: 12px;" "min-width: 24px; min-height: 24px;"); // Popup alarm (only first time crossing threshold to avoid repeated popups) static bool alarmActive = false; if (!alarmActive) { alarmActive = true; QMessageBox::warning(this, "Pressure Alarm", QString("Current pressure %1 MPa exceeds threshold %2 MPa!\nPlease check pump status immediately.") .arg(pressure, 0, 'f', 2) .arg(m_alarmThreshold, 0, 'f', 2)); } // Record to DB logAlarm(pressure, QString("Pressure over threshold: %1 MPa > %2 MPa") .arg(pressure, 0, 'f', 2) .arg(m_alarmThreshold, 0, 'f', 2)); // Reset when pressure returns to normal if (pressure <= m_alarmThreshold) { alarmActive = false; m_pressureLabel->setStyleSheet( "color: #2196F3; font-size: 32px; padding: 10px;"); m_statusLight->setStyleSheet( "background-color: #4CAF50; border-radius: 12px;" "min-width: 24px; min-height: 24px;"); } }
            

            πŸ–ΌοΈ placeholder: Over-threshold alarm screenshot showing red pressure background, red indicator, and alarm popupplaceholder: Over-threshold alarm screenshot showing red pressure background, red indicator, and alarm popup

            5.2 SQLite Fault Logs5.2 SQLite Fault Logs

            Industrial systems must log all alarm events for traceability. We use SQLite:Industrial systems must log all alarm events for traceability. We use SQLite:

            cpp
            // mainwindow.cpp - database initialization void MainWindow::setupDatabase() { m_db = QSqlDatabase::addDatabase("QSQLITE"); m_db.setDatabaseName("pump_alarm_log.db"); if (!m_db.open()) { qWarning() << "Cannot open database:" << m_db.lastError().text(); return; } // Create alarm table QSqlQuery query; query.exec( "CREATE TABLE IF NOT EXISTS alarm_log (" " id INTEGER PRIMARY KEY AUTOINCREMENT," " timestamp DATETIME DEFAULT CURRENT_TIMESTAMP," " pressure REAL," " message TEXT" ")" ); }
            

            5.3 Log and Display Records5.3 Log and Display Records

            cpp
            // mainwindow.cpp - write logs void MainWindow::logAlarm(float pressure, const QString &message) { // Write to DB QSqlQuery query; query.prepare( "INSERT INTO alarm_log (pressure, message) VALUES (?, ?)"); query.addBindValue(pressure); query.addBindValue(message); query.exec(); // Update on-screen table int row = m_logTable->rowCount(); m_logTable->insertRow(row); m_logTable->setItem(row, 0, new QTableWidgetItem( QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss"))); m_logTable->setItem(row, 1, new QTableWidgetItem(QString::number(pressure, 'f', 2))); m_logTable->setItem(row, 2, new QTableWidgetItem(message)); // Auto-scroll to latest row m_logTable->scrollToBottom(); }
            

            Log table has three columns: time, pressure value, and alarm message. Each alarm appends one row and is persisted to SQLite.Log table has three columns: time, pressure value, and alarm message. Each alarm appends one row and is persisted to SQLite.

            πŸ–ΌοΈ placeholder: Fault log table screenshot with multiple records including timestamp, pressure, and alarm messageplaceholder: Fault log table screenshot with multiple records including timestamp, pressure, and alarm message

            5.4 Manually Start/Stop Pump5.4 Manually Start/Stop Pump

            Besides reading data, upper computer should control lower computer too. We do this by writing register values:Besides reading data, upper computer should control lower computer too. We do this by writing register values:

            cpp
            // mainwindow.cpp - pump control void MainWindow::togglePump() { if (!m_modbusClient || m_modbusClient->state() != QModbusDevice::ConnectedState) return; m_pumpRunning = !m_pumpRunning; // Build write request: write 1 (start) or 0 (stop) to address 2 QModbusDataUnit writeUnit( QModbusDataUnit::HoldingRegisters, 2, 1); writeUnit.setValue(0, m_pumpRunning ? 1 : 0); if (auto *reply = m_modbusClient->sendWriteRequest(writeUnit, 1)) { connect(reply, &QModbusReply::finished, this, [this, reply]() { if (reply->error() == QModbusDevice::NoError) { m_pumpButton->setText(m_pumpRunning ? "Stop Pump" : "Start Pump"); m_pumpButton->setStyleSheet(m_pumpRunning ? "background-color: #F44336; color: white; padding: 12px;" : "background-color: #4CAF50; color: white; padding: 12px;"); statusBar()->showMessage( m_pumpRunning ? "Pump started" : "Pump stopped", 2000); } reply->deleteLater(); }); } }
            

            In Modbus Slave, you will see address 2 switching between 0 and 1 as you click the button. This is the upper-computer "control" process.In Modbus Slave, you will see address 2 switching between 0 and 1 as you click the button. This is the upper-computer "control" process.

            πŸ–ΌοΈ placeholder: Pump start/stop button screenshot showing green "Start Pump" and red "Stop Pump" statesplaceholder: Pump start/stop button screenshot showing green "Start Pump" and red "Stop Pump" states

            6.1 Package with windeployqt / macdeployqt6.1 Package with windeployqt / macdeployqt

            Qt provides official deployment tools to collect required dynamic libraries automatically.Qt provides official deployment tools to collect required dynamic libraries automatically.

            Windows:Windows:

            bash
            # Build Release first, then run in build directory: windeployqt PumpHMI.exe
            

            windeployqt copies Qt DLLs, plugins, translation files, etc. next to the executable. That packaged folder can be sent directly.windeployqt copies Qt DLLs, plugins, translation files, etc. next to the executable. That packaged folder can be sent directly.

            macOS:macOS:

            bash
            macdeployqt PumpHMI.app -dmg
            

            This generates a .dmg installer image.This generates a .dmg installer image.

            6.2 Build Installer with Qt Installer Framework6.2 Build Installer with Qt Installer Framework

            If you want a professional setup wizard ("Next -> Next -> Finish"), use Qt Installer Framework:If you want a professional setup wizard ("Next -> Next -> Finish"), use Qt Installer Framework:

            text
            Please help me create an installer for PumpHMI with Qt Installer Framework: 1. Create installer directory structure (config, packages) 2. Configure config.xml (installer name, version, target directory) 3. Put windeployqt output files into packages/com.example.pumphmi/data/ 4. Run binarycreator to generate installer
            

            πŸ–ΌοΈ placeholder: PumpHMI setup wizard screenshot showing install path and progressplaceholder: PumpHMI setup wizard screenshot showing install path and progress

            Congratulations! You have built an industrial-grade pump monitoring HMI system from scratch. Recap:Congratulations! You have built an industrial-grade pump monitoring HMI system from scratch. Recap:

            1. Understood core concepts of upper computer, lower computer, and Modbus protocolUnderstood core concepts of upper computer, lower computer, and Modbus protocol
            2. Simulated a "virtual pump" with Modbus Slave, with no real hardwareSimulated a "virtual pump" with Modbus Slave, with no real hardware
            3. Built upper-lower communication using Qt QModbusTcpClientBuilt upper-lower communication using Qt QModbusTcpClient
            4. Drew real-time rolling pressure trend chart with Qt ChartsDrew real-time rolling pressure trend chart with Qt Charts
            5. Implemented over-threshold popup alarms and SQLite fault logsImplemented over-threshold popup alarms and SQLite fault logs
            6. Implemented remote start/stop pump controlImplemented remote start/stop pump control
            7. The whole process used no real industrial hardware, but the architecture and functions match real factory HMI systems. If you replace Modbus Slave with a real PLC, this app can be used in production scenarios directly.The whole process used no real industrial hardware, but the architecture and functions match real factory HMI systems. If you replace Modbus Slave with a real PLC, this app can be used in production scenarios directly.

              Advanced directions:Advanced directions:

              • Multi-device monitoring: connect multiple lower computers and use tabs/split views for different device dataMulti-device monitoring: connect multiple lower computers and use tabs/split views for different device data
              • Historical playback: read historical data from SQLite and replay trend charts with timeline controlsHistorical playback: read historical data from SQLite and replay trend charts with timeline controls
              • OPC UA protocol: Modbus fits simpler scenarios; complex industrial systems often use OPC UA, also supported by Qt (Qt OPC UA module)OPC UA protocol: Modbus fits simpler scenarios; complex industrial systems often use OPC UA, also supported by Qt (Qt OPC UA module)
              • Web remote monitoring: use Qt WebSocket to push real-time data to browser for mobile viewingWeb remote monitoring: use Qt WebSocket to push real-time data to browser for mobile viewing
              • AI predictive maintenance: feed historical pressure data to ML models to predict failures in advanceAI predictive maintenance: feed historical pressure data to ML models to predict failures in advance

              Use code to protect every device in industrial operations.Use code to protect every device in industrial operations.

              • [Qt Serial Bus Docs](https://doc.qt.io/qt-6/qtserialbus-index.html)[Qt Serial Bus Docs](https://doc.qt.io/qt-6/qtserialbus-index.html)
              • [Qt Modbus TCP Client Example](https://doc.qt.io/qt-6/qtserialbus-modbus-client-example.html)[Qt Modbus TCP Client Example](https://doc.qt.io/qt-6/qtserialbus-modbus-client-example.html)
              • [Qt Charts Docs](https://doc.qt.io/qt-6/qtcharts-index.html)[Qt Charts Docs](https://doc.qt.io/qt-6/qtcharts-index.html)
              • [Modbus Protocol Specs](https://modbus.org/specs.php)[Modbus Protocol Specs](https://modbus.org/specs.php)
              • [Modbus Slave Simulator](https://www.modbustools.com/modbus_slave.html)[Modbus Slave Simulator](https://www.modbustools.com/modbus_slave.html)
              • [Qt Installer Framework Docs](https://doc.qt.io/qtinstallerframework/)[Qt Installer Framework Docs](https://doc.qt.io/qtinstallerframework/)