NC macros and computer code: a comparison with Python

Variables, conditions, loops and subprograms in Python and in NC macro syntax side by side, with the pitfalls that catch machinists: degrees vs. radians, brackets, rounding.

NC macro languages are small programming languages. They were designed decades ago and look old-fashioned, but the ideas behind them are the same as in any modern language. This page puts NC macros next to Python, a language that is often used for teaching because it reads almost like English.

You do not need to install or learn Python. The comparison has two goals. If you have seen Python or a similar language before, you can transfer that knowledge directly. If not, the Python lines help to see the structure behind the cryptic NC syntax.

The NC examples use Fanuc Macro B syntax, because it is the most widespread macro language. The details are explained in Fanuc Macro B. For the language-neutral ideas see Control flow basics.

Variables: names and numbers

In Python a variable has a name. In a Fanuc macro it has a number after the # sign.

depth = 12.5
feed = 180
#100=12.5 (DEPTH)
#101=180 (FEED)

Numbers are hard to remember. Write a comment behind every variable when it gets its first value, and keep a list of which number means what. Newer Fanuc controls can address system variables by name as well, but in shop programs numbers are still the rule.

Python decides itself where a variable lives. On a Fanuc control the number range decides it: #1 to #33 are local to one macro, #100 and up are shared by all programs.

Calculating

Arithmetic looks almost the same. The big difference is the brackets.

