MTConnect agent for LinuxCNC — demo config
First-class MTConnect support for LinuxCNC: a userspace, non-realtime agent that
exposes machine status, a rich kinematic description, and tool data
over MTConnect. It ships an embedded HTTP agent (no external dependency) and can
optionally publish over the standard MTConnect MQTT binding. The /probe
response carries enough kinematic detail for an external tool (e.g. a FreeCAD
Path plugin) to auto-configure a machine.
This directory is a demo sim config. The agent itself installs with LinuxCNC
(bin/mtconnect-agent, package lib/python/mtc, assets under
share/linuxcnc/mtconnect). For the full reference see the mtconnect-agent(1)
man page and the [MTCONNECT] section of the INI configuration chapter.
Quick start
# From this directory:
linuxcnc example.ini # launches the sim + the MTConnect agent
# In another terminal:
curl http://localhost:5000/probe # MTConnectDevices (structure + kinematics)
curl http://localhost:5000/current # latest value of every DataItem
curl http://localhost:5000/sample?from=1&count=100
curl http://localhost:5000/assets # CuttingTool assets (tool table)
You can inspect the generated device model without launching LinuxCNC (after
sourcing scripts/rip-environment in a RIP build, or with LinuxCNC installed):
mtconnect-agent --dump-probe example.ini
mtconnect-agent --dump-probe ../vismach/5axis/table-rotary-tilting/xyzac-trt.ini
Enabling it in your own config
Add a few lines to your INI. The device model is generated automatically from
[TRAJ], [KINS], [AXIS_*] and [JOINT_*].
[MTCONNECT]
ENABLE = 1
DEVICE_NAME = my_mill
UUID = linuxcnc-my-mill-0001
HTTP_PORT = 5000
# HTTP_BIND defaults to 127.0.0.1 (loopback); set 0.0.0.0 to expose on the LAN.
# HTTP_BIND = 127.0.0.1
# TRANSPORT: comma list of http, mqtt, shdr (no inline comments in INI values)
TRANSPORT = http
SAMPLE_HZ = 10
# MQTT_BROKER = localhost
# MQTT_PORT = 1883
# MQTT_PREFIX = MTConnect
Load the agent from a HAL file (see mtconnect.hal):
loadusr -W mtconnect-agent
-W waits until the component is ready; the INI comes from $INI_FILE_NAME.
Loading it in the HAL file (rather than [APPLICATIONS]) makes the status pins
(active, connected) available for linking.
What is exposed
| Endpoint | Content |
|---|---|
/probe |
Device structure: Controller/Path, Axes (Linear/Rotary + Motion), spindle, and the x:Kinematics extension |
/current |
Latest value of every DataItem (execution, mode, positions, spindle, feed, tool) |
/sample |
Sequence-numbered observation history (?from=&count=) |
/assets |
Tool table as CuttingTool assets (location, diameter, length offsets) |
LinuxCNC → MTConnect mapping (linuxcnc.stat())
| MTConnect DataItem | Source |
|---|---|
EMERGENCY_STOP |
task_state |
CONTROLLER_MODE |
task_mode |
EXECUTION |
interp_state, task_paused |
PROGRAM, LINE_NUMBER |
file, motion_line (executing line, not interp read-ahead) |
PATH_FEEDRATE (+ OVERRIDE) |
current_vel, feedrate |
POSITION / ANGLE (ACTUAL/COMMANDED) |
actual_position, position |
ROTARY_VELOCITY, ROTARY_MODE, DIRECTION |
spindle[0] |
TOOL_NUMBER, TOOL_ASSET_ID |
tool_in_spindle |
ASSET_CHANGED |
tool change detection |
CuttingTool assets |
tool_table |
The kinematic description (x:Kinematics)
Standard MTConnect models axes as Linear/Rotary components with Motion
elements (PRISMATIC/REVOLUTE, direction vector). LinuxCNC specifics that do
not map cleanly are carried in a versioned extension namespace
urn:linuxcnc:mtconnect:1 — the primary contract for auto-configuration:
<x:Kinematics module="xyzac-trt-kins" coordinates="XYZAC" joints="5"
params="sparm=identityfirst">
<x:JointMap>
<x:Joint number="0" kind="LINEAR" axis="X" min="-200" max="200" home="0"/>
<x:Joint number="3" kind="ANGULAR" axis="A" min="-100" max="50" home="0"/>
<x:Joint number="4" kind="ANGULAR" axis="C" min="-36000" max="36000" home="0"/>
</x:JointMap>
<x:Axis name="X" kind="LINEAR" vector="1 0 0" min="-200" max="200"/>
<x:Axis name="A" kind="ANGULAR" vector="1 0 0" min="-100" max="50"/>
<x:Axis name="C" kind="ANGULAR" vector="0 0 1" min="-36000" max="36000"/>
</x:Kinematics>
A FreeCAD plugin reads the standard Axes/Motion tree, or the compact
x:Kinematics block, to create/configure a machine: axis list and type,
travel limits, home positions, kinematics module/type, and the joint↔axis map.
Solid models (geometry)
A standard MTConnect twin viewer (e.g. the one at demo.mtconnect.org, or
TrakHound) renders a machine from /probe alone: geometry is referenced, never
streamed. The device model carries <SolidModel href="..."> elements pointing
to external mesh files, plus <CoordinateSystems> and a <Motion> chain; the
viewer fetches each mesh once (from the agent's /models/<name>) and animates it
using the streamed positions. LinuxCNC bundles no viewer and no JavaScript — this
is plain, standards-compliant MTConnect.
Zero-config geometry (MODEL_AUTO)
[MTCONNECT]
MODEL_AUTO = 1
The agent generates simple placeholder box meshes for the base and each axis straight from the travel limits and serves them from memory — any machine gets a functional twin with no mesh files. This is what the example config uses.
Real geometry (per-link meshes)
Supply your own meshes (STL / OBJ / glTF) for fidelity:
[MTCONNECT]
MODEL_DIR = models ; base dir for relative paths (default: INI dir)
MODEL_BASE = frame.stl ; static frame / column (device-level SolidModel)
MODEL_X = x_table.stl ; link that moves with X
MODEL_Y = y_saddle.stl
MODEL_Z = spindle.stl
MODEL_SPINDLE = spindle.stl
Author each mesh in millimetres (MTConnect's canonical unit — the SolidModel
element carries no per-mesh unit) in the machine frame at the all-axes-zero pose.
An explicit
MODEL_<axis> overrides the generated box for that link, so files and
MODEL_AUTO can be mixed.
Topology and direction (both modes)
These describe the mechanics and are not derivable from a trivkins INI:
MODEL_CHAIN = Y X Z ; nesting order of the moving links (base -> tip)
MODEL_PARENT_Z = BASE ; branch: Z (quill/tool) hangs off the base, not X/Y
MODEL_INVERT = X Y ; work-carrying links move opposite the reported coord
The agent then serves each mesh at GET /models/<name>, emits a device-level
<SolidModel> for the base and a per-axis <SolidModel> in each axis's
<Configuration>, and emits <CoordinateSystems> (WORLD → MACHINE) plus a
<Motion> chain (parentIdRef) so the twin nests transforms correctly.
Viewing the twin
The agent serves the geometry but bundles no viewer — rendering is left to a standard MTConnect client, so no third-party JavaScript lives in LinuxCNC. Two paths:
-
A standard MTConnect twin viewer (recommended). Point one at the agent and it renders the machine from
/probe+/models/with zero extra setup: the demo viewer at https://demo.mtconnect.org, TrakHound, or cppagent's demo twin. Expose the agent first with[MTCONNECT]HTTP_BIND = 0.0.0.0(it defaults to loopback only) and give the viewerhttp://<machine>:5000. -
Self-host a small viewer. An earlier revision of this config shipped a ~12 KB three.js viewer (
twin.html) that was removed from the tree to keep third-party JS out of core. It still works against this agent — it fetches/probeand/currentand loads the/models/meshes. To run it yourself, recover it from git history (git show <commit>:share/linuxcnc/mtconnect/twin.html), repoint its three.js import map at a CDN or a locallibjs-three, and serve it same-origin as the agent (e.g. behind a small reverse proxy, since the relativefetchcalls assume one origin).
Using the official cppagent instead of the embedded agent
Put shdr in TRANSPORT and the agent acts as an SHDR adapter (default port
7878), streaming <ts>|id|value lines to an external cppagent. Configure that
cppagent with a Devices.xml, which the generated /probe document doubles as:
mtconnect-agent --dump-probe example.ini > Devices.xml
The dataItemIds in the SHDR stream match the ids in that Devices.xml.
MQTT
With mqtt in TRANSPORT and python3-paho-mqtt installed, the agent publishes
the standard (vendor-neutral) MTConnect MQTT topics:
<prefix>/Probe/<uuid> (retained), <prefix>/Current/<uuid>,
<prefix>/Sample/<uuid>, <prefix>/Asset/<uuid>/<assetId>. A consumer discovers
the whole device from the retained Probe topic.
Home Assistant is not part of the core agent (its MQTT Discovery format is
HA-specific). An optional bridge, contrib/mtconnect-ha-bridge, reuses the
device model to publish HA discovery; configure it with arguments so no HA key
touches the machine INI:
loadusr -W mtconnect-ha-bridge --broker=[HA]BROKER --username=[HA]USER --password=[HA]PASSWORD
Layout (installed)
src/hal/user_comps/mtconnect-agent.py entry point -> bin/mtconnect-agent
lib/python/mtc/ the agent package:
ini_reader.py INI access (linuxcnc.ini, with offline fallback)
kinematics.py build the kinematic model from the INI
observations.py shared DataItem registry (keeps probe and streams in sync)
device_model.py /probe MTConnectDevices document (+ CLI --dump-probe)
lcnc_source.py live status + tool table from linuxcnc.stat()
models.py solid-model config + served mesh registry (SolidModel geometry)
streams.py /current, /sample buffer + XML; /assets XML
agent.py transport-agnostic agent core (document builders)
http_agent.py embedded HTTP server
mqtt_agent.py optional MQTT publisher (vendor-neutral MTConnect binding)
share/linuxcnc/mtconnect/mtconnect-linuxcnc-1.xsd extension schema
configs/sim/axis/mtconnect/ this demo config + tests
contrib/mtconnect-ha-bridge opt-in Home Assistant MQTT-discovery bridge
contrib/ha.py HA discovery helper (used by the bridge)
Testing
. scripts/rip-environment # so mtc is importable and bin/ is on PATH
python3 test_mtc.py # offline suite (no running LinuxCNC required)
python3 validate_schemas.py # validate the documents against the MTConnect XSDs
# (needs the 'xmlschema' package)
test_mtc.py covers kinematics mapping, probe↔registry consistency (3- and
5-axis), the observation buffer + streams, assets, the live HTTP endpoints, and
(when xmlschema is available) schema validation of all four documents.
Custom HAL pins
Expose any HAL pin/signal as a data item with HAL_ITEM lines (read via
hal.get_value, no HAL wiring):
[MTCONNECT]
HAL_ITEM = pin=spindle.0.load, id=spindle_load, type=LOAD, units=PERCENT, component=spindle
HAL_ITEM = pin=hm2_5i25.temp, id=board_temp, type=TEMPERATURE, units=CELSIUS
Standard MTConnect SAMPLE types only (LOAD, TEMPERATURE, PRESSURE, VOLTAGE,
AMPERAGE, FREQUENCY, ANGLE, VELOCITY, TORQUE, …). component= hosts it under a
generic Sensor (default) or an existing component (spindle/controller/path/an
axis). Unsupported types are skipped with a warning. See mtconnect-agent(1).
Status
Complete and packaged for the build: the HTTP transport, HAL component and
schema-valid documents; the vendor-neutral MQTT binding plus the optional
Home Assistant bridge contrib; SolidModel geometry under /models/ for any
standard MTConnect twin viewer; and the SHDR adapter for an external cppagent.
Realtime servo-rate data is explicitly out of scope.