On a Fanuc or any closed control, the M-codes above the standard core belong to the machine builder, and you live with whatever they chose. LinuxCNC flips that: the range M100 through M199 is reserved for you. Any executable file with one of those names becomes a real M-code, callable from any program, and the M-code reference documents the mechanism in a paragraph because a paragraph is genuinely all it takes.
How the mechanism works
When the interpreter reaches a block like M110 P5 Q2.5, it looks for an executable file named M110 in the directories your configuration points at for user commands. If it finds one, it runs it, passing the P and Q word values as the two command-line arguments, and the program pauses until the script exits. Exit success, and the program continues with the next block. That is the whole contract: a file name, an executable bit, two optional numbers in, a blocking call.
Everything interesting follows from the blocking part. The machine genuinely waits, which means a custom M-code can guarantee that the chuck clamped, the air blast finished, or the log line was written before the next move starts. It also means a script that hangs, hangs your program with it.
Setup, step by step
| Step | Action | The gotcha |
|---|---|---|
| 1 | Write the script in anything executable, shell or Python | Keep it short; it blocks the program while it runs |
| 2 | Name the file exactly M100 to M199 | The name is the M number: no extension, capital M |
| 3 | Make it executable | A missing executable bit is the classic silent failure |
| 4 | Put it in the directory your config searches for user commands | Wrong directory means the control reports an unknown M-code |
| 5 | Call it as M1xx P… Q… | P and Q arrive as the first and second command-line arguments |
| 6 | Test from MDI before any program uses it | Never debut a new M-code mid-program on real metal |
The testing row deserves its own habit. A new custom M-code gets three rehearsals: alone in a terminal, where you can see everything it prints; from the MDI line in a sim or with the machine idle; and only then inside a program. The learning ladder for LinuxCNC applies here unchanged, because a custom M-code is just more G-code vocabulary, except you wrote the dictionary entry.
A worked example: the part logger
A script that stamps a log line every time a part finishes is the classic first custom M-code, and it pairs naturally with a part counter in G-code:
#!/bin/bash
# /home/cnc/linuxcnc/nc_files/M119 : log a finished part
echo "$(date '+%F %T') part complete P=$1 Q=$2" >> /home/cnc/parts.log
exit 0
Call M119 P1001 at the end of the program and every cycle appends a timestamped line with the job number you passed in P. Note the explicit exit 0: the interpreter treats the script’s exit status as the verdict, so a clean exit is part of the job, and a nonzero exit is your deliberate way to stop a program when the script detects something wrong.
Good uses share a profile: short, physical or clerical, and worth blocking for. Pulsing a relay, firing an air blast, triggering a camera, writing a log, asserting an interlock. Bad uses share the opposite profile: anything long, anything interactive, anything that should really be a background job. The G-code overview is the place to confirm how words like P and Q parse if your calling blocks get fancy.
Document your dialect
The moment M119 means something on your machine, your machine has a builder layer, and you are the builder. Treat it the way you wish every builder treated you: a one-page table next to the machine listing each custom code, its arguments, and what it touches. Anyone who has hunted undocumented M-codes on a used machine knows exactly why; it is the same manual-owned-edge problem that closed controls have, covered from the other side in posts like the structured program tricks of O-word subroutines, except here the documentation costs you ten minutes instead of an email to a vendor.
For behavior deeper than running a script, LinuxCNC also supports remapping codes to G-code routines and components, but that is a bigger tool for a bigger job. M100 to M199 covers the everyday cases first, and most machines never need more.
Standard core first, custom layer second
Custom M-codes extend a vocabulary, and extensions only help if the base vocabulary is automatic. The standard M-code core, spindle, coolant, tool change, stops and end, is small, stable, and exactly the kind of material that recall practice installs permanently: the 60-second drill rounds on the G-code practice page cycle it daily until M08 versus M09 stops costing a thought. Then the codes you invent sit on top of a foundation instead of a guess, the same way a generated program sits on top of hand-written fluency.
Sources
Frequently asked questions
How do I set up custom M-codes in LinuxCNC?
Write a script, name the file exactly M100 through M199 with no extension, make it executable, and place it in the directory your configuration searches for user commands. A block like M110 P5 Q2.5 runs the file with 5 and 2.5 as its arguments, and execution waits until the script exits.
What are the P and Q words for in LinuxCNC user M-codes?
They are the two values you can hand a custom M-code from G-code. Whatever P and Q carry in the calling block arrives in the script as its two command-line parameters.
Why does my LinuxCNC custom M-code not run?
In order of likelihood: the file name is not exactly the M number, the executable bit is missing, the file is in a directory the configuration does not search, or the script exits with an error. Test alone in a terminal, then from MDI, before it appears in a program.
What is the best way to memorize the standard M-codes before customizing them?
Recall practice. The standard core is small enough to drill into automatic memory in a couple of weeks, and the free G-Code Sprint app runs exactly that drill in 60-second rounds.