// ============================================================ // charging_dock_receiver.scad — Robot-Side Charging Receiver // Issue: #159 Agent: sl-mechanical Date: 2026-03-01 // ============================================================ // // Robot-side contact plate that mates with the charging dock pogo pins. // Each robot variant has a different mounting interface; the contact // geometry is identical across all variants (same pogo pin spacing). // // Variants: // A — lab_receiver() SaltyLab — mounts to underside of stem base ring // B — rover_receiver() SaltyRover — mounts to chassis belly (M4 deck holes) // C — tank_receiver() SaltyTank — mounts to skid plate / hull floor // // Contact geometry (common across variants): // 2× brass contact pads, Ø12 mm × 2 mm (press-fit into PETG housing) // Pad spacing: 20 mm CL-to-CL (matches dock POGO_SPACING exactly) // Contact face Z height matches dock pogo pin Z when robot is level // Polarity: marked + on top pin (conventional: positive = right when // facing dock; negative = left) — must match dock wiring. // // Approach guide nose: // A chamfered V-nose on the forward face guides the receiver block // into the dock's V-funnel. Taper half-angle ≈ 14° matches guide rails. // Nose width = RECV_W = 30 mm (matches dock EXIT_GAP - 2 mm clearance). // // Coordinate convention: // Z = 0 at receiver mounting face (against robot chassis/deck underside). // +Z points downward (toward dock floor). // Contact pads face +Y (toward dock back wall when docked). // Receiver centred on X = 0 (robot centreline). // // RENDER options: // "assembly" all 3 receivers side by side // "lab_stl" SaltyLab receiver (print 1×) // "rover_stl" SaltyRover receiver (print 1×) // "tank_stl" SaltyTank receiver (print 1×) // "contact_pad_2d" DXF — Ø12 mm brass pad profile (order from metal shop) // // Export: // openscad charging_dock_receiver.scad -D 'RENDER="lab_stl"' -o receiver_lab.stl // openscad charging_dock_receiver.scad -D 'RENDER="rover_stl"' -o receiver_rover.stl // openscad charging_dock_receiver.scad -D 'RENDER="tank_stl"' -o receiver_tank.stl // openscad charging_dock_receiver.scad -D 'RENDER="contact_pad_2d"' -o contact_pad.dxf // ============================================================ $fn = 64; e = 0.01; // ── Contact geometry (must match charging_dock.scad) ───────────────────────── POGO_SPACING = 20.0; // CL-to-CL (dock POGO_SPACING) PAD_D = 12.0; // contact pad OD (brass disc) PAD_T = 2.0; // contact pad thickness PAD_RECESS = 1.8; // pad pressed into housing (0.2 mm proud for contact) PAD_PROUD = 0.2; // pad face protrudes from housing face // ── Common receiver body geometry ──────────────────────────────────────────── RECV_W = 30.0; // receiver body width (X) — matches dock EXIT_GAP inner RECV_D = 25.0; // receiver body depth (Y, docking direction) RECV_H = 12.0; // receiver body height (Z, from mount face down) RECV_R = 3.0; // corner radius // V-nose geometry (front Y face — faces dock back wall) NOSE_CHAMFER = 10.0; // chamfer depth on X corners of front face // Polarity indicator slot (on top/mount face: + on right, - on left) POL_SLOT_W = 4.0; POL_SLOT_D = 8.0; POL_SLOT_H = 1.0; // Fasteners M2_D = 2.4; M3_D = 3.3; M4_D = 4.3; // ── Mounting patterns ───────────────────────────────────────────────────────── // SaltyLab stem base ring (Ø25 mm stem, 4× M3 in ring at Ø40 mm BC) LAB_BC_D = 40.0; LAB_BOLT_D = M3_D; LAB_COLLAR_H = 15.0; // collar height above receiver body // SaltyRover deck (M4 grid pattern, 30.5×30.5 mm matching FC pattern on deck) // Receiver uses 4× M4 holes at ±20 mm from centre (clear of deck electronics) ROVER_BOLT_SPC = 40.0; // SaltyTank skid plate (M4 holes matching skid plate bolt pattern) // Uses 4× M4 at ±20 mm X, ±10 mm Y (inset from skid plate M4 positions) TANK_BOLT_SPC_X = 40.0; TANK_BOLT_SPC_Y = 20.0; TANK_NOSE_L = 20.0; // extended nose for tank (wider hull) // ============================================================ // RENDER DISPATCH // ============================================================ RENDER = "assembly"; if (RENDER == "assembly") assembly(); else if (RENDER == "lab_stl") lab_receiver(); else if (RENDER == "rover_stl") rover_receiver(); else if (RENDER == "tank_stl") tank_receiver(); else if (RENDER == "contact_pad_2d") { projection(cut = true) translate([0, 0, -0.5]) linear_extrude(1) circle(d = PAD_D); } // ============================================================ // ASSEMBLY PREVIEW // ============================================================ module assembly() { // SaltyLab receiver color("RoyalBlue", 0.85) translate([-80, 0, 0]) lab_receiver(); // SaltyRover receiver color("OliveDrab", 0.85) translate([0, 0, 0]) rover_receiver(); // SaltyTank receiver color("SaddleBrown", 0.85) translate([80, 0, 0]) tank_receiver(); } // ============================================================ // COMMON RECEIVER BODY // ============================================================ // Internal helper: the shared contact housing + V-nose. // Orientation: mount face = +Z top; contact face = +Y front. // All variant-specific modules call this, then add their mount interface. module _receiver_body() { difference() { union() { // ── Main housing block (rounded) ───────────────────────── linear_extrude(RECV_H) _recv_profile_2d(); // ── V-nose chamfer reinforcement ribs ───────────────────── // Two diagonal ribs at 45° reinforce the chamfered corners for (sx = [-1, 1]) hull() { translate([sx*(RECV_W/2 - NOSE_CHAMFER), RECV_D/2, 0]) cylinder(d = 3, h = RECV_H * 0.6); translate([sx*(RECV_W/2), RECV_D/2 - NOSE_CHAMFER, 0]) cylinder(d = 3, h = RECV_H * 0.6); } } // ── Contact pad bores (2× Ø12 mm, press-fit) ───────────────── // Pads face +Y; bores from Y face into housing for (px = [-POGO_SPACING/2, POGO_SPACING/2]) translate([px, RECV_D/2 + e, RECV_H/2]) rotate([90, 0, 0]) { // Pad press-fit bore cylinder(d = PAD_D + 0.1, h = PAD_RECESS + e); // Wire bore (behind pad, to mount face) translate([0, 0, PAD_RECESS]) cylinder(d = 3.0, h = RECV_D + 2*e); } // ── Polarity indicator slots on top face ────────────────────── // "+" slot: right pad (+X side) translate([POGO_SPACING/2, 0, -e]) cube([POL_SLOT_W, POL_SLOT_D, POL_SLOT_H + e], center = true); // "-" indent: left pad (no slot = negative) // ── Wire routing channel (on mount face / underside) ────────── // Trough connecting both pad bores for neat wire run translate([0, RECV_D/2 - POGO_SPACING/2, RECV_H - 3]) cube([POGO_SPACING + 6, POGO_SPACING, 4], center = true); } } // ── 2D profile of receiver body with chamfered V-nose ──────────────────────── module _recv_profile_2d() { hull() { // Rear corners (full width) for (sx = [-1, 1]) translate([sx*(RECV_W/2 - RECV_R), -RECV_D/2 + RECV_R]) circle(r = RECV_R); // Front corners (chamfered — narrowed by NOSE_CHAMFER) for (sx = [-1, 1]) translate([sx*(RECV_W/2 - NOSE_CHAMFER - RECV_R), RECV_D/2 - RECV_R]) circle(r = RECV_R); } } // ============================================================ // PART A — SALTYLAB RECEIVER // ============================================================ // Mounts to the underside of the SaltyLab chassis stem base ring. // Split collar grips Ø25 mm stem; receiver body hangs below collar. // Z height set so contact pads align with dock pogo pins when robot // rests on flat surface (robot wheel-to-contact-pad height calibrated). // // Receiver height above floor: tune LAB_CONTACT_Z in firmware (UWB/ArUco // approach). Mechanically: receiver sits ~35 mm above ground (stem base // height), matching dock POGO_Z = 35 mm. module lab_receiver() { collar_od = 46.0; // matches sensor_rail.scad STEM_COL_OD collar_h = LAB_COLLAR_H; union() { // ── Common receiver body ──────────────────────────────────── _receiver_body(); // ── Stem collar (split, 2 halves joined with M4 bolts) ─────── // Only the front half printed here; rear half is mirror. translate([0, -RECV_D/2, RECV_H]) difference() { // Half-collar cylinder rotate_extrude(angle = 180) translate([collar_od/2 - 8, 0, 0]) square([8, collar_h]); // Stem bore clearance translate([0, 0, -e]) cylinder(d = 25.5, h = collar_h + 2*e); // 2× M4 clamping bolt bores (through collar flanges) for (cx = [-collar_od/2 + 4, collar_od/2 - 4]) translate([cx, 0, collar_h/2]) rotate([90, 0, 0]) cylinder(d = M4_D, h = collar_od + 2*e, center = true); } // ── M3 receiver-to-collar bolts ─────────────────────────────── // 4× M3 holes connecting collar flange to receiver body top // (These are mounting holes for assembly; not holes in the part) } } // ============================================================ // PART B — SALTYOVER RECEIVER // ============================================================ // Mounts to the underside of the SaltyRover deck plate. // 4× M4 bolts into deck underside (blind holes tapped in deck). // Receiver sits flush with deck belly; contact pads protrude 5 mm below. // Dock pogo Z = 35 mm must equal ground-to-deck-belly height for rover // (approximately 60 mm chassis clearance — shim with spacer if needed). module rover_receiver() { mount_h = 5.0; // mounting flange thickness union() { // ── Common receiver body ──────────────────────────────────── _receiver_body(); // ── Mounting flange (attaches to deck belly) ───────────────── difference() { translate([-(ROVER_BOLT_SPC/2 + 12), -RECV_D/2 - 10, RECV_H]) cube([ROVER_BOLT_SPC + 24, RECV_D + 20, mount_h]); // 4× M4 bolt holes for (fx = [-1, 1]) for (fy = [-1, 1]) translate([fx*ROVER_BOLT_SPC/2, fy*(RECV_D/2 + 5), RECV_H - e]) cylinder(d = M4_D, h = mount_h + 2*e); // Weight-reduction pockets for (sx = [-1, 1]) translate([sx*(ROVER_BOLT_SPC/4 + 6), 0, RECV_H + 1]) cube([ROVER_BOLT_SPC/2 - 4, RECV_D - 4, mount_h], center = true); } } } // ============================================================ // PART C — SALTYTANK RECEIVER // ============================================================ // Mounts to SaltyTank hull floor or replaces a section of skid plate. // Extended front nose (TANK_NOSE_L) for tank's wider hull approach. // Contact pads exposed through skid plate via a 30×16 mm slot. // Ground clearance: tank chassis = 90 mm; dock POGO_Z = 35 mm. // Use ramp shim (see BOM) under dock base to elevate pogo pins to 90 mm // OR set POGO_Z = 90 in dock for a tank-specific dock configuration. // ⚠ Cross-variant dock: set POGO_Z per robot if heights differ. // Compromise: POGO_Z = 60 mm with 25 mm ramp for tank, 25 mm spacer for lab. module tank_receiver() { mount_h = 4.0; nose_l = RECV_D/2 + TANK_NOSE_L; union() { // ── Common receiver body ──────────────────────────────────── _receiver_body(); // ── Extended nose for tank approach ────────────────────────── // Additional chamfered wedge ahead of standard receiver body hull() { // Receiver front face corners for (sx = [-1, 1]) translate([sx*(RECV_W/2 - NOSE_CHAMFER), RECV_D/2, 0]) cylinder(d = 2*RECV_R, h = RECV_H * 0.5); // Extended nose tip (narrowed to 20 mm) for (sx = [-1, 1]) translate([sx*10, RECV_D/2 + TANK_NOSE_L, 0]) cylinder(d = 2*RECV_R, h = RECV_H * 0.4); } // ── Mounting flange (bolts to tank skid plate) ──────────────── difference() { translate([-(TANK_BOLT_SPC_X/2 + 10), -RECV_D/2 - 8, RECV_H]) cube([TANK_BOLT_SPC_X + 20, RECV_D + 16, mount_h]); // 4× M4 bolt holes for (fx = [-1, 1]) for (fy = [-1, 1]) translate([fx*TANK_BOLT_SPC_X/2, fy*TANK_BOLT_SPC_Y/2, RECV_H - e]) cylinder(d = M4_D, h = mount_h + 2*e); } } }