Fanuc Macro B: variables, IF, WHILE and G65 explained
Fanuc Custom Macro B for machinists: variable types, calculations, IF and WHILE, macro calls with G65 and G66, argument letters, system variables and a complete bolt circle cycle.
Fanuc Custom Macro B, usually just called Macro B, is the macro language of Fanuc controls and of many controls that copy the Fanuc dialect. This page explains the parts you need for everyday work. The general ideas behind variables, conditions and loops are explained in Control flow basics.
On older controls Macro B is an option. If the offset screen shows a page for macro variables, the function is installed. The available variable ranges and some details depend on the control generation and the machine builder’s settings. When in doubt, test with harmless values in MDI first.
Variables
A variable is written as # followed by a number. The number decides what kind of variable it is.
| Number | Type | Behavior |
|---|---|---|
| #0 | Always empty | Can be read, never written. Used to check whether another variable is empty |
| #1 to #33 | Local | Belong to one macro call. Arguments of a G65 call land here. Empty at the start of every call |
| #100 to #199 | Common, volatile | Shared by all programs. Cleared when the control is switched off |
| #500 to #999 | Common, retained | Shared by all programs. Keep their value after power-off |
| #1000 and higher | System | Access to machine data: offsets, positions, modal states, alarms, timers |
On older or smaller controls only part of the common ranges may exist, for example #100 to #149 and #500 to #531. Common variables can be viewed and edited on the macro variable page of the offset screen.
Empty and zero
An empty variable (null) and a variable with the value 0 behave differently in some places.
| Situation | #1 empty | #1 = 0 |
|---|---|---|
G01 X50. Y#1 | Y is left out, only X moves | Y moves to 0 |
#2=#1 | #2 becomes empty | #2 becomes 0 |
#2=#1+5 | #2 becomes 5 (empty counts as 0 in calculations) | #2 becomes 5 |
IF[#1 EQ #0] | true | false |
IF[#1 EQ 0] | false | true |
IF[#1 GT 0] | false | false |
Only EQ and NE tell empty and zero apart. All other comparisons treat empty as zero. To check that an argument was given, compare it with #0.
Variables in NC words
Most addresses accept a variable instead of a number. A minus sign in front of the # negates the value.
#24=35.5
#26=12.
G01 X#24 Z-#26 F#9
G#10 X[#24+5.]Rules to know:
- A calculation inside an address needs square brackets:
X[#24+5.]. - O, N and the block skip number after / cannot be variables.
- The control rounds the value to the resolution of the address. With 0.001 mm resolution, 12.34567 becomes 12.346.
- A variable number can be calculated:
#[100+#1]is the variable whose number is 100 plus the value of #1.
Calculating
An assignment has the form #i=<expression>. Constants without a decimal point are treated as whole numbers with a decimal point, so #1=12 is the same as #1=12.0.
| Operation | Example | Note |
|---|---|---|
| Add, subtract | #3=#1+#2, #3=#1-#2 | |
| Multiply, divide | #3=#1*#2, #3=#1/#2 | Division by zero stops with an alarm |
| Remainder | #3=#1 MOD #2 | Both values are rounded to whole numbers first |
| Sine, cosine, tangent | SIN[#1], COS[#1], TAN[#1] | Angle in degrees, 30’ = 0.5° |
| Inverse functions | ASIN[#1], ACOS[#1], ATAN[#1] | Result in degrees |
| Angle from two values | ATAN[#2]/[#1] | Like ATAN2: Y value first, X value second |
| Square root | SQRT[#1] | |
| Absolute value | ABS[#1] | |
| Round | ROUND[#1] | Nearest whole number in calculations |
| Cut off decimals | FIX[#1] | Towards zero: 1.7 gives 1, −1.7 gives −1 |
| Round up | FUP[#1] | Away from zero: 1.2 gives 2, −1.2 gives −2 |
| Power, logarithm | POW[#1,#2], LN[#1], EXP[#1] | Newer controls |
| Bitwise logic | AND, OR, XOR | For PLC signals and bit patterns |
Functions are calculated first, then multiplication and division, then addition and subtraction. Square brackets change the order and can be nested up to five levels. Round brackets are comments.
#5=#1+#2*SIN[#3] (SINE FIRST, THEN *, THEN +)
#6=[#1+#2]*SIN[#3] (BRACKET FIRST)Precision traps
The control calculates with binary numbers. Results that should be whole can come out a tiny bit too small. FIX then cuts off a whole unit. Two habits avoid most problems:
#3=FIX[#2+0.0001] (SAFETY MARGIN BEFORE CUTTING OFF)
IF[ABS[#1-#2] LT 0.001] GOTO 20 (EQUAL WITHIN 0.001 MM)Inside an NC address the control rounds to the resolution of the axis. If a program adds rounded and unrounded values, the tool can end up a few thousandths away from where it started. Round each value with ROUND before adding when exact return positions matter.
Jumps and conditions
GOTO
GOTO n continues the program at the block with sequence number Nn. The number can also come from a variable: GOTO #10.
- The N number must stand at the start of the target block.
- Each N number may exist only once in a program. With duplicates the jump target is undefined.
- Jumping backwards takes longer than jumping forwards, because the control searches the program. For repetitions use WHILE.
IF with GOTO
IF[#26 LE 0] GOTO 900If the condition is true, the program jumps to N900. Otherwise it continues with the next block.
IF with THEN
IF[#11 EQ #0] THEN #11=4If the condition is true, the single macro statement after THEN is executed. THEN cannot be followed by a move or a list of several statements.
Comparison operators
| Operator | Meaning |
|---|---|
| EQ | equal |
| NE | not equal |
| GT | greater than |
| GE | greater than or equal |
| LT | less than |
| LE | less than or equal |
Conditions can be combined with AND, OR and XOR. Each single condition gets its own brackets:
IF[[#24 GT 0] AND [#24 LT 100]] GOTO 10ELSE with jumps
Macro B has no ELSE. A two-way decision uses two jumps:
IF[#17 GT 3.] GOTO 10
#30=200. (FEED FOR SMALL PECK)
GOTO 20
N10 #30=140. (FEED FOR LARGE PECK)
N20 (BOTH CASES CONTINUE HERE)WHILE loops
WHILE[<condition>] DO1
(BLOCKS TO REPEAT)
END1While the condition is true, the blocks between DO and END repeat. When it becomes false, the program continues after END.
- The loop number after DO and END is 1, 2 or 3. It pairs each DO with its END.
- Loops can be nested three levels deep. The inner loop needs a different number than the outer one.
- The same number can be used again for a later loop that does not overlap.
- Two loops must not cross each other: the inner END must come before the outer END.
- A GOTO may jump out of a loop, never into one.
- DO without WHILE runs forever. This is only useful together with a GOTO that leaves the loop.
#20=0 (ROW COUNTER)
WHILE[#20 LT 3] DO1
#21=0 (COLUMN COUNTER)
WHILE[#21 LT 5] DO2
X[#21*15.] Y[#20*10.] (ACTIVE DRILL CYCLE DRILLS HERE)
#21=#21+1
END2
#20=#20+1
END1This drills a grid of 3 rows and 5 columns with 15 mm and 10 mm spacing, using a drilling cycle that was activated before.
Calling macros
M98 or G65
| M98 Pxxxx | G65 Pxxxx | |
|---|---|---|
| Type | Subprogram call | Macro call |
| Arguments | No | Yes, with letters |
| Local variables #1 to #33 | Shared with the calling program | New, empty set for each call |
| Other commands in the same block | Allowed, executed first | Not allowed, alarm |
| Single block stop at the call | Yes | No |
Both end with M99, which returns to the block after the call.
G65 with arguments
G65 P9100 L1 X0 Y0 R40. H6. A15.P is the program number, L the number of repetitions (default 1). All other letters are arguments. Each letter writes its value into a fixed local variable:
| Letter | Variable | Letter | Variable | Letter | 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 |
G, L, N, O and P cannot be used as arguments. Letters that are not given leave their variable empty. A second assignment scheme allows I, J and K up to ten times for passing several points, it is used rarely.
Always write arguments with a decimal point. Depending on the settings, X10 without a point can arrive in the macro as 0.01 or as 10, while X10. always arrives as 10.
Modal call with G66
G66 P9200 Z-8. R2. F120.
G00 X20. Y10.
X50.
X80. Y30.
G67After G66 the macro runs after every block with an axis move, with the arguments from the G66 block. G67 ends the modal call. This is how you build your own drilling-type cycles.
Own G and M codes
Parameters can link a G or M code to a macro program. Parameters 6050 to 6059 assign G codes to programs O9010 to O9019. Parameters 6080 to 6089 assign M codes to programs O9020 to O9029. With 6050 set to 130, the block G130 X0 Y0 R40. H6. calls O9010 with these arguments, exactly like G65 P9010. Changing parameters is a job for someone who knows the machine; check with the machine builder first.
Macro calls can be nested up to four or five levels deep, depending on the control. Each level has its own set of local variables.
Protecting macro programs
Programs O9000 to O9999 can be protected against editing with bit 4 (NE9) of parameter 3202. Finished cycles are usually stored there so that nobody changes them by accident.
Macro blocks and single block
Blocks with a calculation, a jump, a loop or a macro call are macro statements. All other blocks are NC statements. In single block mode the machine stops after each NC statement, but by default it runs through macro statements without stopping. Bit 5 (SBM) of parameter 6000 makes the control stop after macro statements as well, which helps when testing a new macro step by step.
The control also reads macro statements ahead while the machine is still moving. A calculation can therefore be done before the move above it has finished. When a value from the machine must be read after a move, for example a position or a probe result, the read has to wait until the move is done. A dwell or a block that stops read-ahead in between solves this.
System variables
System variables connect the macro to the machine. The tables show the most used ones on Fanuc milling controls. Some numbers differ between control generations and between milling and turning, so check them on your machine before writing to them.
Alarms and messages
| Variable | Use |
|---|---|
| #3000 | #3000=n (TEXT) stops the program with a macro alarm and shows the text. On many controls the alarm number is 3000 plus n |
| #3006 | #3006=1 (TEXT) stops the program and shows the text as a message. Cycle start continues |
IF[#18 LE 0] THEN #3000=1 (RADIUS R MUST BE POSITIVE)
#3006=1 (CHECK CLAMPING THEN CYCLE START)The text should be short, about 26 characters fit on older screens.
Time, counters and run control
| Variable | Use |
|---|---|
| #3001 | Millisecond timer. Starts at 0 at power-on and can be set to 0 by the program |
| #3002 | Hour counter that runs while cycle start is active. Survives power-off |
| #3011 | Date as a number, for example 20261013 |
| #3012 | Time as a number, for example 143005 for 14:30:05 |
| #3003 | Single block and waiting for M/S/T completion on or off |
| #3004 | Feed hold, feed override and exact stop check on or off |
| #3901 | Parts counter (machined parts) |
| #3902 | Required number of parts |
#3001=0
M98 P2000 (MACHINING)
#510=#3001/1000 (CYCLE TIME IN SECONDS)#3003 and #3004 switch off operator functions such as single block or feed hold. Use them only in short sections like tapping, and switch them back on right after.
Modal information
| Variable | Content |
|---|---|
| #4001 to #4030 | Active G code of each group. #4001 is group 1 (G00, G01, …), #4003 group 3 (G90 or G91), #4014 group 14 (work offset 54 to 59) |
| #4107 | Active D number |
| #4109 | Active feed F |
| #4111 | Active H number |
| #4119 | Programmed spindle speed S |
| #4120 | Active tool number T |
These values are read-only. A typical use is to save a state at the start of a macro and restore it at the end:
#30=#4003 (SAVE G90 OR G91)
G91
(INCREMENTAL MOVES)
G#30 (RESTORE)Positions
| Variable | Content |
|---|---|
| #5001 to #5005 | End point of the last block, workpiece coordinates (axes 1 to 5) |
| #5021 to #5025 | Current position in machine coordinates |
| #5041 to #5045 | Current position in workpiece coordinates |
| #5061 to #5065 | Position where the skip signal (probe) switched with G31 |
Axis 1 is usually X, axis 2 Y, axis 3 Z. #5003 is therefore the Z end point of the last block.
Work offsets
| Variable | Offset |
|---|---|
| #5201 to #5205 | External (common) offset |
| #5221 to #5225 | G54 |
| #5241 to #5245 | G55 |
| #5261 to #5265 | G56 |
| #5281 to #5285 | G57 |
| #5301 to #5305 | G58 |
| #5321 to #5325 | G59 |
| #7001 to #7005 | G54.1 P1 |
| #7021 to #7025 | G54.1 P2, and so on in steps of 20 |
Each offset starts 20 numbers after the previous one. More on this topic, including a calculated address for G54 to G59, is on the page Fanuc work offset parameters.
Tool offsets
The variable numbers depend on the offset memory type of the control. On most machining centers with separate values for length and radius and for geometry and wear (memory type C):
| Variables | Content |
|---|---|
| #11001 and up | Length geometry (H) |
| #10001 and up | Length wear (H) |
| #13001 and up | Radius geometry (D) |
| #12001 and up | Radius wear (D) |
The last digits are the offset number: #11005 is the length geometry of H5. Controls with up to 200 offsets also accept the older numbers starting at #2001.
#1=#4120 (ACTIVE TOOL)
#2=#[11000+#1] (ITS LENGTH, IF H NUMBER = T NUMBER)
IF[#2 GT 50.] GOTO 10
#3000=2 (TOOL LENGTH TOO SHORT)
N10Example: bolt circle cycle with input check
The macro drills a bolt circle with the drilling cycle that is active when it is called. It checks the inputs first and leaves the machine in the same state it found it.
O1000 (MAIN PROGRAM)
G90 G54 G17
T5 M06
G43 H5 Z50.
S1800 M03
G98 G81 Z-12. R2. F160 K0 (STORE CYCLE, NO HOLE)
G65 P9100 X60. Y40. R35. H8. A22.5
G65 P9100 X160. Y40. R20. H4.
G80
G28 G91 Z0
M30O9100 (BOLT CIRCLE)
(X=#24 Y=#25 CENTER, R=#18 RADIUS)
(H=#11 HOLES, A=#1 START ANGLE)
IF[#24 EQ #0] GOTO 900
IF[#25 EQ #0] GOTO 900
IF[#18 LE 0] GOTO 910
IF[#11 LT 1] GOTO 920
IF[#1 EQ #0] THEN #1=0
#30=#4003 (SAVE G90/G91)
#10=360./#11 (ANGLE STEP)
#12=0 (COUNTER)
G90
WHILE[#12 LT #11] DO1
#13=#1+#12*#10
X[#24+#18*COS[#13]] Y[#25+#18*SIN[#13]]
#12=#12+1
END1
G#30 (RESTORE G90/G91)
M99
N900 #3000=1 (CENTER X Y MISSING)
N910 #3000=2 (RADIUS R MISSING)
N920 #3000=3 (HOLES H MISSING)What happens:
- The first checks stop the program with a clear alarm if an argument is missing. An empty H also stops at N920, because empty counts as 0 in the LT comparison.
- A missing start angle is not an error, it is set to 0.
- The macro saves G90 or G91, switches to absolute and restores the old state at the end.
- Each X/Y block triggers the drilling cycle that is still active from the main program.
- The alarm blocks sit after M99 and are only reached by the jumps.
Tips for the shop
- Keep a list of every common variable you use and what it means. Write the meaning as a comment in the program where the variable is set.
- Use the retained range #500 and up only for values that must survive power-off, and back them up with the other machine data.
- Check all arguments at the start of a macro. A clear alarm costs nothing, a wrong position costs a part or a spindle.
- Test new macros with the feed override at a low value and with single block, and with parameter 6000 bit 5 set if you need to stop at macro blocks.
- Write arguments and constants with a decimal point.
- Store finished cycles in the O9000 range and protect them.
A practical application of Macro B with system variables and a little geometry is shown in Building your own tilt cycle.