Add 2020 T-slot quick-swap sensor rail for SaltyLab/Rover/Tank variants: - sensor_rail.scad: 2020 T-slot profile, T-nut, thumbscrew, end cap, index pins, stem/post/tank clamp adapters - sensor_rail_brackets.scad: universal T-nut base + RPLIDAR A1M8, D435i, IMX219, UWB anchor, cable clip brackets (tool-free M3 thumbscrew retention) - sensor_rail_BOM.md: purchased hardware, print settings, export commands Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
565 lines
24 KiB
OpenSCAD
565 lines
24 KiB
OpenSCAD
// ============================================================
|
||
// sensor_rail.scad — Universal Sensor Mount Rail System
|
||
// Issue: #138 Agent: sl-mechanical Date: 2026-03-01
|
||
// ============================================================
|
||
//
|
||
// T-slot 20×20 mm rail system for quick-swap sensor mounting
|
||
// across all SaltyLab robot variants.
|
||
//
|
||
// Rail profile: compatible with OpenBuilds 2020 / MISUMI HFS5-2020
|
||
// standard aluminium extrusion (off-the-shelf preferred).
|
||
// Printable PETG sections provided for rapid prototyping only.
|
||
//
|
||
// Height indexing: M5 cross-holes every 25 mm along rail length.
|
||
// Brackets can be positioned at any height (stepless) and locked
|
||
// with a thumbscrew, or indexed to 25 mm grid by aligning to holes.
|
||
//
|
||
// Bracket retention: M3 thumbscrew (no tools, ¼ turn).
|
||
// Each bracket has a printed T-nut in the rail T-groove.
|
||
// Thumbscrew clamps T-nut against groove walls from outside.
|
||
//
|
||
// Cross-variant base adapters (this file):
|
||
// • stem_adapter() — Ø25 mm stem (SaltyLab / SaltyRover mast)
|
||
// • post_adapter() — square tube (SaltyRover vertical posts)
|
||
// • tank_clamp() — flat plate clamp (SaltyTank uprights)
|
||
//
|
||
// Sensor brackets → see sensor_rail_brackets.scad
|
||
//
|
||
// Coordinate convention:
|
||
// Rail runs along Z (vertical).
|
||
// Rail cross-section in X-Y plane.
|
||
// Front face (sensor side) faces +Y.
|
||
// Z = 0 at rail bottom.
|
||
//
|
||
// RENDER options:
|
||
// "assembly" full rail + adapters + bracket ghosts (default)
|
||
// "rail_2d" DXF — rail profile cross-section (spec for extrusion)
|
||
// "rail_section_stl" STL — printable rail section (prototype only)
|
||
// "stem_adapter_stl" STL — Ø25 mm stem adapter (print 1×)
|
||
// "post_adapter_stl" STL — square-tube post adapter (print 1×)
|
||
// "tank_clamp_stl" STL — flat-plate tank upright clamp (print 1×)
|
||
// "end_cap_stl" STL — rail end cap (print 2× per rail)
|
||
// "index_pin_stl" STL — 25 mm index pin set (print 1 set)
|
||
//
|
||
// ── Export commands ─────────────────────────────────────────
|
||
// Rail profile DXF (send to extrusion supplier):
|
||
// openscad sensor_rail.scad -D 'RENDER="rail_2d"' -o sensor_rail_profile.dxf
|
||
// Printable rail section STL (200 mm, prototype):
|
||
// openscad sensor_rail.scad -D 'RENDER="rail_section_stl"' -o sensor_rail_200.stl
|
||
// Stem adapter:
|
||
// openscad sensor_rail.scad -D 'RENDER="stem_adapter_stl"' -o sensor_rail_stem_adapter.stl
|
||
// Post adapter:
|
||
// openscad sensor_rail.scad -D 'RENDER="post_adapter_stl"' -o sensor_rail_post_adapter.stl
|
||
// Tank clamp:
|
||
// openscad sensor_rail.scad -D 'RENDER="tank_clamp_stl"' -o sensor_rail_tank_clamp.stl
|
||
// End cap:
|
||
// openscad sensor_rail.scad -D 'RENDER="end_cap_stl"' -o sensor_rail_end_cap.stl
|
||
// ============================================================
|
||
|
||
$fn = 64;
|
||
e = 0.01;
|
||
|
||
// ── 2020 T-slot profile geometry ─────────────────────────────────────────────
|
||
// Matches OpenBuilds V-Slot 2020 / MISUMI HFS5-2020 / standard 2020 T-slot.
|
||
// ⚠ Do NOT modify these — they must match the aluminium extrusion you purchase.
|
||
RAIL_W = 20.0; // outer width/height of profile
|
||
SLOT_OPEN = 6.0; // T-groove opening width at outer face
|
||
SLOT_INNER_W = 10.2; // T-groove inner width
|
||
SLOT_INNER_H = 5.8; // T-groove inner height (depth)
|
||
SLOT_NECK_H = 3.2; // distance from outer face to T-groove inner
|
||
CENTRAL_BORE = 4.2; // central M5 bore diameter (tap drill)
|
||
CORNER_NOTCH = 1.6; // corner chamfer radius (standard 2020)
|
||
|
||
// ── Rail section parameters ───────────────────────────────────────────────────
|
||
RAIL_LEN = 200.0; // default section length (printable prototype)
|
||
// For aluminium: order in 200 / 250 / 300 mm lengths as needed.
|
||
|
||
// ── Index holes ───────────────────────────────────────────────────────────────
|
||
INDEX_PITCH = 25.0; // index hole spacing (mm) — height adjustment grid
|
||
INDEX_HOLE_D = 5.3; // M5 clearance through rail (perpendicular to Z)
|
||
// Index holes are on the LEFT and RIGHT faces of the rail (±X faces),
|
||
// perpendicular to Z (rail axis). Bracket T-nut has an M5 registration
|
||
// peg that drops into these holes for repeatable indexed positioning.
|
||
|
||
// ── Printable T-nut (used by sensor_rail_brackets.scad) ──────────────────────
|
||
// These dims define the T-nut that slides in the SLOT_INNER T-groove.
|
||
TNUT_W = SLOT_INNER_W - 0.4; // 9.8 mm — 0.4 clearance per side
|
||
TNUT_H = SLOT_INNER_H - 0.3; // 5.5 mm
|
||
TNUT_L = 12.0; // T-nut body length
|
||
TNUT_M3_NUT_AF = 5.5; // M3 hex nut across-flats (DIN 934)
|
||
TNUT_M3_NUT_H = 2.5; // M3 hex nut thickness
|
||
TNUT_BOLT_D = 3.3; // M3 clearance bore through T-nut
|
||
|
||
// ── Thumbscrew (M3 × 16 SHCS + printed thumbwheel) ───────────────────────────
|
||
THUMB_D = 16.0; // thumbwheel OD
|
||
THUMB_H = 8.0; // thumbwheel height
|
||
THUMB_KNURL = 12; // number of knurl ridges on thumbwheel
|
||
|
||
// ── Stem adapter (Ø25 mm — SaltyLab / SaltyRover mast) ───────────────────────
|
||
STEM_OD = 25.0; // SaltyLab / SaltyRover stem OD
|
||
STEM_BORE = 25.4; // collar bore with clearance
|
||
STEM_COL_OD = 46.0; // collar outer diameter
|
||
STEM_COL_H = 40.0; // collar height
|
||
STEM_BOLT_X = 17.0; // M4 clamping bolt CL from stem axis
|
||
STEM_RAIL_W = 60.0; // rail-mounting flange width
|
||
STEM_RAIL_H = 30.0; // rail-mounting flange height
|
||
|
||
// ── Post adapter (square tube, SaltyRover frame posts) ───────────────────────
|
||
POST_W = 20.0; // square tube OD (20×20 mm aluminium post)
|
||
POST_WALL = 2.0; // tube wall thickness
|
||
POST_CLAMP_T = 4.0; // clamp plate thickness
|
||
POST_CLAMP_W = 50.0; // clamp plate width
|
||
POST_CLAMP_H = 60.0; // clamp plate height
|
||
|
||
// ── Tank clamp (flat plate, SaltyTank side frame uprights) ───────────────────
|
||
TANK_PLATE_T = 6.0; // SaltyTank side frame plate thickness (matches FRAME_T)
|
||
TANK_CLAMP_W = 50.0; // clamp body width
|
||
TANK_CLAMP_H = 60.0; // clamp body height
|
||
TANK_BOLT_SPC= 30.0; // M4 bolt spacing for clamp-to-frame attachment
|
||
|
||
// ── Fasteners ─────────────────────────────────────────────────────────────────
|
||
M3_D = 3.3;
|
||
M4_D = 4.3;
|
||
M5_D = 5.3;
|
||
|
||
// ============================================================
|
||
// RENDER DISPATCH
|
||
// ============================================================
|
||
RENDER = "assembly";
|
||
|
||
if (RENDER == "assembly") {
|
||
assembly();
|
||
} else if (RENDER == "rail_2d") {
|
||
projection(cut = true)
|
||
translate([0, 0, -RAIL_W/2])
|
||
rotate([90, 0, 0])
|
||
rail_section(RAIL_W);
|
||
} else if (RENDER == "rail_section_stl") {
|
||
rail_section(RAIL_LEN);
|
||
} else if (RENDER == "stem_adapter_stl") {
|
||
stem_adapter();
|
||
} else if (RENDER == "post_adapter_stl") {
|
||
post_adapter();
|
||
} else if (RENDER == "tank_clamp_stl") {
|
||
tank_clamp();
|
||
} else if (RENDER == "end_cap_stl") {
|
||
rail_end_cap();
|
||
} else if (RENDER == "index_pin_stl") {
|
||
index_pin_set();
|
||
}
|
||
|
||
// ============================================================
|
||
// ASSEMBLY PREVIEW
|
||
// ============================================================
|
||
module assembly() {
|
||
// Rail section (200 mm, vertical)
|
||
color("Silver", 0.85) rail_section(200);
|
||
|
||
// Stem adapter at base
|
||
color("SteelBlue", 0.80)
|
||
translate([0, 0, -STEM_COL_H - 10])
|
||
stem_adapter();
|
||
|
||
// End cap at top
|
||
color("DimGray", 0.80)
|
||
translate([0, 0, 200])
|
||
rail_end_cap();
|
||
|
||
// Ghost sensor brackets (from sensor_rail_brackets.scad)
|
||
// RPLIDAR at top
|
||
%color("OliveDrab", 0.3)
|
||
translate([0, RAIL_W/2 + 5, 170])
|
||
cube([90, 20, 60], center = true);
|
||
|
||
// D435i at middle
|
||
%color("DarkSlateGray", 0.3)
|
||
translate([0, RAIL_W/2 + 5, 100])
|
||
cube([95, 20, 30], center = true);
|
||
|
||
// IMX219 at lower
|
||
%color("Teal", 0.3)
|
||
translate([0, RAIL_W/2 + 5, 50])
|
||
cube([40, 20, 40], center = true);
|
||
|
||
// Index hole markers
|
||
for (z = [0 : INDEX_PITCH : 200])
|
||
%color("Red", 0.4)
|
||
translate([RAIL_W/2 + 2, 0, z])
|
||
rotate([0, 90, 0])
|
||
cylinder(d = 3, h = 4);
|
||
}
|
||
|
||
// ============================================================
|
||
// RAIL SECTION (Part A — aluminium extrusion or PETG print)
|
||
// ============================================================
|
||
// Standard 2020 T-slot profile extruded along Z axis.
|
||
// This OpenSCAD module is primarily for:
|
||
// 1. DXF export to spec the extrusion cross-section for a supplier
|
||
// 2. Printable prototype sections (print in PETG, 5 perims, 60% infill)
|
||
//
|
||
// For production: purchase OpenBuilds V-Slot 2020 in desired length.
|
||
// Search: "2020 V-slot aluminium extrusion" or "2020 T-slot rail"
|
||
// Cut to length with a mitre saw. Tap central M5 bore at ends for
|
||
// end-mounting and rail-to-adapter connections.
|
||
//
|
||
// Index holes: drilled/tapped M5 on LEFT and RIGHT faces (±X)
|
||
// at 25 mm pitch. Specify when ordering pre-drilled extrusion,
|
||
// or drill manually after cutting.
|
||
module rail_section(length = RAIL_LEN) {
|
||
difference() {
|
||
// ── Extrude the 2020 profile ─────────────────────────────────
|
||
linear_extrude(length)
|
||
tslot_profile_2d();
|
||
|
||
// ── Central bore (M5 tap drill, both ends) ────────────────────
|
||
translate([0, 0, -e])
|
||
cylinder(d = CENTRAL_BORE, h = length + 2*e);
|
||
|
||
// ── Index holes (M5 clearance, ±X faces, every 25 mm) ─────────
|
||
for (z = [INDEX_PITCH/2 : INDEX_PITCH : length - INDEX_PITCH/2])
|
||
translate([-RAIL_W/2 - e, 0, z])
|
||
rotate([0, 90, 0])
|
||
cylinder(d = INDEX_HOLE_D, h = RAIL_W + 2*e);
|
||
}
|
||
}
|
||
|
||
// ── 2020 T-slot cross-section (2D profile for linear_extrude) ─────────────────
|
||
// Matches OpenBuilds V-Slot 2020 profile.
|
||
module tslot_profile_2d() {
|
||
difference() {
|
||
// Outer square with corner notches
|
||
difference() {
|
||
square([RAIL_W, RAIL_W], center = true);
|
||
// Corner notches (standard 2020 chamfer)
|
||
for (cx = [-1, 1])
|
||
for (cy = [-1, 1])
|
||
translate([cx * (RAIL_W/2 - CORNER_NOTCH/2),
|
||
cy * (RAIL_W/2 - CORNER_NOTCH/2)])
|
||
rotate([0, 0, 45])
|
||
square([CORNER_NOTCH * 1.41, CORNER_NOTCH * 1.41],
|
||
center = true);
|
||
}
|
||
|
||
// Central lightening bore
|
||
circle(d = CENTRAL_BORE);
|
||
|
||
// Internal corner channels (weight reduction, standard in 2020)
|
||
for (cx = [-1, 1])
|
||
for (cy = [-1, 1])
|
||
translate([cx * (RAIL_W/4 + 0.5), cy * (RAIL_W/4 + 0.5)])
|
||
circle(d = 3.2);
|
||
|
||
// 4× T-grooves (one per face)
|
||
for (rot = [0, 90, 180, 270])
|
||
rotate([0, 0, rot])
|
||
tslot_groove_2d(face_dist = RAIL_W/2);
|
||
}
|
||
}
|
||
|
||
// ── Single T-groove profile (2D, centred on face at face_dist from origin) ────
|
||
module tslot_groove_2d(face_dist) {
|
||
// Outer slot opening (tapered/chamfered entry)
|
||
translate([0, face_dist - SLOT_NECK_H])
|
||
square([SLOT_OPEN, SLOT_NECK_H + e], center = true);
|
||
|
||
// Inner T-groove
|
||
translate([0, face_dist - SLOT_NECK_H - SLOT_INNER_H + e])
|
||
square([SLOT_INNER_W, SLOT_INNER_H + e], center = true);
|
||
}
|
||
|
||
// ============================================================
|
||
// PRINTABLE T-NUT (Part B — print ×N as needed in PETG)
|
||
// ============================================================
|
||
// Slides into the T-groove of the 2020 rail.
|
||
// Captured M3 hex nut allows a thumbscrew to clamp from outside.
|
||
// The T-nut has a registration peg that drops into index holes.
|
||
//
|
||
// Print: PETG, flat face down, 5 perims, 60% infill.
|
||
// Standard M3 hex nut pressed in from top after printing.
|
||
module printable_tnut() {
|
||
difference() {
|
||
union() {
|
||
// Main body (fits inside T-groove)
|
||
cube([TNUT_W, TNUT_L, TNUT_H], center = true);
|
||
|
||
// Wings that sit behind slot opening (wider than SLOT_OPEN)
|
||
// These wings bear against the T-groove inner walls
|
||
translate([0, 0, TNUT_H/2 - 0.8])
|
||
cube([TNUT_W, TNUT_L, 1.6], center = true);
|
||
}
|
||
|
||
// M3 hex nut pocket (press-fit from top)
|
||
translate([0, 0, TNUT_H/2 - TNUT_M3_NUT_H - 0.3])
|
||
cylinder(d = TNUT_M3_NUT_AF / cos(30),
|
||
h = TNUT_M3_NUT_H + 0.4,
|
||
$fn = 6);
|
||
|
||
// M3 clearance bore (through T-nut, for thumbscrew shank)
|
||
cylinder(d = TNUT_BOLT_D, h = TNUT_H + 2*e, center = true);
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// THUMBSCREW WHEEL (Part C — print ×N as needed in PETG)
|
||
// ============================================================
|
||
// Press-fit onto M3×16 SHCS head. Provides finger grip for
|
||
// tool-free tightening. One per bracket.
|
||
// Press-fit bore: 5.6 mm (M3 SHCS head hex socket OD ≈ 5.5 mm)
|
||
module thumbscrew_wheel() {
|
||
difference() {
|
||
union() {
|
||
cylinder(d = THUMB_D, h = THUMB_H);
|
||
// Knurl ridges (cosmetic grooves — helps grip)
|
||
for (i = [0 : THUMB_KNURL - 1])
|
||
rotate([0, 0, i * 360 / THUMB_KNURL])
|
||
translate([THUMB_D/2 - 1, 0, 0])
|
||
cylinder(d = 2.0, h = THUMB_H);
|
||
}
|
||
// M3 SHCS head hex socket bore (press-fit)
|
||
translate([0, 0, -e])
|
||
cylinder(d = 5.7, h = 4);
|
||
// M3 shank clearance bore (bolt passes through)
|
||
cylinder(d = M3_D, h = THUMB_H + 2*e);
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// RAIL END CAP (Part D — print 2× per rail in PETG)
|
||
// ============================================================
|
||
// Safety end cap — prevents T-nuts from sliding off end.
|
||
// Snap-friction fit (no fasteners needed for prototyping).
|
||
// Or: drill M5 through cap + rail end, use M5×10 set screw.
|
||
module rail_end_cap() {
|
||
cap_h = 8;
|
||
cap_plug_h = 6; // depth inserted into rail end
|
||
|
||
difference() {
|
||
union() {
|
||
// Outer flange (≥ rail OD, prevents over-insertion)
|
||
cylinder(d = RAIL_W + 6, h = cap_h);
|
||
|
||
// Inner plug (fits inside rail with 0.3 mm clearance per side)
|
||
translate([0, 0, cap_h - e])
|
||
cube([RAIL_W - 0.6, RAIL_W - 0.6, cap_plug_h], center = true);
|
||
}
|
||
|
||
// Central M5 bore (for optional end-bolt)
|
||
cylinder(d = M5_D, h = cap_h + cap_plug_h + e);
|
||
|
||
// Lightening cutout
|
||
translate([0, 0, 2])
|
||
cube([RAIL_W - 4, RAIL_W - 4, cap_h + cap_plug_h], center = true);
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// INDEX PIN SET (Part E — print 1 set in PETG)
|
||
// ============================================================
|
||
// Small pins that insert through M5 index holes on the rail.
|
||
// When a bracket T-nut has a matching recess, the pin provides
|
||
// positive indexed positioning (true 25 mm grid lock).
|
||
// For non-indexed positioning: leave pins out, use thumbscrew only.
|
||
module index_pin_set() {
|
||
// Print 4 pins on a carrier plate for easy identification
|
||
for (i = [0:3])
|
||
translate([i * 20, 0, 0])
|
||
index_pin();
|
||
}
|
||
|
||
module index_pin() {
|
||
pin_od = 4.9; // M5 hole clearance (5.3 - 0.4 mm)
|
||
pin_len = RAIL_W + 2; // spans full rail width + 1 mm each side
|
||
head_od = 8.0; // knurled head OD (finger pull)
|
||
head_h = 5.0;
|
||
|
||
union() {
|
||
// Pin shaft
|
||
cylinder(d = pin_od, h = pin_len);
|
||
// Knurled head (at one end)
|
||
translate([0, 0, pin_len])
|
||
difference() {
|
||
cylinder(d = head_od, h = head_h);
|
||
for (j = [0:7])
|
||
rotate([0, 0, j*45])
|
||
translate([head_od/2 - 1.2, 0, 0])
|
||
cylinder(d = 2, h = head_h + e);
|
||
}
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// STEM ADAPTER (Part F — SaltyLab / SaltyRover Ø25 mm mast)
|
||
// ============================================================
|
||
// Split collar clamps to the robot's Ø25 mm vertical stem.
|
||
// Two rail-mounting flanges (front + rear) allow one or two
|
||
// sensor rails to be mounted at 180° apart.
|
||
//
|
||
// Print 2× halves (front + rear), join with 2× M4×30 SHCS.
|
||
// Each half is printed flat-face-down with no supports needed.
|
||
module stem_adapter() {
|
||
// Front half only — print 2× and assemble around stem
|
||
stem_adapter_half("front");
|
||
}
|
||
|
||
module stem_adapter_half(side = "front") {
|
||
sy = (side == "front") ? 1 : -1;
|
||
|
||
difference() {
|
||
union() {
|
||
// ── Collar half (semicircle) ─────────────────────────────
|
||
rotate_extrude(angle = 180)
|
||
translate([STEM_COL_OD/2 - (STEM_COL_OD - STEM_BORE)/2, 0, 0])
|
||
square([(STEM_COL_OD - STEM_BORE)/2,
|
||
STEM_COL_H], center = false);
|
||
|
||
// ── Rail mounting flange (extends forward from collar) ────
|
||
// Single flange on front face, centred left-right
|
||
translate([-STEM_RAIL_W/2,
|
||
sy * (STEM_COL_OD/2 - 2),
|
||
STEM_COL_H/2 - STEM_RAIL_H/2])
|
||
cube([STEM_RAIL_W, STEM_COL_OD/4 + 5, STEM_RAIL_H]);
|
||
}
|
||
|
||
// ── Stem bore ────────────────────────────────────────────────
|
||
translate([0, 0, -e])
|
||
cylinder(d = STEM_BORE, h = STEM_COL_H + 2*e);
|
||
|
||
// ── M4 clamping bolt holes (through collar flanges) ───────────
|
||
for (sx = [-1, 1])
|
||
translate([sx * STEM_BOLT_X, 0, STEM_COL_H / 2])
|
||
rotate([90, 0, 0])
|
||
cylinder(d = M4_D, h = STEM_COL_OD + 2*e, center = true);
|
||
|
||
// ── M4 nut pocket (rear half) ─────────────────────────────────
|
||
if (side == "rear") {
|
||
for (sx = [-1, 1])
|
||
translate([sx * STEM_BOLT_X, -STEM_COL_OD/2 + 4,
|
||
STEM_COL_H/2])
|
||
rotate([90, 0, 0]) {
|
||
cylinder(d = 7.5/cos(30), h = 4, $fn = 6); // M4 nut
|
||
}
|
||
}
|
||
|
||
// ── Rail attachment slots in flange (M5 × 2) ─────────────────
|
||
// Slots allow ±5 mm front-back fine adjustment of rail position
|
||
for (rz = [-STEM_RAIL_H/4, STEM_RAIL_H/4])
|
||
translate([0,
|
||
sy * (STEM_COL_OD/2 - 2 + STEM_COL_OD/8 + 5/2),
|
||
STEM_COL_H/2 + rz])
|
||
rotate([90, 0, 0])
|
||
hull() {
|
||
translate([-6, 0, 0])
|
||
cylinder(d = M5_D, h = 10, center = true);
|
||
translate([+6, 0, 0])
|
||
cylinder(d = M5_D, h = 10, center = true);
|
||
}
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// POST ADAPTER (Part G — SaltyRover 20×20 mm square post)
|
||
// ============================================================
|
||
// C-clamp style bracket that wraps around a 20×20 mm aluminium
|
||
// post (common in rover extrusion frames).
|
||
// M4 × 2 clamping bolts through the clamp flanges.
|
||
// Rail mounts forward via 2× M5 slots in the front face.
|
||
module post_adapter() {
|
||
// Outer clamp body
|
||
difference() {
|
||
union() {
|
||
// Main C-clamp body
|
||
translate([-POST_CLAMP_W/2, -POST_W/2 - POST_CLAMP_T,
|
||
0])
|
||
cube([POST_CLAMP_W,
|
||
POST_W + 2*POST_CLAMP_T + POST_CLAMP_W/3,
|
||
POST_CLAMP_H]);
|
||
}
|
||
|
||
// Post cavity (POST_W × POST_W + clearance)
|
||
translate([0, 0, -e])
|
||
cube([POST_W + 0.4, POST_W + 0.4, POST_CLAMP_H + 2*e],
|
||
center = true);
|
||
|
||
// C-clamp opening (rear gap — allows clamping around post)
|
||
translate([-POST_W/2 - POST_CLAMP_T/2 - e, -POST_W/2 - e, -e])
|
||
cube([POST_CLAMP_T + 2*e,
|
||
POST_W + 0.2,
|
||
POST_CLAMP_H + 2*e]);
|
||
|
||
// M4 clamping bolt holes
|
||
for (cz = [POST_CLAMP_H/4, 3*POST_CLAMP_H/4])
|
||
translate([-POST_CLAMP_W/2 - e, 0, cz])
|
||
rotate([0, 90, 0])
|
||
cylinder(d = M4_D, h = POST_CLAMP_W + 2*e);
|
||
|
||
// M4 nut pockets (right flange)
|
||
for (cz = [POST_CLAMP_H/4, 3*POST_CLAMP_H/4])
|
||
translate([POST_CLAMP_W/2 - 5, 0, cz])
|
||
rotate([0, 90, 0])
|
||
cylinder(d = 7.5/cos(30), h = 5, $fn = 6);
|
||
|
||
// Rail mounting slots (M5 × 2, front face)
|
||
for (cz = [POST_CLAMP_H/3, 2*POST_CLAMP_H/3])
|
||
translate([0,
|
||
POST_W/2 + POST_CLAMP_T + POST_CLAMP_W/3 - e,
|
||
cz])
|
||
rotate([90, 0, 0])
|
||
hull() {
|
||
translate([-5, 0, 0])
|
||
cylinder(d = M5_D,
|
||
h = POST_CLAMP_T + 2*e);
|
||
translate([+5, 0, 0])
|
||
cylinder(d = M5_D,
|
||
h = POST_CLAMP_T + 2*e);
|
||
}
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// TANK CLAMP (Part H — SaltyTank side frame upright)
|
||
// ============================================================
|
||
// Flat plate bracket bolts directly to SaltyTank side frame
|
||
// (6 mm Al plate) via 4× M4 SHCS.
|
||
// Rail attaches to the front face via 2× M5 slots.
|
||
module tank_clamp() {
|
||
difference() {
|
||
union() {
|
||
// Main back plate (bolts to tank frame)
|
||
translate([-TANK_CLAMP_W/2, -TANK_PLATE_T - 2, 0])
|
||
cube([TANK_CLAMP_W,
|
||
TANK_PLATE_T + RAIL_W + 10,
|
||
TANK_CLAMP_H]);
|
||
}
|
||
|
||
// Tank frame plate slot (open at rear, receives frame edge)
|
||
translate([-TANK_CLAMP_W/2 + 5, -TANK_PLATE_T - e, -e])
|
||
cube([TANK_CLAMP_W - 10,
|
||
TANK_PLATE_T + 0.5,
|
||
TANK_CLAMP_H + 2*e]);
|
||
|
||
// 4× M4 frame attachment bolts
|
||
for (bx = [-TANK_BOLT_SPC/2, TANK_BOLT_SPC/2])
|
||
for (bz = [TANK_CLAMP_H/4, 3*TANK_CLAMP_H/4])
|
||
translate([bx, -TANK_PLATE_T/2, bz])
|
||
rotate([90, 0, 0])
|
||
cylinder(d = M4_D, h = TANK_PLATE_T + 4, center = true);
|
||
|
||
// Rail mounting slots (M5 × 2, front face)
|
||
for (bz = [TANK_CLAMP_H/3, 2*TANK_CLAMP_H/3])
|
||
translate([0, RAIL_W + 9 - e, bz])
|
||
rotate([90, 0, 0])
|
||
hull() {
|
||
translate([-6, 0, 0])
|
||
cylinder(d = M5_D, h = 10);
|
||
translate([+6, 0, 0])
|
||
cylinder(d = M5_D, h = 10);
|
||
}
|
||
|
||
// Weight-reduction pockets (back plate)
|
||
translate([0, -TANK_PLATE_T/2 - 1, TANK_CLAMP_H/2])
|
||
cube([TANK_CLAMP_W - 20,
|
||
4, TANK_CLAMP_H - 30],
|
||
center = true);
|
||
}
|
||
}
|