Reading & Writing System Variables (Every Control)

Every CNC control keeps a live picture of itself in memory — where each axis is, which work and tool offsets are active, what the probe just touched, which G-codes are modal, how many parts have run, which alarms are set. System variables are the named or numbered handles a part program uses to reach that data: read one into a scratch variable, do arithmetic, and (where the variable allows it) write a new value back. This one mechanism is behind every probing routine, tool-breakage check, adaptive-feed macro, and part-family program ever written. The idea is universal; only the spelling changes from control to control. This article puts the seven common dialects side by side so you can translate a pattern you know into one you don't — and links each control’s own reference for the full list.

Read-Only vs Read/Write — the Universal Caveat

Reading is almost always allowed. Writing is gated. Variables that report machine state — live positions, modal codes, the clock, probe-trigger positions — are read-only on every control; assigning to them is either ignored or throws an error. Variables that hold a stored value — work offsets, tool offsets, general-purpose commons, part counters — are read/write. On top of that split, some writes are locked behind an option or by the machine builder (OEM/PLC territory). Getting this wrong is the most common source of “why won’t it write?” confusion.

ControlRead-only (report state)Read/write (you can assign)The catch
Fanuc / HaasPositions #5001–#5108, modal codes #4000–#4130, clock #3011/#3012Work offsets #5201–#5328/#7001+, tool offsets #2001–#2800/#10001+, commons #100–#199/#500–#999Probe/parameter ranges only present if the option is installed; Haas Settings #20000+ and Parameters #30000+ are read-only
MitsubishiPositions #5001–#510n, PLC inputs #1000–#1035, modal #4001–#4021Work offsets #5221+, tool offsets #10001+/#2001+, commons, date/time #3011/#3012 (writable here)Common-variable count is option-dependent; PLC outputs #1100+ writable
Mazak (EIA)Positions #5001–#5116, modal #4001–#4227, interface inputs #1000+Work offsets #5221+, tool data #40001+ (by tool-data index), interface outputs #1100+No fixed #2001 tool block — resolve the tool-data index via #3020 first
Siemens$AA_IM/$AA_IW positions, $P_ modal data, $VA_ servo data$P_UIFR[] frames, $TC_DP tool data, R-parameters, $AC_ timers/countersWritability varies per $-variable; the System Variables manual marks each. Reading a $A_/$V_ runtime var forces a preprocessing stop
HeidenhainAll FN 18: SYSREAD system data; preassigned Q100–Q199 (probe results, tool data)Q/QL/QR/QS parameters; tables via FN 27/TABDATA WRITE/SQL UPDATEFN 17: SYSWRITE is OEM/PLC-locked — you never write a live datum directly. Writes go through tables, the preset table, or a probing cycle
OkumaSpindle speed VSPS; Renishaw cycle results in VS75–VS89Work offsets VZOFX/Y/Z[n], zero shift VZSFTx, tool offsets VTOFH/D[n], commons VC1–VC200VS system variables reset to null at program end; index ranges depend on OSP version and offset-set option

Three per-control notes worth memorizing. Fanuc/Haas: positions are read-only, offsets are writable — the whole probing workflow depends on that asymmetry. Siemens: read/write status is per-$-variable and documented in the System Variables manual (in synchronized actions, an “SA” column marks read/write eligibility). Heidenhain: the direct write path FN 17: SYSWRITE is machine-builder territory, so a normal NC program changes offsets by writing a table (FN 27, TABDATA WRITE, or SQL) or by letting a touch-probe cycle write the preset table.

The Rosetta Stone — One Task, Seven Dialects

Each row is a job a machinist actually asks of a macro; each column is how that control spells it. Axis suffixes shown are for Z on a typical 3-axis mill (X = first, Y = second, Z = third). Verify against the linked per-control article before you run converted code — option gates and index ranges differ.

