code wiki / (root) / nx_pcb_cam.nx

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}