Skip to content

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.

The original PLC sequences, translated

3 September 2026. First pass at mining MW310.PLC โ€” the machine's actual control logic, R1 4.20, 17 Feb 2005 โ€” into plain English and LinuxCNC terms.

None of this needed the machine. It is all in a text file already captured.

๐Ÿ”ด Read this first: it is a generic multi-model program

MW310.PLC is Masterwood's PLC for a whole product range, not for this machine. It contains eleven different tool-change cycles, four-field and two-field table variants, Atlas and Winner and HP carriage types, and much else this machine does not have.

Two conditional branches select our machine:

Switch Ours
2CAMPI vs 4-field 2CAMPI โ€” two working zones, not four
Tool change cycle CICLO CAMBIO UTENSILE 6 (GIOSTRA POSTERIORE) โ€” "rear carousel", the _CU_6 symbols throughout

โš ๏ธ Reading a branch that does not apply is the main way to get this wrong. Every symbol for this machine's carousel ends _CU_6. Anything referring to C.U. 4, 5, 7-11 or to the horizontal changers is another model.

The instruction vocabulary you need

Tecnos Means LinuxCNC equivalent
SET x / RES x Turn an output on / off M64 P<n> / M65 P<n>
WAITTIM1 <job>,<in>,TIMEOUT Wait for input high, with timeout M66 P<n> L3 Q<sec>
WAITTIM0 <job>,<in>,TIMEOUT Wait for input low M66 P<n> L4 Q<sec>
DELJ <job>,<ms> Dwell G4 P<sec>
TEST x / JPZE label If x is zero, jump o<n> if
LOAD dst,src Copy a bit a HAL net
BUNLOAD var,bit,pin,pin Write bits of a number to output pins HAL, or arithmetic in G-code
OS_xxx A command from the part program A custom M-code
VT_xxx A wait token โ€” program waits for an event M66, or a HAL handshake
R_xxx A configuration register An INI value or HAL parameter

1. The locating stops โ€” BATTUTE

They are commanded per zone, not individually

GEST_BATT_BR_Z1
    TEST  OS_BATT_Z1                      ;is there a command?
    JPZE  GEST_BATT_BR_Z2                 ;no - skip
    LOAD  OF_RIF_X_CAMPO_AEI, RBATT_X1_Z1 ;yes - raise/lower
    LOAD  OF_RIF_X_CAMPO_BFJ, RBATT_X2_Z1 ;the zone 1 stops
    LOAD  OF_RIF_Y_CAMPO_A,   RBATT_Y1_Z1
    LOAD  OF_RIF_Y_CAMPO_E,   RBATT_Y2_Z1
    LOAD  OF_RIF_Y_CAMPO_I,   RBATT_Y3_Z1
    SET   T_BATT_Z1                       ;signal cycle complete

๐Ÿ”ด CORRECTION โ€” the block above is the WRONG BRANCH for this machine

The code quoted above sits under GESTIONE_ISO_BR and its registers are all commented (pianetti). That is the B&R sub-platen option, not this machine.

I walked straight into the trap this document opens by warning about. Recording it rather than quietly fixing it, because it is the single easiest way to get this whole exercise wrong.

The branch that applies here is BLK_PZ_NORMALE โ€” "normal clamping" โ€” and in that branch the stops are raised by the PEDAL logic, not by a program command. The machine sets MBATTUTAX_AEI and MBATTUTAY_A itself when the operator releases the pedal. See ยง2a below, which is the correct branch and was read correctly.

Why this machine is the normal branch

Evidence
The cover sheet describes PIANO SCANALATO + POMPA 250 m3/h A grooved vacuum table. Pianetti are small sub-platens โ€” a different table entirely
No B&R hardware anywhere in the 14-page device list The B&R branch needs a B&R controller
MPIANETTI_BR is a runtime flag, so both paths are compiled in Which one runs is decided by machine configuration, not by the code

โš ๏ธ Not conclusive, but strong. Settle it by watching the machine: press a zone pedal with no program running. If the stops rise, it is the normal branch and the pedal owns them. If nothing happens until a program commands it, it is the B&R branch after all.

What it means for LinuxCNC

If normal branch (likely) If B&R branch
The stops belong to the operator, driven by pedal logic in HAL or ladder The stops belong to the part program, driven by M-codes
M101/M102 in the sim become a manual/setup convenience, not the production path M101/M102 are the production path
The two-press pedal cycle in ยง2a is the real interface The pedal only confirms

