// // NopSCADlib Copyright Chris Palmer 2018 // nop.head@gmail.com // hydraraptor.blogspot.com // // This file is part of NopSCADlib. // // NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the // GNU General Public License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along with NopSCADlib. // If not, see . // // //! PCBs and perfboard with optional components. The shape can be a rectangle with optionally rounded corners or a polygon for odd shapes like Arduino. // panel_clearance = 0.2; include <../core.scad> include include use <../utils/rounded_cylinder.scad> use <../utils/dogbones.scad> use <../utils/tube.scad> use function pcb_name(type) = type[1]; //! Description function pcb_length(type) = type[2]; //! Length function pcb_width(type) = type[3]; //! Width function pcb_thickness(type) = type[4]; //! Thickness function pcb_radius(type) = type[5]; //! Corner radius function pcb_hole_d(type) = type[6]; //! Mounting hole diameter function pcb_land_d(type) = type[7]; //! Pad around mounting hole function pcb_colour(type) = type[8]; //! Colour of the subtrate function pcb_parts_on_bom(type) = type[9]; //! True if the parts should be separate BOM items function pcb_holes(type) = type[10]; //! List of hole positions function pcb_components(type) = type[11]; //! List of components function pcb_accessories(type) = type[12]; //! List of accessories to go on the BOM, SD cards, USB cables, etc. function pcb_grid(type) = type[13]; //! Grid if a perfboard function pcb_polygon(type) = type[14]; //! Optional outline polygon for odd shaped boards function pcb_screw(type, cap = hs_cap) = Len(type[15]) ? type[15] : find_screw(cap, screw_smaller_than(pcb_hole_d(type))); //! Mounting screw type module pcb_grid(type, x, y, z = 0) //! Positions children at specified grid positions translate([-pcb_length(type) / 2 + pcb_grid(type).x + 2.54 * x, -pcb_width(type) / 2 + pcb_grid(type).y + 2.54 * y, pcb_thickness(type) + z]) children(); // allows negative ordinates to represent offsets from the far edge function pcb_coord(type, p) = let(l = pcb_length(type), w = pcb_width(type)) //! Convert offsets from the edge to coordinates relative to the centre [(p.x > 0 ? p.x : l + p.x) - l / 2, (p.y > 0 ? p.y : w + p.y) - w / 2]; module pcb_screw_positions(type) { //! Positions children at the mounting hole positions holes = pcb_holes(type); if(len(holes)) for($i = [0 : len(holes) - 1]) { p = pcb_coord(type, holes[$i]); translate([p.x, p.y, 0]) children(); } } // p p b p p b // i i e i i a // t n l n n s // c o e // h l w w c // c // 2p54header = ["2p54header", 2.54, 12, 3.2, 0.66, "gold", grey20, 8.5]; function hdr_pitch(type) = type[1]; //! Header pitch function hdr_pin_length(type) = type[2]; //! Header pin length function hdr_pin_below(type) = type[3]; //! Header pin length underneath function hdr_pin_width(type) = type[4]; //! Header pin size function hdr_pin_colour(type) = type[5]; //! Header pin colour function hdr_base_colour(type) = type[6]; //! Header insulator colour function hdr_socket_depth(type) = type[7]; //! Socket depth for female housing module pin(type = 2p54header, length = undef) { //! Draw a header pin w = hdr_pin_width(type); l = length == undef ? hdr_pin_length(type) : length; chamfer = w / 2; color(hdr_pin_colour(type)) translate_z(l / 2 -hdr_pin_below(type)) hull() { cube([w, w, l - 2 * chamfer], center = true); cube([w - chamfer, w - chamfer, l], center = true); } } module pin_header(type = 2p54header, cols = 1, rows = 1, smt = false, cutout = false) { //! Draw pin header pitch = hdr_pitch(type); h = pitch; if(cutout) dogbone_rectangle([cols * pitch + 2 * panel_clearance, rows * pitch + 2 * panel_clearance, 100], center = false); else vitamin(str("pin_header(", type[0], cols, rows, arg(smt, false, "smt"), "): Pin header ", cols, " x ", rows)); translate_z(smt ? 3.5 - h : 0) { for(x = [0 : cols - 1], y = [0 : rows - 1]) translate([pitch * (x - (cols - 1) / 2), pitch * (y - (rows - 1) / 2), 0]) pin(type); color(hdr_base_colour(type)) linear_extrude(height = h) for(x = [0 : cols - 1], y = [0 : rows - 1]) translate([pitch * (x - (cols - 1) / 2), pitch * (y - (rows - 1) / 2), pitch / 2]) hull() { chamfer = pitch / 4; square([pitch + eps, pitch - chamfer], center = true); square([pitch - chamfer, pitch + eps], center = true); } } } module idc_transition(type, cols = 5, skip = [], cutout = false) { //! Draw IDC transition header rows = 2; pitch = hdr_pitch(type); height = 7.4; width = 6; length = cols * pitch + 5.08; if(cutout) ; else { vitamin(str("idc_transition(", type[0], ", ", cols, "): IDC transition header ", cols, " x ", rows)); color(hdr_base_colour(type)) rotate([90, 0, 0]) linear_extrude(height = width, center = true, convexity = cols * rows) difference() { translate([0, height / 2]) square([length, height], center = true); for(i = [0 : cols * rows - 1]) translate([pitch / 2 * (i - (cols * rows - 1) / 2), height / 2]) circle(d = pitch / 2 + eps); slot = pitch / 3; translate([0, height / 2 - pitch / 4 + slot / 2]) square([cols * pitch, slot], center = true); } for(x = [0 : cols - 1], y = [0 : rows -1]) if(!in(skip, x)) translate([pitch * (x - (cols - 1) / 2), pitch * (y - (rows - 1) / 2), 0]) pin(type, 2); } } module pin_socket(type = 2p54header, cols = 1, rows = 1, right_angle = false, height = 0, cutout = false) { //! Draw pin socket pitch = hdr_pitch(type); length = pitch * cols + 0.5; width = pitch * rows - 0.08; depth = max(hdr_socket_depth(type), height); ra_offset = 1.5; if(cutout) ; else { vitamin(str("pin_socket(", type[0], ", ", cols, ", ", rows, arg(right_angle, false, "right_angle"), arg(height, 0, "height"), "): Pin socket ", cols, " x ", rows, right_angle ? " right_angle" : "")); color(hdr_base_colour(type)) translate([0, right_angle ? -ra_offset - pitch / 2 : 0, right_angle ? width / 2 : 0]) rotate([right_angle ? 90 : 0, 0, 0]) translate_z(depth / 2) linear_extrude(height = depth, center = true) difference() { square([length, width], center = true); for(x = [0 : cols - 1], y = [0 : rows -1]) translate([pitch * (x - (cols - 1) / 2), pitch * (y - (rows - 1) / 2)]) square(hdr_pin_width(type), center = true); } color(hdr_pin_colour(type)) for(x = [0 : cols - 1], y = [0 : rows -1]) { translate([pitch * (x - (cols - 1) / 2), pitch * (y - (rows - 1) / 2), 0]) pin(type, hdr_pin_below(type) + width / 2 + (y - 0.5) * pitch); if(right_angle) { rotate([-90, 0, 0]) translate([pitch * (x - (cols - 1) / 2), -pitch * (y - (rows - 1) / 2) -width / 2, 0]) pin(type, hdr_pin_below(type) + (y - 0.5) * pitch); w = hdr_pin_width(type); translate([pitch * (x - (cols - 1) / 2), pitch * (y - (rows - 1) / 2) - w / 2, pitch * (y - (rows - 1) / 2) + width / 2 - w / 2]) rotate([0, -90, 0]) rotate_extrude(angle = 90, $fn = 32) translate([0, -w / 2]) square(w); } } } } module chip(length, width, thickness, cutout = false) //! Draw a black cube to represent a chip if(!cutout) color(grey20) translate_z(thickness / 2) cube([length, width, thickness], center = true); module usb_Ax2(cutout = false) { //! Draw USB type A dual socket l = 17; w = 13.25; h = 15.6; flange_t = 0.4; h_flange_h = 0.8; h_flange_l = 11; v_flange_h = 1; v_flange_l = 12.15; bar = 3.4; socket_h = (h - 2 * flange_t - bar) / 2; translate_z(h / 2) if(cutout) rotate([90, 0, 90]) rounded_rectangle([w + 2 * v_flange_h + 2 * panel_clearance, h + 2 * h_flange_h + 2 * panel_clearance, 100], r = cnc_bit_r, center = false); else color("silver") rotate([0, 90, 0]) { linear_extrude(height = l, center = true) difference() { square([h, w], center = true); for(s = [-1, 1]) translate([s * (bar / 2 + socket_h / 2), 0]) square([socket_h, w - 2 * flange_t], center = true); } translate_z(-l / 2 + 0.5) cube([h, w, 1], center = true); translate_z(l / 2 - flange_t) linear_extrude(height = flange_t) difference() { union() { square([h + 2 * h_flange_h, h_flange_l], center = true); square([v_flange_l, w + 2 * v_flange_h], center = true); } square([h - eps, w - eps], center = true); } } } module rj45(cutout = false) { //! Draw RJ45 Ethernet connector l = 21; w = 16; h = 13.5; plug_h = 6.8; plug_w = 12; plug_z = 4; tab_z = 0.8; tab_w = 4; translate_z(h / 2) if(cutout) rotate([90, 0, 90]) dogbone_rectangle([w + 2 * panel_clearance, h + 2 * panel_clearance, 100], center = false); else { rotate([0, 90, 0]) { mouth = plug_z + plug_h - tab_z; color("silver") { linear_extrude(height = l, center = true) difference() { square([h, w], center = true); translate([h / 2 - tab_z - mouth / 2, 0]) square([mouth + 0.1, plug_w + 0.1], center = true); } translate_z(-l / 2) cube([h, w, eps], center = true); } color(grey30) { linear_extrude(height = l - 0.2, center = true) difference() { square([h - 0.1, w - 0.1], center = true); translate([h / 2 - plug_z - plug_h / 2, 0]) square([plug_h, plug_w - 0.1], center = true); translate([h / 2 - tab_z - plug_h / 2, 0]) square([plug_h, tab_w], center = true); } translate_z(-l / 2 + 1) cube([h - 0.1, w - 0.1, 0.1], center = true); } } } } module jack(cutout = false) { //! Draw 3.5mm jack l = 12; w = 7; h = 6; d = 6; ch = 2.5; translate_z(h / 2) if(cutout) rotate([0, 90, 0]) cylinder(d = d + 2 * panel_clearance, h = 100); else color(grey20) rotate([0, 90, 0]) { linear_extrude(height = l / 2) difference() { square([h, w], center = true); circle(d = 3.5); } tube(or = d / 2, ir = 3.5 / 2, h = l / 2 + ch, center = false); translate_z(-l / 4) cube([h, w, l / 2], center = true); } } module hdmi(cutout = false) { //! Draw HDMI socket l = 12; iw1 = 14; iw2 = 10; ih1 = 3; ih2 = 4.5; h = 6.5; t = 0.5; module D() { hull() { translate([-iw1 / 2, h - t - ih1]) square([iw1, ih1]); translate([-iw2 / 2, h - t - ih2]) square([iw2, ih2]); } } if(cutout) rotate([90, 0, 90]) linear_extrude(height = 100) offset(t + panel_clearance) D(); else color("silver") rotate([90, 0, 90]) { linear_extrude(height = l, center = true) difference() { offset(t) D(); D(); } translate_z(-l / 2) linear_extrude(height = 1) offset(t) D(); } } module usb_uA(cutout = false) { //! Draw USB micro A connector l = 6; iw1 = 7; iw2 = 5.7; ih1 = 1; ih2 = 1.85; h = 2.65; t = 0.4; flange_h = 3; flange_w = 8; module D() { hull() { translate([-iw1 / 2, h - t - ih1]) square([iw1, ih1]); translate([-iw2 / 2, h - t - ih2]) square([iw2, ih2]); } } if(cutout) rotate([90, 0, 90]) linear_extrude(height = 100) offset((flange_h - ih2) / 2 + 2 * panel_clearance) D(); else color("silver") rotate([90, 0, 90]) { linear_extrude(height = l, center = true) difference() { offset(t) D(); D(); } translate_z(-l / 2) linear_extrude(height = 1) offset(t) D(); translate_z(l / 2 - t) linear_extrude(height = t) difference() { union() { translate([0, h - t - ih1 / 2]) square([flange_w, ih1], center = true); translate([0, h / 2 + flange_h / 4]) square([iw1, flange_h / 2], center = true); translate([0, h / 2 - flange_h / 4]) square([iw2, flange_h / 2], center = true); } D(); } } } module usb_B(cutout = false) { //! Draw USB B connector l = 16.4; w = 12.2; h = 11; tab_w = 5.6; tab_h = 3.2; d_h = 7.78; d_w = 8.45; d_w2 = 5; d_h2 = d_h - (d_w - d_w2) / 2; module D() hull() { translate([-d_w / 2, 0]) square([d_w, d_h2]); translate([-d_w2 /2, 0]) square([d_w2, d_h]); } if(cutout) translate([50, 0, h / 2 - panel_clearance]) cube([100, w + 2 * panel_clearance, h + 2 * panel_clearance], center = true); else translate_z(h / 2) rotate([90, 0, 90]) { color("silver") { linear_extrude(height = l, center = true) difference() { square([w, h], center = true); translate([0, -d_h / 2]) offset(delta = 0.2) D(); } translate_z(-l / 2 + 0.1) cube([w, h, 0.2], center = true); } color("white") { linear_extrude(height = l - 0.4, center = true) difference() { square([w - 0.2, h - 0.2], center = true); translate([0, -d_h / 2]) difference() { D(); translate([0, d_h / 2]) square([tab_w, tab_h], center = true); } } translate_z( -(l - 0.4) / 2 + 1) cube([w - 0.2, h - 0.2, 2], center = true); } } } module barrel_jack(cutout = false) { //! Draw barrel power jack l = 13.2; w = 8.89; h = 11; bore_d = 6.3; bore_h = 6.5; bore_l = 11.8; pin_d = 2; front = 3.3; r = 0.5; contact_d = 2; contact_w = 4; inset = 1; if(cutout) ; else { color(grey20) rotate([0, 90, 0]) { linear_extrude(height = l, center = true) { difference() { translate([-h / 2, 0]) rounded_square([h, w], r); translate([-bore_h, 0]) circle(d = bore_d); translate([-h / 2 - bore_h, 0]) square([h, w], center = true); } } translate_z(l / 2 - front) linear_extrude(height = front) { difference() { translate([-h / 2, 0]) rounded_square([h, w], r); translate([-bore_h, 0]) circle(d = bore_d); } } translate([-bore_h, 0]) tube(or = w / 2 - 0.5, ir = bore_d / 2, h = l); translate([-bore_h, 0, -l / 2]) cylinder(d = w -1, h = l - bore_l); } color("silver") { translate([l / 2 - inset - pin_d / 2, 0, bore_h]) hull() { sphere(pin_d / 2); rotate([0, -90, 0]) cylinder(d = pin_d, h = bore_l - inset); } hull() { translate([l / 2 - inset - contact_d / 2, 0, bore_h - bore_d / 2]) rotate([90, 0, 0]) cylinder(d = contact_d, h = contact_w, center = true); translate([l / 2 - bore_l, 0, bore_h - bore_d / 2 + contact_d / 4]) cube([eps, contact_w, eps], center = true); } } } } module flex(cutout = false) { //! Draw flexistrip connector l = 20.6; w = 3; h = 5.6; top_l = 22.4; top_t = 1.1; tab_l = 13; tab_w = 1; slot_l = 16.4; slot_w = 0.7; slot_offset = 0.6; if(cutout) ; else { color(grey30) { translate_z(0.5) cube([l, w, 1], center = true); linear_extrude(height = h) difference() { square([l, w], center = true); translate([0, -w / 2 + slot_offset + slot_w / 2]) square([slot_l, slot_w], center = true); } translate_z(h - top_t) linear_extrude(height = top_t) difference() { union() { square([top_l, w], center = true); hull() { translate([0, -w / 2 + (w + tab_w) / 2]) square([tab_l - 1, w + tab_w], center = true); square([tab_l, w], center = true); } } translate([0, -w / 2 + slot_offset + slot_w / 2]) square([slot_l, slot_w], center = true); } } } } module terminal_35(ways) { //! Draw 3.5mm terminal block vitamin(str("terminal_35(", ways, "): Terminal block ", ways, " way 3.5mm")); pitch = 3.5; width = ways * pitch; depth = 7; height = 8.3; chamfer_h = 3; chamfer_d = 1; box_z = 0.5; box_w = 2.88; box_h = 4.1; wire_z = 2; wire_d = 2; pin_l = 4.2; pin_d = 0.9; module single() { screw_r = 1; color("blue") { rotate([90, 0, 0]) linear_extrude(height = pitch, center = true) polygon(points = [ [ depth / 2, 0], [ depth / 2, box_z], [-depth / 2 + 1, box_z], [-depth / 2 + 1, box_z + box_h], [ depth / 2, box_z + box_h], [ depth / 2, height - chamfer_h], [ depth / 2 - chamfer_d, height], [ -screw_r - eps, height], [ -screw_r - eps, box_z + box_h], [ screw_r + eps, box_z + box_h], [ screw_r + eps, height], [-depth / 2, height], [-depth / 2, 0], ]); linear_extrude(height = box_z + box_h) difference() { square([depth, pitch], center = true); translate([1, 0]) square([depth, box_w], center = true); } translate_z(box_z + box_h) linear_extrude(height = height - box_z - box_h) difference() { square([2 * screw_r + 0.1, pitch], center = true); circle(screw_r); } } color("silver") { screw_z = box_z + box_h; translate_z(screw_z) { cylinder(r = screw_r, h = height - screw_z - 1); // screw linear_extrude(height = height - screw_z - 0.5) difference() { circle(1); square([4, 0.5], center = true); // screw slot square([0.5, 1.7], center = true); // second screw slot } } translate_z(box_z - pin_l) cylinder(d = pin_d, h = pin_l + box_z, $fn = 16); // pin translate_z(box_z + box_h / 2) // terminal rotate([0, -90, 0]) { linear_extrude(height = depth - 2, center = true) difference() { square([box_h, box_w], center = true); translate([wire_z - box_z - box_h / 2, 0]) circle(d = wire_d); } translate_z(depth / 2 - 1.5) cube([box_h, box_w, 1], center = true); } } } for(i = [0: ways -1]) translate([0, i * pitch - width / 2 + pitch / 2]) single(); } module terminal_254(ways, skip = []) { //! Draw 0.1" terminal block vitamin(str("terminal_254(", ways, "): Terminal block ", ways, " way 0.1\"")); pitch = 2.54; width = ways * pitch; depth = 6.2; height = 8.5; ledge_height = 5; ledge_depth = 0.7; top = 3; back = 3; module single(skip = false) { screw_r = 1; box_w1 = pitch - 0.4; box_h1 = ledge_height - 0.4; box_w2 = 2; box_h2 = 2; color("lime") { rotate([90, 0, 0]) linear_extrude(height = pitch, center = true, convexity = 5) polygon(points = [ [ depth / 2, 0], [ depth / 2, ledge_height / 2 - box_h1 / 2], [ depth / 2 - 0.5, ledge_height / 2 - box_h1 / 2], [ depth / 2 - 2, ledge_height / 2 - box_h2 / 2], [-depth / 2 + 1, ledge_height / 2 - box_h2 / 2], [-depth / 2 + 1, ledge_height / 2 + box_h2 / 2], [ depth / 2 - 2, ledge_height / 2 + box_h2 / 2], [ depth / 2 - 0.5, ledge_height / 2 + box_h1 / 2], [ depth / 2, ledge_height / 2 + box_h1 / 2], [ depth / 2, ledge_height], [ depth / 2 - ledge_depth, ledge_height], [ top / 2, height], [ screw_r + eps, height], [ screw_r + eps, ledge_height / 2 + box_h2 / 2], [-screw_r - eps, ledge_height / 2 + box_h2 / 2], [-screw_r - eps, height], [ -top / 2, height], [-depth / 2, back], [-depth / 2, 0], ]); translate_z(ledge_height / 2 + box_h2 / 2) linear_extrude(height = height - ledge_height / 2 - box_h2 / 2) difference() { square([screw_r * 2 + 0.1, pitch], center = true); circle(screw_r); } linear_extrude(height = ledge_height) difference() { translate([0.5, 0]) square([depth - 1, pitch], center = true); translate([depth / 2, 0]) { square([9, box_w2], center = true); hull() { square([1, box_w1], center = true); square([4, box_w2], center = true); } } } } if(!skip) color("silver") translate_z(1) { slot_depth = 1; screw_top = height - 1.5; pin_l = 3.3 + ledge_height / 2 - 2; translate_z(ledge_height / 2) // screw cylinder(r = 1, h = screw_top - slot_depth - ledge_height / 2); translate_z(screw_top - slot_depth) // screw head linear_extrude(height = slot_depth) difference() { circle(1); square([4, 0.5], center = true); } translate_z(ledge_height / 2 - 1) rotate([0, 90, 0]) linear_extrude(height = 2, center = true) difference() { square([2, 2], center = true); square([1.5, 1.9], center = true); } translate([-1.5, 0, ledge_height / 2 - 1]) // terminal back cube([1, 2, 2], center = true); translate_z(ledge_height / 2 - 2 - pin_l / 2) cube([0.44, 0.75, pin_l], center = true); // pin } } for(i = [0: ways -1]) translate([0, i * pitch - width / 2 + pitch / 2]) single(in(skip, i)); } module molex_254(ways) { //! Draw molex header vitamin(str("molex_254(", ways, "): Molex KK header ", ways, " way")); pitch = 2.54; width = ways * pitch - 0.1; depth = 6.35; height = 8.15; base = 3.18; back = 1; below = 2.3; above = 9; color("white") union() { translate([ -depth / 2, -width / 2,]) cube([depth, width, base]); w = width - pitch; translate([- depth / 2, -w / 2]) cube([back, w, height]); } color("silver") for(i = [0: ways -1]) translate([0, i * pitch - width / 2 + pitch / 2, (above + below) / 2 - below]) cube([0.44, 0.75, above + below], center = true); } module pcb_component(comp, cutouts = false, angle = undef) { //! Draw pcb component from description function show(comp, part) = (comp[3] == part || comp[3] == str("-",part)) && (!cutouts || angle == undef || angle == comp.z); rotate(comp.z) { if(show(comp, "2p54header")) pin_header(2p54header, comp[4], comp[5], len(comp) > 5 ? comp[6] : false, cutouts); if(show(comp, "2p54socket")) pin_socket(2p54header, comp[4], comp[5], comp[6], len(comp) > 7 ? comp[7] : 0, cutouts); if(show(comp, "chip")) chip(comp[4], comp[5], comp[6], cutouts); if(show(comp, "rj45")) rj45(cutouts); if(show(comp, "usb_Ax2")) usb_Ax2(cutouts); if(show(comp, "usb_uA")) usb_uA(cutouts); if(show(comp, "usb_B")) usb_B(cutouts); if(show(comp, "jack")) jack(cutouts); if(show(comp, "barrel_jack")) barrel_jack(cutouts); if(show(comp, "hdmi")) hdmi(cutouts); if(show(comp, "flex")) flex(cutouts); if(show(comp, "D_plug")) if(!cutouts) translate_z(d_pcb_offset(comp[4])) d_plug(comp[4], pcb = true); if(show(comp, "molex_hdr")) if(!cutouts) molex_254(comp[4]); if(show(comp, "term254")) if(!cutouts) terminal_254(comp[4], comp[5]); if(show(comp, "term35")) if(!cutouts) terminal_35(comp[4]); if(show(comp, "transition")) if(!cutouts) idc_transition(2p54header, comp[4], comp[5]); if(show(comp, "block")) color(comp[7]) if(!cutouts) translate_z(comp[6] / 2) cube([comp[4], comp[5], comp[6]], center = true); else if(comp[8]) translate([-50, 0, comp[6] / 2 - panel_clearance]) cube([100, comp[5] + 2 * panel_clearance, comp[6] + 2 * panel_clearance], center = true); if(show(comp, "button_6mm")) square_button(button_6mm); } } module pcb_components(type, cutouts = false, angle = undef) { //! Draw list of PCB components on the PCB not_on_bom(pcb_parts_on_bom(type)) for(comp = pcb_components(type)) { p = pcb_coord(type, [comp.x, comp.y]); if(comp[3][0] == "-") translate([p.x, p.y]) vflip() pcb_component(comp, cutouts, angle); else translate([p.x, p.y, pcb_thickness(type)]) pcb_component(comp, cutouts, angle); } } module pcb_cutouts(type, angle = undef) pcb_components(type, true, angle); //! Make cut outs to clear components on a PCB module pcb_grid_positions(type) { x0 = pcb_grid(type).x; y0 = pcb_grid(type).y; cols = round((pcb_length(type) - 2 * x0) / inch(0.1)); rows = round((pcb_width(type) - 2 * y0) / inch(0.1)); for(x = [0 : cols], y = [0 : rows]) pcb_grid(type, x, y) children(); } module pcb(type) { //! Draw specified PCB grid = pcb_grid(type); t = pcb_thickness(type); if(pcb_name(type)) vitamin(str("pcb(", type[0], "): ", pcb_name(type))); for(part = pcb_accessories(type)) vitamin(part); pcb_components(type); color(pcb_colour(type)) linear_extrude(height = t) difference() { if(Len(pcb_polygon(type))) polygon(pcb_polygon(type)); else rounded_square([pcb_length(type), pcb_width(type)], r = pcb_radius(type)); pcb_screw_positions(type) circle(d = pcb_hole_d(type) + eps); if(Len(grid)) pcb_grid_positions(type) circle(d = 1 + eps); } color("silver") translate_z(t / 2) pcb_screw_positions(type) tube(or = max(pcb_land_d(type), 1) / 2, ir = pcb_hole_d(type) / 2, h = t + 2 * eps); fr4 = pcb_colour(type) == "green"; plating = 0.15; color(fr4 ? "silver" : "gold") translate_z(-plating) linear_extrude(height = fr4 ? t + 2 * plating : plating) if(Len(grid)) { pcb_grid_positions(type) difference() { circle(d = 2); circle(d = 1); } if(fr4) { // oval lands at the ends screw_x = pcb_coord(type, pcb_holes(type)[0]).x; y0 = pcb_grid(type).y; rows = round((pcb_width(type) - 2 * y0) / inch(0.1)); for(end = [-1, 1], y = [1 : rows - 1]) translate([end * screw_x, y0 + y * inch(0.1) - pcb_width(type) / 2]) hull() for(x = [-1, 1]) translate([x * 1.6 / 2, 0]) circle(d = 2); } } } module pcb_spacer(screw, height, wall = 1.8) { //! Generate STL for PCB spacer stl(str("pcb_spacer", round(screw_radius(screw) * 20), round(height * 10))); ir = screw_clearance_radius(screw); or = corrected_radius(ir) + wall; linear_extrude(height = height) poly_ring(or, ir); } module pcb_base(type, height, thickness, wall = 2) { //! Generate STL for a base with PCB spacers screw = pcb_screw(type); ir = screw_clearance_radius(screw); or = corrected_radius(ir) + wall; union() { linear_extrude(height = thickness) difference() { hull() pcb_screw_positions(type) poly_ring(or, ir); pcb_screw_positions(type) poly_circle(ir); } linear_extrude(height = height) pcb_screw_positions(type) poly_ring(or, ir); } } module pcb_assembly(type, height, thickness) { //! Draw PCB assembly with spaces and fasteners in place translate_z(height) pcb(type); screw = pcb_screw(type); if(!is_undef(screw)) { washer = screw_washer(screw); nut = screw_nut(screw); screw_length = screw_longer_than(height + thickness + pcb_thickness(type) + washer_thickness(washer) + nut_thickness(nut, true)); taper = screw_smaller_than(pcb_hole_d(type)) > 2 * screw_radius(screw); // Arduino? pcb_screw_positions(type) { translate_z(height + pcb_thickness(type)) screw(screw, screw_length); color(pp1_colour) if(taper) { h2 = max(0, height - 2); if(h2) pcb_spacer(screw, h2); pcb_spacer(screw, height, 2 * extrusion_width); // Thin as can be at the top because there is no clearance around the holes. } else pcb_spacer(screw, height); translate_z(-thickness) vflip() nut_and_washer(nut, true); } } }