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.
| Purpose | Python | Fanuc macro |
|---|---|---|
| Add, subtract | a + b, a - b | #1+#2, #1-#2 |
| Multiply, divide | a * b, a / b | #1*#2, #1/#2 |
| Grouping | (a + b) * c | [#1+#2]*#3 |
| Square root | math.sqrt(a) | SQRT[#1] |
| Absolute value | abs(a) | ABS[#1] |
| Sine, cosine | math.sin(x), math.cos(x) | SIN[#1], COS[#1] |
| Arc tangent from two values | math.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
| Meaning | Python | Fanuc macro |
|---|---|---|
| equal | a == b | #1 EQ #2 |
| not equal | a != b | #1 NE #2 |
| greater | a > b | #1 GT #2 |
| greater or equal | a >= b | #1 GE #2 |
| smaller | a < b | #1 LT #2 |
| smaller or equal | a <= b | #1 LE #2 |
| both true | a and b | [...] AND [...] |
| at least one true | a 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=3The 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
END1Python’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
END1Functions 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
| Purpose | Python | Fanuc macro |
|---|---|---|
| Cut off decimals | int(a) | FIX[#1] |
| Round up to a whole number | math.ceil(a) | FUP[#1] |
| Round to nearest | round(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
M99Line 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.