The M101/M102 M-codes already written are still worth having either way โ€” they are how the stops get commanded for setup, testing and any program that does want to drive them. But do not build the CAM post around them until this is settled.

The RBATT_* registers, and the real stop data

The B&R registers are R.130โ€“R.139 (five stops ร— two zones) and RCONF_BLOCCO is R.153. Those belong to the branch above.

๐Ÿ”ด What this machine actually has is Param/BATTUTE.PRM, and it is real captured data:

A 12000 140783 2 0        E 12000  20283 0 0
B 12000 140783 3 0        F 0 0 0 0
C 291090 140783 2 0       G 0 0 0 0
D 291090 140783 3 0       H 291090 20283 1 0
                          I / J / K / L  all zero

Read as hundredths of a millimetre โ€” the machine's convention throughout โ€” the pattern is a grid of two X positions and two Y positions:

X โ‰ˆ 120.00 mm X โ‰ˆ 2910.90 mm
Y โ‰ˆ 1407.83 mm A, B C, D
Y โ‰ˆ 202.83 mm E H

โš ๏ธ Provisional reading. The first two numbers are almost certainly the field's X and Y reference coordinates; the third column (2, 3, 2, 3, 0, 1) is unidentified โ€” a stop index or type. Confirm before relying on it.

โœ… What is certain and useful: fields A, B, C, D, E and H are configured; F, G, I, J, K and L are all zero โ€” not fitted. That is this machine's real field layout, straight off its own disk.

The five-per-zone vs four-output puzzle, resolved

The generic code addresses five stops per zone: X1, X2, Y1, Y2, Y3. This machine has four stop outputs total.

Generic symbol This machine
OF_RIF_X_CAMPO_AEI OF.29
OF_RIF_X_CAMPO_DHL OF.30
OF_RIF_Y_CAMPO_A and OF_RIF_Y_CAMPO_E both on OF.6
OF_RIF_Y_CAMPO_D and OF_RIF_Y_CAMPO_H both on OF.7
OF_RIF_X_CAMPO_BFJ, _CGK, OF_RIF_Y_CAMPO_I, _L โŒ not assigned โ€” four-field machines only

So this machine raises X and Y stops as two pairs per zone, not five individually. That halves the problem.

Pedal and start are handshakes, not just inputs

IF VT_PEDALE==1                    ;program is waiting for pedal 1 PRESSED
   IF IF_BLOCCO_AEI==1
      SET T_PEDALE_BR              ;satisfied - release the program
IF VT_PEDALE==-1                   ;waiting for pedal 1 RELEASED
   IF IF_BLOCCO_AEI==0
      SET T_PEDALE_BR

The program can wait for pressed or released, on either pedal. Likewise VT_START, which waits on the zone start button or falls back to the pedal, and which a config register RCONF_BLOCCO can switch to waiting on a clamp-confirmed input instead.

In LinuxCNC: M66 on the pedal input, L3 for pressed and L4 for released. The fallback and the config switch are o<n> if branches in the subroutine.


2. The tool change โ€” CICLO CAMBIO UTENSILE 6

Nine phases, FASE 0 to FASE 8, about 2300 lines. The generic code handles up to three spindles (_UT1/2/3); this machine has head 1 only, so only the UT1 branch applies.

The shape of it

Phase Does
0 Check the change piston is up (IF_PIST_CAMBIO_SU1), test whether a tool is present (IF_PREUTE1)
1 Park: move W (Z) to the park or approach height, stop the spindle and the inverter, move V (Y) if needed
2 Release the tool โ€” SET OF_SBLOCCO_UT1, then wait for the piston-up input to go low
3 Gripper down โ€” SET OF_PINZA_GIU_CU_6, wait IF_PINZA_GIU_CU_6
4 Release again and wait for IF_PIST_CAMBIO_GIU1 โ€” piston fully down
5 Retry logic: after five attempts with IF_PREUTE1 still low, jump to phase 6
6 Clamp the new tool โ€” RES OF_SBLOCCO_UT1, wait IF_PREUTE1 to confirm the tool is seated
7 Gripper up โ€” SET OF_PINZA_SU_CU_6, wait IF_PINZA_SU_CU_6
8 Finish, restore

Every single step waits on a real confirm input with a timeout. That is why this is tractable to reproduce: a sequence that never assumes either completes or faults out.

