CNC G-Code Subroutines and Multiple Work Offsets: Programming Repeat Jobs and Multi-Fixture Batch Production
This site's guide to CNC workholding covers the physical side of holding stock securely — clamps, vacuum, tape. This guide covers the other half of running efficient batch jobs: the programming side. Once you're cutting the same part more than a few times, typing (or CAM-generating) a full separate program for each copy wastes time and multiplies the chance of a transcription error. Work coordinate offsets and G-code subroutines solve this by letting one program run against multiple physical locations on your spoilboard, or repeat a toolpath block without re-specifying it. This is standard practice on shop-floor CNC machines and increasingly available on desktop GRBL and grblHAL routers like the Wolfpawn 4040 Pro.
Work Coordinate Systems: What G54-G59 Actually Do
Every CNC controller tracks positions relative to a work coordinate system (WCS) with its own zero point — this is the "work zero" you set with a touch probe or manual jog before a job starts. Most controllers support six or more selectable work offsets, conventionally G54 through G59 (and extended ranges like G54.1 P1-P48 on many modern controllers), each storing an independent XYZ zero position. Practically, this means you can physically screw down six identical fixtures or six blanks across your spoilboard, probe or jog to set a separate zero for each one (G54 for fixture 1, G55 for fixture 2, and so on), then run the exact same G-code program six times — once per work offset — without touching the code at all.
OffsetTypical Use G54Default/primary work zero — most controllers boot into this and single-fixture jobs never need to leave it G55-G59Additional independent zero points — one per repeated fixture location, jig position, or stock blank G54.1 P1-P48 (extended offsets)Supported on some grblHAL and industrial controllers for jobs needing more than six locations — check your controller's supported G-code set before relying on thisTo set up a multi-fixture job: home the machine, then for each fixture location, jog or probe to the part's zero point and save it to the corresponding offset register (most senders like gSender, UGS, or CNCjs have a dedicated work coordinate offset panel for this — you don't need to hand-type the storage command in most workflows, though the underlying G10 L20 command is what's happening). Then structure your program to select each offset with a G54/G55/etc. call, run the toolpath, and move to the next.
A Basic Multi-Fixture Program Structure
The simplest approach — and the one that works on nearly every GRBL-class controller without special macro support — repeats the full cutting block once per offset, switching the active work coordinate system at the start of each repeat:
G54 (select fixture 1) G0 Z5 (safe height) G0 X0 Y0 ; ... cutting toolpath ... G0 Z5 (retract) G55 (select fixture 2) G0 Z5 G0 X0 Y0 ; ... identical cutting toolpath ... G0 Z5 G56 (select fixture 3) ; ... repeat ...This is verbose — the toolpath block is duplicated per fixture — but it's completely portable and requires nothing beyond standard G-code your controller already understands. Most CAM software (Fusion 360, VCarve, Carbide Create) can generate this automatically if you tell it your job uses multiple work offsets; check your CAM package's "multiple setups" or "stock/fixture offset" feature before hand-editing G-code.
Subroutines: Repeating a Toolpath Without Duplicating It
Where GRBL's native support ends and grblHAL, Mach3/Mach4, and LinuxCNC pull ahead is subroutine support — defining a block of G-code once as a named or numbered subprogram, then calling it repeatedly with M98/M99 (or controller-specific syntax) instead of pasting the same lines over and over. This matters for two reasons beyond convenience: a shorter program is easier to audit for mistakes before you cut expensive material, and if you need to change the toolpath (a different depth, an added pass), you edit it in exactly one place instead of hunting down every duplicate block.
O0100 (subroutine: pocket cut) G1 Z-2 F300 G1 X10 Y0 G1 X10 Y10 G1 X0 Y10 G1 X0 Y0 G0 Z5 M99 (return from subroutine) (main program) G54 G0 X0 Y0 M98 P0100 (call subroutine at fixture 1) G55 G0 X0 Y0 M98 P0100 (call subroutine at fixture 2) G56 G0 X0 Y0 M98 P0100 (call subroutine at fixture 3) M30 (end program)Support for O-number subroutines and M98/M99 calls varies significantly between controllers — Mach3/Mach4 and LinuxCNC support this natively, grblHAL added subroutine and looping support that plain GRBL lacks, and stock GRBL 1.1 does not support subroutines at all. Check your specific controller's documentation before planning a job around this, and always run a simulation pass (see this site's guide on G-code simulation and verification with CAMotics or NC Viewer) before cutting real material with a subroutine-heavy program, since a single wrong parameter in a shared subroutine now affects every call to it.
Practical Batch Workflow
- Design and prove out the toolpath for a single part first, cut one physical copy, and confirm dimensions and finish before committing to a multi-fixture run.
- Lay out your fixture or blank positions on the spoilboard with enough clearance between them for the cutter, dust boot, and any clamps — measure this against your machine's actual work area, not just the toolpath's bounding box.
- Set and record each work offset carefully — a touch probe (see this site's CNC touch probe guide) makes this fast and repeatable; manual jogging works but is more error-prone across six-plus fixtures.
- Run a full air-cut (Z raised well above the stock) of the complete multi-fixture program first if your controller and sender support it, to visually confirm every fixture position is correct before the bit ever touches material.
- Cut the first fixture alone and inspect it before letting the rest of the batch run unattended — a work offset set wrong for fixture 3 shouldn't have to ruin fixtures 1-6 worth of material before you notice.
For a Wolfpawn 4040 Pro or similar desktop router running GRBL or grblHAL, the practical takeaway is: multiple work offsets (G54-G59) work today with almost any sender and require no special firmware, and they're worth learning even for occasional batch work like a run of signs or a set of matching brackets. Subroutines are a bigger firmware commitment — worth it if you're running grblHAL or considering a Mach3/LinuxCNC controller upgrade specifically for repeat production work, but not something to chase if your batch sizes are small and CAM-generated duplicate toolpaths already get the job done.
Related Guides
- How to Use EstlCAM for CNC Routing: Toolpaths, G-Code, and GRBL Control
- Fusion 360 CAM for Hobby CNC: Complete Guide
- gSender Complete Workflow: Connect, Zero, Run, and Recover
- How to Use LaserGRBL: Free Laser Control Software for GRBL Engravers
- gSender Workflow: Setting Zero, Running Jobs, and Recovery
- CNC Router Beginner Guide: CAM Software, Toolpaths, and Your First Cut
- Mounting a Diode Laser Module on Your CNC Router: Dual-Use Gantry Conversion
- CNC Touch Probes for Desktop Routers: Wiring, Tool Length Offset, and Work Zero Setup