Voltrus HMI Documentation

Installation

Voltrus HMI is a desktop HMI/SCADA builder for industrial teams that want a native macOS authoring experience without licensing per-tag or per-seat. Design dashboards locally, then publish to a runtime instance running on the same Mac, a Linux box, or a VPS. One-time purchase, no subscription.

Download

  • Product page: /voltrus-hmi/ — current download link and purchase options

System Requirements

RequirementMinimum
macOS (authoring)12 Monterey or newer (Apple Silicon & Intel)
RAM8 GB for small projects; 16 GB recommended for trend logging or 50+ screens
Runtime OSmacOS, Linux (Ubuntu 22.04+, Debian 12+), or any VPS with Node.js 20+ for the published runtime
NetworkTCP reachability to your PLCs (port 502 Modbus, 4840 OPC-UA, 102 S7, 44818 EtherNet/IP)
OptionalUSB-to-RS485 adapter for serial Modbus RTU polling from the runtime host

First Launch

If you downloaded the direct build outside the App Store, macOS Gatekeeper may block the unsigned binary on first run. Right-click the app, choose Open, then confirm. This is only required once.

Author vs runtime are the same binary. Run voltrus-hmi with no args for the authoring UI; voltrus-hmi serve ./project.vhmi --port 8080 for the headless runtime on a Linux box or VPS.

Quick Start

1. Connect to a Modbus TCP PLC (5 minutes)

  1. Open the app and create a new project (File → New Project). Save it as line-a.vhmi.
  2. In the Devices panel, click + and choose Modbus TCP.
  3. Enter the PLC IP (e.g. 192.168.1.50), port 502, Unit ID 1.
  4. Click Test Connection. The status pill turns green when the first read succeeds.
  5. Expand the device to browse its register map. Drag a holding register onto the canvas — it becomes a live value widget bound to that address.

2. Build a screen with widgets

  1. Drag widgets from the left palette: Value, Button, Bar Gauge, Tank, Trend Chart, LED, Image Switch.
  2. Drop a Bar Gauge on the canvas. In the right-hand Properties panel, bind its value property to a register by clicking Bind… and picking the register from the device tree.
  3. Drop a Button, bind its On Press action to a Modbus write (FC05 / FC06) on a coil or holding register.
  4. Save the project. The preview pane on the right shows live data immediately.

3. Add a trend chart with logged history

  1. Drag a Trend Chart to the canvas.
  2. In Properties, enable Log history and pick the sampling interval (1 s default; 100 ms for fast loops).
  3. Add 1–4 series, each bound to a register. Historical data persists to a local SQLite log under ./logs/<project>.db.
  4. The chart auto-pans to "live tail" mode; scroll to scrub history, double-click to reset.

4. Publish to operators

  1. Project → Publish… packages the project with the runtime into a self-contained bundle.
  2. For local ops, publish to the same Mac as a runtime on port 8080. Operators open http://your-mac.local:8080 in any modern browser.
  3. For a Linux/VPS runtime, scp the bundle to the host and run voltrus-hmi serve line-a.vhmi --port 8080 behind nginx/Caddy for TLS.
  4. Set up the runtime as a systemd unit or launchd plist so it restarts on reboot.

Configuration

Supported PLC protocols

DriverTransportCommon Devices
Modbus TCPTCP 502Schneider M221/M241, Phoenix Contact, VFDs, power meters
Modbus RTUSerial RS-485Energy analyzers, motor protections, legacy PLCs
OPC-UATCP 4840Siemens S7-1500 (OPC-UA server), Beckhoff, Ignition gateway
Siemens S7ISO-on-TCP 102S7-1200, S7-1500, S7-300/400 (with Put/Get enabled)
EtherNet/IP CIPTCP 44818Allen-Bradley ControlLogix / CompactLogix / Micro800
MQTTTCP 1883 / TLS 8883IIoT gateways, Sparkplug B devices, telemetry brokers

Connection settings

SettingPurpose / Typical Value
Poll intervalHow often registers are read — 1000 ms default; 100 ms for fast loops
TimeoutPer-request timeout before retry — 3000 ms typical
Retry countRetries before flagging the device offline — 3 default
Offline thresholdConsecutive failures before the device shows disconnected in the UI — 5 default
Byte order (32-bit / float)Big-endian (ABCD), Little-endian, Swapped variants — must match the PLC
Scalingy = mx + b per register; specify m (scale), b (offset), and unit label

Widgets and bindings

WidgetBindable Properties
Valuevalue (number/string), visible (bool), alarmColor
LED / Indicatorstate (bool), blinkOnAlarm (bool)
Bar Gauge / Tankvalue, min, max, lowAlarm, highAlarm
ButtonOn Press / On Release actions (write register, run script)
Trend Chart1–4 series, each bound to a logged register with pan/zoom
Image Switchstate (bool); two-state image swap (e.g. valve open/closed)
Custom HTMLJavaScript snippet with access to the live tag namespace — for video walls or third-party widgets

Runtime deployment modes

