// Main PCB of the 2026 hexapod robot — a Teensy 3.6 carrier board. // // Dimensions and placements are all derived from the EasyEDA export // (PCB_test_2026-11-13.json), cross-checked against the EasyEDA renders // (top.png, bottom.png) and photos of the populated board. // // Every component is placed at its actual pad reference point (not an // eyeballed position), with its real-world body dimensions. // // Coordinate frame: X right, Y up, Z up, origin = board bottom-left. // Board top surface at z = PCB_THICK, bottom surface at z = 0. // EasyEDA uses Y-down with the base unit = 10 mil (0.254 mm). Conversion: // x_mm = (x_eda - 3980.315) * 0.254 // y_mm = (3330.7087 - y_eda) * 0.254 /* [Board] */ BOARD_W = 46.0; BOARD_H = 89.0; PCB_THICK = 1.6; CORNER_R = 1.0; /* [Mounting holes] */ HOLE_DIA = 4.0; // M3 clearance HOLE_INSET = 5.0; /* [Rendering] */ SHOW_COMPONENTS = true; SHOW_LABELS = true; TEENSY_ALPHA = 0.4; EXPLODE = 0; /* [Hidden] */ $fa = 2; $fs = 0.3; C_PCB = "#1a1a1a"; C_TEENSY = "#1e5a2a"; C_MODULE = "#1f4fa0"; C_HEADER = "#111"; C_METAL = "#b0b0b0"; C_PLASTIC = "#222"; C_LED_ON = "#55ff55"; C_LABEL = "white"; // ---------- board ---------------------------------------------------- module board_outline_2d() { if (CORNER_R > 0) hull() for (p = [[CORNER_R, CORNER_R], [BOARD_W - CORNER_R, CORNER_R], [CORNER_R, BOARD_H - CORNER_R], [BOARD_W - CORNER_R, BOARD_H - CORNER_R]]) translate(p) circle(r = CORNER_R); else square([BOARD_W, BOARD_H]); } module mounting_holes_2d() { for (x = [HOLE_INSET, BOARD_W - HOLE_INSET], y = [HOLE_INSET, BOARD_H - HOLE_INSET]) translate([x, y]) circle(d = HOLE_DIA); } module pcb() { color(C_PCB) linear_extrude(PCB_THICK) difference() { board_outline_2d(); mounting_holes_2d(); } } // ---------- helpers -------------------------------------------------- module mount_top(cx, cy, rot = 0) { translate([cx, cy, PCB_THICK + EXPLODE]) rotate([0, 0, rot]) children(); } module mount_bottom(cx, cy, rot = 0) { translate([cx, cy, -EXPLODE]) mirror([0, 0, 1]) rotate([0, 0, rot]) children(); } module centered_cube(sx, sy, sz) { translate([-sx/2, -sy/2, 0]) cube([sx, sy, sz]); } module label(txt, size = 2.4, rot = 0) { if (SHOW_LABELS) color(C_LABEL) rotate([0, 0, rot]) linear_extrude(0.2) text(txt, size = size, halign = "center", valign = "center", font = "Liberation Sans"); } // Generic 1xN pin-header strip, pins along Y, strip centered on local origin. // pitch defaults to 2.54 mm (standard); pass 2.0 for JST-PH. module header_1xN(n, h = 8.5, pitch = 2.54) { L = n * pitch; color(C_HEADER) translate([-pitch/2, -L/2 + pitch/2 - pitch/2, 0]) cube([pitch, L, h]); color(C_METAL) for (i = [0:n-1]) translate([0, (i - (n-1)/2) * pitch, h]) cylinder(d = 0.65, h = 2.5); } // Generic MxN pin-header, M rows along X (2.54 pitch) x N pins along Y. module header_MxN(m, n, h = 8.5, pitch = 2.54) { L = n * pitch; W = m * pitch; color(C_HEADER) translate([-W/2, -L/2, 0]) cube([W, L, h]); color(C_METAL) for (i = [0:m-1], j = [0:n-1]) translate([(i - (m-1)/2) * pitch, (j - (n-1)/2) * pitch, h]) cylinder(d = 0.65, h = 2.5); } // ---------- components ----------------------------------------------- // // Every component module below is written so its LOCAL origin (0,0,0) // matches a well-defined reference on the board — usually the geometric // centre of its pad group (= the position the EasyEDA LIB extractor // returned). All geometry is built upward from z = 0. // Teensy 3.6: local origin = centre of the 2x24 pin pattern. // Pin bbox: 15.24 mm (X, two rows at 0.6") x 58.42 mm (Y, 24 pins). // Body is 17.8 x 61 x 1.6 mm, slightly wider than the pin pitch. // Micro-USB jack at +Y end; microSD slot at -Y end. module teensy_3_6() { HDR_H = 11.0; ROW_SPACING = 15.24; // 0.6 inch PIN_SPAN = 24 * 2.54; BODY_L = 61.0; BODY_W = 17.8; BODY_T = 1.6; for (dx = [-ROW_SPACING/2, ROW_SPACING/2]) translate([dx, 0, 0]) header_1xN(24, HDR_H); translate([0, 0, HDR_H]) { color(C_TEENSY, TEENSY_ALPHA) centered_cube(BODY_W, BODY_L, BODY_T); // MK66FX1M0 chip, towards +Y end color("#222") translate([0, 10, BODY_T]) centered_cube(10, 10, 1.3); // micro-USB jack, protrudes past +Y edge color(C_METAL) translate([0, BODY_L/2 - 1, BODY_T]) centered_cube(7.5, 5.5, 2.6); // microSD slot at -Y end color(C_METAL) translate([0, -BODY_L/2 + 7, BODY_T]) centered_cube(12, 14, 1.5); if (SHOW_LABELS) translate([0, -4, BODY_T + 1.5]) label("Teensy 3.6", 2.6); } } // nRF24L01+ with PCB antenna. Local origin = centre of the 2x4 header // (pads span 7.62 x 2.54 mm). Module body is 15.5 x 29 x 1.2 mm; the // header sits near one short edge, so the body extends -Y from origin // and the PCB antenna end hangs off that side of the board. module nrf24() { HDR_H = 7.5; BODY_L = 29.0; BODY_W = 15.5; BODY_T = 1.2; HDR_FROM_EDGE = 3.2; // from body +Y edge to header centre body_y = -(BODY_L/2 - HDR_FROM_EDGE); header_MxN(2, 4, HDR_H); translate([0, body_y, HDR_H]) { color(C_MODULE) centered_cube(BODY_W, BODY_L, BODY_T); // RF shield (can) over the chip area, nearer the header end color(C_METAL) translate([0, BODY_L/2 - 7.5, BODY_T]) centered_cube(BODY_W - 2, 10.0, 1.8); // Cylindrical crystal next to the shield color(C_METAL) translate([-BODY_W/4, BODY_L/2 - 15, BODY_T]) cube([3.2, 8, 2.5]); if (SHOW_LABELS) translate([0, BODY_L/2 - 20, BODY_T + 2.2]) label("nRF24", 2.2); } } // GY-521 (MPU6050) breakout. Local origin = centre of the 1x8 header // (pads span 0 x 17.78 mm, 2.54 pitch along Y). The board extends +X // from the header; body is 15.6 x 21.2 x 1.6 mm. module gy521() { HDR_H = 8.0; BODY_L = 21.2; BODY_W = 15.6; BODY_T = 1.6; header_1xN(8, HDR_H); translate([BODY_W/2, 0, HDR_H]) { color(C_MODULE) centered_cube(BODY_W, BODY_L, BODY_T); // MPU6050 QFN near the board centre color("#222") translate([0, 0, BODY_T]) centered_cube(4, 4, 1.1); if (SHOW_LABELS) translate([0, BODY_L/2 - 3, BODY_T + 1.5]) label("MPU", 2.4); } } // 7805 in TO-220. Local origin = centre of the three TO-252 pads on the // EasyEDA board (the physical build has a through-hole 7805 bent flat // into those pads). Pads span ~4.57 x 7.29 mm. module ic_7805() { BODY_L = 10.0; BODY_W = 4.7; BODY_H = 4.5; // body laid flat along +Y (matching the pad bbox orientation) translate([0, 0, 0]) { color(C_PLASTIC) translate([-BODY_L/2, -BODY_W/2, 0]) cube([BODY_L, BODY_W, BODY_H]); // metal tab color(C_METAL) translate([-BODY_L/2, -BODY_W/2, BODY_H - 1.2]) cube([BODY_L, BODY_W, 1.2]); // 3 leads out of +Y side color(C_METAL) for (dx = [-2.54, 0, 2.54]) translate([dx - 0.3, BODY_W/2, 0]) cube([0.6, 3, 0.6]); if (SHOW_LABELS) translate([0, 0, BODY_H + 0.5]) label("7805", 2.0); } } // Through-hole zener, lying between two pads along X (pad span 3.28 mm). module zener() { color("#444") rotate([0, 90, 0]) cylinder(d = 2.0, h = 3.0, center = true); // cathode band color("#eee") translate([-0.9, 0, 0]) rotate([0, 90, 0]) cylinder(d = 2.05, h = 0.3, center = true); if (SHOW_LABELS) translate([0, 3.5, 0]) label("ZENER", 1.4); } // 2-pin JST-XH battery connector (silkscreen JP7). Pads 2 mm apart in X. module jst_jp7() { BODY_W = 7.6; BODY_D = 4.0; BODY_H = 7.0; color(C_PLASTIC) translate([-BODY_W/2, -BODY_D/2 + 1.0, 0]) cube([BODY_W, BODY_D, BODY_H]); color(C_METAL) translate([-3, -BODY_D/2 + 1.0 - 0.5, BODY_H - 1.5]) cube([6, 0.5, 1.0]); if (SHOW_LABELS) translate([0, 0, BODY_H + 0.5]) label("JP7", 1.8); } // 5-pin JST-PH 2 mm servo header (JP1..JP6). Pads 2 mm pitch along Y, // total span 8 mm. Local origin = pad-group centre. module servo_jst5(label_txt) { PITCH = 2.0; header_1xN(5, 8.5, pitch = PITCH); // JST-PH housing body (sits above the pins when soldered male-up) color(C_PLASTIC) translate([-1.8, -5.0, 0]) cube([3.6, 10.0, 7.0]); if (SHOW_LABELS) translate([3.5, 0, 4]) label(label_txt, 1.8, 90); } // 1206 SMD LED and 2010 SMD resistor, both on the TOP layer. module led_1206() { color(C_LED_ON) translate([-1.6, -0.8, 0]) cube([3.2, 1.6, 0.9]); if (SHOW_LABELS) translate([0, -2.5, 1.0]) label("PWR", 1.6); } module r_2010(txt = "510") { color("#eee") translate([-2.5, -1.25, 0]) cube([5.0, 2.5, 0.6]); if (SHOW_LABELS) translate([0, 0, 0.7]) label(txt, 1.4); } // "Hexapod v1" silkscreen text, at the position of the top-silk TEXT // entry in the EasyEDA file. module title_silk() { if (SHOW_LABELS) translate([6, 80.9, PCB_THICK + 0.02]) color("white") linear_extrude(0.1) text("Hexapod v1", size = 2.8, halign = "left", valign = "center", font = "Liberation Sans"); } // ---------- assembly ------------------------------------------------- // // Placements use the pad-reference centres extracted from the EasyEDA // file (see scratchpad/libs.py output). module components() { // --- TOP side ---- mount_top(22.62, 44.79) teensy_3_6(); mount_top(33.00, 83.00) led_1206(); mount_top(32.55, 78.72) r_2010("510"); title_silk(); // --- BOTTOM side ---- mount_bottom(28.00, 8.00) nrf24(); // header centre; body hangs off -Y edge mount_bottom(19.22, 44.73) gy521(); // header centre; body extends +X mount_bottom(23.00, 79.00) ic_7805(); mount_bottom(22.00, 67.00) zener(); mount_bottom(15.00, 9.00) jst_jp7(); // Servo headers, silkscreen numbering per bottom.png: // left column (x=9) top-to-bottom = JP1, JP2, JP3 // right column (x=38) top-to-bottom = JP4, JP5, JP6 mount_bottom( 9.00, 69.00) servo_jst5("JP1"); mount_bottom( 9.00, 43.00) servo_jst5("JP2"); mount_bottom( 9.00, 20.00) servo_jst5("JP3"); mount_bottom(38.00, 69.00) servo_jst5("JP4"); mount_bottom(38.00, 44.00) servo_jst5("JP5"); mount_bottom(38.00, 20.00) servo_jst5("JP6"); } module main_pcb() { pcb(); if (SHOW_COMPONENTS) components(); } main_pcb();