Field HMI
The Field HMI (ui1) is a Qt6 QML desktop application for local device monitoring and diagnostics over CAN bus. Connect directly to field-deployed ESP32 devices for real-time sensor readout, session inspection, and bus health checks -- without cloud connectivity or network infrastructure.
Use it when:
- Commissioning new devices in the field
- Diagnosing sensor or wiring faults on-site
- Verifying CAN bus integrity before connecting to the wider network
- Monitoring live telemetry where internet access is unavailable
Prerequisites
| Requirement | Version |
|---|---|
| Qt | 6.x (Core, Gui, Widgets, Qml, Quick, QuickControls2) |
| CMake | 3.20+ |
| C++ compiler | C++23-compatible (GCC 12+, Clang 15+) |
| Linux kernel | SocketCAN support enabled |
Install Qt6 development packages for your distribution. On Arch Linux:
sudo pacman -S qt6-base qt6-declarative qt6-quickcontrols2
On Ubuntu/Debian:
sudo apt install qt6-base-dev qt6-declarative-dev libqt6quickcontrols2-dev
Building
Configure and build with CMake from the ui1/ directory:
cd ui1
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
For a debug build with AddressSanitizer and UndefinedBehaviorSanitizer enabled on the CAN transport library:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build
The build produces a RIoT_HMI executable in build/.
CAN Bus Connection
The HMI communicates with field devices using ISO-TP (ISO 15765-2) over SocketCAN. ISO-TP handles segmentation, flow control, and reassembly of messages that exceed the 8-byte CAN frame limit -- supporting payloads up to 4095 bytes.
Physical Layer
| Parameter | Value |
|---|---|
| Bitrate | 500 kbps |
| Interface | can0 (default) |
| Termination | 120 ohm resistor required at each end of the bus |
Bring up the CAN interface before launching the HMI:
sudo ip link set can0 type can bitrate 500000
sudo ip link set can0 up
Protocol Details
The transport layer uses a base CAN ID of 0x700 with node and target IDs encoded into the arbitration field:
CAN ID = 0x700 + (nodeId << 4) + targetId
| Role | Node ID |
|---|---|
| HMI (master) | 0x01 |
| ESP32 (slave) | 0x02 |
Frame types:
| Type | PCI Byte | Purpose |
|---|---|---|
| Single Frame | 0x0n | Complete message in one frame (up to 7 data bytes) |
| First Frame | 0x1n | Initiates multi-frame transfer with 12-bit length field |
| Consecutive Frame | 0x2n | Subsequent segments with 4-bit sequence number (wraps at 15) |
| Flow Control | 0x30 | Receiver acknowledgment specifying block size and separation time |
Timeouts
| Parameter | Default | Description |
|---|---|---|
N_Bs / FC_TIMEOUT_MS | 1000 ms | Max wait for Flow Control after First Frame |
N_Cs / CF_TIMEOUT_MS | 1000 ms | Max time between Consecutive Frames |
N_As | 70 ms | Transmission addressing timeout |
Timeouts are configurable per session through the ProtocolConfig struct in ui1/external/CanTransport/src/core/CanTypes.h.
Virtual CAN for Development
Test without physical hardware using the Linux vcan module:
sudo modprobe vcan
sudo ip link add dev vcan0 type vcan
sudo ip link set vcan0 up
Launch the HMI against the virtual interface and use cansend / candump from can-utils to simulate device traffic.
Real-Time Monitoring
The HMI displays a paginated grid of sensor cards. Each card shows:
- Sensor name and last-seen timestamp
- Live values with numeric precision (3 decimal places)
- Sparkline charts showing recent value history per field
The interface auto-paginates based on window size, fitting as many sensor cards as the viewport allows. Connection status is shown in the header as a live indicator -- green when the CAN bus is active, red when offline.
Sensor data arrives as JSON payloads over ISO-TP. The CANBusInterface class polls for incoming messages, deserializes the JSON, and exposes the data to the QML layer as a reactive QVariantList. The UI updates in real time as new readings arrive.
Data Flow
ESP32 sensor reading
-> ISO-TP segmentation (CanTransport)
-> SocketCAN frames on can0
-> HMI ProtocolManager reassembly
-> CANBusInterface JSON parsing
-> QML property binding
-> Sensor card render
Testing
The CAN transport library (ui1/external/CanTransport/) includes both correctness tests (Google Test) and performance benchmarks (Google Benchmark).
Build the test suite:
cd ui1/external/CanTransport
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
Run all tests and benchmarks:
./build/tests/canStressTest
Run only correctness tests:
./build/tests/canStressTest --test
Run only performance benchmarks:
./build/tests/canStressTest --benchmark
Filter to a specific test suite or benchmark:
./build/tests/canStressTest --gtest_filter="ProtocolManagerTest.*"
./build/tests/canStressTest --benchmark --benchmark_filter="SessionManager.*"
CTest integration is also available:
cd build
ctest --output-on-failure
Test Coverage
| Suite | Scope |
|---|---|
PriorityQueueTest | Binary heap ordering, FIFO within priority levels |
SessionManagerTest | TX/RX session lifecycle, timeout handling |
CallbackRegistryTest | Thread-safe callback registration and dispatch |
ProtocolManagerTest | End-to-end ISO-TP message send/receive |
IntegrationTest | Full stack with mock platform |