TaskFanuc / HaasMitsubishiMazak (EIA)SiemensHeidenhainOkuma
Read current machine position (Z)#5023#5023#5023$AA_IM[Z]FN 18: SYSREAD Q1 = ID240 NR1 IDX3— (see note)
Read current work position (Z)#5043#5043#5043$AA_IW[Z]FN 18: SYSREAD Q1 = ID270 NR1 IDX3— (see note)
Read a probe / skip result (Z)#5063#5063#5063$AA_MW[Z]Q117 (Q115–Q119 = X,Y,Z,4,5)VS77 (Renishaw cycle Z)
Read active tool offset (length)#5083 (active) / #2001+ (stored)#5083 / #10001+n#5083 / #40001+n$TC_DP3[t,d]Q114 (active) / ID50 NR1 (table L)VTOFH[n]
Read active work offset / modal WCS#4014#4012#4012$P_UIFRNUMFN 18: SYSREAD Q1 = ID530 NR1— (see note)
Writes (gated — see caveats above)
Write a work offset (G54 Z)#5223 = …#5223 = …#5223 = …$P_UIFR[1,Z,TR] = …FN 27 datum / preset cycle (Q303/Q305)VZOFZ[n] = …
Write a tool offset (length)#2001 = … / #[11000+H] = …#10001+n = … / G10 L10#40001+n = … / G10$TC_DP3[t,d] = …TABDATA WRITE CORR-TCS / SQL on TOOL.TVTOFH[n] = …

Okuma note: the OSP references in this wiki document the named offset variables (VZOFx, VTOFx) and Renishaw probe outputs (VS75–VS89), but do not document a bare numbered current-position or skip-position system variable equivalent to Fanuc’s #5041/#5063. On an Okuma the read-and-write pattern is normally driven off the probing-cycle outputs (below), not a raw position variable. Heidenhain note: FN 18: SYSREAD always returns metric values regardless of program units, and a real-time read should be preceded by FN 20: WAIT FOR SYNC to flush look-ahead.

The Canonical Task in Every Dialect

Here is the same job — probe the top of a part and store the measured height into the active work offset so the touched face becomes Z0 — written seven ways. It is the “touch off and set zero” pattern that underpins in-process setup. Read the skip/probe position, add it to the stored work offset, retract.

Fanuc / Haas

Feed down with G31 until the probe trips; #5063 latches the Z skip position in work coordinates; shift the G54 Z offset #5223.

(TOUCH TOP OF PART, SET IT AS G54 Z0)
G90 G31 Z-25. F50.        (feed down until the probe trips)
#5223 = #5223 + #5063     (shift G54 Z so the touched face = Z0)
G00 Z25.                  (retract)
part G31 Z-25. F50. 1 · probe trips 2 · position latched into #5061–#5068 3 · motion stops, block ends skip position by axis: X=#5061 · Y=#5062 · Z=#5063

The G31 skip sequence: the control feeds until the probe trips, latches the contact position into #5061–#5068, then stops motion and ends the block.

Mitsubishi (M700/M70)

Near-identical to Fanuc — same G31, same #5063 skip variable, same #5221–#522n work-offset block. Only the block terminator (;) and the millisecond timers differ from Fanuc habits.

(TOUCH TOP OF PART, SET G54 Z0)
G90 G31 Z-25. F200 ;      (feed until the skip signal comes in)
#5223 = #5223 + #5063 ;   (#5063 = Z skip position, workpiece coords)
G00 Z25. ;                (retract)

Mazak (EIA/ISO)