โš ๏ธ Note phase 5. The original retries the tool-present check five times before giving up. Reproduce that โ€” a tool that does not seat first time is normal, not a fault.

This is a separate job (job 50), called by the change cycle:

                                    ;gripper must be DOWN before rotating
SET      OF_PINZA_GIU_CU_6
WAITTIM1 50,IF_PINZA_GIU_CU_6,TIMEOUT
SET      MCHK_PINZA_GIU_CU_6        ;continuous check it STAYS down

                                    ;optional bit inversion, config EN_CU_6 < 0
COPY     29,VAPP_CU_6
SUB      VPINZA_UTE,VAPP_CU_6       ;VAPP = 29 - pocket number

SET      OF_ABIL_GIOSTRA_CU_6       ;enable the carousel
BUNLOAD  VAPP_CU_6,0,OF_BIT1,OF_BIT2   ;write the pocket number
BUNLOAD  VAPP_CU_6,2,OF_BIT3,OF_BIT4   ;as bits across the outputs
BUNLOAD  VAPP_CU_6,4,OF_BIT5
DELJ     50,50                      ;๐Ÿ”ด 50 ms for the bits to settle
SET      OF_POSIZ_GIOSTRA_CU_6      ;๐Ÿ”ด GO
DELJ     50,600                     ;๐Ÿ”ด 600 ms
WAITTIM1 50,IF_POS_RAGG_CU_6,TIMEOUT2  ;wait in-position (COIN)

TST_POS_GIOSTRA,6                   ;read the echo bits back
CMP      VAPP_CU_6,VAPP2_CU_6       ;๐Ÿ”ด commanded == echoed?
JPZE     J50_FINE                   ;yes - done
                                    ;no - wait 7 s and re-check
                                    ;still wrong - ERROR 2.1.1042

CMP      1,VPINZA_UTE               ;going to pocket 1?
WAITTIM1 50,IF_RES_GIOSTRA_CU_6     ;then also wait for the home confirm
RES      OF_POSIZ_GIOSTRA_CU_6

๐Ÿ”ด What this gives us that nothing else did

50 ms bit settle time before the go command
600 ms after the go command, before starting to watch for in-position
7 seconds position-mismatch retry window before erroring
5 attempts tool-present retry in phase 5
Echo verification the commanded bits are compared against the read-back bits, and a mismatch is a hard error, not a warning
Gripper interlock the carousel is never rotated with the gripper up, and the gripper is continuously monitored during rotation

โš ๏ธ This corrects something recorded earlier. The note in control-logic-spec.md said the PLC source shows sequence but not duration, and that timings would have to come from watching the machine. That was wrong โ€” the dwells, the retry counts and the timeout windows are all in the source. Watching the machine is now only useful for confirming them, not discovering them.

๐Ÿ”ด And it sharpens the strobe question

The PLC writes the pocket number into OF_BIT1โ€“OF_BIT5 (OF.43โ€“OF.47) as five position bits, then uses OF_POSIZ_GIOSTRA (OF.42) as the go/strobe.

The wiring reading in servo-drive-interface.md has OF.42 โ†’ SVON, OF.43 โ†’ STB, and OF.44โ€“OF.47 โ†’ P1IN/P2IN/P4IN/P8IN, with P16IN from relay -KA560.

These cannot both be right. Either the drawing transcription is shifted by one โ€” which would give OF.42 โ†’ STB and OF.43โ€“OF.47 โ†’ P1INโ€“P16IN, fitting the PLC exactly โ€” or the machine really does use SVON as a latch.

๐Ÿ”ด The PLC is the stronger evidence: it is what actually ran the machine for 24 years. But settle it on the drawing before building the ATC. Ring out OF.42 and OF.43 to the Panasonic terminals โ€” five minutes with a meter, and it decides the whole tool-change design.


2a. Workpiece holding โ€” ๐Ÿ”ด the polarity is the opposite of the obvious reading

OF.2 / OF.3 TRUE means the workpiece is FREE, not held

The symbols are OF_SBLOCCO_CAMPO_AEI and _DHL, and sblocco is Italian for release. The PLC's own comments call the same bit "il vuoto" โ€” the vacuum โ€” which reads ambiguously. The surrounding logic settles it:

BLK_PZ_A_02                            ;pedal RELEASED
    TEST MBATTUTAX_AEI                 ;is the X stop already raised?
    JPNZ BLK_PZ_A_03                   ;yes - go to _03
    SET  MBATTUTAX_AEI                 ;no - raise the X stop
    SET  MBATTUTAY_A                   ;   and the Y stop
    SET  TOF_SBLOCCO_CAMPO_AEI         ;   and RELEASE the sheet
    SET  M_ALZAPANNELLI_AEI            ;   and raise the panel lifters
