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

RequirementVersion
Qt6.x (Core, Gui, Widgets, Qml, Quick, QuickControls2)
CMake3.20+
C++ compilerC++23-compatible (GCC 12+, Clang 15+)
Linux kernelSocketCAN 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

ParameterValue
Bitrate500 kbps
Interfacecan0 (default)
Termination120 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
RoleNode ID
HMI (master)0x01
ESP32 (slave)0x02

Frame types:

TypePCI BytePurpose
Single Frame0x0nComplete message in one frame (up to 7 data bytes)
First Frame0x1nInitiates multi-frame transfer with 12-bit length field
Consecutive Frame0x2nSubsequent segments with 4-bit sequence number (wraps at 15)
Flow Control0x30Receiver acknowledgment specifying block size and separation time

Timeouts

ParameterDefaultDescription
N_Bs / FC_TIMEOUT_MS1000 msMax wait for Flow Control after First Frame
N_Cs / CF_TIMEOUT_MS1000 msMax time between Consecutive Frames
N_As70 msTransmission 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

SuiteScope
PriorityQueueTestBinary heap ordering, FIFO within priority levels
SessionManagerTestTX/RX session lifecycle, timeout handling
CallbackRegistryTestThread-safe callback registration and dispatch
ProtocolManagerTestEnd-to-end ISO-TP message send/receive
IntegrationTestFull stack with mock platform