# 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
```sh
# 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):
```sh
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_*]`.
```ini
[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:
```xml
```
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 `` elements pointing
to external mesh files, plus `` and a `` chain; the
viewer fetches each mesh once (from the agent's `/models/`) 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`)
```ini
[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:
```ini
[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_` 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:
```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/`, emits a device-level
`` for the base and a per-axis `` in each axis's
``, and emits `` (WORLD → MACHINE) plus a
`` 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 , 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 viewer `http://: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
`/probe` and `/current` and loads the `/models/` meshes. To run it yourself,
recover it from git history
(`git show :share/linuxcnc/mtconnect/twin.html`), repoint its three.js
import map at a CDN or a local `libjs-three`, and serve it same-origin as the
agent (e.g. behind a small reverse proxy, since the relative `fetch` calls
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 `|id|value` lines to an external cppagent. Configure that
cppagent with a `Devices.xml`, which the generated `/probe` document doubles as:
```sh
mtconnect-agent --dump-probe example.ini > Devices.xml
```
The `dataItemId`s 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:
`/Probe/` (retained), `/Current/`,
`/Sample/`, `/Asset//`. 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
```sh
. 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):
```ini
[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.