BLK_PZ_A_03
    RES  TOF_SBLOCCO_CAMPO_AEI         ;HOLD the sheet
    ...
    SET  MBLOCCO_PEZZO                 ;and now START is allowed

Releasing goes with raising the panel lifters; holding goes with permitting START. There is no other consistent reading.

๐Ÿ”ด OF.2 / OF.3 energised = workpiece released and floating. De-energised = workpiece held.

โš ๏ธ This fails safe, which is strong corroboration. If the output drops โ€” controller dead, power lost, wire off โ€” the part is held, not released. Reproduce that sense exactly. Getting it backwards means a sheet that is not held when the router arrives.

The operator workflow is a two-press pedal toggle

This is how the machine actually feels to use, and it is worth reproducing:

Operator Machine
Press 1 Presses the zone pedal Raises the X and Y locating stops, releases the vacuum, and raises the panel lifters so the sheet floats
Slides the sheet against the stops
Press 2 Presses the same pedal again Holds the sheet, drops the lifters, and MBLOCCO_PEZZO now permits START

The state is carried in MPEDALE_A (pedal already pressed) and MBATTUTAX_AEI (stop already raised) โ€” one pedal, two meanings, decided by what the machine is currently doing.

โš ๏ธ M_ALZAPANNELLI (panel lifters) has no output in this machine's I/O map. Either this machine does not have them or they share another output. Generic-code branch โ€” confirm before reproducing.

โญ This closes the loop on the stop interlock. MBLOCCO_PEZZO is set only on the second press, after the stops are up and the sheet is held. So in the original, START is gated on the whole sequence having completed โ€” not merely on a stop-down command. The HAL interlock already specified is the right shape; this shows what it should be wired into.


2b. Axis overtravel โ€” and a detail worth copying

AXEMERG_01
    CMP  0,VMANUALE            ;in MANUAL?
    JPGT AXEMERG_INIZIO        ;yes - always check
    TEST MESEC                 ;machine in motion?
    JPZE AXEMERG_END           ;no - skip
AXEMERG_INIZIO
    BTEST MJOG_ASSE,...        ;is this a JOG?
    JPNZ  DPOS_ASSEU           ;yes - edge detect
    TEST  IF_FC_ASSEU          ;no - LEVEL test
    JUMP  ERR_ASSEU
DPOS_ASSEU
    DPOS  MAPPOGGIO,IF_FC_ASSEU  ;๐Ÿ”ด EDGE: high for ONE cycle only

IF.22 / IF.23 / IF.24 โ€” overtravel on X, Y, Z โ€” are checked whenever the machine is moving or in manual. A trip sets a PLC error and TEMERG, whose comment says it disables axis holding: the overtravel drops the axis enable.

๐Ÿ”ด The detail worth copying: when jogging, the check is edge-triggered, not level-triggered. A level test would latch you on a tripped limit with no way to drive off it. The original detects the transition onto the limit, so you can still jog back off.

LinuxCNC does this natively โ€” wire IF.22-IF.24 to joint.N.neg-lim-sw-in / pos-lim-sw-in and jogging off a limit with the override is built in. Do not build custom logic for this. It is worth knowing the original bothered, though, because it confirms the inputs are usable as real limit switches rather than as a fault summary.


2c. HOLD and the photocells are ONE chain

AND  MAPPOGGIO, IF_HOLD, IF_FOTOC_Z1, IF_FOTOC_Z2
TEST MAPPOGGIO

All three are normally closed, so the AND is true only when everything is healthy. Any one of them going low is treated identically โ€” the HOLD button and a broken photocell beam do the same thing.

What differs is only what it triggers, by machine state:

State Result
During a reset TEMERG โ€” emergency
During jog or step THOLD โ€” feed hold
During execution Further logic, with a 2000 ms delay (DEL1 ... 2000)

In LinuxCNC: the AND chain drives motion.feed-inhibit. Implemented in the sim.

โš ๏ธ Photocells stay deferred to phase 5, with pendulum working (CLAUDE.md ยง9). The logic is wired now so it exists and can be tested, but they must be re-enabled together with pendulum working, never separately.

2d. Vacuum is checked conditionally, not always

