nx_pcb_cam.nx source
↩ module page · 128 lines · 5629 B
1// nx_pcb_cam.nx -- SOVEREIGN PCB isolation-milling CAM: copper geometry -> CNC G-code toolpaths.
2// This CLOSES THE IN-HOUSE LOOP of the Omniforge "chips" leg: instead of sending Gerber to an
3// external fab, we mill the board ourselves on a CNC (or a 3D-printer-derived mill) by cutting
4// AWAY the copper around each trace/pad so the remaining copper is electrically isolated.
5// The core operation is geometric OFFSETTING: each trace centreline is offset perpendicular by
6// (copper_half_width + tool_radius) to both sides; each pad gets a square isolation ring at
7// (pad_radius + tool_radius). INTEGER-ONLY / NO-FLOAT: coordinates in micrometers, perpendicular
8// normals via an inlined integer sqrt (proven Newton idiom, exact for axis-aligned segments),
9// coordinates formatted to decimal mm with the SHARED g_mm6 from nx_pcb_gerber (DRY). Reuses the
10// same toolpath family as the slicer/CAM. NEVER-BRICK (#26): emits G-code FILES only; no machine
11// control / firmware writes here. license_tier: ORIGINAL expect_exit: 0
12import "nx_syscalls.nx"
13import "nx_pcb_gerber.nx"
14const K_MAGIC_12000: i64 = 12000
15
16// proven self-contained integer sqrt (Newton); exact when n is a perfect square (axis-aligned dx/dy)
17func cam_isqrt(v: i64) -> i64 {
18 if v <= 0 { return 0 }
19 if v < 4 { return 1 }
20 var x: i64 = v
21 var y: i64 = (x + 1) >> 1
22 var go: i64 = 1
23 while go == 1 { if y < x { x = y; y = (x + v / x) >> 1 } else { go = 0 } }
24 return x
25}
26
27// append a signed Z in mm: "Z<mm>" or "Z-<mm>"
28func cam_z(buf: *u8, len: *i64, z_um: i64) -> i64 {
29 g_puts(buf, len, "Z" as *u8)
30 if z_um < 0 { g_puts(buf, len, "-" as *u8); g_mm6(buf, len, 0 - z_um) }
31 else { g_mm6(buf, len, z_um) }
32 return 0
33}
34
35// "G0 X<mm> Y<mm>\n" rapid traverse
36func cam_rapid(buf: *u8, len: *i64, x_um: i64, y_um: i64) -> i64 {
37 g_puts(buf, len, "G0 X" as *u8); g_mm6(buf, len, x_um)
38 g_puts(buf, len, " Y" as *u8); g_mm6(buf, len, y_um)
39 g_puts(buf, len, "\n" as *u8)
40 return 0
41}
42
43// "G1 X<mm> Y<mm> F<feed>\n" cutting move
44func cam_cut(buf: *u8, len: *i64, x_um: i64, y_um: i64, feed: i64) -> i64 {
45 g_puts(buf, len, "G1 X" as *u8); g_mm6(buf, len, x_um)
46 g_puts(buf, len, " Y" as *u8); g_mm6(buf, len, y_um)
47 g_puts(buf, len, " F" as *u8); g_puti(buf, len, feed)
48 g_puts(buf, len, "\n" as *u8)
49 return 0
50}
51
52// plunge to cut depth: "G1 Z-<depth> F<plunge>\n"
53func cam_plunge(buf: *u8, len: *i64, cut_depth_um: i64, plunge: i64) -> i64 {
54 g_puts(buf, len, "G1 " as *u8); cam_z(buf, len, 0 - cut_depth_um)
55 g_puts(buf, len, " F" as *u8); g_puti(buf, len, plunge)
56 g_puts(buf, len, "\n" as *u8)
57 return 0
58}
59
60// retract to safe Z: "G0 Z<safe>\n"
61func cam_retract(buf: *u8, len: *i64, safe_z_um: i64) -> i64 {
62 g_puts(buf, len, "G0 " as *u8); cam_z(buf, len, safe_z_um); g_puts(buf, len, "\n" as *u8)
63 return 0
64}
65
66// one straight isolation pass: rapid to start, plunge, cut to end, retract
67func cam_pass(buf: *u8, len: *i64, x1: i64, y1: i64, x2: i64, y2: i64,
68 cut_depth: i64, safe_z: i64, feed: i64, plunge: i64) -> i64 {
69 cam_rapid(buf, len, x1, y1)
70 cam_plunge(buf, len, cut_depth, plunge)
71 cam_cut(buf, len, x2, y2, feed)
72 cam_retract(buf, len, safe_z)
73 return 0
74}
75
76// one closed rectangular isolation ring around a pad
77func cam_ring(buf: *u8, len: *i64, x0: i64, y0: i64, x1: i64, y1: i64,
78 cut_depth: i64, safe_z: i64, feed: i64, plunge: i64) -> i64 {
79 cam_rapid(buf, len, x0, y0)
80 cam_plunge(buf, len, cut_depth, plunge)
81 cam_cut(buf, len, x1, y0, feed)
82 cam_cut(buf, len, x1, y1, feed)
83 cam_cut(buf, len, x0, y1, feed)
84 cam_cut(buf, len, x0, y0, feed) // close the loop
85 cam_retract(buf, len, safe_z)
86 return 0
87}
88
89// Emit isolation-milling G-code for a board into out[]; return byte length.
90// tr_hw[i] = copper half-width of trace i (um). pad_r[i] = pad radius (um).
91func cam_emit(
92 tr_hw: *i64, tr_x1: *i64, tr_y1: *i64, tr_x2: *i64, tr_y2: *i64, n_tr: i64,
93 pad_r: *i64, pad_x: *i64, pad_y: *i64, n_pad: i64,
94 tool_dia_um: i64, cut_depth: i64, safe_z: i64, feed: i64, plunge: i64,
95 out: *u8) -> i64 {
96 let len: *i64 = sys_mmap(8) as *i64
97 len[0] = 0
98 let trad: i64 = tool_dia_um / 2
99 g_puts(out, len, "G04 Nishi Omniforge sovereign isolation-mill CAM*\n" as *u8)
100 g_puts(out, len, "G21\n" as *u8) // millimetres
101 g_puts(out, len, "G90\n" as *u8) // absolute
102 g_puts(out, len, "G94\n" as *u8) // feed per minute
103 g_puts(out, len, "M3 S" as *u8); g_puti(out, len, K_MAGIC_12000); g_puts(out, len, "\n" as *u8) // spindle on
104 cam_retract(out, len, safe_z)
105 var i: i64 = 0
106 while i < n_tr {
107 let dx: i64 = tr_x2[i] - tr_x1[i]
108 let dy: i64 = tr_y2[i] - tr_y1[i]
109 let ln: i64 = cam_isqrt(dx * dx + dy * dy)
110 let off: i64 = tr_hw[i] + trad
111 var nxo: i64 = 0
112 var nyo: i64 = 0
113 if ln > 0 { nxo = (0 - dy) * off / ln; nyo = dx * off / ln }
114 cam_pass(out, len, tr_x1[i] + nxo, tr_y1[i] + nyo, tr_x2[i] + nxo, tr_y2[i] + nyo, cut_depth, safe_z, feed, plunge)
115 cam_pass(out, len, tr_x1[i] - nxo, tr_y1[i] - nyo, tr_x2[i] - nxo, tr_y2[i] - nyo, cut_depth, safe_z, feed, plunge)
116 i = i + 1
117 }
118 i = 0
119 while i < n_pad {
120 let h: i64 = pad_r[i] + trad
121 cam_ring(out, len, pad_x[i] - h, pad_y[i] - h, pad_x[i] + h, pad_y[i] + h, cut_depth, safe_z, feed, plunge)
122 i = i + 1
123 }
124 g_puts(out, len, "M5\n" as *u8) // spindle off
125 cam_retract(out, len, safe_z)
126 g_puts(out, len, "M2\n" as *u8) // program end
127 return len[0]
128}