Parametric OpenSCAD design for 2020 T-slot rail phone mount bracket. Adjustable width 60-85mm, spring-loaded cam quick-release lever, vibration-dampening flexure rib grip pads. PETG 3D-printable, no supports. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
505 lines
24 KiB
OpenSCAD
505 lines
24 KiB
OpenSCAD
// ============================================================
|
||
// phone_mount_bracket.scad — Spring-Loaded Phone Mount for T-Slot Rail
|
||
// Issue: #535 Agent: sl-mechanical Date: 2026-03-07
|
||
// ============================================================
|
||
//
|
||
// Parametric spring-loaded phone mount that clamps to the 2020 aluminium
|
||
// T-slot sensor rail. Adjustable phone width 60–85 mm. Quick-release
|
||
// cam lever for tool-free phone swap. Vibration-damping flexure ribs
|
||
// on grip pads absorb motor/terrain vibration (PETG compliance).
|
||
//
|
||
// Design overview:
|
||
// - Fixed jaw + sliding jaw on a 40 mm guide rail (M4 rod)
|
||
// - Coil spring (Ø8 × 30 mm) compressed between jaw and end-stop —
|
||
// spring pre-load keeps phone clamped at any width in range
|
||
// - Cam lever (printed PETG) rotates 90° to release / lock spring
|
||
// - Anti-vibration flexure ribs on both grip pad faces
|
||
// - Landscape or portrait orientation: bracket rotates on T-nut base
|
||
//
|
||
// Parts (STL exports):
|
||
// Part 1 — tnut_base() Rail attachment base (universal)
|
||
// Part 2 — fixed_jaw() Fixed bottom jaw + guide rail bosses
|
||
// Part 3 — sliding_jaw() Spring-loaded sliding jaw
|
||
// Part 4 — cam_lever() Quick-release cam lever
|
||
// Part 5 — grip_pad() Flexure grip pad (print ×2, TPU optional)
|
||
// Part 6 — assembly_preview() Full assembly
|
||
//
|
||
// Hardware BOM (per mount):
|
||
// 1× M4 × 60 mm SHCS guide rod + spring bolt
|
||
// 1× M4 hex nut end-stop on sliding jaw
|
||
// 1× Ø8 × 30 mm coil spring ~0.5 N/mm rate (spring clamping)
|
||
// 2× M3 × 16 mm SHCS T-nut base thumbscrew + arm bolts
|
||
// 1× M3 hex nut thumbscrew nut in T-nut
|
||
// 4× M2 × 8 mm SHCS grip pad retention bolts (optional)
|
||
//
|
||
// Dimensions:
|
||
// Phone width range : PHONE_W_MIN–PHONE_W_MAX (60–85 mm) parametric
|
||
// Phone thickness : up to PHONE_THICK_MAX (12 mm) — open-front jaw
|
||
// Phone height held : GRIP_SPAN (22 mm each jaw) — portrait/landscape
|
||
// Overall bracket H : ~110 mm W: ~90 mm D: ~55 mm
|
||
//
|
||
// Print settings:
|
||
// Material : PETG (tnut_base, fixed_jaw, sliding_jaw, cam_lever)
|
||
// TPU 95A optional for grip_pad (or PETG for rigidity)
|
||
// Perimeters: 5 (structural parts), 3 (grip_pad)
|
||
// Infill : 40 % gyroid (jaws), 20 % (grip_pad)
|
||
// Supports : none needed (designed for FDM orientation)
|
||
// Layer ht : 0.2 mm
|
||
//
|
||
// Export commands:
|
||
// openscad phone_mount_bracket.scad -D 'RENDER="tnut_base_stl"' -o pm_tnut_base.stl
|
||
// openscad phone_mount_bracket.scad -D 'RENDER="fixed_jaw_stl"' -o pm_fixed_jaw.stl
|
||
// openscad phone_mount_bracket.scad -D 'RENDER="sliding_jaw_stl"' -o pm_sliding_jaw.stl
|
||
// openscad phone_mount_bracket.scad -D 'RENDER="cam_lever_stl"' -o pm_cam_lever.stl
|
||
// openscad phone_mount_bracket.scad -D 'RENDER="grip_pad_stl"' -o pm_grip_pad.stl
|
||
// ============================================================
|
||
|
||
$fn = 64;
|
||
e = 0.01; // epsilon for boolean clearance
|
||
|
||
// ── Phone parameters (adjust to target device) ───────────────────────────────
|
||
PHONE_W_MIN = 60.0; // narrowest phone width supported (mm)
|
||
PHONE_W_MAX = 85.0; // widest phone width supported (mm)
|
||
PHONE_THICK_MAX = 12.0; // max phone body thickness incl. case (mm)
|
||
|
||
// ── Rail geometry (must match sensor_rail.scad) ──────────────────────────────
|
||
RAIL_W = 20.0;
|
||
SLOT_OPEN = 6.0;
|
||
SLOT_INNER_W = 10.2;
|
||
SLOT_INNER_H = 5.8;
|
||
SLOT_NECK_H = 3.2;
|
||
|
||
// ── T-nut constants ───────────────────────────────────────────────────────────
|
||
TNUT_W = 9.8;
|
||
TNUT_H = 5.5;
|
||
TNUT_L = 12.0;
|
||
TNUT_M3_NUT_AF = 5.5;
|
||
TNUT_M3_NUT_H = 2.5;
|
||
TNUT_BOLT_D = 3.3; // M3 clearance
|
||
|
||
// ── Base plate geometry ───────────────────────────────────────────────────────
|
||
BASE_FACE_W = 30.0;
|
||
BASE_FACE_H = 25.0;
|
||
BASE_FACE_T = SLOT_NECK_H + 1.5;
|
||
|
||
// ── Jaw geometry ─────────────────────────────────────────────────────────────
|
||
JAW_BODY_W = 88.0; // jaw outer width (> PHONE_W_MAX for rim)
|
||
JAW_BODY_H = 28.0; // jaw height (Z) — phone grip span
|
||
JAW_BODY_T = 14.0; // jaw depth (Y) — phone cradled this deep
|
||
JAW_WALL_T = 4.0; // jaw side wall thickness
|
||
JAW_LIP_T = 3.0; // front retaining lip thickness
|
||
JAW_LIP_H = 5.0; // front lip height (retains phone)
|
||
PHONE_POCKET_D = PHONE_THICK_MAX + 0.5; // pocket depth for phone
|
||
|
||
// ── Guide rod / spring system ─────────────────────────────────────────────────
|
||
GUIDE_ROD_D = 4.3; // M4 clearance bore in sliding jaw
|
||
GUIDE_BOSS_D = 10.0; // boss OD around guide bore
|
||
GUIDE_BOSS_T = 6.0; // boss length
|
||
SPRING_OD = 8.5; // coil spring OD pocket (spring is Ø8)
|
||
SPRING_L = 32.0; // spring pocket length (spring compressed ~22 mm)
|
||
SPRING_SEAT_T = 3.0; // spring seat wall at end-stop boss
|
||
JAW_TRAVEL = PHONE_W_MAX - PHONE_W_MIN + 4.0; // max jaw travel (mm)
|
||
ARM_SPAN = PHONE_W_MAX + 2 * JAW_WALL_T + 8; // fixed jaw total width
|
||
|
||
// ── Cam lever geometry ────────────────────────────────────────────────────────
|
||
CAM_R_MIN = 5.0; // cam small radius (engaged / clamped)
|
||
CAM_R_MAX = 9.0; // cam large radius (released, spring compressed)
|
||
CAM_THICK = 8.0; // cam disc thickness
|
||
CAM_HANDLE_L = 45.0; // lever arm length
|
||
CAM_HANDLE_W = 8.0; // lever handle width
|
||
CAM_HANDLE_T = 5.0; // lever handle thickness
|
||
CAM_BORE_D = 4.3; // M4 pivot bore
|
||
CAM_DETENT_D = 3.0; // detent ball pocket (3 mm bearing)
|
||
|
||
// ── Grip pad geometry (vibration dampening flexure ribs) ─────────────────────
|
||
PAD_W = JAW_BODY_W - 2*JAW_WALL_T - 2; // pad width
|
||
PAD_H = JAW_BODY_H - 2; // pad height
|
||
PAD_T = 2.5; // pad body thickness
|
||
RIB_H = 1.5; // flexure rib height above pad face
|
||
RIB_W = 1.2; // rib width
|
||
RIB_PITCH = 5.0; // rib pitch (centre-to-centre)
|
||
RIB_COUNT = floor(PAD_W / RIB_PITCH) - 1;
|
||
|
||
// ── Arm geometry (base to jaw body) ──────────────────────────────────────────
|
||
ARM_REACH = 38.0; // distance from rail face to jaw centreline (+Y)
|
||
ARM_T = 4.0; // arm thickness
|
||
ARM_H = BASE_FACE_H;
|
||
|
||
// ── Fasteners ─────────────────────────────────────────────────────────────────
|
||
M2_D = 2.4;
|
||
M3_D = 3.3;
|
||
M4_D = 4.3;
|
||
M4_NUT_AF = 7.0; // M4 hex nut across-flats
|
||
M4_NUT_H = 3.2; // M4 hex nut height
|
||
|
||
// ============================================================
|
||
// RENDER DISPATCH
|
||
// ============================================================
|
||
RENDER = "assembly";
|
||
|
||
if (RENDER == "assembly") assembly_preview();
|
||
else if (RENDER == "tnut_base_stl") tnut_base();
|
||
else if (RENDER == "fixed_jaw_stl") fixed_jaw();
|
||
else if (RENDER == "sliding_jaw_stl") sliding_jaw();
|
||
else if (RENDER == "cam_lever_stl") cam_lever();
|
||
else if (RENDER == "grip_pad_stl") grip_pad();
|
||
|
||
// ============================================================
|
||
// ASSEMBLY PREVIEW
|
||
// ============================================================
|
||
module assembly_preview() {
|
||
// Ghost rail section (20 × 20 × 200)
|
||
%color("Silver", 0.30)
|
||
linear_extrude(200)
|
||
square([RAIL_W, RAIL_W], center = true);
|
||
|
||
// T-nut base at Z=80 on rail
|
||
color("OliveDrab", 0.85)
|
||
translate([0, 0, 80])
|
||
tnut_base();
|
||
|
||
// Fixed jaw assembly (centred, extending +Y from base)
|
||
color("DarkSlateGray", 0.85)
|
||
translate([0, SLOT_NECK_H + BASE_FACE_T + ARM_REACH, 80])
|
||
fixed_jaw();
|
||
|
||
// Sliding jaw — shown at mid-travel (phone ~72 mm wide)
|
||
color("SteelBlue", 0.85)
|
||
translate([PHONE_W_MIN + (PHONE_W_MAX - PHONE_W_MIN)/2,
|
||
SLOT_NECK_H + BASE_FACE_T + ARM_REACH, 80])
|
||
sliding_jaw();
|
||
|
||
// Grip pads on both jaws
|
||
color("DimGray", 0.85) {
|
||
translate([0, SLOT_NECK_H + BASE_FACE_T + ARM_REACH, 80])
|
||
translate([JAW_WALL_T, JAW_BODY_T, JAW_BODY_H/2])
|
||
rotate([90, 0, 0])
|
||
grip_pad();
|
||
translate([PHONE_W_MIN + (PHONE_W_MAX - PHONE_W_MIN)/2,
|
||
SLOT_NECK_H + BASE_FACE_T + ARM_REACH, 80])
|
||
translate([-JAW_WALL_T - PAD_T, JAW_BODY_T, JAW_BODY_H/2])
|
||
rotate([90, 0, 180])
|
||
grip_pad();
|
||
}
|
||
|
||
// Cam lever — shown in locked (clamped) position
|
||
color("OrangeRed", 0.85)
|
||
translate([ARM_SPAN/2 + 6,
|
||
SLOT_NECK_H + BASE_FACE_T + ARM_REACH + GUIDE_BOSS_D/2,
|
||
80 + JAW_BODY_H/2])
|
||
rotate([0, 0, 0])
|
||
cam_lever();
|
||
}
|
||
|
||
// ============================================================
|
||
// PART 1 — T-NUT BASE
|
||
// ============================================================
|
||
// Standard 2020 T-slot rail attachment base.
|
||
// Identical interface to sensor_rail_brackets.scad universal_tnut_base().
|
||
// Arm extends in +Y; rail clamp bolt in -Y face.
|
||
//
|
||
// Print flat (face plate down), PETG, 5 perims, 60 % infill.
|
||
module tnut_base() {
|
||
difference() {
|
||
union() {
|
||
// Face plate (flush against rail outer face)
|
||
translate([-BASE_FACE_W/2, -BASE_FACE_T, 0])
|
||
cube([BASE_FACE_W, BASE_FACE_T, BASE_FACE_H]);
|
||
|
||
// T-nut neck (enters rail slot)
|
||
translate([-TNUT_W/2, 0, (BASE_FACE_H - TNUT_L)/2])
|
||
cube([TNUT_W, SLOT_NECK_H + e, TNUT_L]);
|
||
|
||
// T-nut body (wider, inside T-groove)
|
||
translate([-TNUT_W/2, SLOT_NECK_H - e, (BASE_FACE_H - TNUT_L)/2])
|
||
cube([TNUT_W, TNUT_H - SLOT_NECK_H + e, TNUT_L]);
|
||
|
||
// Arm stub (face plate → jaw)
|
||
translate([-BASE_FACE_W/2, -BASE_FACE_T, 0])
|
||
cube([BASE_FACE_W, BASE_FACE_T + ARM_REACH, ARM_T]);
|
||
}
|
||
|
||
// M3 rail clamp bolt bore (centre of T-nut, through face plate)
|
||
translate([0, -BASE_FACE_T - e, BASE_FACE_H/2])
|
||
rotate([-90, 0, 0])
|
||
cylinder(d = TNUT_BOLT_D, h = BASE_FACE_T + TNUT_H + 2*e);
|
||
|
||
// M3 hex nut pocket (inside T-nut body)
|
||
translate([0, SLOT_NECK_H + 0.3, BASE_FACE_H/2])
|
||
rotate([-90, 0, 0])
|
||
cylinder(d = TNUT_M3_NUT_AF / cos(30),
|
||
h = TNUT_M3_NUT_H + 0.3,
|
||
$fn = 6);
|
||
|
||
// 2× M3 bolt holes for arm-to-jaw bolting
|
||
for (bx = [-10, 10])
|
||
translate([bx, ARM_REACH - BASE_FACE_T - e, ARM_T/2])
|
||
rotate([-90, 0, 0])
|
||
cylinder(d = M3_D, h = 8 + 2*e);
|
||
|
||
// Lightening slot in arm
|
||
translate([0, -BASE_FACE_T/2 + ARM_REACH/2, ARM_T/2])
|
||
cube([BASE_FACE_W - 12, ARM_REACH - 16, ARM_T + 2*e],
|
||
center = true);
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// PART 2 — FIXED JAW
|
||
// ============================================================
|
||
// Fixed lower jaw of the clamping system. Phone sits in the pocket
|
||
// formed by the fixed jaw (bottom) and sliding jaw (top).
|
||
// Two guide bosses on the right wall carry the M4 guide rod + spring.
|
||
// The cam lever pivot boss is on the outer right face.
|
||
//
|
||
// Coordinate origin: centre-bottom of jaw body.
|
||
// Phone entry face: +Y (open front), phone pocket opens toward +Y.
|
||
// Fixed jaw left edge is at X = -JAW_BODY_W/2.
|
||
//
|
||
// Print jaw-pocket-face down, PETG, 5 perims, 40 % infill.
|
||
module fixed_jaw() {
|
||
difference() {
|
||
union() {
|
||
// ── Main jaw body ────────────────────────────────────────────
|
||
translate([-JAW_BODY_W/2, -JAW_BODY_T/2, 0])
|
||
cube([JAW_BODY_W, JAW_BODY_T, JAW_BODY_H]);
|
||
|
||
// ── Front retaining lip (keeps phone from falling forward) ───
|
||
translate([-JAW_BODY_W/2, JAW_BODY_T/2 - JAW_LIP_T, 0])
|
||
cube([JAW_BODY_W, JAW_LIP_T, JAW_LIP_H]);
|
||
|
||
// ── Guide boss right (outer, carries spring + end-stop) ──────
|
||
translate([JAW_BODY_W/2, 0, JAW_BODY_H/2])
|
||
rotate([0, 90, 0])
|
||
cylinder(d = GUIDE_BOSS_D, h = GUIDE_BOSS_T);
|
||
|
||
// ── Cam lever pivot boss (right face, above guide boss) ──────
|
||
translate([JAW_BODY_W/2, 0, JAW_BODY_H/2 + GUIDE_BOSS_D + 4])
|
||
rotate([0, 90, 0])
|
||
cylinder(d = CAM_THICK + 4, h = 6);
|
||
|
||
// ── Arm attachment bosses (left side, connect to tnut_base) ──
|
||
for (bx = [-10, 10])
|
||
translate([bx, -JAW_BODY_T/2 - 8, ARM_T/2])
|
||
cylinder(d = 8, h = 8);
|
||
}
|
||
|
||
// ── Phone pocket (open-top U channel centred in jaw) ────────────
|
||
// Pocket opens toward +Y (front), phone drops in from above.
|
||
translate([0, -JAW_BODY_T/2 - e,
|
||
JAW_LIP_H])
|
||
cube([JAW_BODY_W - 2*JAW_WALL_T,
|
||
PHONE_POCKET_D + JAW_WALL_T,
|
||
JAW_BODY_H - JAW_LIP_H + e],
|
||
center = [true, false, false]);
|
||
|
||
// ── Guide rod bore (M4 clearance, through both guide bosses) ────
|
||
translate([-JAW_BODY_W/2 - e, 0, JAW_BODY_H/2])
|
||
rotate([0, 90, 0])
|
||
cylinder(d = GUIDE_ROD_D,
|
||
h = JAW_BODY_W + GUIDE_BOSS_T + 2*e);
|
||
|
||
// ── Spring pocket (coaxial with guide rod, in right boss) ────────
|
||
translate([JAW_BODY_W/2 + e, 0, JAW_BODY_H/2])
|
||
rotate([0, -90, 0])
|
||
cylinder(d = SPRING_OD, h = SPRING_L);
|
||
|
||
// ── M4 hex nut pocket in spring-seat wall (end-stop nut) ────────
|
||
translate([JAW_BODY_W/2 + GUIDE_BOSS_T + e, 0, JAW_BODY_H/2])
|
||
rotate([0, -90, 0])
|
||
cylinder(d = M4_NUT_AF / cos(30), h = M4_NUT_H + 0.5,
|
||
$fn = 6);
|
||
|
||
// ── Cam pivot bore (M4 pivot, through pivot boss) ────────────────
|
||
translate([JAW_BODY_W/2 - e, 0, JAW_BODY_H/2 + GUIDE_BOSS_D + 4])
|
||
rotate([0, 90, 0])
|
||
cylinder(d = CAM_BORE_D, h = 6 + 2*e);
|
||
|
||
// ── Arm attachment bolt holes (M3, to tnut_base arm stubs) ──────
|
||
for (bx = [-10, 10])
|
||
translate([bx, -JAW_BODY_T/2 - 8 - e, ARM_T/2])
|
||
rotate([-90, 0, 0])
|
||
cylinder(d = M3_D, h = 12 + 2*e);
|
||
|
||
// ── Grip pad seats (recessed Ø1.5 mm, 2 mm deep, optional) ──────
|
||
for (pz = [JAW_BODY_H * 0.3, JAW_BODY_H * 0.7])
|
||
for (px = [-PAD_W/4, PAD_W/4])
|
||
translate([px, -JAW_BODY_T/2 + PHONE_POCKET_D + 1, pz])
|
||
rotate([-90, 0, 0])
|
||
cylinder(d = M2_D, h = 10);
|
||
|
||
// ── Lightening pockets (non-structural core removal) ─────────────
|
||
translate([0, 0, JAW_BODY_H/2])
|
||
cube([JAW_BODY_W - 2*JAW_WALL_T - 4,
|
||
JAW_BODY_T - 2*JAW_WALL_T,
|
||
JAW_BODY_H - JAW_LIP_H - 4],
|
||
center = true);
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// PART 3 — SLIDING JAW
|
||
// ============================================================
|
||
// Upper clamping jaw. Slides along the M4 guide rod.
|
||
// Spring pushes this jaw toward the phone (inward).
|
||
// M4 hex nut on the guide rod limits maximum travel (full open).
|
||
// Cam lever pushes on this jaw face to compress spring (release).
|
||
//
|
||
// Coordinate origin same convention as fixed_jaw() for assembly.
|
||
// Jaw slides in +X direction (away from fixed jaw left wall).
|
||
//
|
||
// Print jaw-pocket-face down, PETG, 5 perims, 40 % infill.
|
||
module sliding_jaw() {
|
||
difference() {
|
||
union() {
|
||
// ── Main jaw body ────────────────────────────────────────────
|
||
translate([-JAW_WALL_T, -JAW_BODY_T/2, 0])
|
||
cube([JAW_BODY_W/2 + JAW_WALL_T, JAW_BODY_T, JAW_BODY_H]);
|
||
|
||
// ── Front retaining lip ──────────────────────────────────────
|
||
translate([-JAW_WALL_T, JAW_BODY_T/2 - JAW_LIP_T, 0])
|
||
cube([JAW_BODY_W/2 + JAW_WALL_T, JAW_LIP_T, JAW_LIP_H]);
|
||
|
||
// ── Guide boss (carries guide rod, spring butts against face) ─
|
||
translate([-JAW_WALL_T - GUIDE_BOSS_T, 0, JAW_BODY_H/2])
|
||
rotate([0, 90, 0])
|
||
cylinder(d = GUIDE_BOSS_D, h = GUIDE_BOSS_T);
|
||
|
||
// ── Cam follower ear (contacts cam lever) ────────────────────
|
||
translate([-JAW_WALL_T - 2, 0,
|
||
JAW_BODY_H/2 + GUIDE_BOSS_D + 4])
|
||
cube([4, CAM_THICK + 2, CAM_THICK + 2], center = true);
|
||
}
|
||
|
||
// ── Phone pocket (inner face, contacts phone side) ───────────────
|
||
translate([-JAW_WALL_T - e, -JAW_BODY_T/2 - e, JAW_LIP_H])
|
||
cube([JAW_BODY_W/2 - JAW_WALL_T + e,
|
||
PHONE_POCKET_D + JAW_WALL_T + 2*e,
|
||
JAW_BODY_H - JAW_LIP_H + e]);
|
||
|
||
// ── Guide rod bore (M4 clearance through boss + jaw wall) ────────
|
||
translate([-JAW_WALL_T - GUIDE_BOSS_T - e, 0, JAW_BODY_H/2])
|
||
rotate([0, 90, 0])
|
||
cylinder(d = GUIDE_ROD_D,
|
||
h = GUIDE_BOSS_T + JAW_WALL_T + 2*e);
|
||
|
||
// ── M4 nut pocket (end-stop nut, rear of guide boss) ────────────
|
||
translate([-JAW_WALL_T - GUIDE_BOSS_T - e, 0, JAW_BODY_H/2])
|
||
rotate([0, 90, 0])
|
||
cylinder(d = M4_NUT_AF / cos(30), h = M4_NUT_H + 1,
|
||
$fn = 6);
|
||
|
||
// ── Cam follower bore (M4 pivot passes through ear) ─────────────
|
||
translate([-JAW_WALL_T - 2 - e, 0,
|
||
JAW_BODY_H/2 + GUIDE_BOSS_D + 4])
|
||
rotate([0, 90, 0])
|
||
cylinder(d = CAM_BORE_D, h = 6 + 2*e);
|
||
|
||
// ── Grip pad seats ───────────────────────────────────────────────
|
||
for (pz = [JAW_BODY_H * 0.3, JAW_BODY_H * 0.7])
|
||
for (px = [JAW_BODY_W/8])
|
||
translate([px, -JAW_BODY_T/2 + PHONE_POCKET_D + 1, pz])
|
||
rotate([-90, 0, 0])
|
||
cylinder(d = M2_D, h = 10);
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// PART 4 — CAM LEVER (QUICK-RELEASE)
|
||
// ============================================================
|
||
// Eccentric cam disc + integral handle lever.
|
||
// Rotates 90° on M4 pivot pin between CLAMPED and RELEASED states:
|
||
// CLAMPED : cam small radius (CAM_R_MIN) toward jaw → spring pushes jaw
|
||
// RELEASED : cam large radius (CAM_R_MAX) toward jaw → compresses spring
|
||
// by (CAM_R_MAX - CAM_R_MIN) = 4 mm, opening jaw
|
||
//
|
||
// Detent ball pocket (Ø3 mm) snaps into rail-dimple for each position.
|
||
// Handle points rearward (-Y) in clamped state for low profile.
|
||
//
|
||
// Print standing on cam edge (cam disc vertical), PETG, 5 perims, 40%.
|
||
module cam_lever() {
|
||
cam_offset = (CAM_R_MAX - CAM_R_MIN) / 2; // 2 mm eccentricity
|
||
union() {
|
||
difference() {
|
||
union() {
|
||
// ── Eccentric cam disc ───────────────────────────────────
|
||
// Offset so pivot bore is eccentric to disc profile
|
||
translate([cam_offset, 0, 0])
|
||
cylinder(r = CAM_R_MAX, h = CAM_THICK, center = true);
|
||
|
||
// ── Lever handle arm ─────────────────────────────────────
|
||
hull() {
|
||
translate([cam_offset, 0, 0])
|
||
cylinder(r = CAM_R_MAX, h = CAM_THICK, center = true);
|
||
translate([cam_offset + CAM_HANDLE_L, 0, 0])
|
||
cylinder(r = CAM_HANDLE_W/2,
|
||
h = CAM_HANDLE_T, center = true);
|
||
}
|
||
}
|
||
|
||
// ── M4 pivot bore (through cam centre) ───────────────────────
|
||
cylinder(d = CAM_BORE_D, h = CAM_THICK + 2*e, center = true);
|
||
|
||
// ── Detent pockets (2× Ø3 mm, at 0° and 90°) ────────────────
|
||
// Pocket at 0° → clamped detent
|
||
translate([CAM_R_MAX - 2, 0, CAM_THICK/2 - 1.5])
|
||
cylinder(d = CAM_DETENT_D + 0.2, h = 2);
|
||
// Pocket at 90° → released detent
|
||
translate([0, CAM_R_MAX - 2, CAM_THICK/2 - 1.5])
|
||
cylinder(d = CAM_DETENT_D + 0.2, h = 2);
|
||
|
||
// ── Lightening recesses on cam disc face ─────────────────────
|
||
for (a = [0, 60, 120, 180, 240, 300])
|
||
translate([cam_offset + (CAM_R_MAX - 4) * cos(a),
|
||
(CAM_R_MAX - 4) * sin(a), 0])
|
||
cylinder(d = 4, h = CAM_THICK + 2*e, center = true);
|
||
|
||
// ── Handle grip grooves ──────────────────────────────────────
|
||
for (i = [0:4])
|
||
translate([cam_offset + 20 + i * 5, 0, 0])
|
||
rotate([90, 0, 0])
|
||
cylinder(d = 2.5, h = CAM_HANDLE_W + 2*e,
|
||
center = true);
|
||
}
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// PART 5 — GRIP PAD (VIBRATION DAMPENING)
|
||
// ============================================================
|
||
// Flat pad with transverse flexure ribs that press against phone side.
|
||
// The rib profile (thin PETG fins) provides compliance in Z (vertical)
|
||
// absorbing vibration transmitted through the bracket.
|
||
// Optional: print in TPU 95A for superior damping.
|
||
// M2 bolts or adhesive-backed foam tape attach pad to jaw pocket face.
|
||
//
|
||
// Pad face (+Y) contacts phone. Mounting face (-Y) bonds to jaw.
|
||
// Ribs run parallel to Z axis (vertical).
|
||
//
|
||
// Print flat (mounting face down), PETG or TPU 95A, 3 perims, 20%.
|
||
module grip_pad() {
|
||
union() {
|
||
// ── Base plate ───────────────────────────────────────────────────
|
||
translate([-PAD_W/2, -PAD_T, -PAD_H/2])
|
||
cube([PAD_W, PAD_T, PAD_H]);
|
||
|
||
// ── Flexure ribs (transverse, dampening in Z) ────────────────────
|
||
// RIB_COUNT ribs spaced RIB_PITCH apart, centred on pad
|
||
for (i = [0 : RIB_COUNT - 1]) {
|
||
rx = -PAD_W/2 + RIB_PITCH/2 + i * RIB_PITCH;
|
||
if (abs(rx) <= PAD_W/2 - RIB_W/2) // stay within pad
|
||
translate([rx, 0, 0])
|
||
cube([RIB_W, RIB_H, PAD_H - 4], center = true);
|
||
}
|
||
|
||
// ── Corner retention nubs (M2 boss for optional bolt-through) ────
|
||
for (px = [-PAD_W/2 + 5, PAD_W/2 - 5])
|
||
for (pz = [-PAD_H/2 + 5, PAD_H/2 - 5])
|
||
translate([px, -PAD_T/2, pz])
|
||
difference() {
|
||
cylinder(d = 5, h = PAD_T, center = true);
|
||
cylinder(d = M2_D, h = PAD_T + 2*e, center = true);
|
||
}
|
||
}
|
||
}
|