What is a custom macro, and how does it differ from a subprogram?
A subprogram (M98) repeats the same moves. A custom macro lets the program calculate, decide and loop. The difference in one line: a subprogram always makes the same part; a macro makes a different part depending on the dimensions you give it.
A concrete example: a bolt circle with 12 holes. With a subprogram you write 12 sets of coordinates. With a macro you supply the diameter and the number of holes, and the control works out the coordinates. When the diameter changes you change one number.
Variables: four different kinds on a FANUC
A variable is written with #. The number you use determines where the variable is valid and whether it survives a power cycle. Not knowing that distinction is the most common mistake on the shop floor.
| Number | Type | Where is it valid? | What happens at power off? |
|---|---|---|---|
| #0 | Null | Everywhere | Always null, cannot be written, read only |
| #1 – #33 | Local variable | Only inside that macro | Cleared. Another macro using #1 cannot corrupt yours. |
| #100 – #199 | Common variable (volatile) | Main program, subprograms and all macros | Cleared — reset when power is removed |
| #500 – #999 | Common variable (retained) | Main program, subprograms and all macros | Retained — the value survives a power cycle |
| #1000 and above | System variable | Everywhere | The control’s own data: offsets, position, alarms, clock, parameters |
#500–#999. Put them in #100–#199 and they reset when the machine is switched off, which you usually discover the next morning.A note from the source manual: depending on bit 6 (NCV) of parameter 8135, the ranges #150–#199 and #550–#999 become usable. Variables in #500–#999 can also be made write protected (read only) by parameter.
Using a variable in place of an address
The value following an address can be replaced by a variable:
The rules, straight from the source
- Addresses O and N cannot take a variable.
O#27orN#1is not allowed. - The n in an optional block skip
/ncannot be a variable. - A variable number cannot be specified by a direct variable.
##30is wrong; write#[#30]. - No value exceeding the maximum allowable for an address can be specified. With
#140=10000,G#140is out of range. - Used as address data, a variable is rounded automatically. On an IS-B machine (1/1000 mm),
#1=12.3456makesG00 X#1becomeG00 X12.346. - The decimal point can be omitted:
#1=123gives a value of 123.000. - Expressions are allowed:
X[#24+#18*COS[#1]]orZ-[#18+#26].
The null variable trap
This causes more trouble than anything else when writing macros. A variable whose value has not been defined is called null. #0 and #3100 are always null.
1. When a null variable is quoted, the address itself is ignored
| Written | Executed when #1 is null | Executed when #1 = 0 |
|---|---|---|
| G90 X100 Y#1 | G90 X100 — no Y at all | G90 X100 Y0 |
A null variable is not the same as zero. The Y axis does not move, and no alarm is raised — it is simply skipped. This is exactly what happens when you forget to pass an argument to a macro.
2. Behaviour in assignment and arithmetic
| Expression | Result when #1 is null | Result when #1 = 0 |
|---|---|---|
| #2 = #1 (local) | null | 0 |
| #2 = #1 * 5 | 0 | 0 |
| #2 = #1 + #1 | 0 | 0 |
| #2001 = #1 (system variable) | 0 | 0 |
Assign a null value directly and it stays null; put it through any calculation and it becomes 0. Assigned to a system variable it always becomes 0.
3. Null in comparisons
The manual’s own wording: null differs from 0 only for EQ and NE. For GE, GT, LE and LT, null is treated as equal to 0.
IF [#1 EQ #0] GOTO 99 — not IF [#1 EQ 0]. The second one also fires when the user really did enter 0.Arithmetic operations and functions
| Operation | Syntax | Notes |
|---|---|---|
| Add / subtract | #i = #j + #k · #i = #j - #k | |
| Multiply / divide | #i = #j * #k · #i = #j / #k | Division by zero raises PS0112 |
| Sine / cosine / tangent | SIN[] · COS[] · TAN[] | Angles are in degrees, not radians |
| Arc tangent | ATAN[]/[] | Angle from the ratio of two values |
| Square root | SQRT[] | |
| Absolute value | ABS[] | |
| Rounding | ROUND[] | To the nearest integer |
| Truncate / round up | FIX[] · FUP[] | Discards the fraction / raises to the next integer |
| Natural log / exponential | LN[] · EXP[] | |
| Read a parameter | #i = PRM[#j] | Reads a machine parameter from the program |
SIN[30] returns 0.5. Do not convert to radians.Comparison and logic operators
FANUC macros do not use >, < or =. Two-letter abbreviations are used instead:
| Operator | Meaning | Maths |
|---|---|---|
EQ | Equal to | = |
NE | Not equal to | ≠ |
GT | Greater than | > |
GE | Greater than or equal to | ≥ |
LT | Less than | < |
LE | Less than or equal to | ≤ |
Conditions can be combined with AND and OR:
Branching and looping: GOTO, IF, WHILE
Unconditional branch — GOTO
Conditional branch — IF … GOTO
Conditional assignment — IF … THEN
Looping — WHILE … DO … END
While the condition holds, everything between DO and END repeats. m can only be 1, 2 or 3.
The nesting rule — important
Nested loops may reuse the same number, but the loops must nest completely inside one another. Overlapped DO ranges raise alarm PS0124.
Calling a macro: G65, G66 and G67
G65 — simple call
Calls the macro once and passes the arguments.
G65 must be specified before any argument. It cannot sit in the middle of the line.Argument table (argument specification I)
| Address | Variable | Address | Variable | Address | Variable |
|---|---|---|---|---|---|
| A | #1 | I | #4 | T | #20 |
| B | #2 | J | #5 | U | #21 |
| C | #3 | K | #6 | V | #22 |
| D | #7 | M | #13 | W | #23 |
| E | #8 | Q | #17 | X | #24 |
| F | #9 | R | #18 | Y | #25 |
| H | #11 | S | #19 | Z | #26 |
So in the example above, inside the macro #24 = 100.0, #25 = 50.0, #18 = 30.0, #26 = −50.0 and #9 = 500.
Argument specification II
A, B and C are used once each; I, J and K can be used up to ten times each. This is the method for passing many points, for example a free-form contour. If both methods appear in one block, the type specified last takes effect.
G66 / G67 — modal call
After G66, the macro is called after every block that moves an axis, until G67 cancels it.
This is very useful for hole patterns: define the drilling macro once with G66, then just write the coordinates.
Nesting limits
- Macro calls (including G65 and G66/G66.1) can nest up to five levels.
- Subprogram calls, macro calls included, can nest up to 15 levels.
- Each macro call raises the local variable level by one — the inner macro’s
#1is not the outer one’s.
The mistakes people actually make
Writing persistent data into the #100 series
A part counter or tool life value in #100–#199 resets when the machine is switched off. Anything that must persist belongs in #500–#999.
Treating a null variable as zero
When an argument is not supplied the variable stays null, not zero. If #1 is null in a line reading Y#1, the Y axis does not move at all — and no alarm appears.
Writing ##30
To give a variable number with a variable, write #[#30]. ##30 is invalid.
Using a single equals sign in a comparison
IF [#1 = 10] is not valid. It must be IF [#1 EQ 10].
Overlapping WHILE loops
DO and END ranges must nest completely. A partially overlapping range raises PS0124.
Putting an NC statement and a macro statement in one block
A motion command and a macro expression on the same line raises PS0127. Put the macro expression on its own line.
Using radians in trigonometry
Angles in FANUC macros are in degrees. SIN[30] = 0.5.
Nesting macro calls deeper than five levels
The macro nesting limit is five. Beyond that you get PS0122.
Writing G65 in the middle of a line
G65 must come before all arguments.
Frequently asked questions
Does every FANUC machine have custom macro?
No, it is an option. There are two levels, Custom Macro A and Custom Macro B; B is the common one today. The quickest way to find out is to type #100=1 in MDI and run it — if the option is absent you get an alarm.
What is the practical difference between #100 and #500?
#100–#199 is for temporary arithmetic and clears at power off. #500–#999 is retained and survives a power cycle. Anything that has to carry across shifts — part counts, tool life — always goes in the #500 series.
Can I change tool offsets from the program?
Yes. Tool offsets are read and written through system variables. The numbering varies with the control series and the number of offsets, so check the system variable table in your machine’s manual. Writing a probe measurement result straight into an offset works exactly this way.
Should I use a subprogram or a macro?
If the same moves repeat identically, a subprogram (M98) is enough and simpler. If coordinates have to be calculated as the size changes, or a decision has to be made, you need a macro.
Why can I only use 1, 2 and 3 in a WHILE loop?
The DO number on a FANUC is limited to 1–3, so you can nest at most three levels of loop. In sequential (non-nested) loops the same number can be reused.
How do I protect a macro program?
Macro programs are usually stored under 9000-series program numbers (O9000–O9999), and those programs can be protected from display and editing by parameter. Common variables #500–#999 can also be made read only by parameter.
What is the equivalent of Siemens R parameters?
Roughly the FANUC #100 and #500 series common variables. Siemens additionally allows named variables via DEF; on a FANUC variables have no names, only numbers.
What do I see on screen while a macro runs?
Macro statements produce no motion, so the block display may not appear to advance. Use the control’s macro variable screen to watch the values — it is the most practical diagnostic during commissioning.
Sources
- FANUC — Series 0i-MODEL F, Common to Lathe System / Machining Center System, OPERATOR’S MANUAL, document no. B-64604EN/01: Section 16.1 Variables, 16.2 System variables, 16.5 Arithmetic and logic operation, 16.7 Reading parameters, 16.8 Macro statements and NC statements, 16.9 Branch and repetition (IF, GOTO, WHILE), 16.10 Macro call (G65, G66, G67), 16.13 Registering custom macro programs, 16.16 Restrictions
- FANUC — Series 0i-MODEL F MAINTENANCE MANUAL, B-64605EN/01, Appendix A (for the macro-related PS alarms)