Skip to content

Create a simple Python remap

Use a Python remap when the code needs interpreter state or logic that would be awkward in an NGC procedure. This example defines M401, requires a P word, and accepts an optional Q word. It reports the supplied values and finishes immediately.

The example machine directory is /data/ctrl/configs/my-machine.

Create toplevel.py beside the machine’s INI file:

import remap

Then add the module path, top-level file, and remap definition to the INI file:

[RS274NGC]
REMAP = M401 modalgroup=10 argspec=Pq python=m401
[PYTHON]
TOPLEVEL = toplevel.py
PATH_PREPEND = /data/ctrl/configs/my-machine

Importing remap makes the functions in remap.py available to the interpreter.

Create remap.py beside toplevel.py:

from emccanon import MESSAGE
from interpreter import INTERP_OK
def m401(self, **words):
if not self.task:
return INTERP_OK
message = "M401 received P=%.6f" % words["p"]
if "q" in words:
message += " Q=%.6f" % words["q"]
MESSAGE(message)
return INTERP_OK

argspec=Pq validates the block before calling the function. The required P and any supplied Q arrive in words under lowercase keys.

LinuxCNC calls the function while generating the program preview as well as while actually running the program. During preview, self.task is false. Put this check at the start of a remap that produces messages or causes other side effects so preview can inspect the program without performing the action.

Returning INTERP_OK completes the remapped code and lets the interpreter continue.

Restart LinuxCNC, then try these commands in MDI:

Command Result
M401 Interpreter error because the required P word is missing.
M401 P25 Operator message: M401 received P=25.000000.
M401 P25 Q2 Operator message: M401 received P=25.000000 Q=2.000000.

Ctrl showing the missing-P error and the two successful M401 operator messages

LinuxCNC normally reads ahead: the interpreter can prepare later blocks while earlier motion and machine commands are still queued. Most remaps should leave that behavior alone.

Some remaps need the result of queued work before Python can make its next decision. Probing is the familiar example: the probe move must finish before its measured position is meaningful. Tool changes and input waits have the same requirement. These operations are called queue busters because they stop read-ahead at that point.

A Python remap can wait at the same boundary by yielding INTERP_EXECUTE_FINISH:

from emccanon import GET_EXTERNAL_DIGITAL_INPUT, MESSAGE, WAIT
from interpreter import INTERP_EXECUTE_FINISH
def m402(self, **words):
if not self.task:
return
# Input 0, digital input, wait until high, 5 s timeout.
WAIT(0, 1, 3, 5.0)
yield INTERP_EXECUTE_FINISH
# The input value is current after LinuxCNC resumes the function.
if GET_EXTERNAL_DIGITAL_INPUT(0, 0):
MESSAGE("M402 input is high")
else:
MESSAGE("M402 input wait timed out")

The four WAIT arguments select input 0, a digital input (1), wait-until-high mode (3), and a five-second timeout. The yield does not run work in the background. It tells LinuxCNC to execute everything queued so far, synchronize the interpreter with the resulting machine state, and then resume the function after the yield.

Because a function containing yield is a generator, finish it with return or by reaching the end; do not return INTERP_OK from it. Do not call self.execute() after the yield. For a long-running machine action, use Remap a machine action: Ctrl’s executor helper already yields at each poll, so the remap does not implement queue busting itself.