CHK_VUOTO_GENERALE
    TEST IF_CONTR_VUOTO        ;general vacuum input
    JPNZ CHK_VUOTO_A           ;healthy - carry on
    COPY ERR7,V_ERRPLC         ;low - error 1006
    SET  TPLCERR

The check runs only when a zone's vacuum flag is on and that zone's program is executing.

๐Ÿ”ด This corrected the interlock I first built. The cut permit originally required vacuum unconditionally โ€” which is safer in isolation but would block jogging and setup with the pump off, a real operational problem. The sim now requires vacuum healthy or no program running, matching the original's intent.

โš ๏ธ The original also has per-field vacuum inputs (IF_VUOTO_AEI and siblings). This machine has only the general input IF.9 โ€” the per-field ones are a generic-code branch and are not fitted here.

2e. Double pressure โ€” not fitted on this machine

The generic code raises OF_DOPPIA_PRESS_AEI for one second whenever a Y stop rises, then drops it, gated on a VPAR_PRESS config value.

โŒ No OF_DOPPIA_PRESS_* output exists in this machine's I/O map. Not fitted. Recorded so nobody goes looking for it.

โš ๏ธ Do not confuse it with OF.19 OF_CONTRO_PRESS โ€” counter-pressure โ€” which is fitted and is a different thing.


3. The drill motor

MOT_PUNTE_ON
    TEST OS_MOTORE_PUNTE_ON       ;command from the program?
    JPZE MOT_PUNTE_ON_END
    CMP  NO_AGGR,VTIPO_AGGR       ;aggregate head fitted?
    ...
    IF   EN_INV_FORI==1           ;drills on an inverter?
         BSET MSTART_INV_PUNTE    ;launch job 113
    ELSE
         SET  MOF_MOT_PUNTE_VERT  ;else just switch the motor on
    ENDIF

The drill motor is commanded by the program (OS_MOTORE_PUNTE_ON), and the code branches on whether an aggregate head is fitted and whether the drills run off an inverter. On this machine OF.9 is a plain contactor output, so the simple branch applies.

โš ๏ธ It also shows the same output serving three roles depending on the fitted head โ€” vertical drills, horizontal drills (MOF_MOTORE_ORIZZ1) or the saw motor (MOF_MOTORE_LAMA). Check which applies here before wiring the interlock.


4. What this changes for the LinuxCNC build

Finding Consequence
๐Ÿ”ด OF.2/OF.3 energised = workpiece RELEASED Fails safe. Getting this backwards means a sheet not held when the router arrives
The pedal is a two-press toggle Press 1 stops up and float, press 2 hold and permit START
Overtravel is edge-checked when jogging LinuxCNC does this natively - do not build custom logic
HOLD and both photocells are one chain Any one low inhibits feed. Wired in the sim to motion.feed-inhibit
๐Ÿ”ด Vacuum is checked only while executing Enforcing it unconditionally blocks jog and setup with the pump off
Double pressure is not fitted Generic branch. Do not confuse with OF.19 counter-pressure
๐Ÿ”ด Which branch owns the stops is UNSETTLED Likely the pedal, not the program. Do not build the CAM post around stop M-codes until a pedal press on the real machine settles it
Fields A, B, C, D, E, H are configured; F, G, I-L are not From BATTUTE.PRM. This machine's real field layout
This is a two-field machine Half the generic stop logic does not apply
Every ATC step waits on a confirm with a timeout Direct M66 translation. The sequence is safe by construction
The carousel verifies echo bits against commanded bits Reproduce this. It catches a carousel that reports the wrong pocket
Real timings exist: 50 ms, 600 ms, 7 s, 5 retries The ATC subroutine can be written now, not after watching the machine
Pedal and start are two-way handshakes M66 with L3 and L4, not just level reads
The OF.42/OF.43 conflict ๐Ÿ”ด Settle with a meter before building the ATC

Still to mine

Zone start and the bar-code / occupancy logic scattered
EN_CU_6 (VP.997) - the carousel bit-inversion switch Needs reading from the machine. It decides whether the pocket number is sent as-is or as 29 - pocket
The third column of BATTUTE.PRM Unidentified
  • control-logic-spec.md โ€” the functional inventory and the three LinuxCNC surfaces
  • io-map.md โ€” every point to its terminal, wire and device
  • servo-drive-interface.md โ€” the carousel I/O as read from the drawing
  • ../linuxcnc-sim/ โ€” where to build and test all of this