saltylab-firmware/chassis/phone_mount_bracket.scad
sl-mechanical fc43135144 feat: Spring-loaded phone mount bracket for T-slot rail (Issue #535)
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>
2026-03-07 09:49:10 -05:00

505 lines
24 KiB
OpenSCAD
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ============================================================
// 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 6085 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_MINPHONE_W_MAX (6085 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);
}
}
}