Working documentation
This page is generated from the project's working notes. It records a real retrofit in progress, corrections included. Anything marked π΄ or β οΈ is unresolved or carries a caveat - read those before acting.
Control logic - what LinuxCNC has to reproduce, and how¶
3 September 2026. Opening document for the software phase. Written for someone who has not used LinuxCNC.
π΄ The central fact: the logic does not have to be invented, it has to be translated. MW310.PLC is 3.87 MB of commented assembler, version R1 4.20 dated 17 Feb 2005, sitting in the capture. It is the machine's actual control logic - the stop sequence, the ATC, every interlock - and it is legible. BATTUTA (fence/stop) alone appears 1715 times across 69 major sections.
1. How LinuxCNC does this at all¶
There is no single "PLC" in LinuxCNC. There are three programming surfaces, and part of the job is deciding which each function belongs on.
| Surface | What it is | What it replaces here |
|---|---|---|
| HAL (Hardware Abstraction Layer) | A real-time signal fabric. Logic blocks (and2, or2, not, timedelay, muxβ¦) wired together with net statements in a .hal text file. Runs every servo period, 1 kHz |
The wiring itself, plus every fast interlock, enable and safety gate |
| ClassicLadder | A genuine ladder-logic PLC that runs as a HAL component, with a graphical editor, timers, counters and sequential function charts | π΄ The direct replacement for MW310.PLC. If a rung makes more sense than a HAL net, it goes here |
G-code, custom M-codes, remapped M6, O-word subroutines |
The part program, plus .ngc subroutines that run on demand |
The ATC sequence, drill selection, anything the program commands rather than the machine enforces |
Supporting pieces worth knowing the names of:
| Thing | Does what |
|---|---|
motion.digital-out-NN / digital-in-NN |
64 general-purpose output and input pins that G-code drives with M62-M66 |
M64 P<n> / M65 P<n> |
Turn a digital output on / off immediately |
M62 P<n> / M63 P<n> |
Same, but synchronised with motion - the output changes exactly at that point in the path |
M66 P<n> L<mode> Q<timeout> |
Wait for a digital input. This is how a sequence waits for a confirm |
iocontrol |
Tool-change handshake: tool-change, tool-changed, tool-prep-number |
halui |
Exposes machine state (program running, feed hold, E-stop) to HAL so interlocks can use it |
tool.tbl |
The tool table - pocket numbers, lengths, diameters |
Rule of thumb for this machine: anything that must be true regardless of what the program says (a stop is up, a guard is open, the vacuum is low) belongs in HAL or ladder. Anything the program commands (drill 3 on, change to tool 7) belongs in G-code.
2. The functional inventory, from the real I/O¶
The router / pantograph spindle¶
| Function | I/O | LinuxCNC |
|---|---|---|
| Spindle contactor | OF.48 OF_MOT_PANT1 |
spindle.0.on via HAL |
| Contactor confirmed | IF.16 IF_MOT_PANT_RES |
HAL input, gate the cycle on it |
| Inverter run | OF.21 OF_MARCIA_INVERTER |
spindle.0.forward |
| Inverter reverse | OF.22 OF_INV_INVERTER |
spindle.0.reverse |
| Speed reached | IF.25 IF_VEL_RAGG |
spindle.0.at-speed β wire this, it is what makes LinuxCNC wait before cutting |
| Head up / down | OF.23 / OF.24 |
Digital outs |
| Head up confirm | IF.10 (series chain) |
Digital in |
| Speed command | analog on CONN.S |
spindle.0.speed-out β 7I77U AOUT3 |
The drills - 10 vertical, 3 horizontal¶
| Function | Outputs | Confirm |
|---|---|---|
| Vertical drill motor | OF.9 OF_MOT_PUNTE_VERT |
β |
| Vertical drills 1-6 | OF.10-OF.15 |
IF.10, all-heads-up series chain |
| Vertical drills 7-10 | OF.25-OF.28 |
same |
| Horizontal drills, heads 1-3 | OF.16, OF.17, OF.18 |
IF.26, IF.27, IF.28 - each "OFF"/retracted |
These are not axes. They are digital outputs, one per drill, plus one motor output.
In LinuxCNC: each maps to a motion.digital-out-NN. The CAM post emits M64 P<n> to drop a drill and M65 P<n> to lift it, around the moves. This is exactly how woodworking retrofits are done - nothing exotic is required.
β οΈ OF.9 is the drill block's motor and must be running before any drill is dropped. That is an interlock, so it belongs in HAL, not in the G-code.
The saw¶
| Function | I/O |
|---|---|
| Blade head down / on | OF.20 OF_LAMA_NON_AGGR_GIU |
| Blade head up confirm | IF.11 IF_LAMA_NON_AGGR_SU |
The workholding - clamps, vacuum, pressure¶
| Function | I/O | Note |
|---|---|---|
| Pedal, clamp fields A/E/I | IF.3 |
Operator request |
| Pedal, clamp fields D/H/L | IF.4 |
|
| Release fields A/E/I | OF.2 OF_SBLOCCO_CAMPO_AEI |
β οΈ sblocco = release. The output is the unclamp |
| Release fields D/H/L | OF.3 |
|
| Counter-pressure | OF.19 OF_CONTRO_PRESS |
Hold-down beam |
| Vacuum control | IF.9 IF_CONTR_VUOTO (N.C.) |
π΄ Interlock - no cut without vacuum |
| Zone start buttons | IF.1 zone 1, IF.2 zone 2 |
|
| Zone lamps | OF.8 A/E/I, OF.37 D/H/L |
|
| Photocells | IF.33 zone 1, IF.34 zone 2 (N.C.) |
Deferred to phase 5 per CLAUDE.md Β§9 |
π΄ The locating pins - RIF / BATTUTA¶
These are what you called the pop-up pins. Italian riferimento = reference/datum; the PLC calls them BATTUTA, a fence or stop.
| Output | Raises |
|---|---|
OF.29 OF_RIF_X_CAMPO_AEI |
X stops, fields A/E/I |
OF.30 OF_RIF_X_CAMPO_DHL |
X stops, fields D/H/L |
OF.6 |
Y reference, fields A and E |
OF.7 |
Y reference, fields D and H |
Four outputs. That is the whole story on the command side.
π΄ There is NO input that confirms a stop is up or down¶
Searched the entire symbolic map: no IF_RIF*, no IF_BATT*, nothing. The machine raises and lowers the stops blind, and has for 24 years.
β DECIDED (Grant, 3 Sep): no sensors will be fitted. The requirement is narrower than a position interlock: the lower-stops command must have been actioned before the spindle is allowed to turn.
β οΈ On the record, so nobody is surprised later: this guarantees the command was given and time allowed. It does not detect a stop that has stuck up. That is exactly the exposure the machine carries today.
The interlock, in HAL¶
Output true = stop raised (the PLC comment se si' sollevo battuta X B - "if so I raise stop X B" - confirms the sense). So the permit is all four outputs false, continuously, for a dwell.
loadrt or4 count=1
loadrt not count=1
loadrt timedelay count=1
loadrt and2 count=1
addf or4.0 servo-thread
addf not.0 servo-thread
addf timedelay.0 servo-thread
addf and2.0 servo-thread
# any stop raised? OF.29, OF.30, OF.6, OF.7
net stop-x-aei => or4.0.in0
net stop-x-dhl => or4.0.in1
net stop-y-ae => or4.0.in2
net stop-y-dh => or4.0.in3
net any-stop-up <= or4.0.out => not.0.in
# all commanded down, held for the dwell
net all-stops-down <= not.0.out => timedelay.0.in
setp timedelay.0.on-delay 2.0 # seconds for the air to retract - tune down from here
setp timedelay.0.off-delay 0 # drop the permit INSTANTLY when a stop is raised
net stops-safe <= timedelay.0.out => and2.0.in0
# gate the spindle
net spindle-cmd => and2.0.in1
net spindle-run <= and2.0.out
π΄ The asymmetry is the whole point and it is easy to get backwards. on-delay waits before permitting; off-delay 0 inhibits instantly. Slow to allow, immediate to stop.
β οΈ Gate the inverter run (OF.21), not the spindle contactor (OF.48). KM200 is a mechanically latched contactor whose reset path is gated on the inverter's ZERO SPEED - dropping it from software fights an interlock that already works. Stopping the run command stops the spindle turning and leaves the contactor logic alone.
β οΈ Start the dwell at 2 seconds and reduce it by measurement, once halscope can watch the real pneumatics.
β οΈ Open question worth a minute's thought: should a raised stop also inhibit traverse, not just the spindle? A stop standing proud while the head crosses the table is a collision. The original presumably handles it by sequence - stops drop at cycle start - rather than by interlock. Grant's call; the HAL to add it is one more and2.
The ATC## The ATC¶
| Direction | I/O |
|---|---|
| Carousel enable | OF.41 |
| Position command | OF.42 |
| Strobe | OF.43 π΄ despite being named BIT1 |
| Position bits 1-4 | OF.44-OF.47 |
Bit 5 (P16IN) |
from relay -KA560, not from an output |
In-position (COIN) |
IF.35 |
| Position echo bits 1-5 | IF.36-IF.40 |
| Gripper up / down cmd | OF.35 / OF.36 |
| Gripper up / down confirm | IF.43 / IF.44 |
| Tool release, head 1 | OF.40 |
| Tool present, head 1 | IF.17 |
| Manual release | IF.18 |
| Change piston up / down | IF.20 / IF.21 |
| Carousel homed | IF.46 |
π΄ The PLC symbol names do not match the hardware on the output side. What MW310.EQU calls BIT1 is physically the strobe, and every position bit shifts by one. Build the ATC from the wiring diagram, never from the symbol names.
How LinuxCNC does a tool change¶
The standard mechanism is remapping M6 to a subroutine. In the INI:
toolchange.ngc is then an ordinary O-word subroutine that runs the sequence with M64/M65 to drive outputs and M66 to wait on inputs. Roughly:
- Read the requested pocket from the tool table
- Move to the change position, spindle stopped and oriented
- Write the 5 position bits, pulse the strobe (
OF.43) M66wait forCOIN(IF.35), with a timeout- Verify the echo bits
IF.36-IF.40match what was asked - Gripper down (
OF.36),M66waitIF.44 - Release the tool (
OF.40),M66waitIF.17to clear - β¦and the reverse to load
Every step has a confirm input, which is what makes this tractable. A sequence that waits on real feedback at every stage either completes or faults out - it never half-changes a tool.
β οΈ Spindle orientation is still unresolved. IF.21 is called "change piston down" by the PLC but the SGM connector schedule labels the same wire E121 as the spindle zero/index sensor. Settle that before writing the ATC - if it really is an index, orientation is achievable; if not, it is not.
Axis overtravel - π΄ a correction¶
IF.22, IF.23, IF.24 are EXTRACORSA ASSE U/V/W - overtravel on X, Y and Z - and they arrive through the ribbon like any other input.
An earlier note said "LinuxCNC will not see the axis limit switches". That was wrong. The drive's own P-OT/N-OT on CN1-42/43 is a separate hardwired path that stays as it is; in addition, the controller gets one overtravel input per axis. Wire these to LinuxCNC's limit inputs.
Housekeeping¶
| Function | I/O |
|---|---|
| Ballscrew nut lubrication | OF.38 OFLUBRCHIOC |
| Oil level | IF.45 (N.C.) |
| Hold | IF.7 (N.C.) |
| End cycle | IF.29 (N.C.) |
| Summary fault | IF.8 (N.C.) - eleven sources, see safety-and-enable-chain.md |
| Feed override, 4-bit | IF.12-IF.15 |
| Jog β, rapid, + | IF.30, IF.31, IF.32 |
3. MW310.PLC is the specification¶
It is legible, structured, commented assembler with #include, conditional compilation and named memory flags. A real extract from the stop-raising logic:
LOAD OF_RIF_X_CAMPO_BFJ,MBATTUTAX_AEI ;se si'sollevo battuta X B
TEST MLUNGO ;pezzo lungo ?
JPZE SET_RES_BATT_X ;salto se no
LOAD OF_RIF_X_CAMPO_CGK,MBATTUTAX_AEI ;se si'sollevo battuta X C
"if so I raise stop X B / long workpiece? / jump if not / if so I raise stop X C"
It even carries the machine's own configuration switches - MLUNGO (long workpiece), MPRGCAMPOA (field A programmed), 2CAMPI vs 4-field builds. The rules for which stop rises for which job are all in there.
β οΈ It is a specification to read, not code to port. The instruction set is Tecnos-specific. Read it for intent, then express that intent in HAL, ladder or G-code.
4. What to do next, in order¶
π΄ Almost all of this can be built and tested on a laptop¶
Grant has not used LinuxCNC and expects trial and error. Trial and error is the right approach here, and LinuxCNC is unusually good at supporting it safely, because the whole thing runs without any hardware attached.
| Tool | What it does |
|---|---|
sim configurations |
LinuxCNC ships with simulated machine configs. The HAL file, the ladder, the M6 remap and the G-code all run on an ordinary laptop with no Mesa card and no machine. Interlocks can be built and proven before a single wire is landed |
halshow |
Live tree of every HAL pin and signal, with current values |
halmeter |
Watch one pin |
halscope |
A software oscilloscope for HAL signals. This is what measures the pneumatic dwell later |
halcmd sets / setp |
Force a signal by hand. Test the stop interlock by forcing stop-x-aei true and confirming the spindle permit drops - no machine, no air, no risk |
So the ATC subroutine and the drill logic can be written, run and debugged in simulation. What the machine is needed for is the last mile: real timings, real pneumatics, and confirming the sequence order matches.
β οΈ This also softens the loss of not being able to capture a live ATC or drilling cycle from MW310. Worth knowing: the drill heads and saw blades already actuate from the MW310 diagnostic interface (HANDOVER.md, machine health). That is enough to time a single drill down-and-up and the ATC gripper individually, even without running a program - which is most of the timing data the PLC source cannot give.
| # | Task | Needs the machine? |
|---|---|---|
| 1 | Mine MW310.PLC function by function - stops, clamps, ATC, zone logic, interlocks. Write each up as a plain-English sequence |
β No. Offline, free, can start today |
| 2 | Decide which surface each function lands on: HAL, ClassicLadder, or G-code | β No |
| 3 | Decide the stop-sensor question above | β No |
| 4 | Write the HAL file against mesa-wiring-schedule.md |
β No |
| 5 | Confirm timings and sequence order against the running machine | β Yes |
Item 1 is the whole job and none of it needs the machine. That is unusual and worth exploiting.
On capturing data from the running machine¶
Ranked by value:
| Source | Value |
|---|---|
β MW310.PLC |
Already captured. Contains the logic itself. Costs nothing |
| Live I/O on the MW310 diagnostic screen | Confirms the map, cheap |
| Video of one ATC cycle and one drilling cycle | β οΈ Downgraded 3 Sep. The source turned out to carry the timings too - 50 ms bit settle, 600 ms after the carousel go command, a 7 s mismatch retry, 5 tool-present attempts. Video now only confirms them. See plc-sequences.md |
| Serial tap | The parameter dump and the command protocol |
β οΈ Do not spend machine time on what is already on the disk - and that turns out to include the timings. plc-sequences.md has the dwells, the retry counts and the timeout windows straight from the source. The live machine is now only for confirmation.
Related¶
io-map.md- every point to its terminal, wire and devicemesa-wiring-schedule.md- where each point lands on Mesasafety-and-enable-chain.md-OF.1, rail8A, what stays hardwiredservo-drive-interface.md- the ATC carousel I/O, mapped point by pointmw310-recon.md- the full software inventory