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.
Carousel rotation โ the complete sequence, with timings¶
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¶
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 |
Related¶
control-logic-spec.mdโ the functional inventory and the three LinuxCNC surfacesio-map.mdโ every point to its terminal, wire and deviceservo-drive-interface.mdโ the carousel I/O as read from the drawing../linuxcnc-sim/โ where to build and test all of this