PurposePythonFanuc macro
Add, subtracta + b, a - b#1+#2, #1-#2
Multiply, dividea * b, a / b#1*#2, #1/#2
Grouping(a + b) * c[#1+#2]*#3
Square rootmath.sqrt(a)SQRT[#1]
Absolute valueabs(a)ABS[#1]
Sine, cosinemath.sin(x), math.cos(x)SIN[#1], COS[#1]
Arc tangent from two valuesmath.atan2(y, x)ATAN[#2]/[#1]
Comment# text(TEXT)

In NC code, round brackets are comments. Everything inside ( ) is ignored by the control. Calculations use square brackets. Writing #3=(#1+#2)*2 is a common beginner error.

Degrees and radians

Python’s sine and cosine expect angles in radians. NC macros work in degrees, because that is what machinists use. In Python you convert first:

x = radius * math.cos(math.radians(30))
#24=#18*COS[30]

If you test a calculation in Python or a spreadsheet and copy the formula into a macro, check the angle unit. A forgotten conversion gives positions that look plausible but are wrong.

Comparisons

MeaningPythonFanuc macro
equala == b#1 EQ #2
not equala != b#1 NE #2
greatera > b#1 GT #2
greater or equala >= b#1 GE #2
smallera < b#1 LT #2
smaller or equala <= b#1 LE #2
both truea and b[...] AND [...]
at least one truea or b[...] OR [...]

The symbols = < > cannot be used in Fanuc conditions. The control expects the two-letter abbreviations.

IF and ELSE

Python marks the block that belongs to an if by indenting it. It has elif for further cases and else for the rest.

if depth > 30:
    peck = 3
elif depth > 10:
    peck = 5
else:
    peck = 0

Fanuc Macro B has two forms of IF. IF [...] THEN executes exactly one statement. IF [...] GOTO n jumps to the block with sequence number Nn. There is no ELSE and no END IF, so the same logic is built with jumps:

IF[#100 GT 30] GOTO 10
IF[#100 GT 10] GOTO 20
#102=0 (NO PECKING)
GOTO 30
N10 #102=3
GOTO 30
N20 #102=5
N30 (CONTINUE)

When only a value has to be set, the short form is easier:

#102=0
IF[#100 GT 10] THEN #102=5
IF[#100 GT 30] THEN #102=3

The order matters here. The last true condition wins, so the strictest case comes last.

Loops

A Python while loop and a Fanuc WHILE loop work the same way. Python uses indentation, Fanuc marks the end with END and a loop number.

current = 0
while current < depth:
    current = current + peck
#110=0
WHILE[#110 LT #100] DO1
#110=#110+#102
END1

Python’s counting loop for hole in range(1, 7): has no direct equivalent. On Fanuc you write the counter yourself:

#120=1
WHILE[#120 LE 6] DO1
(MACHINE HOLE #120)
#120=#120+1
END1

Functions and macro calls

A Python function receives values as parameters and can return a result. A Fanuc macro is a separate program that is called with G65. The values are handed over with letters, and each letter lands in a fixed local variable: X in #24, Y in #25, R in #18, and so on.

def pocket(length, width, depth):
    ...

pocket(80, 50, 12)
G65 P9200 X80. Y50. Z12.
(in O9200: #24=80, #25=50, #26=12)

A macro cannot return a value directly. When the calling program needs a result, the macro writes it into a shared variable such as #150, and the main program reads it from there.

Empty values

In Python a variable can contain None, meaning no value. Fanuc has the same idea, called null or vacant. A local variable is empty when its letter was not given at the call. This is useful for checking whether an argument is missing:

if depth is None:
    raise ValueError("depth missing")
IF[#26 EQ #0] THEN #3000=1 (DEPTH Z MISSING)

#0 is a variable that is always empty. Comparing with #0 asks “is this variable empty?”. Empty and zero are different: an empty variable in a move like X#24 makes the control skip the address completely, while zero moves to X0.

Rounding

PurposePythonFanuc macro
Cut off decimalsint(a)FIX[#1]
Round up to a whole numbermath.ceil(a)FUP[#1]
Round to nearestround(a)ROUND[#1]

Two traps. FUP rounds away from zero, so FUP[-1.2] gives −2, while Python’s math.ceil(-1.2) gives −1. Python’s round(2.5) gives 2, because it rounds a 5 to the nearest even number. The NC control rounds 2.5 up to 3.

Both systems store numbers in binary. A value like 0.002 × 1000 can come out as 1.9999999 instead of 2. Cutting off the decimals then gives 1. Add a small amount before FIX, or compare with a tolerance instead of exact equality:

IF[ABS[#1-#2] LT 0.001] GOTO 50 (#1 AND #2 ARE EQUAL)

Lists

Python has lists to hold many values: depths = [5, 10, 15]. Fanuc has no lists, but a variable number can be calculated. #[100+#1] means “the variable with the number 100 plus the value of #1”. With #1 running from 1 to 3, the program reads #101, #102 and #103 one after the other. This is how macros store tables of values.

What macros can do that Python cannot

Python runs on a computer and moves nothing. A macro runs inside the control and has direct access to the machine. Through system variables it reads and writes tool offsets, work offsets, current positions, active G-codes, the time and part counters, and it can stop the machine with an alarm. This is the real strength of macro programming.

Complete example: bolt circle

The same bolt circle as on the previous page, once in Python and once as a Fanuc macro.

import math

def bolt_circle(cx, cy, radius, holes, start_angle):
    step = 360 / holes
    hole = 1
    while hole <= holes:
        angle = start_angle + (hole - 1) * step
        x = cx + radius * math.cos(math.radians(angle))
        y = cy + radius * math.sin(math.radians(angle))
        print(f"X{x:.3f} Y{y:.3f}")
        hole = hole + 1

bolt_circle(0, 0, 40, 6, 0)

The Python version prints six coordinate pairs. The Fanuc version moves the machine:

O1000 (MAIN PROGRAM)
G90 G54 G17
G00 X0 Y0 Z50.
G98 G81 Z-10. R2. F150 K0 (STORE DRILL CYCLE, NO HOLE)
G65 P9100 X0 Y0 R40. H6. A0
G80
M30

O9100 (BOLT CIRCLE)
(X=#24 Y=#25 CENTER, R=#18 RADIUS)
(H=#11 NUMBER OF HOLES, A=#1 START ANGLE)
#10=360/#11 (ANGLE STEP)
#12=1 (HOLE COUNTER)
WHILE[#12 LE #11] DO1
#13=#1+[#12-1]*#10
#14=#24+#18*COS[#13]
#15=#25+#18*SIN[#13]
X#14 Y#15 (ACTIVE G81 DRILLS HERE)
#12=#12+1
END1
M99

Line by line, the two programs follow the same steps. Only the spelling differs. The next page explains each Fanuc element in detail: Fanuc Macro B.