saltylab-firmware/chassis/charging_dock_receiver.scad
sl-mechanical 8222a0c42e feat(mechanical): charging dock station (Issue #159)
Universal 5 V/5 A charging dock for SaltyLab/Rover/Tank robots:
- charging_dock.scad: weighted base (ballast pockets, floor anchors),
  back wall with 2× 5 A pogo pin housing + wiring channel, V-guide
  funnel rails (±20 mm alignment tolerance), ArUco marker mast (100×100 mm,
  15° tilt), PSU bracket (IRM-30-5), 4-LED status bezel
  (Searching/Aligned/Charging/Full)
- charging_dock_receiver.scad: 3-variant robot-side contact plate with
  Ø12 mm brass pad press-fit, V-nose self-alignment; SaltyLab stem
  collar, SaltyRover deck flange, SaltyTank skid-plate mount
- charging_dock_BOM.md: hardware list, ASCII wiring diagram, INA219
  current-sense LED state logic, pogo height cross-variant shim table,
  assembly sequence, export commands

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 10:15:14 -05:00

332 lines
14 KiB
OpenSCAD
Raw Permalink 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.

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