Mazak’s EIA macro language reads captured positions from the same numbers (#5061–#5076 skip, #5221+ work offsets share Fanuc’s numbering). Real Mazak Renishaw programs read #5041/#5043 the same way.

(TOUCH TOP OF PART, SET G54 Z0)
G90 G31 Z-25. F50           ; skip move down until the probe input fires
#5223 = #5223 + #5063       ; #5063 = present skip-signal Z
G00 Z25.                    ; retract

Siemens (SINUMERIK)

A measuring move captures the trigger position into $AA_MW[Z] (workpiece coords); write it into the G54 frame $P_UIFR[1,Z,TR] and re-select G54 to activate the change. STOPRE ensures the measured value is available before the write.

MEAS=+1 G01 Z-25 F300      ; probe down, trigger on probe 1
STOPRE                     ; make the measured value current
$P_UIFR[1,Z,TR] = $P_UIFR[1,Z,TR] + $AA_MW[Z]  ; shift G54 Z to the face
G54                        ; re-select G54 to apply the new frame
G0 Z25                     ; retract

Heidenhain (TNC 640)

There is no FN 17 write to a live datum — a touch-probe cycle does the write itself, straight into the preset table. Q305 selects the preset-table row, Q303 selects transfer of the measured value into the preset table. (To store into a datum table instead, probe first, then push the value with FN 27: TABWRITE or SQL UPDATE.)

; Probe the top face in the tool axis and set it as the datum.
; The cycle writes PRESET.PR directly - no FN 17 SYSWRITE (OEM-locked).
5 TCH PROBE 417 DATUM IN TS AXIS ~
Q305=+0    ; preset-table row to update (0 = active preset)
Q303=+1    ; transfer the measured value into the preset table
6 CYCL CALL
; To verify afterward, read the active preset row back:
7 FN 18: SYSREAD Q1 = ID530 NR1      ; number of the active preset

Okuma (OSP)

A Renishaw single-surface cycle (O9811) probes the face and stores the measured Z in VS77; add that to the work-zero offset for offset set 1, VZOFZ[1]. (The protected cycle can also update the offset for you when given a work-offset argument; the manual read-then-write below shows the mechanism.)

(PROBE TOP FACE WITH RENISHAW SINGLE-SURFACE CYCLE)
CALL O9811                    (measured Z surface lands in VS77)
VZOFZ[1] = VZOFZ[1] + VS77    (shift work-offset set 1 in Z to the face)

Indirect & Computed Access

The real power of system variables is reaching one whose number or index is computed at run time — e.g. “the length offset for whatever tool is active” without hard-coding the tool.

ControlIndirect formExample
Fanuc / Haas#[ expression ]#104 = #[11000 + #4111] — geometry length offset for the active H number
Mitsubishi#[ expression ]#104 = #[10000 + #11] — tool length offset indexed by an argument
Mazak#[ expression ], tool data by index#104 = #[40000 + #3020] — length for the spindle tool’s data index
Siemensbracket index, R[R n], $AC_PARAM[$AC_MARKER[n]]R[R2] = 1; $P_UIFR[$P_UIFRNUM, Z, TR] — the active frame
HeidenhainIDX index on SYSREAD; QS-driven KEY / labelsFN 18: SYSREAD Q5 = ID50 NR1 IDX+Q10; TABDATA READ Q1 = … KEY "Q10"
Okumavariable index in bracketsVZOFZ[VC1] — offset set chosen by a common variable

Argument Passing — Getting Data Into a Macro

The flip side of reading control data is handing your own values to a subprogram. Each dialect does it differently:

  • Fanuc / Haas / Mitsubishi / Mazak — G65 letter→variable. A macro call maps address letters to local variables: A→#1 B→#2 C→#3 D→#7 F→#9 H→#11 I→#4 R→#18 X→#24 Y→#25 Z→#26…. G65 P8010 A15. B40. H6 lands 15 in #1, 40 in #2, 6 in #11. A fresh local set is created per call level. See the Fanuc, Mitsubishi, and Mazak articles.
  • Siemens — PROC with typed parameters. A subprogram declares its own signature: PROC DRILL(REAL DEPTH, INT COUNT), called as DRILL(-12.5, 6). R-parameters and GUD provide the shared, named alternative. See SINUMERIK R-Parameters & System Variables.
  • Heidenhain — no positional arguments; Q parameters are global. CALL LBL "UP1" and CALL PGM … pass nothing directly — Q parameters remain globally effective across program calls, so you set Q1… before the call and the subprogram reads them. Local scratch uses QL parameters. See Klartext & Q-Parameters.
  • Okuma — named local variables on CALL. CALL O1234 AA11=1.234 BB22=BB22 PC33=1 creates named locals in the subprogram; a leading P (PC33) marks an optional argument testable with EMPTY. See Okuma Argument & Local Variables.

Each Control’s Full Reference

Open the Macro Playground — read and write system variables interactively across seven controls.

References

  • Fanuc, Operator’s Manual / Parameter Manual, FANUC Corporation.
  • Haas, Operator’s Manual / NGC Programming Guide, Haas Automation, Inc.
  • Mitsubishi Electric, CNC 700/70 Series Programming Manual (Machining Center System), IB-1500072.
  • Yamazaki Mazak, Programming Manual (Machining Centers) — EIA/ISO, MAZATROL SmoothAi, H747PB1000E.
  • Siemens, SINUMERIK System Variables Parameter Manual and Programming Guide: Job Planning, Siemens AG.
  • HEIDENHAIN, TNC 640 Klartext Programming User’s Manual, NC software 34059x-11.
  • Okuma, OSP Programming Manual, Okuma Corporation; Renishaw, Inspection Plus Programming Manual, Renishaw plc.

Have a question or want to contribute?

Contact us with corrections, additions, or topics you'd like covered.

Get in Touch