code wiki / (root) / nx_pcb_cam.nx

nx_pcb_cam.nx source

↩ module page · 121 lines · 5457 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" 14import "nx_vecmath.nx" 15const K_MAGIC_12000: i64 = 12000 16 17// proven self-contained integer sqrt (Newton); exact when n is a perfect square (axis-aligned dx/dy) 18func cam_isqrt(v: i64) -> i64 { return vm_isqrt(v) } 19 20// append a signed Z in mm: "Z<mm>" or "Z-<mm>" 21func cam_z(buf: *u8, len: *i64, z_um: i64) -> i64 { 22 g_puts(buf, len, "Z" as *u8) 23 if z_um < 0 { g_puts(buf, len, "-" as *u8); g_mm6(buf, len, 0 - z_um) } 24 else { g_mm6(buf, len, z_um) } 25 return 0 26} 27 28// "G0 X<mm> Y<mm>\n" rapid traverse 29func cam_rapid(buf: *u8, len: *i64, x_um: i64, y_um: i64) -> i64 { 30 g_puts(buf, len, "G0 X" as *u8); g_mm6(buf, len, x_um) 31 g_puts(buf, len, " Y" as *u8); g_mm6(buf, len, y_um) 32 g_puts(buf, len, "\n" as *u8) 33 return 0 34} 35 36// "G1 X<mm> Y<mm> F<feed>\n" cutting move 37func cam_cut(buf: *u8, len: *i64, x_um: i64, y_um: i64, feed: i64) -> i64 { 38 g_puts(buf, len, "G1 X" as *u8); g_mm6(buf, len, x_um) 39 g_puts(buf, len, " Y" as *u8); g_mm6(buf, len, y_um) 40 g_puts(buf, len, " F" as *u8); g_puti(buf, len, feed) 41 g_puts(buf, len, "\n" as *u8) 42 return 0 43} 44 45// plunge to cut depth: "G1 Z-<depth> F<plunge>\n" 46func cam_plunge(buf: *u8, len: *i64, cut_depth_um: i64, plunge: i64) -> i64 { 47 g_puts(buf, len, "G1 " as *u8); cam_z(buf, len, 0 - cut_depth_um) 48 g_puts(buf, len, " F" as *u8); g_puti(buf, len, plunge) 49 g_puts(buf, len, "\n" as *u8) 50 return 0 51} 52 53// retract to safe Z: "G0 Z<safe>\n" 54func cam_retract(buf: *u8, len: *i64, safe_z_um: i64) -> i64 { 55 g_puts(buf, len, "G0 " as *u8); cam_z(buf, len, safe_z_um); g_puts(buf, len, "\n" as *u8) 56 return 0 57} 58 59// one straight isolation pass: rapid to start, plunge, cut to end, retract 60func cam_pass(buf: *u8, len: *i64, x1: i64, y1: i64, x2: i64, y2: i64, 61 cut_depth: i64, safe_z: i64, feed: i64, plunge: i64) -> i64 { 62 cam_rapid(buf, len, x1, y1) 63 cam_plunge(buf, len, cut_depth, plunge) 64 cam_cut(buf, len, x2, y2, feed) 65 cam_retract(buf, len, safe_z) 66 return 0 67} 68 69// one closed rectangular isolation ring around a pad 70func cam_ring(buf: *u8, len: *i64, x0: i64, y0: i64, x1: i64, y1: i64, 71 cut_depth: i64, safe_z: i64, feed: i64, plunge: i64) -> i64 { 72 cam_rapid(buf, len, x0, y0) 73 cam_plunge(buf, len, cut_depth, plunge) 74 cam_cut(buf, len, x1, y0, feed) 75 cam_cut(buf, len, x1, y1, feed) 76 cam_cut(buf, len, x0, y1, feed) 77 cam_cut(buf, len, x0, y0, feed) // close the loop 78 cam_retract(buf, len, safe_z) 79 return 0 80} 81 82// Emit isolation-milling G-code for a board into out[]; return byte length. 83// tr_hw[i] = copper half-width of trace i (um). pad_r[i] = pad radius (um). 84func cam_emit( 85 tr_hw: *i64, tr_x1: *i64, tr_y1: *i64, tr_x2: *i64, tr_y2: *i64, n_tr: i64, 86 pad_r: *i64, pad_x: *i64, pad_y: *i64, n_pad: i64, 87 tool_dia_um: i64, cut_depth: i64, safe_z: i64, feed: i64, plunge: i64, 88 out: *u8) -> i64 { 89 let len: *i64 = sys_mmap(8) as *i64 90 len[0] = 0 91 let trad: i64 = tool_dia_um / 2 92 g_puts(out, len, "G04 Nishi Omniforge sovereign isolation-mill CAM*\n" as *u8) 93 g_puts(out, len, "G21\n" as *u8) // millimetres 94 g_puts(out, len, "G90\n" as *u8) // absolute 95 g_puts(out, len, "G94\n" as *u8) // feed per minute 96 g_puts(out, len, "M3 S" as *u8); g_puti(out, len, K_MAGIC_12000); g_puts(out, len, "\n" as *u8) // spindle on 97 cam_retract(out, len, safe_z) 98 var i: i64 = 0 99 while i < n_tr { 100 let dx: i64 = tr_x2[i] - tr_x1[i] 101 let dy: i64 = tr_y2[i] - tr_y1[i] 102 let ln: i64 = cam_isqrt(dx * dx + dy * dy) 103 let off: i64 = tr_hw[i] + trad 104 var nxo: i64 = 0 105 var nyo: i64 = 0 106 if ln > 0 { nxo = (0 - dy) * off / ln; nyo = dx * off / ln } 107 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) 108 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) 109 i = i + 1 110 } 111 i = 0 112 while i < n_pad { 113 let h: i64 = pad_r[i] + trad 114 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) 115 i = i + 1 116 } 117 g_puts(out, len, "M5\n" as *u8) // spindle off 118 cam_retract(out, len, safe_z) 119 g_puts(out, len, "M2\n" as *u8) // program end 120 return len[0] 121}