ModeUse Case
Same-Mac authoring + runtimeSingle-seat HMI; operators open http://your-mac:8080
Headless Linux runtimeIndustrial PC or VPS; voltrus-hmi serve project.vhmi behind nginx/Caddy
ContainerizedOfficial Docker image — docker run -p 8080:8080 -v ./project.vhmi:/app/project.vhmi voltrus/hmi
Read-only HMI displayStatic export of the screens for a kiosk browser; no writes back to PLCs

Key Features

Drag-and-drop screen builder

Industrial widgets designed for ops — gauges, tanks, valves, motors, trends, alarms — drop onto a canvas, snap to a grid, and bind to live tags with one click. No SVG wrangling, no CSS, no per-screen JavaScript unless you opt into the custom HTML widget.

Native multi-protocol PLC connectivity

Modbus TCP/RTU, OPC-UA, Siemens S7 (1200/1500/300/400), EtherNet/IP CIP, and MQTT (including Sparkplug B) are first-class drivers — not ODBC bridges, not OPC gateways. A single screen can mix tags from a Schneider PLC over Modbus, a Siemens PLC over S7, and a telemetry broker over MQTT in the same trend chart.

Trend logging with local SQLite history

Every bound tag is optionally logged to a per-project SQLite database. Default 1 s sample, configurable down to 100 ms. The trend chart reads the same database for historical scrubbing — no separate historian to deploy. For long-term retention, configure a daily export to CSV or Parquet.

Alarm management

Per-tag alarm thresholds (low-low, low, high, high-high) with configurable latching, acknowledgement, and severity colors. The alarm summary widget shows unacknowledged alarms in priority order with timestamps; operators acknowledge from the dashboard or via the API. Alarm events are written to the same SQLite log for audit.

Headless runtime deployment

Publish a project once and serve it anywhere — a Mac, a Linux industrial PC, a VPS, or a Docker container. The runtime is a single process that serves the screens over HTTP/WebSocket; operators need only a browser. No client install, no plugin, no .NET framework on the operator PC.

Role-based access for write operations

Tag writes (buttons, setpoints) can require a logged-in operator. Configure local users with roles (Operator, Engineer, Admin) or front the runtime with an OIDC provider (Authentik, Keycloak, Cloudflare Access) for SSO. Read-only access remains open by default for ops dashboards on the shop floor.

Troubleshooting

Modbus device shows "Offline" immediately

Symptom: New Modbus TCP device fails Test Connection with "Connection refused" or timeout.
Fix: Verify network reachability from the authoring Mac first:

nc -zv 192.168.1.50 502

If nc fails, the device is offline, on another subnet, or behind a firewall. If nc succeeds but the device still reads offline, the Unit ID is wrong — power meters commonly use 1, some Schneider devices use 255, and gateways may use the downstream slave's address.

Siemens S7 connection: "Cannot connect to PLC"

Symptom: S7 driver returns "TCP connection to 192.168.1.10:102 refused or no response".
Fix: Three likely causes. (1) Put/Get not enabled on the S7-1200/1500 — in TIA Portal under Protection & Security, enable "Permit access with PUT/GET communication". (2) Rack/slot wrong — most S7-1500 CPUs are rack 0 slot 1; S7-1200 is rack 0 slot 1. (3) The optimization level on the data block — symbolically-accessed-only blocks aren't readable; mark the DB as "optimized" with the right access or use absolute addressing.

OPC-UA: certificate rejected

Symptom: OPC-UA connect fails with BadCertificateInvalid or BadSecurityChecksFailed.
Fix: The runtime's self-signed certificate must be trusted by the OPC-UA server. Export the cert from ~/.voltrus-hmi/opcua-cert.pem, upload it to the server's trust list (Siemens TIA under OPC UA → Server → Trusted Clients), then reconnect. For development only, you can disable certificate validation by setting OPCUA_INSECURE=1 in the runtime env — never in production.

Trend chart shows no historical data

Symptom: Live values update, but the chart's history is empty.
Fix: Log history wasn't enabled on the tag when it was added — enabling it doesn't backfill data, only logs from now on. Confirm the property is on, then wait one poll interval. If still empty, check the SQLite log file exists and is writable: ls -la ./logs/<project>.db. A permissions mismatch on the runtime host is the usual cause.

Published runtime shows stale values

Symptom: Browser shows values that don't update; polling never starts.
Fix: WebSocket upgrade failed — the reverse proxy (nginx/Caddy) in front of the runtime isn't forwarding Upgrade: websocket. Add the upgrade headers:

# nginx
location / {
  proxy_pass http://127.0.0.1:8080;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
}

Without WebSocket, the dashboard falls back to long-polling at 1 Hz — values move but look "stuttery".

32-bit values render as garbage

Symptom: A 230.0 V reading shows as 0.003 or 1.5e7.
Fix: Byte order mismatch. Modbus 32-bit floats span two registers and every vendor picks a different order. In the register binding, cycle through Big-endian (ABCD), Little-endian (DCBA), Swapped Big (BADC), Swapped Little (CDAB) — exactly one will produce a sane value. Apply the same setting to all 32-bit tags from the same device.

Support

When reporting an issue, attach the project file (.vhmi, with sensitive IPs redacted), the runtime logs from ./logs/, and note the PLC vendor, protocol, and firmware version involved.