2026 hardware: add main-pcb-2026-11-13/
The Teensy 3.6 carrier board mounted inside the robot body (Teensy
peeks through the top-body hole). Contents:
- PCB_test_2026-11-13.json : EasyEDA PCB source (editor v6.5.61).
- OBJ_test.obj + .mtl : EasyEDA 3D OBJ export (slab + holes only).
- top.png, bottom.png : EasyEDA renders of each side; bottom.png
is the authoritative source for the
JP1-JP7 silkscreen numbering.
- 20190524_234512.jpg : photo of the populated bottom side.
- 20190524_234518.jpg : photo of the top side, powered on.
- main-pcb.scad : parametric OpenSCAD reconstruction. Real
board outline, 4 M3 holes, and every major
component (Teensy 3.6, nRF24L01+, GY-521
IMU, 7805, ZENER, JP7 battery JST, six
JST-PH 2mm servo headers JP1-JP6, POWER
LED + 510R resistor, "Hexapod v1" silk) on
the correct side of the board. Placements
are the pad-group centres pulled from the
EasyEDA JSON, not eyeballed.
- README.md : explains the files, the EasyEDA format and
unit conversion, which side each part is
on, and an authoritative placement table.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
1 parent
cb1e9234c6
commit
b1c94e4149
9 files changed
+753
No files matched your search
@@ -0,0 +1,315 @@
|
||||
// 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();
|
||||
Reference in new issue
Block a user