Skip to content

Remap a machine action

This page extends a regular Python remap with Ctrl’s executor helpers. Use this pattern when a remap must ask machine logic to begin an operation and wait for feedback without blocking LinuxCNC.

Typical examples include workholding, tailstocks, steady rests, parts catchers, doors, and pallet mechanisms. Follow the automatic workholding or tailstock quill guide when integrating either supplied program-level contract.

Consider a remapped M400 that requests a machine operation:

  1. The interpreter reads M400 and calls its Python remap function.
  2. The function starts one executor request and yields while it is pending.
  3. The endpoint carries that request to the machine action controller.
  4. The controller sequences outputs, checks feedback, and reports success or failure.
  5. The remap finishes on success. Failure or timeout stops the part program with an interpreter error.

The remap defines the program-level command. The executor manages the transaction and wait. The machine controller still owns the physical mechanism.

Part Owns
Python remap Code arguments, interpreter preconditions, requested operation, and program-level errors
Executor One request, its timeout, and cleanup after completion or cancellation
Machine action controller Interlocks, output sequencing, sensor checks, and lasting physical state
Safety system Emergency stopping and other safety-rated behavior

Check code availability before assigning a machine-specific command. Define stable semantic operations rather than exposing a valve polarity, raw digital output, or implementation detail to the part program.

For example, a mechanism might define opcode 0 as release and opcode 1 as actuate. Its controller translates those requests into the outputs and feedback conditions used by that machine.

The example machine directory is /data/ctrl/configs/my-machine. Put remap.py and toplevel.py beside its INI file.

Add the mapping and Python search paths to the INI file:

[RS274NGC]
REMAP = M400 modalgroup=10 python=m400_machine_action
[PYTHON]
TOPLEVEL = toplevel.py
PATH_PREPEND = /data/ctrl/configs/my-machine
PATH_APPEND = /usr/share/linuxcnc/ncfiles/remap_lib/python-stdglue

Initialize the executor endpoint in toplevel.py:

import hal
import remap
from stdglue import HalExecutor, init_stdglue
def __init__(self):
init_stdglue(self)
component = hal.component("remap") if self.task else None
self.machine_action = HalExecutor(self, "action", component)
if component is not None:
# Add every endpoint and custom pin before making the component ready.
component.ready()
self.remap_hal = component

LinuxCNC loads the same Python modules for its task and preview interpreters. self.task is true only for the task interpreter, so this creates one HAL component and prevents preview from operating hardware.

HalExecutor adds pins to the supplied component. The caller creates the component, adds all required endpoints and related pins, and calls ready() once.

Define the remap function in remap.py:

from stdglue import executor_execute
def m400_machine_action(self, **words):
yield from executor_execute(self, self.machine_action, request=1)

yield from lets LinuxCNC resume the function repeatedly while the request is pending. The interpreter does not advance beyond the remapped M-code until the generator finishes.

Continue with the executor protocol to wire the pins and define success, failure, timeout, and cancellation behavior.

The remap may validate its G-code words, reject an invalid interpreter state, choose a justified timeout, or translate a terminal diagnostic into a useful program error.

Keep output sequencing, pressure qualification, sensor agreement, servo control, and lasting physical state in the machine action controller. Physical buttons should feed that controller through explicit arbitration and interlocks. They do not need to manufacture an M-code; the remap exists for part programs that need to request and wait for the same action.