Code Library
CNC ProgrammingG-code (Macro B)FANUC control (or builder supporting Macro B) · G65 custom macroMIT licenseAdvanced

Bolt-circle drilling in FANUC Macro B (G65 custom macro)

The Python bolt-circle generator writes a finished program; this is the same drilling pattern living inside the control as a callable custom macro — G65 P2000 A40. B8 Z-12. F120. S2400 D0. E0. and the control computes every hole itself. Same math, same G81/G83 modal trick, FANUC's own control flow instead of a Python for loop — and it validates its arguments before cutting anything.

Before you run it

  • A FANUC control (or Haas/other builder supporting Macro B custom macros) with custom macro B enabled
  • Loaded as its own subprogram (O2000), called via G65 P2000 with the arguments shown
  • A dry run above the part before the first real cut — every time, no exceptions

The code

GitHub
O2000 (BOLT CIRCLE - MACRO B)
(CALL:  G65 P2000 A40. B8 Z-12. F120. S2400 D0. E0.)
(A=RADIUS  B=HOLE COUNT  Z=DEPTH(NEGATIVE)  F=FEED  S=RPM)
(D=PECK INCREMENT, 0 OR OMITTED = PLAIN G81  E=START ANGLE, DEG, DEFAULT 0)

IF[#1 NE #0]GOTO10
#3000=1(BOLT CIRCLE: A/RADIUS REQUIRED)
N10
IF[#2 NE #0]GOTO20
#3000=1(BOLT CIRCLE: B/HOLE COUNT REQUIRED)
N20
IF[#26 NE #0]GOTO30
#3000=1(BOLT CIRCLE: Z/DEPTH REQUIRED)
N30
IF[#8 NE #0]GOTO40
#8=0
N40

#101=#8
#102=0

G90 G94 G17 G21
S#19 M3
G43 H01 Z25.

WHILE[#102 LT #2]DO1
  #110=#1*COS[#101]
  #111=#1*SIN[#101]
  IF[#102 EQ 0]GOTO50
  X#110 Y#111
  GOTO70
  N50
  IF[#7 EQ #0]GOTO60
  G98 G83 X#110 Y#111 Z#26 R2. Q#7 F#9
  GOTO70
  N60
  G98 G81 X#110 Y#111 Z#26 R2. F#9
  N70
  #101=#101+360/#2
  #102=#102+1
END1

G80
G0 Z25.
M5
M99

What you get

What you get

Call:  G65 P2000 A40. B8 Z-12. F120. S2400 D0. E0.

The control moves through the same 8-hole bolt circle as the
Python generator (radius 40, holes 8) - only here every X/Y/Z
motion happens live, computed by the control itself. Nothing to
copy into a program: G65 IS the program.

How it works

  • Argument letters map to fixed variable numbers under FANUC's Argument Specification I — A#1, B#2, Z#26, F#9, S#19 — which is why the call line and the variable numbers inside the macro don't visually match up. That mapping is the first thing to memorize.L6–17
  • COS[#101]/SIN[#101] take degrees, matching #101's own units — Macro B trig is degrees-native, the opposite of nearly every general-purpose language.L27–28
  • WHILE[#102 LT #2]DO1 ... END1 is the loop; the IF[#102 EQ 0] inside it is exactly the same "first hole gets the cycle, the rest are modal X Y" logic as the Python generator — same idea, FANUC's own control flow instead of a for loop.L26–41
  • #3000=1(...) is FANUC's built-in way to fail loud: it raises a custom alarm with the parenthetical text as the message, so a missing required argument stops the program with a readable reason instead of cutting air at X0 Y0.

Gotchas & honest limits

  • COS/SIN take degrees in Macro B, unlike most languages that expect radians — that's the reason this reads naturally as 360/#2 instead of needing a manual conversion.
  • #3000=1(MESSAGE) raises a custom alarm (3001, with the message shown) and stops execution — the FANUC-native way to fail loud on a bad call, instead of silently cutting air at X0 Y0.
  • Argument letters map to fixed variable numbers by control design, not by choice — getting a letter wrong silently reads the wrong variable rather than erroring.
  • This is Macro B, the common FANUC/Haas dialect; Siemens/Heidenhain custom-cycle syntax is completely different. The shape of the logic (loop, first-hole-gets-cycle) transfers; the syntax doesn't.

Goes deeper

Want this adapted to your shop — or built into a real tool?

Samples are the free 80%. The last 20% is the part I do for a living.

Get in touch
Home
Blog
Tools
Code
Email
LinkedIn
Résumé