Dovetail rail + tool-free swappable payload modules for all variants: - payload_bay_rail.scad: 50×12 mm 60° dovetail rail (DXF for CNC Al bar), spring ball detent (Ø6 mm, 50 mm pitch), continuous safety-lock groove (M4 thumbscrew), 4-pin pogo connector housing (GND/5V/12V/UART), lab/rover/tank deck adapter plates - payload_bay_modules.scad: universal _module_base() (male tongue, detent bore, 4× Ø4 mm target pads, lock bore) + 3 example modules: cargo tray (200×100 mm, Velcro slots, bungee cord slots), camera boom (120 mm mast + 80 mm arm, 2020-rail-compatible head, 3-position tilt), cup holder (Ø80 mm tapered, 8-slot flex grip). Includes copy-paste module template. - payload_bay_BOM.md: hardware list, CNC spec (dovetail dimensions, surface finish, connector pocket), load analysis (2 kg rated with Al rail + lock), module developer guide with constraints table Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
463 lines
22 KiB
OpenSCAD
463 lines
22 KiB
OpenSCAD
// ============================================================
|
||
// payload_bay_modules.scad — Payload Bay Module Template + Examples
|
||
// Issue: #170 Agent: sl-mechanical Date: 2026-03-01
|
||
// ============================================================
|
||
//
|
||
// ── HOW TO CREATE A NEW MODULE ──────────────────────────────────────────────
|
||
//
|
||
// 1. Copy the "MODULE TEMPLATE" section at the bottom of this file.
|
||
// 2. Set MODULE_L to your module's Y length (min 60 mm).
|
||
// 3. Add your payload geometry on top of the _module_base() call.
|
||
// 4. The _module_base() provides:
|
||
// • Male dovetail tongue (slides into rail slot)
|
||
// • Spring detent bore(s) (for Ø6 mm ball + spring plunger)
|
||
// • Connector target pads (4× Ø4 mm brass, matching rail pogo pins)
|
||
// • Safety-lock M4 thumbscrew bore (side of tongue)
|
||
// • Bottom-face flush with rail top (Z = 0 at rail top / module base)
|
||
// 5. Your payload geometry sits at Z ≥ 0 (above the rail top face).
|
||
// 6. Add a RENDER dispatch entry and export command.
|
||
//
|
||
// ── DOVETAIL TONGUE GEOMETRY ────────────────────────────────────────────────
|
||
//
|
||
// TONGUE_BOT = DOVE_SLOT_BOT - 2*DOVE_CLEAR = 28.0 - 0.6 = 27.4 mm
|
||
// TONGUE_TOP = DOVE_SLOT_TOP + 2*DOVE_CLEAR = 37.2 + 0.6 = 37.8 mm
|
||
// TONGUE_H = DOVE_H + 0.2 (slight extra depth, no binding at corners)
|
||
//
|
||
// Tongue runs full module length (-Y to +Y in module coords).
|
||
// Module body sits on top of tongue at Z = 0.
|
||
//
|
||
// ── CONNECTOR PADS ──────────────────────────────────────────────────────────
|
||
// 4× Ø4 mm brass discs press-fit into tongue bottom face.
|
||
// Pad positions: must align with rail connector at CONN_Y when module
|
||
// is slid to its intended position (any detent step).
|
||
// Default: pads centred in module length → module must be placed so its
|
||
// centre aligns with CONN_Y on rail. Or: set MODULE_CONN_OFFSET to shift.
|
||
//
|
||
// ── PAYLOAD RATING ──────────────────────────────────────────────────────────
|
||
// 2 kg rated payload when safety-lock thumbscrew is tightened.
|
||
// Detent-only (no thumbscrew): ~0.5 kg (impact / vibration condition).
|
||
//
|
||
// ── RENDERED EXAMPLES ────────────────────────────────────────────────────────
|
||
// Part 1 — cargo_tray() 200×100 mm cargo tray, 30 mm walls
|
||
// Part 2 — camera_boom() L-arm with sensor_rail-compatible head
|
||
// Part 3 — cup_holder() Ø80 mm tapered cup cradle
|
||
//
|
||
// RENDER options:
|
||
// "assembly" all 3 modules on ghost rail
|
||
// "base_stl" module base / tongue only (universal)
|
||
// "cargo_tray_stl" cargo tray module
|
||
// "camera_boom_stl" camera boom module
|
||
// "cup_holder_stl" cup holder module
|
||
// "target_pad_2d" DXF — Ø4 mm brass target pad profile
|
||
//
|
||
// Export:
|
||
// openscad payload_bay_modules.scad -D 'RENDER="base_stl"' -o payload_base.stl
|
||
// openscad payload_bay_modules.scad -D 'RENDER="cargo_tray_stl"' -o payload_cargo_tray.stl
|
||
// openscad payload_bay_modules.scad -D 'RENDER="camera_boom_stl"' -o payload_camera_boom.stl
|
||
// openscad payload_bay_modules.scad -D 'RENDER="cup_holder_stl"' -o payload_cup_holder.stl
|
||
// ============================================================
|
||
|
||
$fn = 64;
|
||
e = 0.01;
|
||
|
||
// ── Rail geometry constants (must match payload_bay_rail.scad) ────────────────
|
||
RAIL_W = 50.0;
|
||
RAIL_T = 12.0;
|
||
DOVE_ANGLE = 30.0;
|
||
DOVE_H = 8.0;
|
||
DOVE_SLOT_BOT = 28.0;
|
||
DOVE_SLOT_TOP = DOVE_SLOT_BOT + 2 * DOVE_H * tan(DOVE_ANGLE);
|
||
DOVE_CLEAR = 0.3;
|
||
DETENT_PITCH = 50.0;
|
||
DETENT_BALL_D = 6.0;
|
||
DETENT_SPG_OD = 6.2;
|
||
DETENT_SPG_L = 16.0;
|
||
CONN_PIN_SPC = 5.0;
|
||
CONN_N_PINS = 4;
|
||
CONN_HOUSING_D = 8.0;
|
||
|
||
// ── Module tongue (male dovetail) geometry ────────────────────────────────────
|
||
TONGUE_BOT = DOVE_SLOT_BOT - 2*DOVE_CLEAR; // 27.4 mm
|
||
TONGUE_TOP = DOVE_SLOT_TOP + 2*DOVE_CLEAR; // 37.8 mm
|
||
TONGUE_H = DOVE_H + 0.2; // 8.2 mm (slight extra depth)
|
||
|
||
// ── Connector target pad ──────────────────────────────────────────────────────
|
||
TARGET_PAD_D = 4.0; // brass pad OD (slightly larger than pogo Ø2 mm)
|
||
TARGET_PAD_T = 1.5; // brass pad thickness
|
||
TARGET_PAD_RECESS = 1.3; // press-fit recess depth (pad is 0.2 mm proud)
|
||
// 4 pads at CONN_PIN_SPC pitch, centred in module tongue
|
||
CONN_SPAN = (CONN_N_PINS - 1) * CONN_PIN_SPC; // 15 mm
|
||
|
||
// ── Module safety lock ────────────────────────────────────────────────────────
|
||
LOCK_BOLT_D = 4.3; // M4 thumbscrew bore through tongue side
|
||
LOCK_BOLT_Z = TONGUE_H/2; // Z of thumbscrew CL from tongue bottom
|
||
// Thumbscrew tightens against continuous groove in rail side.
|
||
|
||
// Fasteners
|
||
M3_D = 3.3;
|
||
M4_D = 4.3;
|
||
|
||
// ============================================================
|
||
// RENDER DISPATCH
|
||
// ============================================================
|
||
RENDER = "assembly";
|
||
|
||
if (RENDER == "assembly") assembly();
|
||
else if (RENDER == "base_stl") _module_base(80);
|
||
else if (RENDER == "cargo_tray_stl") cargo_tray();
|
||
else if (RENDER == "camera_boom_stl") camera_boom();
|
||
else if (RENDER == "cup_holder_stl") cup_holder();
|
||
else if (RENDER == "target_pad_2d") {
|
||
projection(cut = true) translate([0, 0, -0.5])
|
||
linear_extrude(1) circle(d = TARGET_PAD_D);
|
||
}
|
||
|
||
// ============================================================
|
||
// ASSEMBLY PREVIEW
|
||
// ============================================================
|
||
module assembly() {
|
||
// Ghost rail
|
||
%color("Silver", 0.25)
|
||
translate([0, 0, -RAIL_T])
|
||
cube([RAIL_W, 600, RAIL_T], center = true);
|
||
|
||
// Cargo tray at Y=50 (first detent position)
|
||
color("OliveDrab", 0.85)
|
||
translate([0, 50, 0])
|
||
cargo_tray();
|
||
|
||
// Cup holder at Y=300
|
||
color("RoyalBlue", 0.85)
|
||
translate([0, 300, 0])
|
||
cup_holder();
|
||
|
||
// Camera boom at Y=500
|
||
color("DarkSlateGray", 0.85)
|
||
translate([0, 500, 0])
|
||
camera_boom();
|
||
}
|
||
|
||
// ============================================================
|
||
// UNIVERSAL MODULE BASE (_module_base)
|
||
// ============================================================
|
||
// All modules use this as their foundation.
|
||
// Provides: male dovetail tongue, detent bore, connector pads, lock bore.
|
||
//
|
||
// module_len : module length in Y (≥ 60 mm)
|
||
// n_detents : how many ball detent bores to include (1 or 2)
|
||
// conn_offset: Y offset of connector pads from module centre (default 0)
|
||
//
|
||
// After calling _module_base(), add payload geometry at Z = 0 and above.
|
||
// The tongue occupies Z = -(TONGUE_H) to Z = 0.
|
||
// Rail top face is at Z = 0.
|
||
module _module_base(module_len, n_detents = 1, conn_offset = 0) {
|
||
ml = module_len;
|
||
|
||
difference() {
|
||
// ── Dovetail tongue (male) ────────────────────────────────────
|
||
translate([0, ml/2, -TONGUE_H/2])
|
||
linear_extrude(TONGUE_H, center = true, twist = 0,
|
||
convexity = 2)
|
||
_tongue_profile_2d();
|
||
|
||
// ── Spring detent bore(s) (up through tongue top face) ────────
|
||
// 1 detent: at module centre; 2 detents: at ±25 mm from centre
|
||
for (i = [0 : n_detents - 1]) {
|
||
dy = (n_detents == 1)
|
||
? ml/2
|
||
: ml/2 + (i - (n_detents-1)/2) * DETENT_PITCH;
|
||
translate([0, dy, -(TONGUE_H/2) - DETENT_SPG_L/2])
|
||
cylinder(d = DETENT_SPG_OD,
|
||
h = DETENT_SPG_L + TONGUE_H/2 + e);
|
||
}
|
||
|
||
// ── Connector target pad recesses (tongue bottom face) ────────
|
||
for (i = [0 : CONN_N_PINS - 1]) {
|
||
px = (i - (CONN_N_PINS-1)/2) * CONN_PIN_SPC;
|
||
translate([px,
|
||
ml/2 + conn_offset,
|
||
-TONGUE_H + e])
|
||
cylinder(d = TARGET_PAD_D + 0.1,
|
||
h = TARGET_PAD_RECESS + e);
|
||
}
|
||
|
||
// ── Safety-lock M4 thumbscrew bore (right side of tongue) ─────
|
||
// Bore exits tongue right face, tip bears on rail side groove
|
||
translate([TONGUE_TOP/2 + e, ml/2, LOCK_BOLT_Z - TONGUE_H])
|
||
rotate([0, 90, 0])
|
||
cylinder(d = LOCK_BOLT_D,
|
||
h = (TONGUE_TOP - TONGUE_BOT)/2 + 6 + e);
|
||
|
||
// ── Lead-in chamfer on entry end of tongue ────────────────────
|
||
// 2 mm chamfer on bottom corners of tongue at Y=0 end
|
||
translate([0, 0, -TONGUE_H])
|
||
rotate([45, 0, 0])
|
||
cube([TONGUE_TOP + 2*e, 4, 4], center = true);
|
||
}
|
||
}
|
||
|
||
// ── Tongue 2D cross-section (male dovetail, trapezoid wider at bottom) ────────
|
||
// Module tongue: narrower at top (entering slot), wider at bottom (interlocking).
|
||
// Note orientation: tongue points DOWN (-Z), so wider face is at bottom (-Z).
|
||
module _tongue_profile_2d() {
|
||
polygon([
|
||
[-TONGUE_TOP/2, 0], // top-left (at Z = 0, flush with rail top)
|
||
[ TONGUE_TOP/2, 0], // top-right
|
||
[ TONGUE_BOT/2, -TONGUE_H], // bottom-right (interlocking face)
|
||
[-TONGUE_BOT/2, -TONGUE_H], // bottom-left
|
||
]);
|
||
}
|
||
|
||
// ============================================================
|
||
// PART 1 — CARGO TRAY
|
||
// ============================================================
|
||
// 200×100 mm open tray for transporting small items.
|
||
// 30 mm walls, chamfered rim, 4× drainage holes.
|
||
// Two Velcro strip slots on tray floor for cargo retention.
|
||
// Module length: 200 mm (4 detent positions).
|
||
// Weight budget: tray printed ~180 g; payload up to 2 kg.
|
||
TRAY_L = 200.0; // module Y length
|
||
TRAY_W = 100.0; // tray interior width (X)
|
||
TRAY_WALL = 3.0; // tray wall thickness
|
||
TRAY_H = 30.0; // tray wall height above module base
|
||
TRAY_FLOOR_T = 3.0; // tray floor thickness
|
||
|
||
module cargo_tray() {
|
||
// Mount base on rail (2 detents at ±50 mm from module centre)
|
||
_module_base(TRAY_L, n_detents = 2);
|
||
|
||
difference() {
|
||
union() {
|
||
// ── Tray body on top of base ────────────────────────────
|
||
translate([-TRAY_W/2 - TRAY_WALL, 0,
|
||
0])
|
||
cube([TRAY_W + 2*TRAY_WALL,
|
||
TRAY_L,
|
||
TRAY_FLOOR_T + TRAY_H]);
|
||
|
||
// ── Corner gussets (stiffening for 2 kg load) ───────────
|
||
for (cx = [-1, 1]) for (cy = [0, 1])
|
||
hull() {
|
||
translate([cx * TRAY_W/2,
|
||
cy * (TRAY_L - TRAY_WALL),
|
||
0])
|
||
cube([TRAY_WALL + e, TRAY_WALL + e,
|
||
TRAY_FLOOR_T + TRAY_H * 0.6], center = true);
|
||
translate([cx * (TRAY_W/2 - 10),
|
||
cy * (TRAY_L - TRAY_WALL),
|
||
0])
|
||
cylinder(d = TRAY_WALL * 2, h = e);
|
||
}
|
||
}
|
||
|
||
// ── Tray interior cavity ─────────────────────────────────────
|
||
translate([-TRAY_W/2, TRAY_WALL,
|
||
TRAY_FLOOR_T])
|
||
cube([TRAY_W, TRAY_L - 2*TRAY_WALL,
|
||
TRAY_H + e]);
|
||
|
||
// ── Drainage holes (4× Ø8 mm in floor) ──────────────────────
|
||
for (dx = [-TRAY_W/4, TRAY_W/4])
|
||
for (dy = [TRAY_L/4, 3*TRAY_L/4])
|
||
translate([dx, dy, -e])
|
||
cylinder(d = 8, h = TRAY_FLOOR_T + 2*e);
|
||
|
||
// ── Velcro slot × 2 (25 mm wide grooves in floor) ────────────
|
||
for (dy = [TRAY_L/3, 2*TRAY_L/3])
|
||
translate([0, dy, TRAY_FLOOR_T - 1.5])
|
||
cube([TRAY_W - 10, 25, 2 + e], center = true);
|
||
|
||
// ── Rim chamfer (top inner edge, ergonomic) ───────────────────
|
||
translate([0, TRAY_L/2, TRAY_FLOOR_T + TRAY_H + e])
|
||
cube([TRAY_W + 2*TRAY_WALL + 2*e,
|
||
TRAY_L + 2*e, 4], center = true);
|
||
|
||
// ── Side slots for bungee cord retention (3× each long side) ─
|
||
for (sx = [-1, 1])
|
||
for (sy = [TRAY_L/4, TRAY_L/2, 3*TRAY_L/4])
|
||
translate([sx * (TRAY_W/2 + TRAY_WALL/2),
|
||
sy, TRAY_FLOOR_T + TRAY_H/2])
|
||
cube([TRAY_WALL + 2*e, 8, 6], center = true);
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// PART 2 — CAMERA BOOM
|
||
// ============================================================
|
||
// L-shaped arm: vertical mast + horizontal boom.
|
||
// Boom head accepts sensor_rail 2020 T-slot (RAIL_W/2 bolt pattern).
|
||
// Sensor head can be rotated 0/90/180° and locked with M4 bolt.
|
||
// Module length: 80 mm; arm rises 120 mm, boom extends 80 mm forward.
|
||
BOOM_MODULE_L = 80.0;
|
||
BOOM_MAST_H = 120.0; // mast height above rail top (Z)
|
||
BOOM_ARM_L = 80.0; // horizontal boom length (+Y forward)
|
||
BOOM_ARM_W = 20.0; // arm cross-section width
|
||
BOOM_ARM_T = 20.0; // arm cross-section height
|
||
BOOM_HEAD_W = 50.0; // sensor head width (matches 2020 rail flange)
|
||
BOOM_HEAD_H = 20.0; // sensor head plate height
|
||
BOOM_HEAD_T = 5.0; // sensor head plate thickness
|
||
|
||
module camera_boom() {
|
||
_module_base(BOOM_MODULE_L, n_detents = 1);
|
||
|
||
difference() {
|
||
union() {
|
||
// ── Mast (vertical column from module body) ──────────────
|
||
translate([-BOOM_ARM_W/2, BOOM_MODULE_L/2 - BOOM_ARM_T/2, 0])
|
||
cube([BOOM_ARM_W, BOOM_ARM_T, BOOM_MAST_H]);
|
||
|
||
// ── Horizontal boom (from mast top, extends +Y) ──────────
|
||
translate([-BOOM_ARM_W/2,
|
||
BOOM_MODULE_L/2 - BOOM_ARM_T/2,
|
||
BOOM_MAST_H - BOOM_ARM_W])
|
||
cube([BOOM_ARM_W, BOOM_ARM_L, BOOM_ARM_W]);
|
||
|
||
// ── Sensor head plate (at boom tip) ──────────────────────
|
||
translate([-BOOM_HEAD_W/2,
|
||
BOOM_MODULE_L/2 - BOOM_ARM_T/2 + BOOM_ARM_L,
|
||
BOOM_MAST_H - BOOM_ARM_W - BOOM_HEAD_H/2 + BOOM_ARM_W/2])
|
||
cube([BOOM_HEAD_W, BOOM_HEAD_T, BOOM_HEAD_H]);
|
||
|
||
// ── Junction gussets (mast + horizontal boom) ────────────
|
||
translate([-BOOM_ARM_W/2,
|
||
BOOM_MODULE_L/2 - BOOM_ARM_T/2,
|
||
BOOM_MAST_H - BOOM_ARM_W])
|
||
rotate([45, 0, 0])
|
||
cube([BOOM_ARM_W, BOOM_ARM_W * 0.7, BOOM_ARM_W * 0.7]);
|
||
}
|
||
|
||
// ── 2020 sensor-rail bolt pattern in head plate ───────────────
|
||
// 2× M5 slots (matches sensor_rail.scad tank_clamp slot geometry)
|
||
for (sz = [-BOOM_HEAD_H/4, BOOM_HEAD_H/4])
|
||
translate([0,
|
||
BOOM_MODULE_L/2 - BOOM_ARM_T/2 + BOOM_ARM_L + BOOM_HEAD_T + e,
|
||
BOOM_MAST_H - BOOM_ARM_W/2 + sz])
|
||
rotate([90, 0, 0])
|
||
hull() {
|
||
translate([-6, 0, 0])
|
||
cylinder(d = 5.3, h = BOOM_HEAD_T + 2*e);
|
||
translate([+6, 0, 0])
|
||
cylinder(d = 5.3, h = BOOM_HEAD_T + 2*e);
|
||
}
|
||
|
||
// ── Tilt angle slots (3 positions: 0°, ±15°) ─────────────────
|
||
for (ta = [-15, 0, 15]) {
|
||
translate([0,
|
||
BOOM_MODULE_L/2 - BOOM_ARM_T/2 + BOOM_ARM_L/2,
|
||
BOOM_MAST_H - BOOM_ARM_W/2])
|
||
rotate([ta, 0, 0])
|
||
translate([0, BOOM_ARM_L/2, 0])
|
||
cylinder(d = 4.3, h = BOOM_ARM_W + 2*e,
|
||
center = true);
|
||
}
|
||
|
||
// ── Cable tie slots in mast ───────────────────────────────────
|
||
for (cz = [BOOM_MAST_H * 0.3, BOOM_MAST_H * 0.6])
|
||
translate([0, BOOM_MODULE_L/2, cz])
|
||
cube([BOOM_ARM_W + 2*e, 4, 4], center = true);
|
||
|
||
// ── Lightening pockets in mast ────────────────────────────────
|
||
translate([0, BOOM_MODULE_L/2, BOOM_MAST_H * 0.4])
|
||
cube([BOOM_ARM_W - 6, BOOM_ARM_T - 6,
|
||
BOOM_MAST_H * 0.4], center = true);
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// PART 3 — CUP HOLDER
|
||
// ============================================================
|
||
// Tapered cup cradle for standard travel mugs / water bottles.
|
||
// Inner diameter: 80 mm at top, 68 mm at bottom (matches Ø70 mm typical mug).
|
||
// Flexible gripper ribs (cut slots) provide spring retention.
|
||
// Drain hole at bottom for condensation.
|
||
// Module length: 80 mm.
|
||
CUP_MODULE_L = 80.0;
|
||
CUP_INNER_TOP = 80.0; // inner bore OD at top
|
||
CUP_INNER_BOT = 68.0; // inner bore OD at bottom (taper for grip)
|
||
CUP_OUTER_T = 4.0; // wall thickness
|
||
CUP_H = 80.0; // cup holder height
|
||
CUP_GRIP_SLOTS = 8; // number of flex slots (spring grip)
|
||
CUP_SLOT_W = 2.5; // flex slot width
|
||
CUP_SLOT_H = 40.0; // flex slot height (from top)
|
||
|
||
module cup_holder() {
|
||
_module_base(CUP_MODULE_L, n_detents = 1);
|
||
|
||
difference() {
|
||
union() {
|
||
// ── Outer shell (tapered cylinder) ───────────────────────
|
||
cylinder(d1 = CUP_INNER_BOT + 2*CUP_OUTER_T,
|
||
d2 = CUP_INNER_TOP + 2*CUP_OUTER_T,
|
||
h = CUP_H);
|
||
|
||
// ── Base flange (connects to module body footprint) ───────
|
||
hull() {
|
||
cylinder(d = CUP_INNER_BOT + 2*CUP_OUTER_T + 4,
|
||
h = 4);
|
||
translate([0, CUP_MODULE_L/2, 0])
|
||
cube([RAIL_W, CUP_MODULE_L, 4], center = true);
|
||
}
|
||
}
|
||
|
||
// ── Inner bore (tapered cup cavity) ──────────────────────────
|
||
translate([0, 0, CUP_OUTER_T])
|
||
cylinder(d1 = CUP_INNER_BOT, d2 = CUP_INNER_TOP,
|
||
h = CUP_H + e);
|
||
|
||
// ── Base drain hole ──────────────────────────────────────────
|
||
translate([0, 0, -e])
|
||
cylinder(d = 12, h = CUP_OUTER_T + 2*e);
|
||
|
||
// ── Flex grip slots (from top down) ──────────────────────────
|
||
// Slots allow upper rim to flex inward and grip cup body
|
||
for (i = [0 : CUP_GRIP_SLOTS - 1]) {
|
||
angle = i * 360 / CUP_GRIP_SLOTS;
|
||
rotate([0, 0, angle])
|
||
translate([CUP_INNER_TOP/2 + CUP_OUTER_T/2, 0, CUP_H - CUP_SLOT_H])
|
||
cube([CUP_OUTER_T + 2*e, CUP_SLOT_W, CUP_SLOT_H + e],
|
||
center = true);
|
||
}
|
||
|
||
// ── Exterior branding recess (optional label area) ────────────
|
||
translate([CUP_INNER_BOT/2 + CUP_OUTER_T/2 - 0.5, 0, CUP_H/2])
|
||
rotate([0, 90, 0])
|
||
cube([25, 40, 1 + e], center = true);
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// ══ MODULE TEMPLATE ═══════════════════════════════════════════
|
||
// ══ Copy this block to create a new payload module ════════════
|
||
// ============================================================
|
||
//
|
||
// module my_new_module() {
|
||
// MY_LEN = 120.0; // module length — must be multiple of DETENT_PITCH (50 mm)
|
||
//
|
||
// // Always start with the base (provides tongue, pads, detent bore)
|
||
// _module_base(MY_LEN, n_detents = 2);
|
||
//
|
||
// // Add your payload geometry here.
|
||
// // Z = 0 is the rail top face / module mounting face.
|
||
// // Build upward from Z = 0.
|
||
//
|
||
// difference() {
|
||
// union() {
|
||
// // Example: a simple platform
|
||
// translate([-(RAIL_W + 10)/2, 0, 0])
|
||
// cube([RAIL_W + 10, MY_LEN, 10]);
|
||
//
|
||
// // Add your geometry...
|
||
// }
|
||
//
|
||
// // Add your cutouts...
|
||
// }
|
||
// }
|
||
//
|
||
// ── Don't forget to: ──────────────────────────────────────────
|
||
// 1. Add else if (RENDER == "my_module_stl") my_new_module();
|
||
// in the RENDER DISPATCH block above.
|
||
// 2. Add export command in BOM / README.
|
||
// 3. Test: verify tongue fits rail slot (should slide with 0.3 mm clearance).
|
||
// 4. Verify connector pad positions align with CONN_Y on rail.
|
||
// ============================================================
|